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

     1  // Package format implements a data format for files in the ipfs filesystem
     2  // It is not the only format in ipfs, but it is the one that the filesystem assumes
     3  package unixfs
     4  
     5  import (
     6  	"errors"
     7  
     8  	proto "github.com/ipfs/go-ipfs/Godeps/_workspace/src/github.com/gogo/protobuf/proto"
     9  	pb "github.com/ipfs/go-ipfs/unixfs/pb"
    10  )
    11  
    12  const (
    13  	TRaw       = pb.Data_Raw
    14  	TFile      = pb.Data_File
    15  	TDirectory = pb.Data_Directory
    16  	TMetadata  = pb.Data_Metadata
    17  )
    18  
    19  var ErrMalformedFileFormat = errors.New("malformed data in file format")
    20  var ErrInvalidDirLocation = errors.New("found directory node in unexpected place")
    21  var ErrUnrecognizedType = errors.New("unrecognized node type")
    22  
    23  func FromBytes(data []byte) (*pb.Data, error) {
    24  	pbdata := new(pb.Data)
    25  	err := proto.Unmarshal(data, pbdata)
    26  	if err != nil {
    27  		return nil, err
    28  	}
    29  	return pbdata, nil
    30  }
    31  
    32  func FilePBData(data []byte, totalsize uint64) []byte {
    33  	pbfile := new(pb.Data)
    34  	typ := pb.Data_File
    35  	pbfile.Type = &typ
    36  	pbfile.Data = data
    37  	pbfile.Filesize = proto.Uint64(totalsize)
    38  
    39  	data, err := proto.Marshal(pbfile)
    40  	if err != nil {
    41  		// This really shouldnt happen, i promise
    42  		// The only failure case for marshal is if required fields
    43  		// are not filled out, and they all are. If the proto object
    44  		// gets changed and nobody updates this function, the code
    45  		// should panic due to programmer error
    46  		panic(err)
    47  	}
    48  	return data
    49  }
    50  
    51  // Returns Bytes that represent a Directory
    52  func FolderPBData() []byte {
    53  	pbfile := new(pb.Data)
    54  	typ := pb.Data_Directory
    55  	pbfile.Type = &typ
    56  
    57  	data, err := proto.Marshal(pbfile)
    58  	if err != nil {
    59  		//this really shouldnt happen, i promise
    60  		panic(err)
    61  	}
    62  	return data
    63  }
    64  
    65  func WrapData(b []byte) []byte {
    66  	pbdata := new(pb.Data)
    67  	typ := pb.Data_Raw
    68  	pbdata.Data = b
    69  	pbdata.Type = &typ
    70  
    71  	out, err := proto.Marshal(pbdata)
    72  	if err != nil {
    73  		// This shouldnt happen. seriously.
    74  		panic(err)
    75  	}
    76  
    77  	return out
    78  }
    79  
    80  func SymlinkData(path string) ([]byte, error) {
    81  	pbdata := new(pb.Data)
    82  	typ := pb.Data_Symlink
    83  	pbdata.Data = []byte(path)
    84  	pbdata.Type = &typ
    85  
    86  	out, err := proto.Marshal(pbdata)
    87  	if err != nil {
    88  		return nil, err
    89  	}
    90  
    91  	return out, nil
    92  }
    93  
    94  func UnwrapData(data []byte) ([]byte, error) {
    95  	pbdata := new(pb.Data)
    96  	err := proto.Unmarshal(data, pbdata)
    97  	if err != nil {
    98  		return nil, err
    99  	}
   100  	return pbdata.GetData(), nil
   101  }
   102  
   103  func DataSize(data []byte) (uint64, error) {
   104  	pbdata := new(pb.Data)
   105  	err := proto.Unmarshal(data, pbdata)
   106  	if err != nil {
   107  		return 0, err
   108  	}
   109  
   110  	switch pbdata.GetType() {
   111  	case pb.Data_Directory:
   112  		return 0, errors.New("Cant get data size of directory!")
   113  	case pb.Data_File:
   114  		return pbdata.GetFilesize(), nil
   115  	case pb.Data_Raw:
   116  		return uint64(len(pbdata.GetData())), nil
   117  	default:
   118  		return 0, errors.New("Unrecognized node data type!")
   119  	}
   120  }
   121  
   122  type FSNode struct {
   123  	Data []byte
   124  
   125  	// total data size for each child
   126  	blocksizes []uint64
   127  
   128  	// running sum of blocksizes
   129  	subtotal uint64
   130  
   131  	// node type of this node
   132  	Type pb.Data_DataType
   133  }
   134  
   135  func FSNodeFromBytes(b []byte) (*FSNode, error) {
   136  	pbn := new(pb.Data)
   137  	err := proto.Unmarshal(b, pbn)
   138  	if err != nil {
   139  		return nil, err
   140  	}
   141  
   142  	n := new(FSNode)
   143  	n.Data = pbn.Data
   144  	n.blocksizes = pbn.Blocksizes
   145  	n.subtotal = pbn.GetFilesize() - uint64(len(n.Data))
   146  	n.Type = pbn.GetType()
   147  	return n, nil
   148  }
   149  
   150  // AddBlockSize adds the size of the next child block of this node
   151  func (n *FSNode) AddBlockSize(s uint64) {
   152  	n.subtotal += s
   153  	n.blocksizes = append(n.blocksizes, s)
   154  }
   155  
   156  func (n *FSNode) RemoveBlockSize(i int) {
   157  	n.subtotal -= n.blocksizes[i]
   158  	n.blocksizes = append(n.blocksizes[:i], n.blocksizes[i+1:]...)
   159  }
   160  
   161  func (n *FSNode) GetBytes() ([]byte, error) {
   162  	pbn := new(pb.Data)
   163  	pbn.Type = &n.Type
   164  	pbn.Filesize = proto.Uint64(uint64(len(n.Data)) + n.subtotal)
   165  	pbn.Blocksizes = n.blocksizes
   166  	pbn.Data = n.Data
   167  	return proto.Marshal(pbn)
   168  }
   169  
   170  func (n *FSNode) FileSize() uint64 {
   171  	return uint64(len(n.Data)) + n.subtotal
   172  }
   173  
   174  func (n *FSNode) NumChildren() int {
   175  	return len(n.blocksizes)
   176  }
   177  
   178  type Metadata struct {
   179  	MimeType string
   180  	Size     uint64
   181  }
   182  
   183  func MetadataFromBytes(b []byte) (*Metadata, error) {
   184  	pbd := new(pb.Data)
   185  	err := proto.Unmarshal(b, pbd)
   186  	if err != nil {
   187  		return nil, err
   188  	}
   189  	if pbd.GetType() != pb.Data_Metadata {
   190  		return nil, errors.New("incorrect node type")
   191  	}
   192  
   193  	pbm := new(pb.Metadata)
   194  	err = proto.Unmarshal(pbd.Data, pbm)
   195  	if err != nil {
   196  		return nil, err
   197  	}
   198  	md := new(Metadata)
   199  	md.MimeType = pbm.GetMimeType()
   200  	return md, nil
   201  }
   202  
   203  func (m *Metadata) Bytes() ([]byte, error) {
   204  	pbm := new(pb.Metadata)
   205  	pbm.MimeType = &m.MimeType
   206  	return proto.Marshal(pbm)
   207  }
   208  
   209  func BytesForMetadata(m *Metadata) ([]byte, error) {
   210  	pbd := new(pb.Data)
   211  	pbd.Filesize = proto.Uint64(m.Size)
   212  	typ := pb.Data_Metadata
   213  	pbd.Type = &typ
   214  	mdd, err := m.Bytes()
   215  	if err != nil {
   216  		return nil, err
   217  	}
   218  
   219  	pbd.Data = mdd
   220  	return proto.Marshal(pbd)
   221  }