filer.go 6.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192
  1. // Copyright 2014 The lldb Authors. All rights reserved.
  2. // Use of this source code is governed by a BSD-style
  3. // license that can be found in the LICENSE file.
  4. // An abstraction of file like (persistent) storage with optional (abstracted)
  5. // support for structural integrity.
  6. package lldb
  7. import (
  8. "fmt"
  9. "github.com/cznic/mathutil"
  10. )
  11. func doubleTrouble(first, second error) error {
  12. return fmt.Errorf("%q. Additionally, while attempting to recover (rollback): %q", first, second)
  13. }
  14. // A Filer is a []byte-like model of a file or similar entity. It may
  15. // optionally implement support for structural transaction safety. In contrast
  16. // to a file stream, a Filer is not sequentially accessible. ReadAt and WriteAt
  17. // are always "addressed" by an offset and are assumed to perform atomically.
  18. // A Filer is not safe for concurrent access, it's designed for consumption by
  19. // the other objects in package, which should use a Filer from one goroutine
  20. // only or via a mutex. BeginUpdate, EndUpdate and Rollback must be either all
  21. // implemented by a Filer for structural integrity - or they should be all
  22. // no-ops; where/if that requirement is relaxed.
  23. //
  24. // If a Filer wraps another Filer implementation, it usually invokes the same
  25. // methods on the "inner" one, after some possible argument translations etc.
  26. // If a Filer implements the structural transactions handling methods
  27. // (BeginUpdate, EndUpdate and Rollback) as no-ops _and_ wraps another Filer:
  28. // it then still MUST invoke those methods on the inner Filer. This is
  29. // important for the case where a RollbackFiler exists somewhere down the
  30. // chain. It's also important for an Allocator - to know when it must
  31. // invalidate its FLT cache.
  32. type Filer interface {
  33. // BeginUpdate increments the "nesting" counter (initially zero). Every
  34. // call to BeginUpdate must be eventually "balanced" by exactly one of
  35. // EndUpdate or Rollback. Calls to BeginUpdate may nest.
  36. BeginUpdate() error
  37. // Analogous to os.File.Close().
  38. Close() error
  39. // EndUpdate decrements the "nesting" counter. If it's zero after that
  40. // then assume the "storage" has reached structural integrity (after a
  41. // batch of partial updates). If a Filer implements some support for
  42. // that (write ahead log, journal, etc.) then the appropriate actions
  43. // are to be taken for nesting == 0. Invocation of an unbalanced
  44. // EndUpdate is an error.
  45. EndUpdate() error
  46. // Analogous to os.File.Name().
  47. Name() string
  48. // PunchHole deallocates space inside a "file" in the byte range
  49. // starting at off and continuing for size bytes. The actual hole
  50. // created by PunchHole may be smaller than requested. The Filer size
  51. // (as reported by `Size()` does not change when hole punching, even
  52. // when punching the end of a file off. In contrast to the Linux
  53. // implementation of FALLOC_FL_PUNCH_HOLE in `fallocate`(2); a Filer is
  54. // free not only to ignore `PunchHole()` (implement it as a nop), but
  55. // additionally no guarantees about the content of the hole, when
  56. // eventually read back, are required, i.e. any data, not only zeros,
  57. // can be read from the "hole", including just anything what was left
  58. // there - with all of the possible security problems.
  59. PunchHole(off, size int64) error
  60. // As os.File.ReadAt. Note: `off` is an absolute "file pointer"
  61. // address and cannot be negative even when a Filer is a InnerFiler.
  62. ReadAt(b []byte, off int64) (n int, err error)
  63. // Rollback cancels and undoes the innermost pending update level.
  64. // Rollback decrements the "nesting" counter. If a Filer implements
  65. // some support for keeping structural integrity (write ahead log,
  66. // journal, etc.) then the appropriate actions are to be taken.
  67. // Invocation of an unbalanced Rollback is an error.
  68. Rollback() error
  69. // Analogous to os.File.FileInfo().Size().
  70. Size() (int64, error)
  71. // Analogous to os.Sync().
  72. Sync() (err error)
  73. // Analogous to os.File.Truncate().
  74. Truncate(size int64) error
  75. // Analogous to os.File.WriteAt(). Note: `off` is an absolute "file
  76. // pointer" address and cannot be negative even when a Filer is a
  77. // InnerFiler.
  78. WriteAt(b []byte, off int64) (n int, err error)
  79. }
  80. var _ Filer = &InnerFiler{} // Ensure InnerFiler is a Filer.
  81. // A InnerFiler is a Filer with added addressing/size translation.
  82. type InnerFiler struct {
  83. outer Filer
  84. off int64
  85. }
  86. // NewInnerFiler returns a new InnerFiler wrapped by `outer` in a way which
  87. // adds `off` to every access.
  88. //
  89. // For example, considering:
  90. //
  91. // inner := NewInnerFiler(outer, 10)
  92. //
  93. // then
  94. //
  95. // inner.WriteAt([]byte{42}, 4)
  96. //
  97. // translates to
  98. //
  99. // outer.WriteAt([]byte{42}, 14)
  100. //
  101. // But an attempt to emulate
  102. //
  103. // outer.WriteAt([]byte{17}, 9)
  104. //
  105. // by
  106. //
  107. // inner.WriteAt([]byte{17}, -1)
  108. //
  109. // will fail as the `off` parameter can never be < 0. Also note that
  110. //
  111. // inner.Size() == outer.Size() - off,
  112. //
  113. // i.e. `inner` pretends no `outer` exists. Finally, after e.g.
  114. //
  115. // inner.Truncate(7)
  116. // outer.Size() == 17
  117. //
  118. // will be true.
  119. func NewInnerFiler(outer Filer, off int64) *InnerFiler { return &InnerFiler{outer, off} }
  120. // BeginUpdate implements Filer.
  121. func (f *InnerFiler) BeginUpdate() error { return f.outer.BeginUpdate() }
  122. // Close implements Filer.
  123. func (f *InnerFiler) Close() (err error) { return f.outer.Close() }
  124. // EndUpdate implements Filer.
  125. func (f *InnerFiler) EndUpdate() error { return f.outer.EndUpdate() }
  126. // Name implements Filer.
  127. func (f *InnerFiler) Name() string { return f.outer.Name() }
  128. // PunchHole implements Filer. `off`, `size` must be >= 0.
  129. func (f *InnerFiler) PunchHole(off, size int64) error { return f.outer.PunchHole(f.off+off, size) }
  130. // ReadAt implements Filer. `off` must be >= 0.
  131. func (f *InnerFiler) ReadAt(b []byte, off int64) (n int, err error) {
  132. if off < 0 {
  133. return 0, &ErrINVAL{f.outer.Name() + ":ReadAt invalid off", off}
  134. }
  135. return f.outer.ReadAt(b, f.off+off)
  136. }
  137. // Rollback implements Filer.
  138. func (f *InnerFiler) Rollback() error { return f.outer.Rollback() }
  139. // Size implements Filer.
  140. func (f *InnerFiler) Size() (int64, error) {
  141. sz, err := f.outer.Size()
  142. if err != nil {
  143. return 0, err
  144. }
  145. return mathutil.MaxInt64(sz-f.off, 0), nil
  146. }
  147. // Sync() implements Filer.
  148. func (f *InnerFiler) Sync() (err error) {
  149. return f.outer.Sync()
  150. }
  151. // Truncate implements Filer.
  152. func (f *InnerFiler) Truncate(size int64) error { return f.outer.Truncate(size + f.off) }
  153. // WriteAt implements Filer. `off` must be >= 0.
  154. func (f *InnerFiler) WriteAt(b []byte, off int64) (n int, err error) {
  155. if off < 0 {
  156. return 0, &ErrINVAL{f.outer.Name() + ":WriteAt invalid off", off}
  157. }
  158. return f.outer.WriteAt(b, f.off+off)
  159. }