add a --with-local option to ipfs files stat
License: MIT Signed-off-by: Michael Muré <batolettre@gmail.com>
Michael Muré committed
Feb 1, 2018 at 11:48 UTC
69b8383e1c6539a0833e4d0a44bb098cc2453f1d
1 file changed
+88
-26
core/commands/files/files.go
+88
-26
@@ -10,10 +10,14 @@ import (
10
gopath "path"
11
"strings"
12
13
+ "gx/ipfs/QmPSBJL4momYnE7DcUyk2DVhD6rH488ZmHBGLbxNdhU44K/go-humanize"
14
+
15
+ bservice "github.com/ipfs/go-ipfs/blockservice"
16
oldcmds "github.com/ipfs/go-ipfs/commands"
17
lgc "github.com/ipfs/go-ipfs/commands/legacy"
18
core "github.com/ipfs/go-ipfs/core"
19
e "github.com/ipfs/go-ipfs/core/commands/e"
20
+ "github.com/ipfs/go-ipfs/exchange/offline"
21
dag "github.com/ipfs/go-ipfs/merkledag"
22
mfs "github.com/ipfs/go-ipfs/mfs"
23
path "github.com/ipfs/go-ipfs/path"
@@ -69,6 +73,17 @@ var hashOption = cmdkit.StringOption("hash", "Hash function to use. Will set Cid
73
74
var formatError = errors.New("Format was set by multiple options. Only one format option is allowed")
75
76
+type StatOutput struct {
77
+ Hash string
78
+ Size uint64
79
+ CumulativeSize uint64
80
+ Blocks int
81
+ Type string
82
+ WithLocality bool
83
+ Local bool `json:",omitempty"`
84
+ SizeLocal uint64 `json:",omitempty"`
85
+}
86
+
87
const defaultStatFormat = `<hash>
88
Size: <size>
89
CumulativeSize: <cumulsize>
@@ -88,6 +103,7 @@ var filesStatCmd = &cmds.Command{
103
"<hash> <size> <cumulsize> <type> <childs>. Conflicts with other format options.").WithDefault(defaultStatFormat),
104
cmdkit.BoolOption("hash", "Print only hash. Implies '--format=<hash>'. Conflicts with other format options."),
105
cmdkit.BoolOption("size", "Print only size. Implies '--format=<cumulsize>'. Conflicts with other format options."),
106
+ cmdkit.BoolOption("with-local", "Compute the amount of the dag that is local, and if possible the total size"),
107
},
108
Run: func(req *cmds.Request, res cmds.ResponseEmitter, env cmds.Environment) {
109
@@ -108,23 +124,41 @@ var filesStatCmd = &cmds.Command{
124
return
125
}
126
111
- fsn, err := mfs.Lookup(node.FilesRoot, path)
127
+ nd, err := getNodeFromPath(req.Context, node, path)
128
if err != nil {
129
res.SetError(err, cmdkit.ErrNormal)
130
return
131
}
132
117
- o, err := statNode(fsn)
133
+ o, err := statNode(nd)
134
if err != nil {
135
res.SetError(err, cmdkit.ErrNormal)
136
return
137
}
138
139
+ withLocal, _ := req.Options["with-local"].(bool)
140
+ if !withLocal {
141
+ cmds.EmitOnce(res, o)
142
+ return
143
+ }
144
+
145
+ // an offline DAGService will not fetch from the network
146
+ dagserv := dag.NewDAGService(bservice.New(
147
+ node.Blockstore,
148
+ offline.Exchange(node.Blockstore),
149
+ ))
150
+
151
+ local, sizeLocal, err := walkBlock(req.Context, dagserv, nd)
152
+
153
+ o.WithLocality = true
154
+ o.Local = local
155
+ o.SizeLocal = sizeLocal
156
+
157
cmds.EmitOnce(res, o)
158
},
159
Encoders: cmds.EncoderMap{
160
cmds.Text: cmds.MakeEncoder(func(req *cmds.Request, w io.Writer, v interface{}) error {
127
- out, ok := v.(*Object)
161
+ out, ok := v.(*StatOutput)
162
if !ok {
163
return e.TypeErr(out, v)
164
}
@@ -136,11 +170,20 @@ var filesStatCmd = &cmds.Command{
170
s = strings.Replace(s, "<childs>", fmt.Sprintf("%d", out.Blocks), -1)
171
s = strings.Replace(s, "<type>", out.Type, -1)
172
139
- _, err := fmt.Fprintln(w, s)
140
- return err
173
+ fmt.Fprintln(w, s)
174
+
175
+ if out.WithLocality {
176
+ fmt.Fprintf(w, "Local: %s of %s (%.2f%%)\n",
177
+ humanize.Bytes(out.SizeLocal),
178
+ humanize.Bytes(out.CumulativeSize),
179
+ 100.0*float64(out.SizeLocal)/float64(out.CumulativeSize),
180
+ )
181
+ }
182
+
183
+ return nil
184
}),
185
},
143
- Type: Object{},
186
+ Type: StatOutput{},
187
}
188
189
func moreThanOne(a, b, c bool) bool {
@@ -166,12 +209,7 @@ func statGetFormatOptions(req *cmds.Request) (string, error) {
209
}
210
}
211
169
-func statNode(fsn mfs.FSNode) (*Object, error) {
170
- nd, err := fsn.GetNode()
171
- if err != nil {
172
- return nil, err
173
- }
174
-
212
+func statNode(nd ipld.Node) (*StatOutput, error) {
213
c := nd.Cid()
214
215
cumulsize, err := nd.Size()
@@ -187,16 +225,16 @@ func statNode(fsn mfs.FSNode) (*Object, error) {
225
}
226
227
var ndtype string
190
- switch fsn.Type() {
191
- case mfs.TDir:
228
+ switch d.GetType() {
229
+ case ft.TDirectory, ft.THAMTShard:
230
ndtype = "directory"
193
- case mfs.TFile:
231
+ case ft.TFile, ft.TMetadata, ft.TRaw:
232
ndtype = "file"
233
default:
196
- return nil, fmt.Errorf("unrecognized node type: %s", fsn.Type())
234
+ return nil, fmt.Errorf("unrecognized node type: %s", d.GetType())
235
}
236
199
- return &Object{
237
+ return &StatOutput{
238
Hash: c.String(),
239
Blocks: len(nd.Links()),
240
Size: d.GetFilesize(),
@@ -204,7 +242,7 @@ func statNode(fsn mfs.FSNode) (*Object, error) {
242
Type: ndtype,
243
}, nil
244
case *dag.RawNode:
207
- return &Object{
245
+ return &StatOutput{
246
Hash: c.String(),
247
Blocks: 0,
248
Size: cumulsize,
@@ -216,6 +254,38 @@ func statNode(fsn mfs.FSNode) (*Object, error) {
254
}
255
}
256
257
+func walkBlock(ctx context.Context, dagserv ipld.DAGService, nd ipld.Node) (bool, uint64, error) {
258
+ // Start with the block data size
259
+ sizeLocal := uint64(len(nd.RawData()))
260
+
261
+ local := true
262
+
263
+ for _, link := range nd.Links() {
264
+ child, err := dagserv.Get(ctx, link.Cid)
265
+
266
+ if err == ipld.ErrNotFound {
267
+ local = false
268
+ continue
269
+ }
270
+
271
+ if err != nil {
272
+ return local, sizeLocal, err
273
+ }
274
+
275
+ childLocal, childLocalSize, err := walkBlock(ctx, dagserv, child)
276
+
277
+ if err != nil {
278
+ return local, sizeLocal, err
279
+ }
280
+
281
+ // Recursively add the child size
282
+ local = local && childLocal
283
+ sizeLocal += childLocalSize
284
+ }
285
+
286
+ return local, sizeLocal, nil
287
+}
288
+
289
var filesCpCmd = &oldcmds.Command{
290
Helptext: cmdkit.HelpText{
291
Tagline: "Copy files into mfs.",
@@ -298,14 +368,6 @@ func getNodeFromPath(ctx context.Context, node *core.IpfsNode, p string) (ipld.N
368
}
369
}
370
301
-type Object struct {
302
- Hash string
303
- Size uint64
304
- CumulativeSize uint64
305
- Blocks int
306
- Type string
307
-}
308
-
371
type filesLsOutput struct {
372
Entries []mfs.NodeListing
373
}