inode.go 8.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384
  1. // Copyright 2015 Google Inc. All Rights Reserved.
  2. //
  3. // Licensed under the Apache License, Version 2.0 (the "License");
  4. // you may not use this file except in compliance with the License.
  5. // You may obtain a copy of the License at
  6. //
  7. // http://www.apache.org/licenses/LICENSE-2.0
  8. //
  9. // Unless required by applicable law or agreed to in writing, software
  10. // distributed under the License is distributed on an "AS IS" BASIS,
  11. // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  12. // See the License for the specific language governing permissions and
  13. // limitations under the License.
  14. package memfs
  15. import (
  16. "fmt"
  17. "io"
  18. "os"
  19. "time"
  20. "github.com/jacobsa/fuse/fuseops"
  21. "github.com/jacobsa/fuse/fuseutil"
  22. )
  23. // Common attributes for files and directories.
  24. //
  25. // External synchronization is required.
  26. type inode struct {
  27. /////////////////////////
  28. // Mutable state
  29. /////////////////////////
  30. // The current attributes of this inode.
  31. //
  32. // INVARIANT: attrs.Mode &^ (os.ModePerm|os.ModeDir|os.ModeSymlink) == 0
  33. // INVARIANT: !(isDir() && isSymlink())
  34. // INVARIANT: attrs.Size == len(contents)
  35. attrs fuseops.InodeAttributes
  36. // For directories, entries describing the children of the directory. Unused
  37. // entries are of type DT_Unknown.
  38. //
  39. // This array can never be shortened, nor can its elements be moved, because
  40. // we use its indices for Dirent.Offset, which is exposed to the user who
  41. // might be calling readdir in a loop while concurrently modifying the
  42. // directory. Unused entries can, however, be reused.
  43. //
  44. // INVARIANT: If !isDir(), len(entries) == 0
  45. // INVARIANT: For each i, entries[i].Offset == i+1
  46. // INVARIANT: Contains no duplicate names in used entries.
  47. entries []fuseutil.Dirent
  48. // For files, the current contents of the file.
  49. //
  50. // INVARIANT: If !isFile(), len(contents) == 0
  51. contents []byte
  52. // For symlinks, the target of the symlink.
  53. //
  54. // INVARIANT: If !isSymlink(), len(target) == 0
  55. target string
  56. // extended attributes and values
  57. xattrs map[string][]byte
  58. }
  59. ////////////////////////////////////////////////////////////////////////
  60. // Helpers
  61. ////////////////////////////////////////////////////////////////////////
  62. // Create a new inode with the supplied attributes, which need not contain
  63. // time-related information (the inode object will take care of that).
  64. func newInode(
  65. attrs fuseops.InodeAttributes) (in *inode) {
  66. // Update time info.
  67. now := time.Now()
  68. attrs.Mtime = now
  69. attrs.Crtime = now
  70. // Create the object.
  71. in = &inode{
  72. attrs: attrs,
  73. xattrs: make(map[string][]byte),
  74. }
  75. return
  76. }
  77. func (in *inode) CheckInvariants() {
  78. // INVARIANT: attrs.Mode &^ (os.ModePerm|os.ModeDir|os.ModeSymlink) == 0
  79. if !(in.attrs.Mode&^(os.ModePerm|os.ModeDir|os.ModeSymlink) == 0) {
  80. panic(fmt.Sprintf("Unexpected mode: %v", in.attrs.Mode))
  81. }
  82. // INVARIANT: !(isDir() && isSymlink())
  83. if in.isDir() && in.isSymlink() {
  84. panic(fmt.Sprintf("Unexpected mode: %v", in.attrs.Mode))
  85. }
  86. // INVARIANT: attrs.Size == len(contents)
  87. if in.attrs.Size != uint64(len(in.contents)) {
  88. panic(fmt.Sprintf(
  89. "Size mismatch: %d vs. %d",
  90. in.attrs.Size,
  91. len(in.contents)))
  92. }
  93. // INVARIANT: If !isDir(), len(entries) == 0
  94. if !in.isDir() && len(in.entries) != 0 {
  95. panic(fmt.Sprintf("Unexpected entries length: %d", len(in.entries)))
  96. }
  97. // INVARIANT: For each i, entries[i].Offset == i+1
  98. for i, e := range in.entries {
  99. if !(e.Offset == fuseops.DirOffset(i+1)) {
  100. panic(fmt.Sprintf("Unexpected offset for index %d: %d", i, e.Offset))
  101. }
  102. }
  103. // INVARIANT: Contains no duplicate names in used entries.
  104. childNames := make(map[string]struct{})
  105. for _, e := range in.entries {
  106. if e.Type != fuseutil.DT_Unknown {
  107. if _, ok := childNames[e.Name]; ok {
  108. panic(fmt.Sprintf("Duplicate name: %s", e.Name))
  109. }
  110. childNames[e.Name] = struct{}{}
  111. }
  112. }
  113. // INVARIANT: If !isFile(), len(contents) == 0
  114. if !in.isFile() && len(in.contents) != 0 {
  115. panic(fmt.Sprintf("Unexpected length: %d", len(in.contents)))
  116. }
  117. // INVARIANT: If !isSymlink(), len(target) == 0
  118. if !in.isSymlink() && len(in.target) != 0 {
  119. panic(fmt.Sprintf("Unexpected target length: %d", len(in.target)))
  120. }
  121. return
  122. }
  123. func (in *inode) isDir() bool {
  124. return in.attrs.Mode&os.ModeDir != 0
  125. }
  126. func (in *inode) isSymlink() bool {
  127. return in.attrs.Mode&os.ModeSymlink != 0
  128. }
  129. func (in *inode) isFile() bool {
  130. return !(in.isDir() || in.isSymlink())
  131. }
  132. // Return the index of the child within in.entries, if it exists.
  133. //
  134. // REQUIRES: in.isDir()
  135. func (in *inode) findChild(name string) (i int, ok bool) {
  136. if !in.isDir() {
  137. panic("findChild called on non-directory.")
  138. }
  139. var e fuseutil.Dirent
  140. for i, e = range in.entries {
  141. if e.Name == name {
  142. ok = true
  143. return
  144. }
  145. }
  146. return
  147. }
  148. ////////////////////////////////////////////////////////////////////////
  149. // Public methods
  150. ////////////////////////////////////////////////////////////////////////
  151. // Return the number of children of the directory.
  152. //
  153. // REQUIRES: in.isDir()
  154. func (in *inode) Len() (n int) {
  155. for _, e := range in.entries {
  156. if e.Type != fuseutil.DT_Unknown {
  157. n++
  158. }
  159. }
  160. return
  161. }
  162. // Find an entry for the given child name and return its inode ID.
  163. //
  164. // REQUIRES: in.isDir()
  165. func (in *inode) LookUpChild(name string) (
  166. id fuseops.InodeID,
  167. typ fuseutil.DirentType,
  168. ok bool) {
  169. index, ok := in.findChild(name)
  170. if ok {
  171. id = in.entries[index].Inode
  172. typ = in.entries[index].Type
  173. }
  174. return
  175. }
  176. // Add an entry for a child.
  177. //
  178. // REQUIRES: in.isDir()
  179. // REQUIRES: dt != fuseutil.DT_Unknown
  180. func (in *inode) AddChild(
  181. id fuseops.InodeID,
  182. name string,
  183. dt fuseutil.DirentType) {
  184. var index int
  185. // Update the modification time.
  186. in.attrs.Mtime = time.Now()
  187. // No matter where we place the entry, make sure it has the correct Offset
  188. // field.
  189. defer func() {
  190. in.entries[index].Offset = fuseops.DirOffset(index + 1)
  191. }()
  192. // Set up the entry.
  193. e := fuseutil.Dirent{
  194. Inode: id,
  195. Name: name,
  196. Type: dt,
  197. }
  198. // Look for a gap in which we can insert it.
  199. for index = range in.entries {
  200. if in.entries[index].Type == fuseutil.DT_Unknown {
  201. in.entries[index] = e
  202. return
  203. }
  204. }
  205. // Append it to the end.
  206. index = len(in.entries)
  207. in.entries = append(in.entries, e)
  208. }
  209. // Remove an entry for a child.
  210. //
  211. // REQUIRES: in.isDir()
  212. // REQUIRES: An entry for the given name exists.
  213. func (in *inode) RemoveChild(name string) {
  214. // Update the modification time.
  215. in.attrs.Mtime = time.Now()
  216. // Find the entry.
  217. i, ok := in.findChild(name)
  218. if !ok {
  219. panic(fmt.Sprintf("Unknown child: %s", name))
  220. }
  221. // Mark it as unused.
  222. in.entries[i] = fuseutil.Dirent{
  223. Type: fuseutil.DT_Unknown,
  224. Offset: fuseops.DirOffset(i + 1),
  225. }
  226. }
  227. // Serve a ReadDir request.
  228. //
  229. // REQUIRES: in.isDir()
  230. func (in *inode) ReadDir(p []byte, offset int) (n int) {
  231. if !in.isDir() {
  232. panic("ReadDir called on non-directory.")
  233. }
  234. for i := offset; i < len(in.entries); i++ {
  235. e := in.entries[i]
  236. // Skip unused entries.
  237. if e.Type == fuseutil.DT_Unknown {
  238. continue
  239. }
  240. tmp := fuseutil.WriteDirent(p[n:], in.entries[i])
  241. if tmp == 0 {
  242. break
  243. }
  244. n += tmp
  245. }
  246. return
  247. }
  248. // Read from the file's contents. See documentation for ioutil.ReaderAt.
  249. //
  250. // REQUIRES: in.isFile()
  251. func (in *inode) ReadAt(p []byte, off int64) (n int, err error) {
  252. if !in.isFile() {
  253. panic("ReadAt called on non-file.")
  254. }
  255. // Ensure the offset is in range.
  256. if off > int64(len(in.contents)) {
  257. err = io.EOF
  258. return
  259. }
  260. // Read what we can.
  261. n = copy(p, in.contents[off:])
  262. if n < len(p) {
  263. err = io.EOF
  264. }
  265. return
  266. }
  267. // Write to the file's contents. See documentation for ioutil.WriterAt.
  268. //
  269. // REQUIRES: in.isFile()
  270. func (in *inode) WriteAt(p []byte, off int64) (n int, err error) {
  271. if !in.isFile() {
  272. panic("WriteAt called on non-file.")
  273. }
  274. // Update the modification time.
  275. in.attrs.Mtime = time.Now()
  276. // Ensure that the contents slice is long enough.
  277. newLen := int(off) + len(p)
  278. if len(in.contents) < newLen {
  279. padding := make([]byte, newLen-len(in.contents))
  280. in.contents = append(in.contents, padding...)
  281. in.attrs.Size = uint64(newLen)
  282. }
  283. // Copy in the data.
  284. n = copy(in.contents[off:], p)
  285. // Sanity check.
  286. if n != len(p) {
  287. panic(fmt.Sprintf("Unexpected short copy: %v", n))
  288. }
  289. return
  290. }
  291. // Update attributes from non-nil parameters.
  292. func (in *inode) SetAttributes(
  293. size *uint64,
  294. mode *os.FileMode,
  295. mtime *time.Time) {
  296. // Update the modification time.
  297. in.attrs.Mtime = time.Now()
  298. // Truncate?
  299. if size != nil {
  300. intSize := int(*size)
  301. // Update contents.
  302. if intSize <= len(in.contents) {
  303. in.contents = in.contents[:intSize]
  304. } else {
  305. padding := make([]byte, intSize-len(in.contents))
  306. in.contents = append(in.contents, padding...)
  307. }
  308. // Update attributes.
  309. in.attrs.Size = *size
  310. }
  311. // Change mode?
  312. if mode != nil {
  313. in.attrs.Mode = *mode
  314. }
  315. // Change mtime?
  316. if mtime != nil {
  317. in.attrs.Mtime = *mtime
  318. }
  319. }