in internal/fs/fs.go [2039:2101]
func (fs *fileSystem) Rename(
ctx context.Context,
op *fuseops.RenameOp) (err error) {
if fs.newConfig.FileSystem.IgnoreInterrupts {
// When ignore interrupts config is set, we are creating a new context not
// cancellable by parent context.
var cancel context.CancelFunc
ctx, cancel = util.IsolateContextFromParentContext(ctx)
defer cancel()
}
// Find the old and new parents.
fs.mu.Lock()
oldParent := fs.dirInodeOrDie(op.OldParent)
newParent := fs.dirInodeOrDie(op.NewParent)
fs.mu.Unlock()
if oldInode, ok := oldParent.(inode.BucketOwnedInode); !ok {
// The old parent is not owned by any bucket, which means it's the base
// directory that holds all the buckets' root directories. So, this op
// is to rename a bucket, which is not supported.
return fmt.Errorf("rename a bucket: %w", syscall.ENOTSUP)
} else {
// The target path must exist in the same bucket.
oldBucket := oldInode.Bucket().Name()
if newInode, ok := newParent.(inode.BucketOwnedInode); !ok || oldBucket != newInode.Bucket().Name() {
return fmt.Errorf("move out of bucket %q: %w", oldBucket, syscall.ENOTSUP)
}
}
child, err := fs.lookUpOrCreateChildInode(ctx, oldParent, op.OldName)
if err != nil {
return err
}
if child == nil {
return fuse.ENOENT
}
child.DecrementLookupCount(1)
child.Unlock()
childBktOwned, ok := child.(inode.BucketOwnedInode)
if !ok { // Won't happen in ideal case.
return fmt.Errorf("child inode (id %v) is not owned by any bucket", child.ID())
}
if child.Name().IsDir() {
// If 'enable-hns' flag is false, the bucket type is set to 'NonHierarchical' even for HNS buckets because the control client is nil.
// Therefore, an additional 'enable hns' check is not required here.
if childBktOwned.Bucket().BucketType().Hierarchical {
return fs.renameHierarchicalDir(ctx, oldParent, op.OldName, newParent, op.NewName)
}
return fs.renameNonHierarchicalDir(ctx, oldParent, op.OldName, newParent, op.NewName)
}
childFileInode, ok := child.(*inode.FileInode)
if !ok {
return fmt.Errorf("child inode (id %v) is neither file nor directory inode", child.ID())
}
// TODO(b/402335988): Fix rename flow for local files when streaming writes is disabled.
// If object to be renamed is a local file inode and streaming writes are disabled, rename operation is not supported.
if childFileInode.IsLocal() && !fs.newConfig.Write.EnableStreamingWrites {
return fmt.Errorf("cannot rename open file %q: %w", op.OldName, syscall.ENOTSUP)
}
return fs.renameFile(ctx, op, childFileInode, oldParent, newParent)
}