Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(598)

Side by Side Diff: common/archive/ar/reader.go

Issue 2043623002: luci-go: Tools for working with BSD style ar archives. (Closed) Base URL: https://github.com/luci/luci-go@master
Patch Set: Created 4 years, 6 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
OLDNEW
(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 /**
6 * Read an ar archive file.
M-A Ruel 2016/06/06 23:01:05 Do not create 2 package comments
mithro 2016/06/07 12:28:52 So this comment is somehow associated with the pac
7 */
8 package ar
9
10 import (
11 "bytes"
12 "errors"
13 "fmt"
14 "io"
15 "os"
16 "time"
17 )
18
19 type arFileInfo struct {
20 // os.FileInfo parts
21 name string
22 size int64
23 mode uint32
24 modtime uint64
25 // Extra parts
26 uid int
27 gid int
28 }
29
30 // os.FileInfo interface
31 func (fi arFileInfo) Name() string { return fi.name }
M-A Ruel 2016/06/06 23:01:05 use pointer methods otherwise this copies the stru
mithro 2016/06/07 12:28:52 Done.
32 func (fi arFileInfo) Size() int64 { return fi.size }
33 func (fi arFileInfo) Mode() os.FileMode { return os.FileMode(fi.mode) }
34 func (fi arFileInfo) ModTime() time.Time { return time.Unix(int64(fi.modtime), 0 ) }
35 func (fi arFileInfo) IsDir() bool { return fi.Mode().IsDir() }
36 func (fi arFileInfo) Sys() interface{} { return fi }
37
38 // Extra
39 func (fi arFileInfo) UserId() int { return fi.uid }
40 func (fi arFileInfo) GroupId() int { return fi.gid }
41
42 var (
43 ErrHeader = errors.New("archive/ar: invalid ar header")
44 )
45
46 type ReaderStage uint
47
48 const (
49 READ_HEADER ReaderStage = iota
50 READ_BODY = iota
M-A Ruel 2016/06/06 23:01:05 just iota for the first
mithro 2016/06/07 12:28:52 Any idea why gofmt doesn't pick that one up?
M-A Ruel 2016/06/08 20:25:31 I mean you can remove the complete "= iota". It's
51 READ_CLOSED = iota
52 )
53
54 type Reader struct {
55 stage ReaderStage
56 r io.Reader
57 bytesrequired int64
58 needspadding bool
59 }
60
61 func NewReader(r io.Reader) (*Reader, error) {
62 reader := Reader{r: r, bytesrequired: 0, needspadding: false}
63 err := reader.checkBytes("header", []byte("!<arch>\n"))
64 if err != nil {
65 return nil, err
66 } else {
67 return &reader, nil
68 }
69 }
70
71 func (ar *Reader) checkBytes(name string, str []byte) error {
72 buffer := make([]byte, len(str))
73
74 count, err := io.ReadFull(ar.r, buffer)
75 if err != nil {
76 return err
77 }
78
79 if count != len(buffer) {
80 return errors.New(fmt.Sprintf("%s: Not enough data read (only %d , needed %d)", name, count, len(buffer)))
81 }
82
83 if bytes.Equal(str, buffer) {
84 return nil
85 } else {
86 return errors.New(fmt.Sprintf("%s: error in bytes (wanted: %v go t: %v)", name, buffer, str))
87 }
88 }
89
90 func (ar *Reader) Close() error {
91 switch ar.stage {
92 case READ_HEADER:
93 // Good
94 case READ_BODY:
95 return errors.New("Usage error, reading a file.")
96 case READ_CLOSED:
97 return errors.New("Usage error, archive already closed.")
98 default:
99 panic(fmt.Sprintf("Unknown reader mode: %d", ar.stage))
100 }
101 //ar.r.Close()
102 ar.stage = READ_CLOSED
103 return nil
104 }
105
106 func (ar *Reader) readBytes(numbytes int64) error {
107 if numbytes > ar.bytesrequired {
108 return errors.New(fmt.Sprintf("To much data read! Needed %d, got %d", ar.bytesrequired, numbytes))
109 }
110
111 ar.bytesrequired -= numbytes
112 if ar.bytesrequired != 0 {
113 return nil
114 }
115
116 // Padding to 16bit boundary
117 if ar.needspadding {
118 err := ar.checkBytes("padding", []byte("\n"))
119 if err != nil {
120 return err
121 }
122 ar.needspadding = false
123 }
124 ar.stage = READ_HEADER
125 return nil
126 }
127
128 // Check you can write bytes to the ar at this moment.
129 func (ar *Reader) checkRead() error {
130 switch ar.stage {
131 case READ_HEADER:
132 return errors.New("Usage error, need to read header first.")
133 // Good
134 case READ_BODY:
135 return nil
136 case READ_CLOSED:
137 return errors.New("Usage error, archive closed.")
138 default:
139 panic(fmt.Sprintf("Unknown reader mode: %d", ar.stage))
140 }
141 }
142
143 // Check we have finished writing bytes
144 func (ar *Reader) checkFinished() error {
145 if ar.bytesrequired != 0 {
146 return errors.New(fmt.Sprintf("Didn't read enough bytes %d still needed!", ar.bytesrequired))
147 }
148 return nil
149 }
150
151 func (ar *Reader) readPartial(name string, data []byte) error {
152 err := ar.checkRead()
153 if err != nil {
154 return err
155 }
156
157 datalen := int64(len(data))
158 if datalen > ar.bytesrequired {
159 return errors.New(fmt.Sprintf("To much data! Wanted %d, but had %d", ar.bytesrequired, datalen))
160 }
161
162 count, err := io.ReadFull(ar.r, data)
163 ar.readBytes(int64(count))
164 return nil
165 }
166
167 func (ar *Reader) readHeaderBytes(name string, bytes int, formatstr string) (int 64, error) {
168 data := make([]byte, bytes)
169 _, err := io.ReadFull(ar.r, data)
170 if err != nil {
171 return -1, err
172 }
173
174 var output int64 = 0
175 _, err = fmt.Sscanf(string(data), formatstr, &output)
176 if err != nil {
177 return -1, err
178 }
179
180 if output <= 0 {
181 return -1, errors.New(fmt.Sprintf("%s: bad value %d", name, outp ut))
182 }
183 return output, nil
184 }
185
186 func (ar *Reader) readHeader() (*arFileInfo, error) {
187 switch ar.stage {
188 case READ_HEADER:
189 // Good
190 case READ_BODY:
191 return nil, errors.New("Usage error, already writing a file.")
192 case READ_CLOSED:
193 return nil, errors.New("Usage error, archive closed.")
194 default:
195 panic(fmt.Sprintf("Unknown writer mode: %d", ar.stage))
196 }
197
198 var fi arFileInfo
199
200 // File name length prefixed with '#1/' (BSD variant), 16 bytes
201 namelen, err := ar.readHeaderBytes("filename length", 16, "#1/%13d")
202 if err != nil {
203 return nil, err
204 }
205
206 // Modtime, 12 bytes
207 modtime, err := ar.readHeaderBytes("modtime", 12, "%12d")
208 if err != nil {
209 return nil, err
210 }
211 fi.modtime = uint64(modtime)
212
213 // Owner ID, 6 bytes
214 ownerid, err := ar.readHeaderBytes("ownerid", 6, "%6d")
215 if err != nil {
216 return nil, err
217 }
218 fi.uid = int(ownerid)
219
220 // Group ID, 6 bytes
221 groupid, err := ar.readHeaderBytes("groupid", 6, "%6d")
222 if err != nil {
223 return nil, err
224 }
225 fi.gid = int(groupid)
226
227 // File mode, 8 bytes
228 filemod, err := ar.readHeaderBytes("groupid", 8, "%8o")
229 if err != nil {
230 return nil, err
231 }
232 fi.mode = uint32(filemod)
233
234 // File size, 10 bytes
235 size, err := ar.readHeaderBytes("datasize", 10, "%10d")
236 if err != nil {
237 return nil, err
238 }
239 fi.size = size - namelen
240
241 ar.stage = READ_BODY
242 ar.bytesrequired = size
243 ar.needspadding = (ar.bytesrequired%2 != 0)
244
245 // File magic, 2 bytes
246 err = ar.checkBytes("filemagic", []byte("\x60\n"))
247 if err != nil {
248 return nil, err
249 }
250
251 // Filename - BSD variant
252 filename := make([]byte, namelen)
253 err = ar.readPartial("filename", filename)
254 if err != nil {
255 return nil, err
256 }
257 fi.name = string(filename)
258
259 return &fi, nil
260 }
261
262 func (ar *Reader) Read(b []byte) (n int, err error) {
263 err = ar.readPartial("data", b)
264 if err != nil {
265 return -1, err
266 }
267 err = ar.checkFinished()
268 if err != nil {
269 return -1, err
270 }
271 return len(b), nil
272 }
273 func (ar *Reader) Next() (*arFileInfo, error) {
274 return ar.readHeader()
275 }
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698