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