github.com/rclone/rclone@v1.66.1-0.20240517100346-7b89735ae726/vfs/read_write.go (about)

     1  package vfs
     2  
     3  import (
     4  	"fmt"
     5  	"io"
     6  	"os"
     7  	"sync"
     8  
     9  	"github.com/rclone/rclone/fs"
    10  	"github.com/rclone/rclone/fs/log"
    11  	"github.com/rclone/rclone/vfs/vfscache"
    12  )
    13  
    14  // RWFileHandle is a handle that can be open for read and write.
    15  //
    16  // It will be open to a temporary file which, when closed, will be
    17  // transferred to the remote.
    18  type RWFileHandle struct {
    19  	// read only variables
    20  	file  *File
    21  	d     *Dir
    22  	flags int            // open flags
    23  	item  *vfscache.Item // cached file item
    24  
    25  	// read write variables protected by mutex
    26  	mu          sync.Mutex
    27  	offset      int64 // file pointer offset
    28  	closed      bool  // set if handle has been closed
    29  	opened      bool
    30  	writeCalled bool // if any Write() methods have been called
    31  }
    32  
    33  // Lock performs Unix locking, not supported
    34  func (fh *RWFileHandle) Lock() error {
    35  	return os.ErrInvalid
    36  }
    37  
    38  // Unlock performs Unix unlocking, not supported
    39  func (fh *RWFileHandle) Unlock() error {
    40  	return os.ErrInvalid
    41  }
    42  
    43  func newRWFileHandle(d *Dir, f *File, flags int) (fh *RWFileHandle, err error) {
    44  	defer log.Trace(f.Path(), "")("err=%v", &err)
    45  	// get an item to represent this from the cache
    46  	item := d.vfs.cache.Item(f.Path())
    47  
    48  	exists := f.exists() || (item.Exists() && !item.WrittenBack())
    49  
    50  	// if O_CREATE and O_EXCL are set and if path already exists, then return EEXIST
    51  	if flags&(os.O_CREATE|os.O_EXCL) == os.O_CREATE|os.O_EXCL && exists {
    52  		return nil, EEXIST
    53  	}
    54  
    55  	fh = &RWFileHandle{
    56  		file:  f,
    57  		d:     d,
    58  		flags: flags,
    59  		item:  item,
    60  	}
    61  
    62  	// truncate immediately if O_TRUNC is set or O_CREATE is set and file doesn't exist
    63  	if !fh.readOnly() && (fh.flags&os.O_TRUNC != 0 || (fh.flags&os.O_CREATE != 0 && !exists)) {
    64  		err = fh.Truncate(0)
    65  		if err != nil {
    66  			return nil, fmt.Errorf("cache open with O_TRUNC: failed to truncate: %w", err)
    67  		}
    68  		// we definitely need to write back the item even if we don't write to it
    69  		item.Dirty()
    70  	}
    71  
    72  	if !fh.readOnly() {
    73  		fh.file.addWriter(fh)
    74  	}
    75  
    76  	return fh, nil
    77  }
    78  
    79  // readOnly returns whether flags say fh is read only
    80  func (fh *RWFileHandle) readOnly() bool {
    81  	return (fh.flags & accessModeMask) == os.O_RDONLY
    82  }
    83  
    84  // writeOnly returns whether flags say fh is write only
    85  func (fh *RWFileHandle) writeOnly() bool {
    86  	return (fh.flags & accessModeMask) == os.O_WRONLY
    87  }
    88  
    89  // openPending opens the file if there is a pending open
    90  //
    91  // call with the lock held
    92  func (fh *RWFileHandle) openPending() (err error) {
    93  	if fh.opened {
    94  		return nil
    95  	}
    96  	defer log.Trace(fh.logPrefix(), "")("err=%v", &err)
    97  
    98  	fh.file.muRW.Lock()
    99  	defer fh.file.muRW.Unlock()
   100  
   101  	o := fh.file.getObject()
   102  	err = fh.item.Open(o)
   103  	if err != nil {
   104  		return fmt.Errorf("open RW handle failed to open cache file: %w", err)
   105  	}
   106  
   107  	size := fh._size() // update size in file and read size
   108  	if fh.flags&os.O_APPEND != 0 {
   109  		fh.offset = size
   110  		fs.Debugf(fh.logPrefix(), "open at offset %d", fh.offset)
   111  	} else {
   112  		fh.offset = 0
   113  	}
   114  	fh.opened = true
   115  	fh.d.addObject(fh.file) // make sure the directory has this object in it now
   116  	return nil
   117  }
   118  
   119  // String converts it to printable
   120  func (fh *RWFileHandle) String() string {
   121  	if fh == nil {
   122  		return "<nil *RWFileHandle>"
   123  	}
   124  	if fh.file == nil {
   125  		return "<nil *RWFileHandle.file>"
   126  	}
   127  	return fh.file.String() + " (rw)"
   128  }
   129  
   130  // Node returns the Node associated with this - satisfies Noder interface
   131  func (fh *RWFileHandle) Node() Node {
   132  	fh.mu.Lock()
   133  	defer fh.mu.Unlock()
   134  	return fh.file
   135  }
   136  
   137  // updateSize updates the size of the file if necessary
   138  //
   139  // Must be called with fh.mu held
   140  func (fh *RWFileHandle) updateSize() {
   141  	// If read only or not opened then ignore
   142  	if fh.readOnly() || !fh.opened {
   143  		return
   144  	}
   145  	size := fh._size()
   146  	fh.file.setSize(size)
   147  }
   148  
   149  // close the file handle returning EBADF if it has been
   150  // closed already.
   151  //
   152  // Must be called with fh.mu held.
   153  //
   154  // Note that we leave the file around in the cache on error conditions
   155  // to give the user a chance to recover it.
   156  func (fh *RWFileHandle) close() (err error) {
   157  	defer log.Trace(fh.logPrefix(), "")("err=%v", &err)
   158  	fh.file.muRW.Lock()
   159  	defer fh.file.muRW.Unlock()
   160  
   161  	if fh.closed {
   162  		return ECLOSED
   163  	}
   164  
   165  	fh.closed = true
   166  	fh.updateSize()
   167  	if fh.opened {
   168  		err = fh.item.Close(fh.file.setObject)
   169  		fh.opened = false
   170  	} else {
   171  		// apply any pending mod times if any
   172  		_ = fh.file.applyPendingModTime()
   173  	}
   174  
   175  	if !fh.readOnly() {
   176  		fh.file.delWriter(fh)
   177  	}
   178  
   179  	return err
   180  }
   181  
   182  // Close closes the file
   183  func (fh *RWFileHandle) Close() error {
   184  	fh.mu.Lock()
   185  	defer fh.mu.Unlock()
   186  	return fh.close()
   187  }
   188  
   189  // Flush is called each time the file or directory is closed.
   190  // Because there can be multiple file descriptors referring to a
   191  // single opened file, Flush can be called multiple times.
   192  func (fh *RWFileHandle) Flush() error {
   193  	fh.mu.Lock()
   194  	fs.Debugf(fh.logPrefix(), "RWFileHandle.Flush")
   195  	fh.updateSize()
   196  	fh.mu.Unlock()
   197  	return nil
   198  }
   199  
   200  // Release is called when we are finished with the file handle
   201  //
   202  // It isn't called directly from userspace so the error is ignored by
   203  // the kernel
   204  func (fh *RWFileHandle) Release() error {
   205  	fh.mu.Lock()
   206  	defer fh.mu.Unlock()
   207  	fs.Debugf(fh.logPrefix(), "RWFileHandle.Release")
   208  	if fh.closed {
   209  		// Don't return an error if called twice
   210  		return nil
   211  	}
   212  	err := fh.close()
   213  	if err != nil {
   214  		fs.Errorf(fh.logPrefix(), "RWFileHandle.Release error: %v", err)
   215  	}
   216  	return err
   217  }
   218  
   219  // _size returns the size of the underlying file and also sets it in
   220  // the owning file
   221  //
   222  // call with the lock held
   223  func (fh *RWFileHandle) _size() int64 {
   224  	size, err := fh.item.GetSize()
   225  	if err != nil {
   226  		o := fh.file.getObject()
   227  		if o != nil {
   228  			size = o.Size()
   229  		} else {
   230  			fs.Errorf(fh.logPrefix(), "Couldn't read size of file")
   231  			size = 0
   232  		}
   233  	}
   234  	fh.file.setSize(size)
   235  	return size
   236  }
   237  
   238  // Size returns the size of the underlying file
   239  func (fh *RWFileHandle) Size() int64 {
   240  	fh.mu.Lock()
   241  	defer fh.mu.Unlock()
   242  	return fh._size()
   243  }
   244  
   245  // Stat returns info about the file
   246  func (fh *RWFileHandle) Stat() (os.FileInfo, error) {
   247  	fh.mu.Lock()
   248  	defer fh.mu.Unlock()
   249  	return fh.file, nil
   250  }
   251  
   252  // _readAt bytes from the file at off
   253  //
   254  // if release is set then it releases the mutex just before doing the IO
   255  //
   256  // call with lock held
   257  func (fh *RWFileHandle) _readAt(b []byte, off int64, release bool) (n int, err error) {
   258  	defer log.Trace(fh.logPrefix(), "size=%d, off=%d", len(b), off)("n=%d, err=%v", &n, &err)
   259  	if fh.closed {
   260  		return n, ECLOSED
   261  	}
   262  	if fh.writeOnly() {
   263  		return n, EBADF
   264  	}
   265  	if off >= fh._size() {
   266  		return n, io.EOF
   267  	}
   268  	if err = fh.openPending(); err != nil {
   269  		return n, err
   270  	}
   271  	if release {
   272  		// Do the writing with fh.mu unlocked
   273  		fh.mu.Unlock()
   274  	}
   275  
   276  	n, err = fh.item.ReadAt(b, off)
   277  
   278  	if release {
   279  		fh.mu.Lock()
   280  	}
   281  	return n, err
   282  }
   283  
   284  // ReadAt bytes from the file at off
   285  func (fh *RWFileHandle) ReadAt(b []byte, off int64) (n int, err error) {
   286  	fh.mu.Lock()
   287  	defer fh.mu.Unlock()
   288  	return fh._readAt(b, off, true)
   289  }
   290  
   291  // Read bytes from the file
   292  func (fh *RWFileHandle) Read(b []byte) (n int, err error) {
   293  	fh.mu.Lock()
   294  	defer fh.mu.Unlock()
   295  	n, err = fh._readAt(b, fh.offset, false)
   296  	fh.offset += int64(n)
   297  	return n, err
   298  }
   299  
   300  // Seek to new file position
   301  func (fh *RWFileHandle) Seek(offset int64, whence int) (ret int64, err error) {
   302  	fh.mu.Lock()
   303  	defer fh.mu.Unlock()
   304  	if fh.closed {
   305  		return 0, ECLOSED
   306  	}
   307  	if !fh.opened && offset == 0 && whence != 2 {
   308  		return 0, nil
   309  	}
   310  	if err = fh.openPending(); err != nil {
   311  		return ret, err
   312  	}
   313  	switch whence {
   314  	case io.SeekStart:
   315  		fh.offset = 0
   316  	case io.SeekEnd:
   317  		fh.offset = fh._size()
   318  	}
   319  	fh.offset += offset
   320  	// we don't check the offset - the next Read will
   321  	return fh.offset, nil
   322  }
   323  
   324  // _writeAt bytes to the file at off
   325  //
   326  // if release is set then it releases the mutex just before doing the IO
   327  //
   328  // call with lock held
   329  func (fh *RWFileHandle) _writeAt(b []byte, off int64, release bool) (n int, err error) {
   330  	defer log.Trace(fh.logPrefix(), "size=%d, off=%d", len(b), off)("n=%d, err=%v", &n, &err)
   331  	if fh.closed {
   332  		return n, ECLOSED
   333  	}
   334  	if fh.readOnly() {
   335  		return n, EBADF
   336  	}
   337  	if err = fh.openPending(); err != nil {
   338  		return n, err
   339  	}
   340  	if fh.flags&os.O_APPEND != 0 {
   341  		// From open(2): Before each write(2), the file offset is
   342  		// positioned at the end of the file, as if with lseek(2).
   343  		size := fh._size()
   344  		fh.offset = size
   345  		off = fh.offset
   346  	}
   347  	fh.writeCalled = true
   348  	if release {
   349  		// Do the writing with fh.mu unlocked
   350  		fh.mu.Unlock()
   351  	}
   352  	n, err = fh.item.WriteAt(b, off)
   353  	if release {
   354  		fh.mu.Lock()
   355  	}
   356  	if err != nil {
   357  		return n, err
   358  	}
   359  
   360  	_ = fh._size()
   361  	return n, err
   362  }
   363  
   364  // WriteAt bytes to the file at off
   365  func (fh *RWFileHandle) WriteAt(b []byte, off int64) (n int, err error) {
   366  	fh.mu.Lock()
   367  	n, err = fh._writeAt(b, off, true)
   368  	if fh.flags&os.O_APPEND != 0 {
   369  		fh.offset += int64(n)
   370  	}
   371  	fh.mu.Unlock()
   372  	return n, err
   373  }
   374  
   375  // Write bytes to the file
   376  func (fh *RWFileHandle) Write(b []byte) (n int, err error) {
   377  	fh.mu.Lock()
   378  	defer fh.mu.Unlock()
   379  	n, err = fh._writeAt(b, fh.offset, false)
   380  	fh.offset += int64(n)
   381  	return n, err
   382  }
   383  
   384  // WriteString a string to the file
   385  func (fh *RWFileHandle) WriteString(s string) (n int, err error) {
   386  	return fh.Write([]byte(s))
   387  }
   388  
   389  // Truncate file to given size
   390  //
   391  // Call with mutex held
   392  func (fh *RWFileHandle) _truncate(size int64) (err error) {
   393  	if size == fh._size() {
   394  		return nil
   395  	}
   396  	fh.file.setSize(size)
   397  	return fh.item.Truncate(size)
   398  }
   399  
   400  // Truncate file to given size
   401  func (fh *RWFileHandle) Truncate(size int64) (err error) {
   402  	fh.mu.Lock()
   403  	defer fh.mu.Unlock()
   404  	if fh.closed {
   405  		return ECLOSED
   406  	}
   407  	if err = fh.openPending(); err != nil {
   408  		return err
   409  	}
   410  	return fh._truncate(size)
   411  }
   412  
   413  // Sync commits the current contents of the file to stable storage. Typically,
   414  // this means flushing the file system's in-memory copy of recently written
   415  // data to disk.
   416  func (fh *RWFileHandle) Sync() error {
   417  	fh.mu.Lock()
   418  	defer fh.mu.Unlock()
   419  	if fh.closed {
   420  		return ECLOSED
   421  	}
   422  	if !fh.opened {
   423  		return nil
   424  	}
   425  	if fh.readOnly() {
   426  		return nil
   427  	}
   428  	return fh.item.Sync()
   429  }
   430  
   431  func (fh *RWFileHandle) logPrefix() string {
   432  	return fmt.Sprintf("%s(%p)", fh.file.Path(), fh)
   433  }
   434  
   435  // Chdir changes the current working directory to the file, which must
   436  // be a directory.
   437  func (fh *RWFileHandle) Chdir() error {
   438  	return ENOSYS
   439  }
   440  
   441  // Chmod changes the mode of the file to mode.
   442  func (fh *RWFileHandle) Chmod(mode os.FileMode) error {
   443  	return ENOSYS
   444  }
   445  
   446  // Chown changes the numeric uid and gid of the named file.
   447  func (fh *RWFileHandle) Chown(uid, gid int) error {
   448  	return ENOSYS
   449  }
   450  
   451  // Fd returns the integer Unix file descriptor referencing the open file.
   452  func (fh *RWFileHandle) Fd() uintptr {
   453  	return 0xdeadbeef // FIXME
   454  }
   455  
   456  // Name returns the name of the file from the underlying Object.
   457  func (fh *RWFileHandle) Name() string {
   458  	return fh.file.String()
   459  }
   460  
   461  // Readdir reads the contents of the directory associated with file.
   462  func (fh *RWFileHandle) Readdir(n int) ([]os.FileInfo, error) {
   463  	return nil, ENOSYS
   464  }
   465  
   466  // Readdirnames reads the contents of the directory associated with file.
   467  func (fh *RWFileHandle) Readdirnames(n int) (names []string, err error) {
   468  	return nil, ENOSYS
   469  }