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

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: Fixing for maruel's review. 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 // 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 }
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698