1
0
mirror of https://github.com/ipfs/kubo.git synced 2025-05-17 23:16:11 +08:00
Files
kubo/core/coreapi/pin.go
Steven Allen 9738d81c0c switch to new merkledag walk functions
EnumerateChildrenAsync has been renamed to WalkParallel to reflect the fact
that:

1. It visits the root.
2. It's parallel, not async.

To mirror this change, EnumerateChildren has also been renamed to Walk and now
behaves the same (except that it's not parallel).
2019-07-16 16:34:21 -07:00

240 lines
5.2 KiB
Go

package coreapi
import (
"context"
"fmt"
bserv "github.com/ipfs/go-blockservice"
cid "github.com/ipfs/go-cid"
offline "github.com/ipfs/go-ipfs-exchange-offline"
merkledag "github.com/ipfs/go-merkledag"
coreiface "github.com/ipfs/interface-go-ipfs-core"
caopts "github.com/ipfs/interface-go-ipfs-core/options"
path "github.com/ipfs/interface-go-ipfs-core/path"
)
type PinAPI CoreAPI
func (api *PinAPI) Add(ctx context.Context, p path.Path, opts ...caopts.PinAddOption) error {
dagNode, err := api.core().ResolveNode(ctx, p)
if err != nil {
return fmt.Errorf("pin: %s", err)
}
settings, err := caopts.PinAddOptions(opts...)
if err != nil {
return err
}
defer api.blockstore.PinLock().Unlock()
err = api.pinning.Pin(ctx, dagNode, settings.Recursive)
if err != nil {
return fmt.Errorf("pin: %s", err)
}
if err := api.provider.Provide(dagNode.Cid()); err != nil {
return err
}
return api.pinning.Flush()
}
func (api *PinAPI) Ls(ctx context.Context, opts ...caopts.PinLsOption) ([]coreiface.Pin, error) {
settings, err := caopts.PinLsOptions(opts...)
if err != nil {
return nil, err
}
switch settings.Type {
case "all", "direct", "indirect", "recursive":
default:
return nil, fmt.Errorf("invalid type '%s', must be one of {direct, indirect, recursive, all}", settings.Type)
}
return api.pinLsAll(settings.Type, ctx)
}
// Rm pin rm api
func (api *PinAPI) Rm(ctx context.Context, p path.Path, opts ...caopts.PinRmOption) error {
rp, err := api.core().ResolvePath(ctx, p)
if err != nil {
return err
}
settings, err := caopts.PinRmOptions(opts...)
if err != nil {
return err
}
// Note: after unpin the pin sets are flushed to the blockstore, so we need
// to take a lock to prevent a concurrent garbage collection
defer api.blockstore.PinLock().Unlock()
if err = api.pinning.Unpin(ctx, rp.Cid(), settings.Recursive); err != nil {
return err
}
return api.pinning.Flush()
}
func (api *PinAPI) Update(ctx context.Context, from path.Path, to path.Path, opts ...caopts.PinUpdateOption) error {
settings, err := caopts.PinUpdateOptions(opts...)
if err != nil {
return err
}
fp, err := api.core().ResolvePath(ctx, from)
if err != nil {
return err
}
tp, err := api.core().ResolvePath(ctx, to)
if err != nil {
return err
}
defer api.blockstore.PinLock().Unlock()
err = api.pinning.Update(ctx, fp.Cid(), tp.Cid(), settings.Unpin)
if err != nil {
return err
}
return api.pinning.Flush()
}
type pinStatus struct {
cid cid.Cid
ok bool
badNodes []coreiface.BadPinNode
}
// BadNode is used in PinVerifyRes
type badNode struct {
path path.Resolved
err error
}
func (s *pinStatus) Ok() bool {
return s.ok
}
func (s *pinStatus) BadNodes() []coreiface.BadPinNode {
return s.badNodes
}
func (n *badNode) Path() path.Resolved {
return n.path
}
func (n *badNode) Err() error {
return n.err
}
func (api *PinAPI) Verify(ctx context.Context) (<-chan coreiface.PinStatus, error) {
visited := make(map[cid.Cid]*pinStatus)
bs := api.blockstore
DAG := merkledag.NewDAGService(bserv.New(bs, offline.Exchange(bs)))
getLinks := merkledag.GetLinksWithDAG(DAG)
recPins := api.pinning.RecursiveKeys()
var checkPin func(root cid.Cid) *pinStatus
checkPin = func(root cid.Cid) *pinStatus {
if status, ok := visited[root]; ok {
return status
}
links, err := getLinks(ctx, root)
if err != nil {
status := &pinStatus{ok: false, cid: root}
status.badNodes = []coreiface.BadPinNode{&badNode{path: path.IpldPath(root), err: err}}
visited[root] = status
return status
}
status := &pinStatus{ok: true, cid: root}
for _, lnk := range links {
res := checkPin(lnk.Cid)
if !res.ok {
status.ok = false
status.badNodes = append(status.badNodes, res.badNodes...)
}
}
visited[root] = status
return status
}
out := make(chan coreiface.PinStatus)
go func() {
defer close(out)
for _, c := range recPins {
out <- checkPin(c)
}
}()
return out, nil
}
type pinInfo struct {
pinType string
path path.Resolved
}
func (p *pinInfo) Path() path.Resolved {
return p.path
}
func (p *pinInfo) Type() string {
return p.pinType
}
func (api *PinAPI) pinLsAll(typeStr string, ctx context.Context) ([]coreiface.Pin, error) {
keys := make(map[cid.Cid]*pinInfo)
AddToResultKeys := func(keyList []cid.Cid, typeStr string) {
for _, c := range keyList {
keys[c] = &pinInfo{
pinType: typeStr,
path: path.IpldPath(c),
}
}
}
if typeStr == "direct" || typeStr == "all" {
AddToResultKeys(api.pinning.DirectKeys(), "direct")
}
if typeStr == "indirect" || typeStr == "all" {
set := cid.NewSet()
for _, k := range api.pinning.RecursiveKeys() {
err := merkledag.WalkDepth(
ctx, merkledag.GetLinksWithDAG(api.dag), k, 0,
func(c cid.Cid, depth int) bool {
// don't visit the root node, that doesn't count.
return depth == 0 || set.Visit(c)
},
)
if err != nil {
return nil, err
}
}
AddToResultKeys(set.Keys(), "indirect")
}
if typeStr == "recursive" || typeStr == "all" {
AddToResultKeys(api.pinning.RecursiveKeys(), "recursive")
}
out := make([]coreiface.Pin, 0, len(keys))
for _, v := range keys {
out = append(out, v)
}
return out, nil
}
func (api *PinAPI) core() coreiface.CoreAPI {
return (*CoreAPI)(api)
}