...

Source file src/github.com/sassoftware/relic/lib/zipslicer/file.go

Documentation: github.com/sassoftware/relic/lib/zipslicer

     1  //
     2  // Copyright (c) SAS Institute Inc.
     3  //
     4  // Licensed under the Apache License, Version 2.0 (the "License");
     5  // you may not use this file except in compliance with the License.
     6  // You may obtain a copy of the License at
     7  //
     8  //     http://www.apache.org/licenses/LICENSE-2.0
     9  //
    10  // Unless required by applicable law or agreed to in writing, software
    11  // distributed under the License is distributed on an "AS IS" BASIS,
    12  // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    13  // See the License for the specific language governing permissions and
    14  // limitations under the License.
    15  //
    16  
    17  package zipslicer
    18  
    19  import (
    20  	"archive/zip"
    21  	"bufio"
    22  	"bytes"
    23  	"compress/flate"
    24  	"crypto"
    25  	"encoding/binary"
    26  	"errors"
    27  	"hash"
    28  	"hash/crc32"
    29  	"io"
    30  	"io/ioutil"
    31  	"time"
    32  )
    33  
    34  type File struct {
    35  	CreatorVersion   uint16
    36  	ReaderVersion    uint16
    37  	Flags            uint16
    38  	Method           uint16
    39  	ModifiedTime     uint16
    40  	ModifiedDate     uint16
    41  	CRC32            uint32
    42  	CompressedSize   uint64
    43  	UncompressedSize uint64
    44  	Name             string
    45  	Extra            []byte
    46  	Comment          []byte
    47  	InternalAttrs    uint16
    48  	ExternalAttrs    uint32
    49  	Offset           uint64
    50  
    51  	r                 io.ReaderAt
    52  	rs                int64
    53  	raw               []byte
    54  	lfh               zipLocalHeader
    55  	lfhName, lfhExtra []byte
    56  	ddb               []byte
    57  	compd             []byte
    58  }
    59  
    60  type Reader struct {
    61  	f     *File
    62  	rc    io.ReadCloser
    63  	crc   hash.Hash32
    64  	err   error
    65  	nread uint64
    66  }
    67  
    68  func (f *File) readLocalHeader() error {
    69  	if f.lfh.Signature != 0 {
    70  		// already done
    71  		return nil
    72  	}
    73  	sr := io.NewSectionReader(f.r, int64(f.Offset), f.rs)
    74  	var lfhb [fileHeaderLen]byte
    75  	if _, err := io.ReadFull(sr, lfhb[:]); err != nil {
    76  		return err
    77  	}
    78  	binary.Read(bytes.NewReader(lfhb[:]), binary.LittleEndian, &f.lfh)
    79  	if f.lfh.Signature != fileHeaderSignature {
    80  		return errors.New("local file header not found")
    81  	}
    82  	f.lfhName = make([]byte, f.lfh.FilenameLen)
    83  	if _, err := io.ReadFull(sr, f.lfhName); err != nil {
    84  		return err
    85  	}
    86  	f.lfhExtra = make([]byte, f.lfh.ExtraLen)
    87  	if _, err := io.ReadFull(sr, f.lfhExtra); err != nil {
    88  		return err
    89  	}
    90  	return nil
    91  }
    92  
    93  func (f *File) readDataDesc() error {
    94  	if err := f.readLocalHeader(); err != nil {
    95  		return err
    96  	}
    97  	if f.lfh.Flags&0x8 == 0 {
    98  		// no descriptor
    99  		return nil
   100  	}
   101  	if len(f.ddb) != 0 {
   102  		// already done
   103  		return nil
   104  	}
   105  	lfhSize := fileHeaderLen + len(f.lfhName) + len(f.lfhExtra)
   106  	pos := int64(f.Offset) + int64(lfhSize) + int64(f.CompressedSize)
   107  	f.ddb = make([]byte, dataDescriptor64Len)
   108  	if _, err := f.r.ReadAt(f.ddb[:dataDescriptorLen], pos); err != nil {
   109  		return err
   110  	}
   111  	// Read the 32-bit len so we don't overshoot. The underlying stream might
   112  	// not be seekable.
   113  	var desc zipDataDesc
   114  	binary.Read(bytes.NewReader(f.ddb[:dataDescriptorLen]), binary.LittleEndian, &desc)
   115  	if desc.Signature != dataDescriptorSignature {
   116  		return errors.New("data descriptor signature is missing")
   117  	}
   118  	if f.UncompressedSize >= uint32Max || desc.UncompressedSize != uint32(f.UncompressedSize) || desc.CompressedSize != uint32(f.CompressedSize) {
   119  		// 64-bit
   120  		if _, err := f.r.ReadAt(f.ddb[dataDescriptorLen:], pos+dataDescriptorLen); err != nil {
   121  			return err
   122  		}
   123  		var desc64 zipDataDesc64
   124  		binary.Read(bytes.NewReader(f.ddb), binary.LittleEndian, &desc64)
   125  		if desc64.CompressedSize != f.CompressedSize || desc64.UncompressedSize != f.UncompressedSize {
   126  			return errors.New("data descriptor is invalid")
   127  		}
   128  		f.CRC32 = desc64.CRC32
   129  	} else {
   130  		// 32-bit
   131  		f.ddb = f.ddb[:dataDescriptorLen]
   132  		f.CRC32 = desc.CRC32
   133  	}
   134  	return nil
   135  }
   136  
   137  func (f *File) GetDirectoryHeader() ([]byte, error) {
   138  	if len(f.raw) > 0 {
   139  		return f.raw, nil
   140  	}
   141  	hdr := zipCentralDir{
   142  		Signature:        directoryHeaderSignature,
   143  		CreatorVersion:   f.CreatorVersion,
   144  		ReaderVersion:    f.ReaderVersion,
   145  		Flags:            f.Flags,
   146  		Method:           f.Method,
   147  		ModifiedTime:     f.ModifiedTime,
   148  		ModifiedDate:     f.ModifiedDate,
   149  		CRC32:            f.CRC32,
   150  		CompressedSize:   uint32(f.CompressedSize),
   151  		UncompressedSize: uint32(f.UncompressedSize),
   152  		InternalAttrs:    f.InternalAttrs,
   153  		ExternalAttrs:    f.ExternalAttrs,
   154  		Offset:           uint32(f.Offset),
   155  		FilenameLen:      uint16(len(f.Name)),
   156  		ExtraLen:         uint16(len(f.Extra)),
   157  		CommentLen:       uint16(len(f.Comment)),
   158  	}
   159  	if f.CompressedSize >= uint32Max || f.UncompressedSize >= uint32Max || f.Offset >= uint32Max {
   160  		hdr.CompressedSize = uint32Max
   161  		hdr.UncompressedSize = uint32Max
   162  		hdr.Offset = uint32Max
   163  		extra := zip64Extra{
   164  			Signature:        zip64ExtraID,
   165  			RecordSize:       zip64ExtraLen,
   166  			UncompressedSize: f.UncompressedSize,
   167  			CompressedSize:   f.CompressedSize,
   168  			Offset:           f.Offset,
   169  		}
   170  		b := bytes.NewBuffer(make([]byte, 0, zip64ExtraLen+4+len(f.Extra)))
   171  		binary.Write(b, binary.LittleEndian, extra)
   172  		b.Write(f.Extra)
   173  		f.Extra = b.Bytes()
   174  		hdr.ExtraLen = uint16(b.Len())
   175  		hdr.ReaderVersion = zip45
   176  	}
   177  	b := bytes.NewBuffer(make([]byte, 0, directoryHeaderLen+len(f.Name)+len(f.Extra)+len(f.Comment)))
   178  	binary.Write(b, binary.LittleEndian, hdr)
   179  	b.WriteString(f.Name)
   180  	b.Write(f.Extra)
   181  	b.Write(f.Comment)
   182  	return b.Bytes(), nil
   183  }
   184  
   185  func (f *File) GetLocalHeader() ([]byte, error) {
   186  	if err := f.readLocalHeader(); err != nil {
   187  		return nil, err
   188  	}
   189  	b := bytes.NewBuffer(make([]byte, 0, fileHeaderLen+len(f.lfhName)+len(f.lfhExtra)))
   190  	binary.Write(b, binary.LittleEndian, f.lfh)
   191  	b.Write(f.lfhName)
   192  	b.Write(f.lfhExtra)
   193  	return b.Bytes(), nil
   194  }
   195  
   196  func (f *File) GetDataDescriptor() ([]byte, error) {
   197  	if err := f.readDataDesc(); err != nil {
   198  		return nil, err
   199  	}
   200  	return f.ddb, nil
   201  }
   202  
   203  func (f *File) GetTotalSize() (int64, error) {
   204  	if err := f.readDataDesc(); err != nil {
   205  		return 0, err
   206  	}
   207  	return fileHeaderLen + int64(len(f.lfhName)+len(f.lfhExtra)+len(f.ddb)) + int64(f.CompressedSize), nil
   208  }
   209  
   210  func (d *Directory) NewFile(name string, extra, contents []byte, w io.Writer, mtime time.Time, deflate, useDesc bool) (*File, error) {
   211  	var zh zip.FileHeader
   212  	zh.SetModTime(mtime)
   213  	var fb bytes.Buffer
   214  	method := zip.Deflate
   215  	if deflate {
   216  		c, err := flate.NewWriter(&fb, 9)
   217  		if err != nil {
   218  			return nil, err
   219  		}
   220  		if _, err := c.Write(contents); err != nil {
   221  			return nil, err
   222  		}
   223  		if err := c.Close(); err != nil {
   224  			return nil, err
   225  		}
   226  	} else {
   227  		method = zip.Store
   228  		fb.Write(contents)
   229  	}
   230  	crc := crc32.NewIEEE()
   231  	crc.Write(contents)
   232  	sum := crc.Sum32()
   233  	buf := bufio.NewWriter(w)
   234  
   235  	f := &File{
   236  		CreatorVersion:   zip45,
   237  		ReaderVersion:    zip20,
   238  		Method:           method,
   239  		ModifiedTime:     zh.ModifiedTime,
   240  		ModifiedDate:     zh.ModifiedDate,
   241  		CRC32:            sum,
   242  		CompressedSize:   uint64(fb.Len()),
   243  		UncompressedSize: uint64(len(contents)),
   244  		Name:             name,
   245  		Extra:            extra,
   246  
   247  		lfhName:  []byte(name),
   248  		lfhExtra: extra,
   249  		compd:    fb.Bytes(),
   250  	}
   251  	if useDesc {
   252  		f.Flags = 0x8
   253  		f.ReaderVersion = zip45
   254  	}
   255  	f.lfh = zipLocalHeader{
   256  		Signature:     fileHeaderSignature,
   257  		ReaderVersion: f.ReaderVersion,
   258  		Flags:         f.Flags,
   259  		Method:        f.Method,
   260  		ModifiedTime:  f.ModifiedTime,
   261  		ModifiedDate:  f.ModifiedDate,
   262  		FilenameLen:   uint16(len(name)),
   263  		ExtraLen:      uint16(len(extra)),
   264  	}
   265  	if !useDesc {
   266  		f.lfh.CRC32 = f.CRC32
   267  		f.lfh.CompressedSize = uint32(f.CompressedSize)
   268  		f.lfh.UncompressedSize = uint32(f.UncompressedSize)
   269  	}
   270  	if err := binary.Write(buf, binary.LittleEndian, f.lfh); err != nil {
   271  		return nil, err
   272  	}
   273  	if _, err := buf.WriteString(name); err != nil {
   274  		return nil, err
   275  	}
   276  	if _, err := buf.Write(extra); err != nil {
   277  		return nil, err
   278  	}
   279  	if _, err := buf.Write(fb.Bytes()); err != nil {
   280  		return nil, err
   281  	}
   282  	if useDesc {
   283  		desc := zipDataDesc64{
   284  			Signature:        dataDescriptorSignature,
   285  			CRC32:            sum,
   286  			CompressedSize:   uint64(fb.Len()),
   287  			UncompressedSize: uint64(len(contents)),
   288  		}
   289  		ddb := bytes.NewBuffer(make([]byte, 0, dataDescriptor64Len))
   290  		binary.Write(ddb, binary.LittleEndian, desc)
   291  		f.ddb = ddb.Bytes()
   292  		if _, err := buf.Write(f.ddb); err != nil {
   293  			return nil, err
   294  		}
   295  	}
   296  	if err := buf.Flush(); err != nil {
   297  		return nil, err
   298  	}
   299  	return d.AddFile(f)
   300  }
   301  
   302  func (f *File) ModTime() time.Time {
   303  	fh := zip.FileHeader{ModifiedDate: f.ModifiedDate, ModifiedTime: f.ModifiedTime}
   304  	return fh.ModTime()
   305  }
   306  
   307  func (f *File) Open() (io.ReadCloser, error) {
   308  	return f.OpenAndTeeRaw(nil)
   309  }
   310  
   311  // Open zip file for reading, but also write raw zip data to 'sink'.
   312  func (f *File) OpenAndTeeRaw(sink io.Writer) (*Reader, error) {
   313  	if err := f.readLocalHeader(); err != nil {
   314  		return nil, err
   315  	}
   316  	var r io.Reader
   317  	if f.compd != nil {
   318  		r = bytes.NewReader(f.compd)
   319  	} else {
   320  		pos := int64(f.Offset) + fileHeaderLen + int64(f.lfh.FilenameLen) + int64(f.lfh.ExtraLen)
   321  		r = io.NewSectionReader(f.r, pos, int64(f.CompressedSize))
   322  	}
   323  	if sink != nil {
   324  		r = io.TeeReader(r, sink)
   325  	}
   326  	crc := crc32.NewIEEE()
   327  	var rc io.ReadCloser
   328  	switch f.Method {
   329  	case zip.Store:
   330  		rc = ioutil.NopCloser(r)
   331  	case zip.Deflate:
   332  		rc = flate.NewReader(r)
   333  	default:
   334  		return nil, errors.New("unsupported zip compression")
   335  	}
   336  	return &Reader{f: f, rc: rc, crc: crc}, nil
   337  }
   338  
   339  func (r *Reader) Read(d []byte) (int, error) {
   340  	if r.err != nil {
   341  		return 0, r.err
   342  	}
   343  	n, err := r.rc.Read(d)
   344  	r.crc.Write(d[:n])
   345  	r.nread += uint64(n)
   346  	if err == nil {
   347  		return n, nil
   348  	}
   349  	if err != io.EOF {
   350  		r.err = err
   351  		return n, err
   352  	}
   353  	if r.nread != r.f.UncompressedSize {
   354  		return 0, io.ErrUnexpectedEOF
   355  	}
   356  	if r.f.lfh.Flags&0x8 != 0 {
   357  		if err2 := r.f.readDataDesc(); err2 != nil {
   358  			if err2 == io.EOF {
   359  				err = io.ErrUnexpectedEOF
   360  			} else {
   361  				err = err2
   362  			}
   363  		}
   364  	}
   365  	if r.f.CRC32 != 0 && r.crc.Sum32() != r.f.CRC32 {
   366  		err = zip.ErrChecksum
   367  	}
   368  	r.err = err
   369  	return n, err
   370  }
   371  
   372  func (r *Reader) Close() error {
   373  	return r.rc.Close()
   374  }
   375  
   376  func (f *File) Digest(hash crypto.Hash) ([]byte, error) {
   377  	fc, err := f.Open()
   378  	if err != nil {
   379  		return nil, err
   380  	}
   381  	d := hash.New()
   382  	if _, err := io.Copy(d, fc); err != nil {
   383  		return nil, err
   384  	}
   385  	return d.Sum(nil), fc.Close()
   386  }
   387  
   388  // Dump the local header and contents of this file to a writer
   389  func (f *File) Dump(w io.Writer) (int64, error) {
   390  	lfh, err := f.GetLocalHeader()
   391  	if err != nil {
   392  		return 0, err
   393  	}
   394  	if _, err := w.Write(lfh); err != nil {
   395  		return 0, err
   396  	}
   397  	if f.compd != nil {
   398  		if _, err := w.Write(f.compd); err != nil {
   399  			return 0, err
   400  		}
   401  	} else {
   402  		pos := int64(f.Offset) + fileHeaderLen + int64(f.lfh.FilenameLen) + int64(f.lfh.ExtraLen)
   403  		r := io.NewSectionReader(f.r, pos, int64(f.CompressedSize))
   404  		if _, err := io.Copy(w, r); err != nil {
   405  			return 0, err
   406  		}
   407  	}
   408  	ddb, err := f.GetDataDescriptor()
   409  	if err != nil {
   410  		return 0, err
   411  	}
   412  	if _, err := w.Write(ddb); err != nil {
   413  		return 0, err
   414  	}
   415  	return f.GetTotalSize()
   416  }
   417  

View as plain text