1//go:build cmount && cgo && (linux || darwin || freebsd || windows) 2// +build cmount 3// +build cgo 4// +build linux darwin freebsd windows 5 6package cmount 7 8import ( 9 "io" 10 "os" 11 "path" 12 "sync" 13 "sync/atomic" 14 "time" 15 16 "github.com/billziss-gh/cgofuse/fuse" 17 "github.com/pkg/errors" 18 "github.com/rclone/rclone/cmd/mountlib" 19 "github.com/rclone/rclone/fs" 20 "github.com/rclone/rclone/fs/log" 21 "github.com/rclone/rclone/vfs" 22) 23 24const fhUnset = ^uint64(0) 25 26// FS represents the top level filing system 27type FS struct { 28 VFS *vfs.VFS 29 f fs.Fs 30 ready chan (struct{}) 31 mu sync.Mutex // to protect the below 32 handles []vfs.Handle 33 destroyed int32 // read/write with sync/atomic 34} 35 36// NewFS makes a new FS 37func NewFS(VFS *vfs.VFS) *FS { 38 fsys := &FS{ 39 VFS: VFS, 40 f: VFS.Fs(), 41 ready: make(chan (struct{})), 42 } 43 return fsys 44} 45 46// Open a handle returning an integer file handle 47func (fsys *FS) openHandle(handle vfs.Handle) (fh uint64) { 48 fsys.mu.Lock() 49 defer fsys.mu.Unlock() 50 var i int 51 var oldHandle vfs.Handle 52 for i, oldHandle = range fsys.handles { 53 if oldHandle == nil { 54 fsys.handles[i] = handle 55 goto found 56 } 57 } 58 fsys.handles = append(fsys.handles, handle) 59 i = len(fsys.handles) - 1 60found: 61 return uint64(i) 62} 63 64// get the handle for fh, call with the lock held 65func (fsys *FS) _getHandle(fh uint64) (i int, handle vfs.Handle, errc int) { 66 if fh > uint64(len(fsys.handles)) { 67 fs.Debugf(nil, "Bad file handle: too big: 0x%X", fh) 68 return i, nil, -fuse.EBADF 69 } 70 i = int(fh) 71 handle = fsys.handles[i] 72 if handle == nil { 73 fs.Debugf(nil, "Bad file handle: nil handle: 0x%X", fh) 74 return i, nil, -fuse.EBADF 75 } 76 return i, handle, 0 77} 78 79// Get the handle for the file handle 80func (fsys *FS) getHandle(fh uint64) (handle vfs.Handle, errc int) { 81 fsys.mu.Lock() 82 _, handle, errc = fsys._getHandle(fh) 83 fsys.mu.Unlock() 84 return 85} 86 87// Close the handle 88func (fsys *FS) closeHandle(fh uint64) (errc int) { 89 fsys.mu.Lock() 90 i, _, errc := fsys._getHandle(fh) 91 if errc == 0 { 92 fsys.handles[i] = nil 93 } 94 fsys.mu.Unlock() 95 return 96} 97 98// lookup a Node given a path 99func (fsys *FS) lookupNode(path string) (node vfs.Node, errc int) { 100 node, err := fsys.VFS.Stat(path) 101 return node, translateError(err) 102} 103 104// lookup a Dir given a path 105func (fsys *FS) lookupDir(path string) (dir *vfs.Dir, errc int) { 106 node, errc := fsys.lookupNode(path) 107 if errc != 0 { 108 return nil, errc 109 } 110 dir, ok := node.(*vfs.Dir) 111 if !ok { 112 return nil, -fuse.ENOTDIR 113 } 114 return dir, 0 115} 116 117// lookup a parent Dir given a path returning the dir and the leaf 118func (fsys *FS) lookupParentDir(filePath string) (leaf string, dir *vfs.Dir, errc int) { 119 parentDir, leaf := path.Split(filePath) 120 dir, errc = fsys.lookupDir(parentDir) 121 return leaf, dir, errc 122} 123 124// lookup a File given a path 125func (fsys *FS) lookupFile(path string) (file *vfs.File, errc int) { 126 node, errc := fsys.lookupNode(path) 127 if errc != 0 { 128 return nil, errc 129 } 130 file, ok := node.(*vfs.File) 131 if !ok { 132 return nil, -fuse.EISDIR 133 } 134 return file, 0 135} 136 137// get a node and handle from the path or from the fh if not fhUnset 138// 139// handle may be nil 140func (fsys *FS) getNode(path string, fh uint64) (node vfs.Node, handle vfs.Handle, errc int) { 141 if fh == fhUnset { 142 node, errc = fsys.lookupNode(path) 143 } else { 144 handle, errc = fsys.getHandle(fh) 145 if errc == 0 { 146 node = handle.Node() 147 } 148 } 149 return 150} 151 152// stat fills up the stat block for Node 153func (fsys *FS) stat(node vfs.Node, stat *fuse.Stat_t) (errc int) { 154 Size := uint64(node.Size()) 155 Blocks := (Size + 511) / 512 156 modTime := node.ModTime() 157 Mode := node.Mode().Perm() 158 if node.IsDir() { 159 Mode |= fuse.S_IFDIR 160 } else { 161 Mode |= fuse.S_IFREG 162 } 163 //stat.Dev = 1 164 stat.Ino = node.Inode() // FIXME do we need to set the inode number? 165 stat.Mode = uint32(Mode) 166 stat.Nlink = 1 167 stat.Uid = fsys.VFS.Opt.UID 168 stat.Gid = fsys.VFS.Opt.GID 169 //stat.Rdev 170 stat.Size = int64(Size) 171 t := fuse.NewTimespec(modTime) 172 stat.Atim = t 173 stat.Mtim = t 174 stat.Ctim = t 175 stat.Blksize = 512 176 stat.Blocks = int64(Blocks) 177 stat.Birthtim = t 178 // fs.Debugf(nil, "stat = %+v", *stat) 179 return 0 180} 181 182// Init is called after the filesystem is ready 183func (fsys *FS) Init() { 184 defer log.Trace(fsys.f, "")("") 185 close(fsys.ready) 186} 187 188// Destroy is called when it is unmounted (note that depending on how 189// the file system is terminated the file system may not receive the 190// Destroy call). 191func (fsys *FS) Destroy() { 192 defer log.Trace(fsys.f, "")("") 193 atomic.StoreInt32(&fsys.destroyed, 1) 194} 195 196// Getattr reads the attributes for path 197func (fsys *FS) Getattr(path string, stat *fuse.Stat_t, fh uint64) (errc int) { 198 defer log.Trace(path, "fh=0x%X", fh)("errc=%v", &errc) 199 node, _, errc := fsys.getNode(path, fh) 200 if errc == 0 { 201 errc = fsys.stat(node, stat) 202 } 203 return 204} 205 206// Opendir opens path as a directory 207func (fsys *FS) Opendir(path string) (errc int, fh uint64) { 208 defer log.Trace(path, "")("errc=%d, fh=0x%X", &errc, &fh) 209 handle, err := fsys.VFS.OpenFile(path, os.O_RDONLY, 0777) 210 if err != nil { 211 return translateError(err), fhUnset 212 } 213 return 0, fsys.openHandle(handle) 214} 215 216// Readdir reads the directory at dirPath 217func (fsys *FS) Readdir(dirPath string, 218 fill func(name string, stat *fuse.Stat_t, ofst int64) bool, 219 ofst int64, 220 fh uint64) (errc int) { 221 itemsRead := -1 222 defer log.Trace(dirPath, "ofst=%d, fh=0x%X", ofst, fh)("items=%d, errc=%d", &itemsRead, &errc) 223 224 dir, errc := fsys.lookupDir(dirPath) 225 if errc != 0 { 226 return errc 227 } 228 229 // We can't seek in directories and FUSE should know that so 230 // return an error if ofst is ever set. 231 if ofst > 0 { 232 return -fuse.ESPIPE 233 } 234 235 nodes, err := dir.ReadDirAll() 236 if err != nil { 237 return translateError(err) 238 } 239 240 // Optionally, create a struct stat that describes the file as 241 // for getattr (but FUSE only looks at st_ino and the 242 // file-type bits of st_mode). 243 // 244 // We have called host.SetCapReaddirPlus() so WinFsp will 245 // use the full stat information - a Useful optimization on 246 // Windows. 247 // 248 // NB we are using the first mode for readdir: The readdir 249 // implementation ignores the offset parameter, and passes 250 // zero to the filler function's offset. The filler function 251 // will not return '1' (unless an error happens), so the whole 252 // directory is read in a single readdir operation. 253 fill(".", nil, 0) 254 fill("..", nil, 0) 255 for _, node := range nodes { 256 name := node.Name() 257 if len(name) > mountlib.MaxLeafSize { 258 fs.Errorf(dirPath, "Name too long (%d bytes) for FUSE, skipping: %s", len(name), name) 259 continue 260 } 261 // We have called host.SetCapReaddirPlus() so supply the stat information 262 // It is very cheap at this point so supply it regardless of OS capabilities 263 var stat fuse.Stat_t 264 _ = fsys.stat(node, &stat) // not capable of returning an error 265 fill(name, &stat, 0) 266 } 267 itemsRead = len(nodes) 268 return 0 269} 270 271// Releasedir finished reading the directory 272func (fsys *FS) Releasedir(path string, fh uint64) (errc int) { 273 defer log.Trace(path, "fh=0x%X", fh)("errc=%d", &errc) 274 return fsys.closeHandle(fh) 275} 276 277// Statfs reads overall stats on the filesystem 278func (fsys *FS) Statfs(path string, stat *fuse.Statfs_t) (errc int) { 279 defer log.Trace(path, "")("stat=%+v, errc=%d", stat, &errc) 280 const blockSize = 4096 281 total, _, free := fsys.VFS.Statfs() 282 stat.Blocks = uint64(total) / blockSize // Total data blocks in file system. 283 stat.Bfree = uint64(free) / blockSize // Free blocks in file system. 284 stat.Bavail = stat.Bfree // Free blocks in file system if you're not root. 285 stat.Files = 1e9 // Total files in file system. 286 stat.Ffree = 1e9 // Free files in file system. 287 stat.Bsize = blockSize // Block size 288 stat.Namemax = 255 // Maximum file name length? 289 stat.Frsize = blockSize // Fragment size, smallest addressable data size in the file system. 290 mountlib.ClipBlocks(&stat.Blocks) 291 mountlib.ClipBlocks(&stat.Bfree) 292 mountlib.ClipBlocks(&stat.Bavail) 293 return 0 294} 295 296// OpenEx opens a file 297func (fsys *FS) OpenEx(path string, fi *fuse.FileInfo_t) (errc int) { 298 defer log.Trace(path, "flags=0x%X", fi.Flags)("errc=%d, fh=0x%X", &errc, &fi.Fh) 299 fi.Fh = fhUnset 300 301 // translate the fuse flags to os flags 302 flags := translateOpenFlags(fi.Flags) 303 handle, err := fsys.VFS.OpenFile(path, flags, 0777) 304 if err != nil { 305 return translateError(err) 306 } 307 308 // If size unknown then use direct io to read 309 if entry := handle.Node().DirEntry(); entry != nil && entry.Size() < 0 { 310 fi.DirectIo = true 311 } 312 313 fi.Fh = fsys.openHandle(handle) 314 return 0 315} 316 317// Open opens a file 318func (fsys *FS) Open(path string, flags int) (errc int, fh uint64) { 319 var fi = fuse.FileInfo_t{ 320 Flags: flags, 321 } 322 errc = fsys.OpenEx(path, &fi) 323 return errc, fi.Fh 324} 325 326// CreateEx creates and opens a file. 327func (fsys *FS) CreateEx(filePath string, mode uint32, fi *fuse.FileInfo_t) (errc int) { 328 defer log.Trace(filePath, "flags=0x%X, mode=0%o", fi.Flags, mode)("errc=%d, fh=0x%X", &errc, &fi.Fh) 329 fi.Fh = fhUnset 330 leaf, parentDir, errc := fsys.lookupParentDir(filePath) 331 if errc != 0 { 332 return errc 333 } 334 file, err := parentDir.Create(leaf, fi.Flags) 335 if err != nil { 336 return translateError(err) 337 } 338 // translate the fuse flags to os flags 339 flags := translateOpenFlags(fi.Flags) | os.O_CREATE 340 handle, err := file.Open(flags) 341 if err != nil { 342 return translateError(err) 343 } 344 fi.Fh = fsys.openHandle(handle) 345 return 0 346} 347 348// Create creates and opens a file. 349func (fsys *FS) Create(filePath string, flags int, mode uint32) (errc int, fh uint64) { 350 var fi = fuse.FileInfo_t{ 351 Flags: flags, 352 } 353 errc = fsys.CreateEx(filePath, mode, &fi) 354 return errc, fi.Fh 355} 356 357// Truncate truncates a file to size 358func (fsys *FS) Truncate(path string, size int64, fh uint64) (errc int) { 359 defer log.Trace(path, "size=%d, fh=0x%X", size, fh)("errc=%d", &errc) 360 node, handle, errc := fsys.getNode(path, fh) 361 if errc != 0 { 362 return errc 363 } 364 var err error 365 if handle != nil { 366 err = handle.Truncate(size) 367 } else { 368 err = node.Truncate(size) 369 } 370 if err != nil { 371 return translateError(err) 372 } 373 return 0 374} 375 376// Read data from file handle 377func (fsys *FS) Read(path string, buff []byte, ofst int64, fh uint64) (n int) { 378 defer log.Trace(path, "ofst=%d, fh=0x%X", ofst, fh)("n=%d", &n) 379 handle, errc := fsys.getHandle(fh) 380 if errc != 0 { 381 return errc 382 } 383 n, err := handle.ReadAt(buff, ofst) 384 if err == io.EOF { 385 } else if err != nil { 386 return translateError(err) 387 } 388 return n 389} 390 391// Write data to file handle 392func (fsys *FS) Write(path string, buff []byte, ofst int64, fh uint64) (n int) { 393 defer log.Trace(path, "ofst=%d, fh=0x%X", ofst, fh)("n=%d", &n) 394 handle, errc := fsys.getHandle(fh) 395 if errc != 0 { 396 return errc 397 } 398 n, err := handle.WriteAt(buff, ofst) 399 if err != nil { 400 return translateError(err) 401 } 402 return n 403} 404 405// Flush flushes an open file descriptor or path 406func (fsys *FS) Flush(path string, fh uint64) (errc int) { 407 defer log.Trace(path, "fh=0x%X", fh)("errc=%d", &errc) 408 handle, errc := fsys.getHandle(fh) 409 if errc != 0 { 410 return errc 411 } 412 return translateError(handle.Flush()) 413} 414 415// Release closes the file if still open 416func (fsys *FS) Release(path string, fh uint64) (errc int) { 417 defer log.Trace(path, "fh=0x%X", fh)("errc=%d", &errc) 418 handle, errc := fsys.getHandle(fh) 419 if errc != 0 { 420 return errc 421 } 422 _ = fsys.closeHandle(fh) 423 return translateError(handle.Release()) 424} 425 426// Unlink removes a file. 427func (fsys *FS) Unlink(filePath string) (errc int) { 428 defer log.Trace(filePath, "")("errc=%d", &errc) 429 leaf, parentDir, errc := fsys.lookupParentDir(filePath) 430 if errc != 0 { 431 return errc 432 } 433 return translateError(parentDir.RemoveName(leaf)) 434} 435 436// Mkdir creates a directory. 437func (fsys *FS) Mkdir(dirPath string, mode uint32) (errc int) { 438 defer log.Trace(dirPath, "mode=0%o", mode)("errc=%d", &errc) 439 leaf, parentDir, errc := fsys.lookupParentDir(dirPath) 440 if errc != 0 { 441 return errc 442 } 443 _, err := parentDir.Mkdir(leaf) 444 return translateError(err) 445} 446 447// Rmdir removes a directory 448func (fsys *FS) Rmdir(dirPath string) (errc int) { 449 defer log.Trace(dirPath, "")("errc=%d", &errc) 450 leaf, parentDir, errc := fsys.lookupParentDir(dirPath) 451 if errc != 0 { 452 return errc 453 } 454 return translateError(parentDir.RemoveName(leaf)) 455} 456 457// Rename renames a file. 458func (fsys *FS) Rename(oldPath string, newPath string) (errc int) { 459 defer log.Trace(oldPath, "newPath=%q", newPath)("errc=%d", &errc) 460 return translateError(fsys.VFS.Rename(oldPath, newPath)) 461} 462 463// Windows sometimes seems to send times that are the epoch which is 464// 1601-01-01 +/- timezone so filter out times that are earlier than 465// this. 466var invalidDateCutoff = time.Date(1601, 1, 2, 0, 0, 0, 0, time.UTC) 467 468// Utimens changes the access and modification times of a file. 469func (fsys *FS) Utimens(path string, tmsp []fuse.Timespec) (errc int) { 470 defer log.Trace(path, "tmsp=%+v", tmsp)("errc=%d", &errc) 471 node, errc := fsys.lookupNode(path) 472 if errc != 0 { 473 return errc 474 } 475 if tmsp == nil || len(tmsp) < 2 { 476 fs.Debugf(path, "Utimens: Not setting time as timespec isn't complete: %v", tmsp) 477 return 0 478 } 479 t := tmsp[1].Time() 480 if t.Before(invalidDateCutoff) { 481 fs.Debugf(path, "Utimens: Not setting out of range time: %v", t) 482 return 0 483 } 484 fs.Debugf(path, "Utimens: SetModTime: %v", t) 485 return translateError(node.SetModTime(t)) 486} 487 488// Mknod creates a file node. 489func (fsys *FS) Mknod(path string, mode uint32, dev uint64) (errc int) { 490 defer log.Trace(path, "mode=0x%X, dev=0x%X", mode, dev)("errc=%d", &errc) 491 return -fuse.ENOSYS 492} 493 494// Fsync synchronizes file contents. 495func (fsys *FS) Fsync(path string, datasync bool, fh uint64) (errc int) { 496 defer log.Trace(path, "datasync=%v, fh=0x%X", datasync, fh)("errc=%d", &errc) 497 // This is a no-op for rclone 498 return 0 499} 500 501// Link creates a hard link to a file. 502func (fsys *FS) Link(oldpath string, newpath string) (errc int) { 503 defer log.Trace(oldpath, "newpath=%q", newpath)("errc=%d", &errc) 504 return -fuse.ENOSYS 505} 506 507// Symlink creates a symbolic link. 508func (fsys *FS) Symlink(target string, newpath string) (errc int) { 509 defer log.Trace(target, "newpath=%q", newpath)("errc=%d", &errc) 510 return -fuse.ENOSYS 511} 512 513// Readlink reads the target of a symbolic link. 514func (fsys *FS) Readlink(path string) (errc int, linkPath string) { 515 defer log.Trace(path, "")("linkPath=%q, errc=%d", &linkPath, &errc) 516 return -fuse.ENOSYS, "" 517} 518 519// Chmod changes the permission bits of a file. 520func (fsys *FS) Chmod(path string, mode uint32) (errc int) { 521 defer log.Trace(path, "mode=0%o", mode)("errc=%d", &errc) 522 // This is a no-op for rclone 523 return 0 524} 525 526// Chown changes the owner and group of a file. 527func (fsys *FS) Chown(path string, uid uint32, gid uint32) (errc int) { 528 defer log.Trace(path, "uid=%d, gid=%d", uid, gid)("errc=%d", &errc) 529 // This is a no-op for rclone 530 return 0 531} 532 533// Access checks file access permissions. 534func (fsys *FS) Access(path string, mask uint32) (errc int) { 535 defer log.Trace(path, "mask=0%o", mask)("errc=%d", &errc) 536 // This is a no-op for rclone 537 return 0 538} 539 540// Fsyncdir synchronizes directory contents. 541func (fsys *FS) Fsyncdir(path string, datasync bool, fh uint64) (errc int) { 542 defer log.Trace(path, "datasync=%v, fh=0x%X", datasync, fh)("errc=%d", &errc) 543 // This is a no-op for rclone 544 return 0 545} 546 547// Setxattr sets extended attributes. 548func (fsys *FS) Setxattr(path string, name string, value []byte, flags int) (errc int) { 549 return -fuse.ENOSYS 550} 551 552// Getxattr gets extended attributes. 553func (fsys *FS) Getxattr(path string, name string) (errc int, value []byte) { 554 return -fuse.ENOSYS, nil 555} 556 557// Removexattr removes extended attributes. 558func (fsys *FS) Removexattr(path string, name string) (errc int) { 559 return -fuse.ENOSYS 560} 561 562// Listxattr lists extended attributes. 563func (fsys *FS) Listxattr(path string, fill func(name string) bool) (errc int) { 564 return -fuse.ENOSYS 565} 566 567// Translate errors from mountlib 568func translateError(err error) (errc int) { 569 if err == nil { 570 return 0 571 } 572 switch errors.Cause(err) { 573 case vfs.OK: 574 return 0 575 case vfs.ENOENT, fs.ErrorDirNotFound, fs.ErrorObjectNotFound: 576 return -fuse.ENOENT 577 case vfs.EEXIST, fs.ErrorDirExists: 578 return -fuse.EEXIST 579 case vfs.EPERM, fs.ErrorPermissionDenied: 580 return -fuse.EPERM 581 case vfs.ECLOSED: 582 return -fuse.EBADF 583 case vfs.ENOTEMPTY: 584 return -fuse.ENOTEMPTY 585 case vfs.ESPIPE: 586 return -fuse.ESPIPE 587 case vfs.EBADF: 588 return -fuse.EBADF 589 case vfs.EROFS: 590 return -fuse.EROFS 591 case vfs.ENOSYS, fs.ErrorNotImplemented: 592 return -fuse.ENOSYS 593 case vfs.EINVAL: 594 return -fuse.EINVAL 595 } 596 fs.Errorf(nil, "IO error: %v", err) 597 return -fuse.EIO 598} 599 600// Translate Open Flags from FUSE to os (as used in the vfs layer) 601func translateOpenFlags(inFlags int) (outFlags int) { 602 switch inFlags & fuse.O_ACCMODE { 603 case fuse.O_RDONLY: 604 outFlags = os.O_RDONLY 605 case fuse.O_WRONLY: 606 outFlags = os.O_WRONLY 607 case fuse.O_RDWR: 608 outFlags = os.O_RDWR 609 } 610 if inFlags&fuse.O_APPEND != 0 { 611 outFlags |= os.O_APPEND 612 } 613 if inFlags&fuse.O_CREAT != 0 { 614 outFlags |= os.O_CREATE 615 } 616 if inFlags&fuse.O_EXCL != 0 { 617 outFlags |= os.O_EXCL 618 } 619 if inFlags&fuse.O_TRUNC != 0 { 620 outFlags |= os.O_TRUNC 621 } 622 // NB O_SYNC isn't defined by fuse 623 return outFlags 624} 625 626// Make sure interfaces are satisfied 627var ( 628 _ fuse.FileSystemInterface = (*FS)(nil) 629 _ fuse.FileSystemOpenEx = (*FS)(nil) 630 //_ fuse.FileSystemChflags = (*FS)(nil) 631 //_ fuse.FileSystemSetcrtime = (*FS)(nil) 632 //_ fuse.FileSystemSetchgtime = (*FS)(nil) 633) 634