diff --git a/cmd/mount/dir.go b/cmd/mount/dir.go index d71c8c71b..1155b1353 100644 --- a/cmd/mount/dir.go +++ b/cmd/mount/dir.go @@ -126,7 +126,7 @@ func (d *Dir) Create(ctx context.Context, req *fuse.CreateRequest, resp *fuse.Cr if err != nil { return nil, nil, translateError(err) } - return &File{file}, &WriteFileHandle{fh}, err + return &File{file}, &FileHandle{fh}, err } var _ fusefs.NodeMkdirer = (*Dir)(nil) diff --git a/cmd/mount/file.go b/cmd/mount/file.go index ecb7e1df3..016d47334 100644 --- a/cmd/mount/file.go +++ b/cmd/mount/file.go @@ -69,20 +69,12 @@ func (f *File) Open(ctx context.Context, req *fuse.OpenRequest, resp *fuse.OpenR return nil, translateError(err) } - switch h := handle.(type) { - case *vfs.ReadFileHandle: - if f.VFS().Opt.NoSeek { - resp.Flags |= fuse.OpenNonSeekable - } - fh = &ReadFileHandle{h} - case *vfs.WriteFileHandle: + // See if seeking is supported and set FUSE hint accordingly + if _, err = handle.Seek(0, 1); err != nil { resp.Flags |= fuse.OpenNonSeekable - fh = &WriteFileHandle{h} - default: - panic("unknown file handle type") } - return fh, nil + return &FileHandle{handle}, nil } // Check interface satisfied diff --git a/cmd/mount/write.go b/cmd/mount/handle.go similarity index 72% rename from cmd/mount/write.go rename to cmd/mount/handle.go index 42f17c8b8..c37bf9400 100644 --- a/cmd/mount/write.go +++ b/cmd/mount/handle.go @@ -3,7 +3,6 @@ package mount import ( - "errors" "io" "bazil.org/fuse" @@ -13,11 +12,9 @@ import ( "golang.org/x/net/context" ) -var errClosedFileHandle = errors.New("Attempt to use closed file handle") - -// WriteFileHandle is an open for write handle on a File -type WriteFileHandle struct { - *vfs.WriteFileHandle +// FileHandle is an open for read file handle on a File +type FileHandle struct { + vfs.Handle } // Check interface satisfied @@ -39,12 +36,12 @@ func (fh *FileHandle) Read(ctx context.Context, req *fuse.ReadRequest, resp *fus } // Check interface satisfied -var _ fusefs.HandleWriter = (*WriteFileHandle)(nil) +var _ fusefs.HandleWriter = (*FileHandle)(nil) // Write data to the file handle -func (fh *WriteFileHandle) Write(ctx context.Context, req *fuse.WriteRequest, resp *fuse.WriteResponse) (err error) { +func (fh *FileHandle) Write(ctx context.Context, req *fuse.WriteRequest, resp *fuse.WriteResponse) (err error) { defer fs.Trace(fh, "len=%d, offset=%d", len(req.Data), req.Offset)("written=%d, err=%v", &resp.Size, &err) - n, err := fh.WriteFileHandle.WriteAt(req.Data, req.Offset) + n, err := fh.Handle.WriteAt(req.Data, req.Offset) if err != nil { return translateError(err) } @@ -52,6 +49,9 @@ func (fh *WriteFileHandle) Write(ctx context.Context, req *fuse.WriteRequest, re return nil } +// Check interface satisfied +var _ fusefs.HandleFlusher = (*FileHandle)(nil) + // 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 @@ -67,18 +67,18 @@ func (fh *WriteFileHandle) Write(ctx context.Context, req *fuse.WriteRequest, re // // 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(ctx context.Context, req *fuse.FlushRequest) (err error) { +func (fh *FileHandle) Flush(ctx context.Context, req *fuse.FlushRequest) (err error) { defer fs.Trace(fh, "")("err=%v", &err) - return translateError(fh.WriteFileHandle.Flush()) + return translateError(fh.Handle.Flush()) } -var _ fusefs.HandleReleaser = (*WriteFileHandle)(nil) +var _ fusefs.HandleReleaser = (*FileHandle)(nil) // 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(ctx context.Context, req *fuse.ReleaseRequest) (err error) { +func (fh *FileHandle) Release(ctx context.Context, req *fuse.ReleaseRequest) (err error) { defer fs.Trace(fh, "")("err=%v", &err) - return translateError(fh.WriteFileHandle.Release()) + return translateError(fh.Handle.Release()) } diff --git a/cmd/mount/read.go b/cmd/mount/read.go deleted file mode 100644 index d2ff56314..000000000 --- a/cmd/mount/read.go +++ /dev/null @@ -1,57 +0,0 @@ -// +build linux darwin freebsd - -package mount - -import ( - "bazil.org/fuse" - fusefs "bazil.org/fuse/fs" - "github.com/ncw/rclone/fs" - "github.com/ncw/rclone/vfs" - "golang.org/x/net/context" -) - -// ReadFileHandle is an open for read file handle on a File -type ReadFileHandle struct { - *vfs.ReadFileHandle -} - -// Check interface satisfied -var _ fusefs.Handle = (*ReadFileHandle)(nil) - -// Check interface satisfied -var _ fusefs.HandleReader = (*ReadFileHandle)(nil) - -// Read from the file handle -func (fh *ReadFileHandle) Read(ctx context.Context, req *fuse.ReadRequest, resp *fuse.ReadResponse) (err error) { - var n int - defer fs.Trace(fh, "len=%d, offset=%d", req.Size, req.Offset)("read=%d, err=%v", &n, &err) - data := make([]byte, req.Size) - n, err = fh.ReadFileHandle.ReadAt(data, req.Offset) - if err != nil { - return translateError(err) - } - resp.Data = data[:n] - return nil -} - -// Check interface satisfied -var _ fusefs.HandleFlusher = (*ReadFileHandle)(nil) - -// Flush is called each time the file or directory is closed. -// Because there can be multiple file descriptors referring to a -// single opened file, Flush can be called multiple times. -func (fh *ReadFileHandle) Flush(ctx context.Context, req *fuse.FlushRequest) (err error) { - defer fs.Trace(fh, "")("err=%v", &err) - return translateError(fh.ReadFileHandle.Flush()) -} - -var _ fusefs.HandleReleaser = (*ReadFileHandle)(nil) - -// 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 *ReadFileHandle) Release(ctx context.Context, req *fuse.ReleaseRequest) (err error) { - defer fs.Trace(fh, "")("err=%v", &err) - return translateError(fh.ReadFileHandle.Release()) -} diff --git a/vfs/read.go b/vfs/read.go index 6e30f6aef..0b14f1ab3 100644 --- a/vfs/read.go +++ b/vfs/read.go @@ -124,8 +124,11 @@ func (fh *ReadFileHandle) seek(offset int64, reopen bool) (err error) { return nil } -// Seek the file +// Seek the file - returns ESPIPE if seeking isn't possible func (fh *ReadFileHandle) Seek(offset int64, whence int) (n int64, err error) { + if fh.noSeek { + return 0, ESPIPE + } size := fh.o.Size() switch whence { case 0: diff --git a/vfs/vfs.go b/vfs/vfs.go index ca182c923..cf4aa9002 100644 --- a/vfs/vfs.go +++ b/vfs/vfs.go @@ -102,6 +102,9 @@ type Handle interface { Write(b []byte) (n int, err error) WriteAt(b []byte, off int64) (n int, err error) WriteString(s string) (n int, err error) + // Additional methods useful for FUSE filesystems + Flush() error + Release() error } // baseHandle implements all the missing methods @@ -124,6 +127,8 @@ func (h baseHandle) Truncate(size int64) error { retu func (h baseHandle) Write(b []byte) (n int, err error) { return 0, ENOSYS } func (h baseHandle) WriteAt(b []byte, off int64) (n int, err error) { return 0, ENOSYS } func (h baseHandle) WriteString(s string) (n int, err error) { return 0, ENOSYS } +func (h baseHandle) Flush() (err error) { return ENOSYS } +func (h baseHandle) Release() (err error) { return ENOSYS } // Check interfaces var ( @@ -131,7 +136,6 @@ var ( _ Handle = (*ReadFileHandle)(nil) _ Handle = (*WriteFileHandle)(nil) _ Handle = (*DirHandle)(nil) - _ Handle = (*os.File)(nil) ) // VFS represents the top level filing system