commit
069fff34a5
|
@ -117,6 +117,16 @@ func Convert(
|
|||
io = to
|
||||
co = &to.commonOp
|
||||
|
||||
case *bazilfuse.RenameRequest:
|
||||
to := &RenameOp{
|
||||
OldParent: InodeID(typed.Header.Node),
|
||||
OldName: typed.OldName,
|
||||
NewParent: InodeID(typed.NewDir),
|
||||
NewName: typed.NewName,
|
||||
}
|
||||
io = to
|
||||
co = &to.commonOp
|
||||
|
||||
case *bazilfuse.RemoveRequest:
|
||||
if typed.Dir {
|
||||
to := &RmDirOp{
|
||||
|
|
|
@ -358,6 +358,58 @@ func (o *CreateSymlinkOp) toBazilfuseResponse() (bfResp interface{}) {
|
|||
// Unlinking
|
||||
////////////////////////////////////////////////////////////////////////
|
||||
|
||||
// Rename a file or directory, given the IDs of the original parent directory
|
||||
// and the new one (which may be the same).
|
||||
//
|
||||
// In Linux, this is called by vfs_rename (https://goo.gl/eERItT), which is
|
||||
// called by sys_renameat2 (https://goo.gl/fCC9qC).
|
||||
//
|
||||
// The kernel takes care of ensuring that the source and destination are not
|
||||
// identical (in which case it does nothing), that the rename is not across
|
||||
// file system boundaries, and that the destination doesn't already exist with
|
||||
// the wrong type. Some subtleties that the file system must care about:
|
||||
//
|
||||
// * If the new name is an existing directory, the file system must ensure it
|
||||
// is empty before replacing it, returning ENOTEMPTY otherwise. (This is
|
||||
// per the posix spec: http://goo.gl/4XtT79)
|
||||
//
|
||||
// * The rename must be atomic from the point of view of an observer of the
|
||||
// new name. That is, if the new name already exists, there must be no
|
||||
// point at which it doesn't exist.
|
||||
//
|
||||
// * It is okay for the new name to be modified before the old name is
|
||||
// removed; these need not be atomic. In fact, the Linux man page
|
||||
// explicitly says this is likely (cf. https://goo.gl/Y1wVZc).
|
||||
//
|
||||
// * Linux bends over backwards (https://goo.gl/pLDn3r) to ensure that
|
||||
// neither the old nor the new parent can be concurrently modified. But
|
||||
// it's not clear whether OS X does this, and in any case it doesn't matter
|
||||
// for file systems that may be modified remotely. Therefore a careful file
|
||||
// system implementor should probably ensure if possible that the unlink
|
||||
// step in the "link new name, unlink old name" process doesn't unlink a
|
||||
// different inode than the one that was linked to the new name. Still,
|
||||
// posix and the man pages are imprecise about the actual semantics of a
|
||||
// rename if it's not atomic, so it is probably not disastrous to be loose
|
||||
// about this.
|
||||
//
|
||||
type RenameOp struct {
|
||||
commonOp
|
||||
|
||||
// The old parent directory, and the name of the entry within it to be
|
||||
// relocated.
|
||||
OldParent InodeID
|
||||
OldName string
|
||||
|
||||
// The new parent directory, and the name of the entry to be created or
|
||||
// overwritten within it.
|
||||
NewParent InodeID
|
||||
NewName string
|
||||
}
|
||||
|
||||
func (o *RenameOp) toBazilfuseResponse() (bfResp interface{}) {
|
||||
return
|
||||
}
|
||||
|
||||
// Unlink a directory from its parent. Because directories cannot have a link
|
||||
// count above one, this means the directory inode should be deleted as well
|
||||
// once the kernel sends ForgetInodeOp.
|
||||
|
|
|
@ -47,6 +47,7 @@ type FileSystem interface {
|
|||
MkDir(*fuseops.MkDirOp) error
|
||||
CreateFile(*fuseops.CreateFileOp) error
|
||||
CreateSymlink(*fuseops.CreateSymlinkOp) error
|
||||
Rename(*fuseops.RenameOp) error
|
||||
RmDir(*fuseops.RmDirOp) error
|
||||
Unlink(*fuseops.UnlinkOp) error
|
||||
OpenDir(*fuseops.OpenDirOp) error
|
||||
|
@ -148,6 +149,9 @@ func (s *fileSystemServer) handleOp(op fuseops.Op) {
|
|||
case *fuseops.CreateSymlinkOp:
|
||||
err = s.fs.CreateSymlink(typed)
|
||||
|
||||
case *fuseops.RenameOp:
|
||||
err = s.fs.Rename(typed)
|
||||
|
||||
case *fuseops.RmDirOp:
|
||||
err = s.fs.RmDir(typed)
|
||||
|
||||
|
|
|
@ -70,6 +70,12 @@ func (fs *NotImplementedFileSystem) CreateSymlink(
|
|||
return
|
||||
}
|
||||
|
||||
func (fs *NotImplementedFileSystem) Rename(
|
||||
op *fuseops.RenameOp) (err error) {
|
||||
err = fuse.ENOSYS
|
||||
return
|
||||
}
|
||||
|
||||
func (fs *NotImplementedFileSystem) RmDir(
|
||||
op *fuseops.RmDirOp) (err error) {
|
||||
err = fuse.ENOSYS
|
||||
|
|
|
@ -202,10 +202,14 @@ func (in *inode) Len() (n int) {
|
|||
// Find an entry for the given child name and return its inode ID.
|
||||
//
|
||||
// REQUIRES: in.isDir()
|
||||
func (in *inode) LookUpChild(name string) (id fuseops.InodeID, ok bool) {
|
||||
func (in *inode) LookUpChild(name string) (
|
||||
id fuseops.InodeID,
|
||||
typ fuseutil.DirentType,
|
||||
ok bool) {
|
||||
index, ok := in.findChild(name)
|
||||
if ok {
|
||||
id = in.entries[index].Inode
|
||||
typ = in.entries[index].Type
|
||||
}
|
||||
|
||||
return
|
||||
|
@ -274,7 +278,7 @@ func (in *inode) RemoveChild(name string) {
|
|||
// Serve a ReadDir request.
|
||||
//
|
||||
// REQUIRES: in.isDir()
|
||||
func (in *inode) ReadDir(offset int, size int) (data []byte, err error) {
|
||||
func (in *inode) ReadDir(offset int, size int) (data []byte) {
|
||||
if !in.isDir() {
|
||||
panic("ReadDir called on non-directory.")
|
||||
}
|
||||
|
|
|
@ -192,7 +192,7 @@ func (fs *memFS) LookUpInode(
|
|||
inode := fs.getInodeOrDie(op.Parent)
|
||||
|
||||
// Does the directory have an entry with the given name?
|
||||
childID, ok := inode.LookUpChild(op.Name)
|
||||
childID, _, ok := inode.LookUpChild(op.Name)
|
||||
if !ok {
|
||||
err = fuse.ENOENT
|
||||
return
|
||||
|
@ -262,7 +262,7 @@ func (fs *memFS) MkDir(
|
|||
|
||||
// Ensure that the name doesn't already exist, so we don't wind up with a
|
||||
// duplicate.
|
||||
_, exists := parent.LookUpChild(op.Name)
|
||||
_, _, exists := parent.LookUpChild(op.Name)
|
||||
if exists {
|
||||
err = fuse.EEXIST
|
||||
return
|
||||
|
@ -305,7 +305,7 @@ func (fs *memFS) CreateFile(
|
|||
|
||||
// Ensure that the name doesn't already exist, so we don't wind up with a
|
||||
// duplicate.
|
||||
_, exists := parent.LookUpChild(op.Name)
|
||||
_, _, exists := parent.LookUpChild(op.Name)
|
||||
if exists {
|
||||
err = fuse.EEXIST
|
||||
return
|
||||
|
@ -355,7 +355,7 @@ func (fs *memFS) CreateSymlink(
|
|||
|
||||
// Ensure that the name doesn't already exist, so we don't wind up with a
|
||||
// duplicate.
|
||||
_, exists := parent.LookUpChild(op.Name)
|
||||
_, _, exists := parent.LookUpChild(op.Name)
|
||||
if exists {
|
||||
err = fuse.EEXIST
|
||||
return
|
||||
|
@ -396,6 +396,46 @@ func (fs *memFS) CreateSymlink(
|
|||
return
|
||||
}
|
||||
|
||||
func (fs *memFS) Rename(
|
||||
op *fuseops.RenameOp) (err error) {
|
||||
fs.mu.Lock()
|
||||
defer fs.mu.Unlock()
|
||||
|
||||
// Ask the old parent for the child's inode ID and type.
|
||||
oldParent := fs.getInodeOrDie(op.OldParent)
|
||||
childID, childType, ok := oldParent.LookUpChild(op.OldName)
|
||||
|
||||
if !ok {
|
||||
err = fuse.ENOENT
|
||||
return
|
||||
}
|
||||
|
||||
// If the new name exists already in the new parent, make sure it's not a
|
||||
// non-empty directory, then delete it.
|
||||
newParent := fs.getInodeOrDie(op.NewParent)
|
||||
existingID, _, ok := newParent.LookUpChild(op.NewName)
|
||||
if ok {
|
||||
existing := fs.getInodeOrDie(existingID)
|
||||
if existing.isDir() && len(existing.ReadDir(0, 1024)) > 0 {
|
||||
err = fuse.ENOTEMPTY
|
||||
return
|
||||
}
|
||||
|
||||
newParent.RemoveChild(op.NewName)
|
||||
}
|
||||
|
||||
// Link the new name.
|
||||
newParent.AddChild(
|
||||
childID,
|
||||
op.NewName,
|
||||
childType)
|
||||
|
||||
// Finally, remove the old name from the old parent.
|
||||
oldParent.RemoveChild(op.OldName)
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
func (fs *memFS) RmDir(
|
||||
op *fuseops.RmDirOp) (err error) {
|
||||
fs.mu.Lock()
|
||||
|
@ -405,7 +445,7 @@ func (fs *memFS) RmDir(
|
|||
parent := fs.getInodeOrDie(op.Parent)
|
||||
|
||||
// Find the child within the parent.
|
||||
childID, ok := parent.LookUpChild(op.Name)
|
||||
childID, _, ok := parent.LookUpChild(op.Name)
|
||||
if !ok {
|
||||
err = fuse.ENOENT
|
||||
return
|
||||
|
@ -438,7 +478,7 @@ func (fs *memFS) Unlink(
|
|||
parent := fs.getInodeOrDie(op.Parent)
|
||||
|
||||
// Find the child within the parent.
|
||||
childID, ok := parent.LookUpChild(op.Name)
|
||||
childID, _, ok := parent.LookUpChild(op.Name)
|
||||
if !ok {
|
||||
err = fuse.ENOENT
|
||||
return
|
||||
|
@ -482,11 +522,7 @@ func (fs *memFS) ReadDir(
|
|||
inode := fs.getInodeOrDie(op.Inode)
|
||||
|
||||
// Serve the request.
|
||||
op.Data, err = inode.ReadDir(int(op.Offset), op.Size)
|
||||
if err != nil {
|
||||
err = fmt.Errorf("inode.ReadDir: %v", err)
|
||||
return
|
||||
}
|
||||
op.Data = inode.ReadDir(int(op.Offset), op.Size)
|
||||
|
||||
return
|
||||
}
|
||||
|
|
|
@ -1271,3 +1271,380 @@ func (t *MemFSTest) MkdirInParallel() {
|
|||
func (t *MemFSTest) SymlinkInParallel() {
|
||||
fusetesting.RunSymlinkInParallelTest(t.Ctx, t.Dir)
|
||||
}
|
||||
|
||||
func (t *MemFSTest) RenameWithinDir_File() {
|
||||
var err error
|
||||
|
||||
// Create a parent directory.
|
||||
parentPath := path.Join(t.Dir, "parent")
|
||||
|
||||
err = os.Mkdir(parentPath, 0700)
|
||||
AssertEq(nil, err)
|
||||
|
||||
// And a file within it.
|
||||
oldPath := path.Join(parentPath, "foo")
|
||||
|
||||
err = ioutil.WriteFile(oldPath, []byte("taco"), 0400)
|
||||
AssertEq(nil, err)
|
||||
|
||||
// Rename it.
|
||||
newPath := path.Join(parentPath, "bar")
|
||||
|
||||
err = os.Rename(oldPath, newPath)
|
||||
AssertEq(nil, err)
|
||||
|
||||
// The old name shouldn't work.
|
||||
_, err = os.Stat(oldPath)
|
||||
ExpectTrue(os.IsNotExist(err), "err: %v", err)
|
||||
|
||||
_, err = ioutil.ReadFile(oldPath)
|
||||
ExpectTrue(os.IsNotExist(err), "err: %v", err)
|
||||
|
||||
// The new name should.
|
||||
fi, err := os.Stat(newPath)
|
||||
AssertEq(nil, err)
|
||||
ExpectEq(len("taco"), fi.Size())
|
||||
ExpectEq(os.FileMode(0400), fi.Mode())
|
||||
|
||||
contents, err := ioutil.ReadFile(newPath)
|
||||
AssertEq(nil, err)
|
||||
ExpectEq("taco", string(contents))
|
||||
|
||||
// There should only be the new entry in the directory.
|
||||
entries, err := fusetesting.ReadDirPicky(parentPath)
|
||||
AssertEq(nil, err)
|
||||
AssertEq(1, len(entries))
|
||||
fi = entries[0]
|
||||
|
||||
ExpectEq(path.Base(newPath), fi.Name())
|
||||
ExpectEq(os.FileMode(0400), fi.Mode())
|
||||
}
|
||||
|
||||
func (t *MemFSTest) RenameWithinDir_Directory() {
|
||||
var err error
|
||||
|
||||
// Create a parent directory.
|
||||
parentPath := path.Join(t.Dir, "parent")
|
||||
|
||||
err = os.Mkdir(parentPath, 0700)
|
||||
AssertEq(nil, err)
|
||||
|
||||
// And a non-empty directory within it.
|
||||
oldPath := path.Join(parentPath, "foo")
|
||||
|
||||
err = os.MkdirAll(path.Join(oldPath, "child"), 0700)
|
||||
AssertEq(nil, err)
|
||||
|
||||
// Rename it.
|
||||
newPath := path.Join(parentPath, "bar")
|
||||
|
||||
err = os.Rename(oldPath, newPath)
|
||||
AssertEq(nil, err)
|
||||
|
||||
// The old name shouldn't work.
|
||||
_, err = os.Stat(oldPath)
|
||||
ExpectTrue(os.IsNotExist(err), "err: %v", err)
|
||||
|
||||
// The new name should.
|
||||
fi, err := os.Stat(newPath)
|
||||
AssertEq(nil, err)
|
||||
ExpectEq(os.FileMode(0700)|os.ModeDir, fi.Mode())
|
||||
|
||||
// There should only be the new entry in the parent.
|
||||
entries, err := fusetesting.ReadDirPicky(parentPath)
|
||||
AssertEq(nil, err)
|
||||
AssertEq(1, len(entries))
|
||||
fi = entries[0]
|
||||
|
||||
ExpectEq(path.Base(newPath), fi.Name())
|
||||
ExpectEq(os.FileMode(0700)|os.ModeDir, fi.Mode())
|
||||
|
||||
// And the child should still be present.
|
||||
entries, err = fusetesting.ReadDirPicky(newPath)
|
||||
AssertEq(nil, err)
|
||||
AssertEq(1, len(entries))
|
||||
fi = entries[0]
|
||||
|
||||
ExpectEq("child", fi.Name())
|
||||
ExpectEq(os.FileMode(0700)|os.ModeDir, fi.Mode())
|
||||
}
|
||||
|
||||
func (t *MemFSTest) RenameWithinDir_SameName() {
|
||||
var err error
|
||||
|
||||
// Create a parent directory.
|
||||
parentPath := path.Join(t.Dir, "parent")
|
||||
|
||||
err = os.Mkdir(parentPath, 0700)
|
||||
AssertEq(nil, err)
|
||||
|
||||
// And a file within it.
|
||||
filePath := path.Join(parentPath, "foo")
|
||||
|
||||
err = ioutil.WriteFile(filePath, []byte("taco"), 0400)
|
||||
AssertEq(nil, err)
|
||||
|
||||
// Attempt to rename it.
|
||||
err = os.Rename(filePath, filePath)
|
||||
AssertEq(nil, err)
|
||||
|
||||
// The file should still exist.
|
||||
contents, err := ioutil.ReadFile(filePath)
|
||||
AssertEq(nil, err)
|
||||
ExpectEq("taco", string(contents))
|
||||
|
||||
// There should only be the one entry in the directory.
|
||||
entries, err := fusetesting.ReadDirPicky(parentPath)
|
||||
AssertEq(nil, err)
|
||||
AssertEq(1, len(entries))
|
||||
fi := entries[0]
|
||||
|
||||
ExpectEq(path.Base(filePath), fi.Name())
|
||||
ExpectEq(os.FileMode(0400), fi.Mode())
|
||||
}
|
||||
|
||||
func (t *MemFSTest) RenameAcrossDirs_File() {
|
||||
var err error
|
||||
|
||||
// Create two parent directories.
|
||||
oldParentPath := path.Join(t.Dir, "old")
|
||||
newParentPath := path.Join(t.Dir, "new")
|
||||
|
||||
err = os.Mkdir(oldParentPath, 0700)
|
||||
AssertEq(nil, err)
|
||||
|
||||
err = os.Mkdir(newParentPath, 0700)
|
||||
AssertEq(nil, err)
|
||||
|
||||
// And a file within the first.
|
||||
oldPath := path.Join(oldParentPath, "foo")
|
||||
|
||||
err = ioutil.WriteFile(oldPath, []byte("taco"), 0400)
|
||||
AssertEq(nil, err)
|
||||
|
||||
// Rename it.
|
||||
newPath := path.Join(newParentPath, "bar")
|
||||
|
||||
err = os.Rename(oldPath, newPath)
|
||||
AssertEq(nil, err)
|
||||
|
||||
// The old name shouldn't work.
|
||||
_, err = os.Stat(oldPath)
|
||||
ExpectTrue(os.IsNotExist(err), "err: %v", err)
|
||||
|
||||
_, err = ioutil.ReadFile(oldPath)
|
||||
ExpectTrue(os.IsNotExist(err), "err: %v", err)
|
||||
|
||||
// The new name should.
|
||||
fi, err := os.Stat(newPath)
|
||||
AssertEq(nil, err)
|
||||
ExpectEq(len("taco"), fi.Size())
|
||||
ExpectEq(os.FileMode(0400), fi.Mode())
|
||||
|
||||
contents, err := ioutil.ReadFile(newPath)
|
||||
AssertEq(nil, err)
|
||||
ExpectEq("taco", string(contents))
|
||||
|
||||
// Check the old parent.
|
||||
entries, err := fusetesting.ReadDirPicky(oldParentPath)
|
||||
AssertEq(nil, err)
|
||||
AssertEq(0, len(entries))
|
||||
|
||||
// And the new one.
|
||||
entries, err = fusetesting.ReadDirPicky(newParentPath)
|
||||
AssertEq(nil, err)
|
||||
AssertEq(1, len(entries))
|
||||
fi = entries[0]
|
||||
|
||||
ExpectEq(path.Base(newPath), fi.Name())
|
||||
ExpectEq(os.FileMode(0400), fi.Mode())
|
||||
}
|
||||
|
||||
func (t *MemFSTest) RenameAcrossDirs_Directory() {
|
||||
var err error
|
||||
|
||||
// Create two parent directories.
|
||||
oldParentPath := path.Join(t.Dir, "old")
|
||||
newParentPath := path.Join(t.Dir, "new")
|
||||
|
||||
err = os.Mkdir(oldParentPath, 0700)
|
||||
AssertEq(nil, err)
|
||||
|
||||
err = os.Mkdir(newParentPath, 0700)
|
||||
AssertEq(nil, err)
|
||||
|
||||
// And a non-empty directory within the first.
|
||||
oldPath := path.Join(oldParentPath, "foo")
|
||||
|
||||
err = os.MkdirAll(path.Join(oldPath, "child"), 0700)
|
||||
AssertEq(nil, err)
|
||||
|
||||
// Rename it.
|
||||
newPath := path.Join(newParentPath, "bar")
|
||||
|
||||
err = os.Rename(oldPath, newPath)
|
||||
AssertEq(nil, err)
|
||||
|
||||
// The old name shouldn't work.
|
||||
_, err = os.Stat(oldPath)
|
||||
ExpectTrue(os.IsNotExist(err), "err: %v", err)
|
||||
|
||||
// The new name should.
|
||||
fi, err := os.Stat(newPath)
|
||||
AssertEq(nil, err)
|
||||
ExpectEq(os.FileMode(0700)|os.ModeDir, fi.Mode())
|
||||
|
||||
// And the child should still be present.
|
||||
entries, err := fusetesting.ReadDirPicky(newPath)
|
||||
AssertEq(nil, err)
|
||||
AssertEq(1, len(entries))
|
||||
fi = entries[0]
|
||||
|
||||
ExpectEq("child", fi.Name())
|
||||
ExpectEq(os.FileMode(0700)|os.ModeDir, fi.Mode())
|
||||
|
||||
// Check the old parent.
|
||||
entries, err = fusetesting.ReadDirPicky(oldParentPath)
|
||||
AssertEq(nil, err)
|
||||
AssertEq(0, len(entries))
|
||||
|
||||
// And the new one.
|
||||
entries, err = fusetesting.ReadDirPicky(newParentPath)
|
||||
AssertEq(nil, err)
|
||||
AssertEq(1, len(entries))
|
||||
fi = entries[0]
|
||||
|
||||
ExpectEq(path.Base(newPath), fi.Name())
|
||||
ExpectEq(os.FileMode(0700)|os.ModeDir, fi.Mode())
|
||||
}
|
||||
|
||||
func (t *MemFSTest) RenameOutOfFileSystem() {
|
||||
var err error
|
||||
|
||||
// Create a file.
|
||||
oldPath := path.Join(t.Dir, "foo")
|
||||
|
||||
err = ioutil.WriteFile(oldPath, []byte("taco"), 0400)
|
||||
AssertEq(nil, err)
|
||||
|
||||
// Attempt to move it out of the file system.
|
||||
tempDir, err := ioutil.TempDir("", "memfs_test")
|
||||
AssertEq(nil, err)
|
||||
defer os.RemoveAll(tempDir)
|
||||
|
||||
err = os.Rename(oldPath, path.Join(tempDir, "bar"))
|
||||
ExpectThat(err, Error(HasSubstr("cross-device")))
|
||||
}
|
||||
|
||||
func (t *MemFSTest) RenameIntoFileSystem() {
|
||||
var err error
|
||||
|
||||
// Create a file outside of our file system.
|
||||
f, err := ioutil.TempFile("", "memfs_test")
|
||||
AssertEq(nil, err)
|
||||
defer f.Close()
|
||||
|
||||
oldPath := f.Name()
|
||||
defer os.Remove(oldPath)
|
||||
|
||||
// Attempt to move it into the file system.
|
||||
err = os.Rename(oldPath, path.Join(t.Dir, "bar"))
|
||||
ExpectThat(err, Error(HasSubstr("cross-device")))
|
||||
}
|
||||
|
||||
func (t *MemFSTest) RenameOverExistingFile() {
|
||||
var err error
|
||||
|
||||
// Create two files.
|
||||
oldPath := path.Join(t.Dir, "foo")
|
||||
err = ioutil.WriteFile(oldPath, []byte("taco"), 0400)
|
||||
AssertEq(nil, err)
|
||||
|
||||
newPath := path.Join(t.Dir, "bar")
|
||||
err = ioutil.WriteFile(newPath, []byte("burrito"), 0600)
|
||||
AssertEq(nil, err)
|
||||
|
||||
// Rename one over the other.
|
||||
err = os.Rename(oldPath, newPath)
|
||||
AssertEq(nil, err)
|
||||
|
||||
// Check the file contents.
|
||||
contents, err := ioutil.ReadFile(newPath)
|
||||
AssertEq(nil, err)
|
||||
ExpectEq("taco", string(contents))
|
||||
|
||||
// And the parent listing.
|
||||
entries, err := fusetesting.ReadDirPicky(t.Dir)
|
||||
AssertEq(nil, err)
|
||||
AssertEq(1, len(entries))
|
||||
fi := entries[0]
|
||||
|
||||
ExpectEq(path.Base(newPath), fi.Name())
|
||||
ExpectEq(os.FileMode(0400), fi.Mode())
|
||||
ExpectEq(len("taco"), fi.Size())
|
||||
}
|
||||
|
||||
func (t *MemFSTest) RenameOverExistingDirectory() {
|
||||
var err error
|
||||
|
||||
// Create two directories, the first non-empty.
|
||||
oldPath := path.Join(t.Dir, "foo")
|
||||
err = os.MkdirAll(path.Join(oldPath, "child"), 0700)
|
||||
AssertEq(nil, err)
|
||||
|
||||
newPath := path.Join(t.Dir, "bar")
|
||||
err = os.Mkdir(newPath, 0600)
|
||||
AssertEq(nil, err)
|
||||
|
||||
// Renaming over the non-empty one shouldn't work.
|
||||
err = os.Rename(newPath, oldPath)
|
||||
ExpectThat(err, Error(HasSubstr("not empty")))
|
||||
|
||||
// But the other way around should.
|
||||
err = os.Rename(oldPath, newPath)
|
||||
AssertEq(nil, err)
|
||||
|
||||
// Check the parent listing.
|
||||
entries, err := fusetesting.ReadDirPicky(t.Dir)
|
||||
AssertEq(nil, err)
|
||||
AssertEq(1, len(entries))
|
||||
fi := entries[0]
|
||||
|
||||
ExpectEq(path.Base(newPath), fi.Name())
|
||||
ExpectEq(os.FileMode(0700)|os.ModeDir, fi.Mode())
|
||||
|
||||
// And the directory itself.
|
||||
entries, err = fusetesting.ReadDirPicky(newPath)
|
||||
AssertEq(nil, err)
|
||||
AssertEq(1, len(entries))
|
||||
fi = entries[0]
|
||||
|
||||
ExpectEq("child", fi.Name())
|
||||
}
|
||||
|
||||
func (t *MemFSTest) RenameOverExisting_WrongType() {
|
||||
var err error
|
||||
|
||||
// Create a file and a directory.
|
||||
filePath := path.Join(t.Dir, "foo")
|
||||
err = ioutil.WriteFile(filePath, []byte("taco"), 0400)
|
||||
AssertEq(nil, err)
|
||||
|
||||
dirPath := path.Join(t.Dir, "bar")
|
||||
err = os.Mkdir(dirPath, 0700)
|
||||
AssertEq(nil, err)
|
||||
|
||||
// Renaming one over the other shouldn't work.
|
||||
err = os.Rename(filePath, dirPath)
|
||||
ExpectThat(err, Error(HasSubstr("is a directory")))
|
||||
|
||||
err = os.Rename(dirPath, filePath)
|
||||
ExpectThat(err, Error(HasSubstr("not a directory")))
|
||||
}
|
||||
|
||||
func (t *MemFSTest) RenameNonExistentFile() {
|
||||
var err error
|
||||
|
||||
err = os.Rename(path.Join(t.Dir, "foo"), path.Join(t.Dir, "bar"))
|
||||
ExpectThat(err, Error(HasSubstr("no such file")))
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue