From 37981787054fadf87615557f892dbd28a139ca79 Mon Sep 17 00:00:00 2001
From: rht <rhtbot@gmail.com>
Date: Thu, 11 Jun 2015 20:44:08 +0700
Subject: [PATCH] Don't use tar reader for '-C' only flag

Currently `ipfs get -C <hash>` returns error even if <hash> is a file.
This PR is for the case when the compress flag is enabled, use the
dagreader directly and pipe to a gzip processor.

License: MIT
Signed-off-by: rht <rhtbot@gmail.com>
---
 core/commands/get.go | 68 ++++++++++++++++++++++++++++++++------------
 1 file changed, 50 insertions(+), 18 deletions(-)

diff --git a/core/commands/get.go b/core/commands/get.go
index 4c573928a..d8108a3f3 100644
--- a/core/commands/get.go
+++ b/core/commands/get.go
@@ -1,6 +1,7 @@
 package commands
 
 import (
+	"bufio"
 	"compress/gzip"
 	"errors"
 	"fmt"
@@ -16,6 +17,7 @@ import (
 	core "github.com/ipfs/go-ipfs/core"
 	path "github.com/ipfs/go-ipfs/path"
 	tar "github.com/ipfs/go-ipfs/thirdparty/tar"
+	uio "github.com/ipfs/go-ipfs/unixfs/io"
 	utar "github.com/ipfs/go-ipfs/unixfs/tar"
 )
 
@@ -63,7 +65,20 @@ may also specify the level of compression by specifying '-l=<1-9>'.
 			return
 		}
 
-		reader, err := get(req.Context().Context, node, req.Arguments()[0], cmplvl)
+		// Validate path string
+		p, err := path.ParsePath(req.Arguments()[0])
+		if err != nil {
+			res.SetError(fmt.Errorf("failed to validate path: %v", err), cmds.ErrNormal)
+			return
+		}
+		var reader io.Reader
+
+		if archive, _, _ := req.Option("archive").Bool(); !archive && cmplvl != gzip.NoCompression {
+			// only use this when the flag is '-C' without '-a'
+			reader, err = getZip(req.Context().Context, node, p, cmplvl)
+		} else {
+			reader, err = get(req.Context().Context, node, p, cmplvl)
+		}
 		if err != nil {
 			res.SetError(err, cmds.ErrNormal)
 			return
@@ -89,8 +104,8 @@ may also specify the level of compression by specifying '-l=<1-9>'.
 			return
 		}
 
-		if archive, _, _ := req.Option("archive").Bool(); archive {
-			if !strings.HasSuffix(outPath, ".tar") {
+		if archive, _, _ := req.Option("archive").Bool(); archive || cmplvl != gzip.NoCompression {
+			if archive && !strings.HasSuffix(outPath, ".tar") {
 				outPath += ".tar"
 			}
 			if cmplvl != gzip.NoCompression {
@@ -127,19 +142,7 @@ may also specify the level of compression by specifying '-l=<1-9>'.
 		bar.Output = os.Stderr
 
 		// wrap the reader with the progress bar proxy reader
-		// if the output is compressed, also wrap it in a gzip.Reader
-		var reader io.Reader
-		if cmplvl != gzip.NoCompression {
-			gzipReader, err := gzip.NewReader(outReader)
-			if err != nil {
-				res.SetError(err, cmds.ErrNormal)
-				return
-			}
-			defer gzipReader.Close()
-			reader = bar.NewProxyReader(gzipReader)
-		} else {
-			reader = bar.NewProxyReader(outReader)
-		}
+		reader := bar.NewProxyReader(outReader)
 
 		bar.Start()
 		defer bar.Finish()
@@ -166,8 +169,7 @@ func getCompressOptions(req cmds.Request) (int, error) {
 	return gzip.NoCompression, nil
 }
 
-func get(ctx context.Context, node *core.IpfsNode, p string, compression int) (io.Reader, error) {
-	pathToResolve := path.Path(p)
+func get(ctx context.Context, node *core.IpfsNode, pathToResolve path.Path, compression int) (io.Reader, error) {
 	dagnode, err := core.Resolve(ctx, node, pathToResolve)
 	if err != nil {
 		return nil, err
@@ -175,3 +177,33 @@ func get(ctx context.Context, node *core.IpfsNode, p string, compression int) (i
 
 	return utar.NewReader(pathToResolve, node.DAG, dagnode, compression)
 }
+
+// getZip is equivalent to `ipfs getdag $hash | gzip`
+func getZip(ctx context.Context, node *core.IpfsNode, pathToResolve path.Path, compression int) (io.Reader, error) {
+	dagnode, err := core.Resolve(ctx, node, pathToResolve)
+	if err != nil {
+		return nil, err
+	}
+
+	reader, err := uio.NewDagReader(ctx, dagnode, node.DAG)
+	if err != nil {
+		return nil, err
+	}
+
+	pr, pw := io.Pipe()
+	gw, err := gzip.NewWriterLevel(pw, compression)
+	if err != nil {
+		return nil, err
+	}
+	bufin := bufio.NewReader(reader)
+	go func() {
+		_, err := bufin.WriteTo(gw)
+		if err != nil {
+			log.Error("Fail to compress the stream")
+		}
+		gw.Close()
+		pw.Close()
+	}()
+
+	return pr, nil
+}
-- 
GitLab