diff --git a/cmd/cmount/fs.go b/cmd/cmount/fs.go index feff7d5cc..59d8bcd2b 100644 --- a/cmd/cmount/fs.go +++ b/cmd/cmount/fs.go @@ -337,31 +337,21 @@ func (fsys *FS) Statfs(path string, stat *fuse.Statfs_t) (errc int) { // Open opens a file func (fsys *FS) Open(path string, flags int) (errc int, fh uint64) { defer fs.Trace(path, "flags=0x%X", flags)("errc=%d, fh=0x%X", &errc, &fh) - file, errc := fsys.lookupFile(path) + + // fuse flags are based off syscall flags as are os flags, so + // should be compatible + handle, err := fsys.VFS.OpenFile(path, flags, 0777) if errc != 0 { - return errc, fhUnset + return translateError(err), fhUnset } - rdwrMode := flags & fuse.O_ACCMODE - var err error - var handle vfs.Noder - switch { - case rdwrMode == fuse.O_RDONLY: - handle, err = file.OpenRead() - if err != nil { - return translateError(err), fhUnset - } - return 0, fsys.openFilesRd.Open(handle) - case rdwrMode == fuse.O_WRONLY || (rdwrMode == fuse.O_RDWR && (flags&fuse.O_TRUNC) != 0): - handle, err = file.OpenWrite() - if err != nil { - return translateError(err), fhUnset - } - return 0, fsys.openFilesWr.Open(handle) - case rdwrMode == fuse.O_RDWR: - fs.Errorf(path, "Can't open for Read and Write") - return -fuse.EPERM, fhUnset + + switch fh := handle.(type) { + case *vfs.WriteFileHandle: + return 0, fsys.openFilesWr.Open(fh) + case *vfs.ReadFileHandle: + return 0, fsys.openFilesRd.Open(fh) } - fs.Errorf(path, "Can't figure out how to open with flags: 0x%X", flags) + return -fuse.EPERM, fhUnset } @@ -648,6 +638,8 @@ func translateError(err error) (errc int) { return -fuse.EBADF case vfs.EROFS: return -fuse.EROFS + case vfs.ENOSYS: + return -fuse.ENOSYS } fs.Errorf(nil, "IO error: %v", err) return -fuse.EIO diff --git a/cmd/mount/file.go b/cmd/mount/file.go index 024acc2ee..35a4f76ca 100644 --- a/cmd/mount/file.go +++ b/cmd/mount/file.go @@ -9,7 +9,6 @@ import ( fusefs "bazil.org/fuse/fs" "github.com/ncw/rclone/fs" "github.com/ncw/rclone/vfs" - "github.com/pkg/errors" "golang.org/x/net/context" ) @@ -62,40 +61,27 @@ var _ fusefs.NodeOpener = (*File)(nil) // Open the file for read or write func (f *File) Open(ctx context.Context, req *fuse.OpenRequest, resp *fuse.OpenResponse) (fh fusefs.Handle, err error) { defer fs.Trace(f, "flags=%v", req.Flags)("fh=%v, err=%v", &fh, &err) - switch { - case req.Flags.IsReadOnly(): - if f.VFS().Opt.NoSeek { - resp.Flags |= fuse.OpenNonSeekable - } - var rfh *vfs.ReadFileHandle - rfh, err = f.File.OpenRead() - fh = &ReadFileHandle{rfh} - case req.Flags.IsWriteOnly() || (req.Flags.IsReadWrite() && (req.Flags&fuse.OpenTruncate) != 0): - resp.Flags |= fuse.OpenNonSeekable - var wfh *vfs.WriteFileHandle - wfh, err = f.File.OpenWrite() - fh = &WriteFileHandle{wfh} - case req.Flags.IsReadWrite(): - err = errors.New("can't open for read and write simultaneously") - default: - err = errors.Errorf("can't figure out how to open with flags %v", req.Flags) - } - - /* - // File was opened in append-only mode, all writes will go to end - // of file. OS X does not provide this information. - OpenAppend OpenFlags = syscall.O_APPEND - OpenCreate OpenFlags = syscall.O_CREAT - OpenDirectory OpenFlags = syscall.O_DIRECTORY - OpenExclusive OpenFlags = syscall.O_EXCL - OpenNonblock OpenFlags = syscall.O_NONBLOCK - OpenSync OpenFlags = syscall.O_SYNC - OpenTruncate OpenFlags = syscall.O_TRUNC - */ + // fuse flags are based off syscall flags as are os flags, so + // should be compatible + handle, err := f.File.Open(int(resp.Flags)) if err != nil { 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: + resp.Flags |= fuse.OpenNonSeekable + fh = &WriteFileHandle{h} + default: + panic("unknown file handle type") + } + return fh, nil } diff --git a/cmd/mount/fs.go b/cmd/mount/fs.go index 8327d0e90..99bb55b5a 100644 --- a/cmd/mount/fs.go +++ b/cmd/mount/fs.go @@ -84,6 +84,8 @@ func translateError(err error) error { return fuse.Errno(syscall.EBADF) case vfs.EROFS: return fuse.Errno(syscall.EROFS) + case vfs.ENOSYS: + return fuse.Errno(syscall.ENOSYS) } return err } diff --git a/vfs/dir.go b/vfs/dir.go index 7ba6a96a3..b2f71eb77 100644 --- a/vfs/dir.go +++ b/vfs/dir.go @@ -211,7 +211,7 @@ func (d *Dir) _readDir() error { d.items[name] = newDir(d.vfs, d.f, d, dir) default: err = errors.Errorf("unknown type %T", item) - fs.Errorf(d.path, "readDir error: %v", err) + fs.Errorf(d, "readDir error: %v", err) return err } } @@ -276,12 +276,11 @@ func (d *Dir) SetModTime(modTime time.Time) error { // // Stat need not to handle the names "." and "..". func (d *Dir) Stat(name string) (node Node, err error) { - path := path.Join(d.path, name) // fs.Debugf(path, "Dir.Stat") node, err = d.stat(name) if err != nil { if err != ENOENT { - fs.Errorf(path, "Dir.Stat error: %v", err) + fs.Errorf(d, "Dir.Stat error: %v", err) } return nil, err } @@ -307,6 +306,16 @@ func (d *Dir) ReadDirAll() (items Nodes, err error) { return items, nil } +// Open the directory according to the flags provided +func (d *Dir) Open(flags int) (fd Handle, err error) { + rdwrMode := flags & (os.O_RDONLY | os.O_WRONLY | os.O_RDWR) + if rdwrMode != os.O_RDONLY { + fs.Errorf(d, "Can only open directories read only") + return nil, os.ErrPermission + } + return newDirHandle(d), nil +} + // Create makes a new file func (d *Dir) Create(name string) (*File, *WriteFileHandle, error) { if d.vfs.Opt.ReadOnly { @@ -319,7 +328,7 @@ func (d *Dir) Create(name string) (*File, *WriteFileHandle, error) { file := newFile(d, nil, name) fh, err := newWriteFileHandle(d, file, src) if err != nil { - fs.Errorf(path, "Dir.Create error: %v", err) + fs.Errorf(d, "Dir.Create error: %v", err) return nil, nil, err } // fs.Debugf(path, "Dir.Create OK") @@ -335,7 +344,7 @@ func (d *Dir) Mkdir(name string) (*Dir, error) { // fs.Debugf(path, "Dir.Mkdir") err := d.f.Mkdir(path) if err != nil { - fs.Errorf(path, "Dir.Mkdir failed to create directory: %v", err) + fs.Errorf(d, "Dir.Mkdir failed to create directory: %v", err) return nil, err } fsDir := fs.NewDir(path, time.Now()) @@ -353,17 +362,17 @@ func (d *Dir) Remove() error { // Check directory is empty first empty, err := d.isEmpty() if err != nil { - fs.Errorf(d.path, "Dir.Remove dir error: %v", err) + fs.Errorf(d, "Dir.Remove dir error: %v", err) return err } if !empty { - fs.Errorf(d.path, "Dir.Remove not empty") + fs.Errorf(d, "Dir.Remove not empty") return ENOTEMPTY } // remove directory err = d.f.Rmdir(d.path) if err != nil { - fs.Errorf(d.path, "Dir.Remove failed to remove directory: %v", err) + fs.Errorf(d, "Dir.Remove failed to remove directory: %v", err) return err } // Remove the item from the parent directory listing @@ -381,7 +390,7 @@ func (d *Dir) RemoveAll() error { // Remove contents of the directory nodes, err := d.ReadDirAll() if err != nil { - fs.Errorf(d.path, "Dir.RemoveAll failed to read directory: %v", err) + fs.Errorf(d, "Dir.RemoveAll failed to read directory: %v", err) return err } for _, node := range nodes { @@ -406,11 +415,10 @@ func (d *Dir) RemoveName(name string) error { if d.vfs.Opt.ReadOnly { return EROFS } - path := path.Join(d.path, name) // fs.Debugf(path, "Dir.Remove") node, err := d.stat(name) if err != nil { - fs.Errorf(path, "Dir.Remove error: %v", err) + fs.Errorf(d, "Dir.Remove error: %v", err) return err } return node.Remove() diff --git a/vfs/dir_handle.go b/vfs/dir_handle.go index 2a1e84286..b60065b2b 100644 --- a/vfs/dir_handle.go +++ b/vfs/dir_handle.go @@ -19,11 +19,27 @@ func newDirHandle(d *Dir) *DirHandle { } } +// String converts it to printable +func (fh *DirHandle) String() string { + if fh == nil { + return "" + } + if fh.d == nil { + return "" + } + return fh.d.String() + " (r)" +} + // Stat returns info about the current directory func (fh *DirHandle) Stat() (fi os.FileInfo, err error) { return fh.d, nil } +// Node returns the Node assocuated with this - satisfies Noder interface +func (fh *DirHandle) Node() Node { + return fh.d +} + // Readdir reads the contents of the directory associated with file and returns // a slice of up to n FileInfo values, as would be returned by Lstat, in // directory order. Subsequent calls on the same file will yield further diff --git a/vfs/file.go b/vfs/file.go index 0dc9e711c..d7043428e 100644 --- a/vfs/file.go +++ b/vfs/file.go @@ -161,7 +161,7 @@ func (f *File) applyPendingModTime() error { case fs.ErrorCantSetModTime, fs.ErrorCantSetModTimeWithoutDelete: // do nothing, in order to not break "touch somefile" if it exists already default: - fs.Errorf(f.o, "File.applyPendingModTime error: %v", err) + fs.Errorf(f, "File.applyPendingModTime error: %v", err) return err } @@ -216,7 +216,7 @@ func (f *File) OpenRead() (fh *ReadFileHandle, err error) { err = errors.Wrap(err, "open for read") if err != nil { - fs.Errorf(o, "File.OpenRead failed: %v", err) + fs.Errorf(f, "File.OpenRead failed: %v", err) return nil, err } return fh, nil @@ -239,7 +239,7 @@ func (f *File) OpenWrite() (fh *WriteFileHandle, err error) { err = errors.Wrap(err, "open for write") if err != nil { - fs.Errorf(o, "File.OpenWrite failed: %v", err) + fs.Errorf(f, "File.OpenWrite failed: %v", err) return nil, err } return fh, nil @@ -259,7 +259,7 @@ func (f *File) Remove() error { } err := f.o.Remove() if err != nil { - fs.Errorf(f.o, "File.Remove file error: %v", err) + fs.Errorf(f, "File.Remove file error: %v", err) return err } // Remove the item from the directory listing @@ -286,3 +286,27 @@ func (f *File) Dir() *Dir { func (f *File) VFS() *VFS { return f.d.vfs } + +// Open a file according to the flags provided +func (f *File) Open(flags int) (fd Handle, err error) { + rdwrMode := flags & (os.O_RDONLY | os.O_WRONLY | os.O_RDWR) + var read bool + switch { + case rdwrMode == os.O_RDONLY: + read = true + case rdwrMode == os.O_WRONLY || (rdwrMode == os.O_RDWR && (flags&os.O_TRUNC) != 0): + read = false + case rdwrMode == os.O_RDWR: + fs.Errorf(f, "Can't open for Read and Write") + return nil, os.ErrPermission + default: + fs.Errorf(f, "Can't figure out how to open with flags: 0x%X", flags) + return nil, os.ErrPermission + } + if read { + fd, err = f.OpenRead() + } else { + fd, err = f.OpenWrite() + } + return fd, err +} diff --git a/vfs/vfs.go b/vfs/vfs.go index 5329eeac9..8c5a1e723 100644 --- a/vfs/vfs.go +++ b/vfs/vfs.go @@ -50,6 +50,7 @@ type Node interface { RemoveAll() error DirEntry() fs.DirEntry VFS() *VFS + Open(flags int) (Handle, error) } // Check interfaces @@ -78,6 +79,7 @@ var ( _ Noder = (*Dir)(nil) _ Noder = (*ReadFileHandle)(nil) _ Noder = (*WriteFileHandle)(nil) + _ Noder = (*DirHandle)(nil) ) // Handle is the interface statisified by open files or directories. @@ -246,47 +248,19 @@ func (vfs *VFS) StatParent(name string) (dir *Dir, leaf string, err error) { // OpenFile a file according to the flags and perm provided func (vfs *VFS) OpenFile(name string, flags int, perm os.FileMode) (fd Handle, err error) { - rdwrMode := flags & (os.O_RDONLY | os.O_WRONLY | os.O_RDWR) - var read bool - switch { - case rdwrMode == os.O_RDONLY: - read = true - case rdwrMode == os.O_WRONLY || (rdwrMode == os.O_RDWR && (flags&os.O_TRUNC) != 0): - read = false - case rdwrMode == os.O_RDWR: - fs.Errorf(name, "Can't open for Read and Write") - return nil, os.ErrPermission - default: - fs.Errorf(name, "Can't figure out how to open with flags: 0x%X", flags) - return nil, os.ErrPermission - } node, err := vfs.Stat(name) if err != nil { - if err == os.ErrNotExist && !read { - return vfs.createFile(name, flags, perm) + if err == ENOENT && flags&os.O_CREATE != 0 { + dir, leaf, err := vfs.StatParent(name) + if err != nil { + return nil, err + } + _, fd, err = dir.Create(leaf) + return fd, err } return nil, err } - if node.IsFile() { - file := node.(*File) - if read { - fd, err = file.OpenRead() - } else { - fd, err = file.OpenWrite() - } - } else { - fd, err = newDirHandle(node.(*Dir)), nil - } - return fd, err -} - -func (vfs *VFS) createFile(name string, flags int, perm os.FileMode) (fd Handle, err error) { - dir, leaf, err := vfs.StatParent(name) - if err != nil { - return nil, err - } - _, fd, err = dir.Create(leaf) - return fd, err + return node.Open(flags) } // Rename oldName to newName