mirror of
https://github.com/netbirdio/gvisor.git
synced 2026-05-22 17:12:49 -07:00
Implement RENAME_NOREPLACE for all VFS2 filesystem implementations.
PiperOrigin-RevId: 377966969
This commit is contained in:
@@ -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() {
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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() {
|
||||
|
||||
@@ -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() {
|
||||
|
||||
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user