@cryptotaxi247 / kubo / commits / 36249b929

core/commands: Added 'get' command

Matt Bell committed Jan 22, 2015 at 00:54 UTC 36249b9292633da22dc96f0d955ffa863b57dfff
2 files changed +179
core/commands/get.go new
+178
@@ -0,0 +1,178 @@
1 +package commands
2 +
3 +import (
4 + "archive/tar"
5 + "bytes"
6 + "io"
7 + p "path"
8 +
9 + cmds "github.com/jbenet/go-ipfs/commands"
10 + core "github.com/jbenet/go-ipfs/core"
11 + dag "github.com/jbenet/go-ipfs/merkledag"
12 + uio "github.com/jbenet/go-ipfs/unixfs/io"
13 + upb "github.com/jbenet/go-ipfs/unixfs/pb"
14 +
15 + proto "github.com/jbenet/go-ipfs/Godeps/_workspace/src/code.google.com/p/goprotobuf/proto"
16 +)
17 +
18 +var GetCmd = &cmds.Command{
19 + Helptext: cmds.HelpText{
20 + Tagline: "Download IPFS objects",
21 + ShortDescription: `
22 +Retrieves the object named by <ipfs-path> and stores the data to disk.
23 +
24 +By default, the output will be stored at ./<ipfs-path>, but an alternate path
25 +can be specified with '--output=<path>' or '-o=<path>'.
26 +
27 +To output a TAR archive instead of unpacked files, use '--archive' or '-a'.
28 +`,
29 + },
30 +
31 + Arguments: []cmds.Argument{
32 + cmds.StringArg("ipfs-path", true, true, "The path to the IPFS object(s) to be outputted").EnableStdin(),
33 + },
34 + Options: []cmds.Option{
35 + cmds.StringOption("output", "o", "The path where output should be stored"),
36 + cmds.BoolOption("archive", "a", "Output a TAR archive"),
37 + },
38 + Run: func(req cmds.Request, res cmds.Response) {
39 + node, err := req.Context().GetNode()
40 + if err != nil {
41 + res.SetError(err, cmds.ErrNormal)
42 + return
43 + }
44 +
45 + reader, err := get(node, req.Arguments())
46 + if err != nil {
47 + res.SetError(err, cmds.ErrNormal)
48 + return
49 + }
50 + res.SetOutput(reader)
51 + },
52 +
53 + // TODO: create a PostRun that splits the archive up into files
54 +}
55 +
56 +func get(node *core.IpfsNode, paths []string) (io.Reader, error) {
57 + reader := &getReader{signalChan: make(chan struct{})}
58 + writer := tar.NewWriter(&reader.buf)
59 +
60 + go func() {
61 + for _, path := range paths {
62 + _, err := copyFile(node, writer, path, nil, reader.signalChan)
63 + if err != nil {
64 + log.Error(err)
65 + return
66 + }
67 + }
68 +
69 + err := writer.Flush()
70 + if err != nil {
71 + log.Error(err)
72 + return
73 + }
74 +
75 + reader.Close()
76 + reader.Signal()
77 + }()
78 +
79 + return reader, nil
80 +}
81 +
82 +func copyFile(node *core.IpfsNode, writer *tar.Writer, path string, dagnode *dag.Node, signal chan struct{}) (int64, error) {
83 + var err error
84 + if dagnode == nil {
85 + dagnode, err = node.Resolver.ResolvePath(path)
86 + if err != nil {
87 + return 0, err
88 + }
89 + }
90 +
91 + pb := new(upb.Data)
92 + err = proto.Unmarshal(dagnode.Data, pb)
93 + if err != nil {
94 + return 0, err
95 + }
96 +
97 + written := int64(0)
98 + if pb.GetType() == upb.Data_Directory {
99 + err = writer.WriteHeader(&tar.Header{
100 + Name: path,
101 + Typeflag: tar.TypeDir,
102 + Mode: 0777,
103 + // TODO: set mode, dates, etc. when added to unixFS
104 + })
105 + if err != nil {
106 + return 0, err
107 + }
108 +
109 + for _, link := range dagnode.Links {
110 + n, err := copyFile(node, writer, p.Join(path, link.Name), link.Node, signal)
111 + if err != nil {
112 + return 0, err
113 + }
114 + written += n
115 + }
116 + return written, nil
117 +
118 + } else {
119 + err = writer.WriteHeader(&tar.Header{
120 + Name: path,
121 + Size: int64(pb.GetFilesize()),
122 + Typeflag: tar.TypeReg,
123 + Mode: 0644,
124 + // TODO: set mode, dates, etc. when added to unixFS
125 + })
126 + if err != nil {
127 + return 0, err
128 + }
129 +
130 + reader, err := uio.NewDagReader(dagnode, node.DAG)
131 + if err != nil {
132 + return 0, err
133 + }
134 +
135 + buf := make([]byte, 32*1024)
136 + for {
137 + nr, err := reader.Read(buf)
138 + if nr > 0 {
139 + nw, err := writer.Write(buf[:nr])
140 + if err != nil {
141 + return written, err
142 + }
143 + written += int64(nw)
144 + signal <- struct{}{}
145 + }
146 + if err == io.EOF {
147 + break
148 + }
149 + if err != nil {
150 + return written, err
151 + }
152 + }
153 + return written, nil
154 + }
155 +}
156 +
157 +type getReader struct {
158 + buf bytes.Buffer
159 + closed bool
160 + signalChan chan struct{}
161 +}
162 +
163 +func (i *getReader) Read(p []byte) (int, error) {
164 + <-i.signalChan
165 + n, err := i.buf.Read(p)
166 + if err == io.EOF && !i.closed {
167 + return n, nil
168 + }
169 + return n, err
170 +}
171 +
172 +func (i *getReader) Signal() {
173 + i.signalChan <- struct{}{}
174 +}
175 +
176 +func (i *getReader) Close() {
177 + i.closed = true
178 +}
core/commands/root.go
+1
@@ -82,6 +82,7 @@ var rootSubcommands = map[string]*cmds.Command{
82 "config": ConfigCmd,
83 "dht": DhtCmd,
84 "diag": DiagCmd,
85 + "get": GetCmd,
86 "id": IDCmd,
87 "log": LogCmd,
88 "ls": LsCmd,