1  
     2  
     3  
     4  
     5  
     6  
     7  package archive
     8  
     9  import (
    10  	"bufio"
    11  	"bytes"
    12  	"cmd/internal/bio"
    13  	"cmd/internal/goobj"
    14  	"errors"
    15  	"fmt"
    16  	"io"
    17  	"log"
    18  	"os"
    19  	"strconv"
    20  	"strings"
    21  	"time"
    22  	"unicode/utf8"
    23  )
    24  
    25  
    40  
    41  
    42  
    43  
    44  type Data struct {
    45  	Offset int64
    46  	Size   int64
    47  }
    48  
    49  type Archive struct {
    50  	f       *os.File
    51  	Entries []Entry
    52  }
    53  
    54  func (a *Archive) File() *os.File { return a.f }
    55  
    56  type Entry struct {
    57  	Name  string
    58  	Type  EntryType
    59  	Mtime int64
    60  	Uid   int
    61  	Gid   int
    62  	Mode  os.FileMode
    63  	Data
    64  	Obj *GoObj 
    65  }
    66  
    67  type EntryType int
    68  
    69  const (
    70  	EntryPkgDef EntryType = iota
    71  	EntryGoObj
    72  	EntryNativeObj
    73  	EntrySentinelNonObj
    74  )
    75  
    76  func (e *Entry) String() string {
    77  	return fmt.Sprintf("%s %6d/%-6d %12d %s %s",
    78  		(e.Mode & 0777).String(),
    79  		e.Uid,
    80  		e.Gid,
    81  		e.Size,
    82  		time.Unix(e.Mtime, 0).Format(timeFormat),
    83  		e.Name)
    84  }
    85  
    86  type GoObj struct {
    87  	TextHeader []byte
    88  	Arch       string
    89  	Data
    90  }
    91  
    92  const (
    93  	entryHeader = "%s%-12d%-6d%-6d%-8o%-10d`\n"
    94  	
    95  	entryLen   = 16 + 12 + 6 + 6 + 8 + 10 + 1 + 1
    96  	timeFormat = "Jan _2 15:04 2006"
    97  )
    98  
    99  var (
   100  	archiveHeader = []byte("!<arch>\n")
   101  	archiveMagic  = []byte("`\n")
   102  	goobjHeader   = []byte("go objec") 
   103  
   104  	errCorruptArchive   = errors.New("corrupt archive")
   105  	errTruncatedArchive = errors.New("truncated archive")
   106  	errCorruptObject    = errors.New("corrupt object file")
   107  	errNotObject        = errors.New("unrecognized object file format")
   108  )
   109  
   110  type ErrGoObjOtherVersion struct{ magic []byte }
   111  
   112  func (e ErrGoObjOtherVersion) Error() string {
   113  	return fmt.Sprintf("go object of a different version: %q", e.magic)
   114  }
   115  
   116  
   117  type objReader struct {
   118  	a      *Archive
   119  	b      *bio.Reader
   120  	err    error
   121  	offset int64
   122  	limit  int64
   123  	tmp    [256]byte
   124  }
   125  
   126  func (r *objReader) init(f *os.File) {
   127  	r.a = &Archive{f, nil}
   128  	r.offset, _ = f.Seek(0, io.SeekCurrent)
   129  	r.limit, _ = f.Seek(0, io.SeekEnd)
   130  	f.Seek(r.offset, io.SeekStart)
   131  	r.b = bio.NewReader(f)
   132  }
   133  
   134  
   135  
   136  
   137  
   138  func (r *objReader) error(err error) error {
   139  	if r.err == nil {
   140  		if err == io.EOF {
   141  			err = io.ErrUnexpectedEOF
   142  		}
   143  		r.err = err
   144  	}
   145  	
   146  	return r.err
   147  }
   148  
   149  
   150  func (r *objReader) peek(n int) ([]byte, error) {
   151  	if r.err != nil {
   152  		return nil, r.err
   153  	}
   154  	if r.offset >= r.limit {
   155  		r.error(io.ErrUnexpectedEOF)
   156  		return nil, r.err
   157  	}
   158  	b, err := r.b.Peek(n)
   159  	if err != nil {
   160  		if err != bufio.ErrBufferFull {
   161  			r.error(err)
   162  		}
   163  	}
   164  	return b, err
   165  }
   166  
   167  
   168  
   169  
   170  
   171  
   172  func (r *objReader) readByte() byte {
   173  	if r.err != nil {
   174  		return 0
   175  	}
   176  	if r.offset >= r.limit {
   177  		r.error(io.ErrUnexpectedEOF)
   178  		return 0
   179  	}
   180  	b, err := r.b.ReadByte()
   181  	if err != nil {
   182  		if err == io.EOF {
   183  			err = io.ErrUnexpectedEOF
   184  		}
   185  		r.error(err)
   186  		b = 0
   187  	} else {
   188  		r.offset++
   189  	}
   190  	return b
   191  }
   192  
   193  
   194  
   195  
   196  
   197  func (r *objReader) readFull(b []byte) error {
   198  	if r.err != nil {
   199  		return r.err
   200  	}
   201  	if r.offset+int64(len(b)) > r.limit {
   202  		return r.error(io.ErrUnexpectedEOF)
   203  	}
   204  	n, err := io.ReadFull(r.b, b)
   205  	r.offset += int64(n)
   206  	if err != nil {
   207  		return r.error(err)
   208  	}
   209  	return nil
   210  }
   211  
   212  
   213  func (r *objReader) skip(n int64) {
   214  	if n < 0 {
   215  		r.error(fmt.Errorf("debug/goobj: internal error: misuse of skip"))
   216  	}
   217  	if n < int64(len(r.tmp)) {
   218  		
   219  		
   220  		r.readFull(r.tmp[:n])
   221  	} else if n <= int64(r.b.Buffered()) {
   222  		
   223  		
   224  		for n > int64(len(r.tmp)) {
   225  			r.readFull(r.tmp[:])
   226  			n -= int64(len(r.tmp))
   227  		}
   228  		r.readFull(r.tmp[:n])
   229  	} else {
   230  		
   231  		r.b.MustSeek(r.offset+n, io.SeekStart)
   232  		r.offset += n
   233  	}
   234  }
   235  
   236  
   237  func New(f *os.File) (*Archive, error) {
   238  	_, err := f.Write(archiveHeader)
   239  	if err != nil {
   240  		return nil, err
   241  	}
   242  	return &Archive{f: f}, nil
   243  }
   244  
   245  
   246  func Parse(f *os.File, verbose bool) (*Archive, error) {
   247  	var r objReader
   248  	r.init(f)
   249  	t, err := r.peek(8)
   250  	if err != nil {
   251  		if err == io.EOF {
   252  			err = io.ErrUnexpectedEOF
   253  		}
   254  		return nil, err
   255  	}
   256  
   257  	switch {
   258  	default:
   259  		return nil, errNotObject
   260  
   261  	case bytes.Equal(t, archiveHeader):
   262  		if err := r.parseArchive(verbose); err != nil {
   263  			return nil, err
   264  		}
   265  	case bytes.Equal(t, goobjHeader):
   266  		off := r.offset
   267  		o := &GoObj{}
   268  		if err := r.parseObject(o, r.limit-off); err != nil {
   269  			return nil, err
   270  		}
   271  		r.a.Entries = []Entry{{
   272  			Name: f.Name(),
   273  			Type: EntryGoObj,
   274  			Data: Data{off, r.limit - off},
   275  			Obj:  o,
   276  		}}
   277  	}
   278  
   279  	return r.a, nil
   280  }
   281  
   282  
   283  
   284  func trimSpace(b []byte) string {
   285  	return string(bytes.TrimRight(b, " "))
   286  }
   287  
   288  
   289  func (r *objReader) parseArchive(verbose bool) error {
   290  	r.readFull(r.tmp[:8]) 
   291  	for r.offset < r.limit {
   292  		if err := r.readFull(r.tmp[:60]); err != nil {
   293  			return err
   294  		}
   295  		data := r.tmp[:60]
   296  
   297  		
   298  		
   299  		
   300  		
   301  		
   302  		
   303  		
   304  		
   305  		
   306  		
   307  		
   308  		
   309  		
   310  		
   311  		
   312  		
   313  		if len(data) < 60 {
   314  			return errTruncatedArchive
   315  		}
   316  		if !bytes.Equal(data[58:60], archiveMagic) {
   317  			return errCorruptArchive
   318  		}
   319  		name := trimSpace(data[0:16])
   320  		var err error
   321  		get := func(start, end, base, bitsize int) int64 {
   322  			if err != nil {
   323  				return 0
   324  			}
   325  			var v int64
   326  			v, err = strconv.ParseInt(trimSpace(data[start:end]), base, bitsize)
   327  			return v
   328  		}
   329  		size := get(48, 58, 10, 64)
   330  		var (
   331  			mtime    int64
   332  			uid, gid int
   333  			mode     os.FileMode
   334  		)
   335  		if verbose {
   336  			mtime = get(16, 28, 10, 64)
   337  			uid = int(get(28, 34, 10, 32))
   338  			gid = int(get(34, 40, 10, 32))
   339  			mode = os.FileMode(get(40, 48, 8, 32))
   340  		}
   341  		if err != nil {
   342  			return errCorruptArchive
   343  		}
   344  		data = data[60:]
   345  		fsize := size + size&1
   346  		if fsize < 0 || fsize < size {
   347  			return errCorruptArchive
   348  		}
   349  		switch name {
   350  		case "__.PKGDEF":
   351  			r.a.Entries = append(r.a.Entries, Entry{
   352  				Name:  name,
   353  				Type:  EntryPkgDef,
   354  				Mtime: mtime,
   355  				Uid:   uid,
   356  				Gid:   gid,
   357  				Mode:  mode,
   358  				Data:  Data{r.offset, size},
   359  			})
   360  			r.skip(size)
   361  		case "preferlinkext", "dynimportfail":
   362  			if size == 0 {
   363  				
   364  				
   365  				
   366  				r.a.Entries = append(r.a.Entries, Entry{
   367  					Name:  name,
   368  					Type:  EntrySentinelNonObj,
   369  					Mtime: mtime,
   370  					Uid:   uid,
   371  					Gid:   gid,
   372  					Mode:  mode,
   373  					Data:  Data{r.offset, size},
   374  				})
   375  				break
   376  			}
   377  			fallthrough
   378  		default:
   379  			var typ EntryType
   380  			var o *GoObj
   381  			offset := r.offset
   382  			p, err := r.peek(8)
   383  			if err != nil {
   384  				return err
   385  			}
   386  			if bytes.Equal(p, goobjHeader) {
   387  				typ = EntryGoObj
   388  				o = &GoObj{}
   389  				err := r.parseObject(o, size)
   390  				if err != nil {
   391  					return err
   392  				}
   393  			} else {
   394  				typ = EntryNativeObj
   395  				r.skip(size)
   396  			}
   397  			r.a.Entries = append(r.a.Entries, Entry{
   398  				Name:  name,
   399  				Type:  typ,
   400  				Mtime: mtime,
   401  				Uid:   uid,
   402  				Gid:   gid,
   403  				Mode:  mode,
   404  				Data:  Data{offset, size},
   405  				Obj:   o,
   406  			})
   407  		}
   408  		if size&1 != 0 {
   409  			r.skip(1)
   410  		}
   411  	}
   412  	return nil
   413  }
   414  
   415  
   416  
   417  
   418  
   419  
   420  func (r *objReader) parseObject(o *GoObj, size int64) error {
   421  	h := make([]byte, 0, 256)
   422  	var c1, c2, c3 byte
   423  	for {
   424  		c1, c2, c3 = c2, c3, r.readByte()
   425  		h = append(h, c3)
   426  		
   427  		
   428  		if r.err != nil {
   429  			return errCorruptObject
   430  		}
   431  		if c1 == '\n' && c2 == '!' && c3 == '\n' {
   432  			break
   433  		}
   434  	}
   435  	o.TextHeader = h
   436  	hs := strings.Fields(string(h))
   437  	if len(hs) >= 4 {
   438  		o.Arch = hs[3]
   439  	}
   440  	o.Offset = r.offset
   441  	o.Size = size - int64(len(h))
   442  
   443  	p, err := r.peek(8)
   444  	if err != nil {
   445  		return err
   446  	}
   447  	if !bytes.Equal(p, []byte(goobj.Magic)) {
   448  		if bytes.HasPrefix(p, []byte("\x00go1")) && bytes.HasSuffix(p, []byte("ld")) {
   449  			return r.error(ErrGoObjOtherVersion{p[1:]}) 
   450  		}
   451  		return r.error(errCorruptObject)
   452  	}
   453  	r.skip(o.Size)
   454  	return nil
   455  }
   456  
   457  
   458  func (a *Archive) AddEntry(typ EntryType, name string, mtime int64, uid, gid int, mode os.FileMode, size int64, r io.Reader) {
   459  	off, err := a.f.Seek(0, io.SeekEnd)
   460  	if err != nil {
   461  		log.Fatal(err)
   462  	}
   463  	n, err := fmt.Fprintf(a.f, entryHeader, exactly16Bytes(name), mtime, uid, gid, mode, size)
   464  	if err != nil || n != entryLen {
   465  		log.Fatal("writing entry header: ", err)
   466  	}
   467  	n1, _ := io.CopyN(a.f, r, size)
   468  	if n1 != size {
   469  		log.Fatal(err)
   470  	}
   471  	if (off+size)&1 != 0 {
   472  		a.f.Write([]byte{0}) 
   473  	}
   474  	a.Entries = append(a.Entries, Entry{
   475  		Name:  name,
   476  		Type:  typ,
   477  		Mtime: mtime,
   478  		Uid:   uid,
   479  		Gid:   gid,
   480  		Mode:  mode,
   481  		Data:  Data{off + entryLen, size},
   482  	})
   483  }
   484  
   485  
   486  
   487  
   488  func exactly16Bytes(s string) string {
   489  	for len(s) > 16 {
   490  		_, wid := utf8.DecodeLastRuneInString(s)
   491  		s = s[:len(s)-wid]
   492  	}
   493  	const sixteenSpaces = "                "
   494  	s += sixteenSpaces[:16-len(s)]
   495  	return s
   496  }
   497  
   498  
   499  const HeaderSize = 60
   500  
   501  func ReadHeader(b *bufio.Reader, name string) int {
   502  	var buf [HeaderSize]byte
   503  	if _, err := io.ReadFull(b, buf[:]); err != nil {
   504  		return -1
   505  	}
   506  	aname := strings.Trim(string(buf[0:16]), " ")
   507  	if !strings.HasPrefix(aname, name) {
   508  		return -1
   509  	}
   510  	asize := strings.Trim(string(buf[48:58]), " ")
   511  	i, _ := strconv.Atoi(asize)
   512  	return i
   513  }
   514  
   515  func FormatHeader(arhdr []byte, name string, size int64) {
   516  	copy(arhdr[:], fmt.Sprintf("%-16s%-12d%-6d%-6d%-8o%-10d`\n", name, 0, 0, 0, 0644, size))
   517  }
   518  
View as plain text