forked from insomniacslk/u-root
-
Notifications
You must be signed in to change notification settings - Fork 1
/
cpio.go
139 lines (123 loc) · 3.06 KB
/
cpio.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
// Copyright 2013-2020 the u-root Authors. All rights reserved
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
// cpio operates on cpio files using a cpio package
// It only implements basic cpio options.
//
//
// Synopsis:
// cpio
//
// Description:
//
// Options:
// o: output an archive to stdout given a pattern
// i: output files from a stdin stream
// t: print table of contents
// -v: debug prints
//
// Bugs: in i mode, it can't use non-seekable stdin, i.e. a pipe. Yep, this sucks.
// But if we implement seek on such things, we have to do it by reading, which
// really sucks. It's doable, we'll do it if we have to, but for now I'd like
// to avoid the complexity. cpio is a 40 year old concept. If you want something
// better, see ../archive which has a VTOC and separates data from metadata (unlike cpio).
// We could test for ESPIPE and fix it that way ... later.
package main
import (
"bufio"
"flag"
"fmt"
"io"
"log"
"os"
"github.com/u-root/u-root/pkg/cpio"
)
var (
debug = func(string, ...interface{}) {}
d = flag.Bool("v", false, "Debug prints")
format = flag.String("H", "newc", "format")
)
func usage() {
log.Fatalf("Usage: cpio")
}
func main() {
flag.Parse()
if *d {
debug = log.Printf
}
a := flag.Args()
debug("Args %v", a)
if len(a) < 1 {
usage()
}
op := a[0]
archiver, err := cpio.Format(*format)
if err != nil {
log.Fatalf("Format %q not supported: %v", *format, err)
}
switch op {
case "i":
var inums map[uint64]string
inums = make(map[uint64]string)
rr := archiver.Reader(os.Stdin)
for {
rec, err := rr.ReadRecord()
if err == io.EOF {
break
}
if err != nil {
log.Fatalf("error reading records: %v", err)
}
debug("Creating %s\n", rec)
// A file with zero size could be a hard link to another file
// in the archive. The file with contents always comes first.
if rec.Info.FileSize == 0 {
if _, ok := inums[rec.Info.Ino]; ok {
err := os.Link(inums[rec.Info.Ino], rec.Name)
if err != nil {
log.Fatal(err)
}
continue
}
}
inums[rec.Info.Ino] = rec.Name
if err := cpio.CreateFile(rec); err != nil {
log.Printf("Creating %q failed: %v", rec.Name, err)
}
}
case "o":
rw := archiver.Writer(os.Stdout)
cr := cpio.NewRecorder()
scanner := bufio.NewScanner(os.Stdin)
for scanner.Scan() {
name := scanner.Text()
rec, err := cr.GetRecord(name)
if err != nil {
log.Fatalf("Getting record of %q failed: %v", name, err)
}
if err := rw.WriteRecord(rec); err != nil {
log.Fatalf("Writing record %q failed: %v", name, err)
}
}
if err := scanner.Err(); err != nil {
log.Fatalf("Error reading stdin: %v", err)
}
if err := cpio.WriteTrailer(rw); err != nil {
log.Fatalf("Error writing trailer record: %v", err)
}
case "t":
rr := archiver.Reader(os.Stdin)
for {
rec, err := rr.ReadRecord()
if err == io.EOF {
break
}
if err != nil {
log.Fatalf("error reading records: %v", err)
}
fmt.Println(rec)
}
default:
usage()
}
}