@cryptotaxi247 / kubo / commits / c4c70cb53

commands: add `--force` option to `files cp` command (#10823)

* 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

Andrew Gillis committed Jun 10, 2025 at 08:21 UTC c4c70cb53c8b8934415835091e7cda3e72242411
3 files changed +57 -4
core/commands/files.go
+39 -3
@@ -440,10 +440,10 @@ being GC'ed.
440 cmds.StringArg("dest", true, false, "Destination within MFS."),
441 },
442 Options: []cmds.Option{
443 + cmds.BoolOption(forceOptionName, "Force overwrite of existing files."),
444 cmds.BoolOption(filesParentsOptionName, "p", "Make parent directories as needed."),
445 },
446 Run: func(req *cmds.Request, res cmds.ResponseEmitter, env cmds.Environment) error {
446 - mkParents, _ := req.Options[filesParentsOptionName].(bool)
447 nd, err := cmdenv.GetNode(env)
448 if err != nil {
449 return err
@@ -459,8 +459,6 @@ being GC'ed.
459 return err
460 }
461
462 - flush, _ := req.Options[filesFlushOptionName].(bool)
463 -
462 src, err := checkPath(req.Arguments[0])
463 if err != nil {
464 return err
@@ -500,6 +498,7 @@ being GC'ed.
498 return errFilesCpInvalidUnixFS
499 }
500
501 + mkParents, _ := req.Options[filesParentsOptionName].(bool)
502 if mkParents {
503 err := ensureContainingDirectoryExists(nd.FilesRoot, dst, prefix)
504 if err != nil {
@@ -507,11 +506,19 @@ being GC'ed.
506 }
507 }
508
509 + force, _ := req.Options[forceOptionName].(bool)
510 + if force {
511 + if err = unlinkNodeIfExists(nd, dst); err != nil {
512 + return fmt.Errorf("cp: cannot unlink existing file: %s", err)
513 + }
514 + }
515 +
516 err = mfs.PutNode(nd.FilesRoot, dst, node)
517 if err != nil {
518 return fmt.Errorf("cp: cannot put node in path %s: %s", dst, err)
519 }
520
521 + flush, _ := req.Options[filesFlushOptionName].(bool)
522 if flush {
523 if _, err := mfs.FlushPath(req.Context, nd.FilesRoot, dst); err != nil {
524 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
553 }
554 }
555
556 +func unlinkNodeIfExists(node *core.IpfsNode, path string) error {
557 + dir, name := gopath.Split(path)
558 + parent, err := mfs.Lookup(node.FilesRoot, dir)
559 + if err != nil {
560 + if errors.Is(err, os.ErrNotExist) {
561 + return nil
562 + }
563 + return err
564 + }
565 +
566 + pdir, ok := parent.(*mfs.Directory)
567 + if !ok {
568 + return fmt.Errorf("not a directory: %s", dir)
569 + }
570 +
571 + // Attempt to unlink if child is a file, ignore error since
572 + // we are only concerned with unlinking an existing file.
573 + child, err := pdir.Child(name)
574 + if err != nil {
575 + return nil // no child file, nothing to unlink
576 + }
577 +
578 + if child.Type() != mfs.TFile {
579 + return fmt.Errorf("not a file: %s", path)
580 + }
581 +
582 + return pdir.Unlink(name)
583 +}
584 +
585 type filesLsOutput struct {
586 Entries []mfs.NodeListing
587 }
docs/changelogs/v0.36.md
+6 -1
@@ -10,7 +10,8 @@ This release was brought to you by the [Shipyard](http://ipshipyard.com/) team.
10
11 - [Overview](#overview)
12 - [🔦 Highlights](#-highlights)
13 - - [Update go-log to v2](#update-go-log-to-v2
13 + - [Update go-log to v2](#update-go-log-to-v2)
14 + - [Overwrite option for files cp command](#overwrite-option-for-files-cp-command)
15 - [Option for filestore command to remove bad blocks](#option-for-filestore-command-to-remove-bad-blocks)
16 - [📦️ Important dependency updates](#-important-dependency-updates)
17 - [📝 Changelog](#-changelog)
@@ -29,6 +30,10 @@ go-log v2 has been out for quite a while now and it is time to deprecate v1.
30 - Fixes `ipfs log tail`
31 - Removes support for `ContextWithLoggable` as this is not needed for tracing-like functionality
32
33 +#### Overwrite option for files cp command
34 +
35 +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.
36 +
37 #### Option for filestore command to remove bad blocks
38
39 The `filestore` command has a new option, `--remove-bad-blocks`, to verify objects in the filestore and remove those that fail verification.
test/sharness/t0250-files-api.sh
+12
@@ -674,6 +674,18 @@ test_files_api() {
674 ipfs files ls /adir | grep foobar
675 '
676
677 + test_expect_success "test copy --force overwrites files" '
678 + ipfs files cp /ipfs/$FILE1 /file1 &&
679 + ipfs files cp /ipfs/$FILE2 /file2 &&
680 + ipfs files cp --force /file1 /file2 &&
681 + test "`ipfs files read /file1`" = "`ipfs files read /file2`"
682 + '
683 +
684 + test_expect_success "clean up" '
685 + ipfs files rm /file1 &&
686 + ipfs files rm /file2
687 + '
688 +
689 test_expect_success "should fail to write file and create intermediate directories with no --parents flag set $EXTRA" '
690 echo "ipfs rocks" | test_must_fail ipfs files write --create /parents/foo/ipfs.txt
691 '