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

Side by Side Diff: common/archive/ar/writer.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 // Write an ar archive file with BSD style filenames.
6
7 package ar
8
9 import (
10 "errors"
11 "fmt"
12 "io"
13 "log"
14 "os"
15 )
16
17 const DEFAULT_MODTIME = 1447140471
18 const DEFAULT_USER = 1000
19 const DEFAULT_GROUP = 1000
20 const DEFAULT_MODE = 0100640 // 100640 -- Octal
21
22 type writerStage uint
23
24 const (
25 WRITE_HEADER writerStage = iota
M-A Ruel 2016/06/10 17:51:11 Use golang style; https://github.com/golang/go/wik
mithro 2016/06/14 10:57:52 Done.
26 WRITE_BODY
27 WRITE_CLOSED
28 )
29
30 type Writer struct {
31 w io.Writer
32 stage writerStage
M-A Ruel 2016/06/10 17:51:11 make it a bool, unless you want to delay the "!<ar
mithro 2016/06/14 10:57:53 Done.
33
34 bytesrequired int64
35 needspadding bool
36 }
37
38 func NewWriter(w io.Writer) *Writer {
39 io.WriteString(w, "!<arch>\n")
M-A Ruel 2016/06/10 17:51:11 do not ignore the error here or lazy write the hea
mithro 2016/06/14 10:57:52 Done.
40 return &Writer{w: w, stage: WRITE_HEADER}
41 }
42
43 func (aw *Writer) Close() error {
44 switch aw.stage {
M-A Ruel 2016/06/10 17:51:11 same comment as reader, just flush the file correc
mithro 2016/06/14 10:57:52 Most things report an error on a double Close() (a
45 case WRITE_HEADER:
46 // Good
47 case WRITE_BODY:
48 return errors.New("usage error, writing a file")
49 case WRITE_CLOSED:
50 return errors.New("usage error, archive already closed")
51 default:
52 log.Fatalf("unknown writer mode: %d", aw.stage)
53 }
54 aw.stage = WRITE_CLOSED
55 aw.w = nil
56 return nil
57 }
58
59 func (aw *Writer) wroteBytes(numbytes int64) error {
60 if numbytes > aw.bytesrequired {
61 log.Fatalf("to much data written, needed %d, got %d", aw.bytesre quired, numbytes)
62 }
63
64 aw.bytesrequired -= numbytes
65 if aw.bytesrequired != 0 {
66 return nil
67 }
68
69 // Padding to 16bit boundary
70 if aw.needspadding {
71 io.WriteString(aw.w, "\n")
72 aw.needspadding = false
73 }
74 aw.stage = WRITE_HEADER
75 return nil
76 }
77
78 // Check you can write bytes to the ar at this moment.
M-A Ruel 2016/06/10 17:51:11 // canWriteContent returns nil if the stream is in
mithro 2016/06/14 10:57:53 Done.
79 func (aw *Writer) checkWrite() error {
80 switch aw.stage {
M-A Ruel 2016/06/10 17:51:11 if a.r == nil { return errors.New(... } if a.inH
mithro 2016/06/14 10:57:52 I still don't like this change, I personally think
81 case WRITE_HEADER:
82 return errors.New("usage error, need to write header first")
83 // Good
84 case WRITE_BODY:
85 return nil
86 case WRITE_CLOSED:
87 return errors.New("usage error, archive closed")
88 default:
89 log.Fatalf("unknown writer mode: %d", aw.stage)
90 }
91 return nil
92 }
93
94 // Check we have finished writing bytes
95 func (aw *Writer) checkFinished() {
96 if aw.bytesrequired != 0 {
97 log.Fatalf("didn't write enough bytes %d still needed, archive c orrupted", aw.bytesrequired)
M-A Ruel 2016/06/10 17:51:11 return errors.New() then have the call sites retu
mithro 2016/06/14 10:57:52 Kinda done - see my comment previously.
98 }
99 }
100
101 func (aw *Writer) writePartial(data []byte) error {
102 err := aw.checkWrite()
M-A Ruel 2016/06/10 17:51:11 similar to reader, inline here and at other places
mithro 2016/06/14 10:57:52 Done.
103 if err != nil {
104 return err
105 }
106
107 datalen := int64(len(data))
108 if datalen > aw.bytesrequired {
109 return fmt.Errorf("to much data, needed %d, got %d", aw.bytesreq uired, datalen)
110 }
111
112 aw.w.Write(data)
113 aw.wroteBytes(datalen)
114 return nil
115 }
116
117 func (aw *Writer) WriteReader(data io.Reader) error {
M-A Ruel 2016/06/10 17:51:11 https://golang.org/pkg/io/#ReaderFrom would probab
mithro 2016/06/14 10:57:52 I agree that interface looks like what this should
118 err := aw.checkWrite()
119 if err != nil {
120 return err
121 }
122
123 count, err := io.Copy(aw.w, data)
124 if err != nil {
125 log.Fatalf("error while copying (%s), archive is probably corrup ted", err)
126 }
127 aw.wroteBytes(count)
128 aw.checkFinished()
129
130 return nil
131 }
132
133 func (aw *Writer) WriteBytes(data []byte) error {
M-A Ruel 2016/06/10 17:51:11 https://golang.org/pkg/io/#ReaderFrom would probab
mithro 2016/06/14 10:57:53 io.Copy() seems to use this function under the hoo
134 err := aw.checkWrite()
135 if err != nil {
136 return err
137 }
138
139 datalen := int64(len(data))
140 if datalen != aw.bytesrequired {
141 return fmt.Errorf("wrong amount of data, needed %d, got %d", aw. bytesrequired, datalen)
142 }
143
144 aw.writePartial(data)
M-A Ruel 2016/06/10 17:51:11 return value is ignored
mithro 2016/06/14 10:57:53 Fixed.
145 aw.checkFinished()
146 return nil
147 }
148
149 func (aw *Writer) writeHeaderBytes(name string, size int64, modtime uint64, owne rid uint, groupid uint, filemod uint) error {
M-A Ruel 2016/06/10 17:51:11 writeHeaderInt or something like that?
mithro 2016/06/14 10:57:52 Went with writeHeaderInternal
150 switch aw.stage {
151 case WRITE_HEADER:
152 // Good
153 case WRITE_BODY:
154 return errors.New("usage error, already writing a file.")
155 case WRITE_CLOSED:
156 return errors.New("usage error, archive closed.")
157 default:
158 log.Fatalf("unknown writer mode: %d", aw.stage)
159 }
160
161 // File name length prefixed with '#1/' (BSD variant), 16 bytes
162 fmt.Fprintf(aw.w, "#1/%-13d", len(name))
M-A Ruel 2016/06/10 17:51:11 do not ignore the return value
mithro 2016/06/14 10:57:52 Done.
163
164 // Modtime, 12 bytes
165 fmt.Fprintf(aw.w, "%-12d", modtime)
166
167 // Owner ID, 6 bytes
168 fmt.Fprintf(aw.w, "%-6d", ownerid)
169
170 // Group ID, 6 bytes
171 fmt.Fprintf(aw.w, "%-6d", groupid)
172
173 // File mode, 8 bytes
174 fmt.Fprintf(aw.w, "%-8o", filemod)
175
176 // In BSD variant, file size includes the filename length
177 aw.bytesrequired = int64(len(name)) + size
178
179 // File size, 10 bytes
180 fmt.Fprintf(aw.w, "%-10d", aw.bytesrequired)
181
182 // File magic, 2 bytes
183 io.WriteString(aw.w, "\x60\n")
184
185 aw.stage = WRITE_BODY
186 aw.needspadding = (aw.bytesrequired%2 != 0)
187
188 // Filename - BSD variant
189 return aw.writePartial([]byte(name))
190 }
191
192 func (aw *Writer) WriteHeaderDefault(name string, size int64) error {
193 return aw.writeHeaderBytes(name, size, 1447140471, DEFAULT_USER, DEFAULT _GROUP, DEFAULT_MODE)
194 }
195
196 func (aw *Writer) WriteHeader(stat os.FileInfo) error {
197 if stat.IsDir() {
198 return errors.New("only work with files, not directories")
199 }
200
201 mode := stat.Mode()
202 if mode&os.ModeSymlink == os.ModeSymlink {
203 return errors.New("only work with files, not symlinks")
204 }
205
206 /* FIXME: Should we also exclude other "special" files?
207 if (stat.Mode().ModeType != 0) {
208 return &argError{stat, "Only work with plain files."}
209 }
210 */
211
212 // FIXME: Where do we get user/group from - they don't appear to be in G o's Mode() object?
M-A Ruel 2016/06/10 17:51:11 Don't bother, I'd prefer you to not claim to suppo
mithro 2016/06/14 10:57:53 Done.
213 return aw.writeHeaderBytes(stat.Name(), stat.Size(), uint64(stat.ModTime ().Unix()), DEFAULT_USER, DEFAULT_GROUP, uint(mode&os.ModePerm))
214 }
215
216 func (aw *Writer) Add(name string, data []byte) error {
217 err := aw.WriteHeaderDefault(name, int64(len(data)))
218 if err != nil {
219 return err
220 }
221
222 return aw.WriteBytes(data)
223 }
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698