github.com/keltia/go-ipfs@v0.3.8-0.20150909044612-210793031c63/unixfs/archive/tar/writer.go (about)

     1  package tar
     2  
     3  import (
     4  	"archive/tar"
     5  	"io"
     6  	"path"
     7  	"time"
     8  
     9  	proto "github.com/ipfs/go-ipfs/Godeps/_workspace/src/github.com/gogo/protobuf/proto"
    10  	cxt "github.com/ipfs/go-ipfs/Godeps/_workspace/src/golang.org/x/net/context"
    11  
    12  	mdag "github.com/ipfs/go-ipfs/merkledag"
    13  	ft "github.com/ipfs/go-ipfs/unixfs"
    14  	uio "github.com/ipfs/go-ipfs/unixfs/io"
    15  	upb "github.com/ipfs/go-ipfs/unixfs/pb"
    16  )
    17  
    18  // Writer is a utility structure that helps to write
    19  // unixfs merkledag nodes as a tar archive format.
    20  // It wraps any io.Writer.
    21  type Writer struct {
    22  	Dag  mdag.DAGService
    23  	TarW *tar.Writer
    24  
    25  	ctx cxt.Context
    26  }
    27  
    28  // NewWriter wraps given io.Writer.
    29  func NewWriter(ctx cxt.Context, dag mdag.DAGService, archive bool, compression int, w io.Writer) (*Writer, error) {
    30  	return &Writer{
    31  		Dag:  dag,
    32  		TarW: tar.NewWriter(w),
    33  		ctx:  ctx,
    34  	}, nil
    35  }
    36  
    37  func (w *Writer) writeDir(nd *mdag.Node, fpath string) error {
    38  	if err := writeDirHeader(w.TarW, fpath); err != nil {
    39  		return err
    40  	}
    41  
    42  	for i, ng := range w.Dag.GetDAG(w.ctx, nd) {
    43  		child, err := ng.Get(w.ctx)
    44  		if err != nil {
    45  			return err
    46  		}
    47  
    48  		npath := path.Join(fpath, nd.Links[i].Name)
    49  		if err := w.WriteNode(child, npath); err != nil {
    50  			return err
    51  		}
    52  	}
    53  
    54  	return nil
    55  }
    56  
    57  func (w *Writer) writeFile(nd *mdag.Node, pb *upb.Data, fpath string) error {
    58  	if err := writeFileHeader(w.TarW, fpath, pb.GetFilesize()); err != nil {
    59  		return err
    60  	}
    61  
    62  	dagr := uio.NewDataFileReader(w.ctx, nd, pb, w.Dag)
    63  	_, err := dagr.WriteTo(w.TarW)
    64  	return err
    65  }
    66  
    67  func (w *Writer) WriteNode(nd *mdag.Node, fpath string) error {
    68  	pb := new(upb.Data)
    69  	if err := proto.Unmarshal(nd.Data, pb); err != nil {
    70  		return err
    71  	}
    72  
    73  	switch pb.GetType() {
    74  	case upb.Data_Metadata:
    75  		fallthrough
    76  	case upb.Data_Directory:
    77  		return w.writeDir(nd, fpath)
    78  	case upb.Data_Raw:
    79  		fallthrough
    80  	case upb.Data_File:
    81  		return w.writeFile(nd, pb, fpath)
    82  	case upb.Data_Symlink:
    83  		return writeSymlinkHeader(w.TarW, string(pb.GetData()), fpath)
    84  	default:
    85  		return ft.ErrUnrecognizedType
    86  	}
    87  }
    88  
    89  func (w *Writer) Close() error {
    90  	return w.TarW.Close()
    91  }
    92  
    93  func writeDirHeader(w *tar.Writer, fpath string) error {
    94  	return w.WriteHeader(&tar.Header{
    95  		Name:     fpath,
    96  		Typeflag: tar.TypeDir,
    97  		Mode:     0777,
    98  		ModTime:  time.Now(),
    99  		// TODO: set mode, dates, etc. when added to unixFS
   100  	})
   101  }
   102  
   103  func writeFileHeader(w *tar.Writer, fpath string, size uint64) error {
   104  	return w.WriteHeader(&tar.Header{
   105  		Name:     fpath,
   106  		Size:     int64(size),
   107  		Typeflag: tar.TypeReg,
   108  		Mode:     0644,
   109  		ModTime:  time.Now(),
   110  		// TODO: set mode, dates, etc. when added to unixFS
   111  	})
   112  }
   113  
   114  func writeSymlinkHeader(w *tar.Writer, target, fpath string) error {
   115  	return w.WriteHeader(&tar.Header{
   116  		Name:     fpath,
   117  		Linkname: target,
   118  		Mode:     0777,
   119  		Typeflag: tar.TypeSymlink,
   120  	})
   121  }