@cryptotaxi247 / kubo / commits / 325e1c083

"pin verify": basic implementation

License: MIT Signed-off-by: Kevin Atkinson <k@kevina.org>

Kevin Atkinson committed Mar 29, 2017 at 17:02 UTC 325e1c083943b94f2a4ef2d77af00ae41a43f3a2
1 file changed +107
core/commands/pin.go
+107
@@ -27,6 +27,7 @@ var PinCmd = &cmds.Command{
27 "add": addPinCmd,
28 "rm": rmPinCmd,
29 "ls": listPinCmd,
30 + "verify": verifyPinCmd,
31 "update": updatePinCmd,
32 },
33 }
@@ -410,6 +411,27 @@ new pin and removing the old one.
411 },
412 }
413
414 +var verifyPinCmd = &cmds.Command{
415 + Helptext: cmds.HelpText{
416 + Tagline: "Verify that recursive pins are complete.",
417 + },
418 + Run: func(req cmds.Request, res cmds.Response) {
419 + n, _ := req.InvocContext().GetNode()
420 +
421 + rdr, wtr := io.Pipe()
422 + out := pinVerify(req.Context(), n)
423 +
424 + go func() {
425 + defer wtr.Close()
426 + for r := range out {
427 + r.Format(wtr)
428 + }
429 + }()
430 +
431 + res.SetOutput(rdr)
432 + },
433 +}
434 +
435 type RefKeyObject struct {
436 Type string
437 }
@@ -492,6 +514,91 @@ func pinLsAll(typeStr string, ctx context.Context, n *core.IpfsNode) (map[string
514 return keys, nil
515 }
516
517 +type pinStatus struct {
518 + // use pointer to array slice to reduce memory usage
519 + // the value should not be nil unless uninitialized
520 + badNodes *[]badNode
521 +}
522 +
523 +type badNode struct {
524 + cid *cid.Cid
525 + err error
526 +}
527 +
528 +type pinVerifyRes struct {
529 + cid *cid.Cid
530 + pinStatus
531 +}
532 +
533 +func pinVerify(ctx context.Context, n *core.IpfsNode) <-chan pinVerifyRes {
534 + visited := make(map[string]pinStatus)
535 + getLinks := n.DAG.GetOfflineLinkService().GetLinks
536 + emptySlice := &[]badNode{}
537 + recPins := n.Pinning.RecursiveKeys()
538 +
539 + var checkPin func(root *cid.Cid) pinStatus
540 + checkPin = func(root *cid.Cid) pinStatus {
541 + key := root.String()
542 + if status, ok := visited[key]; ok {
543 + if status.badNodes == nil {
544 + return pinStatus{&[]badNode{badNode{
545 + cid: root,
546 + err: fmt.Errorf("Cycle Detected.")}}}
547 + }
548 + return status
549 + }
550 +
551 + links, err := getLinks(ctx, root)
552 + if err != nil {
553 + status := pinStatus{&[]badNode{badNode{cid: root, err: err}}}
554 + visited[key] = status
555 + return status
556 + }
557 +
558 + status := pinStatus{}
559 + visited[key] = status // paranoid mode cycle detection
560 + for _, lnk := range links {
561 + res := checkPin(lnk.Cid)
562 + if len(*res.badNodes) > 0 {
563 + if status.badNodes == nil {
564 + status.badNodes = res.badNodes
565 + } else {
566 + slice := append(*status.badNodes, *res.badNodes...)
567 + status.badNodes = &slice
568 + }
569 + }
570 + }
571 + if status.badNodes == nil {
572 + status.badNodes = emptySlice // prevent special cases
573 + }
574 +
575 + visited[key] = status
576 + return status
577 + }
578 +
579 + out := make(chan pinVerifyRes)
580 + go func() {
581 + defer close(out)
582 + for _, cid := range recPins {
583 + pinStatus := checkPin(cid)
584 + out <- pinVerifyRes{cid, pinStatus}
585 + }
586 + }()
587 +
588 + return out
589 +}
590 +
591 +func (r pinVerifyRes) Format(out io.Writer) {
592 + if len(*r.badNodes) == 0 {
593 + fmt.Fprintf(out, "%s ok\n", r.cid)
594 + } else {
595 + fmt.Fprintf(out, "%s broken\n", r.cid)
596 + for _, e := range *r.badNodes {
597 + fmt.Fprintf(out, " %s: %s\n", e.cid, e.err)
598 + }
599 + }
600 +}
601 +
602 func cidsToStrings(cs []*cid.Cid) []string {
603 out := make([]string, 0, len(cs))
604 for _, c := range cs {