Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Adder: Fix multi-file add so it works as expected. #3258

Closed
wants to merge 4 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions commands/files/slicefile.go
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,10 @@ func (f *SliceFile) IsDirectory() bool {
return true
}

func (f *SliceFile) NumFiles() int {
return len(f.files)
}

func (f *SliceFile) NextFile() (File, error) {
if f.n >= len(f.files) {
return nil, io.EOF
Expand Down
14 changes: 13 additions & 1 deletion core/commands/add.go
Original file line number Diff line number Diff line change
Expand Up @@ -80,6 +80,16 @@ You can now refer to the added file in a gateway, like so:
cmds.BoolOption(pinOptionName, "Pin this object when adding.").Default(true),
},
PreRun: func(req cmds.Request) error {
wrap, _, _ := req.Option(wrapOptionName).Bool()
recursive, _, _ := req.Option(cmds.RecLong).Bool()
sliceFile, ok := req.Files().(*files.SliceFile)
if !ok {
return fmt.Errorf("type assertion failed: req.Files().(*files.SliceFile)")
}
if !wrap && recursive && sliceFile.NumFiles() > 1 {
return fmt.Errorf("adding multiple directories without '-w' unsupported")
}

if quiet, _, _ := req.Option(quietOptionName).Bool(); quiet {
return nil
}
Expand Down Expand Up @@ -135,6 +145,7 @@ You can now refer to the added file in a gateway, like so:
silent, _, _ := req.Option(silentOptionName).Bool()
chunker, _, _ := req.Option(chunkerOptionName).String()
dopin, _, _ := req.Option(pinOptionName).Bool()
recursive, _, _ := req.Option(cmds.RecLong).Bool()

if hash {
nilnode, err := core.NewNode(n.Context(), &core.BuildCfg{
Expand All @@ -160,7 +171,8 @@ You can now refer to the added file in a gateway, like so:
outChan := make(chan interface{}, 8)
res.SetOutput((<-chan interface{})(outChan))

fileAdder, err := coreunix.NewAdder(req.Context(), n.Pinning, n.Blockstore, dserv)
useRoot := wrap || recursive
fileAdder, err := coreunix.NewAdder(req.Context(), n.Pinning, n.Blockstore, dserv, useRoot)
if err != nil {
res.SetError(err, cmds.ErrNormal)
return
Expand Down
82 changes: 56 additions & 26 deletions core/coreunix/add.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package coreunix

import (
"errors"
"fmt"
"io"
"io/ioutil"
Expand Down Expand Up @@ -64,14 +65,8 @@ type AddedObject struct {
Bytes int64 `json:",omitempty"`
}

func NewAdder(ctx context.Context, p pin.Pinner, bs bstore.GCBlockstore, ds dag.DAGService) (*Adder, error) {
mr, err := mfs.NewRoot(ctx, ds, unixfs.EmptyDirNode(), nil)
if err != nil {
return nil, err
}

return &Adder{
mr: mr,
func NewAdder(ctx context.Context, p pin.Pinner, bs bstore.GCBlockstore, ds dag.DAGService, useRoot bool) (*Adder, error) {
adder := &Adder{
ctx: ctx,
pinning: p,
blockstore: bs,
Expand All @@ -82,8 +77,17 @@ func NewAdder(ctx context.Context, p pin.Pinner, bs bstore.GCBlockstore, ds dag.
Trickle: false,
Wrap: false,
Chunker: "",
}, nil
}

if useRoot {
mr, err := mfs.NewRoot(ctx, ds, unixfs.EmptyDirNode(), nil)
if err != nil {
return nil, err
}
adder.mr = mr
}

return adder, nil
}

// Internal structure for holding the switches passed to the `add` call
Expand Down Expand Up @@ -130,6 +134,10 @@ func (adder Adder) add(reader io.Reader) (*dag.Node, error) {
}

func (adder *Adder) RootNode() (*dag.Node, error) {
if adder.mr == nil {
return nil, nil
}

// for memoizing
if adder.root != nil {
return adder.root, nil
Expand All @@ -153,6 +161,10 @@ func (adder *Adder) RootNode() (*dag.Node, error) {
}

func (adder *Adder) PinRoot() error {
if adder.mr == nil {
return nil
}

root, err := adder.RootNode()
if err != nil {
return err
Expand All @@ -179,6 +191,13 @@ func (adder *Adder) PinRoot() error {
}

func (adder *Adder) Finalize() (*dag.Node, error) {
if adder.mr == nil && adder.Pin {
err := adder.pinning.Flush()
return nil, err
} else if adder.mr == nil {
return nil, nil
}

root := adder.mr.GetValue()

// cant just call adder.RootNode() here as we need the name for printing
Expand Down Expand Up @@ -250,7 +269,7 @@ func (adder *Adder) outputDirs(path string, fsn mfs.FSNode) error {
func Add(n *core.IpfsNode, r io.Reader) (string, error) {
defer n.Blockstore.PinLock().Unlock()

fileAdder, err := NewAdder(n.Context(), n.Pinning, n.Blockstore, n.DAG)
fileAdder, err := NewAdder(n.Context(), n.Pinning, n.Blockstore, n.DAG, true)
if err != nil {
return "", err
}
Expand Down Expand Up @@ -278,7 +297,7 @@ func AddR(n *core.IpfsNode, root string) (key string, err error) {
}
defer f.Close()

fileAdder, err := NewAdder(n.Context(), n.Pinning, n.Blockstore, n.DAG)
fileAdder, err := NewAdder(n.Context(), n.Pinning, n.Blockstore, n.DAG, true)
if err != nil {
return "", err
}
Expand All @@ -302,7 +321,7 @@ func AddR(n *core.IpfsNode, root string) (key string, err error) {
// the directory, and and error if any.
func AddWrapped(n *core.IpfsNode, r io.Reader, filename string) (string, *dag.Node, error) {
file := files.NewReaderFile(filename, filename, ioutil.NopCloser(r), nil)
fileAdder, err := NewAdder(n.Context(), n.Pinning, n.Blockstore, n.DAG)
fileAdder, err := NewAdder(n.Context(), n.Pinning, n.Blockstore, n.DAG, true)
if err != nil {
return "", nil, err
}
Expand All @@ -324,23 +343,30 @@ func AddWrapped(n *core.IpfsNode, r io.Reader, filename string) (string, *dag.No
return gopath.Join(c.String(), filename), dagnode, nil
}

func (adder *Adder) addNode(node *dag.Node, path string) error {
// patch it into the root
if path == "" {
path = node.Cid().String()
}
func (adder *Adder) pinOrAddNode(node *dag.Node, path string) error {
if adder.Pin && adder.mr == nil {

adder.pinning.PinWithMode(node.Cid(), pin.Recursive)

} else if adder.mr != nil {

// patch it into the root
if path == "" {
path = node.Cid().String()
}

dir := gopath.Dir(path)
if dir != "." {
if err := mfs.Mkdir(adder.mr, dir, true, false); err != nil {
return err
}
}

dir := gopath.Dir(path)
if dir != "." {
if err := mfs.Mkdir(adder.mr, dir, true, false); err != nil {
if err := mfs.PutNode(adder.mr, path, node); err != nil {
return err
}
}

if err := mfs.PutNode(adder.mr, path, node); err != nil {
return err
}

if !adder.Silent {
return outputDagnode(adder.Out, path, node)
}
Expand Down Expand Up @@ -384,7 +410,7 @@ func (adder *Adder) addFile(file files.File) error {
return err
}

return adder.addNode(dagnode, s.FileName())
return adder.pinOrAddNode(dagnode, s.FileName())
}

// case for regular file
Expand All @@ -401,10 +427,14 @@ func (adder *Adder) addFile(file files.File) error {
}

// patch it into the root
return adder.addNode(dagnode, file.FileName())
return adder.pinOrAddNode(dagnode, file.FileName())
}

func (adder *Adder) addDir(dir files.File) error {
if adder.mr == nil {
return errors.New("cannot add directories without mfs root")
}

log.Infof("adding directory: %s", dir.FileName())

err := mfs.Mkdir(adder.mr, dir.FileName(), true, false)
Expand Down
2 changes: 1 addition & 1 deletion core/coreunix/add_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,7 @@ func TestAddGCLive(t *testing.T) {

errs := make(chan error)
out := make(chan interface{})
adder, err := NewAdder(context.Background(), node.Pinning, node.Blockstore, node.DAG)
adder, err := NewAdder(context.Background(), node.Pinning, node.Blockstore, node.DAG, true)
if err != nil {
t.Fatal(err)
}
Expand Down
2 changes: 1 addition & 1 deletion test/sharness/t0040-add-and-cat.sh
Original file line number Diff line number Diff line change
Expand Up @@ -285,7 +285,7 @@ test_expect_success "'ipfs add' with stdin input succeeds" '

test_expect_success "'ipfs add' output looks good" '
HASH="QmZDhWpi8NvKrekaYYhxKCdNVGWsFFe1CREnAjP1QbPaB3" &&
echo "added $HASH $HASH" >expected &&
echo "added $HASH " >expected &&
test_cmp expected actual
'

Expand Down
62 changes: 62 additions & 0 deletions test/sharness/t0046-multifile-add.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
#!/bin/sh
#
# Copyright (c) 2014 Christian Couder
# MIT Licensed; see the LICENSE file in this repository.
#

test_description="Test add and cat commands"

. lib/test-lib.sh

test_init_ipfs

test_expect_success "create some files" '
echo A > fileA &&
echo B > fileB &&
echo C > fileC
'

test_expect_success "add files all at once" '
ipfs add -q fileA fileB fileC > hashes
'

test_expect_success "unpin one of the files" '
ipfs pin rm `head -1 hashes` > pin-out
'

test_expect_success "unpin output looks good" '
echo "unpinned `head -1 hashes`" > pin-expect
test_cmp pin-expect pin-out
'

test_expect_success "create files with same name but in different directories" '
mkdir dirA &&
mkdir dirB &&
echo AA > dirA/fileA &&
echo BA > dirB/fileA
'

test_expect_success "add files with same name but in different directories" '
ipfs add -q dirA/fileA dirB/fileA > hashes
'

cat <<EOF | LC_ALL=C sort > cat-expected
AA
BA
EOF

test_expect_success "check that both files are added" '
cat hashes | xargs ipfs cat | LC_ALL=C sort > cat-actual
test_cmp cat-expected cat-actual
'

test_expect_success "adding multiple directories fails cleanly" '
test_must_fail ipfs add -q -r dirA dirB
'

test_expect_success "adding multiple directories with -w is okay" '
ipfs add -q -r -w dirA dirB > hashes &&
ipfs ls `tail -1 hashes` > ls-res
'

test_done
11 changes: 2 additions & 9 deletions test/sharness/t0080-repo.sh
Original file line number Diff line number Diff line change
Expand Up @@ -29,11 +29,6 @@ test_expect_success "'ipfs repo gc' succeeds" '
ipfs repo gc >gc_out_actual
'

test_expect_success "'ipfs repo gc' looks good (patch root)" '
PATCH_ROOT=QmQXirSbubiySKnqaFyfs5YzziXRB5JEVQVjU6xsd7innr &&
grep "removed $PATCH_ROOT" gc_out_actual
'

test_expect_success "'ipfs repo gc' doesnt remove file" '
ipfs cat "$HASH" >out &&
test_cmp out afile
Expand Down Expand Up @@ -104,8 +99,7 @@ test_expect_success "remove direct pin" '

test_expect_success "'ipfs repo gc' removes file" '
ipfs repo gc >actual7 &&
grep "removed $HASH" actual7 &&
grep "removed $PATCH_ROOT" actual7
grep "removed $HASH" actual7
'

test_expect_success "'ipfs refs local' no longer shows file" '
Expand All @@ -114,8 +108,7 @@ test_expect_success "'ipfs refs local' no longer shows file" '
grep "QmYCvbfNbCwFR45HiNP45rwJgvatpiW38D961L5qAhUM5Y" actual8 &&
grep "$EMPTY_DIR" actual8 &&
grep "$HASH_WELCOME_DOCS" actual8 &&
test_must_fail grep "$HASH" actual8 &&
test_must_fail grep "$PATCH_ROOT" actual8
test_must_fail grep "$HASH" actual8
'

test_expect_success "adding multiblock random file succeeds" '
Expand Down