OLD | NEW |
---|---|
(Empty) | |
1 // Copyright 2016 The LUCI Authors. All rights reserved. | |
2 // Use of this source code is governed under the Apache License, Version 2.0 | |
3 // that can be found in the LICENSE file. | |
4 | |
5 // Read an ar file with BSD formatted file names. | |
6 | |
7 package ar | |
8 | |
9 import ( | |
10 "bytes" | |
11 "errors" | |
12 "fmt" | |
13 "io" | |
14 "log" | |
15 "os" | |
16 "time" | |
17 ) | |
18 | |
19 type ArFileInfo interface { | |
20 os.FileInfo | |
21 UserId() int | |
22 GroupId() int | |
23 } | |
24 | |
25 type arFileInfoData struct { | |
26 // os.FileInfo parts | |
27 name string | |
28 size int64 | |
29 mode uint32 | |
30 modtime uint64 | |
31 // Extra parts | |
32 uid int | |
33 gid int | |
34 } | |
35 | |
36 // os.FileInfo interface | |
37 func (fi *arFileInfoData) Name() string { return fi.name } | |
38 func (fi *arFileInfoData) Size() int64 { return fi.size } | |
39 func (fi *arFileInfoData) Mode() os.FileMode { return os.FileMode(fi.mode) } | |
40 func (fi *arFileInfoData) ModTime() time.Time { return time.Unix(int64(fi.modtim e), 0) } | |
41 func (fi *arFileInfoData) IsDir() bool { return fi.Mode().IsDir() } | |
42 func (fi *arFileInfoData) Sys() interface{} { return fi } | |
43 | |
44 // Extra | |
45 func (fi *arFileInfoData) UserId() int { return fi.uid } | |
46 func (fi *arFileInfoData) GroupId() int { return fi.gid } | |
47 | |
48 type readerStage uint | |
49 | |
50 const ( | |
51 READ_HEADER readerStage = iota | |
M-A Ruel
2016/06/10 17:51:10
don't export the const either
mithro
2016/06/14 10:57:51
Done and now obsolete.
| |
52 READ_BODY | |
53 READ_CLOSED | |
M-A Ruel
2016/06/10 17:51:11
Not needed, can detect with r == nil
mithro
2016/06/14 10:57:51
Done and now obsolete.
| |
54 ) | |
55 | |
56 type Reader struct { | |
57 stage readerStage | |
58 r io.Reader | |
59 bytesrequired int64 | |
M-A Ruel
2016/06/10 17:51:10
streamSizeRemaining would probably be more underst
mithro
2016/06/14 10:57:52
Done and now obsolete.
| |
60 needspadding bool | |
61 } | |
62 | |
63 func NewReader(r io.Reader) (*Reader, error) { | |
64 reader := Reader{r: r, stage: READ_HEADER} | |
65 err := reader.checkBytes("header", []byte("!<arch>\n")) | |
66 if err != nil { | |
M-A Ruel
2016/06/10 17:51:10
merge with line 65
mithro
2016/06/14 10:57:51
Done.
| |
67 return nil, err | |
68 } | |
69 return &reader, nil | |
70 } | |
71 | |
72 func (ar *Reader) checkBytes(name string, str []byte) error { | |
73 buffer := make([]byte, len(str)) | |
74 | |
75 _, err := io.ReadFull(ar.r, buffer) | |
M-A Ruel
2016/06/10 17:51:10
merge with line 76
mithro
2016/06/14 10:57:51
Done.
| |
76 if err != nil { | |
77 return fmt.Errorf("%s: error in reading bytes (%v)", name, err) | |
78 } | |
79 | |
80 if bytes.Equal(str, buffer) { | |
81 return nil | |
82 } else { | |
M-A Ruel
2016/06/10 17:51:10
no need for else, I'd recommend to to reverse the
mithro
2016/06/14 10:57:51
Done.
| |
83 return fmt.Errorf("%s: error in bytes (wanted: %v got: %v)", nam e, buffer, str) | |
84 } | |
85 } | |
86 | |
87 func (ar *Reader) Close() error { | |
88 switch ar.stage { | |
89 case READ_HEADER: | |
90 // Good | |
91 case READ_BODY: | |
92 return errors.New("usage error, reading a file.") | |
M-A Ruel
2016/06/10 17:51:10
Why is it a problem?
mithro
2016/06/14 10:57:51
I guess in the reading case it isn't.
| |
93 case READ_CLOSED: | |
94 return errors.New("usage error, archive already closed.") | |
M-A Ruel
2016/06/10 17:51:11
I don't think it's worth reporting to the user.
mithro
2016/06/14 10:57:52
This is how other things in Go seem to behave. The
| |
95 default: | |
96 log.Fatalf("unknown reader mode: %d", ar.stage) | |
97 } | |
98 ar.stage = READ_CLOSED | |
M-A Ruel
2016/06/10 17:51:10
When ar.r is nil, it's closed.
So stage becomes a
mithro
2016/06/14 10:57:52
Done and now obsolete.
| |
99 ar.r = nil | |
100 return nil | |
101 } | |
102 | |
103 func (ar *Reader) completeReadBytes(numbytes int64) error { | |
M-A Ruel
2016/06/10 17:51:10
So this function is actually readPadding(), right?
mithro
2016/06/14 10:57:52
Now obsolete.
| |
104 if numbytes > ar.bytesrequired { | |
105 return fmt.Errorf("to much data read, needed %d, got %d", ar.byt esrequired, numbytes) | |
106 } | |
107 | |
108 ar.bytesrequired -= numbytes | |
109 if ar.bytesrequired != 0 { | |
M-A Ruel
2016/06/10 17:51:10
when can this happen?
mithro
2016/06/14 10:57:52
When a partial read is used.
| |
110 return nil | |
111 } | |
112 | |
113 // Padding to 16bit boundary | |
114 if ar.needspadding { | |
115 err := ar.checkBytes("padding", []byte("\n")) | |
M-A Ruel
2016/06/10 17:51:11
merge with next line
[]byte{'\n'}
mithro
2016/06/14 10:57:52
Done.
| |
116 if err != nil { | |
117 return err | |
118 } | |
119 ar.needspadding = false | |
120 } | |
121 ar.stage = READ_HEADER | |
122 return nil | |
123 } | |
124 | |
125 // Check we have finished writing bytes | |
M-A Ruel
2016/06/10 17:51:10
writing?
mithro
2016/06/14 10:57:52
Done.
| |
126 func (ar *Reader) checkFinished() error { | |
127 if ar.bytesrequired != 0 { | |
128 return fmt.Errorf("didn't read enough %d bytes still needed", ar .bytesrequired) | |
129 } | |
130 return nil | |
131 } | |
132 | |
133 func (ar *Reader) readPartial(name string, data []byte) error { | |
134 // Check you can read bytes from the ar at this moment. | |
135 switch ar.stage { | |
136 case READ_HEADER: | |
137 return errors.New("usage error, need to read header first") | |
M-A Ruel
2016/06/10 17:51:10
Keep in mind it's an internal function, it should
mithro
2016/06/14 10:57:51
Now obsolete.
| |
138 case READ_BODY: | |
139 // Good | |
140 case READ_CLOSED: | |
141 return errors.New("usage error, archive closed") | |
142 default: | |
143 log.Fatalf("unknown reader mode: %d", ar.stage) | |
144 } | |
145 | |
146 datalen := int64(len(data)) | |
M-A Ruel
2016/06/10 17:51:10
inline datalen
mithro
2016/06/14 10:57:51
Done.
| |
147 if datalen > ar.bytesrequired { | |
148 return fmt.Errorf("to much data, wanted %d, but had %d", ar.byte srequired, datalen) | |
149 } | |
150 | |
151 count, err := io.ReadFull(ar.r, data) | |
152 if err != nil { | |
153 return err | |
154 } | |
155 ar.completeReadBytes(int64(count)) | |
156 return nil | |
157 } | |
158 | |
159 func (ar *Reader) readHeaderBytes(name string, bytes int, formatstr string) (int 64, error) { | |
M-A Ruel
2016/06/10 17:51:10
readHeaderInt64() ?
mithro
2016/06/14 10:57:51
Done.
| |
160 data := make([]byte, bytes) | |
161 _, err := io.ReadFull(ar.r, data) | |
162 if err != nil { | |
163 return -1, err | |
164 } | |
165 | |
166 var output int64 = 0 | |
M-A Ruel
2016/06/10 17:51:10
=0 is not needed
mithro
2016/06/14 10:57:52
Done.
| |
167 _, err = fmt.Sscanf(string(data), formatstr, &output) | |
M-A Ruel
2016/06/10 17:51:10
merge with next line
mithro
2016/06/14 10:57:51
Done.
| |
168 if err != nil { | |
169 return -1, err | |
170 } | |
171 | |
172 if output <= 0 { | |
173 return -1, fmt.Errorf("%s: bad value %d", name, output) | |
174 } | |
175 return output, nil | |
176 } | |
177 | |
178 func (ar *Reader) readHeader() (*arFileInfoData, error) { | |
M-A Ruel
2016/06/10 17:51:10
Inline into Next()
mithro
2016/06/14 10:57:51
Done.
| |
179 switch ar.stage { | |
180 case READ_HEADER: | |
181 // Good | |
182 case READ_BODY: | |
183 return nil, errors.New("usage error, already writing a file") | |
184 case READ_CLOSED: | |
185 return nil, errors.New("usage error, archive closed") | |
186 default: | |
187 log.Fatalf("unknown reader mode: %d", ar.stage) | |
188 } | |
189 | |
190 var fi arFileInfoData | |
191 | |
192 // File name length prefixed with '#1/' (BSD variant), 16 bytes | |
193 namelen, err := ar.readHeaderBytes("filename length", 16, "#1/%13d") | |
194 if err != nil { | |
195 return nil, err | |
196 } | |
197 | |
198 // Modtime, 12 bytes | |
199 modtime, err := ar.readHeaderBytes("modtime", 12, "%12d") | |
200 if err != nil { | |
201 return nil, err | |
202 } | |
203 fi.modtime = uint64(modtime) | |
204 | |
205 // Owner ID, 6 bytes | |
206 ownerid, err := ar.readHeaderBytes("ownerid", 6, "%6d") | |
207 if err != nil { | |
208 return nil, err | |
209 } | |
210 fi.uid = int(ownerid) | |
211 | |
212 // Group ID, 6 bytes | |
213 groupid, err := ar.readHeaderBytes("groupid", 6, "%6d") | |
214 if err != nil { | |
215 return nil, err | |
216 } | |
217 fi.gid = int(groupid) | |
218 | |
219 // File mode, 8 bytes | |
220 filemod, err := ar.readHeaderBytes("filemod", 8, "%8o") | |
221 if err != nil { | |
222 return nil, err | |
223 } | |
224 fi.mode = uint32(filemod) | |
225 | |
226 // File size, 10 bytes | |
227 size, err := ar.readHeaderBytes("datasize", 10, "%10d") | |
228 if err != nil { | |
229 return nil, err | |
230 } | |
231 fi.size = size - namelen | |
232 | |
233 // File magic, 2 bytes | |
234 err = ar.checkBytes("filemagic", []byte("\x60\n")) | |
M-A Ruel
2016/06/10 17:51:11
create a byte array directly
merge with next line
mithro
2016/06/14 10:57:51
Done.
| |
235 if err != nil { | |
236 return nil, err | |
237 } | |
238 | |
239 ar.stage = READ_BODY | |
240 ar.bytesrequired = size | |
241 ar.needspadding = (ar.bytesrequired%2 != 0) | |
242 | |
243 // Filename - BSD variant | |
244 filename := make([]byte, namelen) | |
245 err = ar.readPartial("filename", filename) | |
M-A Ruel
2016/06/10 17:51:10
merge
mithro
2016/06/14 10:57:52
Done.
| |
246 if err != nil { | |
247 return nil, err | |
248 } | |
249 fi.name = string(filename) | |
M-A Ruel
2016/06/10 17:51:10
encoding is assumed to be utf8?
mithro
2016/06/14 10:57:52
The "spec" seems to indicate it should be ASCII bu
| |
250 | |
251 return &fi, nil | |
252 } | |
253 | |
254 func (ar *Reader) Read(b []byte) (n int, err error) { | |
255 err = ar.readPartial("data", b) | |
M-A Ruel
2016/06/10 17:51:11
I'd prefer to not use named return values
merge
mithro
2016/06/14 10:57:52
I actually didn't even know that was possible! Fix
| |
256 if err != nil { | |
257 return -1, err | |
258 } | |
259 err = ar.checkFinished() | |
260 if err != nil { | |
261 return -1, err | |
262 } | |
263 return len(b), nil | |
264 } | |
265 | |
266 func (ar *Reader) Next() (ArFileInfo, error) { | |
267 return ar.readHeader() | |
268 } | |
OLD | NEW |