X-Git-Url: https://git.arvados.org/arvados.git/blobdiff_plain/846e3037de341d73e593a670b0d0e77bc3e893c1..f416c6e0370c78a9aa18c2e461e0861b404a669a:/sdk/go/arvados/fs_collection.go diff --git a/sdk/go/arvados/fs_collection.go b/sdk/go/arvados/fs_collection.go index afe92c9911..f4dae746e2 100644 --- a/sdk/go/arvados/fs_collection.go +++ b/sdk/go/arvados/fs_collection.go @@ -1563,18 +1563,46 @@ func (dn *dirnode) snapshot() (*dirnode, error) { } func (dn *dirnode) Splice(repl inode) error { - repldn, ok := repl.(*dirnode) - if !ok { - return fmt.Errorf("cannot use Splice to replace a directory with a file: %w", ErrInvalidArgument) - } - repldn, err := repldn.snapshot() + repl, err := repl.Snapshot() if err != nil { - return err + return fmt.Errorf("cannot copy snapshot: %w", err) + } + switch repl := repl.(type) { + default: + return fmt.Errorf("cannot splice snapshot containing %T: %w", repl, ErrInvalidArgument) + case *dirnode: + dn.Lock() + defer dn.Unlock() + dn.inodes = repl.inodes + dn.setTreeFS(dn.fs) + case *filenode: + dn.parent.Lock() + defer dn.parent.Unlock() + removing, err := dn.parent.Child(dn.fileinfo.name, nil) + if err != nil { + return fmt.Errorf("cannot use Splice to replace a top-level directory with a file: %w", ErrInvalidOperation) + } else if removing != dn { + // If ../thisdirname is not this dirnode, it + // must be an inode that wraps a dirnode, like + // a collectionFileSystem or deferrednode. + if deferred, ok := removing.(*deferrednode); ok { + // More useful to report the type of + // the wrapped node rather than just + // *deferrednode. (We know the real + // inode is already loaded because dn + // is inside it.) + removing = deferred.realinode() + } + return fmt.Errorf("cannot use Splice to attach a file at top level of %T: %w", removing, ErrInvalidOperation) + } + dn.Lock() + defer dn.Unlock() + _, err = dn.parent.Child(dn.fileinfo.name, func(inode) (inode, error) { return repl, nil }) + if err != nil { + return fmt.Errorf("error replacing filenode: dn.parent.Child(): %w", err) + } + repl.fs = dn.fs } - dn.Lock() - defer dn.Unlock() - dn.inodes = repldn.inodes - dn.setTreeFS(dn.fs) return nil }