github.com/artpar/rclone@v1.67.3/cmd/mount/dir.go (about) 1 //go:build linux 2 3 package mount 4 5 import ( 6 "context" 7 "fmt" 8 "io" 9 "os" 10 "syscall" 11 "time" 12 13 "bazil.org/fuse" 14 fusefs "bazil.org/fuse/fs" 15 "github.com/artpar/rclone/cmd/mountlib" 16 "github.com/artpar/rclone/fs" 17 "github.com/artpar/rclone/fs/log" 18 "github.com/artpar/rclone/vfs" 19 ) 20 21 // Dir represents a directory entry 22 type Dir struct { 23 *vfs.Dir 24 fsys *FS 25 } 26 27 // Check interface satisfied 28 var _ fusefs.Node = (*Dir)(nil) 29 30 // Attr updates the attributes of a directory 31 func (d *Dir) Attr(ctx context.Context, a *fuse.Attr) (err error) { 32 defer log.Trace(d, "")("attr=%+v, err=%v", a, &err) 33 a.Valid = d.fsys.opt.AttrTimeout 34 a.Gid = d.VFS().Opt.GID 35 a.Uid = d.VFS().Opt.UID 36 a.Mode = os.ModeDir | d.VFS().Opt.DirPerms 37 modTime := d.ModTime() 38 a.Atime = modTime 39 a.Mtime = modTime 40 a.Ctime = modTime 41 // FIXME include Valid so get some caching? 42 // FIXME fs.Debugf(d.path, "Dir.Attr %+v", a) 43 return nil 44 } 45 46 // Check interface satisfied 47 var _ fusefs.NodeSetattrer = (*Dir)(nil) 48 49 // Setattr handles attribute changes from FUSE. Currently supports ModTime only. 50 func (d *Dir) Setattr(ctx context.Context, req *fuse.SetattrRequest, resp *fuse.SetattrResponse) (err error) { 51 defer log.Trace(d, "stat=%+v", req)("err=%v", &err) 52 if d.VFS().Opt.NoModTime { 53 return nil 54 } 55 56 if req.Valid.MtimeNow() { 57 err = d.SetModTime(time.Now()) 58 } else if req.Valid.Mtime() { 59 err = d.SetModTime(req.Mtime) 60 } 61 62 return translateError(err) 63 } 64 65 // Check interface satisfied 66 var _ fusefs.NodeRequestLookuper = (*Dir)(nil) 67 68 // Lookup looks up a specific entry in the receiver. 69 // 70 // Lookup should return a Node corresponding to the entry. If the 71 // name does not exist in the directory, Lookup should return ENOENT. 72 // 73 // Lookup need not to handle the names "." and "..". 74 func (d *Dir) Lookup(ctx context.Context, req *fuse.LookupRequest, resp *fuse.LookupResponse) (node fusefs.Node, err error) { 75 defer log.Trace(d, "name=%q", req.Name)("node=%+v, err=%v", &node, &err) 76 mnode, err := d.Dir.Stat(req.Name) 77 if err != nil { 78 return nil, translateError(err) 79 } 80 resp.EntryValid = d.fsys.opt.AttrTimeout 81 // Check the mnode to see if it has a fuse Node cached 82 // We must return the same fuse nodes for vfs Nodes 83 node, ok := mnode.Sys().(fusefs.Node) 84 if ok { 85 return node, nil 86 } 87 switch x := mnode.(type) { 88 case *vfs.File: 89 node = &File{x, d.fsys} 90 case *vfs.Dir: 91 node = &Dir{x, d.fsys} 92 default: 93 panic("bad type") 94 } 95 // Cache the node for later 96 mnode.SetSys(node) 97 return node, nil 98 } 99 100 // Check interface satisfied 101 var _ fusefs.HandleReadDirAller = (*Dir)(nil) 102 103 // ReadDirAll reads the contents of the directory 104 func (d *Dir) ReadDirAll(ctx context.Context) (dirents []fuse.Dirent, err error) { 105 itemsRead := -1 106 defer log.Trace(d, "")("item=%d, err=%v", &itemsRead, &err) 107 items, err := d.Dir.ReadDirAll() 108 if err != nil { 109 return nil, translateError(err) 110 } 111 dirents = append(dirents, fuse.Dirent{ 112 Type: fuse.DT_Dir, 113 Name: ".", 114 }, fuse.Dirent{ 115 Type: fuse.DT_Dir, 116 Name: "..", 117 }) 118 for _, node := range items { 119 name := node.Name() 120 if len(name) > mountlib.MaxLeafSize { 121 fs.Errorf(d, "Name too long (%d bytes) for FUSE, skipping: %s", len(name), name) 122 continue 123 } 124 var dirent = fuse.Dirent{ 125 // Inode FIXME ??? 126 Type: fuse.DT_File, 127 Name: name, 128 } 129 if node.IsDir() { 130 dirent.Type = fuse.DT_Dir 131 } 132 dirents = append(dirents, dirent) 133 } 134 itemsRead = len(dirents) 135 return dirents, nil 136 } 137 138 var _ fusefs.NodeCreater = (*Dir)(nil) 139 140 // Create makes a new file 141 func (d *Dir) Create(ctx context.Context, req *fuse.CreateRequest, resp *fuse.CreateResponse) (node fusefs.Node, handle fusefs.Handle, err error) { 142 defer log.Trace(d, "name=%q", req.Name)("node=%v, handle=%v, err=%v", &node, &handle, &err) 143 file, err := d.Dir.Create(req.Name, int(req.Flags)) 144 if err != nil { 145 return nil, nil, translateError(err) 146 } 147 fh, err := file.Open(int(req.Flags) | os.O_CREATE) 148 if err != nil { 149 return nil, nil, translateError(err) 150 } 151 node = &File{file, d.fsys} 152 file.SetSys(node) // cache the FUSE node for later 153 return node, &FileHandle{fh}, err 154 } 155 156 var _ fusefs.NodeMkdirer = (*Dir)(nil) 157 158 // Mkdir creates a new directory 159 func (d *Dir) Mkdir(ctx context.Context, req *fuse.MkdirRequest) (node fusefs.Node, err error) { 160 defer log.Trace(d, "name=%q", req.Name)("node=%+v, err=%v", &node, &err) 161 dir, err := d.Dir.Mkdir(req.Name) 162 if err != nil { 163 return nil, translateError(err) 164 } 165 node = &Dir{dir, d.fsys} 166 dir.SetSys(node) // cache the FUSE node for later 167 return node, nil 168 } 169 170 var _ fusefs.NodeRemover = (*Dir)(nil) 171 172 // Remove removes the entry with the given name from 173 // the receiver, which must be a directory. The entry to be removed 174 // may correspond to a file (unlink) or to a directory (rmdir). 175 func (d *Dir) Remove(ctx context.Context, req *fuse.RemoveRequest) (err error) { 176 defer log.Trace(d, "name=%q", req.Name)("err=%v", &err) 177 err = d.Dir.RemoveName(req.Name) 178 if err != nil { 179 return translateError(err) 180 } 181 return nil 182 } 183 184 // Invalidate a leaf in a directory 185 func (d *Dir) invalidateEntry(dirNode fusefs.Node, leaf string) { 186 fs.Debugf(dirNode, "Invalidating %q", leaf) 187 err := d.fsys.server.InvalidateEntry(dirNode, leaf) 188 if err != nil { 189 fs.Debugf(dirNode, "Failed to invalidate %q: %v", leaf, err) 190 } 191 } 192 193 // Check interface satisfied 194 var _ fusefs.NodeRenamer = (*Dir)(nil) 195 196 // Rename the file 197 func (d *Dir) Rename(ctx context.Context, req *fuse.RenameRequest, newDir fusefs.Node) (err error) { 198 defer log.Trace(d, "oldName=%q, newName=%q, newDir=%+v", req.OldName, req.NewName, newDir)("err=%v", &err) 199 destDir, ok := newDir.(*Dir) 200 if !ok { 201 return fmt.Errorf("unknown Dir type %T", newDir) 202 } 203 204 err = d.Dir.Rename(req.OldName, req.NewName, destDir.Dir) 205 if err != nil { 206 return translateError(err) 207 } 208 209 // Invalidate the new directory entry so it gets re-read (in 210 // the background otherwise we cause a deadlock) 211 // 212 // See https://github.com/artpar/rclone/issues/4977 for why 213 go d.invalidateEntry(newDir, req.NewName) 214 //go d.invalidateEntry(d, req.OldName) 215 216 return nil 217 } 218 219 // Check interface satisfied 220 var _ fusefs.NodeFsyncer = (*Dir)(nil) 221 222 // Fsync the directory 223 func (d *Dir) Fsync(ctx context.Context, req *fuse.FsyncRequest) (err error) { 224 defer log.Trace(d, "")("err=%v", &err) 225 err = d.Dir.Sync() 226 if err != nil { 227 return translateError(err) 228 } 229 return nil 230 } 231 232 // Check interface satisfied 233 var _ fusefs.NodeLinker = (*Dir)(nil) 234 235 // Link creates a new directory entry in the receiver based on an 236 // existing Node. Receiver must be a directory. 237 func (d *Dir) Link(ctx context.Context, req *fuse.LinkRequest, old fusefs.Node) (newNode fusefs.Node, err error) { 238 defer log.Trace(d, "req=%v, old=%v", req, old)("new=%v, err=%v", &newNode, &err) 239 return nil, syscall.ENOSYS 240 } 241 242 // Check interface satisfied 243 var _ fusefs.NodeMknoder = (*Dir)(nil) 244 245 // Mknod is called to create a file. Since we define create this will 246 // be called in preference, however NFS likes to call it for some 247 // reason. We don't actually create a file here just the Node. 248 func (d *Dir) Mknod(ctx context.Context, req *fuse.MknodRequest) (node fusefs.Node, err error) { 249 defer log.Trace(d, "name=%v, mode=%d, rdev=%d", req.Name, req.Mode, req.Rdev)("node=%v, err=%v", &node, &err) 250 if req.Rdev != 0 { 251 fs.Errorf(d, "Can't create device node %q", req.Name) 252 return nil, fuse.Errno(syscall.EIO) 253 } 254 var cReq = fuse.CreateRequest{ 255 Name: req.Name, 256 Flags: fuse.OpenFlags(os.O_CREATE | os.O_WRONLY), 257 Mode: req.Mode, 258 Umask: req.Umask, 259 } 260 var cResp fuse.CreateResponse 261 node, handle, err := d.Create(ctx, &cReq, &cResp) 262 if err != nil { 263 return nil, err 264 } 265 err = handle.(io.Closer).Close() 266 if err != nil { 267 return nil, err 268 } 269 return node, nil 270 }