2017-10-28 21:01:34 +02:00
|
|
|
package vfs
|
2017-05-02 23:35:07 +02:00
|
|
|
|
|
|
|
import (
|
2019-06-17 10:34:30 +02:00
|
|
|
"context"
|
2017-05-02 23:35:07 +02:00
|
|
|
"io"
|
2017-10-29 22:11:17 +01:00
|
|
|
"os"
|
2017-05-02 23:35:07 +02:00
|
|
|
"sync"
|
2019-08-07 17:41:45 +02:00
|
|
|
"sync/atomic"
|
2017-09-16 22:49:08 +02:00
|
|
|
"time"
|
2017-05-02 23:35:07 +02:00
|
|
|
|
2019-07-28 19:47:38 +02:00
|
|
|
"github.com/rclone/rclone/fs"
|
|
|
|
"github.com/rclone/rclone/fs/operations"
|
2017-05-02 23:35:07 +02:00
|
|
|
)
|
|
|
|
|
|
|
|
// WriteFileHandle is an open for write handle on a File
|
|
|
|
type WriteFileHandle struct {
|
2017-10-29 22:11:17 +01:00
|
|
|
baseHandle
|
2017-05-02 23:35:07 +02:00
|
|
|
mu sync.Mutex
|
2019-08-07 17:41:45 +02:00
|
|
|
cond *sync.Cond // cond lock for out of sequence writes
|
|
|
|
closed bool // set if handle has been closed
|
2017-05-02 23:35:07 +02:00
|
|
|
remote string
|
|
|
|
pipeWriter *io.PipeWriter
|
|
|
|
o fs.Object
|
|
|
|
result chan error
|
|
|
|
file *File
|
|
|
|
writeCalled bool // set the first time Write() is called
|
|
|
|
offset int64
|
2017-11-20 19:01:42 +01:00
|
|
|
opened bool
|
|
|
|
flags int
|
|
|
|
truncated bool
|
2017-05-02 23:35:07 +02:00
|
|
|
}
|
|
|
|
|
2017-10-27 22:41:34 +02:00
|
|
|
// Check interfaces
|
|
|
|
var (
|
|
|
|
_ io.Writer = (*WriteFileHandle)(nil)
|
|
|
|
_ io.WriterAt = (*WriteFileHandle)(nil)
|
|
|
|
_ io.Closer = (*WriteFileHandle)(nil)
|
|
|
|
)
|
|
|
|
|
2017-11-20 19:01:42 +01:00
|
|
|
func newWriteFileHandle(d *Dir, f *File, remote string, flags int) (*WriteFileHandle, error) {
|
2017-05-02 23:35:07 +02:00
|
|
|
fh := &WriteFileHandle{
|
2017-11-06 13:25:54 +01:00
|
|
|
remote: remote,
|
2017-11-20 19:01:42 +01:00
|
|
|
flags: flags,
|
2017-05-02 23:35:07 +02:00
|
|
|
result: make(chan error, 1),
|
|
|
|
file: f,
|
|
|
|
}
|
2019-08-07 17:41:45 +02:00
|
|
|
fh.cond = sync.NewCond(&fh.mu)
|
2017-11-20 19:01:42 +01:00
|
|
|
fh.file.addWriter(fh)
|
|
|
|
return fh, nil
|
|
|
|
}
|
|
|
|
|
|
|
|
// returns whether it is OK to truncate the file
|
|
|
|
func (fh *WriteFileHandle) safeToTruncate() bool {
|
|
|
|
return fh.truncated || fh.flags&os.O_TRUNC != 0 || !fh.file.exists()
|
|
|
|
}
|
|
|
|
|
|
|
|
// openPending opens the file if there is a pending open
|
|
|
|
//
|
|
|
|
// call with the lock held
|
|
|
|
func (fh *WriteFileHandle) openPending() (err error) {
|
|
|
|
if fh.opened {
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
if !fh.safeToTruncate() {
|
2017-12-02 13:06:05 +01:00
|
|
|
fs.Errorf(fh.remote, "WriteFileHandle: Can't open for write without O_TRUNC on existing file without --vfs-cache-mode >= writes")
|
2017-11-20 19:01:42 +01:00
|
|
|
return EPERM
|
|
|
|
}
|
2017-06-26 12:58:12 +02:00
|
|
|
var pipeReader *io.PipeReader
|
|
|
|
pipeReader, fh.pipeWriter = io.Pipe()
|
2017-05-02 23:35:07 +02:00
|
|
|
go func() {
|
2017-10-29 22:14:05 +01:00
|
|
|
// NB Rcat deals with Stats.Transferring etc
|
2020-04-14 19:03:45 +02:00
|
|
|
o, err := operations.Rcat(context.TODO(), fh.file.Fs(), fh.remote, pipeReader, time.Now())
|
2017-06-26 12:58:12 +02:00
|
|
|
if err != nil {
|
2017-09-16 22:49:08 +02:00
|
|
|
fs.Errorf(fh.remote, "WriteFileHandle.New Rcat failed: %v", err)
|
2017-06-26 12:58:12 +02:00
|
|
|
}
|
2017-09-16 22:49:08 +02:00
|
|
|
// Close the pipeReader so the pipeWriter fails with ErrClosedPipe
|
|
|
|
_ = pipeReader.Close()
|
2017-05-02 23:35:07 +02:00
|
|
|
fh.o = o
|
|
|
|
fh.result <- err
|
|
|
|
}()
|
|
|
|
fh.file.setSize(0)
|
2017-11-20 19:01:42 +01:00
|
|
|
fh.truncated = true
|
2020-04-14 19:03:45 +02:00
|
|
|
fh.file.Dir().addObject(fh.file) // make sure the directory has this object in it now
|
2017-11-20 19:01:42 +01:00
|
|
|
fh.opened = true
|
|
|
|
return nil
|
2017-05-02 23:35:07 +02:00
|
|
|
}
|
|
|
|
|
2017-05-09 12:29:02 +02:00
|
|
|
// String converts it to printable
|
|
|
|
func (fh *WriteFileHandle) String() string {
|
|
|
|
if fh == nil {
|
|
|
|
return "<nil *WriteFileHandle>"
|
|
|
|
}
|
2017-11-03 17:11:44 +01:00
|
|
|
fh.mu.Lock()
|
|
|
|
defer fh.mu.Unlock()
|
2017-05-09 12:29:02 +02:00
|
|
|
if fh.file == nil {
|
|
|
|
return "<nil *WriteFileHandle.file>"
|
|
|
|
}
|
|
|
|
return fh.file.String() + " (w)"
|
|
|
|
}
|
|
|
|
|
2017-05-02 23:35:07 +02:00
|
|
|
// Node returns the Node assocuated with this - satisfies Noder interface
|
|
|
|
func (fh *WriteFileHandle) Node() Node {
|
2017-11-03 17:11:44 +01:00
|
|
|
fh.mu.Lock()
|
|
|
|
defer fh.mu.Unlock()
|
2017-05-02 23:35:07 +02:00
|
|
|
return fh.file
|
|
|
|
}
|
|
|
|
|
2017-10-27 22:41:34 +02:00
|
|
|
// WriteAt writes len(p) bytes from p to the underlying data stream at offset
|
|
|
|
// off. It returns the number of bytes written from p (0 <= n <= len(p)) and
|
|
|
|
// any error encountered that caused the write to stop early. WriteAt must
|
|
|
|
// return a non-nil error if it returns n < len(p).
|
|
|
|
//
|
|
|
|
// If WriteAt is writing to a destination with a seek offset, WriteAt should
|
|
|
|
// not affect nor be affected by the underlying seek offset.
|
|
|
|
//
|
|
|
|
// Clients of WriteAt can execute parallel WriteAt calls on the same
|
|
|
|
// destination if the ranges do not overlap.
|
|
|
|
//
|
|
|
|
// Implementations must not retain p.
|
|
|
|
func (fh *WriteFileHandle) WriteAt(p []byte, off int64) (n int, err error) {
|
2017-05-02 23:35:07 +02:00
|
|
|
fh.mu.Lock()
|
|
|
|
defer fh.mu.Unlock()
|
2017-11-03 17:11:44 +01:00
|
|
|
return fh.writeAt(p, off)
|
|
|
|
}
|
|
|
|
|
|
|
|
// Implementatino of WriteAt - call with lock held
|
|
|
|
func (fh *WriteFileHandle) writeAt(p []byte, off int64) (n int, err error) {
|
2019-05-10 14:22:55 +02:00
|
|
|
// defer log.Trace(fh.remote, "len=%d off=%d", len(p), off)("n=%d, fh.off=%d, err=%v", &n, &fh.offset, &err)
|
2017-05-02 23:35:07 +02:00
|
|
|
if fh.closed {
|
2017-11-20 19:01:42 +01:00
|
|
|
fs.Errorf(fh.remote, "WriteFileHandle.Write: error: %v", EBADF)
|
2017-11-03 12:35:36 +01:00
|
|
|
return 0, ECLOSED
|
2017-05-02 23:35:07 +02:00
|
|
|
}
|
2019-08-07 17:41:45 +02:00
|
|
|
if fh.offset != off {
|
|
|
|
// Set a background timer so we don't wait forever
|
2020-04-14 19:03:45 +02:00
|
|
|
maxWait := fh.file.VFS().Opt.WriteWait
|
2020-02-04 11:56:00 +01:00
|
|
|
timeout := time.NewTimer(maxWait)
|
2019-08-07 17:41:45 +02:00
|
|
|
done := make(chan struct{})
|
|
|
|
abort := int32(0)
|
|
|
|
go func() {
|
|
|
|
select {
|
|
|
|
case <-timeout.C:
|
|
|
|
// set abort flag an give all the waiting goroutines a kick on timeout
|
|
|
|
atomic.StoreInt32(&abort, 1)
|
|
|
|
fh.cond.Broadcast()
|
|
|
|
case <-done:
|
|
|
|
}
|
|
|
|
}()
|
|
|
|
// Wait for an in-sequence write or abort
|
|
|
|
for fh.offset != off && atomic.LoadInt32(&abort) == 0 {
|
|
|
|
// fs.Debugf(fh.remote, "waiting for in-sequence write to %d", off)
|
|
|
|
fh.cond.Wait()
|
|
|
|
}
|
|
|
|
// tidy up end timer
|
|
|
|
close(done)
|
|
|
|
timeout.Stop()
|
2019-05-10 14:22:55 +02:00
|
|
|
}
|
2017-10-29 22:14:05 +01:00
|
|
|
if fh.offset != off {
|
2017-12-02 13:06:05 +01:00
|
|
|
fs.Errorf(fh.remote, "WriteFileHandle.Write: can't seek in file without --vfs-cache-mode >= writes")
|
2017-10-29 22:14:05 +01:00
|
|
|
return 0, ESPIPE
|
|
|
|
}
|
2017-11-20 19:01:42 +01:00
|
|
|
if err = fh.openPending(); err != nil {
|
|
|
|
return 0, err
|
|
|
|
}
|
2017-05-02 23:35:07 +02:00
|
|
|
fh.writeCalled = true
|
2017-10-27 22:41:34 +02:00
|
|
|
n, err = fh.pipeWriter.Write(p)
|
|
|
|
fh.offset += int64(n)
|
2017-05-02 23:35:07 +02:00
|
|
|
fh.file.setSize(fh.offset)
|
|
|
|
if err != nil {
|
|
|
|
fs.Errorf(fh.remote, "WriteFileHandle.Write error: %v", err)
|
|
|
|
return 0, err
|
|
|
|
}
|
2017-05-09 12:39:33 +02:00
|
|
|
// fs.Debugf(fh.remote, "WriteFileHandle.Write OK (%d bytes written)", n)
|
2019-08-07 17:41:45 +02:00
|
|
|
fh.cond.Broadcast() // wake everyone up waiting for an in-sequence read
|
2017-10-27 22:41:34 +02:00
|
|
|
return n, nil
|
|
|
|
}
|
|
|
|
|
|
|
|
// Write writes len(p) bytes from p to the underlying data stream. It returns
|
|
|
|
// the number of bytes written from p (0 <= n <= len(p)) and any error
|
|
|
|
// encountered that caused the write to stop early. Write must return a non-nil
|
|
|
|
// error if it returns n < len(p). Write must not modify the slice data, even
|
|
|
|
// temporarily.
|
|
|
|
//
|
|
|
|
// Implementations must not retain p.
|
|
|
|
func (fh *WriteFileHandle) Write(p []byte) (n int, err error) {
|
2017-11-03 17:11:44 +01:00
|
|
|
fh.mu.Lock()
|
|
|
|
defer fh.mu.Unlock()
|
2017-10-27 22:41:34 +02:00
|
|
|
// Since we can't seek, just call WriteAt with the current offset
|
2017-11-03 17:11:44 +01:00
|
|
|
return fh.writeAt(p, fh.offset)
|
2017-05-02 23:35:07 +02:00
|
|
|
}
|
|
|
|
|
2017-11-16 10:31:33 +01:00
|
|
|
// WriteString a string to the file
|
|
|
|
func (fh *WriteFileHandle) WriteString(s string) (n int, err error) {
|
|
|
|
return fh.Write([]byte(s))
|
|
|
|
}
|
|
|
|
|
2017-05-08 19:05:12 +02:00
|
|
|
// Offset returns the offset of the file pointer
|
2017-05-02 23:35:07 +02:00
|
|
|
func (fh *WriteFileHandle) Offset() (offset int64) {
|
2017-11-03 17:11:44 +01:00
|
|
|
fh.mu.Lock()
|
|
|
|
defer fh.mu.Unlock()
|
2017-05-02 23:35:07 +02:00
|
|
|
return fh.offset
|
|
|
|
}
|
|
|
|
|
|
|
|
// close the file handle returning EBADF if it has been
|
|
|
|
// closed already.
|
|
|
|
//
|
|
|
|
// Must be called with fh.mu held
|
2017-11-20 19:01:42 +01:00
|
|
|
func (fh *WriteFileHandle) close() (err error) {
|
2017-05-02 23:35:07 +02:00
|
|
|
if fh.closed {
|
2017-11-03 12:35:36 +01:00
|
|
|
return ECLOSED
|
2017-05-02 23:35:07 +02:00
|
|
|
}
|
|
|
|
fh.closed = true
|
2017-11-18 12:55:39 +01:00
|
|
|
// leave writer open until file is transferred
|
2018-03-06 21:47:11 +01:00
|
|
|
defer func() {
|
|
|
|
fh.file.delWriter(fh, false)
|
|
|
|
fh.file.finishWriterClose()
|
|
|
|
}()
|
2017-11-20 19:01:42 +01:00
|
|
|
// If file not opened and not safe to truncate then then leave file intact
|
|
|
|
if !fh.opened && !fh.safeToTruncate() {
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
if err = fh.openPending(); err != nil {
|
|
|
|
return err
|
|
|
|
}
|
2017-05-02 23:35:07 +02:00
|
|
|
writeCloseErr := fh.pipeWriter.Close()
|
2017-11-20 19:01:42 +01:00
|
|
|
err = <-fh.result
|
2017-05-02 23:35:07 +02:00
|
|
|
if err == nil {
|
|
|
|
fh.file.setObject(fh.o)
|
|
|
|
err = writeCloseErr
|
|
|
|
}
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
2017-10-29 22:14:05 +01:00
|
|
|
// Close closes the file
|
|
|
|
func (fh *WriteFileHandle) Close() error {
|
|
|
|
fh.mu.Lock()
|
|
|
|
defer fh.mu.Unlock()
|
|
|
|
return fh.close()
|
|
|
|
}
|
|
|
|
|
2017-05-02 23:35:07 +02:00
|
|
|
// Flush is called on each close() of a file descriptor. So if a
|
|
|
|
// filesystem wants to return write errors in close() and the file has
|
|
|
|
// cached dirty data, this is a good place to write back data and
|
|
|
|
// return any errors. Since many applications ignore close() errors
|
|
|
|
// this is not always useful.
|
|
|
|
//
|
|
|
|
// NOTE: The flush() method may be called more than once for each
|
|
|
|
// open(). This happens if more than one file descriptor refers to an
|
|
|
|
// opened file due to dup(), dup2() or fork() calls. It is not
|
|
|
|
// possible to determine if a flush is final, so each flush should be
|
|
|
|
// treated equally. Multiple write-flush sequences are relatively
|
|
|
|
// rare, so this shouldn't be a problem.
|
|
|
|
//
|
|
|
|
// Filesystems shouldn't assume that flush will always be called after
|
|
|
|
// some writes, or that if will be called at all.
|
|
|
|
func (fh *WriteFileHandle) Flush() error {
|
|
|
|
fh.mu.Lock()
|
|
|
|
defer fh.mu.Unlock()
|
2017-10-29 22:14:05 +01:00
|
|
|
if fh.closed {
|
|
|
|
fs.Debugf(fh.remote, "WriteFileHandle.Flush nothing to do")
|
|
|
|
return nil
|
|
|
|
}
|
2017-05-09 12:39:33 +02:00
|
|
|
// fs.Debugf(fh.remote, "WriteFileHandle.Flush")
|
2017-05-02 23:35:07 +02:00
|
|
|
// If Write hasn't been called then ignore the Flush - Release
|
|
|
|
// will pick it up
|
|
|
|
if !fh.writeCalled {
|
2018-01-20 11:10:55 +01:00
|
|
|
fs.Debugf(fh.remote, "WriteFileHandle.Flush unwritten handle, writing 0 bytes to avoid race conditions")
|
|
|
|
_, err := fh.writeAt([]byte{}, fh.offset)
|
|
|
|
return err
|
2017-05-02 23:35:07 +02:00
|
|
|
}
|
|
|
|
err := fh.close()
|
|
|
|
if err != nil {
|
|
|
|
fs.Errorf(fh.remote, "WriteFileHandle.Flush error: %v", err)
|
|
|
|
} else {
|
2017-05-09 12:39:33 +02:00
|
|
|
// fs.Debugf(fh.remote, "WriteFileHandle.Flush OK")
|
2017-05-02 23:35:07 +02:00
|
|
|
}
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
|
|
|
// Release is called when we are finished with the file handle
|
|
|
|
//
|
|
|
|
// It isn't called directly from userspace so the error is ignored by
|
|
|
|
// the kernel
|
|
|
|
func (fh *WriteFileHandle) Release() error {
|
|
|
|
fh.mu.Lock()
|
|
|
|
defer fh.mu.Unlock()
|
|
|
|
if fh.closed {
|
|
|
|
fs.Debugf(fh.remote, "WriteFileHandle.Release nothing to do")
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
fs.Debugf(fh.remote, "WriteFileHandle.Release closing")
|
|
|
|
err := fh.close()
|
|
|
|
if err != nil {
|
|
|
|
fs.Errorf(fh.remote, "WriteFileHandle.Release error: %v", err)
|
|
|
|
} else {
|
2017-05-09 12:39:33 +02:00
|
|
|
// fs.Debugf(fh.remote, "WriteFileHandle.Release OK")
|
2017-05-02 23:35:07 +02:00
|
|
|
}
|
|
|
|
return err
|
|
|
|
}
|
2017-10-24 22:06:06 +02:00
|
|
|
|
2017-10-29 22:11:17 +01:00
|
|
|
// Stat returns info about the file
|
|
|
|
func (fh *WriteFileHandle) Stat() (os.FileInfo, error) {
|
2017-11-03 17:11:44 +01:00
|
|
|
fh.mu.Lock()
|
|
|
|
defer fh.mu.Unlock()
|
2017-10-29 22:11:17 +01:00
|
|
|
return fh.file, nil
|
|
|
|
}
|
2017-11-16 10:31:33 +01:00
|
|
|
|
|
|
|
// Truncate file to given size
|
|
|
|
func (fh *WriteFileHandle) Truncate(size int64) (err error) {
|
|
|
|
fh.mu.Lock()
|
|
|
|
defer fh.mu.Unlock()
|
|
|
|
if fh.closed {
|
|
|
|
return ECLOSED
|
|
|
|
}
|
|
|
|
if size != fh.offset {
|
2018-01-25 10:51:43 +01:00
|
|
|
fs.Errorf(fh.remote, "WriteFileHandle: Truncate: Can't change size without --vfs-cache-mode >= writes")
|
2017-11-16 10:31:33 +01:00
|
|
|
return EPERM
|
|
|
|
}
|
|
|
|
// File is correct size
|
2017-11-20 19:01:42 +01:00
|
|
|
if size == 0 {
|
|
|
|
fh.truncated = true
|
|
|
|
}
|
2017-11-16 10:31:33 +01:00
|
|
|
return nil
|
|
|
|
}
|
2017-11-16 11:55:24 +01:00
|
|
|
|
|
|
|
// Read reads up to len(p) bytes into p.
|
|
|
|
func (fh *WriteFileHandle) Read(p []byte) (n int, err error) {
|
2018-01-25 10:51:43 +01:00
|
|
|
fs.Errorf(fh.remote, "WriteFileHandle: Read: Can't read and write to file without --vfs-cache-mode >= minimal")
|
2017-11-16 11:55:24 +01:00
|
|
|
return 0, EPERM
|
|
|
|
}
|
|
|
|
|
|
|
|
// ReadAt reads len(p) bytes into p starting at offset off in the
|
|
|
|
// underlying input source. It returns the number of bytes read (0 <=
|
|
|
|
// n <= len(p)) and any error encountered.
|
|
|
|
func (fh *WriteFileHandle) ReadAt(p []byte, off int64) (n int, err error) {
|
2018-01-25 10:51:43 +01:00
|
|
|
fs.Errorf(fh.remote, "WriteFileHandle: ReadAt: Can't read and write to file without --vfs-cache-mode >= minimal")
|
2017-11-16 11:55:24 +01:00
|
|
|
return 0, EPERM
|
|
|
|
}
|
2017-11-18 16:48:49 +01:00
|
|
|
|
|
|
|
// Sync commits the current contents of the file to stable storage. Typically,
|
|
|
|
// this means flushing the file system's in-memory copy of recently written
|
|
|
|
// data to disk.
|
|
|
|
func (fh *WriteFileHandle) Sync() error {
|
|
|
|
return nil
|
|
|
|
}
|