copyOnWriteFs.go 6.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254
  1. package afero
  2. import (
  3. "fmt"
  4. "os"
  5. "path/filepath"
  6. "syscall"
  7. "time"
  8. )
  9. // The CopyOnWriteFs is a union filesystem: a read only base file system with
  10. // a possibly writeable layer on top. Changes to the file system will only
  11. // be made in the overlay: Changing an existing file in the base layer which
  12. // is not present in the overlay will copy the file to the overlay ("changing"
  13. // includes also calls to e.g. Chtimes() and Chmod()).
  14. //
  15. // Reading directories is currently only supported via Open(), not OpenFile().
  16. type CopyOnWriteFs struct {
  17. base Fs
  18. layer Fs
  19. }
  20. func NewCopyOnWriteFs(base Fs, layer Fs) Fs {
  21. return &CopyOnWriteFs{base: base, layer: layer}
  22. }
  23. // Returns true if the file is not in the overlay
  24. func (u *CopyOnWriteFs) isBaseFile(name string) (bool, error) {
  25. if _, err := u.layer.Stat(name); err == nil {
  26. return false, nil
  27. }
  28. _, err := u.base.Stat(name)
  29. if err != nil {
  30. if oerr, ok := err.(*os.PathError); ok {
  31. if oerr.Err == os.ErrNotExist || oerr.Err == syscall.ENOENT || oerr.Err == syscall.ENOTDIR {
  32. return false, nil
  33. }
  34. }
  35. if err == syscall.ENOENT {
  36. return false, nil
  37. }
  38. }
  39. return true, err
  40. }
  41. func (u *CopyOnWriteFs) copyToLayer(name string) error {
  42. return copyToLayer(u.base, u.layer, name)
  43. }
  44. func (u *CopyOnWriteFs) Chtimes(name string, atime, mtime time.Time) error {
  45. b, err := u.isBaseFile(name)
  46. if err != nil {
  47. return err
  48. }
  49. if b {
  50. if err := u.copyToLayer(name); err != nil {
  51. return err
  52. }
  53. }
  54. return u.layer.Chtimes(name, atime, mtime)
  55. }
  56. func (u *CopyOnWriteFs) Chmod(name string, mode os.FileMode) error {
  57. b, err := u.isBaseFile(name)
  58. if err != nil {
  59. return err
  60. }
  61. if b {
  62. if err := u.copyToLayer(name); err != nil {
  63. return err
  64. }
  65. }
  66. return u.layer.Chmod(name, mode)
  67. }
  68. func (u *CopyOnWriteFs) Stat(name string) (os.FileInfo, error) {
  69. fi, err := u.layer.Stat(name)
  70. if err != nil {
  71. origErr := err
  72. if e, ok := err.(*os.PathError); ok {
  73. err = e.Err
  74. }
  75. if err == syscall.ENOENT || err == syscall.ENOTDIR {
  76. return u.base.Stat(name)
  77. }
  78. return nil, origErr
  79. }
  80. return fi, nil
  81. }
  82. // Renaming files present only in the base layer is not permitted
  83. func (u *CopyOnWriteFs) Rename(oldname, newname string) error {
  84. b, err := u.isBaseFile(oldname)
  85. if err != nil {
  86. return err
  87. }
  88. if b {
  89. return syscall.EPERM
  90. }
  91. return u.layer.Rename(oldname, newname)
  92. }
  93. // Removing files present only in the base layer is not permitted. If
  94. // a file is present in the base layer and the overlay, only the overlay
  95. // will be removed.
  96. func (u *CopyOnWriteFs) Remove(name string) error {
  97. err := u.layer.Remove(name)
  98. switch err {
  99. case syscall.ENOENT:
  100. _, err = u.base.Stat(name)
  101. if err == nil {
  102. return syscall.EPERM
  103. }
  104. return syscall.ENOENT
  105. default:
  106. return err
  107. }
  108. }
  109. func (u *CopyOnWriteFs) RemoveAll(name string) error {
  110. err := u.layer.RemoveAll(name)
  111. switch err {
  112. case syscall.ENOENT:
  113. _, err = u.base.Stat(name)
  114. if err == nil {
  115. return syscall.EPERM
  116. }
  117. return syscall.ENOENT
  118. default:
  119. return err
  120. }
  121. }
  122. func (u *CopyOnWriteFs) OpenFile(name string, flag int, perm os.FileMode) (File, error) {
  123. b, err := u.isBaseFile(name)
  124. if err != nil {
  125. return nil, err
  126. }
  127. if flag&(os.O_WRONLY|os.O_RDWR|os.O_APPEND|os.O_CREATE|os.O_TRUNC) != 0 {
  128. if b {
  129. if err = u.copyToLayer(name); err != nil {
  130. return nil, err
  131. }
  132. return u.layer.OpenFile(name, flag, perm)
  133. }
  134. dir := filepath.Dir(name)
  135. isaDir, err := IsDir(u.base, dir)
  136. if err != nil && !os.IsNotExist(err) {
  137. return nil, err
  138. }
  139. if isaDir {
  140. if err = u.layer.MkdirAll(dir, 0777); err != nil {
  141. return nil, err
  142. }
  143. return u.layer.OpenFile(name, flag, perm)
  144. }
  145. isaDir, err = IsDir(u.layer, dir)
  146. if err != nil {
  147. return nil, err
  148. }
  149. if isaDir {
  150. return u.layer.OpenFile(name, flag, perm)
  151. }
  152. return nil, &os.PathError{Op: "open", Path: name, Err: syscall.ENOTDIR} // ...or os.ErrNotExist?
  153. }
  154. if b {
  155. return u.base.OpenFile(name, flag, perm)
  156. }
  157. return u.layer.OpenFile(name, flag, perm)
  158. }
  159. // This function handles the 9 different possibilities caused
  160. // by the union which are the intersection of the following...
  161. // layer: doesn't exist, exists as a file, and exists as a directory
  162. // base: doesn't exist, exists as a file, and exists as a directory
  163. func (u *CopyOnWriteFs) Open(name string) (File, error) {
  164. // Since the overlay overrides the base we check that first
  165. b, err := u.isBaseFile(name)
  166. if err != nil {
  167. return nil, err
  168. }
  169. // If overlay doesn't exist, return the base (base state irrelevant)
  170. if b {
  171. return u.base.Open(name)
  172. }
  173. // If overlay is a file, return it (base state irrelevant)
  174. dir, err := IsDir(u.layer, name)
  175. if err != nil {
  176. return nil, err
  177. }
  178. if !dir {
  179. return u.layer.Open(name)
  180. }
  181. // Overlay is a directory, base state now matters.
  182. // Base state has 3 states to check but 2 outcomes:
  183. // A. It's a file or non-readable in the base (return just the overlay)
  184. // B. It's an accessible directory in the base (return a UnionFile)
  185. // If base is file or nonreadable, return overlay
  186. dir, err = IsDir(u.base, name)
  187. if !dir || err != nil {
  188. return u.layer.Open(name)
  189. }
  190. // Both base & layer are directories
  191. // Return union file (if opens are without error)
  192. bfile, bErr := u.base.Open(name)
  193. lfile, lErr := u.layer.Open(name)
  194. // If either have errors at this point something is very wrong. Return nil and the errors
  195. if bErr != nil || lErr != nil {
  196. return nil, fmt.Errorf("BaseErr: %v\nOverlayErr: %v", bErr, lErr)
  197. }
  198. return &UnionFile{base: bfile, layer: lfile}, nil
  199. }
  200. func (u *CopyOnWriteFs) Mkdir(name string, perm os.FileMode) error {
  201. dir, err := IsDir(u.base, name)
  202. if err != nil {
  203. return u.layer.MkdirAll(name, perm)
  204. }
  205. if dir {
  206. return syscall.EEXIST
  207. }
  208. return u.layer.MkdirAll(name, perm)
  209. }
  210. func (u *CopyOnWriteFs) Name() string {
  211. return "CopyOnWriteFs"
  212. }
  213. func (u *CopyOnWriteFs) MkdirAll(name string, perm os.FileMode) error {
  214. dir, err := IsDir(u.base, name)
  215. if err != nil {
  216. return u.layer.MkdirAll(name, perm)
  217. }
  218. if dir {
  219. return syscall.EEXIST
  220. }
  221. return u.layer.MkdirAll(name, perm)
  222. }
  223. func (u *CopyOnWriteFs) Create(name string) (File, error) {
  224. return u.OpenFile(name, os.O_CREATE|os.O_TRUNC|os.O_RDWR, 0666)
  225. }