Source file
src/cmd/pack/pack.go
1
2
3
4
5 package main
6
7 import (
8 "cmd/internal/archive"
9 "cmd/internal/objabi"
10 "cmd/internal/telemetry/counter"
11 "fmt"
12 "io"
13 "io/fs"
14 "log"
15 "os"
16 "path/filepath"
17 )
18
19 const usageMessage = `Usage: pack op file.a [name....]
20 Where op is one of cprtx optionally followed by v for verbose output.
21 For compatibility with old Go build environments the op string grc is
22 accepted as a synonym for c.
23
24 For more information, run
25 go doc cmd/pack`
26
27 func usage() {
28 fmt.Fprintln(os.Stderr, usageMessage)
29 os.Exit(2)
30 }
31
32 func main() {
33 log.SetFlags(0)
34 log.SetPrefix("pack: ")
35 counter.Open()
36 objabi.Flagparse(usage)
37
38 if len(os.Args) < 3 {
39 log.Print("not enough arguments")
40 fmt.Fprintln(os.Stderr)
41 usage()
42 }
43 setOp(os.Args[1])
44 counter.Inc("pack/invocations")
45 counter.Inc("pack/op:" + string(op))
46 var ar *Archive
47 switch op {
48 case 'p':
49 ar = openArchive(os.Args[2], os.O_RDONLY, os.Args[3:])
50 ar.scan(ar.printContents)
51 case 'r':
52 ar = openArchive(os.Args[2], os.O_RDWR|os.O_CREATE, os.Args[3:])
53 ar.addFiles()
54 case 'c':
55 ar = openArchive(os.Args[2], os.O_RDWR|os.O_TRUNC|os.O_CREATE, os.Args[3:])
56 ar.addPkgdef()
57 ar.addFiles()
58 case 't':
59 ar = openArchive(os.Args[2], os.O_RDONLY, os.Args[3:])
60 ar.scan(ar.tableOfContents)
61 case 'x':
62 ar = openArchive(os.Args[2], os.O_RDONLY, os.Args[3:])
63 ar.scan(ar.extractContents)
64 default:
65 log.Printf("invalid operation %q", os.Args[1])
66 fmt.Fprintln(os.Stderr)
67 usage()
68 }
69 if len(ar.files) > 0 {
70 log.Fatalf("file %q not in archive", ar.files[0])
71 }
72 }
73
74
75
76
77
78 var (
79 op rune
80 verbose bool
81 )
82
83
84 func setOp(arg string) {
85
86
87
88
89 if arg == "grc" {
90 arg = "c"
91 }
92
93 for _, r := range arg {
94 switch r {
95 case 'c', 'p', 'r', 't', 'x':
96 if op != 0 {
97
98 usage()
99 }
100 op = r
101 case 'v':
102 if verbose {
103
104 usage()
105 }
106 verbose = true
107 default:
108 usage()
109 }
110 }
111 }
112
113 const (
114 arHeader = "!<arch>\n"
115 )
116
117
118
119 type Archive struct {
120 a *archive.Archive
121 files []string
122 pad int
123 matchAll bool
124 }
125
126
127 func openArchive(name string, mode int, files []string) *Archive {
128 f, err := os.OpenFile(name, mode, 0666)
129 if err != nil {
130 log.Fatal(err)
131 }
132 var a *archive.Archive
133 if mode&os.O_TRUNC != 0 {
134 a, err = archive.New(f)
135 } else {
136 a, err = archive.Parse(f, verbose)
137 if err != nil && mode&os.O_CREATE != 0 {
138 a, err = archive.New(f)
139 }
140 }
141 if err != nil {
142 log.Fatal(err)
143 }
144 for _, f := range a.Entries {
145 if !filepath.IsLocal(f.Name) || filepath.Base(f.Name) != f.Name {
146 log.Fatalf("%q: invalid name", f.Name)
147 }
148 }
149 return &Archive{
150 a: a,
151 files: files,
152 matchAll: len(files) == 0,
153 }
154 }
155
156
157 func (ar *Archive) scan(action func(*archive.Entry)) {
158 for i := range ar.a.Entries {
159 e := &ar.a.Entries[i]
160 action(e)
161 }
162 }
163
164
165 func listEntry(e *archive.Entry, verbose bool) {
166 if verbose {
167 fmt.Fprintf(stdout, "%s\n", e.String())
168 } else {
169 fmt.Fprintf(stdout, "%s\n", e.Name)
170 }
171 }
172
173
174 func (ar *Archive) output(e *archive.Entry, w io.Writer) {
175 r := io.NewSectionReader(ar.a.File(), e.Offset, e.Size)
176 n, err := io.Copy(w, r)
177 if err != nil {
178 log.Fatal(err)
179 }
180 if n != e.Size {
181 log.Fatal("short file")
182 }
183 }
184
185
186
187 func (ar *Archive) match(e *archive.Entry) bool {
188 if ar.matchAll {
189 return true
190 }
191 for i, name := range ar.files {
192 if e.Name == name {
193 copy(ar.files[i:], ar.files[i+1:])
194 ar.files = ar.files[:len(ar.files)-1]
195 return true
196 }
197 }
198 return false
199 }
200
201
202
203
204 func (ar *Archive) addFiles() {
205 if len(ar.files) == 0 {
206 usage()
207 }
208 for _, file := range ar.files {
209 if verbose {
210 fmt.Printf("%s\n", file)
211 }
212
213 f, err := os.Open(file)
214 if err != nil {
215 log.Fatal(err)
216 }
217 aro, err := archive.Parse(f, false)
218 if err != nil || !isGoCompilerObjFile(aro) {
219 f.Seek(0, io.SeekStart)
220 ar.addFile(f)
221 goto close
222 }
223
224 for _, e := range aro.Entries {
225 if e.Type != archive.EntryGoObj || e.Name != "_go_.o" {
226 continue
227 }
228 ar.a.AddEntry(archive.EntryGoObj, filepath.Base(file), 0, 0, 0, 0644, e.Size, io.NewSectionReader(f, e.Offset, e.Size))
229 }
230 close:
231 f.Close()
232 }
233 ar.files = nil
234 }
235
236
237 type FileLike interface {
238 Name() string
239 Stat() (fs.FileInfo, error)
240 Read([]byte) (int, error)
241 Close() error
242 }
243
244
245 func (ar *Archive) addFile(fd FileLike) {
246
247
248 info, err := fd.Stat()
249 if err != nil {
250 log.Fatal(err)
251 }
252
253 mtime := int64(0)
254 uid := 0
255 gid := 0
256 ar.a.AddEntry(archive.EntryNativeObj, info.Name(), mtime, uid, gid, info.Mode(), info.Size(), fd)
257 }
258
259
260
261
262 func (ar *Archive) addPkgdef() {
263 done := false
264 for _, file := range ar.files {
265 f, err := os.Open(file)
266 if err != nil {
267 log.Fatal(err)
268 }
269 aro, err := archive.Parse(f, false)
270 if err != nil || !isGoCompilerObjFile(aro) {
271 goto close
272 }
273
274 for _, e := range aro.Entries {
275 if e.Type != archive.EntryPkgDef {
276 continue
277 }
278 if verbose {
279 fmt.Printf("__.PKGDEF # %s\n", file)
280 }
281 ar.a.AddEntry(archive.EntryPkgDef, "__.PKGDEF", 0, 0, 0, 0644, e.Size, io.NewSectionReader(f, e.Offset, e.Size))
282 done = true
283 }
284 close:
285 f.Close()
286 if done {
287 break
288 }
289 }
290 }
291
292
293
294
295 var stdout io.Writer = os.Stdout
296
297
298 func (ar *Archive) printContents(e *archive.Entry) {
299 ar.extractContents1(e, stdout)
300 }
301
302
303 func (ar *Archive) tableOfContents(e *archive.Entry) {
304 if ar.match(e) {
305 listEntry(e, verbose)
306 }
307 }
308
309
310 func (ar *Archive) extractContents(e *archive.Entry) {
311 ar.extractContents1(e, nil)
312 }
313
314 func (ar *Archive) extractContents1(e *archive.Entry, out io.Writer) {
315 if ar.match(e) {
316 if verbose {
317 listEntry(e, false)
318 }
319 if out == nil {
320 f, err := os.OpenFile(e.Name, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, 0444 )
321 if err != nil {
322 log.Fatal(err)
323 }
324 defer f.Close()
325 out = f
326 }
327 ar.output(e, out)
328 }
329 }
330
331
332
333
334 func isGoCompilerObjFile(a *archive.Archive) bool {
335 switch len(a.Entries) {
336 case 1:
337 return (a.Entries[0].Type == archive.EntryGoObj && a.Entries[0].Name == "_go_.o") ||
338 (a.Entries[0].Type == archive.EntryPkgDef && a.Entries[0].Name == "__.PKGDEF")
339 case 2:
340 var foundPkgDef, foundGo bool
341 for _, e := range a.Entries {
342 if e.Type == archive.EntryPkgDef && e.Name == "__.PKGDEF" {
343 foundPkgDef = true
344 }
345 if e.Type == archive.EntryGoObj && e.Name == "_go_.o" {
346 foundGo = true
347 }
348 }
349 return foundPkgDef && foundGo
350 default:
351 return false
352 }
353 }
354
View as plain text