hello_fs.go 5.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269
  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 hellofs
  15. import (
  16. "io"
  17. "os"
  18. "strings"
  19. "golang.org/x/net/context"
  20. "github.com/jacobsa/fuse"
  21. "github.com/jacobsa/fuse/fuseops"
  22. "github.com/jacobsa/fuse/fuseutil"
  23. "github.com/jacobsa/timeutil"
  24. )
  25. // Create a file system with a fixed structure that looks like this:
  26. //
  27. // hello
  28. // dir/
  29. // world
  30. //
  31. // Each file contains the string "Hello, world!".
  32. func NewHelloFS(clock timeutil.Clock) (server fuse.Server, err error) {
  33. fs := &helloFS{
  34. Clock: clock,
  35. }
  36. server = fuseutil.NewFileSystemServer(fs)
  37. return
  38. }
  39. type helloFS struct {
  40. fuseutil.NotImplementedFileSystem
  41. Clock timeutil.Clock
  42. }
  43. const (
  44. rootInode fuseops.InodeID = fuseops.RootInodeID + iota
  45. helloInode
  46. dirInode
  47. worldInode
  48. )
  49. type inodeInfo struct {
  50. attributes fuseops.InodeAttributes
  51. // File or directory?
  52. dir bool
  53. // For directories, children.
  54. children []fuseutil.Dirent
  55. }
  56. // We have a fixed directory structure.
  57. var gInodeInfo = map[fuseops.InodeID]inodeInfo{
  58. // root
  59. rootInode: inodeInfo{
  60. attributes: fuseops.InodeAttributes{
  61. Nlink: 1,
  62. Mode: 0555 | os.ModeDir,
  63. },
  64. dir: true,
  65. children: []fuseutil.Dirent{
  66. fuseutil.Dirent{
  67. Offset: 1,
  68. Inode: helloInode,
  69. Name: "hello",
  70. Type: fuseutil.DT_File,
  71. },
  72. fuseutil.Dirent{
  73. Offset: 2,
  74. Inode: dirInode,
  75. Name: "dir",
  76. Type: fuseutil.DT_Directory,
  77. },
  78. },
  79. },
  80. // hello
  81. helloInode: inodeInfo{
  82. attributes: fuseops.InodeAttributes{
  83. Nlink: 1,
  84. Mode: 0444,
  85. Size: uint64(len("Hello, world!")),
  86. },
  87. },
  88. // dir
  89. dirInode: inodeInfo{
  90. attributes: fuseops.InodeAttributes{
  91. Nlink: 1,
  92. Mode: 0555 | os.ModeDir,
  93. },
  94. dir: true,
  95. children: []fuseutil.Dirent{
  96. fuseutil.Dirent{
  97. Offset: 1,
  98. Inode: worldInode,
  99. Name: "world",
  100. Type: fuseutil.DT_File,
  101. },
  102. },
  103. },
  104. // world
  105. worldInode: inodeInfo{
  106. attributes: fuseops.InodeAttributes{
  107. Nlink: 1,
  108. Mode: 0444,
  109. Size: uint64(len("Hello, world!")),
  110. },
  111. },
  112. }
  113. func findChildInode(
  114. name string,
  115. children []fuseutil.Dirent) (inode fuseops.InodeID, err error) {
  116. for _, e := range children {
  117. if e.Name == name {
  118. inode = e.Inode
  119. return
  120. }
  121. }
  122. err = fuse.ENOENT
  123. return
  124. }
  125. func (fs *helloFS) patchAttributes(
  126. attr *fuseops.InodeAttributes) {
  127. now := fs.Clock.Now()
  128. attr.Atime = now
  129. attr.Mtime = now
  130. attr.Crtime = now
  131. }
  132. func (fs *helloFS) StatFS(
  133. ctx context.Context,
  134. op *fuseops.StatFSOp) (err error) {
  135. return
  136. }
  137. func (fs *helloFS) LookUpInode(
  138. ctx context.Context,
  139. op *fuseops.LookUpInodeOp) (err error) {
  140. // Find the info for the parent.
  141. parentInfo, ok := gInodeInfo[op.Parent]
  142. if !ok {
  143. err = fuse.ENOENT
  144. return
  145. }
  146. // Find the child within the parent.
  147. childInode, err := findChildInode(op.Name, parentInfo.children)
  148. if err != nil {
  149. return
  150. }
  151. // Copy over information.
  152. op.Entry.Child = childInode
  153. op.Entry.Attributes = gInodeInfo[childInode].attributes
  154. // Patch attributes.
  155. fs.patchAttributes(&op.Entry.Attributes)
  156. return
  157. }
  158. func (fs *helloFS) GetInodeAttributes(
  159. ctx context.Context,
  160. op *fuseops.GetInodeAttributesOp) (err error) {
  161. // Find the info for this inode.
  162. info, ok := gInodeInfo[op.Inode]
  163. if !ok {
  164. err = fuse.ENOENT
  165. return
  166. }
  167. // Copy over its attributes.
  168. op.Attributes = info.attributes
  169. // Patch attributes.
  170. fs.patchAttributes(&op.Attributes)
  171. return
  172. }
  173. func (fs *helloFS) OpenDir(
  174. ctx context.Context,
  175. op *fuseops.OpenDirOp) (err error) {
  176. // Allow opening any directory.
  177. return
  178. }
  179. func (fs *helloFS) ReadDir(
  180. ctx context.Context,
  181. op *fuseops.ReadDirOp) (err error) {
  182. // Find the info for this inode.
  183. info, ok := gInodeInfo[op.Inode]
  184. if !ok {
  185. err = fuse.ENOENT
  186. return
  187. }
  188. if !info.dir {
  189. err = fuse.EIO
  190. return
  191. }
  192. entries := info.children
  193. // Grab the range of interest.
  194. if op.Offset > fuseops.DirOffset(len(entries)) {
  195. err = fuse.EIO
  196. return
  197. }
  198. entries = entries[op.Offset:]
  199. // Resume at the specified offset into the array.
  200. for _, e := range entries {
  201. n := fuseutil.WriteDirent(op.Dst[op.BytesRead:], e)
  202. if n == 0 {
  203. break
  204. }
  205. op.BytesRead += n
  206. }
  207. return
  208. }
  209. func (fs *helloFS) OpenFile(
  210. ctx context.Context,
  211. op *fuseops.OpenFileOp) (err error) {
  212. // Allow opening any file.
  213. return
  214. }
  215. func (fs *helloFS) ReadFile(
  216. ctx context.Context,
  217. op *fuseops.ReadFileOp) (err error) {
  218. // Let io.ReaderAt deal with the semantics.
  219. reader := strings.NewReader("Hello, world!")
  220. op.BytesRead, err = reader.ReadAt(op.Dst, op.Offset)
  221. // Special case: FUSE doesn't expect us to return io.EOF.
  222. if err == io.EOF {
  223. err = nil
  224. }
  225. return
  226. }