diff --git a/cmd/khepri/cmd_fsck.go b/cmd/khepri/cmd_fsck.go index 63ea29680..bc71a37de 100644 --- a/cmd/khepri/cmd_fsck.go +++ b/cmd/khepri/cmd_fsck.go @@ -1,76 +1,123 @@ package main -import "github.com/fd0/khepri/backend" +import ( + "errors" + "fmt" -// func fsck_tree(be backend.Server, id backend.ID) (bool, error) { -// log.Printf(" checking dir %s", id) + "github.com/fd0/khepri" + "github.com/fd0/khepri/backend" +) -// buf, err := be.GetBlob(id) -// if err != nil { -// return false, err -// } +func init() { + commands["fsck"] = commandFsck +} -// tree := &khepri.Tree{} -// err = json.Unmarshal(buf, tree) -// if err != nil { -// return false, err -// } +func fsckFile(ch *khepri.ContentHandler, IDs []backend.ID) error { + for _, id := range IDs { + debug("checking data blob %v\n", id) -// if !id.Equal(backend.IDFromData(buf)) { -// return false, nil -// } - -// return true, nil -// } - -// func fsck_snapshot(be backend.Server, id backend.ID) (bool, error) { -// log.Printf("checking snapshot %s", id) - -// sn, err := khepri.LoadSnapshot(be, id) -// if err != nil { -// return false, err -// } - -// return fsck_tree(be, sn.Content) -// } - -func commandFsck(be backend.Server, args []string) error { - // var snapshots backend.IDs - // var err error - - // if len(args) != 0 { - // snapshots = make(backend.IDs, 0, len(args)) - - // for _, arg := range args { - // id, err := backend.ParseID(arg) - // if err != nil { - // log.Fatal(err) - // } - - // snapshots = append(snapshots, id) - // } - // } else { - // snapshots, err = be.ListRefs() - - // if err != nil { - // log.Fatalf("error reading list of snapshot IDs: %v", err) - // } - // } - - // log.Printf("checking %d snapshots", len(snapshots)) - - // for _, id := range snapshots { - // ok, err := fsck_snapshot(be, id) - - // if err != nil { - // log.Printf("error checking snapshot %s: %v", id, err) - // continue - // } - - // if !ok { - // log.Printf("snapshot %s failed", id) - // } - // } + // load content + _, err := ch.Load(backend.Data, id) + if err != nil { + return err + } + } + + return nil +} + +func fsckTree(ch *khepri.ContentHandler, id backend.ID) error { + debug("checking tree %v\n", id) + + tree, err := khepri.LoadTree(ch, id) + if err != nil { + return err + } + + for i, node := range tree { + if node.Name == "" { + return fmt.Errorf("node %v of tree %v has no name", i, id) + } + + if node.Type == "" { + return fmt.Errorf("node %q of tree %v has no type", node.Name, id) + } + + switch node.Type { + case "file": + if node.Content == nil { + return fmt.Errorf("file node %q of tree %v has no content", node.Name, id) + } + + err := fsckFile(ch, node.Content) + if err != nil { + return err + } + case "dir": + if node.Subtree == nil { + return fmt.Errorf("dir node %q of tree %v has no subtree", node.Name, id) + } + + err := fsckTree(ch, node.Subtree) + if err != nil { + return err + } + } + } + + return nil +} + +func fsck_snapshot(be backend.Server, key *khepri.Key, id backend.ID) error { + debug("checking snapshot %v\n", id) + + ch, err := khepri.NewContentHandler(be, key) + if err != nil { + return err + } + + sn, err := ch.LoadSnapshot(id) + if err != nil { + return err + } + + if sn.Content == nil { + return fmt.Errorf("snapshot %v has no content", sn.ID) + } + + if sn.Map == nil { + return fmt.Errorf("snapshot %v has no map", sn.ID) + } + + return fsckTree(ch, sn.Content) +} + +func commandFsck(be backend.Server, key *khepri.Key, args []string) error { + if len(args) == 0 { + return errors.New("usage: fsck [all|snapshot-id]") + } + + if len(args) == 1 && args[0] != "all" { + snapshotID, err := backend.FindSnapshot(be, args[0]) + if err != nil { + return fmt.Errorf("invalid id %q: %v", args[1], err) + } + + return fsck_snapshot(be, key, snapshotID) + } + + list, err := be.List(backend.Snapshot) + if err != nil { + return err + } + + for _, snapshotID := range list { + err := fsck_snapshot(be, key, snapshotID) + + if err != nil { + return err + } + } return nil }