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