github.com/rclone/rclone@v1.66.1-0.20240517100346-7b89735ae726/vfs/file.go (about) 1 package vfs 2 3 import ( 4 "context" 5 "errors" 6 "fmt" 7 "os" 8 "path" 9 "sync" 10 "sync/atomic" 11 "time" 12 13 "github.com/rclone/rclone/fs" 14 "github.com/rclone/rclone/fs/log" 15 "github.com/rclone/rclone/fs/operations" 16 "github.com/rclone/rclone/vfs/vfscommon" 17 ) 18 19 // The File object is tightly coupled to the Dir object. Since they 20 // both have locks there is plenty of potential for deadlocks. In 21 // order to mitigate this, we use the following conventions 22 // 23 // File may **only** call these methods from Dir with the File lock 24 // held. 25 // 26 // Dir.Fs 27 // Dir.VFS 28 // 29 // (As these are read only and do not need to take the Dir mutex.) 30 // 31 // File may **not** call any other Dir methods with the File lock 32 // held. This preserves total lock ordering and makes File subordinate 33 // to Dir as far as locking is concerned, preventing deadlocks. 34 // 35 // File may **not** read any members of Dir directly. 36 37 // File represents a file 38 type File struct { 39 inode uint64 // inode number - read only 40 size atomic.Int64 // size of file 41 42 muRW sync.Mutex // synchronize RWFileHandle.openPending(), RWFileHandle.close() and File.Remove 43 44 mu sync.RWMutex // protects the following 45 d *Dir // parent directory 46 dPath string // path of parent directory. NB dir rename means all Files are flushed 47 o fs.Object // NB o may be nil if file is being written 48 leaf string // leaf name of the object 49 writers []Handle // writers for this file 50 virtualModTime *time.Time // modtime for backends with Precision == fs.ModTimeNotSupported 51 pendingModTime time.Time // will be applied once o becomes available, i.e. after file was written 52 pendingRenameFun func(ctx context.Context) error // will be run/renamed after all writers close 53 sys atomic.Value // user defined info to be attached here 54 nwriters atomic.Int32 // len(writers) 55 appendMode bool // file was opened with O_APPEND 56 } 57 58 // newFile creates a new File 59 // 60 // o may be nil 61 func newFile(d *Dir, dPath string, o fs.Object, leaf string) *File { 62 f := &File{ 63 d: d, 64 dPath: dPath, 65 o: o, 66 leaf: leaf, 67 inode: newInode(), 68 } 69 if o != nil { 70 f.size.Store(o.Size()) 71 } 72 return f 73 } 74 75 // String converts it to printable 76 func (f *File) String() string { 77 if f == nil { 78 return "<nil *File>" 79 } 80 return f.Path() 81 } 82 83 // IsFile returns true for File - satisfies Node interface 84 func (f *File) IsFile() bool { 85 return true 86 } 87 88 // IsDir returns false for File - satisfies Node interface 89 func (f *File) IsDir() bool { 90 return false 91 } 92 93 // Mode bits of the file or directory - satisfies Node interface 94 func (f *File) Mode() (mode os.FileMode) { 95 f.mu.RLock() 96 defer f.mu.RUnlock() 97 mode = f.d.vfs.Opt.FilePerms 98 if f.appendMode { 99 mode |= os.ModeAppend 100 } 101 return mode 102 } 103 104 // Name (base) of the directory - satisfies Node interface 105 func (f *File) Name() (name string) { 106 f.mu.RLock() 107 defer f.mu.RUnlock() 108 return f.leaf 109 } 110 111 // _path returns the full path of the file 112 // use when lock is held 113 func (f *File) _path() string { 114 return path.Join(f.dPath, f.leaf) 115 } 116 117 // Path returns the full path of the file 118 func (f *File) Path() string { 119 f.mu.RLock() 120 dPath, leaf := f.dPath, f.leaf 121 f.mu.RUnlock() 122 return path.Join(dPath, leaf) 123 } 124 125 // Sys returns underlying data source (can be nil) - satisfies Node interface 126 func (f *File) Sys() interface{} { 127 return f.sys.Load() 128 } 129 130 // SetSys sets the underlying data source (can be nil) - satisfies Node interface 131 func (f *File) SetSys(x interface{}) { 132 f.sys.Store(x) 133 } 134 135 // Inode returns the inode number - satisfies Node interface 136 func (f *File) Inode() uint64 { 137 return f.inode 138 } 139 140 // Node returns the Node associated with this - satisfies Noder interface 141 func (f *File) Node() Node { 142 return f 143 } 144 145 // renameDir - call when parent directory has been renamed 146 func (f *File) renameDir(dPath string) { 147 f.mu.RLock() 148 f.dPath = dPath 149 f.mu.RUnlock() 150 } 151 152 // applyPendingRename runs a previously set rename operation if there are no 153 // more remaining writers. Call without lock held. 154 func (f *File) applyPendingRename() { 155 f.mu.RLock() 156 fun := f.pendingRenameFun 157 writing := f._writingInProgress() 158 f.mu.RUnlock() 159 if fun == nil || writing { 160 return 161 } 162 fs.Debugf(f.Path(), "Running delayed rename now") 163 if err := fun(context.TODO()); err != nil { 164 fs.Errorf(f.Path(), "delayed File.Rename error: %v", err) 165 } 166 } 167 168 // rename attempts to immediately rename a file if there are no open writers. 169 // Otherwise it will queue the rename operation on the remote until no writers 170 // remain. 171 func (f *File) rename(ctx context.Context, destDir *Dir, newName string) error { 172 f.mu.RLock() 173 d := f.d 174 oldPendingRenameFun := f.pendingRenameFun 175 f.mu.RUnlock() 176 177 if features := d.Fs().Features(); features.Move == nil && features.Copy == nil { 178 err := fmt.Errorf("Fs %q can't rename files (no server-side Move or Copy)", d.Fs()) 179 fs.Errorf(f.Path(), "Dir.Rename error: %v", err) 180 return err 181 } 182 183 oldPath := f.Path() 184 // File.mu is unlocked here to call Dir.Path() 185 newPath := path.Join(destDir.Path(), newName) 186 187 renameCall := func(ctx context.Context) (err error) { 188 // chain rename calls if any 189 if oldPendingRenameFun != nil { 190 err := oldPendingRenameFun(ctx) 191 if err != nil { 192 return err 193 } 194 } 195 196 f.mu.RLock() 197 o := f.o 198 d := f.d 199 f.mu.RUnlock() 200 var newObject fs.Object 201 // if o is nil then are writing the file so no need to rename the object 202 if o != nil { 203 if o.Remote() == newPath { 204 return nil // no need to rename 205 } 206 207 // do the move of the remote object 208 dstOverwritten, _ := d.Fs().NewObject(ctx, newPath) 209 newObject, err = operations.Move(ctx, d.Fs(), dstOverwritten, newPath, o) 210 if err != nil { 211 fs.Errorf(f.Path(), "File.Rename error: %v", err) 212 return err 213 } 214 215 // newObject can be nil here for example if --dry-run 216 if newObject == nil { 217 err = errors.New("rename failed: nil object returned") 218 fs.Errorf(f.Path(), "File.Rename %v", err) 219 return err 220 } 221 } 222 // Rename in the cache 223 if d.vfs.cache != nil && d.vfs.cache.Exists(oldPath) { 224 if err := d.vfs.cache.Rename(oldPath, newPath, newObject); err != nil { 225 fs.Infof(f.Path(), "File.Rename failed in Cache: %v", err) 226 } 227 } 228 // Update the node with the new details 229 fs.Debugf(f.Path(), "Updating file with %v %p", newObject, f) 230 // f.rename(destDir, newObject) 231 f.mu.Lock() 232 if newObject != nil { 233 f.o = newObject 234 } 235 f.pendingRenameFun = nil 236 f.mu.Unlock() 237 return nil 238 } 239 240 // rename the file object 241 dPath := destDir.Path() 242 f.mu.Lock() 243 f.d = destDir 244 f.dPath = dPath 245 f.leaf = newName 246 writing := f._writingInProgress() 247 f.mu.Unlock() 248 249 // Delay the rename if not using RW caching. For the minimal case we 250 // need to look in the cache to see if caching is in use. 251 CacheMode := d.vfs.Opt.CacheMode 252 if writing && 253 (CacheMode < vfscommon.CacheModeMinimal || 254 (CacheMode == vfscommon.CacheModeMinimal && !destDir.vfs.cache.Exists(oldPath))) { 255 fs.Debugf(oldPath, "File is currently open, delaying rename %p", f) 256 f.mu.Lock() 257 f.pendingRenameFun = renameCall 258 f.mu.Unlock() 259 return nil 260 } 261 262 return renameCall(ctx) 263 } 264 265 // addWriter adds a write handle to the file 266 func (f *File) addWriter(h Handle) { 267 f.mu.Lock() 268 f.writers = append(f.writers, h) 269 f.nwriters.Add(1) 270 f.mu.Unlock() 271 } 272 273 // delWriter removes a write handle from the file 274 func (f *File) delWriter(h Handle) { 275 f.mu.Lock() 276 defer f.applyPendingRename() 277 defer f.mu.Unlock() 278 var found = -1 279 for i := range f.writers { 280 if f.writers[i] == h { 281 found = i 282 break 283 } 284 } 285 if found >= 0 { 286 f.writers = append(f.writers[:found], f.writers[found+1:]...) 287 f.nwriters.Add(-1) 288 } else { 289 fs.Debugf(f._path(), "File.delWriter couldn't find handle") 290 } 291 } 292 293 // activeWriters returns the number of writers on the file 294 // 295 // Note that we don't take the mutex here. If we do then we can get a 296 // deadlock. 297 func (f *File) activeWriters() int { 298 return int(f.nwriters.Load()) 299 } 300 301 // _roundModTime rounds the time passed in to the Precision of the 302 // underlying Fs 303 // 304 // It should be called with the lock held 305 func (f *File) _roundModTime(modTime time.Time) time.Time { 306 precision := f.d.f.Precision() 307 if precision == fs.ModTimeNotSupported { 308 return modTime 309 } 310 return modTime.Truncate(precision) 311 } 312 313 // ModTime returns the modified time of the file 314 // 315 // if NoModTime is set then it returns the mod time of the directory 316 func (f *File) ModTime() (modTime time.Time) { 317 f.mu.RLock() 318 d, o, pendingModTime, virtualModTime := f.d, f.o, f.pendingModTime, f.virtualModTime 319 f.mu.RUnlock() 320 321 // Set the virtual modtime up for backends which don't support setting modtime 322 // 323 // Note that we only cache modtime values that we have returned to the OS 324 // if we haven't returned a value to the OS then we can change it 325 defer func() { 326 if f.d.f.Precision() == fs.ModTimeNotSupported && (virtualModTime == nil || !virtualModTime.Equal(modTime)) { 327 f.virtualModTime = &modTime 328 fs.Debugf(f._path(), "Set virtual modtime to %v", f.virtualModTime) 329 } 330 }() 331 332 if d.vfs.Opt.NoModTime { 333 return d.ModTime() 334 } 335 // Read the modtime from a dirty item if it exists 336 if f.d.vfs.Opt.CacheMode >= vfscommon.CacheModeMinimal { 337 if item := f.d.vfs.cache.DirtyItem(f._path()); item != nil { 338 modTime, err := item.GetModTime() 339 if err != nil { 340 fs.Errorf(f._path(), "ModTime: Item GetModTime failed: %v", err) 341 } else { 342 return f._roundModTime(modTime) 343 } 344 } 345 } 346 if !pendingModTime.IsZero() { 347 return f._roundModTime(pendingModTime) 348 } 349 if virtualModTime != nil && !virtualModTime.IsZero() { 350 fs.Debugf(f._path(), "Returning virtual modtime %v", f.virtualModTime) 351 return f._roundModTime(*virtualModTime) 352 } 353 if o == nil { 354 return time.Now() 355 } 356 return o.ModTime(context.TODO()) 357 } 358 359 // nonNegative returns 0 if i is -ve, i otherwise 360 func nonNegative(i int64) int64 { 361 if i >= 0 { 362 return i 363 } 364 return 0 365 } 366 367 // Size of the file 368 func (f *File) Size() int64 { 369 f.mu.RLock() 370 defer f.mu.RUnlock() 371 372 // Read the size from a dirty item if it exists 373 if f.d.vfs.Opt.CacheMode >= vfscommon.CacheModeMinimal { 374 if item := f.d.vfs.cache.DirtyItem(f._path()); item != nil { 375 size, err := item.GetSize() 376 if err != nil { 377 fs.Errorf(f._path(), "Size: Item GetSize failed: %v", err) 378 } else { 379 return size 380 } 381 } 382 } 383 384 // if o is nil it isn't valid yet or there are writers, so return the size so far 385 if f._writingInProgress() { 386 return f.size.Load() 387 } 388 return nonNegative(f.o.Size()) 389 } 390 391 // SetModTime sets the modtime for the file 392 // 393 // if NoModTime is set then it does nothing 394 func (f *File) SetModTime(modTime time.Time) error { 395 f.mu.Lock() 396 defer f.mu.Unlock() 397 if f.d.vfs.Opt.NoModTime { 398 return nil 399 } 400 if f.d.vfs.Opt.ReadOnly { 401 return EROFS 402 } 403 404 f.pendingModTime = modTime 405 406 // set the time of the file in the cache 407 if f.d.vfs.cache != nil && f.d.vfs.cache.Exists(f._path()) { 408 f.d.vfs.cache.SetModTime(f._path(), f.pendingModTime) 409 } 410 411 // Only update the ModTime when there are no writers, setObject will do it 412 if !f._writingInProgress() { 413 return f._applyPendingModTime() 414 } 415 416 // queue up for later, hoping f.o becomes available 417 return nil 418 } 419 420 // Apply a pending mod time 421 // Call with the mutex held 422 func (f *File) _applyPendingModTime() error { 423 if f.pendingModTime.IsZero() { 424 return nil 425 } 426 defer func() { f.pendingModTime = time.Time{} }() 427 428 if f.o == nil { 429 return errors.New("cannot apply ModTime, file object is not available") 430 } 431 432 dt := f.pendingModTime.Sub(f.o.ModTime(context.Background())) 433 modifyWindow := f.o.Fs().Precision() 434 if dt < modifyWindow && dt > -modifyWindow { 435 fs.Debugf(f.o, "Not setting pending mod time %v as it is already set", f.pendingModTime) 436 return nil 437 } 438 439 // set the time of the object 440 err := f.o.SetModTime(context.TODO(), f.pendingModTime) 441 switch err { 442 case nil: 443 fs.Debugf(f.o, "Applied pending mod time %v OK", f.pendingModTime) 444 case fs.ErrorCantSetModTime, fs.ErrorCantSetModTimeWithoutDelete: 445 // do nothing, in order to not break "touch somefile" if it exists already 446 default: 447 fs.Errorf(f.o, "Failed to apply pending mod time %v: %v", f.pendingModTime, err) 448 return err 449 } 450 451 return nil 452 } 453 454 // Apply a pending mod time 455 func (f *File) applyPendingModTime() error { 456 f.mu.Lock() 457 defer f.mu.Unlock() 458 return f._applyPendingModTime() 459 } 460 461 // _writingInProgress returns true of there are any open writers 462 // Call with read lock held 463 func (f *File) _writingInProgress() bool { 464 return f.o == nil || len(f.writers) != 0 465 } 466 467 // writingInProgress returns true of there are any open writers 468 func (f *File) writingInProgress() bool { 469 f.mu.RLock() 470 defer f.mu.RUnlock() 471 return f.o == nil || len(f.writers) != 0 472 } 473 474 // Update the size while writing 475 func (f *File) setSize(n int64) { 476 f.size.Store(n) 477 } 478 479 // Update the object when written and add it to the directory 480 func (f *File) setObject(o fs.Object) { 481 f.mu.Lock() 482 f.o = o 483 _ = f._applyPendingModTime() 484 d := f.d 485 f.mu.Unlock() 486 487 // Release File.mu before calling Dir method 488 d.addObject(f) 489 } 490 491 // Update the object but don't update the directory cache - for use by 492 // the directory cache 493 func (f *File) setObjectNoUpdate(o fs.Object) { 494 f.mu.Lock() 495 f.o = o 496 f.virtualModTime = nil 497 fs.Debugf(f._path(), "Reset virtual modtime") 498 f.mu.Unlock() 499 } 500 501 // Get the current fs.Object - may be nil 502 func (f *File) getObject() fs.Object { 503 f.mu.RLock() 504 defer f.mu.RUnlock() 505 return f.o 506 } 507 508 // exists returns whether the file exists already 509 func (f *File) exists() bool { 510 f.mu.RLock() 511 defer f.mu.RUnlock() 512 return f.o != nil 513 } 514 515 // Wait for f.o to become non nil for a short time returning it or an 516 // error. Use when opening a read handle. 517 // 518 // Call without the mutex held 519 func (f *File) waitForValidObject() (o fs.Object, err error) { 520 for i := 0; i < 50; i++ { 521 f.mu.RLock() 522 o = f.o 523 nwriters := len(f.writers) 524 f.mu.RUnlock() 525 if o != nil { 526 return o, nil 527 } 528 if nwriters == 0 { 529 return nil, errors.New("can't open file - writer failed") 530 } 531 time.Sleep(100 * time.Millisecond) 532 } 533 return nil, ENOENT 534 } 535 536 // openRead open the file for read 537 func (f *File) openRead() (fh *ReadFileHandle, err error) { 538 // if o is nil it isn't valid yet 539 _, err = f.waitForValidObject() 540 if err != nil { 541 return nil, err 542 } 543 // fs.Debugf(f.Path(), "File.openRead") 544 545 fh, err = newReadFileHandle(f) 546 if err != nil { 547 fs.Debugf(f.Path(), "File.openRead failed: %v", err) 548 return nil, err 549 } 550 return fh, nil 551 } 552 553 // openWrite open the file for write 554 func (f *File) openWrite(flags int) (fh *WriteFileHandle, err error) { 555 f.mu.RLock() 556 d := f.d 557 f.mu.RUnlock() 558 559 if d.vfs.Opt.ReadOnly { 560 return nil, EROFS 561 } 562 // fs.Debugf(f.Path(), "File.openWrite") 563 564 fh, err = newWriteFileHandle(d, f, f.Path(), flags) 565 if err != nil { 566 fs.Debugf(f.Path(), "File.openWrite failed: %v", err) 567 return nil, err 568 } 569 return fh, nil 570 } 571 572 // openRW open the file for read and write using a temporary file 573 // 574 // It uses the open flags passed in. 575 func (f *File) openRW(flags int) (fh *RWFileHandle, err error) { 576 f.mu.RLock() 577 d := f.d 578 f.mu.RUnlock() 579 580 // FIXME chunked 581 if flags&accessModeMask != os.O_RDONLY && d.vfs.Opt.ReadOnly { 582 return nil, EROFS 583 } 584 // fs.Debugf(f.Path(), "File.openRW") 585 586 fh, err = newRWFileHandle(d, f, flags) 587 if err != nil { 588 fs.Debugf(f.Path(), "File.openRW failed: %v", err) 589 return nil, err 590 } 591 return fh, nil 592 } 593 594 // Sync the file 595 // 596 // Note that we don't do anything except return OK 597 func (f *File) Sync() error { 598 return nil 599 } 600 601 // Remove the file 602 func (f *File) Remove() (err error) { 603 defer log.Trace(f.Path(), "")("err=%v", &err) 604 f.mu.RLock() 605 d := f.d 606 f.mu.RUnlock() 607 608 if d.vfs.Opt.ReadOnly { 609 return EROFS 610 } 611 612 // Remove the object from the cache 613 wasWriting := false 614 if d.vfs.cache != nil && d.vfs.cache.Exists(f.Path()) { 615 wasWriting = d.vfs.cache.Remove(f.Path()) 616 } 617 618 f.muRW.Lock() // muRW must be locked before mu to avoid 619 f.mu.Lock() // deadlock in RWFileHandle.openPending and .close 620 if f.o != nil { 621 err = f.o.Remove(context.TODO()) 622 } 623 f.mu.Unlock() 624 f.muRW.Unlock() 625 if err != nil { 626 if wasWriting { 627 // Ignore error deleting file if was writing it as it may not be uploaded yet 628 err = nil 629 fs.Debugf(f._path(), "Ignoring File.Remove file error as uploading: %v", err) 630 } else { 631 fs.Debugf(f._path(), "File.Remove file error: %v", err) 632 } 633 } 634 635 // Remove the item from the directory listing 636 // called with File.mu released when there is no error removing the underlying file 637 if err == nil { 638 d.delObject(f.Name()) 639 } 640 return err 641 } 642 643 // RemoveAll the file - same as remove for files 644 func (f *File) RemoveAll() error { 645 return f.Remove() 646 } 647 648 // DirEntry returns the underlying fs.DirEntry - may be nil 649 func (f *File) DirEntry() (entry fs.DirEntry) { 650 f.mu.RLock() 651 defer f.mu.RUnlock() 652 return f.o 653 } 654 655 // Dir returns the directory this file is in 656 func (f *File) Dir() *Dir { 657 f.mu.RLock() 658 defer f.mu.RUnlock() 659 return f.d 660 } 661 662 // VFS returns the instance of the VFS 663 func (f *File) VFS() *VFS { 664 f.mu.RLock() 665 defer f.mu.RUnlock() 666 return f.d.vfs 667 } 668 669 // Fs returns the underlying Fs for the file 670 func (f *File) Fs() fs.Fs { 671 f.mu.RLock() 672 defer f.mu.RUnlock() 673 return f.d.Fs() 674 } 675 676 // Open a file according to the flags provided 677 // 678 // O_RDONLY open the file read-only. 679 // O_WRONLY open the file write-only. 680 // O_RDWR open the file read-write. 681 // 682 // O_APPEND append data to the file when writing. 683 // O_CREATE create a new file if none exists. 684 // O_EXCL used with O_CREATE, file must not exist 685 // O_SYNC open for synchronous I/O. 686 // O_TRUNC if possible, truncate file when opened 687 // 688 // We ignore O_SYNC and O_EXCL 689 func (f *File) Open(flags int) (fd Handle, err error) { 690 defer log.Trace(f.Path(), "flags=%s", decodeOpenFlags(flags))("fd=%v, err=%v", &fd, &err) 691 var ( 692 write bool // if set need write support 693 read bool // if set need read support 694 rdwrMode = flags & accessModeMask 695 ) 696 697 // http://pubs.opengroup.org/onlinepubs/7908799/xsh/open.html 698 // The result of using O_TRUNC with O_RDONLY is undefined. 699 // Linux seems to truncate the file, but we prefer to return EINVAL 700 if rdwrMode == os.O_RDONLY && flags&os.O_TRUNC != 0 { 701 return nil, EINVAL 702 } 703 704 // Figure out the read/write intents 705 switch { 706 case rdwrMode == os.O_RDONLY: 707 read = true 708 case rdwrMode == os.O_WRONLY: 709 write = true 710 case rdwrMode == os.O_RDWR: 711 read = true 712 write = true 713 default: 714 fs.Debugf(f.Path(), "Can't figure out how to open with flags: 0x%X", flags) 715 return nil, EPERM 716 } 717 718 // If append is set then set read to force openRW 719 if flags&os.O_APPEND != 0 { 720 read = true 721 f.mu.Lock() 722 f.appendMode = true 723 f.mu.Unlock() 724 } 725 726 // If truncate is set then set write to force openRW 727 if flags&os.O_TRUNC != 0 { 728 write = true 729 } 730 731 // If create is set then set write to force openRW 732 if flags&os.O_CREATE != 0 { 733 write = true 734 } 735 736 // Open the correct sort of handle 737 f.mu.RLock() 738 d := f.d 739 f.mu.RUnlock() 740 CacheMode := d.vfs.Opt.CacheMode 741 if CacheMode >= vfscommon.CacheModeMinimal && (d.vfs.cache.InUse(f.Path()) || d.vfs.cache.Exists(f.Path())) { 742 fd, err = f.openRW(flags) 743 } else if read && write { 744 if CacheMode >= vfscommon.CacheModeMinimal { 745 fd, err = f.openRW(flags) 746 } else { 747 // Open write only and hope the user doesn't 748 // want to read. If they do they will get an 749 // EPERM plus an Error log. 750 fd, err = f.openWrite(flags) 751 } 752 } else if write { 753 if CacheMode >= vfscommon.CacheModeWrites { 754 fd, err = f.openRW(flags) 755 } else { 756 fd, err = f.openWrite(flags) 757 } 758 } else if read { 759 if CacheMode >= vfscommon.CacheModeFull { 760 fd, err = f.openRW(flags) 761 } else { 762 fd, err = f.openRead() 763 } 764 } else { 765 fs.Debugf(f.Path(), "Can't figure out how to open with flags: 0x%X", flags) 766 return nil, EPERM 767 } 768 // if creating a file, add the file to the directory 769 if err == nil && flags&os.O_CREATE != 0 { 770 // called without File.mu held 771 d.addObject(f) 772 } 773 return fd, err 774 } 775 776 // Truncate changes the size of the named file. 777 func (f *File) Truncate(size int64) (err error) { 778 // make a copy of fh.writers with the lock held then unlock so 779 // we can call other file methods. 780 f.mu.Lock() 781 writers := make([]Handle, len(f.writers)) 782 copy(writers, f.writers) 783 f.mu.Unlock() 784 785 // If have writers then call truncate for each writer 786 if len(writers) != 0 { 787 var openWriters = len(writers) 788 fs.Debugf(f.Path(), "Truncating %d file handles", len(writers)) 789 for _, h := range writers { 790 truncateErr := h.Truncate(size) 791 if truncateErr == ECLOSED { 792 // Ignore ECLOSED since file handle can get closed while this is running 793 openWriters-- 794 } else if truncateErr != nil { 795 err = truncateErr 796 } 797 } 798 // If at least one open writer return here 799 if openWriters > 0 { 800 return err 801 } 802 } 803 804 // if o is nil it isn't valid yet 805 o, err := f.waitForValidObject() 806 if err != nil { 807 return err 808 } 809 810 // If no writers, and size is already correct then all done 811 if o.Size() == size { 812 return nil 813 } 814 815 fs.Debugf(f.Path(), "Truncating file") 816 817 // Otherwise if no writers then truncate the file by opening 818 // the file and truncating it. 819 flags := os.O_WRONLY 820 if size == 0 { 821 flags |= os.O_TRUNC 822 } 823 fh, err := f.Open(flags) 824 if err != nil { 825 return err 826 } 827 defer fs.CheckClose(fh, &err) 828 if size != 0 { 829 return fh.Truncate(size) 830 } 831 return nil 832 }