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  }