github.com/10XDev/rclone@v1.52.3-0.20200626220027-16af9ab76b2a/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  	closed      bool       // set if handle has been closed
    20  	remote      string
    21  	pipeWriter  *io.PipeWriter
    22  	o           fs.Object
    23  	result      chan error
    24  	file        *File
    25  	writeCalled bool // set the first time Write() is called
    26  	offset      int64
    27  	opened      bool
    28  	flags       int
    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.NewCond(&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())
    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 assocuated 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  // Implementatino 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, false)
   193  		fh.file.finishWriterClose()
   194  	}()
   195  	// If file not opened and not safe to truncate then leave file intact
   196  	if !fh.opened && !fh.safeToTruncate() {
   197  		return nil
   198  	}
   199  	if err = fh.openPending(); err != nil {
   200  		return err
   201  	}
   202  	writeCloseErr := fh.pipeWriter.Close()
   203  	err = <-fh.result
   204  	if err == nil {
   205  		fh.file.setObject(fh.o)
   206  		err = writeCloseErr
   207  	}
   208  	return err
   209  }
   210  
   211  // Close closes the file
   212  func (fh *WriteFileHandle) Close() error {
   213  	fh.mu.Lock()
   214  	defer fh.mu.Unlock()
   215  	return fh.close()
   216  }
   217  
   218  // Flush is called on each close() of a file descriptor. So if a
   219  // filesystem wants to return write errors in close() and the file has
   220  // cached dirty data, this is a good place to write back data and
   221  // return any errors. Since many applications ignore close() errors
   222  // this is not always useful.
   223  //
   224  // NOTE: The flush() method may be called more than once for each
   225  // open(). This happens if more than one file descriptor refers to an
   226  // opened file due to dup(), dup2() or fork() calls. It is not
   227  // possible to determine if a flush is final, so each flush should be
   228  // treated equally. Multiple write-flush sequences are relatively
   229  // rare, so this shouldn't be a problem.
   230  //
   231  // Filesystems shouldn't assume that flush will always be called after
   232  // some writes, or that if will be called at all.
   233  func (fh *WriteFileHandle) Flush() error {
   234  	fh.mu.Lock()
   235  	defer fh.mu.Unlock()
   236  	if fh.closed {
   237  		fs.Debugf(fh.remote, "WriteFileHandle.Flush nothing to do")
   238  		return nil
   239  	}
   240  	// fs.Debugf(fh.remote, "WriteFileHandle.Flush")
   241  	// If Write hasn't been called then ignore the Flush - Release
   242  	// will pick it up
   243  	if !fh.writeCalled {
   244  		fs.Debugf(fh.remote, "WriteFileHandle.Flush unwritten handle, writing 0 bytes to avoid race conditions")
   245  		_, err := fh.writeAt([]byte{}, fh.offset)
   246  		return err
   247  	}
   248  	err := fh.close()
   249  	if err != nil {
   250  		fs.Errorf(fh.remote, "WriteFileHandle.Flush error: %v", err)
   251  	} else {
   252  		// fs.Debugf(fh.remote, "WriteFileHandle.Flush OK")
   253  	}
   254  	return err
   255  }
   256  
   257  // Release is called when we are finished with the file handle
   258  //
   259  // It isn't called directly from userspace so the error is ignored by
   260  // the kernel
   261  func (fh *WriteFileHandle) Release() error {
   262  	fh.mu.Lock()
   263  	defer fh.mu.Unlock()
   264  	if fh.closed {
   265  		fs.Debugf(fh.remote, "WriteFileHandle.Release nothing to do")
   266  		return nil
   267  	}
   268  	fs.Debugf(fh.remote, "WriteFileHandle.Release closing")
   269  	err := fh.close()
   270  	if err != nil {
   271  		fs.Errorf(fh.remote, "WriteFileHandle.Release error: %v", err)
   272  	} else {
   273  		// fs.Debugf(fh.remote, "WriteFileHandle.Release OK")
   274  	}
   275  	return err
   276  }
   277  
   278  // Stat returns info about the file
   279  func (fh *WriteFileHandle) Stat() (os.FileInfo, error) {
   280  	fh.mu.Lock()
   281  	defer fh.mu.Unlock()
   282  	return fh.file, nil
   283  }
   284  
   285  // Truncate file to given size
   286  func (fh *WriteFileHandle) Truncate(size int64) (err error) {
   287  	fh.mu.Lock()
   288  	defer fh.mu.Unlock()
   289  	if fh.closed {
   290  		return ECLOSED
   291  	}
   292  	if size != fh.offset {
   293  		fs.Errorf(fh.remote, "WriteFileHandle: Truncate: Can't change size without --vfs-cache-mode >= writes")
   294  		return EPERM
   295  	}
   296  	// File is correct size
   297  	if size == 0 {
   298  		fh.truncated = true
   299  	}
   300  	return nil
   301  }
   302  
   303  // Read reads up to len(p) bytes into p.
   304  func (fh *WriteFileHandle) Read(p []byte) (n int, err error) {
   305  	fs.Errorf(fh.remote, "WriteFileHandle: Read: Can't read and write to file without --vfs-cache-mode >= minimal")
   306  	return 0, EPERM
   307  }
   308  
   309  // ReadAt reads len(p) bytes into p starting at offset off in the
   310  // underlying input source. It returns the number of bytes read (0 <=
   311  // n <= len(p)) and any error encountered.
   312  func (fh *WriteFileHandle) ReadAt(p []byte, off int64) (n int, err error) {
   313  	fs.Errorf(fh.remote, "WriteFileHandle: ReadAt: Can't read and write to file without --vfs-cache-mode >= minimal")
   314  	return 0, EPERM
   315  }
   316  
   317  // Sync commits the current contents of the file to stable storage. Typically,
   318  // this means flushing the file system's in-memory copy of recently written
   319  // data to disk.
   320  func (fh *WriteFileHandle) Sync() error {
   321  	return nil
   322  }