diff --git a/core/commands/block.go b/core/commands/block.go index bc892594247..8fe3ac3e612 100644 --- a/core/commands/block.go +++ b/core/commands/block.go @@ -146,6 +146,7 @@ than 'sha2-256' or format to anything other than 'v0' will result in CIDv1. cmdkit.StringOption(blockFormatOptionName, "f", "cid format for blocks to be created with."), cmdkit.StringOption(mhtypeOptionName, "multihash hash function").WithDefault("sha2-256"), cmdkit.IntOption(mhlenOptionName, "multihash hash length").WithDefault(-1), + cmdkit.BoolOption(pinOptionName, "pin added blocks recursively").WithDefault(false), }, Run: func(req *cmds.Request, res cmds.ResponseEmitter, env cmds.Environment) error { api, err := cmdenv.GetApi(env, req) @@ -178,7 +179,12 @@ than 'sha2-256' or format to anything other than 'v0' will result in CIDv1. } } - p, err := api.Block().Put(req.Context, file, options.Block.Hash(mhtval, mhlen), options.Block.Format(format)) + pin, _ := req.Options[pinOptionName].(bool) + + p, err := api.Block().Put(req.Context, file, + options.Block.Hash(mhtval, mhlen), + options.Block.Format(format), + options.Block.Pin(pin)) if err != nil { return err } diff --git a/core/coreapi/block.go b/core/coreapi/block.go index fafd24755f4..1a75630aaa1 100644 --- a/core/coreapi/block.go +++ b/core/coreapi/block.go @@ -10,6 +10,7 @@ import ( util "github.com/ipfs/go-ipfs/blocks/blockstoreutil" coreiface "github.com/ipfs/go-ipfs/core/coreapi/interface" caopts "github.com/ipfs/go-ipfs/core/coreapi/interface/options" + pin "github.com/ipfs/go-ipfs/pin" cid "gx/ipfs/QmR8BauakNcBa3RbE4nbQu76PDiJgoQgz8AJdhJuiU4TAw/go-cid" blocks "gx/ipfs/QmWoXtvgC8inqFkAATB7cp2Dax7XBi9VDvSg9RCCZufmRk/go-block-format" @@ -23,7 +24,7 @@ type BlockStat struct { } func (api *BlockAPI) Put(ctx context.Context, src io.Reader, opts ...caopts.BlockPutOption) (coreiface.BlockStat, error) { - _, pref, err := caopts.BlockPutOptions(opts...) + settings, pref, err := caopts.BlockPutOptions(opts...) if err != nil { return nil, err } @@ -43,11 +44,19 @@ func (api *BlockAPI) Put(ctx context.Context, src io.Reader, opts ...caopts.Bloc return nil, err } + if settings.Pin { + defer api.blockstore.PinLock().Unlock() + } + err = api.blocks.AddBlock(b) if err != nil { return nil, err } + if settings.Pin { + api.pinning.PinWithMode(b.Cid(), pin.Recursive) + } + return &BlockStat{path: coreiface.IpldPath(b.Cid()), size: len(data)}, nil } diff --git a/core/coreapi/interface/options/block.go b/core/coreapi/interface/options/block.go index ea4ae26bb61..40dfba79ab2 100644 --- a/core/coreapi/interface/options/block.go +++ b/core/coreapi/interface/options/block.go @@ -10,6 +10,7 @@ type BlockPutSettings struct { Codec string MhType uint64 MhLength int + Pin bool } type BlockRmSettings struct { @@ -24,6 +25,7 @@ func BlockPutOptions(opts ...BlockPutOption) (*BlockPutSettings, cid.Prefix, err Codec: "", MhType: mh.SHA2_256, MhLength: -1, + Pin: false, } for _, opt := range opts { @@ -105,6 +107,15 @@ func (blockOpts) Hash(mhType uint64, mhLen int) BlockPutOption { } } +// Pin is an option for Block.Put which specifies whether to (recursively) pin +// added blocks +func (blockOpts) Pin(pin bool) BlockPutOption { + return func(settings *BlockPutSettings) error { + settings.Pin = pin + return nil + } +} + // Force is an option for Block.Rm which, when set to true, will ignore // non-existing blocks func (blockOpts) Force(force bool) BlockRmOption { diff --git a/core/coreapi/interface/tests/block.go b/core/coreapi/interface/tests/block.go index 427ad3357be..c2ee70a3a6e 100644 --- a/core/coreapi/interface/tests/block.go +++ b/core/coreapi/interface/tests/block.go @@ -26,6 +26,7 @@ func (tp *provider) TestBlock(t *testing.T) { t.Run("TestBlockGet", tp.TestBlockGet) t.Run("TestBlockRm", tp.TestBlockRm) t.Run("TestBlockStat", tp.TestBlockStat) + t.Run("TestBlockPin", tp.TestBlockPin) } func (tp *provider) TestBlockPut(t *testing.T) { @@ -203,3 +204,40 @@ func (tp *provider) TestBlockStat(t *testing.T) { t.Error("length doesn't match") } } + +func (tp *provider) TestBlockPin(t *testing.T) { + ctx, cancel := context.WithCancel(context.Background()) + defer cancel() + api, err := tp.makeAPI(ctx) + if err != nil { + t.Error(err) + } + + _, err = api.Block().Put(ctx, strings.NewReader(`Hello`)) + if err != nil { + t.Fatal(err) + } + + if pins, err := api.Pin().Ls(ctx); err != nil || len(pins) != 0 { + t.Fatal("expected 0 pins") + } + + res, err := api.Block().Put(ctx, strings.NewReader(`Hello`), opt.Block.Pin(true)) + if err != nil { + t.Fatal(err) + } + + pins, err := api.Pin().Ls(ctx) + if err != nil { + return + } + if len(pins) != 1 { + t.Fatal("expected 1 pin") + } + if pins[0].Type() != "recursive" { + t.Error("expected a recursive pin") + } + if pins[0].Path().String() != res.Path().String() { + t.Error("pin path didn't match") + } +}