github.com/xhghs/rclone@v1.51.1-0.20200430155106-e186a28cced8/vfs/write.go (about)

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