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
'