1
0
mirror of https://github.com/ipfs/kubo.git synced 2025-06-29 01:12:24 +08:00

add a --with-local option to ipfs files stat

License: MIT
Signed-off-by: Michael Muré <batolettre@gmail.com>
This commit is contained in:
Michael Muré
2018-02-01 11:48:08 +01:00
parent d716a93fae
commit 69b8383e1c

View File

@ -10,10 +10,14 @@ import (
gopath "path" gopath "path"
"strings" "strings"
"gx/ipfs/QmPSBJL4momYnE7DcUyk2DVhD6rH488ZmHBGLbxNdhU44K/go-humanize"
bservice "github.com/ipfs/go-ipfs/blockservice"
oldcmds "github.com/ipfs/go-ipfs/commands" oldcmds "github.com/ipfs/go-ipfs/commands"
lgc "github.com/ipfs/go-ipfs/commands/legacy" lgc "github.com/ipfs/go-ipfs/commands/legacy"
core "github.com/ipfs/go-ipfs/core" core "github.com/ipfs/go-ipfs/core"
e "github.com/ipfs/go-ipfs/core/commands/e" e "github.com/ipfs/go-ipfs/core/commands/e"
"github.com/ipfs/go-ipfs/exchange/offline"
dag "github.com/ipfs/go-ipfs/merkledag" dag "github.com/ipfs/go-ipfs/merkledag"
mfs "github.com/ipfs/go-ipfs/mfs" mfs "github.com/ipfs/go-ipfs/mfs"
path "github.com/ipfs/go-ipfs/path" path "github.com/ipfs/go-ipfs/path"
@ -69,6 +73,17 @@ var hashOption = cmdkit.StringOption("hash", "Hash function to use. Will set Cid
var formatError = errors.New("Format was set by multiple options. Only one format option is allowed") var formatError = errors.New("Format was set by multiple options. Only one format option is allowed")
type StatOutput struct {
Hash string
Size uint64
CumulativeSize uint64
Blocks int
Type string
WithLocality bool
Local bool `json:",omitempty"`
SizeLocal uint64 `json:",omitempty"`
}
const defaultStatFormat = `<hash> const defaultStatFormat = `<hash>
Size: <size> Size: <size>
CumulativeSize: <cumulsize> CumulativeSize: <cumulsize>
@ -88,6 +103,7 @@ var filesStatCmd = &cmds.Command{
"<hash> <size> <cumulsize> <type> <childs>. Conflicts with other format options.").WithDefault(defaultStatFormat), "<hash> <size> <cumulsize> <type> <childs>. Conflicts with other format options.").WithDefault(defaultStatFormat),
cmdkit.BoolOption("hash", "Print only hash. Implies '--format=<hash>'. Conflicts with other format options."), cmdkit.BoolOption("hash", "Print only hash. Implies '--format=<hash>'. Conflicts with other format options."),
cmdkit.BoolOption("size", "Print only size. Implies '--format=<cumulsize>'. Conflicts with other format options."), cmdkit.BoolOption("size", "Print only size. Implies '--format=<cumulsize>'. Conflicts with other format options."),
cmdkit.BoolOption("with-local", "Compute the amount of the dag that is local, and if possible the total size"),
}, },
Run: func(req *cmds.Request, res cmds.ResponseEmitter, env cmds.Environment) { Run: func(req *cmds.Request, res cmds.ResponseEmitter, env cmds.Environment) {
@ -108,23 +124,41 @@ var filesStatCmd = &cmds.Command{
return return
} }
fsn, err := mfs.Lookup(node.FilesRoot, path) nd, err := getNodeFromPath(req.Context, node, path)
if err != nil { if err != nil {
res.SetError(err, cmdkit.ErrNormal) res.SetError(err, cmdkit.ErrNormal)
return return
} }
o, err := statNode(fsn) o, err := statNode(nd)
if err != nil { if err != nil {
res.SetError(err, cmdkit.ErrNormal) res.SetError(err, cmdkit.ErrNormal)
return return
} }
withLocal, _ := req.Options["with-local"].(bool)
if !withLocal {
cmds.EmitOnce(res, o)
return
}
// an offline DAGService will not fetch from the network
dagserv := dag.NewDAGService(bservice.New(
node.Blockstore,
offline.Exchange(node.Blockstore),
))
local, sizeLocal, err := walkBlock(req.Context, dagserv, nd)
o.WithLocality = true
o.Local = local
o.SizeLocal = sizeLocal
cmds.EmitOnce(res, o) cmds.EmitOnce(res, o)
}, },
Encoders: cmds.EncoderMap{ Encoders: cmds.EncoderMap{
cmds.Text: cmds.MakeEncoder(func(req *cmds.Request, w io.Writer, v interface{}) error { cmds.Text: cmds.MakeEncoder(func(req *cmds.Request, w io.Writer, v interface{}) error {
out, ok := v.(*Object) out, ok := v.(*StatOutput)
if !ok { if !ok {
return e.TypeErr(out, v) return e.TypeErr(out, v)
} }
@ -136,11 +170,20 @@ var filesStatCmd = &cmds.Command{
s = strings.Replace(s, "<childs>", fmt.Sprintf("%d", out.Blocks), -1) s = strings.Replace(s, "<childs>", fmt.Sprintf("%d", out.Blocks), -1)
s = strings.Replace(s, "<type>", out.Type, -1) s = strings.Replace(s, "<type>", out.Type, -1)
_, err := fmt.Fprintln(w, s) fmt.Fprintln(w, s)
return err
if out.WithLocality {
fmt.Fprintf(w, "Local: %s of %s (%.2f%%)\n",
humanize.Bytes(out.SizeLocal),
humanize.Bytes(out.CumulativeSize),
100.0*float64(out.SizeLocal)/float64(out.CumulativeSize),
)
}
return nil
}), }),
}, },
Type: Object{}, Type: StatOutput{},
} }
func moreThanOne(a, b, c bool) bool { func moreThanOne(a, b, c bool) bool {
@ -166,12 +209,7 @@ func statGetFormatOptions(req *cmds.Request) (string, error) {
} }
} }
func statNode(fsn mfs.FSNode) (*Object, error) { func statNode(nd ipld.Node) (*StatOutput, error) {
nd, err := fsn.GetNode()
if err != nil {
return nil, err
}
c := nd.Cid() c := nd.Cid()
cumulsize, err := nd.Size() cumulsize, err := nd.Size()
@ -187,16 +225,16 @@ func statNode(fsn mfs.FSNode) (*Object, error) {
} }
var ndtype string var ndtype string
switch fsn.Type() { switch d.GetType() {
case mfs.TDir: case ft.TDirectory, ft.THAMTShard:
ndtype = "directory" ndtype = "directory"
case mfs.TFile: case ft.TFile, ft.TMetadata, ft.TRaw:
ndtype = "file" ndtype = "file"
default: default:
return nil, fmt.Errorf("unrecognized node type: %s", fsn.Type()) return nil, fmt.Errorf("unrecognized node type: %s", d.GetType())
} }
return &Object{ return &StatOutput{
Hash: c.String(), Hash: c.String(),
Blocks: len(nd.Links()), Blocks: len(nd.Links()),
Size: d.GetFilesize(), Size: d.GetFilesize(),
@ -204,7 +242,7 @@ func statNode(fsn mfs.FSNode) (*Object, error) {
Type: ndtype, Type: ndtype,
}, nil }, nil
case *dag.RawNode: case *dag.RawNode:
return &Object{ return &StatOutput{
Hash: c.String(), Hash: c.String(),
Blocks: 0, Blocks: 0,
Size: cumulsize, Size: cumulsize,
@ -216,6 +254,38 @@ func statNode(fsn mfs.FSNode) (*Object, error) {
} }
} }
func walkBlock(ctx context.Context, dagserv ipld.DAGService, nd ipld.Node) (bool, uint64, error) {
// Start with the block data size
sizeLocal := uint64(len(nd.RawData()))
local := true
for _, link := range nd.Links() {
child, err := dagserv.Get(ctx, link.Cid)
if err == ipld.ErrNotFound {
local = false
continue
}
if err != nil {
return local, sizeLocal, err
}
childLocal, childLocalSize, err := walkBlock(ctx, dagserv, child)
if err != nil {
return local, sizeLocal, err
}
// Recursively add the child size
local = local && childLocal
sizeLocal += childLocalSize
}
return local, sizeLocal, nil
}
var filesCpCmd = &oldcmds.Command{ var filesCpCmd = &oldcmds.Command{
Helptext: cmdkit.HelpText{ Helptext: cmdkit.HelpText{
Tagline: "Copy files into mfs.", Tagline: "Copy files into mfs.",
@ -298,14 +368,6 @@ func getNodeFromPath(ctx context.Context, node *core.IpfsNode, p string) (ipld.N
} }
} }
type Object struct {
Hash string
Size uint64
CumulativeSize uint64
Blocks int
Type string
}
type filesLsOutput struct { type filesLsOutput struct {
Entries []mfs.NodeListing Entries []mfs.NodeListing
} }