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 }