commands: add --force option to files cp command (#10823)
Some checks failed
CodeQL / codeql (push) Has been cancelled
Docker Build / docker-build (push) Has been cancelled
Gateway Conformance / gateway-conformance (push) Has been cancelled
Gateway Conformance / gateway-conformance-libp2p-experiment (push) Has been cancelled
Go Build / go-build (push) Has been cancelled
Go Check / go-check (push) Has been cancelled
Go Lint / go-lint (push) Has been cancelled
Go Test / go-test (push) Has been cancelled
Interop / interop-prep (push) Has been cancelled
Sharness / sharness-test (push) Has been cancelled
Spell Check / spellcheck (push) Has been cancelled
Interop / helia-interop (push) Has been cancelled
Interop / ipfs-webui (push) Has been cancelled

* commands: add `--force` option to `files cp` command

Adds a `--force` option to allow the `ipfs files cp` command to overwrite existig files. Returns error is trying to overwrite directories.

Replaces #4079

Closes #2074

* Update test/sharness/t0250-files-api.sh
This commit is contained in:
Andrew Gillis 2025-06-10 08:21:54 -07:00 committed by GitHub
parent 34cf31855a
commit c4c70cb53c
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
3 changed files with 57 additions and 4 deletions

View File

@ -440,10 +440,10 @@ being GC'ed.
cmds.StringArg("dest", true, false, "Destination within MFS."),
},
Options: []cmds.Option{
cmds.BoolOption(forceOptionName, "Force overwrite of existing files."),
cmds.BoolOption(filesParentsOptionName, "p", "Make parent directories as needed."),
},
Run: func(req *cmds.Request, res cmds.ResponseEmitter, env cmds.Environment) error {
mkParents, _ := req.Options[filesParentsOptionName].(bool)
nd, err := cmdenv.GetNode(env)
if err != nil {
return err
@ -459,8 +459,6 @@ being GC'ed.
return err
}
flush, _ := req.Options[filesFlushOptionName].(bool)
src, err := checkPath(req.Arguments[0])
if err != nil {
return err
@ -500,6 +498,7 @@ being GC'ed.
return errFilesCpInvalidUnixFS
}
mkParents, _ := req.Options[filesParentsOptionName].(bool)
if mkParents {
err := ensureContainingDirectoryExists(nd.FilesRoot, dst, prefix)
if err != nil {
@ -507,11 +506,19 @@ being GC'ed.
}
}
force, _ := req.Options[forceOptionName].(bool)
if force {
if err = unlinkNodeIfExists(nd, dst); err != nil {
return fmt.Errorf("cp: cannot unlink existing file: %s", err)
}
}
err = mfs.PutNode(nd.FilesRoot, dst, node)
if err != nil {
return fmt.Errorf("cp: cannot put node in path %s: %s", dst, err)
}
flush, _ := req.Options[filesFlushOptionName].(bool)
if flush {
if _, err := mfs.FlushPath(req.Context, nd.FilesRoot, dst); err != nil {
return fmt.Errorf("cp: cannot flush the created file %s: %s", dst, err)
@ -546,6 +553,35 @@ func getNodeFromPath(ctx context.Context, node *core.IpfsNode, api iface.CoreAPI
}
}
func unlinkNodeIfExists(node *core.IpfsNode, path string) error {
dir, name := gopath.Split(path)
parent, err := mfs.Lookup(node.FilesRoot, dir)
if err != nil {
if errors.Is(err, os.ErrNotExist) {
return nil
}
return err
}
pdir, ok := parent.(*mfs.Directory)
if !ok {
return fmt.Errorf("not a directory: %s", dir)
}
// Attempt to unlink if child is a file, ignore error since
// we are only concerned with unlinking an existing file.
child, err := pdir.Child(name)
if err != nil {
return nil // no child file, nothing to unlink
}
if child.Type() != mfs.TFile {
return fmt.Errorf("not a file: %s", path)
}
return pdir.Unlink(name)
}
type filesLsOutput struct {
Entries []mfs.NodeListing
}

View File

@ -10,7 +10,8 @@ This release was brought to you by the [Shipyard](http://ipshipyard.com/) team.
- [Overview](#overview)
- [🔦 Highlights](#-highlights)
- [Update go-log to v2](#update-go-log-to-v2
- [Update go-log to v2](#update-go-log-to-v2)
- [Overwrite option for files cp command](#overwrite-option-for-files-cp-command)
- [Option for filestore command to remove bad blocks](#option-for-filestore-command-to-remove-bad-blocks)
- [📦️ Important dependency updates](#-important-dependency-updates)
- [📝 Changelog](#-changelog)
@ -29,6 +30,10 @@ go-log v2 has been out for quite a while now and it is time to deprecate v1.
- Fixes `ipfs log tail`
- Removes support for `ContextWithLoggable` as this is not needed for tracing-like functionality
#### Overwrite option for files cp command
The `ipfs files cp` command has a `--force` option to allow it to overwrite existing files. Attempting to overwrite an existing directory results in an error.
#### Option for filestore command to remove bad blocks
The `filestore` command has a new option, `--remove-bad-blocks`, to verify objects in the filestore and remove those that fail verification.

View File

@ -674,6 +674,18 @@ test_files_api() {
ipfs files ls /adir | grep foobar
'
test_expect_success "test copy --force overwrites files" '
ipfs files cp /ipfs/$FILE1 /file1 &&
ipfs files cp /ipfs/$FILE2 /file2 &&
ipfs files cp --force /file1 /file2 &&
test "`ipfs files read /file1`" = "`ipfs files read /file2`"
'
test_expect_success "clean up" '
ipfs files rm /file1 &&
ipfs files rm /file2
'
test_expect_success "should fail to write file and create intermediate directories with no --parents flag set $EXTRA" '
echo "ipfs rocks" | test_must_fail ipfs files write --create /parents/foo/ipfs.txt
'