github.com/xhghs/rclone@v1.51.1-0.20200430155106-e186a28cced8/vfs/read_write.go (about) 1 package vfs 2 3 import ( 4 "context" 5 "fmt" 6 "io" 7 "io/ioutil" 8 "os" 9 "runtime" 10 "sync" 11 12 "github.com/pkg/errors" 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/lib/file" 17 ) 18 19 // RWFileHandle is a handle that can be open for read and write. 20 // 21 // It will be open to a temporary file which, when closed, will be 22 // transferred to the remote. 23 type RWFileHandle struct { 24 *os.File 25 mu sync.Mutex 26 closed bool // set if handle has been closed 27 file *File 28 d *Dir 29 opened bool 30 flags int // open flags 31 writeCalled bool // if any Write() methods have been called 32 changed bool // file contents was changed in any other way 33 } 34 35 // Check interfaces 36 var ( 37 _ io.Reader = (*RWFileHandle)(nil) 38 _ io.ReaderAt = (*RWFileHandle)(nil) 39 _ io.Writer = (*RWFileHandle)(nil) 40 _ io.WriterAt = (*RWFileHandle)(nil) 41 _ io.Seeker = (*RWFileHandle)(nil) 42 _ io.Closer = (*RWFileHandle)(nil) 43 ) 44 45 func newRWFileHandle(d *Dir, f *File, flags int) (fh *RWFileHandle, err error) { 46 // if O_CREATE and O_EXCL are set and if path already exists, then return EEXIST 47 if flags&(os.O_CREATE|os.O_EXCL) == os.O_CREATE|os.O_EXCL && f.exists() { 48 return nil, EEXIST 49 } 50 51 fh = &RWFileHandle{ 52 file: f, 53 d: d, 54 flags: flags, 55 } 56 57 // mark the file as open in the cache - must be done before the mkdir 58 fh.d.vfs.cache.open(fh.file.Path()) 59 60 // Make a place for the file 61 _, err = d.vfs.cache.mkdir(fh.file.Path()) 62 if err != nil { 63 fh.d.vfs.cache.close(fh.file.Path()) 64 return nil, errors.Wrap(err, "open RW handle failed to make cache directory") 65 } 66 67 rdwrMode := fh.flags & accessModeMask 68 if rdwrMode != os.O_RDONLY { 69 fh.file.addWriter(fh) 70 } 71 72 // truncate or create files immediately to prepare the cache 73 if fh.flags&os.O_TRUNC != 0 || fh.flags&(os.O_CREATE) != 0 && !f.exists() { 74 if err := fh.openPending(false); err != nil { 75 fh.file.delWriter(fh, false) 76 return nil, err 77 } 78 } 79 80 return fh, nil 81 } 82 83 // copy an object to or from the remote while accounting for it 84 func copyObj(f fs.Fs, dst fs.Object, remote string, src fs.Object) (newDst fs.Object, err error) { 85 if operations.NeedTransfer(context.TODO(), dst, src) { 86 newDst, err = operations.Copy(context.TODO(), f, dst, remote, src) 87 } else { 88 newDst = dst 89 } 90 return newDst, err 91 } 92 93 // openPending opens the file if there is a pending open 94 // 95 // call with the lock held 96 func (fh *RWFileHandle) openPending(truncate bool) (err error) { 97 if fh.opened { 98 return nil 99 } 100 101 fh.file.muRW.Lock() 102 defer fh.file.muRW.Unlock() 103 104 o := fh.file.getObject() 105 106 var fd *os.File 107 cacheFileOpenFlags := fh.flags 108 // if not truncating the file, need to read it first 109 if fh.flags&os.O_TRUNC == 0 && !truncate { 110 // If the remote object exists AND its cached file exists locally AND there are no 111 // other RW handles with it open, then attempt to update it. 112 if o != nil && fh.file.rwOpens() == 0 { 113 cacheObj, err := fh.d.vfs.cache.f.NewObject(context.TODO(), fh.file.Path()) 114 if err == nil && cacheObj != nil { 115 _, err = copyObj(fh.d.vfs.cache.f, cacheObj, fh.file.Path(), o) 116 if err != nil { 117 return errors.Wrap(err, "open RW handle failed to update cached file") 118 } 119 } 120 } 121 122 // try to open a exising cache file 123 fd, err = file.OpenFile(fh.file.osPath(), cacheFileOpenFlags&^os.O_CREATE, 0600) 124 if os.IsNotExist(err) { 125 // cache file does not exist, so need to fetch it if we have an object to fetch 126 // it from 127 if o != nil { 128 _, err = copyObj(fh.d.vfs.cache.f, nil, fh.file.Path(), o) 129 if err != nil { 130 cause := errors.Cause(err) 131 if cause != fs.ErrorObjectNotFound && cause != fs.ErrorDirNotFound { 132 // return any non NotFound errors 133 return errors.Wrap(err, "open RW handle failed to cache file") 134 } 135 // continue here with err=fs.Error{Object,Dir}NotFound 136 } 137 } 138 // if err == nil, then we have cached the file successfully, otherwise err is 139 // indicating some kind of non existent file/directory either 140 // os.IsNotExist(err) or fs.Error{Object,Dir}NotFound 141 if err != nil { 142 if fh.flags&os.O_CREATE != 0 { 143 // if the object wasn't found AND O_CREATE is set then 144 // ignore error as we are about to create the file 145 fh.file.setSize(0) 146 fh.changed = true 147 } else { 148 return errors.Wrap(err, "open RW handle failed to cache file") 149 } 150 } 151 } else if err != nil { 152 return errors.Wrap(err, "cache open file failed") 153 } else { 154 fs.Debugf(fh.logPrefix(), "Opened existing cached copy with flags=%s", decodeOpenFlags(fh.flags)) 155 } 156 } else { 157 // Set the size to 0 since we are truncating and flag we need to write it back 158 fh.file.setSize(0) 159 fh.changed = true 160 if fh.flags&os.O_CREATE == 0 && fh.file.exists() { 161 // create an empty file if it exists on the source 162 err = ioutil.WriteFile(fh.file.osPath(), []byte{}, 0600) 163 if err != nil { 164 return errors.Wrap(err, "cache open failed to create zero length file") 165 } 166 } 167 // Windows doesn't seem to deal well with O_TRUNC and 168 // certain access modes so so truncate the file if it 169 // exists in these cases. 170 if runtime.GOOS == "windows" && fh.flags&os.O_APPEND != 0 { 171 cacheFileOpenFlags &^= os.O_TRUNC 172 _, err = os.Stat(fh.file.osPath()) 173 if err == nil { 174 err = os.Truncate(fh.file.osPath(), 0) 175 if err != nil { 176 return errors.Wrap(err, "cache open failed to truncate") 177 } 178 } 179 } 180 } 181 182 if fd == nil { 183 fs.Debugf(fh.logPrefix(), "Opening cached copy with flags=%s", decodeOpenFlags(fh.flags)) 184 fd, err = file.OpenFile(fh.file.osPath(), cacheFileOpenFlags, 0600) 185 if err != nil { 186 return errors.Wrap(err, "cache open file failed") 187 } 188 } 189 fh.File = fd 190 fh.opened = true 191 fh.file.addRWOpen() 192 fh.d.addObject(fh.file) // make sure the directory has this object in it now 193 return nil 194 } 195 196 // String converts it to printable 197 func (fh *RWFileHandle) String() string { 198 if fh == nil { 199 return "<nil *RWFileHandle>" 200 } 201 fh.mu.Lock() 202 defer fh.mu.Unlock() 203 if fh.file == nil { 204 return "<nil *RWFileHandle.file>" 205 } 206 return fh.file.String() + " (rw)" 207 } 208 209 // Node returns the Node assocuated with this - satisfies Noder interface 210 func (fh *RWFileHandle) Node() Node { 211 fh.mu.Lock() 212 defer fh.mu.Unlock() 213 return fh.file 214 } 215 216 // Returns whether the file needs to be written back. 217 // 218 // If write hasn't been called and the file hasn't been changed in any other 219 // way we haven't modified it so we don't need to transfer it 220 // 221 // Must be called with fh.mu held 222 func (fh *RWFileHandle) modified() bool { 223 if !fh.writeCalled && !fh.changed { 224 fs.Debugf(fh.logPrefix(), "not modified so not transferring") 225 return false 226 } 227 return true 228 } 229 230 // flushWrites flushes any pending writes to cloud storage 231 // 232 // Must be called with fh.muRW held 233 func (fh *RWFileHandle) flushWrites(closeFile bool) error { 234 if fh.closed && !closeFile { 235 return nil 236 } 237 238 rdwrMode := fh.flags & accessModeMask 239 writer := rdwrMode != os.O_RDONLY 240 241 // If read only then return 242 if !fh.opened && rdwrMode == os.O_RDONLY { 243 return nil 244 } 245 246 isCopied := false 247 if writer { 248 isCopied = fh.file.delWriter(fh, fh.modified()) 249 defer fh.file.finishWriterClose() 250 } 251 252 // If we aren't creating or truncating the file then 253 // we haven't modified it so don't need to transfer it 254 if fh.flags&(os.O_CREATE|os.O_TRUNC) != 0 { 255 if err := fh.openPending(false); err != nil { 256 return err 257 } 258 } 259 260 if writer && fh.opened { 261 fi, err := fh.File.Stat() 262 if err != nil { 263 fs.Errorf(fh.logPrefix(), "Failed to stat cache file: %v", err) 264 } else { 265 fh.file.setSize(fi.Size()) 266 } 267 } 268 269 // Close the underlying file 270 if fh.opened && closeFile { 271 err := fh.File.Close() 272 if err != nil { 273 err = errors.Wrap(err, "failed to close cache file") 274 return err 275 } 276 } 277 278 if isCopied { 279 // Transfer the temp file to the remote 280 cacheObj, err := fh.d.vfs.cache.f.NewObject(context.TODO(), fh.file.Path()) 281 if err != nil { 282 err = errors.Wrap(err, "failed to find cache file") 283 fs.Errorf(fh.logPrefix(), "%v", err) 284 return err 285 } 286 287 objPath := fh.file.Path() 288 objOld := fh.file.getObject() 289 if objOld != nil { 290 objPath = objOld.Remote() // use the path of the actual object if available 291 } 292 o, err := copyObj(fh.d.vfs.f, objOld, objPath, cacheObj) 293 if err != nil { 294 err = errors.Wrap(err, "failed to transfer file from cache to remote") 295 fs.Errorf(fh.logPrefix(), "%v", err) 296 return err 297 } 298 fh.file.setObject(o) 299 fs.Debugf(o, "transferred to remote") 300 } 301 302 return nil 303 } 304 305 // close the file handle returning EBADF if it has been 306 // closed already. 307 // 308 // Must be called with fh.mu held 309 // 310 // Note that we leave the file around in the cache on error conditions 311 // to give the user a chance to recover it. 312 func (fh *RWFileHandle) close() (err error) { 313 defer log.Trace(fh.logPrefix(), "")("err=%v", &err) 314 fh.file.muRW.Lock() 315 defer fh.file.muRW.Unlock() 316 317 if fh.closed { 318 return ECLOSED 319 } 320 fh.closed = true 321 defer func() { 322 if fh.opened { 323 fh.file.delRWOpen() 324 } 325 fh.d.vfs.cache.close(fh.file.Path()) 326 }() 327 328 return fh.flushWrites(true) 329 } 330 331 // Close closes the file 332 func (fh *RWFileHandle) Close() error { 333 fh.mu.Lock() 334 defer fh.mu.Unlock() 335 return fh.close() 336 } 337 338 // Flush is called each time the file or directory is closed. 339 // Because there can be multiple file descriptors referring to a 340 // single opened file, Flush can be called multiple times. 341 func (fh *RWFileHandle) Flush() error { 342 fh.mu.Lock() 343 defer fh.mu.Unlock() 344 if !fh.opened { 345 return nil 346 } 347 if fh.closed { 348 fs.Debugf(fh.logPrefix(), "RWFileHandle.Flush nothing to do") 349 return nil 350 } 351 // fs.Debugf(fh.logPrefix(), "RWFileHandle.Flush") 352 if !fh.opened { 353 fs.Debugf(fh.logPrefix(), "RWFileHandle.Flush ignoring flush on unopened handle") 354 return nil 355 } 356 357 // If Write hasn't been called then ignore the Flush - Release 358 // will pick it up 359 if !fh.writeCalled { 360 fs.Debugf(fh.logPrefix(), "RWFileHandle.Flush ignoring flush on unwritten handle") 361 return nil 362 } 363 364 fh.file.muRW.Lock() 365 defer fh.file.muRW.Unlock() 366 err := fh.flushWrites(false) 367 368 if err != nil { 369 fs.Errorf(fh.logPrefix(), "RWFileHandle.Flush error: %v", err) 370 } else { 371 // fs.Debugf(fh.logPrefix(), "RWFileHandle.Flush OK") 372 } 373 return err 374 } 375 376 // Release is called when we are finished with the file handle 377 // 378 // It isn't called directly from userspace so the error is ignored by 379 // the kernel 380 func (fh *RWFileHandle) Release() error { 381 fh.mu.Lock() 382 defer fh.mu.Unlock() 383 if fh.closed { 384 fs.Debugf(fh.logPrefix(), "RWFileHandle.Release nothing to do") 385 return nil 386 } 387 fs.Debugf(fh.logPrefix(), "RWFileHandle.Release closing") 388 err := fh.close() 389 if err != nil { 390 fs.Errorf(fh.logPrefix(), "RWFileHandle.Release error: %v", err) 391 } else { 392 // fs.Debugf(fh.logPrefix(), "RWFileHandle.Release OK") 393 } 394 return err 395 } 396 397 // Size returns the size of the underlying file 398 func (fh *RWFileHandle) Size() int64 { 399 fh.mu.Lock() 400 defer fh.mu.Unlock() 401 if !fh.opened { 402 return fh.file.Size() 403 } 404 fi, err := fh.File.Stat() 405 if err != nil { 406 return 0 407 } 408 return fi.Size() 409 } 410 411 // Stat returns info about the file 412 func (fh *RWFileHandle) Stat() (os.FileInfo, error) { 413 fh.mu.Lock() 414 defer fh.mu.Unlock() 415 return fh.file, nil 416 } 417 418 // readFn is a general purpose read function - pass in a closure to do 419 // the actual read 420 func (fh *RWFileHandle) readFn(read func() (int, error)) (n int, err error) { 421 fh.mu.Lock() 422 defer fh.mu.Unlock() 423 if fh.closed { 424 return 0, ECLOSED 425 } 426 if fh.flags&accessModeMask == os.O_WRONLY { 427 return 0, EBADF 428 } 429 if err = fh.openPending(false); err != nil { 430 return n, err 431 } 432 return read() 433 } 434 435 // Read bytes from the file 436 func (fh *RWFileHandle) Read(b []byte) (n int, err error) { 437 return fh.readFn(func() (int, error) { 438 return fh.File.Read(b) 439 }) 440 } 441 442 // ReadAt bytes from the file at off 443 func (fh *RWFileHandle) ReadAt(b []byte, off int64) (n int, err error) { 444 return fh.readFn(func() (int, error) { 445 return fh.File.ReadAt(b, off) 446 }) 447 } 448 449 // Seek to new file position 450 func (fh *RWFileHandle) Seek(offset int64, whence int) (ret int64, err error) { 451 fh.mu.Lock() 452 defer fh.mu.Unlock() 453 if fh.closed { 454 return 0, ECLOSED 455 } 456 if !fh.opened && offset == 0 && whence != 2 { 457 return 0, nil 458 } 459 if err = fh.openPending(false); err != nil { 460 return ret, err 461 } 462 return fh.File.Seek(offset, whence) 463 } 464 465 // writeFn general purpose write call 466 // 467 // Pass a closure to do the actual write 468 func (fh *RWFileHandle) writeFn(write func() error) (err error) { 469 fh.mu.Lock() 470 defer fh.mu.Unlock() 471 if fh.closed { 472 return ECLOSED 473 } 474 if fh.flags&accessModeMask == os.O_RDONLY { 475 return EBADF 476 } 477 if err = fh.openPending(false); err != nil { 478 return err 479 } 480 fh.writeCalled = true 481 err = write() 482 if err != nil { 483 return err 484 } 485 fi, err := fh.File.Stat() 486 if err != nil { 487 return errors.Wrap(err, "failed to stat cache file") 488 } 489 fh.file.setSize(fi.Size()) 490 return nil 491 } 492 493 // Write bytes to the file 494 func (fh *RWFileHandle) Write(b []byte) (n int, err error) { 495 err = fh.writeFn(func() error { 496 n, err = fh.File.Write(b) 497 return err 498 }) 499 return n, err 500 } 501 502 // WriteAt bytes to the file at off 503 func (fh *RWFileHandle) WriteAt(b []byte, off int64) (n int, err error) { 504 err = fh.writeFn(func() error { 505 n, err = fh.File.WriteAt(b, off) 506 return err 507 }) 508 return n, err 509 } 510 511 // WriteString a string to the file 512 func (fh *RWFileHandle) WriteString(s string) (n int, err error) { 513 err = fh.writeFn(func() error { 514 n, err = fh.File.WriteString(s) 515 return err 516 }) 517 return n, err 518 } 519 520 // Truncate file to given size 521 func (fh *RWFileHandle) Truncate(size int64) (err error) { 522 fh.mu.Lock() 523 defer fh.mu.Unlock() 524 if fh.closed { 525 return ECLOSED 526 } 527 if err = fh.openPending(size == 0); err != nil { 528 return err 529 } 530 fh.changed = true 531 fh.file.setSize(size) 532 return fh.File.Truncate(size) 533 } 534 535 // Sync commits the current contents of the file to stable storage. Typically, 536 // this means flushing the file system's in-memory copy of recently written 537 // data to disk. 538 func (fh *RWFileHandle) Sync() error { 539 fh.mu.Lock() 540 defer fh.mu.Unlock() 541 if fh.closed { 542 return ECLOSED 543 } 544 if !fh.opened { 545 return nil 546 } 547 if fh.flags&accessModeMask == os.O_RDONLY { 548 return nil 549 } 550 return fh.File.Sync() 551 } 552 553 func (fh *RWFileHandle) logPrefix() string { 554 return fmt.Sprintf("%s(%p)", fh.file.Path(), fh) 555 }