summaryrefslogtreecommitdiffhomepage
path: root/runsc
diff options
context:
space:
mode:
authorFabricio Voznika <fvoznika@google.com>2020-08-18 13:57:03 -0700
committergVisor bot <gvisor-bot@google.com>2020-08-18 13:58:42 -0700
commit760c131da17250b6adbb8d08dd52e9a3d652b2c1 (patch)
tree18f0f2a432c0bc933a38044df4a6d87e23be3d7a /runsc
parent4141dc0d2c8c46b14dbae83aab304fa338ebafc5 (diff)
Return EROFS if mount is read-only
PiperOrigin-RevId: 327300635
Diffstat (limited to 'runsc')
-rw-r--r--runsc/fsgofer/fsgofer.go104
-rw-r--r--runsc/fsgofer/fsgofer_test.go84
2 files changed, 117 insertions, 71 deletions
diff --git a/runsc/fsgofer/fsgofer.go b/runsc/fsgofer/fsgofer.go
index c6694c278..639de9ca1 100644
--- a/runsc/fsgofer/fsgofer.go
+++ b/runsc/fsgofer/fsgofer.go
@@ -46,6 +46,8 @@ const (
invalidMode = p9.OpenFlags(math.MaxUint32)
openFlags = syscall.O_NOFOLLOW | syscall.O_CLOEXEC
+
+ allowedOpenFlags = unix.O_TRUNC
)
// Config sets configuration options for each attach point.
@@ -357,10 +359,16 @@ func (l *localFile) Open(flags p9.OpenFlags) (*fd.FD, p9.QID, uint32, error) {
if l.isOpen() {
panic(fmt.Sprintf("attempting to open already opened file: %q", l.hostPath))
}
+ mode := flags & p9.OpenFlagsModeMask
+ if mode == p9.WriteOnly || mode == p9.ReadWrite || flags&p9.OpenTruncate != 0 {
+ if err := l.checkROMount(); err != nil {
+ return nil, p9.QID{}, 0, err
+ }
+ }
// Check if control file can be used or if a new open must be created.
var newFile *fd.FD
- if flags == p9.ReadOnly && l.controlReadable {
+ if mode == p9.ReadOnly && l.controlReadable && flags.OSFlags()&allowedOpenFlags == 0 {
log.Debugf("Open reusing control file, flags: %v, %q", flags, l.hostPath)
newFile = l.file
} else {
@@ -369,8 +377,8 @@ func (l *localFile) Open(flags p9.OpenFlags) (*fd.FD, p9.QID, uint32, error) {
// name_to_handle_at and open_by_handle_at aren't supported by overlay2.
log.Debugf("Open reopening file, flags: %v, %q", flags, l.hostPath)
var err error
- // Constrain open flags to the open mode and O_TRUNC.
- newFile, err = reopenProcFd(l.file, openFlags|(flags.OSFlags()&(syscall.O_ACCMODE|syscall.O_TRUNC)))
+ osFlags := flags.OSFlags() & (syscall.O_ACCMODE | allowedOpenFlags)
+ newFile, err = reopenProcFd(l.file, openFlags|osFlags)
if err != nil {
return nil, p9.QID{}, 0, extractErrno(err)
}
@@ -389,31 +397,31 @@ func (l *localFile) Open(flags p9.OpenFlags) (*fd.FD, p9.QID, uint32, error) {
}
l.file = newFile
}
- l.mode = flags & p9.OpenFlagsModeMask
+ l.mode = mode
return fd, l.qid, 0, nil
}
// Create implements p9.File.
-func (l *localFile) Create(name string, mode p9.OpenFlags, perm p9.FileMode, uid p9.UID, gid p9.GID) (*fd.FD, p9.File, p9.QID, uint32, error) {
- conf := l.attachPoint.conf
- if conf.ROMount {
- if conf.PanicOnWrite {
- panic("attempt to write to RO mount")
- }
- return nil, nil, p9.QID{}, 0, syscall.EBADF
+func (l *localFile) Create(name string, p9Flags p9.OpenFlags, perm p9.FileMode, uid p9.UID, gid p9.GID) (*fd.FD, p9.File, p9.QID, uint32, error) {
+ if err := l.checkROMount(); err != nil {
+ return nil, nil, p9.QID{}, 0, err
}
+ // Set file creation flags, plus allowed open flags from caller.
+ osFlags := openFlags | syscall.O_CREAT | syscall.O_EXCL
+ osFlags |= p9Flags.OSFlags() & allowedOpenFlags
+
// 'file' may be used for other operations (e.g. Walk), so read access is
// always added to flags. Note that resulting file might have a wider mode
// than needed for each particular case.
- flags := openFlags | syscall.O_CREAT | syscall.O_EXCL
+ mode := p9Flags & p9.OpenFlagsModeMask
if mode == p9.WriteOnly {
- flags |= syscall.O_RDWR
+ osFlags |= syscall.O_RDWR
} else {
- flags |= mode.OSFlags()
+ osFlags |= mode.OSFlags() & unix.O_ACCMODE
}
- child, err := fd.OpenAt(l.file, name, flags, uint32(perm.Permissions()))
+ child, err := fd.OpenAt(l.file, name, osFlags, uint32(perm.Permissions()))
if err != nil {
return nil, nil, p9.QID{}, 0, extractErrno(err)
}
@@ -449,12 +457,8 @@ func (l *localFile) Create(name string, mode p9.OpenFlags, perm p9.FileMode, uid
// Mkdir implements p9.File.
func (l *localFile) Mkdir(name string, perm p9.FileMode, uid p9.UID, gid p9.GID) (p9.QID, error) {
- conf := l.attachPoint.conf
- if conf.ROMount {
- if conf.PanicOnWrite {
- panic("attempt to write to RO mount")
- }
- return p9.QID{}, syscall.EBADF
+ if err := l.checkROMount(); err != nil {
+ return p9.QID{}, err
}
if err := syscall.Mkdirat(l.file.FD(), name, uint32(perm.Permissions())); err != nil {
@@ -637,12 +641,8 @@ func (l *localFile) fillAttr(stat syscall.Stat_t) (p9.AttrMask, p9.Attr) {
// cannot be changed atomically and user may see partial changes when
// an error happens.
func (l *localFile) SetAttr(valid p9.SetAttrMask, attr p9.SetAttr) error {
- conf := l.attachPoint.conf
- if conf.ROMount {
- if conf.PanicOnWrite {
- panic("attempt to write to RO mount")
- }
- return syscall.EBADF
+ if err := l.checkROMount(); err != nil {
+ return err
}
allowed := p9.SetAttrMask{
@@ -804,12 +804,8 @@ func (*localFile) Rename(p9.File, string) error {
// RenameAt implements p9.File.RenameAt.
func (l *localFile) RenameAt(oldName string, directory p9.File, newName string) error {
- conf := l.attachPoint.conf
- if conf.ROMount {
- if conf.PanicOnWrite {
- panic("attempt to write to RO mount")
- }
- return syscall.EBADF
+ if err := l.checkROMount(); err != nil {
+ return err
}
newParent := directory.(*localFile)
@@ -855,12 +851,8 @@ func (l *localFile) WriteAt(p []byte, offset uint64) (int, error) {
// Symlink implements p9.File.
func (l *localFile) Symlink(target, newName string, uid p9.UID, gid p9.GID) (p9.QID, error) {
- conf := l.attachPoint.conf
- if conf.ROMount {
- if conf.PanicOnWrite {
- panic("attempt to write to RO mount")
- }
- return p9.QID{}, syscall.EBADF
+ if err := l.checkROMount(); err != nil {
+ return p9.QID{}, err
}
if err := unix.Symlinkat(target, l.file.FD(), newName); err != nil {
@@ -895,12 +887,8 @@ func (l *localFile) Symlink(target, newName string, uid p9.UID, gid p9.GID) (p9.
// Link implements p9.File.
func (l *localFile) Link(target p9.File, newName string) error {
- conf := l.attachPoint.conf
- if conf.ROMount {
- if conf.PanicOnWrite {
- panic("attempt to write to RO mount")
- }
- return syscall.EBADF
+ if err := l.checkROMount(); err != nil {
+ return err
}
targetFile := target.(*localFile)
@@ -912,12 +900,8 @@ func (l *localFile) Link(target p9.File, newName string) error {
// Mknod implements p9.File.
func (l *localFile) Mknod(name string, mode p9.FileMode, _ uint32, _ uint32, _ p9.UID, _ p9.GID) (p9.QID, error) {
- conf := l.attachPoint.conf
- if conf.ROMount {
- if conf.PanicOnWrite {
- panic("attempt to write to RO mount")
- }
- return p9.QID{}, syscall.EROFS
+ if err := l.checkROMount(); err != nil {
+ return p9.QID{}, err
}
hostPath := path.Join(l.hostPath, name)
@@ -948,12 +932,8 @@ func (l *localFile) Mknod(name string, mode p9.FileMode, _ uint32, _ uint32, _ p
// UnlinkAt implements p9.File.
func (l *localFile) UnlinkAt(name string, flags uint32) error {
- conf := l.attachPoint.conf
- if conf.ROMount {
- if conf.PanicOnWrite {
- panic("attempt to write to RO mount")
- }
- return syscall.EBADF
+ if err := l.checkROMount(); err != nil {
+ return err
}
if err := unix.Unlinkat(l.file.FD(), name, int(flags)); err != nil {
@@ -1178,3 +1158,13 @@ func extractErrno(err error) syscall.Errno {
log.Debugf("Unknown error: %v, defaulting to EIO", err)
return syscall.EIO
}
+
+func (l *localFile) checkROMount() error {
+ if conf := l.attachPoint.conf; conf.ROMount {
+ if conf.PanicOnWrite {
+ panic("attempt to write to RO mount")
+ }
+ return syscall.EROFS
+ }
+ return nil
+}
diff --git a/runsc/fsgofer/fsgofer_test.go b/runsc/fsgofer/fsgofer_test.go
index 94f167417..8ed703584 100644
--- a/runsc/fsgofer/fsgofer_test.go
+++ b/runsc/fsgofer/fsgofer_test.go
@@ -491,30 +491,50 @@ func TestLink(t *testing.T) {
}
func TestROMountChecks(t *testing.T) {
+ const want = syscall.EROFS
runCustom(t, allTypes, roConfs, func(t *testing.T, s state) {
- if _, _, _, _, err := s.file.Create("some_file", p9.ReadWrite, 0777, p9.UID(os.Getuid()), p9.GID(os.Getgid())); err != syscall.EBADF {
- t.Errorf("%v: Create() should have failed, got: %v, expected: syscall.EBADF", s, err)
+ if s.fileType != syscall.S_IFLNK {
+ if _, _, _, err := s.file.Open(p9.WriteOnly); err != want {
+ t.Errorf("Open() should have failed, got: %v, expected: %v", err, want)
+ }
+ if _, _, _, err := s.file.Open(p9.ReadWrite); err != want {
+ t.Errorf("Open() should have failed, got: %v, expected: %v", err, want)
+ }
+ if _, _, _, err := s.file.Open(p9.ReadOnly | p9.OpenTruncate); err != want {
+ t.Errorf("Open() should have failed, got: %v, expected: %v", err, want)
+ }
+ f, _, _, err := s.file.Open(p9.ReadOnly)
+ if err != nil {
+ t.Errorf("Open() failed: %v", err)
+ }
+ if f != nil {
+ _ = f.Close()
+ }
+ }
+
+ if _, _, _, _, err := s.file.Create("some_file", p9.ReadWrite, 0777, p9.UID(os.Getuid()), p9.GID(os.Getgid())); err != want {
+ t.Errorf("Create() should have failed, got: %v, expected: %v", err, want)
}
- if _, err := s.file.Mkdir("some_dir", 0777, p9.UID(os.Getuid()), p9.GID(os.Getgid())); err != syscall.EBADF {
- t.Errorf("%v: MkDir() should have failed, got: %v, expected: syscall.EBADF", s, err)
+ if _, err := s.file.Mkdir("some_dir", 0777, p9.UID(os.Getuid()), p9.GID(os.Getgid())); err != want {
+ t.Errorf("MkDir() should have failed, got: %v, expected: %v", err, want)
}
- if err := s.file.RenameAt("some_file", s.file, "other_file"); err != syscall.EBADF {
- t.Errorf("%v: Rename() should have failed, got: %v, expected: syscall.EBADF", s, err)
+ if err := s.file.RenameAt("some_file", s.file, "other_file"); err != want {
+ t.Errorf("Rename() should have failed, got: %v, expected: %v", err, want)
}
- if _, err := s.file.Symlink("some_place", "some_symlink", p9.UID(os.Getuid()), p9.GID(os.Getgid())); err != syscall.EBADF {
- t.Errorf("%v: Symlink() should have failed, got: %v, expected: syscall.EBADF", s, err)
+ if _, err := s.file.Symlink("some_place", "some_symlink", p9.UID(os.Getuid()), p9.GID(os.Getgid())); err != want {
+ t.Errorf("Symlink() should have failed, got: %v, expected: %v", err, want)
}
- if err := s.file.UnlinkAt("some_file", 0); err != syscall.EBADF {
- t.Errorf("%v: UnlinkAt() should have failed, got: %v, expected: syscall.EBADF", s, err)
+ if err := s.file.UnlinkAt("some_file", 0); err != want {
+ t.Errorf("UnlinkAt() should have failed, got: %v, expected: %v", err, want)
}
- if err := s.file.Link(s.file, "some_link"); err != syscall.EBADF {
- t.Errorf("%v: Link() should have failed, got: %v, expected: syscall.EBADF", s, err)
+ if err := s.file.Link(s.file, "some_link"); err != want {
+ t.Errorf("Link() should have failed, got: %v, expected: %v", err, want)
}
valid := p9.SetAttrMask{Size: true}
attr := p9.SetAttr{Size: 0}
- if err := s.file.SetAttr(valid, attr); err != syscall.EBADF {
- t.Errorf("%v: SetAttr() should have failed, got: %v, expected: syscall.EBADF", s, err)
+ if err := s.file.SetAttr(valid, attr); err != want {
+ t.Errorf("SetAttr() should have failed, got: %v, expected: %v", err, want)
}
})
}
@@ -522,6 +542,9 @@ func TestROMountChecks(t *testing.T) {
func TestROMountPanics(t *testing.T) {
conf := Config{ROMount: true, PanicOnWrite: true}
runCustom(t, allTypes, []Config{conf}, func(t *testing.T, s state) {
+ if s.fileType != syscall.S_IFLNK {
+ assertPanic(t, func() { s.file.Open(p9.WriteOnly) })
+ }
assertPanic(t, func() { s.file.Create("some_file", p9.ReadWrite, 0777, p9.UID(os.Getuid()), p9.GID(os.Getgid())) })
assertPanic(t, func() { s.file.Mkdir("some_dir", 0777, p9.UID(os.Getuid()), p9.GID(os.Getgid())) })
assertPanic(t, func() { s.file.RenameAt("some_file", s.file, "other_file") })
@@ -741,3 +764,36 @@ func TestDoubleAttachError(t *testing.T) {
t.Fatalf("Attach should have failed, got %v want non-nil", err)
}
}
+
+func TestTruncate(t *testing.T) {
+ runCustom(t, []uint32{syscall.S_IFDIR}, rwConfs, func(t *testing.T, s state) {
+ child, err := createFile(s.file, "test")
+ if err != nil {
+ t.Fatalf("createFile() failed, err: %v", err)
+ }
+ defer child.Close()
+ want := []byte("foobar")
+ w, err := child.WriteAt(want, 0)
+ if err != nil {
+ t.Fatalf("Write() failed, err: %v", err)
+ }
+ if w != len(want) {
+ t.Fatalf("Write() was partial, got: %d, expected: %d", w, len(want))
+ }
+
+ _, l, err := s.file.Walk([]string{"test"})
+ if err != nil {
+ t.Fatalf("Walk(%s) failed, err: %v", "test", err)
+ }
+ if _, _, _, err := l.Open(p9.ReadOnly | p9.OpenTruncate); err != nil {
+ t.Fatalf("Open() failed, err: %v", err)
+ }
+ _, mask, attr, err := l.GetAttr(p9.AttrMask{Size: true})
+ if !mask.Size {
+ t.Fatalf("GetAttr() didn't return size: %+v", mask)
+ }
+ if attr.Size != 0 {
+ t.Fatalf("truncate didn't work, want: 0, got: %d", attr.Size)
+ }
+ })
+}