// RenameAt implements vfs.FilesystemImpl.RenameAt.
RenameAt implements vfs.FilesystemImpl.RenameAt.
// RenameAt implements vfs . FilesystemImpl . RenameAt .
func (fs *filesystem) RenameAt(ctx context.Context, rp *vfs.ResolvingPath, oldParentVD vfs.VirtualDentry, oldName string, opts vfs.RenameOptions) error {
	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 {
		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()
	if mnt != oldParentVD.Mount() {
		return syserror.EXDEV
	}
	if err := mnt.CheckBeginWrite(); err != nil {
		return err
	}
	defer mnt.EndWrite()
	oldParentDir := oldParentVD.Dentry().Impl().(*dentry).inode.impl.(*directory)
	if err := oldParentDir.inode.checkPermissions(rp.Credentials(), vfs.MayWrite|vfs.MayExec); err != nil {
		return err
	}
	renamed, ok := oldParentDir.childMap[oldName]
	if !ok {
		return syserror.ENOENT
	}
	if err := oldParentDir.mayDelete(rp.Credentials(), renamed); err != nil {
		return err
	}
	if renamed.inode.isDir() {
		if renamed == &newParentDir.dentry || genericIsAncestorDentry(renamed, &newParentDir.dentry) {
			return syserror.EINVAL
		}
		if oldParentDir != newParentDir {
			if err := renamed.inode.checkPermissions(rp.Credentials(), vfs.MayWrite); err != nil {
				return err
			}
		}
	} else {
		if opts.MustBeDir || rp.MustBeDir() {
			return syserror.ENOTDIR
		}
	}
	if err := newParentDir.inode.checkPermissions(rp.Credentials(), vfs.MayWrite|vfs.MayExec); err != nil {
		return err
	}
	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() {
				return syserror.EISDIR
			}
			if len(replacedDir.childMap) != 0 {
				return syserror.ENOTEMPTY
			}
		} else {
			if rp.MustBeDir() {
				return syserror.ENOTDIR
			}
			if renamed.inode.isDir() {
				return syserror.ENOTDIR
			}
		}
	} else {
		if renamed.inode.isDir() && newParentDir.inode.nlink == maxLinks {
			return syserror.EMLINK
		}
	}
	if newParentDir.dentry.vfsd.IsDead() {
		return syserror.ENOENT
	}
	if renamed == replaced {
		return nil
	}
	vfsObj := rp.VirtualFilesystem()
	mntns := vfs.MountNamespaceFromContext(ctx)
	defer mntns.DecRef(ctx)
	var replacedVFSD *vfs.Dentry
	if replaced != nil {
		replacedVFSD = &replaced.vfsd
	}
	if err := vfsObj.PrepareRenameDentry(mntns, &renamed.vfsd, replacedVFSD); err != nil {
		return err
	}
	if replaced != nil {
		newParentDir.removeChildLocked(replaced)
		if replaced.inode.isDir() {
			replaced.inode.decLinksLocked(ctx)
			newParentDir.inode.decLinksLocked(ctx)
		}
		replaced.inode.decLinksLocked(ctx)
	}
	oldParentDir.removeChildLocked(renamed)
	newParentDir.insertChildLocked(renamed, newName)
	vfsObj.CommitRenameReplaceDentry(ctx, &renamed.vfsd, replacedVFSD)
	oldParentDir.inode.touchCMtime()
	if oldParentDir != newParentDir {
		if renamed.inode.isDir() {
			oldParentDir.inode.decLinksLocked(ctx)
			newParentDir.inode.incLinksLocked()
		}
		newParentDir.inode.touchCMtime()
	}
	renamed.inode.touchCtime()
	vfs.InotifyRename(ctx, &renamed.inode.watches, &oldParentDir.inode.watches, &newParentDir.inode.watches, oldName, newName, renamed.inode.isDir())
	return nil
}
func ( fs * filesystem ) RenameAt ( ctx context . Context , rp * vfs . ResolvingPath , oldParentVD vfs . VirtualDentry , oldName string , opts vfs . RenameOptions ) error { 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 { 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 ( ) if mnt != oldParentVD . Mount ( ) { return syserror . EXDEV } if err := mnt . CheckBeginWrite ( ) ; err != nil { return err } defer mnt . EndWrite ( ) oldParentDir := oldParentVD . Dentry ( ) . Impl ( ) . ( * dentry ) . inode . impl . ( * directory ) if err := oldParentDir . inode . checkPermissions ( rp . Credentials ( ) , vfs . MayWrite | vfs . MayExec ) ; err != nil { return err } renamed , ok := oldParentDir . childMap [ oldName ] if ! ok { return syserror . ENOENT } if err := oldParentDir . mayDelete ( rp . Credentials ( ) , renamed ) ; err != nil { return err } if renamed . inode . isDir ( ) { if renamed == & newParentDir . dentry || genericIsAncestorDentry ( renamed , & newParentDir . dentry ) { return syserror . EINVAL } if oldParentDir != newParentDir { if err := renamed . inode . checkPermissions ( rp . Credentials ( ) , vfs . MayWrite ) ; err != nil { return err } } } else { if opts . MustBeDir || rp . MustBeDir ( ) { return syserror . ENOTDIR } } if err := newParentDir . inode . checkPermissions ( rp . Credentials ( ) , vfs . MayWrite | vfs . MayExec ) ; err != nil { return err } 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 ( ) { return syserror . EISDIR } if len ( replacedDir . childMap ) != 0 { return syserror . ENOTEMPTY } } else { if rp . MustBeDir ( ) { return syserror . ENOTDIR } if renamed . inode . isDir ( ) { return syserror . ENOTDIR } } } else { if renamed . inode . isDir ( ) && newParentDir . inode . nlink == maxLinks { return syserror . EMLINK } } if newParentDir . dentry . vfsd . IsDead ( ) { return syserror . ENOENT } if renamed == replaced { return nil } vfsObj := rp . VirtualFilesystem ( ) mntns := vfs . MountNamespaceFromContext ( ctx ) defer mntns . DecRef ( ctx ) var replacedVFSD * vfs . Dentry if replaced != nil { replacedVFSD = & replaced . vfsd } if err := vfsObj . PrepareRenameDentry ( mntns , & renamed . vfsd , replacedVFSD ) ; err != nil { return err } if replaced != nil { newParentDir . removeChildLocked ( replaced ) if replaced . inode . isDir ( ) { replaced . inode . decLinksLocked ( ctx ) newParentDir . inode . decLinksLocked ( ctx ) } replaced . inode . decLinksLocked ( ctx ) } oldParentDir . removeChildLocked ( renamed ) newParentDir . insertChildLocked ( renamed , newName ) vfsObj . CommitRenameReplaceDentry ( ctx , & renamed . vfsd , replacedVFSD ) oldParentDir . inode . touchCMtime ( ) if oldParentDir != newParentDir { if renamed . inode . isDir ( ) { oldParentDir . inode . decLinksLocked ( ctx ) newParentDir . inode . incLinksLocked ( ) } newParentDir . inode . touchCMtime ( ) } renamed . inode . touchCtime ( ) vfs . InotifyRename ( ctx , & renamed . inode . watches , & oldParentDir . inode . watches , & newParentDir . inode . watches , oldName , newName , renamed . inode . isDir ( ) ) return nil }
RenameAt implements vfs.FilesystemImpl.RenameAt.
RenameAt implements vfs . FilesystemImpl . RenameAt .
// Resolve newParentDir first to verify that it's on this Mount. // TODO(b/145974740): Support other renameat2 flags. // Note that we don't need to call rp.CheckMount(), since if renamed is a // mount point then we want to rename the mount point, not anything in the // mounted filesystem. // Writability is needed to change renamed's "..". // tmpfs never calls VFS.InvalidateDentry(), so newParentDir.dentry can // only be dead if it was deleted. // Linux places this check before some of those above; we do it here for // simplicity, under the assumption that applications are not intentionally // doing noop renames expecting them to succeed where non-noop renames // would fail. // Remove links for replaced/. and replaced/..