github.com/10XDev/rclone@v1.52.3-0.20200626220027-16af9ab76b2a/vfs/vfs.go (about) 1 // Package vfs provides a virtual filing system layer over rclone's 2 // native objects. 3 // 4 // It attempts to behave in a similar way to Go's filing system 5 // manipulation code in the os package. The same named function 6 // should behave in an identical fashion. The objects also obey Go's 7 // standard interfaces. 8 // 9 // Note that paths don't start or end with /, so the root directory 10 // may be referred to as "". However Stat strips slashes so you can 11 // use paths with slashes in. 12 // 13 // It also includes directory caching 14 // 15 // The vfs package returns Error values to signal precisely which 16 // error conditions have ocurred. It may also return general errors 17 // it receives. It tries to use os Error values (eg os.ErrExist) 18 // where possible. 19 20 //go:generate sh -c "go run make_open_tests.go | gofmt > open_test.go" 21 22 package vfs 23 24 import ( 25 "context" 26 "fmt" 27 "io/ioutil" 28 "os" 29 "path" 30 "sort" 31 "strings" 32 "sync" 33 "sync/atomic" 34 "time" 35 36 "github.com/rclone/rclone/fs" 37 "github.com/rclone/rclone/fs/cache" 38 "github.com/rclone/rclone/fs/log" 39 "github.com/rclone/rclone/vfs/vfscache" 40 "github.com/rclone/rclone/vfs/vfscommon" 41 ) 42 43 // Node represents either a directory (*Dir) or a file (*File) 44 type Node interface { 45 os.FileInfo 46 IsFile() bool 47 Inode() uint64 48 SetModTime(modTime time.Time) error 49 Sync() error 50 Remove() error 51 RemoveAll() error 52 DirEntry() fs.DirEntry 53 VFS() *VFS 54 Open(flags int) (Handle, error) 55 Truncate(size int64) error 56 Path() string 57 SetSys(interface{}) 58 } 59 60 // Check interfaces 61 var ( 62 _ Node = (*File)(nil) 63 _ Node = (*Dir)(nil) 64 ) 65 66 // Nodes is a slice of Node 67 type Nodes []Node 68 69 // Sort functions 70 func (ns Nodes) Len() int { return len(ns) } 71 func (ns Nodes) Swap(i, j int) { ns[i], ns[j] = ns[j], ns[i] } 72 func (ns Nodes) Less(i, j int) bool { return ns[i].Path() < ns[j].Path() } 73 74 // Noder represents something which can return a node 75 type Noder interface { 76 fmt.Stringer 77 Node() Node 78 } 79 80 // Check interfaces 81 var ( 82 _ Noder = (*File)(nil) 83 _ Noder = (*Dir)(nil) 84 _ Noder = (*ReadFileHandle)(nil) 85 _ Noder = (*WriteFileHandle)(nil) 86 _ Noder = (*RWFileHandle)(nil) 87 _ Noder = (*DirHandle)(nil) 88 ) 89 90 // OsFiler is the methods on *os.File 91 type OsFiler interface { 92 Chdir() error 93 Chmod(mode os.FileMode) error 94 Chown(uid, gid int) error 95 Close() error 96 Fd() uintptr 97 Name() string 98 Read(b []byte) (n int, err error) 99 ReadAt(b []byte, off int64) (n int, err error) 100 Readdir(n int) ([]os.FileInfo, error) 101 Readdirnames(n int) (names []string, err error) 102 Seek(offset int64, whence int) (ret int64, err error) 103 Stat() (os.FileInfo, error) 104 Sync() error 105 Truncate(size int64) error 106 Write(b []byte) (n int, err error) 107 WriteAt(b []byte, off int64) (n int, err error) 108 WriteString(s string) (n int, err error) 109 } 110 111 // Handle is the interface statisified by open files or directories. 112 // It is the methods on *os.File, plus a few more useful for FUSE 113 // filingsystems. Not all of them are supported. 114 type Handle interface { 115 OsFiler 116 // Additional methods useful for FUSE filesystems 117 Flush() error 118 Release() error 119 Node() Node 120 // Size() int64 121 } 122 123 // baseHandle implements all the missing methods 124 type baseHandle struct{} 125 126 func (h baseHandle) Chdir() error { return ENOSYS } 127 func (h baseHandle) Chmod(mode os.FileMode) error { return ENOSYS } 128 func (h baseHandle) Chown(uid, gid int) error { return ENOSYS } 129 func (h baseHandle) Close() error { return ENOSYS } 130 func (h baseHandle) Fd() uintptr { return 0 } 131 func (h baseHandle) Name() string { return "" } 132 func (h baseHandle) Read(b []byte) (n int, err error) { return 0, ENOSYS } 133 func (h baseHandle) ReadAt(b []byte, off int64) (n int, err error) { return 0, ENOSYS } 134 func (h baseHandle) Readdir(n int) ([]os.FileInfo, error) { return nil, ENOSYS } 135 func (h baseHandle) Readdirnames(n int) (names []string, err error) { return nil, ENOSYS } 136 func (h baseHandle) Seek(offset int64, whence int) (ret int64, err error) { return 0, ENOSYS } 137 func (h baseHandle) Stat() (os.FileInfo, error) { return nil, ENOSYS } 138 func (h baseHandle) Sync() error { return nil } 139 func (h baseHandle) Truncate(size int64) error { return ENOSYS } 140 func (h baseHandle) Write(b []byte) (n int, err error) { return 0, ENOSYS } 141 func (h baseHandle) WriteAt(b []byte, off int64) (n int, err error) { return 0, ENOSYS } 142 func (h baseHandle) WriteString(s string) (n int, err error) { return 0, ENOSYS } 143 func (h baseHandle) Flush() (err error) { return ENOSYS } 144 func (h baseHandle) Release() (err error) { return ENOSYS } 145 func (h baseHandle) Node() Node { return nil } 146 147 //func (h baseHandle) Size() int64 { return 0 } 148 149 // Check interfaces 150 var ( 151 _ OsFiler = (*os.File)(nil) 152 _ Handle = (*baseHandle)(nil) 153 _ Handle = (*ReadFileHandle)(nil) 154 _ Handle = (*WriteFileHandle)(nil) 155 _ Handle = (*DirHandle)(nil) 156 ) 157 158 // VFS represents the top level filing system 159 type VFS struct { 160 f fs.Fs 161 root *Dir 162 Opt vfscommon.Options 163 cache *vfscache.Cache 164 cancel context.CancelFunc 165 usageMu sync.Mutex 166 usageTime time.Time 167 usage *fs.Usage 168 pollChan chan time.Duration 169 } 170 171 // New creates a new VFS and root directory. If opt is nil, then 172 // DefaultOpt will be used 173 func New(f fs.Fs, opt *vfscommon.Options) *VFS { 174 fsDir := fs.NewDir("", time.Now()) 175 vfs := &VFS{ 176 f: f, 177 } 178 179 // Make a copy of the options 180 if opt != nil { 181 vfs.Opt = *opt 182 } else { 183 vfs.Opt = vfscommon.DefaultOpt 184 } 185 186 // Mask the permissions with the umask 187 vfs.Opt.DirPerms &= ^os.FileMode(vfs.Opt.Umask) 188 vfs.Opt.FilePerms &= ^os.FileMode(vfs.Opt.Umask) 189 190 // Make sure directories are returned as directories 191 vfs.Opt.DirPerms |= os.ModeDir 192 193 // Create root directory 194 vfs.root = newDir(vfs, f, nil, fsDir) 195 196 // Start polling function 197 if do := vfs.f.Features().ChangeNotify; do != nil { 198 vfs.pollChan = make(chan time.Duration) 199 do(context.TODO(), vfs.root.changeNotify, vfs.pollChan) 200 vfs.pollChan <- vfs.Opt.PollInterval 201 } else { 202 fs.Infof(f, "poll-interval is not supported by this remote") 203 } 204 205 vfs.SetCacheMode(vfs.Opt.CacheMode) 206 207 // add the remote control 208 vfs.addRC() 209 210 // Pin the Fs into the cache so that when we use cache.NewFs 211 // with the same remote string we get this one. The Pin is 212 // removed by Shutdown 213 cache.Pin(f) 214 return vfs 215 } 216 217 // Fs returns the Fs passed into the New call 218 func (vfs *VFS) Fs() fs.Fs { 219 return vfs.f 220 } 221 222 // SetCacheMode change the cache mode 223 func (vfs *VFS) SetCacheMode(cacheMode vfscommon.CacheMode) { 224 vfs.Shutdown() 225 vfs.cache = nil 226 if cacheMode > vfscommon.CacheModeOff { 227 ctx, cancel := context.WithCancel(context.Background()) 228 cache, err := vfscache.New(ctx, vfs.f, &vfs.Opt) // FIXME pass on context or get from Opt? 229 if err != nil { 230 fs.Errorf(nil, "Failed to create vfs cache - disabling: %v", err) 231 vfs.Opt.CacheMode = vfscommon.CacheModeOff 232 cancel() 233 return 234 } 235 vfs.Opt.CacheMode = cacheMode 236 vfs.cancel = cancel 237 vfs.cache = cache 238 } 239 } 240 241 // Shutdown stops any background go-routines 242 func (vfs *VFS) Shutdown() { 243 // Unpin the Fs from the cache 244 cache.Unpin(vfs.f) 245 if vfs.cancel != nil { 246 vfs.cancel() 247 vfs.cancel = nil 248 } 249 } 250 251 // CleanUp deletes the contents of the on disk cache 252 func (vfs *VFS) CleanUp() error { 253 if vfs.Opt.CacheMode == vfscommon.CacheModeOff { 254 return nil 255 } 256 return vfs.cache.CleanUp() 257 } 258 259 // FlushDirCache empties the directory cache 260 func (vfs *VFS) FlushDirCache() { 261 vfs.root.ForgetAll() 262 } 263 264 // WaitForWriters sleeps until all writers have finished or 265 // time.Duration has elapsed 266 func (vfs *VFS) WaitForWriters(timeout time.Duration) { 267 defer log.Trace(nil, "timeout=%v", timeout)("") 268 const tickTime = 1 * time.Second 269 deadline := time.NewTimer(timeout) 270 defer deadline.Stop() 271 tick := time.NewTimer(tickTime) 272 defer tick.Stop() 273 tick.Stop() 274 for { 275 writers := vfs.root.countActiveWriters() 276 if writers == 0 { 277 return 278 } 279 fs.Debugf(nil, "Still %d writers active, waiting %v", writers, tickTime) 280 tick.Reset(tickTime) 281 select { 282 case <-tick.C: 283 break 284 case <-deadline.C: 285 fs.Errorf(nil, "Exiting even though %d writers are active after %v", writers, timeout) 286 return 287 } 288 } 289 } 290 291 // Root returns the root node 292 func (vfs *VFS) Root() (*Dir, error) { 293 // fs.Debugf(vfs.f, "Root()") 294 return vfs.root, nil 295 } 296 297 var inodeCount uint64 298 299 // newInode creates a new unique inode number 300 func newInode() (inode uint64) { 301 return atomic.AddUint64(&inodeCount, 1) 302 } 303 304 // Stat finds the Node by path starting from the root 305 // 306 // It is the equivalent of os.Stat - Node contains the os.FileInfo 307 // interface. 308 func (vfs *VFS) Stat(path string) (node Node, err error) { 309 path = strings.Trim(path, "/") 310 node = vfs.root 311 for path != "" { 312 i := strings.IndexRune(path, '/') 313 var name string 314 if i < 0 { 315 name, path = path, "" 316 } else { 317 name, path = path[:i], path[i+1:] 318 } 319 if name == "" { 320 continue 321 } 322 dir, ok := node.(*Dir) 323 if !ok { 324 // We need to look in a directory, but found a file 325 return nil, ENOENT 326 } 327 node, err = dir.Stat(name) 328 if err != nil { 329 return nil, err 330 } 331 } 332 return 333 } 334 335 // StatParent finds the parent directory and the leaf name of a path 336 func (vfs *VFS) StatParent(name string) (dir *Dir, leaf string, err error) { 337 name = strings.Trim(name, "/") 338 parent, leaf := path.Split(name) 339 node, err := vfs.Stat(parent) 340 if err != nil { 341 return nil, "", err 342 } 343 if node.IsFile() { 344 return nil, "", os.ErrExist 345 } 346 dir = node.(*Dir) 347 return dir, leaf, nil 348 } 349 350 // decodeOpenFlags returns a string representing the open flags 351 func decodeOpenFlags(flags int) string { 352 var out []string 353 rdwrMode := flags & accessModeMask 354 switch rdwrMode { 355 case os.O_RDONLY: 356 out = append(out, "O_RDONLY") 357 case os.O_WRONLY: 358 out = append(out, "O_WRONLY") 359 case os.O_RDWR: 360 out = append(out, "O_RDWR") 361 default: 362 out = append(out, fmt.Sprintf("0x%X", rdwrMode)) 363 } 364 if flags&os.O_APPEND != 0 { 365 out = append(out, "O_APPEND") 366 } 367 if flags&os.O_CREATE != 0 { 368 out = append(out, "O_CREATE") 369 } 370 if flags&os.O_EXCL != 0 { 371 out = append(out, "O_EXCL") 372 } 373 if flags&os.O_SYNC != 0 { 374 out = append(out, "O_SYNC") 375 } 376 if flags&os.O_TRUNC != 0 { 377 out = append(out, "O_TRUNC") 378 } 379 flags &^= accessModeMask | os.O_APPEND | os.O_CREATE | os.O_EXCL | os.O_SYNC | os.O_TRUNC 380 if flags != 0 { 381 out = append(out, fmt.Sprintf("0x%X", flags)) 382 } 383 return strings.Join(out, "|") 384 } 385 386 // OpenFile a file according to the flags and perm provided 387 func (vfs *VFS) OpenFile(name string, flags int, perm os.FileMode) (fd Handle, err error) { 388 defer log.Trace(name, "flags=%s, perm=%v", decodeOpenFlags(flags), perm)("fd=%v, err=%v", &fd, &err) 389 390 // http://pubs.opengroup.org/onlinepubs/7908799/xsh/open.html 391 // The result of using O_TRUNC with O_RDONLY is undefined. 392 // Linux seems to truncate the file, but we prefer to return EINVAL 393 if flags&accessModeMask == os.O_RDONLY && flags&os.O_TRUNC != 0 { 394 return nil, EINVAL 395 } 396 397 node, err := vfs.Stat(name) 398 if err != nil { 399 if err != ENOENT || flags&os.O_CREATE == 0 { 400 return nil, err 401 } 402 // If not found and O_CREATE then create the file 403 dir, leaf, err := vfs.StatParent(name) 404 if err != nil { 405 return nil, err 406 } 407 node, err = dir.Create(leaf, flags) 408 if err != nil { 409 return nil, err 410 } 411 } 412 return node.Open(flags) 413 } 414 415 // Open opens the named file for reading. If successful, methods on 416 // the returned file can be used for reading; the associated file 417 // descriptor has mode O_RDONLY. 418 func (vfs *VFS) Open(name string) (Handle, error) { 419 return vfs.OpenFile(name, os.O_RDONLY, 0) 420 } 421 422 // Create creates the named file with mode 0666 (before umask), truncating 423 // it if it already exists. If successful, methods on the returned 424 // File can be used for I/O; the associated file descriptor has mode 425 // O_RDWR. 426 func (vfs *VFS) Create(name string) (Handle, error) { 427 return vfs.OpenFile(name, os.O_RDWR|os.O_CREATE|os.O_TRUNC, 0666) 428 } 429 430 // Rename oldName to newName 431 func (vfs *VFS) Rename(oldName, newName string) error { 432 // find the parent directories 433 oldDir, oldLeaf, err := vfs.StatParent(oldName) 434 if err != nil { 435 return err 436 } 437 newDir, newLeaf, err := vfs.StatParent(newName) 438 if err != nil { 439 return err 440 } 441 err = oldDir.Rename(oldLeaf, newLeaf, newDir) 442 if err != nil { 443 return err 444 } 445 return nil 446 } 447 448 // This works out the missing values from (total, used, free) using 449 // unknownFree as the intended free space 450 func fillInMissingSizes(total, used, free, unknownFree int64) (newTotal, newUsed, newFree int64) { 451 if total < 0 { 452 if free >= 0 { 453 total = free 454 } else { 455 total = unknownFree 456 } 457 if used >= 0 { 458 total += used 459 } 460 } 461 // total is now defined 462 if used < 0 { 463 if free >= 0 { 464 used = total - free 465 } else { 466 used = 0 467 } 468 } 469 // used is now defined 470 if free < 0 { 471 free = total - used 472 } 473 return total, used, free 474 } 475 476 // If the total size isn't known then we will aim for this many bytes free (1PB) 477 const unknownFreeBytes = 1 << 50 478 479 // Statfs returns into about the filing system if known 480 // 481 // The values will be -1 if they aren't known 482 // 483 // This information is cached for the DirCacheTime interval 484 func (vfs *VFS) Statfs() (total, used, free int64) { 485 // defer log.Trace("/", "")("total=%d, used=%d, free=%d", &total, &used, &free) 486 vfs.usageMu.Lock() 487 defer vfs.usageMu.Unlock() 488 total, used, free = -1, -1, -1 489 doAbout := vfs.f.Features().About 490 if doAbout != nil && (vfs.usageTime.IsZero() || time.Since(vfs.usageTime) >= vfs.Opt.DirCacheTime) { 491 var err error 492 vfs.usage, err = doAbout(context.TODO()) 493 vfs.usageTime = time.Now() 494 if err != nil { 495 fs.Errorf(vfs.f, "Statfs failed: %v", err) 496 return 497 } 498 } 499 if u := vfs.usage; u != nil { 500 if u.Total != nil { 501 total = *u.Total 502 } 503 if u.Free != nil { 504 free = *u.Free 505 } 506 if u.Used != nil { 507 used = *u.Used 508 } 509 } 510 total, used, free = fillInMissingSizes(total, used, free, unknownFreeBytes) 511 return 512 } 513 514 // Remove removes the named file or (empty) directory. 515 func (vfs *VFS) Remove(name string) error { 516 node, err := vfs.Stat(name) 517 if err != nil { 518 return err 519 } 520 err = node.Remove() 521 if err != nil { 522 return err 523 } 524 return nil 525 } 526 527 // Chtimes changes the access and modification times of the named file, similar 528 // to the Unix utime() or utimes() functions. 529 // 530 // The underlying filesystem may truncate or round the values to a less precise 531 // time unit. 532 func (vfs *VFS) Chtimes(name string, atime time.Time, mtime time.Time) error { 533 node, err := vfs.Stat(name) 534 if err != nil { 535 return err 536 } 537 err = node.SetModTime(mtime) 538 if err != nil { 539 return err 540 } 541 return nil 542 } 543 544 // Mkdir creates a new directory with the specified name and permission bits 545 // (before umask). 546 func (vfs *VFS) Mkdir(name string, perm os.FileMode) error { 547 dir, leaf, err := vfs.StatParent(name) 548 if err != nil { 549 return err 550 } 551 _, err = dir.Mkdir(leaf) 552 if err != nil { 553 return err 554 } 555 return nil 556 } 557 558 // ReadDir reads the directory named by dirname and returns 559 // a list of directory entries sorted by filename. 560 func (vfs *VFS) ReadDir(dirname string) ([]os.FileInfo, error) { 561 f, err := vfs.Open(dirname) 562 if err != nil { 563 return nil, err 564 } 565 list, err := f.Readdir(-1) 566 closeErr := f.Close() 567 if err != nil { 568 return nil, err 569 } 570 if closeErr != nil { 571 return nil, closeErr 572 } 573 sort.Slice(list, func(i, j int) bool { return list[i].Name() < list[j].Name() }) 574 return list, nil 575 } 576 577 // ReadFile reads the file named by filename and returns the contents. 578 // A successful call returns err == nil, not err == EOF. Because ReadFile 579 // reads the whole file, it does not treat an EOF from Read as an error 580 // to be reported. 581 func (vfs *VFS) ReadFile(filename string) (b []byte, err error) { 582 f, err := vfs.Open(filename) 583 if err != nil { 584 return nil, err 585 } 586 defer fs.CheckClose(f, &err) 587 return ioutil.ReadAll(f) 588 }