...

Source file src/pkg/cmd/pack/pack.go

     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	}
   562	

View as plain text