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 {
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)

View File

@ -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

View File

@ -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)

View File

@ -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

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
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 {

View File

@ -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()")