flush_fs.go 6.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338
  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 flushfs
  15. import (
  16. "fmt"
  17. "os"
  18. "sync"
  19. "golang.org/x/net/context"
  20. "github.com/jacobsa/fuse"
  21. "github.com/jacobsa/fuse/fuseops"
  22. "github.com/jacobsa/fuse/fuseutil"
  23. )
  24. // Create a file system whose sole contents are a file named "foo" and a
  25. // directory named "bar".
  26. //
  27. // The file may be opened for reading and/or writing. Its initial contents are
  28. // empty. Whenever a flush or fsync is received, the supplied function will be
  29. // called with the current contents of the file and its status returned.
  30. //
  31. // The directory cannot be modified.
  32. func NewFileSystem(
  33. reportFlush func(string) error,
  34. reportFsync func(string) error) (server fuse.Server, err error) {
  35. fs := &flushFS{
  36. reportFlush: reportFlush,
  37. reportFsync: reportFsync,
  38. }
  39. server = fuseutil.NewFileSystemServer(fs)
  40. return
  41. }
  42. const (
  43. fooID = fuseops.RootInodeID + 1 + iota
  44. barID
  45. )
  46. type flushFS struct {
  47. fuseutil.NotImplementedFileSystem
  48. reportFlush func(string) error
  49. reportFsync func(string) error
  50. mu sync.Mutex
  51. fooContents []byte // GUARDED_BY(mu)
  52. }
  53. ////////////////////////////////////////////////////////////////////////
  54. // Helpers
  55. ////////////////////////////////////////////////////////////////////////
  56. // LOCKS_REQUIRED(fs.mu)
  57. func (fs *flushFS) rootAttributes() fuseops.InodeAttributes {
  58. return fuseops.InodeAttributes{
  59. Nlink: 1,
  60. Mode: 0777 | os.ModeDir,
  61. }
  62. }
  63. // LOCKS_REQUIRED(fs.mu)
  64. func (fs *flushFS) fooAttributes() fuseops.InodeAttributes {
  65. return fuseops.InodeAttributes{
  66. Nlink: 1,
  67. Mode: 0777,
  68. Size: uint64(len(fs.fooContents)),
  69. }
  70. }
  71. // LOCKS_REQUIRED(fs.mu)
  72. func (fs *flushFS) barAttributes() fuseops.InodeAttributes {
  73. return fuseops.InodeAttributes{
  74. Nlink: 1,
  75. Mode: 0777 | os.ModeDir,
  76. }
  77. }
  78. // LOCKS_REQUIRED(fs.mu)
  79. func (fs *flushFS) getAttributes(id fuseops.InodeID) (
  80. attrs fuseops.InodeAttributes,
  81. err error) {
  82. switch id {
  83. case fuseops.RootInodeID:
  84. attrs = fs.rootAttributes()
  85. return
  86. case fooID:
  87. attrs = fs.fooAttributes()
  88. return
  89. case barID:
  90. attrs = fs.barAttributes()
  91. return
  92. default:
  93. err = fuse.ENOENT
  94. return
  95. }
  96. }
  97. ////////////////////////////////////////////////////////////////////////
  98. // FileSystem methods
  99. ////////////////////////////////////////////////////////////////////////
  100. func (fs *flushFS) StatFS(
  101. ctx context.Context,
  102. op *fuseops.StatFSOp) (err error) {
  103. return
  104. }
  105. func (fs *flushFS) LookUpInode(
  106. ctx context.Context,
  107. op *fuseops.LookUpInodeOp) (err error) {
  108. fs.mu.Lock()
  109. defer fs.mu.Unlock()
  110. // Sanity check.
  111. if op.Parent != fuseops.RootInodeID {
  112. err = fuse.ENOENT
  113. return
  114. }
  115. // Set up the entry.
  116. switch op.Name {
  117. case "foo":
  118. op.Entry = fuseops.ChildInodeEntry{
  119. Child: fooID,
  120. Attributes: fs.fooAttributes(),
  121. }
  122. case "bar":
  123. op.Entry = fuseops.ChildInodeEntry{
  124. Child: barID,
  125. Attributes: fs.barAttributes(),
  126. }
  127. default:
  128. err = fuse.ENOENT
  129. return
  130. }
  131. return
  132. }
  133. func (fs *flushFS) GetInodeAttributes(
  134. ctx context.Context,
  135. op *fuseops.GetInodeAttributesOp) (err error) {
  136. fs.mu.Lock()
  137. defer fs.mu.Unlock()
  138. op.Attributes, err = fs.getAttributes(op.Inode)
  139. return
  140. }
  141. func (fs *flushFS) SetInodeAttributes(
  142. ctx context.Context,
  143. op *fuseops.SetInodeAttributesOp) (err error) {
  144. fs.mu.Lock()
  145. defer fs.mu.Unlock()
  146. // Ignore any changes and simply return existing attributes.
  147. op.Attributes, err = fs.getAttributes(op.Inode)
  148. return
  149. }
  150. func (fs *flushFS) OpenFile(
  151. ctx context.Context,
  152. op *fuseops.OpenFileOp) (err error) {
  153. fs.mu.Lock()
  154. defer fs.mu.Unlock()
  155. // Sanity check.
  156. if op.Inode != fooID {
  157. err = fuse.ENOSYS
  158. return
  159. }
  160. return
  161. }
  162. func (fs *flushFS) ReadFile(
  163. ctx context.Context,
  164. op *fuseops.ReadFileOp) (err error) {
  165. fs.mu.Lock()
  166. defer fs.mu.Unlock()
  167. // Ensure the offset is in range.
  168. if op.Offset > int64(len(fs.fooContents)) {
  169. return
  170. }
  171. // Read what we can.
  172. op.BytesRead = copy(op.Dst, fs.fooContents[op.Offset:])
  173. return
  174. }
  175. func (fs *flushFS) WriteFile(
  176. ctx context.Context,
  177. op *fuseops.WriteFileOp) (err error) {
  178. fs.mu.Lock()
  179. defer fs.mu.Unlock()
  180. // Ensure that the contents slice is long enough.
  181. newLen := int(op.Offset) + len(op.Data)
  182. if len(fs.fooContents) < newLen {
  183. padding := make([]byte, newLen-len(fs.fooContents))
  184. fs.fooContents = append(fs.fooContents, padding...)
  185. }
  186. // Copy in the data.
  187. n := copy(fs.fooContents[op.Offset:], op.Data)
  188. // Sanity check.
  189. if n != len(op.Data) {
  190. panic(fmt.Sprintf("Unexpected short copy: %v", n))
  191. }
  192. return
  193. }
  194. func (fs *flushFS) SyncFile(
  195. ctx context.Context,
  196. op *fuseops.SyncFileOp) (err error) {
  197. fs.mu.Lock()
  198. defer fs.mu.Unlock()
  199. err = fs.reportFsync(string(fs.fooContents))
  200. return
  201. }
  202. func (fs *flushFS) FlushFile(
  203. ctx context.Context,
  204. op *fuseops.FlushFileOp) (err error) {
  205. fs.mu.Lock()
  206. defer fs.mu.Unlock()
  207. err = fs.reportFlush(string(fs.fooContents))
  208. return
  209. }
  210. func (fs *flushFS) OpenDir(
  211. ctx context.Context,
  212. op *fuseops.OpenDirOp) (err error) {
  213. fs.mu.Lock()
  214. defer fs.mu.Unlock()
  215. // Sanity check.
  216. switch op.Inode {
  217. case fuseops.RootInodeID:
  218. case barID:
  219. default:
  220. err = fuse.ENOENT
  221. return
  222. }
  223. return
  224. }
  225. func (fs *flushFS) ReadDir(
  226. ctx context.Context,
  227. op *fuseops.ReadDirOp) (err error) {
  228. fs.mu.Lock()
  229. defer fs.mu.Unlock()
  230. // Create the appropriate listing.
  231. var dirents []fuseutil.Dirent
  232. switch op.Inode {
  233. case fuseops.RootInodeID:
  234. dirents = []fuseutil.Dirent{
  235. fuseutil.Dirent{
  236. Offset: 1,
  237. Inode: fooID,
  238. Name: "foo",
  239. Type: fuseutil.DT_File,
  240. },
  241. fuseutil.Dirent{
  242. Offset: 2,
  243. Inode: barID,
  244. Name: "bar",
  245. Type: fuseutil.DT_Directory,
  246. },
  247. }
  248. case barID:
  249. default:
  250. err = fmt.Errorf("Unexpected inode: %v", op.Inode)
  251. return
  252. }
  253. // If the offset is for the end of the listing, we're done. Otherwise we
  254. // expect it to be for the start.
  255. switch op.Offset {
  256. case fuseops.DirOffset(len(dirents)):
  257. return
  258. case 0:
  259. default:
  260. err = fmt.Errorf("Unexpected offset: %v", op.Offset)
  261. return
  262. }
  263. // Fill in the listing.
  264. for _, de := range dirents {
  265. n := fuseutil.WriteDirent(op.Dst[op.BytesRead:], de)
  266. // We don't support doing this in anything more than one shot.
  267. if n == 0 {
  268. err = fmt.Errorf("Couldn't fit listing in %v bytes", len(op.Dst))
  269. return
  270. }
  271. op.BytesRead += n
  272. }
  273. return
  274. }