mirror of
https://github.com/ipfs/kubo.git
synced 2025-05-17 23:16:11 +08:00

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).
240 lines
5.2 KiB
Go
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)
|
|
}
|