1
2
3
4
5 package tar
6
7
8
9
10 import (
11 "io"
12 "os"
13 "strconv"
14 )
15
16 var (
17 ErrWriteTooLong = os.NewError("write too long")
18 ErrFieldTooLong = os.NewError("header field too long")
19 ErrWriteAfterClose = os.NewError("write after close")
20 )
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37 type Writer struct {
38 w io.Writer
39 err os.Error
40 nb int64
41 pad int64
42 closed bool
43 usedBinary bool
44 }
45
46
47 func NewWriter(w io.Writer) *Writer { return &Writer{w: w} }
48
49
50 func (tw *Writer) Flush() os.Error {
51 n := tw.nb + tw.pad
52 for n > 0 && tw.err == nil {
53 nr := n
54 if nr > blockSize {
55 nr = blockSize
56 }
57 var nw int
58 nw, tw.err = tw.w.Write(zeroBlock[0:nr])
59 n -= int64(nw)
60 }
61 tw.nb = 0
62 tw.pad = 0
63 return tw.err
64 }
65
66
67 func (tw *Writer) cString(b []byte, s string) {
68 if len(s) > len(b) {
69 if tw.err == nil {
70 tw.err = ErrFieldTooLong
71 }
72 return
73 }
74 copy(b, s)
75 if len(s) < len(b) {
76 b[len(s)] = 0
77 }
78 }
79
80
81 func (tw *Writer) octal(b []byte, x int64) {
82 s := strconv.Itob64(x, 8)
83
84 for len(s)+1 < len(b) {
85 s = "0" + s
86 }
87 tw.cString(b, s)
88 }
89
90
91 func (tw *Writer) numeric(b []byte, x int64) {
92
93 s := strconv.Itob64(x, 8)
94 if len(s) < len(b) {
95 tw.octal(b, x)
96 return
97 }
98
99 tw.usedBinary = true
100 for i := len(b) - 1; x > 0 && i >= 0; i-- {
101 b[i] = byte(x)
102 x >>= 8
103 }
104 b[0] |= 0x80
105 }
106
107
108
109
110 func (tw *Writer) WriteHeader(hdr *Header) os.Error {
111 if tw.closed {
112 return ErrWriteAfterClose
113 }
114 if tw.err == nil {
115 tw.Flush()
116 }
117 if tw.err != nil {
118 return tw.err
119 }
120
121 tw.nb = int64(hdr.Size)
122 tw.pad = -tw.nb & (blockSize - 1)
123
124 header := make([]byte, blockSize)
125 s := slicer(header)
126
127
128 copy(s.next(100), []byte(hdr.Name))
129
130 tw.octal(s.next(8), hdr.Mode)
131 tw.numeric(s.next(8), int64(hdr.Uid))
132 tw.numeric(s.next(8), int64(hdr.Gid))
133 tw.numeric(s.next(12), hdr.Size)
134 tw.numeric(s.next(12), hdr.Mtime)
135 s.next(8)
136 s.next(1)[0] = hdr.Typeflag
137 s.next(100)
138 copy(s.next(8), []byte("ustar\x0000"))
139 tw.cString(s.next(32), hdr.Uname)
140 tw.cString(s.next(32), hdr.Gname)
141 tw.numeric(s.next(8), hdr.Devmajor)
142 tw.numeric(s.next(8), hdr.Devminor)
143
144
145 if tw.usedBinary {
146 copy(header[257:265], []byte("ustar \x00"))
147 }
148
149
150
151 chksum, _ := checksum(header)
152 tw.octal(header[148:155], chksum)
153 header[155] = ' '
154
155 if tw.err != nil {
156
157 return tw.err
158 }
159
160 _, tw.err = tw.w.Write(header)
161
162 return tw.err
163 }
164
165
166
167
168 func (tw *Writer) Write(b []byte) (n int, err os.Error) {
169 if tw.closed {
170 err = ErrWriteTooLong
171 return
172 }
173 overwrite := false
174 if int64(len(b)) > tw.nb {
175 b = b[0:tw.nb]
176 overwrite = true
177 }
178 n, err = tw.w.Write(b)
179 tw.nb -= int64(n)
180 if err == nil && overwrite {
181 err = ErrWriteTooLong
182 return
183 }
184 tw.err = err
185 return
186 }
187
188
189
190 func (tw *Writer) Close() os.Error {
191 if tw.err != nil || tw.closed {
192 return tw.err
193 }
194 tw.Flush()
195 tw.closed = true
196
197
198 for i := 0; i < 2; i++ {
199 _, tw.err = tw.w.Write(zeroBlock)
200 if tw.err != nil {
201 break
202 }
203 }
204 return tw.err
205 }