 Chromium Code Reviews
 Chromium Code Reviews Issue 2043623002:
  luci-go: Tools for working with BSD style ar archives.  (Closed) 
  Base URL: https://github.com/luci/luci-go@master
    
  
    Issue 2043623002:
  luci-go: Tools for working with BSD style ar archives.  (Closed) 
  Base URL: https://github.com/luci/luci-go@master| 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 /** | |
| 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 } | |
| OLD | NEW |