| 1 | package coreapi |
| 2 | |
| 3 | import ( |
| 4 | "bytes" |
| 5 | "context" |
| 6 | "errors" |
| 7 | "io" |
| 8 | |
| 9 | "github.com/ipfs/boxo/path" |
| 10 | pin "github.com/ipfs/boxo/pinning/pinner" |
| 11 | blocks "github.com/ipfs/go-block-format" |
| 12 | cid "github.com/ipfs/go-cid" |
| 13 | coreiface "github.com/ipfs/kubo/core/coreiface" |
| 14 | caopts "github.com/ipfs/kubo/core/coreiface/options" |
| 15 | "go.opentelemetry.io/otel/attribute" |
| 16 | "go.opentelemetry.io/otel/trace" |
| 17 | |
| 18 | util "github.com/ipfs/kubo/blocks/blockstoreutil" |
| 19 | "github.com/ipfs/kubo/tracing" |
| 20 | ) |
| 21 | |
| 22 | type BlockAPI CoreAPI |
| 23 | |
| 24 | type BlockStat struct { |
| 25 | path path.ImmutablePath |
| 26 | size int |
| 27 | } |
| 28 | |
| 29 | func (api *BlockAPI) Put(ctx context.Context, src io.Reader, opts ...caopts.BlockPutOption) (coreiface.BlockStat, error) { |
| 30 | ctx, span := tracing.Span(ctx, "CoreAPI.BlockAPI", "Put") |
| 31 | defer span.End() |
| 32 | |
| 33 | settings, err := caopts.BlockPutOptions(opts...) |
| 34 | if err != nil { |
| 35 | return nil, err |
| 36 | } |
| 37 | |
| 38 | data, err := io.ReadAll(src) |
| 39 | if err != nil { |
| 40 | return nil, err |
| 41 | } |
| 42 | |
| 43 | bcid, err := settings.CidPrefix.Sum(data) |
| 44 | if err != nil { |
| 45 | return nil, err |
| 46 | } |
| 47 | |
| 48 | b, err := blocks.NewBlockWithCid(data, bcid) |
| 49 | if err != nil { |
| 50 | return nil, err |
| 51 | } |
| 52 | |
| 53 | if settings.Pin { |
| 54 | defer api.blockstore.PinLock(ctx).Unlock(ctx) |
| 55 | } |
| 56 | |
| 57 | err = api.blocks.AddBlock(ctx, b) |
| 58 | if err != nil { |
| 59 | return nil, err |
| 60 | } |
| 61 | |
| 62 | if settings.Pin { |
| 63 | if err = api.pinning.PinWithMode(ctx, b.Cid(), pin.Recursive, ""); err != nil { |
| 64 | return nil, err |
| 65 | } |
| 66 | if err := api.pinning.Flush(ctx); err != nil { |
| 67 | return nil, err |
| 68 | } |
| 69 | } |
| 70 | |
| 71 | return &BlockStat{path: path.FromCid(b.Cid()), size: len(data)}, nil |
| 72 | } |
| 73 | |
| 74 | func (api *BlockAPI) Get(ctx context.Context, p path.Path) (io.Reader, error) { |
| 75 | ctx, span := tracing.Span(ctx, "CoreAPI.BlockAPI", "Get", trace.WithAttributes(attribute.String("path", p.String()))) |
| 76 | defer span.End() |
| 77 | rp, _, err := api.core().ResolvePath(ctx, p) |
| 78 | if err != nil { |
| 79 | return nil, err |
| 80 | } |
| 81 | |
| 82 | b, err := api.blocks.GetBlock(ctx, rp.RootCid()) |
| 83 | if err != nil { |
| 84 | return nil, err |
| 85 | } |
| 86 | |
| 87 | return bytes.NewReader(b.RawData()), nil |
| 88 | } |
| 89 | |
| 90 | func (api *BlockAPI) Rm(ctx context.Context, p path.Path, opts ...caopts.BlockRmOption) error { |
| 91 | ctx, span := tracing.Span(ctx, "CoreAPI.BlockAPI", "Rm", trace.WithAttributes(attribute.String("path", p.String()))) |
| 92 | defer span.End() |
| 93 | |
| 94 | rp, _, err := api.core().ResolvePath(ctx, p) |
| 95 | if err != nil { |
| 96 | return err |
| 97 | } |
| 98 | |
| 99 | settings, err := caopts.BlockRmOptions(opts...) |
| 100 | if err != nil { |
| 101 | return err |
| 102 | } |
| 103 | cids := []cid.Cid{rp.RootCid()} |
| 104 | o := util.RmBlocksOpts{Force: settings.Force} |
| 105 | |
| 106 | out, err := util.RmBlocks(ctx, api.blockstore, api.pinning, cids, o) |
| 107 | if err != nil { |
| 108 | return err |
| 109 | } |
| 110 | |
| 111 | select { |
| 112 | case res, ok := <-out: |
| 113 | if !ok { |
| 114 | return nil |
| 115 | } |
| 116 | |
| 117 | remBlock, ok := res.(*util.RemovedBlock) |
| 118 | if !ok { |
| 119 | return errors.New("got unexpected output from util.RmBlocks") |
| 120 | } |
| 121 | |
| 122 | if remBlock.Error != nil { |
| 123 | return remBlock.Error |
| 124 | } |
| 125 | return nil |
| 126 | case <-ctx.Done(): |
| 127 | return ctx.Err() |
| 128 | } |
| 129 | } |
| 130 | |
| 131 | func (api *BlockAPI) Stat(ctx context.Context, p path.Path) (coreiface.BlockStat, error) { |
| 132 | ctx, span := tracing.Span(ctx, "CoreAPI.BlockAPI", "Stat", trace.WithAttributes(attribute.String("path", p.String()))) |
| 133 | defer span.End() |
| 134 | |
| 135 | rp, _, err := api.core().ResolvePath(ctx, p) |
| 136 | if err != nil { |
| 137 | return nil, err |
| 138 | } |
| 139 | |
| 140 | b, err := api.blocks.GetBlock(ctx, rp.RootCid()) |
| 141 | if err != nil { |
| 142 | return nil, err |
| 143 | } |
| 144 | |
| 145 | return &BlockStat{ |
| 146 | path: path.FromCid(b.Cid()), |
| 147 | size: len(b.RawData()), |
| 148 | }, nil |
| 149 | } |
| 150 | |
| 151 | func (bs *BlockStat) Size() int { |
| 152 | return bs.size |
| 153 | } |
| 154 | |
| 155 | func (bs *BlockStat) Path() path.ImmutablePath { |
| 156 | return bs.path |
| 157 | } |
| 158 | |
| 159 | func (api *BlockAPI) core() coreiface.CoreAPI { |
| 160 | return (*CoreAPI)(api) |
| 161 | } |