github.com/10XDev/rclone@v1.52.3-0.20200626220027-16af9ab76b2a/cmd/mount2/node.go (about) 1 // +build linux darwin,amd64 2 3 package mount2 4 5 import ( 6 "context" 7 "os" 8 "path" 9 "syscall" 10 11 fusefs "github.com/hanwen/go-fuse/v2/fs" 12 "github.com/hanwen/go-fuse/v2/fuse" 13 "github.com/rclone/rclone/cmd/mountlib" 14 "github.com/rclone/rclone/fs" 15 "github.com/rclone/rclone/fs/log" 16 "github.com/rclone/rclone/vfs" 17 ) 18 19 // Node represents a directory or file 20 type Node struct { 21 fusefs.Inode 22 node vfs.Node 23 fsys *FS 24 } 25 26 // Node types must be InodeEmbedders 27 var _ fusefs.InodeEmbedder = (*Node)(nil) 28 29 // newNode creates a new fusefs.Node from a vfs Node 30 func newNode(fsys *FS, vfsNode vfs.Node) (node *Node) { 31 // Check the vfsNode to see if it has a fuse Node cached 32 // We must return the same fuse nodes for vfs Nodes 33 node, ok := vfsNode.Sys().(*Node) 34 if ok { 35 return node 36 } 37 node = &Node{ 38 node: vfsNode, 39 fsys: fsys, 40 } 41 // Cache the node for later 42 vfsNode.SetSys(node) 43 return node 44 } 45 46 // String used for pretty printing. 47 func (n *Node) String() string { 48 return n.node.Path() 49 } 50 51 // lookup a Node in a directory 52 func (n *Node) lookupVfsNodeInDir(leaf string) (vfsNode vfs.Node, errno syscall.Errno) { 53 dir, ok := n.node.(*vfs.Dir) 54 if !ok { 55 return nil, syscall.ENOTDIR 56 } 57 vfsNode, err := dir.Stat(leaf) 58 return vfsNode, translateError(err) 59 } 60 61 // // lookup a Dir given a path 62 // func (n *Node) lookupDir(path string) (dir *vfs.Dir, code fuse.Status) { 63 // node, code := fsys.lookupVfsNodeInDir(path) 64 // if !code.Ok() { 65 // return nil, code 66 // } 67 // dir, ok := n.(*vfs.Dir) 68 // if !ok { 69 // return nil, fuse.ENOTDIR 70 // } 71 // return dir, fuse.OK 72 // } 73 74 // // lookup a parent Dir given a path returning the dir and the leaf 75 // func (n *Node) lookupParentDir(filePath string) (leaf string, dir *vfs.Dir, code fuse.Status) { 76 // parentDir, leaf := path.Split(filePath) 77 // dir, code = fsys.lookupDir(parentDir) 78 // return leaf, dir, code 79 // } 80 81 // Statfs implements statistics for the filesystem that holds this 82 // Inode. If not defined, the `out` argument will zeroed with an OK 83 // result. This is because OSX filesystems must Statfs, or the mount 84 // will not work. 85 func (n *Node) Statfs(ctx context.Context, out *fuse.StatfsOut) syscall.Errno { 86 defer log.Trace(n, "")("out=%+v", &out) 87 out = new(fuse.StatfsOut) 88 const blockSize = 4096 89 const fsBlocks = (1 << 50) / blockSize 90 out.Blocks = fsBlocks // Total data blocks in file system. 91 out.Bfree = fsBlocks // Free blocks in file system. 92 out.Bavail = fsBlocks // Free blocks in file system if you're not root. 93 out.Files = 1e9 // Total files in file system. 94 out.Ffree = 1e9 // Free files in file system. 95 out.Bsize = blockSize // Block size 96 out.NameLen = 255 // Maximum file name length? 97 out.Frsize = blockSize // Fragment size, smallest addressable data size in the file system. 98 mountlib.ClipBlocks(&out.Blocks) 99 mountlib.ClipBlocks(&out.Bfree) 100 mountlib.ClipBlocks(&out.Bavail) 101 return 0 102 } 103 104 var _ = (fusefs.NodeStatfser)((*Node)(nil)) 105 106 // Getattr reads attributes for an Inode. The library will ensure that 107 // Mode and Ino are set correctly. For files that are not opened with 108 // FOPEN_DIRECTIO, Size should be set so it can be read correctly. If 109 // returning zeroed permissions, the default behavior is to change the 110 // mode of 0755 (directory) or 0644 (files). This can be switched off 111 // with the Options.NullPermissions setting. If blksize is unset, 4096 112 // is assumed, and the 'blocks' field is set accordingly. 113 func (n *Node) Getattr(ctx context.Context, f fusefs.FileHandle, out *fuse.AttrOut) syscall.Errno { 114 setAttrOut(n.node, out) 115 return 0 116 } 117 118 var _ = (fusefs.NodeGetattrer)((*Node)(nil)) 119 120 // Setattr sets attributes for an Inode. 121 func (n *Node) Setattr(ctx context.Context, f fusefs.FileHandle, in *fuse.SetAttrIn, out *fuse.AttrOut) (errno syscall.Errno) { 122 defer log.Trace(n, "in=%v", in)("out=%#v, errno=%v", &out, &errno) 123 var err error 124 setAttrOut(n.node, out) 125 size, ok := in.GetSize() 126 if ok { 127 err = n.node.Truncate(int64(size)) 128 if err != nil { 129 return translateError(err) 130 } 131 out.Attr.Size = size 132 } 133 mtime, ok := in.GetMTime() 134 if ok { 135 err = n.node.SetModTime(mtime) 136 if err != nil { 137 return translateError(err) 138 } 139 out.Attr.Mtime = uint64(mtime.Unix()) 140 out.Attr.Mtimensec = uint32(mtime.Nanosecond()) 141 } 142 return 0 143 } 144 145 var _ = (fusefs.NodeSetattrer)((*Node)(nil)) 146 147 // Open opens an Inode (of regular file type) for reading. It 148 // is optional but recommended to return a FileHandle. 149 func (n *Node) Open(ctx context.Context, flags uint32) (fh fusefs.FileHandle, fuseFlags uint32, errno syscall.Errno) { 150 defer log.Trace(n, "flags=%#o", flags)("errno=%v", &errno) 151 // fuse flags are based off syscall flags as are os flags, so 152 // should be compatible 153 handle, err := n.node.Open(int(flags)) 154 if err != nil { 155 return nil, 0, translateError(err) 156 } 157 // If size unknown then use direct io to read 158 if entry := n.node.DirEntry(); entry != nil && entry.Size() < 0 { 159 fuseFlags |= fuse.FOPEN_DIRECT_IO 160 } 161 return newFileHandle(handle), fuseFlags, 0 162 } 163 164 var _ = (fusefs.NodeOpener)((*Node)(nil)) 165 166 // Lookup should find a direct child of a directory by the child's name. If 167 // the entry does not exist, it should return ENOENT and optionally 168 // set a NegativeTimeout in `out`. If it does exist, it should return 169 // attribute data in `out` and return the Inode for the child. A new 170 // inode can be created using `Inode.NewInode`. The new Inode will be 171 // added to the FS tree automatically if the return status is OK. 172 // 173 // If a directory does not implement NodeLookuper, the library looks 174 // for an existing child with the given name. 175 // 176 // The input to a Lookup is {parent directory, name string}. 177 // 178 // Lookup, if successful, must return an *Inode. Once the Inode is 179 // returned to the kernel, the kernel can issue further operations, 180 // such as Open or Getxattr on that node. 181 // 182 // A successful Lookup also returns an EntryOut. Among others, this 183 // contains file attributes (mode, size, mtime, etc.). 184 // 185 // FUSE supports other operations that modify the namespace. For 186 // example, the Symlink, Create, Mknod, Link methods all create new 187 // children in directories. Hence, they also return *Inode and must 188 // populate their fuse.EntryOut arguments. 189 func (n *Node) Lookup(ctx context.Context, name string, out *fuse.EntryOut) (inode *fusefs.Inode, errno syscall.Errno) { 190 defer log.Trace(n, "name=%q", name)("inode=%v, attr=%v, errno=%v", &inode, &out, &errno) 191 vfsNode, errno := n.lookupVfsNodeInDir(name) 192 if errno != 0 { 193 return nil, errno 194 } 195 newNode := newNode(n.fsys, vfsNode) 196 197 // FIXME 198 // out.SetEntryTimeout(dt time.Duration) 199 // out.SetAttrTimeout(dt time.Duration) 200 setEntryOut(vfsNode, out) 201 202 return n.NewInode(ctx, newNode, fusefs.StableAttr{Mode: out.Attr.Mode}), 0 203 } 204 205 var _ = (fusefs.NodeLookuper)((*Node)(nil)) 206 207 // Opendir opens a directory Inode for reading its 208 // contents. The actual reading is driven from Readdir, so 209 // this method is just for performing sanity/permission 210 // checks. The default is to return success. 211 func (n *Node) Opendir(ctx context.Context) syscall.Errno { 212 if !n.node.IsDir() { 213 return syscall.ENOTDIR 214 } 215 return 0 216 } 217 218 var _ = (fusefs.NodeOpendirer)((*Node)(nil)) 219 220 type dirStream struct { 221 nodes []os.FileInfo 222 i int 223 } 224 225 // HasNext indicates if there are further entries. HasNext 226 // might be called on already closed streams. 227 func (ds *dirStream) HasNext() bool { 228 return ds.i < len(ds.nodes) 229 } 230 231 // Next retrieves the next entry. It is only called if HasNext 232 // has previously returned true. The Errno return may be used to 233 // indicate I/O errors 234 func (ds *dirStream) Next() (de fuse.DirEntry, errno syscall.Errno) { 235 // defer log.Trace(nil, "")("de=%+v, errno=%v", &de, &errno) 236 fi := ds.nodes[ds.i] 237 de = fuse.DirEntry{ 238 // Mode is the file's mode. Only the high bits (eg. S_IFDIR) 239 // are considered. 240 Mode: getMode(fi), 241 242 // Name is the basename of the file in the directory. 243 Name: path.Base(fi.Name()), 244 245 // Ino is the inode number. 246 Ino: 0, // FIXME 247 } 248 ds.i++ 249 return de, 0 250 } 251 252 // Close releases resources related to this directory 253 // stream. 254 func (ds *dirStream) Close() { 255 } 256 257 var _ fusefs.DirStream = (*dirStream)(nil) 258 259 // Readdir opens a stream of directory entries. 260 // 261 // Readdir essentiallly returns a list of strings, and it is allowed 262 // for Readdir to return different results from Lookup. For example, 263 // you can return nothing for Readdir ("ls my-fuse-mount" is empty), 264 // while still implementing Lookup ("ls my-fuse-mount/a-specific-file" 265 // shows a single file). 266 // 267 // If a directory does not implement NodeReaddirer, a list of 268 // currently known children from the tree is returned. This means that 269 // static in-memory file systems need not implement NodeReaddirer. 270 func (n *Node) Readdir(ctx context.Context) (ds fusefs.DirStream, errno syscall.Errno) { 271 defer log.Trace(n, "")("ds=%v, errno=%v", &ds, &errno) 272 if !n.node.IsDir() { 273 return nil, syscall.ENOTDIR 274 } 275 fh, err := n.node.Open(os.O_RDONLY) 276 if err != nil { 277 return nil, translateError(err) 278 } 279 defer func() { 280 closeErr := fh.Close() 281 if errno == 0 && closeErr != nil { 282 errno = translateError(closeErr) 283 } 284 }() 285 items, err := fh.Readdir(-1) 286 if err != nil { 287 return nil, translateError(err) 288 } 289 return &dirStream{ 290 nodes: items, 291 }, 0 292 } 293 294 var _ = (fusefs.NodeReaddirer)((*Node)(nil)) 295 296 // Mkdir is similar to Lookup, but must create a directory entry and Inode. 297 // Default is to return EROFS. 298 func (n *Node) Mkdir(ctx context.Context, name string, mode uint32, out *fuse.EntryOut) (inode *fusefs.Inode, errno syscall.Errno) { 299 defer log.Trace(name, "mode=0%o", mode)("inode=%v, errno=%v", &inode, &errno) 300 dir, ok := n.node.(*vfs.Dir) 301 if !ok { 302 return nil, syscall.ENOTDIR 303 } 304 newDir, err := dir.Mkdir(name) 305 if err != nil { 306 return nil, translateError(err) 307 } 308 newNode := newNode(n.fsys, newDir) 309 setEntryOut(newNode.node, out) 310 newInode := n.NewInode(ctx, newNode, fusefs.StableAttr{Mode: out.Attr.Mode}) 311 return newInode, 0 312 } 313 314 var _ = (fusefs.NodeMkdirer)((*Node)(nil)) 315 316 // Create is similar to Lookup, but should create a new 317 // child. It typically also returns a FileHandle as a 318 // reference for future reads/writes. 319 // Default is to return EROFS. 320 func (n *Node) Create(ctx context.Context, name string, flags uint32, mode uint32, out *fuse.EntryOut) (node *fusefs.Inode, fh fusefs.FileHandle, fuseFlags uint32, errno syscall.Errno) { 321 defer log.Trace(n, "name=%q, flags=%#o, mode=%#o", name, flags, mode)("node=%v, fh=%v, flags=%#o, errno=%v", &node, &fh, &fuseFlags, &errno) 322 dir, ok := n.node.(*vfs.Dir) 323 if !ok { 324 return nil, nil, 0, syscall.ENOTDIR 325 } 326 // translate the fuse flags to os flags 327 osFlags := int(flags) | os.O_CREATE 328 file, err := dir.Create(name, osFlags) 329 if err != nil { 330 return nil, nil, 0, translateError(err) 331 } 332 handle, err := file.Open(osFlags) 333 if err != nil { 334 return nil, nil, 0, translateError(err) 335 } 336 fh = newFileHandle(handle) 337 // FIXME 338 // fh = &fusefs.WithFlags{ 339 // File: fh, 340 // //FuseFlags: fuse.FOPEN_NONSEEKABLE, 341 // OpenFlags: flags, 342 // } 343 344 // Find the created node 345 vfsNode, errno := n.lookupVfsNodeInDir(name) 346 if errno != 0 { 347 return nil, nil, 0, errno 348 } 349 setEntryOut(vfsNode, out) 350 newNode := newNode(n.fsys, vfsNode) 351 fs.Debugf(nil, "attr=%#v", out.Attr) 352 newInode := n.NewInode(ctx, newNode, fusefs.StableAttr{Mode: out.Attr.Mode}) 353 return newInode, fh, 0, 0 354 } 355 356 var _ = (fusefs.NodeCreater)((*Node)(nil)) 357 358 // Unlink should remove a child from this directory. If the 359 // return status is OK, the Inode is removed as child in the 360 // FS tree automatically. Default is to return EROFS. 361 func (n *Node) Unlink(ctx context.Context, name string) (errno syscall.Errno) { 362 defer log.Trace(n, "name=%q", name)("errno=%v", &errno) 363 vfsNode, errno := n.lookupVfsNodeInDir(name) 364 if errno != 0 { 365 return errno 366 } 367 return translateError(vfsNode.Remove()) 368 } 369 370 var _ = (fusefs.NodeUnlinker)((*Node)(nil)) 371 372 // Rmdir is like Unlink but for directories. 373 // Default is to return EROFS. 374 func (n *Node) Rmdir(ctx context.Context, name string) (errno syscall.Errno) { 375 defer log.Trace(n, "name=%q", name)("errno=%v", &errno) 376 vfsNode, errno := n.lookupVfsNodeInDir(name) 377 if errno != 0 { 378 return errno 379 } 380 return translateError(vfsNode.Remove()) 381 } 382 383 var _ = (fusefs.NodeRmdirer)((*Node)(nil)) 384 385 // Rename should move a child from one directory to a different 386 // one. The change is effected in the FS tree if the return status is 387 // OK. Default is to return EROFS. 388 func (n *Node) Rename(ctx context.Context, oldName string, newParent fusefs.InodeEmbedder, newName string, flags uint32) (errno syscall.Errno) { 389 defer log.Trace(n, "oldName=%q, newParent=%v, newName=%q", oldName, newParent, newName)("errno=%v", &errno) 390 oldDir, ok := n.node.(*vfs.Dir) 391 if !ok { 392 return syscall.ENOTDIR 393 } 394 newParentNode, ok := newParent.(*Node) 395 if !ok { 396 fs.Errorf(n, "newParent was not a *Node") 397 return syscall.EIO 398 } 399 newDir, ok := newParentNode.node.(*vfs.Dir) 400 if !ok { 401 return syscall.ENOTDIR 402 } 403 return translateError(oldDir.Rename(oldName, newName, newDir)) 404 } 405 406 var _ = (fusefs.NodeRenamer)((*Node)(nil))