diff --git a/cmd/cmount/fs.go b/cmd/cmount/fs.go index a24debea9..0c1a931ac 100644 --- a/cmd/cmount/fs.go +++ b/cmd/cmount/fs.go @@ -44,6 +44,9 @@ func NewFS(f fs.Fs) *FS { if noChecksum { fsys.FS.NoChecksum() } + if readOnly { + fsys.FS.ReadOnly() + } return fsys } @@ -659,6 +662,8 @@ func translateError(err error) (errc int) { return -fuse.ESPIPE case mountlib.EBADF: return -fuse.EBADF + case mountlib.EROFS: + return -fuse.EROFS } } fs.Errorf(nil, "IO error: %v", err) diff --git a/cmd/mount/fs.go b/cmd/mount/fs.go index 3897b6346..455864b3a 100644 --- a/cmd/mount/fs.go +++ b/cmd/mount/fs.go @@ -5,6 +5,8 @@ package mount import ( + "syscall" + "bazil.org/fuse" fusefs "bazil.org/fuse/fs" "github.com/ncw/rclone/cmd/mountlib" @@ -34,6 +36,9 @@ func NewFS(f fs.Fs) *FS { if noChecksum { fsys.FS.NoChecksum() } + if readOnly { + fsys.FS.ReadOnly() + } return fsys } @@ -75,12 +80,20 @@ func translateError(err error) error { cause := errors.Cause(err) if mErr, ok := cause.(mountlib.Error); ok { switch mErr { + case mountlib.OK: + return nil case mountlib.ENOENT: return fuse.ENOENT case mountlib.ENOTEMPTY: - return fuse.EEXIST // return fuse.ENOTEMPTY - doesn't exist though so use EEXIST + return fuse.Errno(syscall.ENOTEMPTY) case mountlib.EEXIST: return fuse.EEXIST + case mountlib.ESPIPE: + return fuse.Errno(syscall.ESPIPE) + case mountlib.EBADF: + return fuse.Errno(syscall.EBADF) + case mountlib.EROFS: + return fuse.Errno(syscall.EROFS) } } return err diff --git a/cmd/mountlib/dir.go b/cmd/mountlib/dir.go index 2ed85c04f..214082241 100644 --- a/cmd/mountlib/dir.go +++ b/cmd/mountlib/dir.go @@ -242,6 +242,9 @@ func (d *Dir) ModTime() time.Time { // SetModTime sets the modTime for this dir func (d *Dir) SetModTime(modTime time.Time) error { + if d.fsys.readOnly { + return EROFS + } d.mu.Lock() defer d.mu.Unlock() d.modTime = modTime @@ -312,6 +315,9 @@ func (d *Dir) ReadDirAll() (items []*DirEntry, err error) { // Create makes a new file func (d *Dir) Create(name string) (*File, *WriteFileHandle, error) { + if d.fsys.readOnly { + return nil, nil, EROFS + } path := path.Join(d.path, name) // fs.Debugf(path, "Dir.Create") src := newCreateInfo(d.f, path) @@ -328,6 +334,9 @@ func (d *Dir) Create(name string) (*File, *WriteFileHandle, error) { // Mkdir creates a new directory func (d *Dir) Mkdir(name string) (*Dir, error) { + if d.fsys.readOnly { + return nil, EROFS + } path := path.Join(d.path, name) // fs.Debugf(path, "Dir.Mkdir") err := d.f.Mkdir(path) @@ -349,6 +358,9 @@ func (d *Dir) Mkdir(name string) (*Dir, error) { // the receiver, which must be a directory. The entry to be removed // may correspond to a file (unlink) or to a directory (rmdir). func (d *Dir) Remove(name string) error { + if d.fsys.readOnly { + return EROFS + } path := path.Join(d.path, name) // fs.Debugf(path, "Dir.Remove") item, err := d.lookupNode(name) @@ -393,6 +405,9 @@ func (d *Dir) Remove(name string) error { // Rename the file func (d *Dir) Rename(oldName, newName string, destDir *Dir) error { + if d.fsys.readOnly { + return EROFS + } oldPath := path.Join(d.path, oldName) newPath := path.Join(destDir.path, newName) // fs.Debugf(oldPath, "Dir.Rename to %q", newPath) diff --git a/cmd/mountlib/errors.go b/cmd/mountlib/errors.go index 430a07e60..6c00e78cc 100644 --- a/cmd/mountlib/errors.go +++ b/cmd/mountlib/errors.go @@ -7,6 +7,8 @@ import "fmt" // Error describes low level errors in a cross platform way type Error byte +// NB if changing errors translateError in cmd/mount/fs.go, cmd/cmount/fs.go + // Low level errors const ( OK Error = iota @@ -15,6 +17,7 @@ const ( EEXIST ESPIPE EBADF + EROFS ) var errorNames = []string{ @@ -24,6 +27,7 @@ var errorNames = []string{ EEXIST: "File exists", ESPIPE: "Illegal seek", EBADF: "Bad file descriptor", + EROFS: "Read only file system", } // Error renders the error as a string diff --git a/cmd/mountlib/file.go b/cmd/mountlib/file.go index 404e8b664..9a91c9e9b 100644 --- a/cmd/mountlib/file.go +++ b/cmd/mountlib/file.go @@ -93,6 +93,9 @@ func (f *File) Attr(noModTime bool) (modTime time.Time, Size, Blocks uint64, err // SetModTime sets the modtime for the file func (f *File) SetModTime(modTime time.Time) error { + if f.d.fsys.readOnly { + return EROFS + } f.mu.Lock() defer f.mu.Unlock() @@ -188,6 +191,9 @@ func (f *File) OpenRead() (fh *ReadFileHandle, err error) { // OpenWrite open the file for write func (f *File) OpenWrite() (fh *WriteFileHandle, err error) { + if f.d.fsys.readOnly { + return nil, EROFS + } // if o is nil it isn't valid yet o, err := f.waitForValidObject() if err != nil { diff --git a/cmd/mountlib/fs.go b/cmd/mountlib/fs.go index 89c2c4591..0266f97e9 100644 --- a/cmd/mountlib/fs.go +++ b/cmd/mountlib/fs.go @@ -39,6 +39,7 @@ type FS struct { root *Dir noSeek bool // don't allow seeking if set noChecksum bool // don't check checksums if set + readOnly bool // if set FS is read only } // NewFS creates a new filing system and root directory @@ -66,6 +67,13 @@ func (fsys *FS) NoChecksum() *FS { return fsys } +// ReadOnly sets the fs into read only mode, returning EROFS for any +// write operations. +func (fsys *FS) ReadOnly() *FS { + fsys.readOnly = true + return fsys +} + // Root returns the root node func (fsys *FS) Root() (*Dir, error) { // fs.Debugf(fsys.f, "Root()")