github.com/hanwen/go-fuse@v1.0.0/fuse/pathfs/pathfs.go (about) 1 // Copyright 2016 the Go-FUSE Authors. All rights reserved. 2 // Use of this source code is governed by a BSD-style 3 // license that can be found in the LICENSE file. 4 5 package pathfs 6 7 import ( 8 "fmt" 9 "log" 10 "path/filepath" 11 "strings" 12 "sync" 13 "time" 14 15 "github.com/hanwen/go-fuse/fuse" 16 "github.com/hanwen/go-fuse/fuse/nodefs" 17 ) 18 19 // refCountedInode is used in clientInodeMap. The reference count is used to decide 20 // if the entry in clientInodeMap can be dropped. 21 type refCountedInode struct { 22 node *pathInode 23 refCount int 24 } 25 26 // PathNodeFs is the file system that can translate an inode back to a 27 // path. The path name is then used to call into an object that has 28 // the FileSystem interface. 29 // 30 // Lookups (ie. FileSystem.GetAttr) may return a inode number in its 31 // return value. The inode number ("clientInode") is used to indicate 32 // linked files. 33 type PathNodeFs struct { 34 debug bool 35 fs FileSystem 36 root *pathInode 37 connector *nodefs.FileSystemConnector 38 39 // protects clientInodeMap 40 pathLock sync.RWMutex 41 42 // This map lists all the parent links known for a given inode number. 43 clientInodeMap map[uint64]*refCountedInode 44 45 options *PathNodeFsOptions 46 } 47 48 // SetDebug toggles debug information: it will log path names for 49 // each operation processed. 50 func (fs *PathNodeFs) SetDebug(dbg bool) { 51 fs.debug = dbg 52 } 53 54 // Mount mounts a another node filesystem with the given root on the 55 // path. The last component of the path should not exist yet. 56 func (fs *PathNodeFs) Mount(path string, root nodefs.Node, opts *nodefs.Options) fuse.Status { 57 dir, name := filepath.Split(path) 58 if dir != "" { 59 dir = filepath.Clean(dir) 60 } 61 parent := fs.LookupNode(dir) 62 if parent == nil { 63 return fuse.ENOENT 64 } 65 return fs.connector.Mount(parent, name, root, opts) 66 } 67 68 // ForgetClientInodes forgets all known information on client inodes. 69 func (fs *PathNodeFs) ForgetClientInodes() { 70 if !fs.options.ClientInodes { 71 return 72 } 73 fs.pathLock.Lock() 74 fs.clientInodeMap = map[uint64]*refCountedInode{} 75 fs.root.forgetClientInodes() 76 fs.pathLock.Unlock() 77 } 78 79 // Rereads all inode numbers for all known files. 80 func (fs *PathNodeFs) RereadClientInodes() { 81 if !fs.options.ClientInodes { 82 return 83 } 84 fs.ForgetClientInodes() 85 fs.root.updateClientInodes() 86 } 87 88 // UnmountNode unmounts the node filesystem with the given root. 89 func (fs *PathNodeFs) UnmountNode(node *nodefs.Inode) fuse.Status { 90 return fs.connector.Unmount(node) 91 } 92 93 // UnmountNode unmounts the node filesystem with the given root. 94 func (fs *PathNodeFs) Unmount(path string) fuse.Status { 95 node := fs.Node(path) 96 if node == nil { 97 return fuse.ENOENT 98 } 99 return fs.connector.Unmount(node) 100 } 101 102 // String returns a name for this file system 103 func (fs *PathNodeFs) String() string { 104 name := fs.fs.String() 105 if name == "defaultFileSystem" { 106 name = fmt.Sprintf("%T", fs.fs) 107 name = strings.TrimLeft(name, "*") 108 } 109 return name 110 } 111 112 // Connector returns the FileSystemConnector (the bridge to the raw 113 // protocol) for this PathNodeFs. 114 func (fs *PathNodeFs) Connector() *nodefs.FileSystemConnector { 115 return fs.connector 116 } 117 118 // Node looks up the Inode that corresponds to the given path name, or 119 // returns nil if not found. 120 func (fs *PathNodeFs) Node(name string) *nodefs.Inode { 121 n, rest := fs.LastNode(name) 122 if len(rest) > 0 { 123 return nil 124 } 125 return n 126 } 127 128 // Like Node, but use Lookup to discover inodes we may not have yet. 129 func (fs *PathNodeFs) LookupNode(name string) *nodefs.Inode { 130 return fs.connector.LookupNode(fs.Root().Inode(), name) 131 } 132 133 // Path constructs a path for the given Inode. If the file system 134 // implements hard links through client-inode numbers, the path may 135 // not be unique. 136 func (fs *PathNodeFs) Path(node *nodefs.Inode) string { 137 pNode := node.Node().(*pathInode) 138 return pNode.GetPath() 139 } 140 141 // LastNode finds the deepest inode known corresponding to a path. The 142 // unknown part of the filename is also returned. 143 func (fs *PathNodeFs) LastNode(name string) (*nodefs.Inode, []string) { 144 return fs.connector.Node(fs.Root().Inode(), name) 145 } 146 147 // FileNotify notifies that file contents were changed within the 148 // given range. Use negative offset for metadata-only invalidation, 149 // and zero-length for invalidating all content. 150 func (fs *PathNodeFs) FileNotify(path string, off int64, length int64) fuse.Status { 151 node, r := fs.connector.Node(fs.root.Inode(), path) 152 if len(r) > 0 { 153 return fuse.ENOENT 154 } 155 return fs.connector.FileNotify(node, off, length) 156 } 157 158 // EntryNotify makes the kernel forget the entry data from the given 159 // name from a directory. After this call, the kernel will issue a 160 // new lookup request for the given name when necessary. 161 func (fs *PathNodeFs) EntryNotify(dir string, name string) fuse.Status { 162 node, rest := fs.connector.Node(fs.root.Inode(), dir) 163 if len(rest) > 0 { 164 return fuse.ENOENT 165 } 166 return fs.connector.EntryNotify(node, name) 167 } 168 169 // Notify ensures that the path name is invalidates: if the inode is 170 // known, it issues an file content Notify, if not, an entry notify 171 // for the path is issued. The latter will clear out non-existence 172 // cache entries. 173 func (fs *PathNodeFs) Notify(path string) fuse.Status { 174 node, rest := fs.connector.Node(fs.root.Inode(), path) 175 if len(rest) > 0 { 176 return fs.connector.EntryNotify(node, rest[0]) 177 } 178 return fs.connector.FileNotify(node, 0, 0) 179 } 180 181 // AllFiles returns all open files for the inode corresponding with 182 // the given mask. 183 func (fs *PathNodeFs) AllFiles(name string, mask uint32) []nodefs.WithFlags { 184 n := fs.Node(name) 185 if n == nil { 186 return nil 187 } 188 return n.Files(mask) 189 } 190 191 // NewPathNodeFs returns a file system that translates from inodes to 192 // path names. 193 func NewPathNodeFs(fs FileSystem, opts *PathNodeFsOptions) *PathNodeFs { 194 root := &pathInode{} 195 root.fs = fs 196 197 if opts == nil { 198 opts = &PathNodeFsOptions{} 199 } 200 201 pfs := &PathNodeFs{ 202 fs: fs, 203 root: root, 204 clientInodeMap: map[uint64]*refCountedInode{}, 205 options: opts, 206 } 207 root.pathFs = pfs 208 return pfs 209 } 210 211 // Root returns the root node for the path filesystem. 212 func (fs *PathNodeFs) Root() nodefs.Node { 213 return fs.root 214 } 215 216 // This is a combination of dentry (entry in the file/directory and 217 // the inode). This structure is used to implement glue for FSes where 218 // there is a one-to-one mapping of paths and inodes. 219 type pathInode struct { 220 pathFs *PathNodeFs 221 fs FileSystem 222 223 // This is to correctly resolve hardlinks of the underlying 224 // real filesystem. 225 clientInode uint64 226 inode *nodefs.Inode 227 } 228 229 func (n *pathInode) OnMount(conn *nodefs.FileSystemConnector) { 230 n.pathFs.connector = conn 231 n.pathFs.fs.OnMount(n.pathFs) 232 } 233 234 func (n *pathInode) OnUnmount() { 235 } 236 237 // Drop all known client inodes. Must have the treeLock. 238 func (n *pathInode) forgetClientInodes() { 239 n.clientInode = 0 240 for _, ch := range n.Inode().FsChildren() { 241 ch.Node().(*pathInode).forgetClientInodes() 242 } 243 } 244 245 func (fs *pathInode) Deletable() bool { 246 return true 247 } 248 249 func (n *pathInode) Inode() *nodefs.Inode { 250 return n.inode 251 } 252 253 func (n *pathInode) SetInode(node *nodefs.Inode) { 254 n.inode = node 255 } 256 257 // Reread all client nodes below this node. Must run outside the treeLock. 258 func (n *pathInode) updateClientInodes() { 259 n.GetAttr(&fuse.Attr{}, nil, nil) 260 for _, ch := range n.Inode().FsChildren() { 261 ch.Node().(*pathInode).updateClientInodes() 262 } 263 } 264 265 // GetPath returns the path relative to the mount governing this 266 // inode. It returns nil for mount if the file was deleted or the 267 // filesystem unmounted. 268 func (n *pathInode) GetPath() string { 269 if n == n.pathFs.root { 270 return "" 271 } 272 273 pathLen := 1 274 275 // The simple solution is to collect names, and reverse join 276 // them, them, but since this is a hot path, we take some 277 // effort to avoid allocations. 278 279 walkUp := n.Inode() 280 281 // TODO - guess depth? 282 segments := make([]string, 0, 10) 283 for { 284 parent, name := walkUp.Parent() 285 if parent == nil { 286 break 287 } 288 segments = append(segments, name) 289 pathLen += len(name) + 1 290 walkUp = parent 291 } 292 pathLen-- 293 294 pathBytes := make([]byte, 0, pathLen) 295 for i := len(segments) - 1; i >= 0; i-- { 296 pathBytes = append(pathBytes, segments[i]...) 297 if i > 0 { 298 pathBytes = append(pathBytes, '/') 299 } 300 } 301 302 path := string(pathBytes) 303 if n.pathFs.debug { 304 log.Printf("Inode = %q (%s)", path, n.fs.String()) 305 } 306 307 if walkUp != n.pathFs.root.Inode() { 308 // This might happen if the node has been removed from 309 // the tree using unlink, but we are forced to run 310 // some file system operation, because the file is 311 // still opened. 312 313 return ".deleted." + n.inode.String() 314 } 315 316 return path 317 } 318 319 func (n *pathInode) OnAdd(parent *nodefs.Inode, name string) { 320 // TODO it would be logical to increment the clientInodeMap reference count 321 // here. However, as the inode number is loaded lazily, we cannot do it 322 // yet. 323 } 324 325 func (n *pathInode) rmChild(name string) *pathInode { 326 childInode := n.Inode().RmChild(name) 327 if childInode == nil { 328 return nil 329 } 330 return childInode.Node().(*pathInode) 331 } 332 333 func (n *pathInode) OnRemove(parent *nodefs.Inode, name string) { 334 if n.clientInode == 0 || !n.pathFs.options.ClientInodes || n.Inode().IsDir() { 335 return 336 } 337 338 n.pathFs.pathLock.Lock() 339 r := n.pathFs.clientInodeMap[n.clientInode] 340 if r != nil { 341 r.refCount-- 342 if r.refCount == 0 { 343 delete(n.pathFs.clientInodeMap, n.clientInode) 344 } 345 } 346 n.pathFs.pathLock.Unlock() 347 } 348 349 // setClientInode sets the inode number if has not been set yet. 350 // This function exists to allow lazy-loading of the inode number. 351 func (n *pathInode) setClientInode(ino uint64) { 352 n.pathFs.pathLock.Lock() 353 defer n.pathFs.pathLock.Unlock() 354 if ino == 0 || n.clientInode != 0 || !n.pathFs.options.ClientInodes || n.Inode().IsDir() { 355 return 356 } 357 n.clientInode = ino 358 n.pathFs.clientInodeMap[ino] = &refCountedInode{node: n, refCount: 1} 359 } 360 361 func (n *pathInode) OnForget() { 362 if n.clientInode == 0 || !n.pathFs.options.ClientInodes || n.Inode().IsDir() { 363 return 364 } 365 n.pathFs.pathLock.Lock() 366 delete(n.pathFs.clientInodeMap, n.clientInode) 367 n.pathFs.pathLock.Unlock() 368 } 369 370 //////////////////////////////////////////////////////////////// 371 // FS operations 372 373 func (n *pathInode) StatFs() *fuse.StatfsOut { 374 return n.fs.StatFs(n.GetPath()) 375 } 376 377 func (n *pathInode) Readlink(c *fuse.Context) ([]byte, fuse.Status) { 378 path := n.GetPath() 379 380 val, err := n.fs.Readlink(path, c) 381 return []byte(val), err 382 } 383 384 func (n *pathInode) Access(mode uint32, context *fuse.Context) (code fuse.Status) { 385 p := n.GetPath() 386 return n.fs.Access(p, mode, context) 387 } 388 389 func (n *pathInode) GetXAttr(attribute string, context *fuse.Context) (data []byte, code fuse.Status) { 390 return n.fs.GetXAttr(n.GetPath(), attribute, context) 391 } 392 393 func (n *pathInode) RemoveXAttr(attr string, context *fuse.Context) fuse.Status { 394 p := n.GetPath() 395 return n.fs.RemoveXAttr(p, attr, context) 396 } 397 398 func (n *pathInode) SetXAttr(attr string, data []byte, flags int, context *fuse.Context) fuse.Status { 399 return n.fs.SetXAttr(n.GetPath(), attr, data, flags, context) 400 } 401 402 func (n *pathInode) ListXAttr(context *fuse.Context) (attrs []string, code fuse.Status) { 403 return n.fs.ListXAttr(n.GetPath(), context) 404 } 405 406 func (n *pathInode) Flush(file nodefs.File, openFlags uint32, context *fuse.Context) (code fuse.Status) { 407 return file.Flush() 408 } 409 410 func (n *pathInode) OpenDir(context *fuse.Context) ([]fuse.DirEntry, fuse.Status) { 411 return n.fs.OpenDir(n.GetPath(), context) 412 } 413 414 func (n *pathInode) Mknod(name string, mode uint32, dev uint32, context *fuse.Context) (*nodefs.Inode, fuse.Status) { 415 fullPath := filepath.Join(n.GetPath(), name) 416 code := n.fs.Mknod(fullPath, mode, dev, context) 417 var child *nodefs.Inode 418 if code.Ok() { 419 pNode := n.createChild(name, false) 420 child = pNode.Inode() 421 } 422 return child, code 423 } 424 425 func (n *pathInode) Mkdir(name string, mode uint32, context *fuse.Context) (*nodefs.Inode, fuse.Status) { 426 fullPath := filepath.Join(n.GetPath(), name) 427 code := n.fs.Mkdir(fullPath, mode, context) 428 var child *nodefs.Inode 429 if code.Ok() { 430 pNode := n.createChild(name, true) 431 child = pNode.Inode() 432 } 433 return child, code 434 } 435 436 func (n *pathInode) Unlink(name string, context *fuse.Context) (code fuse.Status) { 437 code = n.fs.Unlink(filepath.Join(n.GetPath(), name), context) 438 if code.Ok() { 439 n.Inode().RmChild(name) 440 } 441 return code 442 } 443 444 func (n *pathInode) Rmdir(name string, context *fuse.Context) (code fuse.Status) { 445 code = n.fs.Rmdir(filepath.Join(n.GetPath(), name), context) 446 if code.Ok() { 447 n.Inode().RmChild(name) 448 } 449 return code 450 } 451 452 func (n *pathInode) Symlink(name string, content string, context *fuse.Context) (*nodefs.Inode, fuse.Status) { 453 fullPath := filepath.Join(n.GetPath(), name) 454 code := n.fs.Symlink(content, fullPath, context) 455 var child *nodefs.Inode 456 if code.Ok() { 457 pNode := n.createChild(name, false) 458 child = pNode.Inode() 459 } 460 return child, code 461 } 462 463 func (n *pathInode) Rename(oldName string, newParent nodefs.Node, newName string, context *fuse.Context) (code fuse.Status) { 464 p := newParent.(*pathInode) 465 oldPath := filepath.Join(n.GetPath(), oldName) 466 newPath := filepath.Join(p.GetPath(), newName) 467 code = n.fs.Rename(oldPath, newPath, context) 468 if code.Ok() { 469 // The rename may have overwritten another file, remove it from the tree 470 p.Inode().RmChild(newName) 471 ch := n.Inode().RmChild(oldName) 472 if ch != nil { 473 // oldName may have been forgotten in the meantime. 474 p.Inode().AddChild(newName, ch) 475 } 476 } 477 return code 478 } 479 480 func (n *pathInode) Link(name string, existingFsnode nodefs.Node, context *fuse.Context) (*nodefs.Inode, fuse.Status) { 481 if !n.pathFs.options.ClientInodes { 482 return nil, fuse.ENOSYS 483 } 484 485 newPath := filepath.Join(n.GetPath(), name) 486 existing := existingFsnode.(*pathInode) 487 oldPath := existing.GetPath() 488 code := n.fs.Link(oldPath, newPath, context) 489 490 var a *fuse.Attr 491 if code.Ok() { 492 a, code = n.fs.GetAttr(newPath, context) 493 } 494 495 var child *nodefs.Inode 496 if code.Ok() { 497 if existing.clientInode != 0 && existing.clientInode == a.Ino { 498 child = existing.Inode() 499 n.Inode().AddChild(name, existing.Inode()) 500 } else { 501 pNode := n.createChild(name, false) 502 child = pNode.Inode() 503 pNode.clientInode = a.Ino 504 } 505 } 506 return child, code 507 } 508 509 func (n *pathInode) Create(name string, flags uint32, mode uint32, context *fuse.Context) (nodefs.File, *nodefs.Inode, fuse.Status) { 510 var child *nodefs.Inode 511 fullPath := filepath.Join(n.GetPath(), name) 512 file, code := n.fs.Create(fullPath, flags, mode, context) 513 if code.Ok() { 514 pNode := n.createChild(name, false) 515 child = pNode.Inode() 516 } 517 return file, child, code 518 } 519 520 func (n *pathInode) createChild(name string, isDir bool) *pathInode { 521 i := &pathInode{ 522 fs: n.fs, 523 pathFs: n.pathFs, 524 } 525 526 n.Inode().NewChild(name, isDir, i) 527 528 return i 529 } 530 531 func (n *pathInode) Open(flags uint32, context *fuse.Context) (file nodefs.File, code fuse.Status) { 532 p := n.GetPath() 533 file, code = n.fs.Open(p, flags, context) 534 if n.pathFs.debug { 535 file = &nodefs.WithFlags{ 536 File: file, 537 Description: n.GetPath(), 538 } 539 } 540 return 541 } 542 543 func (n *pathInode) Lookup(out *fuse.Attr, name string, context *fuse.Context) (*nodefs.Inode, fuse.Status) { 544 fullPath := filepath.Join(n.GetPath(), name) 545 fi, code := n.fs.GetAttr(fullPath, context) 546 node := n.Inode().GetChild(name) 547 if node != nil && (!code.Ok() || node.IsDir() != fi.IsDir()) { 548 n.Inode().RmChild(name) 549 node = nil 550 } 551 552 if code.Ok() { 553 if node == nil { 554 node = n.findChild(fi, name, fullPath).Inode() 555 } 556 *out = *fi 557 } 558 559 return node, code 560 } 561 562 func (n *pathInode) findChild(fi *fuse.Attr, name string, fullPath string) (out *pathInode) { 563 if fi.Ino > 0 { 564 n.pathFs.pathLock.RLock() 565 r := n.pathFs.clientInodeMap[fi.Ino] 566 if r != nil { 567 out = r.node 568 r.refCount++ 569 if fi.Nlink == 1 { 570 log.Printf("Found linked inode, but Nlink == 1, ino=%d, fullPath=%q", fi.Ino, fullPath) 571 } 572 } 573 n.pathFs.pathLock.RUnlock() 574 } 575 576 if out == nil { 577 out = n.createChild(name, fi.IsDir()) 578 out.setClientInode(fi.Ino) 579 } else { 580 n.Inode().AddChild(name, out.Inode()) 581 } 582 return out 583 } 584 585 func (n *pathInode) GetAttr(out *fuse.Attr, file nodefs.File, context *fuse.Context) (code fuse.Status) { 586 var fi *fuse.Attr 587 if file == nil { 588 // Linux currently (tested on v4.4) does not pass a file descriptor for 589 // fstat. To be able to stat a deleted file we have to find ourselves 590 // an open fd. 591 file = n.Inode().AnyFile() 592 } 593 // If we have found an open file, try to fstat it. 594 if file != nil { 595 code = file.GetAttr(out) 596 if code.Ok() { 597 return code 598 } 599 // ENOSYS and EBADF are retried below. Error out for other codes. 600 if code != fuse.ENOSYS && code != fuse.EBADF { 601 return code 602 } 603 } 604 // If we don't have an open file, or fstat on it failed due to an internal 605 // error, stat by path. 606 if file == nil || code == fuse.ENOSYS || code == fuse.EBADF { 607 fi, code = n.fs.GetAttr(n.GetPath(), context) 608 if !code.Ok() { 609 return code 610 } 611 // This is a bug in the filesystem implementation, but let's not 612 // crash. 613 if fi == nil { 614 log.Printf("Bug: fs.GetAttr returned OK with nil data") 615 return fuse.EINVAL 616 } 617 } 618 // Set inode number (unless already set or disabled). 619 n.setClientInode(fi.Ino) 620 // Help filesystems that forget to set Nlink. 621 if !fi.IsDir() && fi.Nlink == 0 { 622 fi.Nlink = 1 623 } 624 *out = *fi 625 return code 626 } 627 628 func (n *pathInode) Chmod(file nodefs.File, perms uint32, context *fuse.Context) (code fuse.Status) { 629 // Note that Linux currently (Linux 4.4) DOES NOT pass a file descriptor 630 // to FUSE for fchmod. We still check because that may change in the future. 631 if file != nil { 632 code = file.Chmod(perms) 633 if code != fuse.ENOSYS { 634 return code 635 } 636 } 637 638 files := n.Inode().Files(fuse.O_ANYWRITE) 639 for _, f := range files { 640 // TODO - pass context 641 code = f.Chmod(perms) 642 if code.Ok() { 643 return 644 } 645 } 646 647 if len(files) == 0 || code == fuse.ENOSYS || code == fuse.EBADF { 648 code = n.fs.Chmod(n.GetPath(), perms, context) 649 } 650 return code 651 } 652 653 func (n *pathInode) Chown(file nodefs.File, uid uint32, gid uint32, context *fuse.Context) (code fuse.Status) { 654 // Note that Linux currently (Linux 4.4) DOES NOT pass a file descriptor 655 // to FUSE for fchown. We still check because it may change in the future. 656 if file != nil { 657 code = file.Chown(uid, gid) 658 if code != fuse.ENOSYS { 659 return code 660 } 661 } 662 663 files := n.Inode().Files(fuse.O_ANYWRITE) 664 for _, f := range files { 665 // TODO - pass context 666 code = f.Chown(uid, gid) 667 if code.Ok() { 668 return code 669 } 670 } 671 if len(files) == 0 || code == fuse.ENOSYS || code == fuse.EBADF { 672 // TODO - can we get just FATTR_GID but not FATTR_UID ? 673 code = n.fs.Chown(n.GetPath(), uid, gid, context) 674 } 675 return code 676 } 677 678 func (n *pathInode) Truncate(file nodefs.File, size uint64, context *fuse.Context) (code fuse.Status) { 679 // A file descriptor was passed in AND the filesystem implements the 680 // operation on the file handle. This the common case for ftruncate. 681 if file != nil { 682 code = file.Truncate(size) 683 if code != fuse.ENOSYS { 684 return code 685 } 686 } 687 688 files := n.Inode().Files(fuse.O_ANYWRITE) 689 for _, f := range files { 690 // TODO - pass context 691 code = f.Truncate(size) 692 if code.Ok() { 693 return code 694 } 695 } 696 if len(files) == 0 || code == fuse.ENOSYS || code == fuse.EBADF { 697 code = n.fs.Truncate(n.GetPath(), size, context) 698 } 699 return code 700 } 701 702 func (n *pathInode) Utimens(file nodefs.File, atime *time.Time, mtime *time.Time, context *fuse.Context) (code fuse.Status) { 703 // Note that Linux currently (Linux 4.4) DOES NOT pass a file descriptor 704 // to FUSE for futimens. We still check because it may change in the future. 705 if file != nil { 706 code = file.Utimens(atime, mtime) 707 if code != fuse.ENOSYS { 708 return code 709 } 710 } 711 712 files := n.Inode().Files(fuse.O_ANYWRITE) 713 for _, f := range files { 714 // TODO - pass context 715 code = f.Utimens(atime, mtime) 716 if code.Ok() { 717 return code 718 } 719 } 720 if len(files) == 0 || code == fuse.ENOSYS || code == fuse.EBADF { 721 code = n.fs.Utimens(n.GetPath(), atime, mtime, context) 722 } 723 return code 724 } 725 726 func (n *pathInode) Fallocate(file nodefs.File, off uint64, size uint64, mode uint32, context *fuse.Context) (code fuse.Status) { 727 if file != nil { 728 code = file.Allocate(off, size, mode) 729 if code.Ok() { 730 return code 731 } 732 } 733 734 files := n.Inode().Files(fuse.O_ANYWRITE) 735 for _, f := range files { 736 // TODO - pass context 737 code = f.Allocate(off, size, mode) 738 if code.Ok() { 739 return code 740 } 741 } 742 743 return code 744 } 745 746 func (n *pathInode) Read(file nodefs.File, dest []byte, off int64, context *fuse.Context) (fuse.ReadResult, fuse.Status) { 747 if file != nil { 748 return file.Read(dest, off) 749 } 750 return nil, fuse.ENOSYS 751 } 752 753 func (n *pathInode) Write(file nodefs.File, data []byte, off int64, context *fuse.Context) (written uint32, code fuse.Status) { 754 if file != nil { 755 return file.Write(data, off) 756 } 757 return 0, fuse.ENOSYS 758 } 759 760 func (n *pathInode) GetLk(file nodefs.File, owner uint64, lk *fuse.FileLock, flags uint32, out *fuse.FileLock, context *fuse.Context) (code fuse.Status) { 761 if file != nil { 762 return file.GetLk(owner, lk, flags, out) 763 } 764 return fuse.ENOSYS 765 } 766 767 func (n *pathInode) SetLk(file nodefs.File, owner uint64, lk *fuse.FileLock, flags uint32, context *fuse.Context) (code fuse.Status) { 768 if file != nil { 769 return file.SetLk(owner, lk, flags) 770 } 771 return fuse.ENOSYS 772 } 773 774 func (n *pathInode) SetLkw(file nodefs.File, owner uint64, lk *fuse.FileLock, flags uint32, context *fuse.Context) (code fuse.Status) { 775 if file != nil { 776 return file.SetLkw(owner, lk, flags) 777 } 778 return fuse.ENOSYS 779 }