mirror of
https://github.com/netbirdio/gvisor.git
synced 2026-05-22 17:12:49 -07:00
Remove references to replaced child in Rename in ramfs/agentfs
In the case of a rename replacing an existing destination inode, ramfs Rename failed to first remove the replaced inode. This caused: 1. A leak of a reference to the inode (making it live indefinitely). 2. For directories, a leak of the replaced directory's .. link to the parent. This would cause the parent's link count to incorrectly increase. (2) is much simpler to test than (1), so that's what I've done. agentfs has a similar bug with link count only, so the Dirent layer informs the Inode if this is a replacing rename. Fixes #133 PiperOrigin-RevId: 239105698 Change-Id: I4450af2462d8ae3339def812287213d2cbeebde0
This commit is contained in:
@@ -1563,6 +1563,7 @@ func Rename(ctx context.Context, root *Dirent, oldParent *Dirent, oldName string
|
||||
}
|
||||
|
||||
// newName doesn't exist; simply create it below.
|
||||
replaced = nil
|
||||
} else {
|
||||
// Check constraints on the dirent being replaced.
|
||||
|
||||
@@ -1620,7 +1621,7 @@ func Rename(ctx context.Context, root *Dirent, oldParent *Dirent, oldName string
|
||||
replaced.DecRef()
|
||||
}
|
||||
|
||||
if err := renamed.Inode.Rename(ctx, oldParent, renamed, newParent, newName); err != nil {
|
||||
if err := renamed.Inode.Rename(ctx, oldParent, renamed, newParent, newName, replaced != nil); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
|
||||
@@ -338,7 +338,7 @@ func (InodeNotDirectory) RemoveDirectory(context.Context, *fs.Inode, string) err
|
||||
}
|
||||
|
||||
// Rename implements fs.FileOperations.Rename.
|
||||
func (InodeNotDirectory) Rename(context.Context, *fs.Inode, string, *fs.Inode, string) error {
|
||||
func (InodeNotDirectory) Rename(context.Context, *fs.Inode, string, *fs.Inode, string, bool) error {
|
||||
return syserror.EINVAL
|
||||
}
|
||||
|
||||
@@ -378,7 +378,7 @@ func (InodeNoopTruncate) Truncate(context.Context, *fs.Inode, int64) error {
|
||||
type InodeNotRenameable struct{}
|
||||
|
||||
// Rename implements fs.InodeOperations.Rename.
|
||||
func (InodeNotRenameable) Rename(context.Context, *fs.Inode, string, *fs.Inode, string) error {
|
||||
func (InodeNotRenameable) Rename(context.Context, *fs.Inode, string, *fs.Inode, string, bool) error {
|
||||
return syserror.EINVAL
|
||||
}
|
||||
|
||||
|
||||
@@ -298,7 +298,7 @@ func (i *inodeOperations) RemoveDirectory(ctx context.Context, dir *fs.Inode, na
|
||||
}
|
||||
|
||||
// Rename renames this node.
|
||||
func (i *inodeOperations) Rename(ctx context.Context, oldParent *fs.Inode, oldName string, newParent *fs.Inode, newName string) error {
|
||||
func (i *inodeOperations) Rename(ctx context.Context, oldParent *fs.Inode, oldName string, newParent *fs.Inode, newName string, replacement bool) error {
|
||||
// Unwrap the new parent to a *inodeOperations.
|
||||
newParentInodeOperations, ok := newParent.InodeOperations.(*inodeOperations)
|
||||
if !ok {
|
||||
@@ -323,7 +323,12 @@ func (i *inodeOperations) Rename(ctx context.Context, oldParent *fs.Inode, oldNa
|
||||
oldParentInodeOperations.cachingInodeOps.DecLinks(ctx)
|
||||
}
|
||||
if i.session().cachePolicy.cacheUAttrs(newParent) {
|
||||
newParentInodeOperations.cachingInodeOps.IncLinks(ctx)
|
||||
// Only IncLinks if there is a new addition to
|
||||
// newParent. If this is replacement, then the total
|
||||
// count remains the same.
|
||||
if !replacement {
|
||||
newParentInodeOperations.cachingInodeOps.IncLinks(ctx)
|
||||
}
|
||||
}
|
||||
}
|
||||
if i.session().cachePolicy.cacheReaddir() {
|
||||
|
||||
@@ -296,7 +296,7 @@ func (i *inodeOperations) RemoveDirectory(ctx context.Context, dir *fs.Inode, na
|
||||
}
|
||||
|
||||
// Rename implements fs.InodeOperations.Rename.
|
||||
func (i *inodeOperations) Rename(ctx context.Context, oldParent *fs.Inode, oldName string, newParent *fs.Inode, newName string) error {
|
||||
func (i *inodeOperations) Rename(ctx context.Context, oldParent *fs.Inode, oldName string, newParent *fs.Inode, newName string, replacement bool) error {
|
||||
op, ok := oldParent.InodeOperations.(*inodeOperations)
|
||||
if !ok {
|
||||
return syscall.EXDEV
|
||||
|
||||
@@ -152,7 +152,8 @@ func (i *Inode) WriteOut(ctx context.Context) error {
|
||||
// Lookup calls i.InodeOperations.Lookup with i as the directory.
|
||||
func (i *Inode) Lookup(ctx context.Context, name string) (*Dirent, error) {
|
||||
if i.overlay != nil {
|
||||
return overlayLookup(ctx, i.overlay, i, name)
|
||||
d, _, err := overlayLookup(ctx, i.overlay, i, name)
|
||||
return d, err
|
||||
}
|
||||
return i.InodeOperations.Lookup(ctx, i, name)
|
||||
}
|
||||
@@ -211,11 +212,11 @@ func (i *Inode) Remove(ctx context.Context, d *Dirent, remove *Dirent) error {
|
||||
}
|
||||
|
||||
// Rename calls i.InodeOperations.Rename with the given arguments.
|
||||
func (i *Inode) Rename(ctx context.Context, oldParent *Dirent, renamed *Dirent, newParent *Dirent, newName string) error {
|
||||
func (i *Inode) Rename(ctx context.Context, oldParent *Dirent, renamed *Dirent, newParent *Dirent, newName string, replacement bool) error {
|
||||
if i.overlay != nil {
|
||||
return overlayRename(ctx, i.overlay, oldParent, renamed, newParent, newName)
|
||||
return overlayRename(ctx, i.overlay, oldParent, renamed, newParent, newName, replacement)
|
||||
}
|
||||
return i.InodeOperations.Rename(ctx, oldParent.Inode, renamed.name, newParent.Inode, newName)
|
||||
return i.InodeOperations.Rename(ctx, oldParent.Inode, renamed.name, newParent.Inode, newName, replacement)
|
||||
}
|
||||
|
||||
// Bind calls i.InodeOperations.Bind with i as the directory.
|
||||
|
||||
@@ -133,12 +133,15 @@ type InodeOperations interface {
|
||||
// removed is empty.
|
||||
RemoveDirectory(ctx context.Context, dir *Inode, name string) error
|
||||
|
||||
// Rename atomically renames oldName under oldParent to newName
|
||||
// under newParent where oldParent and newParent are directories.
|
||||
// Rename atomically renames oldName under oldParent to newName under
|
||||
// newParent where oldParent and newParent are directories.
|
||||
//
|
||||
// Implementations are responsible for rejecting renames that
|
||||
// replace non-empty directories.
|
||||
Rename(ctx context.Context, oldParent *Inode, oldName string, newParent *Inode, newName string) error
|
||||
// If replacement is true, then newName already exists and this call
|
||||
// will replace it with oldName.
|
||||
//
|
||||
// Implementations are responsible for rejecting renames that replace
|
||||
// non-empty directories.
|
||||
Rename(ctx context.Context, oldParent *Inode, oldName string, newParent *Inode, newName string, replacement bool) error
|
||||
|
||||
// Bind binds a new socket under dir at the given name.
|
||||
// Implementations must ensure that name does not already exist.
|
||||
|
||||
@@ -44,7 +44,11 @@ func overlayWriteOut(ctx context.Context, o *overlayEntry) error {
|
||||
return err
|
||||
}
|
||||
|
||||
func overlayLookup(ctx context.Context, parent *overlayEntry, inode *Inode, name string) (*Dirent, error) {
|
||||
// overlayLookup performs a lookup in parent.
|
||||
//
|
||||
// If name exists, it returns true if the Dirent is in the upper, false if the
|
||||
// Dirent is in the lower.
|
||||
func overlayLookup(ctx context.Context, parent *overlayEntry, inode *Inode, name string) (*Dirent, bool, error) {
|
||||
// Hot path. Avoid defers.
|
||||
parent.copyMu.RLock()
|
||||
|
||||
@@ -71,7 +75,7 @@ func overlayLookup(ctx context.Context, parent *overlayEntry, inode *Inode, name
|
||||
// We encountered an error that an overlay cannot handle,
|
||||
// we must propagate it to the caller.
|
||||
parent.copyMu.RUnlock()
|
||||
return nil, err
|
||||
return nil, false, err
|
||||
}
|
||||
if child != nil {
|
||||
if child.IsNegative() {
|
||||
@@ -93,23 +97,23 @@ func overlayLookup(ctx context.Context, parent *overlayEntry, inode *Inode, name
|
||||
// that negative Dirent being cached in
|
||||
// the Dirent tree, so we can return
|
||||
// one from the overlay.
|
||||
return NewNegativeDirent(name), nil
|
||||
return NewNegativeDirent(name), false, nil
|
||||
}
|
||||
// Upper fs is not OK with a negative Dirent
|
||||
// being cached in the Dirent tree, so don't
|
||||
// return one.
|
||||
return nil, syserror.ENOENT
|
||||
return nil, false, syserror.ENOENT
|
||||
}
|
||||
entry, err := newOverlayEntry(ctx, upperInode, nil, false)
|
||||
if err != nil {
|
||||
// Don't leak resources.
|
||||
upperInode.DecRef()
|
||||
parent.copyMu.RUnlock()
|
||||
return nil, err
|
||||
return nil, false, err
|
||||
}
|
||||
d, err := NewDirent(newOverlayInode(ctx, entry, inode.MountSource), name), nil
|
||||
parent.copyMu.RUnlock()
|
||||
return d, err
|
||||
return d, true, err
|
||||
}
|
||||
}
|
||||
|
||||
@@ -127,7 +131,7 @@ func overlayLookup(ctx context.Context, parent *overlayEntry, inode *Inode, name
|
||||
upperInode.DecRef()
|
||||
}
|
||||
parent.copyMu.RUnlock()
|
||||
return nil, err
|
||||
return nil, false, err
|
||||
}
|
||||
if child != nil {
|
||||
if !child.IsNegative() {
|
||||
@@ -158,9 +162,9 @@ func overlayLookup(ctx context.Context, parent *overlayEntry, inode *Inode, name
|
||||
// one as well. See comments above regarding negativeUpperChild
|
||||
// for more info.
|
||||
if negativeUpperChild {
|
||||
return NewNegativeDirent(name), nil
|
||||
return NewNegativeDirent(name), false, nil
|
||||
}
|
||||
return nil, syserror.ENOENT
|
||||
return nil, false, syserror.ENOENT
|
||||
}
|
||||
|
||||
// Did we find a lower Inode? Remember this because we may decide we don't
|
||||
@@ -195,11 +199,11 @@ func overlayLookup(ctx context.Context, parent *overlayEntry, inode *Inode, name
|
||||
lowerInode.DecRef()
|
||||
}
|
||||
parent.copyMu.RUnlock()
|
||||
return nil, err
|
||||
return nil, false, err
|
||||
}
|
||||
d, err := NewDirent(newOverlayInode(ctx, entry, inode.MountSource), name), nil
|
||||
parent.copyMu.RUnlock()
|
||||
return d, err
|
||||
return d, upperInode != nil, err
|
||||
}
|
||||
|
||||
func overlayCreate(ctx context.Context, o *overlayEntry, parent *Dirent, name string, flags FileFlags, perm FilePermissions) (*File, error) {
|
||||
@@ -317,7 +321,7 @@ func overlayRemove(ctx context.Context, o *overlayEntry, parent *Dirent, child *
|
||||
return nil
|
||||
}
|
||||
|
||||
func overlayRename(ctx context.Context, o *overlayEntry, oldParent *Dirent, renamed *Dirent, newParent *Dirent, newName string) error {
|
||||
func overlayRename(ctx context.Context, o *overlayEntry, oldParent *Dirent, renamed *Dirent, newParent *Dirent, newName string, replacement bool) error {
|
||||
// To be able to copy these up below, they have to be part of an
|
||||
// overlay file system.
|
||||
//
|
||||
@@ -327,36 +331,57 @@ func overlayRename(ctx context.Context, o *overlayEntry, oldParent *Dirent, rena
|
||||
return syserror.EXDEV
|
||||
}
|
||||
|
||||
// Check here if the file to be replaced exists and is a non-empty
|
||||
// directory. If we copy up first, we may end up copying the directory
|
||||
// but none of its children, so the directory will appear empty in the
|
||||
// upper fs, which will then allow the rename to proceed when it should
|
||||
// return ENOTEMPTY.
|
||||
replaced, err := newParent.Inode.Lookup(ctx, newName)
|
||||
if err != nil && err != syserror.ENOENT {
|
||||
return err
|
||||
}
|
||||
if err == nil {
|
||||
// NOTE: We must drop the reference on replaced before we make
|
||||
// the rename call. For that reason we can't use defer.
|
||||
if !replaced.IsNegative() && IsDir(replaced.Inode.StableAttr) {
|
||||
children, err := readdirOne(ctx, replaced)
|
||||
if err != nil {
|
||||
replaced.DecRef()
|
||||
return err
|
||||
}
|
||||
|
||||
// readdirOne ensures that "." and ".." are not
|
||||
// included among the returned children, so we don't
|
||||
// need to bother checking for them.
|
||||
if len(children) > 0 {
|
||||
replaced.DecRef()
|
||||
return syserror.ENOTEMPTY
|
||||
}
|
||||
if replacement {
|
||||
// Check here if the file to be replaced exists and is a
|
||||
// non-empty directory. If we copy up first, we may end up
|
||||
// copying the directory but none of its children, so the
|
||||
// directory will appear empty in the upper fs, which will then
|
||||
// allow the rename to proceed when it should return ENOTEMPTY.
|
||||
//
|
||||
// NOTE: Ideally, we'd just pass in the replaced
|
||||
// Dirent from Rename, but we must drop the reference on
|
||||
// replaced before we make the rename call, so Rename can't
|
||||
// pass the Dirent to the Inode without significantly
|
||||
// complicating the API. Thus we look it up again here.
|
||||
//
|
||||
// For the same reason we can't use defer here.
|
||||
replaced, inUpper, err := overlayLookup(ctx, newParent.Inode.overlay, newParent.Inode, newName)
|
||||
// If err == ENOENT or a negative Dirent is returned, then
|
||||
// newName has been removed out from under us. That's fine;
|
||||
// filesystems where that can happen must handle stale
|
||||
// 'replaced'.
|
||||
if err != nil && err != syserror.ENOENT {
|
||||
return err
|
||||
}
|
||||
if err == nil {
|
||||
if !inUpper {
|
||||
// newName doesn't exist in
|
||||
// newParent.Inode.overlay.upper, thus from
|
||||
// that Inode's perspective this won't be a
|
||||
// replacing rename.
|
||||
replacement = false
|
||||
}
|
||||
|
||||
replaced.DecRef()
|
||||
if !replaced.IsNegative() && IsDir(replaced.Inode.StableAttr) {
|
||||
children, err := readdirOne(ctx, replaced)
|
||||
if err != nil {
|
||||
replaced.DecRef()
|
||||
return err
|
||||
}
|
||||
|
||||
// readdirOne ensures that "." and ".." are not
|
||||
// included among the returned children, so we don't
|
||||
// need to bother checking for them.
|
||||
if len(children) > 0 {
|
||||
replaced.DecRef()
|
||||
return syserror.ENOTEMPTY
|
||||
}
|
||||
}
|
||||
|
||||
replaced.DecRef()
|
||||
}
|
||||
}
|
||||
|
||||
if err := copyUpLockedForRename(ctx, renamed); err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -364,7 +389,7 @@ func overlayRename(ctx context.Context, o *overlayEntry, oldParent *Dirent, rena
|
||||
return err
|
||||
}
|
||||
oldName := renamed.name
|
||||
if err := o.upper.InodeOperations.Rename(ctx, oldParent.Inode.overlay.upper, oldName, newParent.Inode.overlay.upper, newName); err != nil {
|
||||
if err := o.upper.InodeOperations.Rename(ctx, oldParent.Inode.overlay.upper, oldName, newParent.Inode.overlay.upper, newName, replacement); err != nil {
|
||||
return err
|
||||
}
|
||||
if renamed.Inode.overlay.lowerExists {
|
||||
|
||||
@@ -132,7 +132,7 @@ func (n *MockInodeOperations) CreateDirectory(context.Context, *Inode, string, F
|
||||
}
|
||||
|
||||
// Rename implements fs.InodeOperations.Rename.
|
||||
func (n *MockInodeOperations) Rename(ctx context.Context, oldParent *Inode, oldName string, newParent *Inode, newName string) error {
|
||||
func (n *MockInodeOperations) Rename(ctx context.Context, oldParent *Inode, oldName string, newParent *Inode, newName string, replacement bool) error {
|
||||
n.renameCalled = true
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -15,6 +15,7 @@
|
||||
package ramfs
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"sync"
|
||||
"syscall"
|
||||
|
||||
@@ -383,8 +384,8 @@ func (d *Dir) GetFile(ctx context.Context, dirent *fs.Dirent, flags fs.FileFlags
|
||||
}
|
||||
|
||||
// Rename implements fs.InodeOperations.Rename.
|
||||
func (*Dir) Rename(ctx context.Context, oldParent *fs.Inode, oldName string, newParent *fs.Inode, newName string) error {
|
||||
return Rename(ctx, oldParent.InodeOperations, oldName, newParent.InodeOperations, newName)
|
||||
func (*Dir) Rename(ctx context.Context, oldParent *fs.Inode, oldName string, newParent *fs.Inode, newName string, replacement bool) error {
|
||||
return Rename(ctx, oldParent.InodeOperations, oldName, newParent.InodeOperations, newName, replacement)
|
||||
}
|
||||
|
||||
// dirFileOperations implements fs.FileOperations for a ramfs directory.
|
||||
@@ -456,7 +457,7 @@ func hasChildren(ctx context.Context, inode *fs.Inode) (bool, error) {
|
||||
}
|
||||
|
||||
// Rename renames from a *ramfs.Dir to another *ramfs.Dir.
|
||||
func Rename(ctx context.Context, oldParent fs.InodeOperations, oldName string, newParent fs.InodeOperations, newName string) error {
|
||||
func Rename(ctx context.Context, oldParent fs.InodeOperations, oldName string, newParent fs.InodeOperations, newName string, replacement bool) error {
|
||||
op, ok := oldParent.(*Dir)
|
||||
if !ok {
|
||||
return syserror.EXDEV
|
||||
@@ -469,8 +470,14 @@ func Rename(ctx context.Context, oldParent fs.InodeOperations, oldName string, n
|
||||
np.mu.Lock()
|
||||
defer np.mu.Unlock()
|
||||
|
||||
// Check whether the ramfs entry to be replaced is a non-empty directory.
|
||||
if replaced, ok := np.children[newName]; ok {
|
||||
// Is this is an overwriting rename?
|
||||
if replacement {
|
||||
replaced, ok := np.children[newName]
|
||||
if !ok {
|
||||
panic(fmt.Sprintf("Dirent claims rename is replacement, but %q is missing from %+v", newName, np))
|
||||
}
|
||||
|
||||
// Non-empty directories cannot be replaced.
|
||||
if fs.IsDir(replaced.StableAttr) {
|
||||
if ok, err := hasChildren(ctx, replaced); err != nil {
|
||||
return err
|
||||
@@ -478,6 +485,13 @@ func Rename(ctx context.Context, oldParent fs.InodeOperations, oldName string, n
|
||||
return syserror.ENOTEMPTY
|
||||
}
|
||||
}
|
||||
|
||||
// Remove the replaced child and drop our reference on it.
|
||||
inode, err := np.removeChildLocked(ctx, newName)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
inode.DecRef()
|
||||
}
|
||||
|
||||
// Be careful, we may have already grabbed this mutex above.
|
||||
|
||||
@@ -107,8 +107,8 @@ func (f *fileInodeOperations) Mappable(*fs.Inode) memmap.Mappable {
|
||||
}
|
||||
|
||||
// Rename implements fs.InodeOperations.Rename.
|
||||
func (*fileInodeOperations) Rename(ctx context.Context, oldParent *fs.Inode, oldName string, newParent *fs.Inode, newName string) error {
|
||||
return rename(ctx, oldParent, oldName, newParent, newName)
|
||||
func (*fileInodeOperations) Rename(ctx context.Context, oldParent *fs.Inode, oldName string, newParent *fs.Inode, newName string, replacement bool) error {
|
||||
return rename(ctx, oldParent, oldName, newParent, newName, replacement)
|
||||
}
|
||||
|
||||
// GetFile implements fs.InodeOperations.GetFile.
|
||||
|
||||
@@ -38,7 +38,7 @@ var fsInfo = fs.Info{
|
||||
}
|
||||
|
||||
// rename implements fs.InodeOperations.Rename for tmpfs nodes.
|
||||
func rename(ctx context.Context, oldParent *fs.Inode, oldName string, newParent *fs.Inode, newName string) error {
|
||||
func rename(ctx context.Context, oldParent *fs.Inode, oldName string, newParent *fs.Inode, newName string, replacement bool) error {
|
||||
op, ok := oldParent.InodeOperations.(*Dir)
|
||||
if !ok {
|
||||
return syserror.EXDEV
|
||||
@@ -47,7 +47,7 @@ func rename(ctx context.Context, oldParent *fs.Inode, oldName string, newParent
|
||||
if !ok {
|
||||
return syserror.EXDEV
|
||||
}
|
||||
return ramfs.Rename(ctx, op.ramfsDir, oldName, np.ramfsDir, newName)
|
||||
return ramfs.Rename(ctx, op.ramfsDir, oldName, np.ramfsDir, newName, replacement)
|
||||
}
|
||||
|
||||
// Dir is a directory.
|
||||
@@ -238,8 +238,8 @@ func (d *Dir) newCreateOps() *ramfs.CreateOps {
|
||||
}
|
||||
|
||||
// Rename implements fs.InodeOperations.Rename.
|
||||
func (d *Dir) Rename(ctx context.Context, oldParent *fs.Inode, oldName string, newParent *fs.Inode, newName string) error {
|
||||
return rename(ctx, oldParent, oldName, newParent, newName)
|
||||
func (d *Dir) Rename(ctx context.Context, oldParent *fs.Inode, oldName string, newParent *fs.Inode, newName string, replacement bool) error {
|
||||
return rename(ctx, oldParent, oldName, newParent, newName, replacement)
|
||||
}
|
||||
|
||||
// StatFS implments fs.InodeOperations.StatFS.
|
||||
@@ -266,8 +266,8 @@ func NewSymlink(ctx context.Context, target string, owner fs.FileOwner, msrc *fs
|
||||
}
|
||||
|
||||
// Rename implements fs.InodeOperations.Rename.
|
||||
func (s *Symlink) Rename(ctx context.Context, oldParent *fs.Inode, oldName string, newParent *fs.Inode, newName string) error {
|
||||
return rename(ctx, oldParent, oldName, newParent, newName)
|
||||
func (s *Symlink) Rename(ctx context.Context, oldParent *fs.Inode, oldName string, newParent *fs.Inode, newName string, replacement bool) error {
|
||||
return rename(ctx, oldParent, oldName, newParent, newName, replacement)
|
||||
}
|
||||
|
||||
// StatFS returns the tmpfs info.
|
||||
@@ -295,8 +295,8 @@ func NewSocket(ctx context.Context, socket transport.BoundEndpoint, owner fs.Fil
|
||||
}
|
||||
|
||||
// Rename implements fs.InodeOperations.Rename.
|
||||
func (s *Socket) Rename(ctx context.Context, oldParent *fs.Inode, oldName string, newParent *fs.Inode, newName string) error {
|
||||
return rename(ctx, oldParent, oldName, newParent, newName)
|
||||
func (s *Socket) Rename(ctx context.Context, oldParent *fs.Inode, oldName string, newParent *fs.Inode, newName string, replacement bool) error {
|
||||
return rename(ctx, oldParent, oldName, newParent, newName, replacement)
|
||||
}
|
||||
|
||||
// StatFS returns the tmpfs info.
|
||||
@@ -332,8 +332,8 @@ func NewFifo(ctx context.Context, owner fs.FileOwner, perms fs.FilePermissions,
|
||||
}
|
||||
|
||||
// Rename implements fs.InodeOperations.Rename.
|
||||
func (f *Fifo) Rename(ctx context.Context, oldParent *fs.Inode, oldName string, newParent *fs.Inode, newName string) error {
|
||||
return rename(ctx, oldParent, oldName, newParent, newName)
|
||||
func (f *Fifo) Rename(ctx context.Context, oldParent *fs.Inode, oldName string, newParent *fs.Inode, newName string, replacement bool) error {
|
||||
return rename(ctx, oldParent, oldName, newParent, newName, replacement)
|
||||
}
|
||||
|
||||
// StatFS returns the tmpfs info.
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
# gVisor is a general-purpose sandbox.
|
||||
|
||||
licenses(["notice"])
|
||||
package(licenses = ["notice"])
|
||||
|
||||
exports_files(["LICENSE"])
|
||||
|
||||
@@ -155,10 +155,11 @@ TEST(RenameTest, DirectoryToOwnChildDirectory) {
|
||||
}
|
||||
|
||||
TEST(RenameTest, FileOverwritesFile) {
|
||||
auto dir = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateDir());
|
||||
auto f1 = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateFileWith(
|
||||
GetAbsoluteTestTmpdir(), "first", TempPath::kDefaultFileMode));
|
||||
dir.path(), "first", TempPath::kDefaultFileMode));
|
||||
auto f2 = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateFileWith(
|
||||
GetAbsoluteTestTmpdir(), "second", TempPath::kDefaultFileMode));
|
||||
dir.path(), "second", TempPath::kDefaultFileMode));
|
||||
ASSERT_THAT(rename(f1.path().c_str(), f2.path().c_str()), SyscallSucceeds());
|
||||
EXPECT_THAT(Exists(f1.path()), IsPosixErrorOkAndHolds(false));
|
||||
|
||||
@@ -168,6 +169,26 @@ TEST(RenameTest, FileOverwritesFile) {
|
||||
EXPECT_EQ("first", f2_contents);
|
||||
}
|
||||
|
||||
TEST(RenameTest, DirectoryOverwritesDirectoryLinkCount) {
|
||||
auto parent1 = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateDir());
|
||||
EXPECT_THAT(Links(parent1.path()), IsPosixErrorOkAndHolds(2));
|
||||
|
||||
auto parent2 = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateDir());
|
||||
EXPECT_THAT(Links(parent2.path()), IsPosixErrorOkAndHolds(2));
|
||||
|
||||
auto dir1 = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateDirIn(parent1.path()));
|
||||
auto dir2 = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateDirIn(parent2.path()));
|
||||
|
||||
EXPECT_THAT(Links(parent1.path()), IsPosixErrorOkAndHolds(3));
|
||||
EXPECT_THAT(Links(parent2.path()), IsPosixErrorOkAndHolds(3));
|
||||
|
||||
ASSERT_THAT(rename(dir1.path().c_str(), dir2.path().c_str()),
|
||||
SyscallSucceeds());
|
||||
|
||||
EXPECT_THAT(Links(parent1.path()), IsPosixErrorOkAndHolds(2));
|
||||
EXPECT_THAT(Links(parent2.path()), IsPosixErrorOkAndHolds(3));
|
||||
}
|
||||
|
||||
TEST(RenameTest, FileDoesNotExist) {
|
||||
auto dir = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateDir());
|
||||
const std::string source = JoinPath(dir.path(), "source");
|
||||
|
||||
Reference in New Issue
Block a user