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