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