cacheOnReadFs.go 6.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297
  1. package afero
  2. import (
  3. "os"
  4. "syscall"
  5. "time"
  6. )
  7. // If the cache duration is 0, cache time will be unlimited, i.e. once
  8. // a file is in the layer, the base will never be read again for this file.
  9. //
  10. // For cache times greater than 0, the modification time of a file is
  11. // checked. Note that a lot of file system implementations only allow a
  12. // resolution of a second for timestamps... or as the godoc for os.Chtimes()
  13. // states: "The underlying filesystem may truncate or round the values to a
  14. // less precise time unit."
  15. //
  16. // This caching union will forward all write calls also to the base file
  17. // system first. To prevent writing to the base Fs, wrap it in a read-only
  18. // filter - Note: this will also make the overlay read-only, for writing files
  19. // in the overlay, use the overlay Fs directly, not via the union Fs.
  20. type CacheOnReadFs struct {
  21. base Fs
  22. layer Fs
  23. cacheTime time.Duration
  24. }
  25. func NewCacheOnReadFs(base Fs, layer Fs, cacheTime time.Duration) Fs {
  26. return &CacheOnReadFs{base: base, layer: layer, cacheTime: cacheTime}
  27. }
  28. type cacheState int
  29. const (
  30. cacheUnknown cacheState = iota
  31. // not present in the overlay, unknown if it exists in the base:
  32. cacheMiss
  33. // present in the overlay and in base, base file is newer:
  34. cacheStale
  35. // present in the overlay - with cache time == 0 it may exist in the base,
  36. // with cacheTime > 0 it exists in the base and is same age or newer in the
  37. // overlay
  38. cacheHit
  39. // happens if someone writes directly to the overlay without
  40. // going through this union
  41. cacheLocal
  42. )
  43. func (u *CacheOnReadFs) cacheStatus(name string) (state cacheState, fi os.FileInfo, err error) {
  44. var lfi, bfi os.FileInfo
  45. lfi, err = u.layer.Stat(name)
  46. if err == nil {
  47. if u.cacheTime == 0 {
  48. return cacheHit, lfi, nil
  49. }
  50. if lfi.ModTime().Add(u.cacheTime).Before(time.Now()) {
  51. bfi, err = u.base.Stat(name)
  52. if err != nil {
  53. return cacheLocal, lfi, nil
  54. }
  55. if bfi.ModTime().After(lfi.ModTime()) {
  56. return cacheStale, bfi, nil
  57. }
  58. }
  59. return cacheHit, lfi, nil
  60. }
  61. if err == syscall.ENOENT {
  62. return cacheMiss, nil, nil
  63. }
  64. var ok bool
  65. if err, ok = err.(*os.PathError); ok {
  66. if err == os.ErrNotExist {
  67. return cacheMiss, nil, nil
  68. }
  69. }
  70. return cacheMiss, nil, err
  71. }
  72. func (u *CacheOnReadFs) copyToLayer(name string) error {
  73. return copyToLayer(u.base, u.layer, name)
  74. }
  75. func (u *CacheOnReadFs) Chtimes(name string, atime, mtime time.Time) error {
  76. st, _, err := u.cacheStatus(name)
  77. if err != nil {
  78. return err
  79. }
  80. switch st {
  81. case cacheLocal:
  82. case cacheHit:
  83. err = u.base.Chtimes(name, atime, mtime)
  84. case cacheStale, cacheMiss:
  85. if err := u.copyToLayer(name); err != nil {
  86. return err
  87. }
  88. err = u.base.Chtimes(name, atime, mtime)
  89. }
  90. if err != nil {
  91. return err
  92. }
  93. return u.layer.Chtimes(name, atime, mtime)
  94. }
  95. func (u *CacheOnReadFs) Chmod(name string, mode os.FileMode) error {
  96. st, _, err := u.cacheStatus(name)
  97. if err != nil {
  98. return err
  99. }
  100. switch st {
  101. case cacheLocal:
  102. case cacheHit:
  103. err = u.base.Chmod(name, mode)
  104. case cacheStale, cacheMiss:
  105. if err := u.copyToLayer(name); err != nil {
  106. return err
  107. }
  108. err = u.base.Chmod(name, mode)
  109. }
  110. if err != nil {
  111. return err
  112. }
  113. return u.layer.Chmod(name, mode)
  114. }
  115. func (u *CacheOnReadFs) Stat(name string) (os.FileInfo, error) {
  116. st, fi, err := u.cacheStatus(name)
  117. if err != nil {
  118. return nil, err
  119. }
  120. switch st {
  121. case cacheMiss:
  122. return u.base.Stat(name)
  123. default: // cacheStale has base, cacheHit and cacheLocal the layer os.FileInfo
  124. return fi, nil
  125. }
  126. }
  127. func (u *CacheOnReadFs) Rename(oldname, newname string) error {
  128. st, _, err := u.cacheStatus(oldname)
  129. if err != nil {
  130. return err
  131. }
  132. switch st {
  133. case cacheLocal:
  134. case cacheHit:
  135. err = u.base.Rename(oldname, newname)
  136. case cacheStale, cacheMiss:
  137. if err := u.copyToLayer(oldname); err != nil {
  138. return err
  139. }
  140. err = u.base.Rename(oldname, newname)
  141. }
  142. if err != nil {
  143. return err
  144. }
  145. return u.layer.Rename(oldname, newname)
  146. }
  147. func (u *CacheOnReadFs) Remove(name string) error {
  148. st, _, err := u.cacheStatus(name)
  149. if err != nil {
  150. return err
  151. }
  152. switch st {
  153. case cacheLocal:
  154. case cacheHit, cacheStale, cacheMiss:
  155. err = u.base.Remove(name)
  156. }
  157. if err != nil {
  158. return err
  159. }
  160. return u.layer.Remove(name)
  161. }
  162. func (u *CacheOnReadFs) RemoveAll(name string) error {
  163. st, _, err := u.cacheStatus(name)
  164. if err != nil {
  165. return err
  166. }
  167. switch st {
  168. case cacheLocal:
  169. case cacheHit, cacheStale, cacheMiss:
  170. err = u.base.RemoveAll(name)
  171. }
  172. if err != nil {
  173. return err
  174. }
  175. return u.layer.RemoveAll(name)
  176. }
  177. func (u *CacheOnReadFs) OpenFile(name string, flag int, perm os.FileMode) (File, error) {
  178. st, _, err := u.cacheStatus(name)
  179. if err != nil {
  180. return nil, err
  181. }
  182. switch st {
  183. case cacheLocal, cacheHit:
  184. default:
  185. if err := u.copyToLayer(name); err != nil {
  186. return nil, err
  187. }
  188. }
  189. if flag&(os.O_WRONLY|syscall.O_RDWR|os.O_APPEND|os.O_CREATE|os.O_TRUNC) != 0 {
  190. bfi, err := u.base.OpenFile(name, flag, perm)
  191. if err != nil {
  192. return nil, err
  193. }
  194. lfi, err := u.layer.OpenFile(name, flag, perm)
  195. if err != nil {
  196. bfi.Close() // oops, what if O_TRUNC was set and file opening in the layer failed...?
  197. return nil, err
  198. }
  199. return &UnionFile{base: bfi, layer: lfi}, nil
  200. }
  201. return u.layer.OpenFile(name, flag, perm)
  202. }
  203. func (u *CacheOnReadFs) Open(name string) (File, error) {
  204. st, fi, err := u.cacheStatus(name)
  205. if err != nil {
  206. return nil, err
  207. }
  208. switch st {
  209. case cacheLocal:
  210. return u.layer.Open(name)
  211. case cacheMiss:
  212. bfi, err := u.base.Stat(name)
  213. if err != nil {
  214. return nil, err
  215. }
  216. if bfi.IsDir() {
  217. return u.base.Open(name)
  218. }
  219. if err := u.copyToLayer(name); err != nil {
  220. return nil, err
  221. }
  222. return u.layer.Open(name)
  223. case cacheStale:
  224. if !fi.IsDir() {
  225. if err := u.copyToLayer(name); err != nil {
  226. return nil, err
  227. }
  228. return u.layer.Open(name)
  229. }
  230. case cacheHit:
  231. if !fi.IsDir() {
  232. return u.layer.Open(name)
  233. }
  234. }
  235. // the dirs from cacheHit, cacheStale fall down here:
  236. bfile, _ := u.base.Open(name)
  237. lfile, err := u.layer.Open(name)
  238. if err != nil && bfile == nil {
  239. return nil, err
  240. }
  241. return &UnionFile{base: bfile, layer: lfile}, nil
  242. }
  243. func (u *CacheOnReadFs) Mkdir(name string, perm os.FileMode) error {
  244. err := u.base.Mkdir(name, perm)
  245. if err != nil {
  246. return err
  247. }
  248. return u.layer.MkdirAll(name, perm) // yes, MkdirAll... we cannot assume it exists in the cache
  249. }
  250. func (u *CacheOnReadFs) Name() string {
  251. return "CacheOnReadFs"
  252. }
  253. func (u *CacheOnReadFs) MkdirAll(name string, perm os.FileMode) error {
  254. err := u.base.MkdirAll(name, perm)
  255. if err != nil {
  256. return err
  257. }
  258. return u.layer.MkdirAll(name, perm)
  259. }
  260. func (u *CacheOnReadFs) Create(name string) (File, error) {
  261. bfh, err := u.base.Create(name)
  262. if err != nil {
  263. return nil, err
  264. }
  265. lfh, err := u.layer.Create(name)
  266. if err != nil {
  267. // oops, see comment about OS_TRUNC above, should we remove? then we have to
  268. // remember if the file did not exist before
  269. bfh.Close()
  270. return nil, err
  271. }
  272. return &UnionFile{base: bfh, layer: lfh}, nil
  273. }