github.com/gagliardetto/golang-go@v0.0.0-20201020153340-53909ea70814/cmd/pack/pack.go (about)

     1  // Copyright 2014 The Go Authors. All rights reserved.
     2  // Use of this source code is governed by a BSD-style
     3  // license that can be found in the LICENSE file.
     4  
     5  package main
     6  
     7  import (
     8  	"fmt"
     9  	"io"
    10  	"log"
    11  	"os"
    12  	"path/filepath"
    13  	"strconv"
    14  	"strings"
    15  	"time"
    16  	"unicode/utf8"
    17  )
    18  
    19  /*
    20  The archive format is:
    21  
    22  First, on a line by itself
    23  	!<arch>
    24  
    25  Then zero or more file records. Each file record has a fixed-size one-line header
    26  followed by data bytes followed by an optional padding byte. The header is:
    27  
    28  	%-16s%-12d%-6d%-6d%-8o%-10d`
    29  	name mtime uid gid mode size
    30  
    31  (note the trailing backquote). The %-16s here means at most 16 *bytes* of
    32  the name, and if shorter, space padded on the right.
    33  */
    34  
    35  const usageMessage = `Usage: pack op file.a [name....]
    36  Where op is one of cprtx optionally followed by v for verbose output.
    37  For compatibility with old Go build environments the op string grc is
    38  accepted as a synonym for c.
    39  
    40  For more information, run
    41  	go doc cmd/pack`
    42  
    43  func usage() {
    44  	fmt.Fprintln(os.Stderr, usageMessage)
    45  	os.Exit(2)
    46  }
    47  
    48  func main() {
    49  	log.SetFlags(0)
    50  	log.SetPrefix("pack: ")
    51  	// need "pack op archive" at least.
    52  	if len(os.Args) < 3 {
    53  		log.Print("not enough arguments")
    54  		fmt.Fprintln(os.Stderr)
    55  		usage()
    56  	}
    57  	setOp(os.Args[1])
    58  	var ar *Archive
    59  	switch op {
    60  	case 'p':
    61  		ar = archive(os.Args[2], os.O_RDONLY, os.Args[3:])
    62  		ar.scan(ar.printContents)
    63  	case 'r':
    64  		ar = archive(os.Args[2], os.O_RDWR, os.Args[3:])
    65  		ar.scan(ar.skipContents)
    66  		ar.addFiles()
    67  	case 'c':
    68  		ar = archive(os.Args[2], os.O_RDWR|os.O_TRUNC, os.Args[3:])
    69  		ar.addPkgdef()
    70  		ar.addFiles()
    71  	case 't':
    72  		ar = archive(os.Args[2], os.O_RDONLY, os.Args[3:])
    73  		ar.scan(ar.tableOfContents)
    74  	case 'x':
    75  		ar = archive(os.Args[2], os.O_RDONLY, os.Args[3:])
    76  		ar.scan(ar.extractContents)
    77  	default:
    78  		log.Printf("invalid operation %q", os.Args[1])
    79  		fmt.Fprintln(os.Stderr)
    80  		usage()
    81  	}
    82  	if len(ar.files) > 0 {
    83  		log.Fatalf("file %q not in archive", ar.files[0])
    84  	}
    85  }
    86  
    87  // The unusual ancestry means the arguments are not Go-standard.
    88  // These variables hold the decoded operation specified by the first argument.
    89  // op holds the operation we are doing (prtx).
    90  // verbose tells whether the 'v' option was specified.
    91  var (
    92  	op      rune
    93  	verbose bool
    94  )
    95  
    96  // setOp parses the operation string (first argument).
    97  func setOp(arg string) {
    98  	// Recognize 'go tool pack grc' because that was the
    99  	// formerly canonical way to build a new archive
   100  	// from a set of input files. Accepting it keeps old
   101  	// build systems working with both Go 1.2 and Go 1.3.
   102  	if arg == "grc" {
   103  		arg = "c"
   104  	}
   105  
   106  	for _, r := range arg {
   107  		switch r {
   108  		case 'c', 'p', 'r', 't', 'x':
   109  			if op != 0 {
   110  				// At most one can be set.
   111  				usage()
   112  			}
   113  			op = r
   114  		case 'v':
   115  			if verbose {
   116  				// Can be set only once.
   117  				usage()
   118  			}
   119  			verbose = true
   120  		default:
   121  			usage()
   122  		}
   123  	}
   124  }
   125  
   126  const (
   127  	arHeader    = "!<arch>\n"
   128  	entryHeader = "%s%-12d%-6d%-6d%-8o%-10d`\n"
   129  	// In entryHeader the first entry, the name, is always printed as 16 bytes right-padded.
   130  	entryLen   = 16 + 12 + 6 + 6 + 8 + 10 + 1 + 1
   131  	timeFormat = "Jan _2 15:04 2006"
   132  )
   133  
   134  // An Archive represents an open archive file. It is always scanned sequentially
   135  // from start to end, without backing up.
   136  type Archive struct {
   137  	fd       *os.File // Open file descriptor.
   138  	files    []string // Explicit list of files to be processed.
   139  	pad      int      // Padding bytes required at end of current archive file
   140  	matchAll bool     // match all files in archive
   141  }
   142  
   143  // archive opens (and if necessary creates) the named archive.
   144  func archive(name string, mode int, files []string) *Archive {
   145  	// If the file exists, it must be an archive. If it doesn't exist, or if
   146  	// we're doing the c command, indicated by O_TRUNC, truncate the archive.
   147  	if !existingArchive(name) || mode&os.O_TRUNC != 0 {
   148  		create(name)
   149  		mode &^= os.O_TRUNC
   150  	}
   151  	fd, err := os.OpenFile(name, mode, 0)
   152  	if err != nil {
   153  		log.Fatal(err)
   154  	}
   155  	checkHeader(fd)
   156  	return &Archive{
   157  		fd:       fd,
   158  		files:    files,
   159  		matchAll: len(files) == 0,
   160  	}
   161  }
   162  
   163  // create creates and initializes an archive that does not exist.
   164  func create(name string) {
   165  	fd, err := os.Create(name)
   166  	if err != nil {
   167  		log.Fatal(err)
   168  	}
   169  	_, err = fmt.Fprint(fd, arHeader)
   170  	if err != nil {
   171  		log.Fatal(err)
   172  	}
   173  	fd.Close()
   174  }
   175  
   176  // existingArchive reports whether the file exists and is a valid archive.
   177  // If it exists but is not an archive, existingArchive will exit.
   178  func existingArchive(name string) bool {
   179  	fd, err := os.Open(name)
   180  	if err != nil {
   181  		if os.IsNotExist(err) {
   182  			return false
   183  		}
   184  		log.Fatalf("cannot open file: %s", err)
   185  	}
   186  	checkHeader(fd)
   187  	fd.Close()
   188  	return true
   189  }
   190  
   191  // checkHeader verifies the header of the file. It assumes the file
   192  // is positioned at 0 and leaves it positioned at the end of the header.
   193  func checkHeader(fd *os.File) {
   194  	buf := make([]byte, len(arHeader))
   195  	_, err := io.ReadFull(fd, buf)
   196  	if err != nil || string(buf) != arHeader {
   197  		log.Fatalf("%s is not an archive: bad header", fd.Name())
   198  	}
   199  }
   200  
   201  // An Entry is the internal representation of the per-file header information of one entry in the archive.
   202  type Entry struct {
   203  	name  string
   204  	mtime int64
   205  	uid   int
   206  	gid   int
   207  	mode  os.FileMode
   208  	size  int64
   209  }
   210  
   211  func (e *Entry) String() string {
   212  	return fmt.Sprintf("%s %6d/%-6d %12d %s %s",
   213  		(e.mode & 0777).String(),
   214  		e.uid,
   215  		e.gid,
   216  		e.size,
   217  		time.Unix(e.mtime, 0).Format(timeFormat),
   218  		e.name)
   219  }
   220  
   221  // readMetadata reads and parses the metadata for the next entry in the archive.
   222  func (ar *Archive) readMetadata() *Entry {
   223  	buf := make([]byte, entryLen)
   224  	_, err := io.ReadFull(ar.fd, buf)
   225  	if err == io.EOF {
   226  		// No entries left.
   227  		return nil
   228  	}
   229  	if err != nil || buf[entryLen-2] != '`' || buf[entryLen-1] != '\n' {
   230  		log.Fatal("file is not an archive: bad entry")
   231  	}
   232  	entry := new(Entry)
   233  	entry.name = strings.TrimRight(string(buf[:16]), " ")
   234  	if len(entry.name) == 0 {
   235  		log.Fatal("file is not an archive: bad name")
   236  	}
   237  	buf = buf[16:]
   238  	str := string(buf)
   239  	get := func(width, base, bitsize int) int64 {
   240  		v, err := strconv.ParseInt(strings.TrimRight(str[:width], " "), base, bitsize)
   241  		if err != nil {
   242  			log.Fatal("file is not an archive: bad number in entry: ", err)
   243  		}
   244  		str = str[width:]
   245  		return v
   246  	}
   247  	// %-16s%-12d%-6d%-6d%-8o%-10d`
   248  	entry.mtime = get(12, 10, 64)
   249  	entry.uid = int(get(6, 10, 32))
   250  	entry.gid = int(get(6, 10, 32))
   251  	entry.mode = os.FileMode(get(8, 8, 32))
   252  	entry.size = get(10, 10, 64)
   253  	return entry
   254  }
   255  
   256  // scan scans the archive and executes the specified action on each entry.
   257  // When action returns, the file offset is at the start of the next entry.
   258  func (ar *Archive) scan(action func(*Entry)) {
   259  	for {
   260  		entry := ar.readMetadata()
   261  		if entry == nil {
   262  			break
   263  		}
   264  		action(entry)
   265  	}
   266  }
   267  
   268  // listEntry prints to standard output a line describing the entry.
   269  func listEntry(entry *Entry, verbose bool) {
   270  	if verbose {
   271  		fmt.Fprintf(stdout, "%s\n", entry)
   272  	} else {
   273  		fmt.Fprintf(stdout, "%s\n", entry.name)
   274  	}
   275  }
   276  
   277  // output copies the entry to the specified writer.
   278  func (ar *Archive) output(entry *Entry, w io.Writer) {
   279  	n, err := io.Copy(w, io.LimitReader(ar.fd, entry.size))
   280  	if err != nil {
   281  		log.Fatal(err)
   282  	}
   283  	if n != entry.size {
   284  		log.Fatal("short file")
   285  	}
   286  	if entry.size&1 == 1 {
   287  		_, err := ar.fd.Seek(1, io.SeekCurrent)
   288  		if err != nil {
   289  			log.Fatal(err)
   290  		}
   291  	}
   292  }
   293  
   294  // skip skips the entry without reading it.
   295  func (ar *Archive) skip(entry *Entry) {
   296  	size := entry.size
   297  	if size&1 == 1 {
   298  		size++
   299  	}
   300  	_, err := ar.fd.Seek(size, io.SeekCurrent)
   301  	if err != nil {
   302  		log.Fatal(err)
   303  	}
   304  }
   305  
   306  // match reports whether the entry matches the argument list.
   307  // If it does, it also drops the file from the to-be-processed list.
   308  func (ar *Archive) match(entry *Entry) bool {
   309  	if ar.matchAll {
   310  		return true
   311  	}
   312  	for i, name := range ar.files {
   313  		if entry.name == name {
   314  			copy(ar.files[i:], ar.files[i+1:])
   315  			ar.files = ar.files[:len(ar.files)-1]
   316  			return true
   317  		}
   318  	}
   319  	return false
   320  }
   321  
   322  // addFiles adds files to the archive. The archive is known to be
   323  // sane and we are positioned at the end. No attempt is made
   324  // to check for existing files.
   325  func (ar *Archive) addFiles() {
   326  	if len(ar.files) == 0 {
   327  		usage()
   328  	}
   329  	for _, file := range ar.files {
   330  		if verbose {
   331  			fmt.Printf("%s\n", file)
   332  		}
   333  
   334  		if !isGoCompilerObjFile(file) {
   335  			fd, err := os.Open(file)
   336  			if err != nil {
   337  				log.Fatal(err)
   338  			}
   339  			ar.addFile(fd)
   340  			continue
   341  		}
   342  
   343  		aro := archive(file, os.O_RDONLY, nil)
   344  		aro.scan(func(entry *Entry) {
   345  			if entry.name != "_go_.o" {
   346  				aro.skip(entry)
   347  				return
   348  			}
   349  			ar.startFile(filepath.Base(file), 0, 0, 0, 0644, entry.size)
   350  			aro.output(entry, ar.fd)
   351  			ar.endFile()
   352  		})
   353  	}
   354  	ar.files = nil
   355  }
   356  
   357  // FileLike abstracts the few methods we need, so we can test without needing real files.
   358  type FileLike interface {
   359  	Name() string
   360  	Stat() (os.FileInfo, error)
   361  	Read([]byte) (int, error)
   362  	Close() error
   363  }
   364  
   365  // addFile adds a single file to the archive
   366  func (ar *Archive) addFile(fd FileLike) {
   367  	defer fd.Close()
   368  	// Format the entry.
   369  	// First, get its info.
   370  	info, err := fd.Stat()
   371  	if err != nil {
   372  		log.Fatal(err)
   373  	}
   374  	// mtime, uid, gid are all zero so repeated builds produce identical output.
   375  	mtime := int64(0)
   376  	uid := 0
   377  	gid := 0
   378  	ar.startFile(info.Name(), mtime, uid, gid, info.Mode(), info.Size())
   379  	n64, err := io.Copy(ar.fd, fd)
   380  	if err != nil {
   381  		log.Fatal("writing file: ", err)
   382  	}
   383  	if n64 != info.Size() {
   384  		log.Fatalf("writing file: wrote %d bytes; file is size %d", n64, info.Size())
   385  	}
   386  	ar.endFile()
   387  }
   388  
   389  // startFile writes the archive entry header.
   390  func (ar *Archive) startFile(name string, mtime int64, uid, gid int, mode os.FileMode, size int64) {
   391  	n, err := fmt.Fprintf(ar.fd, entryHeader, exactly16Bytes(name), mtime, uid, gid, mode, size)
   392  	if err != nil || n != entryLen {
   393  		log.Fatal("writing entry header: ", err)
   394  	}
   395  	ar.pad = int(size & 1)
   396  }
   397  
   398  // endFile writes the archive entry tail (a single byte of padding, if the file size was odd).
   399  func (ar *Archive) endFile() {
   400  	if ar.pad != 0 {
   401  		_, err := ar.fd.Write([]byte{0})
   402  		if err != nil {
   403  			log.Fatal("writing archive: ", err)
   404  		}
   405  		ar.pad = 0
   406  	}
   407  }
   408  
   409  // addPkgdef adds the __.PKGDEF file to the archive, copied
   410  // from the first Go object file on the file list, if any.
   411  // The archive is known to be empty.
   412  func (ar *Archive) addPkgdef() {
   413  	done := false
   414  	for _, file := range ar.files {
   415  		if !isGoCompilerObjFile(file) {
   416  			continue
   417  		}
   418  		aro := archive(file, os.O_RDONLY, nil)
   419  		aro.scan(func(entry *Entry) {
   420  			if entry.name != "__.PKGDEF" {
   421  				aro.skip(entry)
   422  				return
   423  			}
   424  			if verbose {
   425  				fmt.Printf("__.PKGDEF # %s\n", file)
   426  			}
   427  			ar.startFile("__.PKGDEF", 0, 0, 0, 0644, entry.size)
   428  			aro.output(entry, ar.fd)
   429  			ar.endFile()
   430  			done = true
   431  		})
   432  		if done {
   433  			break
   434  		}
   435  	}
   436  }
   437  
   438  // exactly16Bytes truncates the string if necessary so it is at most 16 bytes long,
   439  // then pads the result with spaces to be exactly 16 bytes.
   440  // Fmt uses runes for its width calculation, but we need bytes in the entry header.
   441  func exactly16Bytes(s string) string {
   442  	for len(s) > 16 {
   443  		_, wid := utf8.DecodeLastRuneInString(s)
   444  		s = s[:len(s)-wid]
   445  	}
   446  	const sixteenSpaces = "                "
   447  	s += sixteenSpaces[:16-len(s)]
   448  	return s
   449  }
   450  
   451  // Finally, the actual commands. Each is an action.
   452  
   453  // can be modified for testing.
   454  var stdout io.Writer = os.Stdout
   455  
   456  // printContents implements the 'p' command.
   457  func (ar *Archive) printContents(entry *Entry) {
   458  	if ar.match(entry) {
   459  		if verbose {
   460  			listEntry(entry, false)
   461  		}
   462  		ar.output(entry, stdout)
   463  	} else {
   464  		ar.skip(entry)
   465  	}
   466  }
   467  
   468  // skipContents implements the first part of the 'r' command.
   469  // It just scans the archive to make sure it's intact.
   470  func (ar *Archive) skipContents(entry *Entry) {
   471  	ar.skip(entry)
   472  }
   473  
   474  // tableOfContents implements the 't' command.
   475  func (ar *Archive) tableOfContents(entry *Entry) {
   476  	if ar.match(entry) {
   477  		listEntry(entry, verbose)
   478  	}
   479  	ar.skip(entry)
   480  }
   481  
   482  // extractContents implements the 'x' command.
   483  func (ar *Archive) extractContents(entry *Entry) {
   484  	if ar.match(entry) {
   485  		if verbose {
   486  			listEntry(entry, false)
   487  		}
   488  		fd, err := os.OpenFile(entry.name, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, entry.mode)
   489  		if err != nil {
   490  			log.Fatal(err)
   491  		}
   492  		ar.output(entry, fd)
   493  		fd.Close()
   494  	} else {
   495  		ar.skip(entry)
   496  	}
   497  }
   498  
   499  // isGoCompilerObjFile reports whether file is an object file created
   500  // by the Go compiler.
   501  func isGoCompilerObjFile(file string) bool {
   502  	fd, err := os.Open(file)
   503  	if err != nil {
   504  		log.Fatal(err)
   505  	}
   506  
   507  	// Check for "!<arch>\n" header.
   508  	buf := make([]byte, len(arHeader))
   509  	_, err = io.ReadFull(fd, buf)
   510  	if err != nil {
   511  		if err == io.EOF {
   512  			return false
   513  		}
   514  		log.Fatal(err)
   515  	}
   516  	if string(buf) != arHeader {
   517  		return false
   518  	}
   519  
   520  	// Check for exactly two entries: "__.PKGDEF" and "_go_.o".
   521  	match := []string{"__.PKGDEF", "_go_.o"}
   522  	buf = make([]byte, entryLen)
   523  	for {
   524  		_, err := io.ReadFull(fd, buf)
   525  		if err != nil {
   526  			if err == io.EOF {
   527  				// No entries left.
   528  				return true
   529  			}
   530  			log.Fatal(err)
   531  		}
   532  		if buf[entryLen-2] != '`' || buf[entryLen-1] != '\n' {
   533  			return false
   534  		}
   535  
   536  		name := strings.TrimRight(string(buf[:16]), " ")
   537  		for {
   538  			if len(match) == 0 {
   539  				return false
   540  			}
   541  			var next string
   542  			next, match = match[0], match[1:]
   543  			if name == next {
   544  				break
   545  			}
   546  		}
   547  
   548  		size, err := strconv.ParseInt(strings.TrimRight(string(buf[48:58]), " "), 10, 64)
   549  		if err != nil {
   550  			return false
   551  		}
   552  		if size&1 != 0 {
   553  			size++
   554  		}
   555  
   556  		_, err = fd.Seek(size, io.SeekCurrent)
   557  		if err != nil {
   558  			log.Fatal(err)
   559  		}
   560  	}
   561  }