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