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

     1  package vfs
     2  
     3  import (
     4  	"context"
     5  	"io"
     6  	"os"
     7  	"sync"
     8  	"time"
     9  
    10  	"github.com/rclone/rclone/fs"
    11  	"github.com/rclone/rclone/fs/operations"
    12  )
    13  
    14  // WriteFileHandle is an open for write handle on a File
    15  type WriteFileHandle struct {
    16  	baseHandle
    17  	mu          sync.Mutex
    18  	cond        sync.Cond // cond lock for out of sequence writes
    19  	remote      string
    20  	pipeWriter  *io.PipeWriter
    21  	o           fs.Object
    22  	result      chan error
    23  	file        *File
    24  	offset      int64
    25  	flags       int
    26  	closed      bool // set if handle has been closed
    27  	writeCalled bool // set the first time Write() is called
    28  	opened      bool
    29  	truncated   bool
    30  }
    31  
    32  // Check interfaces
    33  var (
    34  	_ io.Writer   = (*WriteFileHandle)(nil)
    35  	_ io.WriterAt = (*WriteFileHandle)(nil)
    36  	_ io.Closer   = (*WriteFileHandle)(nil)
    37  )
    38  
    39  func newWriteFileHandle(d *Dir, f *File, remote string, flags int) (*WriteFileHandle, error) {
    40  	fh := &WriteFileHandle{
    41  		remote: remote,
    42  		flags:  flags,
    43  		result: make(chan error, 1),
    44  		file:   f,
    45  	}
    46  	fh.cond = sync.Cond{L: &fh.mu}
    47  	fh.file.addWriter(fh)
    48  	return fh, nil
    49  }
    50  
    51  // returns whether it is OK to truncate the file
    52  func (fh *WriteFileHandle) safeToTruncate() bool {
    53  	return fh.truncated || fh.flags&os.O_TRUNC != 0 || !fh.file.exists()
    54  }
    55  
    56  // openPending opens the file if there is a pending open
    57  //
    58  // call with the lock held
    59  func (fh *WriteFileHandle) openPending() (err error) {
    60  	if fh.opened {
    61  		return nil
    62  	}
    63  	if !fh.safeToTruncate() {
    64  		fs.Errorf(fh.remote, "WriteFileHandle: Can't open for write without O_TRUNC on existing file without --vfs-cache-mode >= writes")
    65  		return EPERM
    66  	}
    67  	var pipeReader *io.PipeReader
    68  	pipeReader, fh.pipeWriter = io.Pipe()
    69  	go func() {
    70  		// NB Rcat deals with Stats.Transferring, etc.
    71  		o, err := operations.Rcat(context.TODO(), fh.file.Fs(), fh.remote, pipeReader, time.Now(), nil)
    72  		if err != nil {
    73  			fs.Errorf(fh.remote, "WriteFileHandle.New Rcat failed: %v", err)
    74  		}
    75  		// Close the pipeReader so the pipeWriter fails with ErrClosedPipe
    76  		_ = pipeReader.Close()
    77  		fh.o = o
    78  		fh.result <- err
    79  	}()
    80  	fh.file.setSize(0)
    81  	fh.truncated = true
    82  	fh.file.Dir().addObject(fh.file) // make sure the directory has this object in it now
    83  	fh.opened = true
    84  	return nil
    85  }
    86  
    87  // String converts it to printable
    88  func (fh *WriteFileHandle) String() string {
    89  	if fh == nil {
    90  		return "<nil *WriteFileHandle>"
    91  	}
    92  	fh.mu.Lock()
    93  	defer fh.mu.Unlock()
    94  	if fh.file == nil {
    95  		return "<nil *WriteFileHandle.file>"
    96  	}
    97  	return fh.file.String() + " (w)"
    98  }
    99  
   100  // Node returns the Node associated with this - satisfies Noder interface
   101  func (fh *WriteFileHandle) Node() Node {
   102  	fh.mu.Lock()
   103  	defer fh.mu.Unlock()
   104  	return fh.file
   105  }
   106  
   107  // WriteAt writes len(p) bytes from p to the underlying data stream at offset
   108  // off. It returns the number of bytes written from p (0 <= n <= len(p)) and
   109  // any error encountered that caused the write to stop early. WriteAt must
   110  // return a non-nil error if it returns n < len(p).
   111  //
   112  // If WriteAt is writing to a destination with a seek offset, WriteAt should
   113  // not affect nor be affected by the underlying seek offset.
   114  //
   115  // Clients of WriteAt can execute parallel WriteAt calls on the same
   116  // destination if the ranges do not overlap.
   117  //
   118  // Implementations must not retain p.
   119  func (fh *WriteFileHandle) WriteAt(p []byte, off int64) (n int, err error) {
   120  	fh.mu.Lock()
   121  	defer fh.mu.Unlock()
   122  	return fh.writeAt(p, off)
   123  }
   124  
   125  // Implementation of WriteAt - call with lock held
   126  func (fh *WriteFileHandle) writeAt(p []byte, off int64) (n int, err error) {
   127  	// defer log.Trace(fh.remote, "len=%d off=%d", len(p), off)("n=%d, fh.off=%d, err=%v", &n, &fh.offset, &err)
   128  	if fh.closed {
   129  		fs.Errorf(fh.remote, "WriteFileHandle.Write: error: %v", EBADF)
   130  		return 0, ECLOSED
   131  	}
   132  	if fh.offset != off {
   133  		waitSequential("write", fh.remote, &fh.cond, fh.file.VFS().Opt.WriteWait, &fh.offset, off)
   134  	}
   135  	if fh.offset != off {
   136  		fs.Errorf(fh.remote, "WriteFileHandle.Write: can't seek in file without --vfs-cache-mode >= writes")
   137  		return 0, ESPIPE
   138  	}
   139  	if err = fh.openPending(); err != nil {
   140  		return 0, err
   141  	}
   142  	fh.writeCalled = true
   143  	n, err = fh.pipeWriter.Write(p)
   144  	fh.offset += int64(n)
   145  	fh.file.setSize(fh.offset)
   146  	if err != nil {
   147  		fs.Errorf(fh.remote, "WriteFileHandle.Write error: %v", err)
   148  		return 0, err
   149  	}
   150  	// fs.Debugf(fh.remote, "WriteFileHandle.Write OK (%d bytes written)", n)
   151  	fh.cond.Broadcast() // wake everyone up waiting for an in-sequence read
   152  	return n, nil
   153  }
   154  
   155  // Write writes len(p) bytes from p to the underlying data stream. It returns
   156  // the number of bytes written from p (0 <= n <= len(p)) and any error
   157  // encountered that caused the write to stop early. Write must return a non-nil
   158  // error if it returns n < len(p). Write must not modify the slice data, even
   159  // temporarily.
   160  //
   161  // Implementations must not retain p.
   162  func (fh *WriteFileHandle) Write(p []byte) (n int, err error) {
   163  	fh.mu.Lock()
   164  	defer fh.mu.Unlock()
   165  	// Since we can't seek, just call WriteAt with the current offset
   166  	return fh.writeAt(p, fh.offset)
   167  }
   168  
   169  // WriteString a string to the file
   170  func (fh *WriteFileHandle) WriteString(s string) (n int, err error) {
   171  	return fh.Write([]byte(s))
   172  }
   173  
   174  // Offset returns the offset of the file pointer
   175  func (fh *WriteFileHandle) Offset() (offset int64) {
   176  	fh.mu.Lock()
   177  	defer fh.mu.Unlock()
   178  	return fh.offset
   179  }
   180  
   181  // close the file handle returning EBADF if it has been
   182  // closed already.
   183  //
   184  // Must be called with fh.mu held
   185  func (fh *WriteFileHandle) close() (err error) {
   186  	if fh.closed {
   187  		return ECLOSED
   188  	}
   189  	fh.closed = true
   190  	// leave writer open until file is transferred
   191  	defer func() {
   192  		fh.file.delWriter(fh)
   193  	}()
   194  	// If file not opened and not safe to truncate then leave file intact
   195  	if !fh.opened && !fh.safeToTruncate() {
   196  		return nil
   197  	}
   198  	if err = fh.openPending(); err != nil {
   199  		return err
   200  	}
   201  	writeCloseErr := fh.pipeWriter.Close()
   202  	err = <-fh.result
   203  	if err == nil {
   204  		fh.file.setObject(fh.o)
   205  		err = writeCloseErr
   206  	} else {
   207  		// Remove vfs file entry when no object is present
   208  		if fh.file.getObject() == nil {
   209  			_ = fh.file.Remove()
   210  		}
   211  	}
   212  	return err
   213  }
   214  
   215  // Close closes the file
   216  func (fh *WriteFileHandle) Close() error {
   217  	fh.mu.Lock()
   218  	defer fh.mu.Unlock()
   219  	return fh.close()
   220  }
   221  
   222  // Flush is called on each close() of a file descriptor. So if a
   223  // filesystem wants to return write errors in close() and the file has
   224  // cached dirty data, this is a good place to write back data and
   225  // return any errors. Since many applications ignore close() errors
   226  // this is not always useful.
   227  //
   228  // NOTE: The flush() method may be called more than once for each
   229  // open(). This happens if more than one file descriptor refers to an
   230  // opened file due to dup(), dup2() or fork() calls. It is not
   231  // possible to determine if a flush is final, so each flush should be
   232  // treated equally. Multiple write-flush sequences are relatively
   233  // rare, so this shouldn't be a problem.
   234  //
   235  // Filesystems shouldn't assume that flush will always be called after
   236  // some writes, or that if will be called at all.
   237  func (fh *WriteFileHandle) Flush() error {
   238  	fh.mu.Lock()
   239  	defer fh.mu.Unlock()
   240  	if fh.closed {
   241  		fs.Debugf(fh.remote, "WriteFileHandle.Flush nothing to do")
   242  		return nil
   243  	}
   244  	// fs.Debugf(fh.remote, "WriteFileHandle.Flush")
   245  	// If Write hasn't been called then ignore the Flush - Release
   246  	// will pick it up
   247  	if !fh.writeCalled {
   248  		fs.Debugf(fh.remote, "WriteFileHandle.Flush unwritten handle, writing 0 bytes to avoid race conditions")
   249  		_, err := fh.writeAt([]byte{}, fh.offset)
   250  		return err
   251  	}
   252  	err := fh.close()
   253  	if err != nil {
   254  		fs.Errorf(fh.remote, "WriteFileHandle.Flush error: %v", err)
   255  		//} else {
   256  		// fs.Debugf(fh.remote, "WriteFileHandle.Flush OK")
   257  	}
   258  	return err
   259  }
   260  
   261  // Release is called when we are finished with the file handle
   262  //
   263  // It isn't called directly from userspace so the error is ignored by
   264  // the kernel
   265  func (fh *WriteFileHandle) Release() error {
   266  	fh.mu.Lock()
   267  	defer fh.mu.Unlock()
   268  	if fh.closed {
   269  		fs.Debugf(fh.remote, "WriteFileHandle.Release nothing to do")
   270  		return nil
   271  	}
   272  	fs.Debugf(fh.remote, "WriteFileHandle.Release closing")
   273  	err := fh.close()
   274  	if err != nil {
   275  		fs.Errorf(fh.remote, "WriteFileHandle.Release error: %v", err)
   276  		//} else {
   277  		// fs.Debugf(fh.remote, "WriteFileHandle.Release OK")
   278  	}
   279  	return err
   280  }
   281  
   282  // Stat returns info about the file
   283  func (fh *WriteFileHandle) Stat() (os.FileInfo, error) {
   284  	fh.mu.Lock()
   285  	defer fh.mu.Unlock()
   286  	return fh.file, nil
   287  }
   288  
   289  // Truncate file to given size
   290  func (fh *WriteFileHandle) Truncate(size int64) (err error) {
   291  	// defer log.Trace(fh.remote, "size=%d", size)("err=%v", &err)
   292  	fh.mu.Lock()
   293  	defer fh.mu.Unlock()
   294  	if size != fh.offset {
   295  		fs.Errorf(fh.remote, "WriteFileHandle: Truncate: Can't change size without --vfs-cache-mode >= writes")
   296  		return EPERM
   297  	}
   298  	// File is correct size
   299  	if size == 0 {
   300  		fh.truncated = true
   301  	}
   302  	return nil
   303  }
   304  
   305  // Read reads up to len(p) bytes into p.
   306  func (fh *WriteFileHandle) Read(p []byte) (n int, err error) {
   307  	fs.Errorf(fh.remote, "WriteFileHandle: Read: Can't read and write to file without --vfs-cache-mode >= minimal")
   308  	return 0, EPERM
   309  }
   310  
   311  // ReadAt reads len(p) bytes into p starting at offset off in the
   312  // underlying input source. It returns the number of bytes read (0 <=
   313  // n <= len(p)) and any error encountered.
   314  func (fh *WriteFileHandle) ReadAt(p []byte, off int64) (n int, err error) {
   315  	fs.Errorf(fh.remote, "WriteFileHandle: ReadAt: Can't read and write to file without --vfs-cache-mode >= minimal")
   316  	return 0, EPERM
   317  }
   318  
   319  // Sync commits the current contents of the file to stable storage. Typically,
   320  // this means flushing the file system's in-memory copy of recently written
   321  // data to disk.
   322  func (fh *WriteFileHandle) Sync() error {
   323  	return nil
   324  }
   325  
   326  // Name returns the name of the file from the underlying Object.
   327  func (fh *WriteFileHandle) Name() string {
   328  	return fh.file.String()
   329  }