diff options
-rw-r--r-- | pkg/sentry/fsimpl/gofer/filesystem.go | 22 | ||||
-rw-r--r-- | pkg/sentry/fsimpl/kernfs/filesystem.go | 13 | ||||
-rw-r--r-- | pkg/sentry/fsimpl/overlay/filesystem.go | 16 | ||||
-rw-r--r-- | pkg/sentry/fsimpl/tmpfs/filesystem.go | 19 | ||||
-rw-r--r-- | test/syscalls/linux/rename.cc | 56 |
5 files changed, 105 insertions, 21 deletions
diff --git a/pkg/sentry/fsimpl/gofer/filesystem.go b/pkg/sentry/fsimpl/gofer/filesystem.go index 91ec4a142..eb09d54c3 100644 --- a/pkg/sentry/fsimpl/gofer/filesystem.go +++ b/pkg/sentry/fsimpl/gofer/filesystem.go @@ -1194,11 +1194,7 @@ func (fs *filesystem) ReadlinkAt(ctx context.Context, rp *vfs.ResolvingPath) (st // RenameAt implements vfs.FilesystemImpl.RenameAt. func (fs *filesystem) RenameAt(ctx context.Context, rp *vfs.ResolvingPath, oldParentVD vfs.VirtualDentry, oldName string, opts vfs.RenameOptions) error { - if opts.Flags != 0 { - // Requires 9P support. - return syserror.EINVAL - } - + // Resolve newParent first to verify that it's on this Mount. var ds *[]*dentry fs.renameMu.Lock() defer fs.renameMuUnlockAndCheckCaching(ctx, &ds) @@ -1206,8 +1202,21 @@ func (fs *filesystem) RenameAt(ctx context.Context, rp *vfs.ResolvingPath, oldPa if err != nil { return err } + + if opts.Flags&^linux.RENAME_NOREPLACE != 0 { + return syserror.EINVAL + } + if fs.opts.interop == InteropModeShared && opts.Flags&linux.RENAME_NOREPLACE != 0 { + // Requires 9P support to synchronize with other remote filesystem + // users. + return syserror.EINVAL + } + newName := rp.Component() if newName == "." || newName == ".." { + if opts.Flags&linux.RENAME_NOREPLACE != 0 { + return syserror.EEXIST + } return syserror.EBUSY } mnt := rp.Mount() @@ -1280,6 +1289,9 @@ func (fs *filesystem) RenameAt(ctx context.Context, rp *vfs.ResolvingPath, oldPa } var replacedVFSD *vfs.Dentry if replaced != nil { + if opts.Flags&linux.RENAME_NOREPLACE != 0 { + return syserror.EEXIST + } replacedVFSD = &replaced.vfsd if replaced.isDir() { if !renamed.isDir() { diff --git a/pkg/sentry/fsimpl/kernfs/filesystem.go b/pkg/sentry/fsimpl/kernfs/filesystem.go index f50b0fb08..8fac53c60 100644 --- a/pkg/sentry/fsimpl/kernfs/filesystem.go +++ b/pkg/sentry/fsimpl/kernfs/filesystem.go @@ -635,12 +635,6 @@ func (fs *Filesystem) ReadlinkAt(ctx context.Context, rp *vfs.ResolvingPath) (st // RenameAt implements vfs.FilesystemImpl.RenameAt. func (fs *Filesystem) RenameAt(ctx context.Context, rp *vfs.ResolvingPath, oldParentVD vfs.VirtualDentry, oldName string, opts vfs.RenameOptions) error { - // Only RENAME_NOREPLACE is supported. - if opts.Flags&^linux.RENAME_NOREPLACE != 0 { - return syserror.EINVAL - } - noReplace := opts.Flags&linux.RENAME_NOREPLACE != 0 - fs.mu.Lock() defer fs.processDeferredDecRefs(ctx) defer fs.mu.Unlock() @@ -651,6 +645,13 @@ func (fs *Filesystem) RenameAt(ctx context.Context, rp *vfs.ResolvingPath, oldPa if err != nil { return err } + + // Only RENAME_NOREPLACE is supported. + if opts.Flags&^linux.RENAME_NOREPLACE != 0 { + return syserror.EINVAL + } + noReplace := opts.Flags&linux.RENAME_NOREPLACE != 0 + mnt := rp.Mount() if mnt != oldParentVD.Mount() { return syserror.EXDEV diff --git a/pkg/sentry/fsimpl/overlay/filesystem.go b/pkg/sentry/fsimpl/overlay/filesystem.go index 46c500427..6b6fa0bd5 100644 --- a/pkg/sentry/fsimpl/overlay/filesystem.go +++ b/pkg/sentry/fsimpl/overlay/filesystem.go @@ -1017,10 +1017,7 @@ func (fs *filesystem) ReadlinkAt(ctx context.Context, rp *vfs.ResolvingPath) (st // RenameAt implements vfs.FilesystemImpl.RenameAt. func (fs *filesystem) RenameAt(ctx context.Context, rp *vfs.ResolvingPath, oldParentVD vfs.VirtualDentry, oldName string, opts vfs.RenameOptions) error { - if opts.Flags != 0 { - return syserror.EINVAL - } - + // Resolve newParent first to verify that it's on this Mount. var ds *[]*dentry fs.renameMu.Lock() defer fs.renameMuUnlockAndCheckDrop(ctx, &ds) @@ -1028,8 +1025,16 @@ func (fs *filesystem) RenameAt(ctx context.Context, rp *vfs.ResolvingPath, oldPa if err != nil { return err } + + if opts.Flags&^linux.RENAME_NOREPLACE != 0 { + return syserror.EINVAL + } + newName := rp.Component() if newName == "." || newName == ".." { + if opts.Flags&linux.RENAME_NOREPLACE != 0 { + return syserror.EEXIST + } return syserror.EBUSY } mnt := rp.Mount() @@ -1093,6 +1098,9 @@ func (fs *filesystem) RenameAt(ctx context.Context, rp *vfs.ResolvingPath, oldPa return err } if replaced != nil { + if opts.Flags&linux.RENAME_NOREPLACE != 0 { + return syserror.EEXIST + } replacedVFSD = &replaced.vfsd if replaced.isDir() { if !renamed.isDir() { diff --git a/pkg/sentry/fsimpl/tmpfs/filesystem.go b/pkg/sentry/fsimpl/tmpfs/filesystem.go index 766289e60..ee7ff2961 100644 --- a/pkg/sentry/fsimpl/tmpfs/filesystem.go +++ b/pkg/sentry/fsimpl/tmpfs/filesystem.go @@ -496,20 +496,24 @@ func (fs *filesystem) ReadlinkAt(ctx context.Context, rp *vfs.ResolvingPath) (st // RenameAt implements vfs.FilesystemImpl.RenameAt. func (fs *filesystem) RenameAt(ctx context.Context, rp *vfs.ResolvingPath, oldParentVD vfs.VirtualDentry, oldName string, opts vfs.RenameOptions) error { - if opts.Flags != 0 { - // TODO(b/145974740): Support renameat2 flags. - return syserror.EINVAL - } - - // Resolve newParent first to verify that it's on this Mount. + // Resolve newParentDir first to verify that it's on this Mount. fs.mu.Lock() defer fs.mu.Unlock() newParentDir, err := walkParentDirLocked(ctx, rp, rp.Start().Impl().(*dentry)) if err != nil { return err } + + if opts.Flags&^linux.RENAME_NOREPLACE != 0 { + // TODO(b/145974740): Support other renameat2 flags. + return syserror.EINVAL + } + newName := rp.Component() if newName == "." || newName == ".." { + if opts.Flags&linux.RENAME_NOREPLACE != 0 { + return syserror.EEXIST + } return syserror.EBUSY } mnt := rp.Mount() @@ -556,6 +560,9 @@ func (fs *filesystem) RenameAt(ctx context.Context, rp *vfs.ResolvingPath, oldPa } replaced, ok := newParentDir.childMap[newName] if ok { + if opts.Flags&linux.RENAME_NOREPLACE != 0 { + return syserror.EEXIST + } replacedDir, ok := replaced.inode.impl.(*directory) if ok { if !renamed.inode.isDir() { diff --git a/test/syscalls/linux/rename.cc b/test/syscalls/linux/rename.cc index 76a8da65f..561eed99f 100644 --- a/test/syscalls/linux/rename.cc +++ b/test/syscalls/linux/rename.cc @@ -26,6 +26,8 @@ #include "test/util/temp_path.h" #include "test/util/test_util.h" +using ::testing::AnyOf; + namespace gvisor { namespace testing { @@ -438,6 +440,60 @@ TEST(RenameTest, SysfsDirectoryToSelf) { EXPECT_THAT(rename(path.c_str(), path.c_str()), SyscallSucceeds()); } +#ifndef SYS_renameat2 +#if defined(__x86_64__) +#define SYS_renameat2 316 +#elif defined(__aarch64__) +#define SYS_renameat2 276 +#else +#error "Unknown architecture" +#endif +#endif // SYS_renameat2 + +#ifndef RENAME_NOREPLACE +#define RENAME_NOREPLACE (1 << 0) +#endif // RENAME_NOREPLACE + +int renameat2(int olddirfd, const char* oldpath, int newdirfd, + const char* newpath, unsigned int flags) { + return syscall(SYS_renameat2, olddirfd, oldpath, newdirfd, newpath, flags); +} + +TEST(Renameat2Test, NoReplaceSuccess) { + auto f = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateFile()); + std::string const newpath = NewTempAbsPath(); + // renameat2 may fail with ENOSYS (if the syscall is unsupported) or EINVAL + // (if flags are unsupported), or succeed (if RENAME_NOREPLACE is operating + // correctly). + EXPECT_THAT( + renameat2(AT_FDCWD, f.path().c_str(), AT_FDCWD, newpath.c_str(), + RENAME_NOREPLACE), + AnyOf(SyscallFailsWithErrno(AnyOf(ENOSYS, EINVAL)), SyscallSucceeds())); +} + +TEST(Renameat2Test, NoReplaceExisting) { + auto f1 = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateFile()); + auto f2 = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateFile()); + // renameat2 may fail with ENOSYS (if the syscall is unsupported), EINVAL (if + // flags are unsupported), or EEXIST (if RENAME_NOREPLACE is operating + // correctly). + EXPECT_THAT(renameat2(AT_FDCWD, f1.path().c_str(), AT_FDCWD, + f2.path().c_str(), RENAME_NOREPLACE), + SyscallFailsWithErrno(AnyOf(ENOSYS, EINVAL, EEXIST))); +} + +TEST(Renameat2Test, NoReplaceDot) { + auto d1 = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateDir()); + auto d2 = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateDir()); + // renameat2 may fail with ENOSYS (if the syscall is unsupported), EINVAL (if + // flags are unsupported), or EEXIST (if RENAME_NOREPLACE is operating + // correctly). + EXPECT_THAT( + renameat2(AT_FDCWD, d1.path().c_str(), AT_FDCWD, + absl::StrCat(d2.path(), "/.").c_str(), RENAME_NOREPLACE), + SyscallFailsWithErrno(AnyOf(ENOSYS, EINVAL, EEXIST))); +} + } // namespace } // namespace testing |