cmount, mount, mountlib: make --read-only reject modify operations

Normally mount/cmount use `-o ro` to get the kernel to mark the fs as
read only.  However this is ignored by WinFsp, so in addition if
`--read-only` is in effect then return EROFS ("Read only File System")
from all methods which attempt to modify something.
This commit is contained in:
Nick Craig-Wood 2017-05-11 13:15:51 +01:00
parent 8be8a8e41b
commit b259f8b752
6 changed files with 52 additions and 1 deletions

View File

@ -44,6 +44,9 @@ func NewFS(f fs.Fs) *FS {
if noChecksum { if noChecksum {
fsys.FS.NoChecksum() fsys.FS.NoChecksum()
} }
if readOnly {
fsys.FS.ReadOnly()
}
return fsys return fsys
} }
@ -659,6 +662,8 @@ func translateError(err error) (errc int) {
return -fuse.ESPIPE return -fuse.ESPIPE
case mountlib.EBADF: case mountlib.EBADF:
return -fuse.EBADF return -fuse.EBADF
case mountlib.EROFS:
return -fuse.EROFS
} }
} }
fs.Errorf(nil, "IO error: %v", err) fs.Errorf(nil, "IO error: %v", err)

View File

@ -5,6 +5,8 @@
package mount package mount
import ( import (
"syscall"
"bazil.org/fuse" "bazil.org/fuse"
fusefs "bazil.org/fuse/fs" fusefs "bazil.org/fuse/fs"
"github.com/ncw/rclone/cmd/mountlib" "github.com/ncw/rclone/cmd/mountlib"
@ -34,6 +36,9 @@ func NewFS(f fs.Fs) *FS {
if noChecksum { if noChecksum {
fsys.FS.NoChecksum() fsys.FS.NoChecksum()
} }
if readOnly {
fsys.FS.ReadOnly()
}
return fsys return fsys
} }
@ -75,12 +80,20 @@ func translateError(err error) error {
cause := errors.Cause(err) cause := errors.Cause(err)
if mErr, ok := cause.(mountlib.Error); ok { if mErr, ok := cause.(mountlib.Error); ok {
switch mErr { switch mErr {
case mountlib.OK:
return nil
case mountlib.ENOENT: case mountlib.ENOENT:
return fuse.ENOENT return fuse.ENOENT
case mountlib.ENOTEMPTY: case mountlib.ENOTEMPTY:
return fuse.EEXIST // return fuse.ENOTEMPTY - doesn't exist though so use EEXIST return fuse.Errno(syscall.ENOTEMPTY)
case mountlib.EEXIST: case mountlib.EEXIST:
return fuse.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 return err

View File

@ -242,6 +242,9 @@ func (d *Dir) ModTime() time.Time {
// SetModTime sets the modTime for this dir // SetModTime sets the modTime for this dir
func (d *Dir) SetModTime(modTime time.Time) error { func (d *Dir) SetModTime(modTime time.Time) error {
if d.fsys.readOnly {
return EROFS
}
d.mu.Lock() d.mu.Lock()
defer d.mu.Unlock() defer d.mu.Unlock()
d.modTime = modTime d.modTime = modTime
@ -312,6 +315,9 @@ func (d *Dir) ReadDirAll() (items []*DirEntry, err error) {
// Create makes a new file // Create makes a new file
func (d *Dir) Create(name string) (*File, *WriteFileHandle, error) { func (d *Dir) Create(name string) (*File, *WriteFileHandle, error) {
if d.fsys.readOnly {
return nil, nil, EROFS
}
path := path.Join(d.path, name) path := path.Join(d.path, name)
// fs.Debugf(path, "Dir.Create") // fs.Debugf(path, "Dir.Create")
src := newCreateInfo(d.f, path) src := newCreateInfo(d.f, path)
@ -328,6 +334,9 @@ func (d *Dir) Create(name string) (*File, *WriteFileHandle, error) {
// Mkdir creates a new directory // Mkdir creates a new directory
func (d *Dir) Mkdir(name string) (*Dir, error) { func (d *Dir) Mkdir(name string) (*Dir, error) {
if d.fsys.readOnly {
return nil, EROFS
}
path := path.Join(d.path, name) path := path.Join(d.path, name)
// fs.Debugf(path, "Dir.Mkdir") // fs.Debugf(path, "Dir.Mkdir")
err := d.f.Mkdir(path) 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 // the receiver, which must be a directory. The entry to be removed
// may correspond to a file (unlink) or to a directory (rmdir). // may correspond to a file (unlink) or to a directory (rmdir).
func (d *Dir) Remove(name string) error { func (d *Dir) Remove(name string) error {
if d.fsys.readOnly {
return EROFS
}
path := path.Join(d.path, name) path := path.Join(d.path, name)
// fs.Debugf(path, "Dir.Remove") // fs.Debugf(path, "Dir.Remove")
item, err := d.lookupNode(name) item, err := d.lookupNode(name)
@ -393,6 +405,9 @@ func (d *Dir) Remove(name string) error {
// Rename the file // Rename the file
func (d *Dir) Rename(oldName, newName string, destDir *Dir) error { func (d *Dir) Rename(oldName, newName string, destDir *Dir) error {
if d.fsys.readOnly {
return EROFS
}
oldPath := path.Join(d.path, oldName) oldPath := path.Join(d.path, oldName)
newPath := path.Join(destDir.path, newName) newPath := path.Join(destDir.path, newName)
// fs.Debugf(oldPath, "Dir.Rename to %q", newPath) // fs.Debugf(oldPath, "Dir.Rename to %q", newPath)

View File

@ -7,6 +7,8 @@ import "fmt"
// Error describes low level errors in a cross platform way // Error describes low level errors in a cross platform way
type Error byte type Error byte
// NB if changing errors translateError in cmd/mount/fs.go, cmd/cmount/fs.go
// Low level errors // Low level errors
const ( const (
OK Error = iota OK Error = iota
@ -15,6 +17,7 @@ const (
EEXIST EEXIST
ESPIPE ESPIPE
EBADF EBADF
EROFS
) )
var errorNames = []string{ var errorNames = []string{
@ -24,6 +27,7 @@ var errorNames = []string{
EEXIST: "File exists", EEXIST: "File exists",
ESPIPE: "Illegal seek", ESPIPE: "Illegal seek",
EBADF: "Bad file descriptor", EBADF: "Bad file descriptor",
EROFS: "Read only file system",
} }
// Error renders the error as a string // Error renders the error as a string

View File

@ -93,6 +93,9 @@ func (f *File) Attr(noModTime bool) (modTime time.Time, Size, Blocks uint64, err
// SetModTime sets the modtime for the file // SetModTime sets the modtime for the file
func (f *File) SetModTime(modTime time.Time) error { func (f *File) SetModTime(modTime time.Time) error {
if f.d.fsys.readOnly {
return EROFS
}
f.mu.Lock() f.mu.Lock()
defer f.mu.Unlock() defer f.mu.Unlock()
@ -188,6 +191,9 @@ func (f *File) OpenRead() (fh *ReadFileHandle, err error) {
// OpenWrite open the file for write // OpenWrite open the file for write
func (f *File) OpenWrite() (fh *WriteFileHandle, err error) { 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 // if o is nil it isn't valid yet
o, err := f.waitForValidObject() o, err := f.waitForValidObject()
if err != nil { if err != nil {

View File

@ -39,6 +39,7 @@ type FS struct {
root *Dir root *Dir
noSeek bool // don't allow seeking if set noSeek bool // don't allow seeking if set
noChecksum bool // don't check checksums 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 // NewFS creates a new filing system and root directory
@ -66,6 +67,13 @@ func (fsys *FS) NoChecksum() *FS {
return fsys 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 // Root returns the root node
func (fsys *FS) Root() (*Dir, error) { func (fsys *FS) Root() (*Dir, error) {
// fs.Debugf(fsys.f, "Root()") // fs.Debugf(fsys.f, "Root()")