Skip to content

Commit

Permalink
feat(cmds/add): --to-files option as files cp
Browse files Browse the repository at this point in the history
  • Loading branch information
schomatis committed Jul 21, 2022
1 parent cf82ae5 commit 6b45c92
Show file tree
Hide file tree
Showing 2 changed files with 90 additions and 9 deletions.
70 changes: 67 additions & 3 deletions core/commands/add.go
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,8 @@ import (
"github.com/cheggaaa/pb"
cmds "github.com/ipfs/go-ipfs-cmds"
files "github.com/ipfs/go-ipfs-files"
ipld "github.com/ipfs/go-ipld-format"
mfs "github.com/ipfs/go-mfs"
coreiface "github.com/ipfs/interface-go-ipfs-core"
"github.com/ipfs/interface-go-ipfs-core/options"
mh "github.com/multiformats/go-multihash"
Expand Down Expand Up @@ -45,6 +47,7 @@ const (
hashOptionName = "hash"
inlineOptionName = "inline"
inlineLimitOptionName = "inline-limit"
toFilesOptionName = "to-files"
)

const adderOutChanSize = 8
Expand Down Expand Up @@ -79,6 +82,13 @@ You can now refer to the added file in a gateway, like so:
/ipfs/QmaG4FuMqEBnQNn3C8XJ5bpW8kLs7zq2ZXgHptJHbKDDVx/example.jpg
Files imported with 'ipfs add' are protected from GC (implicit '--pin=true'),
but it is up to you to remember the returned CID to get the data back later.
Passing '--to-files' creates a reference in Files API (MFS), making it easier
to find it in the future. See 'ipfs files --help' to learn more about using MFS
for keeping track of added files and directories.
The chunker option, '-s', specifies the chunking strategy that dictates
how to break files into blocks. Blocks with same content can
be deduplicated. Different chunking strategies will produce different
Expand Down Expand Up @@ -132,14 +142,15 @@ only-hash, and progress/status related flags) will change the final hash.
cmds.BoolOption(onlyHashOptionName, "n", "Only chunk and hash - do not write to disk."),
cmds.BoolOption(wrapOptionName, "w", "Wrap files with a directory object."),
cmds.StringOption(chunkerOptionName, "s", "Chunking algorithm, size-[bytes], rabin-[min]-[avg]-[max] or buzhash").WithDefault("size-262144"),
cmds.BoolOption(pinOptionName, "Pin this object when adding.").WithDefault(true),
cmds.BoolOption(rawLeavesOptionName, "Use raw blocks for leaf nodes."),
cmds.BoolOption(noCopyOptionName, "Add the file using filestore. Implies raw-leaves. (experimental)"),
cmds.BoolOption(fstoreCacheOptionName, "Check the filestore for pre-existing blocks. (experimental)"),
cmds.IntOption(cidVersionOptionName, "CID version. Defaults to 0 unless an option that depends on CIDv1 is passed. Passing version 1 will cause the raw-leaves option to default to true."),
cmds.StringOption(hashOptionName, "Hash function to use. Implies CIDv1 if not sha2-256. (experimental)").WithDefault("sha2-256"),
cmds.BoolOption(inlineOptionName, "Inline small blocks into CIDs. (experimental)"),
cmds.IntOption(inlineLimitOptionName, "Maximum block size to inline. (experimental)").WithDefault(32),
cmds.BoolOption(pinOptionName, "Pin locally to protect added files from garbage collection.").WithDefault(true),
cmds.StringOption(toFilesOptionName, "Add reference to Files API (MFS) at the provided path."),
},
PreRun: func(req *cmds.Request, env cmds.Environment) error {
quiet, _ := req.Options[quietOptionName].(bool)
Expand Down Expand Up @@ -180,10 +191,11 @@ only-hash, and progress/status related flags) will change the final hash.
hashFunStr, _ := req.Options[hashOptionName].(string)
inline, _ := req.Options[inlineOptionName].(bool)
inlineLimit, _ := req.Options[inlineLimitOptionName].(int)
toFilesStr, toFilesSet := req.Options[toFilesOptionName].(string)

hashFunCode, ok := mh.Names[strings.ToLower(hashFunStr)]
if !ok {
return fmt.Errorf("unrecognized hash function: %s", strings.ToLower(hashFunStr))
return fmt.Errorf("unrecognized hash function: %q", strings.ToLower(hashFunStr))
}

enc, err := cmdenv.GetCidEncoder(req)
Expand All @@ -198,6 +210,42 @@ only-hash, and progress/status related flags) will change the final hash.
})
}

ipfsNode, err := cmdenv.GetNode(env)
if err != nil {
return err
}
toFilesDst := ""
if toFilesSet {
dirIterator := toadd.Entries()
if !dirIterator.Next() {
if dirIterator.Err() != nil {
return dirIterator.Err()
}
return fmt.Errorf("%s: to-no file entry to copy to MFS path %q", toFilesOptionName, toFilesStr)
}
srcName := dirIterator.Name()
if dirIterator.Next() {
return fmt.Errorf("%s: more than one entry to copy to MFS path %q: %q and %q", toFilesOptionName, toFilesStr, srcName, dirIterator.Name())
}

if toFilesStr == "" {
toFilesStr = "/"
}
toFilesDst, err = checkPath(toFilesStr)
if err != nil {
return fmt.Errorf("%s: %w", toFilesOptionName, err)
}

if toFilesDst[len(toFilesDst)-1] == '/' {
toFilesDst += path.Base(srcName)
}

_, err = mfs.Lookup(ipfsNode.FilesRoot, path.Dir(toFilesDst))
if err != nil {
return fmt.Errorf("%s: MFS destination directory %q does not exist: %w", toFilesOptionName, path.Dir(toFilesDst), err)
}
}

opts := []options.UnixfsAddOption{
options.Unixfs.Hash(hashFunCode),

Expand Down Expand Up @@ -240,7 +288,23 @@ only-hash, and progress/status related flags) will change the final hash.
go func() {
var err error
defer close(events)
_, err = api.Unixfs().Add(req.Context, addit.Node(), opts...)
pathAdded, err := api.Unixfs().Add(req.Context, addit.Node(), opts...)
if err != nil {
errCh <- err
return
}
if toFilesSet {
var nodeAdded ipld.Node
nodeAdded, err = api.Dag().Get(req.Context, pathAdded.Cid())
if err != nil {
errCh <- err
return
}
err = mfs.PutNode(ipfsNode.FilesRoot, toFilesDst, nodeAdded)
if err != nil {
err = fmt.Errorf("%s: cannot put node in path %s: %s", toFilesOptionName, toFilesDst, err)
}
}
errCh <- err
}()

Expand Down
29 changes: 23 additions & 6 deletions core/commands/files.go
Original file line number Diff line number Diff line change
Expand Up @@ -581,7 +581,7 @@ const (

var filesReadCmd = &cmds.Command{
Helptext: cmds.HelpText{
Tagline: "Read a file in a given MFS.",
Tagline: "Read a file from MFS.",
ShortDescription: `
Read a specified number of bytes from a file at a given offset. By default,
it will read the entire file similar to the Unix cat.
Expand Down Expand Up @@ -724,11 +724,16 @@ const (

var filesWriteCmd = &cmds.Command{
Helptext: cmds.HelpText{
Tagline: "Write to a mutable file in a given filesystem.",
Tagline: "Append to (modify) a file in MFS.",
ShortDescription: `
Write data to a file in a given filesystem. This command allows you to specify
a beginning offset to write to. The entire length of the input will be
written.
A low-level MFS command that allows you to append data to a file. If you want
to add a file without modifying an existing one, use 'ipfs add --to-files'
instead.
`,
LongDescription: `
A low-level MFS command that allows you to append data at the end of a file, or
specify a beginning offset within a file to write to. The entire length of the
input will be written.
If the '--create' option is specified, the file will be created if it does not
exist. Nonexistent intermediate directories will not be created unless the
Expand All @@ -755,6 +760,18 @@ WARNING:
Usage of the '--flush=false' option does not guarantee data durability until
the tree has been flushed. This can be accomplished by running 'ipfs files
stat' on the file or any of its ancestors.
WARNING:
The CID produced by 'files write' will be different from 'ipfs add' because
'ipfs file write' creates a trickle-dag optimized for append-only operations
See '--trickle' in 'ipfs add --help' for more information.
If you want to add a file without modifying an existing one,
use 'ipfs add --to-files' instead. Below will add to MFS at /myfs/file.txt:
ipfs add --to-files /myfs/ file.txt
`,
},
Arguments: []cmds.Argument{
Expand Down Expand Up @@ -1019,7 +1036,7 @@ func updatePath(rt *mfs.Root, pth string, builder cid.Builder) error {

var filesRmCmd = &cmds.Command{
Helptext: cmds.HelpText{
Tagline: "Remove a file.",
Tagline: "Remove a file from MFS.",
ShortDescription: `
Remove files or directories.
Expand Down

0 comments on commit 6b45c92

Please sign in to comment.