blob: 8673bad316663d524be38d40068aec7880562c96 [file] [log] [blame]
David Symondsd6d0a392009-07-08 17:15:18 -07001// Copyright 2009 The Go Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style
3// license that can be found in the LICENSE file.
4
5package tar
6
7// TODO(dsymonds):
8// - catch more errors (no first header, write after close, etc.)
9
10import (
Robert Griesemer5a1d3322009-12-15 15:33:31 -080011 "io"
12 "os"
13 "strconv"
David Symondsd6d0a392009-07-08 17:15:18 -070014)
15
16var (
Robert Griesemer5a1d3322009-12-15 15:33:31 -080017 ErrWriteTooLong = os.NewError("write too long")
18 ErrFieldTooLong = os.NewError("header field too long")
19 ErrWriteAfterClose = os.NewError("write after close")
David Symondsd6d0a392009-07-08 17:15:18 -070020)
21
22// A Writer provides sequential writing of a tar archive in POSIX.1 format.
23// A tar archive consists of a sequence of files.
24// Call WriteHeader to begin a new file, and then call Write to supply that file's data,
25// writing at most hdr.Size bytes in total.
26//
27// Example:
Christopher Wedgwoodae729a42010-04-11 10:29:07 -070028// tw := tar.NewWriter(w)
29// hdr := new(Header)
30// hdr.Size = length of data in bytes
David Symondsd6d0a392009-07-08 17:15:18 -070031// // populate other hdr fields as desired
32// if err := tw.WriteHeader(hdr); err != nil {
33// // handle error
34// }
Christopher Wedgwoodae729a42010-04-11 10:29:07 -070035// io.Copy(tw, data)
36// tw.Close()
David Symondsd6d0a392009-07-08 17:15:18 -070037type Writer struct {
Robert Griesemer5a1d3322009-12-15 15:33:31 -080038 w io.Writer
39 err os.Error
40 nb int64 // number of unwritten bytes for current file entry
41 pad int64 // amount of padding to write after current file entry
42 closed bool
43 usedBinary bool // whether the binary numeric field extension was used
David Symondsd6d0a392009-07-08 17:15:18 -070044}
45
46// NewWriter creates a new Writer writing to w.
Robert Griesemer5a1d3322009-12-15 15:33:31 -080047func NewWriter(w io.Writer) *Writer { return &Writer{w: w} }
David Symondsd6d0a392009-07-08 17:15:18 -070048
49// Flush finishes writing the current file (optional).
50func (tw *Writer) Flush() os.Error {
Robert Griesemer5a1d3322009-12-15 15:33:31 -080051 n := tw.nb + tw.pad
David Symondsd6d0a392009-07-08 17:15:18 -070052 for n > 0 && tw.err == nil {
Robert Griesemer5a1d3322009-12-15 15:33:31 -080053 nr := n
David Symondsd6d0a392009-07-08 17:15:18 -070054 if nr > blockSize {
Robert Griesemer40621d52009-11-09 12:07:39 -080055 nr = blockSize
David Symondsd6d0a392009-07-08 17:15:18 -070056 }
Robert Griesemer5a1d3322009-12-15 15:33:31 -080057 var nw int
58 nw, tw.err = tw.w.Write(zeroBlock[0:nr])
59 n -= int64(nw)
David Symondsd6d0a392009-07-08 17:15:18 -070060 }
Robert Griesemer5a1d3322009-12-15 15:33:31 -080061 tw.nb = 0
62 tw.pad = 0
63 return tw.err
David Symondsd6d0a392009-07-08 17:15:18 -070064}
65
66// Write s into b, terminating it with a NUL if there is room.
67func (tw *Writer) cString(b []byte, s string) {
68 if len(s) > len(b) {
69 if tw.err == nil {
Robert Griesemer40621d52009-11-09 12:07:39 -080070 tw.err = ErrFieldTooLong
David Symondsd6d0a392009-07-08 17:15:18 -070071 }
Robert Griesemer5a1d3322009-12-15 15:33:31 -080072 return
David Symondsd6d0a392009-07-08 17:15:18 -070073 }
Russ Coxd86ab012010-10-26 21:52:54 -070074 copy(b, s)
David Symondsd6d0a392009-07-08 17:15:18 -070075 if len(s) < len(b) {
Robert Griesemer40621d52009-11-09 12:07:39 -080076 b[len(s)] = 0
David Symondsd6d0a392009-07-08 17:15:18 -070077 }
78}
79
80// Encode x as an octal ASCII string and write it into b with leading zeros.
81func (tw *Writer) octal(b []byte, x int64) {
Robert Griesemer5a1d3322009-12-15 15:33:31 -080082 s := strconv.Itob64(x, 8)
David Symondsd6d0a392009-07-08 17:15:18 -070083 // leading zeros, but leave room for a NUL.
Russ Cox650bff62009-10-06 14:55:39 -070084 for len(s)+1 < len(b) {
Robert Griesemer16989342009-11-09 21:09:34 -080085 s = "0" + s
David Symondsd6d0a392009-07-08 17:15:18 -070086 }
Robert Griesemer5a1d3322009-12-15 15:33:31 -080087 tw.cString(b, s)
David Symondsd6d0a392009-07-08 17:15:18 -070088}
89
David Symondsc17dde22009-10-05 04:08:24 -070090// Write x into b, either as octal or as binary (GNUtar/star extension).
91func (tw *Writer) numeric(b []byte, x int64) {
92 // Try octal first.
Robert Griesemer5a1d3322009-12-15 15:33:31 -080093 s := strconv.Itob64(x, 8)
David Symondsc17dde22009-10-05 04:08:24 -070094 if len(s) < len(b) {
Robert Griesemer5a1d3322009-12-15 15:33:31 -080095 tw.octal(b, x)
96 return
David Symondsc17dde22009-10-05 04:08:24 -070097 }
98 // Too big: use binary (big-endian).
Robert Griesemer5a1d3322009-12-15 15:33:31 -080099 tw.usedBinary = true
Robert Griesemer16989342009-11-09 21:09:34 -0800100 for i := len(b) - 1; x > 0 && i >= 0; i-- {
Robert Griesemer5a1d3322009-12-15 15:33:31 -0800101 b[i] = byte(x)
102 x >>= 8
David Symondsc17dde22009-10-05 04:08:24 -0700103 }
Robert Griesemer5a1d3322009-12-15 15:33:31 -0800104 b[0] |= 0x80 // highest bit indicates binary format
David Symondsc17dde22009-10-05 04:08:24 -0700105}
106
David Symondsd6d0a392009-07-08 17:15:18 -0700107// WriteHeader writes hdr and prepares to accept the file's contents.
108// WriteHeader calls Flush if it is not the first header.
Christopher Wedgwoodfe0eb172009-12-14 11:35:02 -0800109// Calling after a Close will return ErrWriteAfterClose.
David Symondsd6d0a392009-07-08 17:15:18 -0700110func (tw *Writer) WriteHeader(hdr *Header) os.Error {
Christopher Wedgwoodfe0eb172009-12-14 11:35:02 -0800111 if tw.closed {
112 return ErrWriteAfterClose
113 }
David Symondsd6d0a392009-07-08 17:15:18 -0700114 if tw.err == nil {
Robert Griesemer40621d52009-11-09 12:07:39 -0800115 tw.Flush()
David Symondsd6d0a392009-07-08 17:15:18 -0700116 }
117 if tw.err != nil {
Robert Griesemer40621d52009-11-09 12:07:39 -0800118 return tw.err
David Symondsd6d0a392009-07-08 17:15:18 -0700119 }
120
Robert Griesemer5a1d3322009-12-15 15:33:31 -0800121 tw.nb = int64(hdr.Size)
122 tw.pad = -tw.nb & (blockSize - 1) // blockSize is a power of two
David Symondsd6d0a392009-07-08 17:15:18 -0700123
Robert Griesemer5a1d3322009-12-15 15:33:31 -0800124 header := make([]byte, blockSize)
125 s := slicer(header)
David Symondsd6d0a392009-07-08 17:15:18 -0700126
127 // TODO(dsymonds): handle names longer than 100 chars
Russ Cox9750adb2010-02-25 16:01:29 -0800128 copy(s.next(100), []byte(hdr.Name))
David Symondsd6d0a392009-07-08 17:15:18 -0700129
Russ Cox9750adb2010-02-25 16:01:29 -0800130 tw.octal(s.next(8), hdr.Mode) // 100:108
Rob Pike5cd8c832010-04-22 14:01:33 -0700131 tw.numeric(s.next(8), int64(hdr.Uid)) // 108:116
132 tw.numeric(s.next(8), int64(hdr.Gid)) // 116:124
Russ Cox9750adb2010-02-25 16:01:29 -0800133 tw.numeric(s.next(12), hdr.Size) // 124:136
134 tw.numeric(s.next(12), hdr.Mtime) // 136:148
135 s.next(8) // chksum (148:156)
136 s.next(1)[0] = hdr.Typeflag // 156:157
137 s.next(100) // linkname (157:257)
138 copy(s.next(8), []byte("ustar\x0000")) // 257:265
139 tw.cString(s.next(32), hdr.Uname) // 265:297
140 tw.cString(s.next(32), hdr.Gname) // 297:329
141 tw.numeric(s.next(8), hdr.Devmajor) // 329:337
142 tw.numeric(s.next(8), hdr.Devminor) // 337:345
David Symondsc17dde22009-10-05 04:08:24 -0700143
144 // Use the GNU magic instead of POSIX magic if we used any GNU extensions.
145 if tw.usedBinary {
Russ Cox9750adb2010-02-25 16:01:29 -0800146 copy(header[257:265], []byte("ustar \x00"))
David Symondsc17dde22009-10-05 04:08:24 -0700147 }
David Symondsd6d0a392009-07-08 17:15:18 -0700148
149 // The chksum field is terminated by a NUL and a space.
150 // This is different from the other octal fields.
Robert Griesemer5a1d3322009-12-15 15:33:31 -0800151 chksum, _ := checksum(header)
152 tw.octal(header[148:155], chksum)
153 header[155] = ' '
David Symondsd6d0a392009-07-08 17:15:18 -0700154
155 if tw.err != nil {
156 // problem with header; probably integer too big for a field.
Robert Griesemer40621d52009-11-09 12:07:39 -0800157 return tw.err
David Symondsd6d0a392009-07-08 17:15:18 -0700158 }
159
Robert Griesemer5a1d3322009-12-15 15:33:31 -0800160 _, tw.err = tw.w.Write(header)
David Symondsd6d0a392009-07-08 17:15:18 -0700161
Robert Griesemer5a1d3322009-12-15 15:33:31 -0800162 return tw.err
David Symondsd6d0a392009-07-08 17:15:18 -0700163}
164
165// Write writes to the current entry in the tar archive.
166// Write returns the error ErrWriteTooLong if more than
167// hdr.Size bytes are written after WriteHeader.
Evan Shaw4db3a162009-11-19 20:43:30 -0800168func (tw *Writer) Write(b []byte) (n int, err os.Error) {
Christopher Wedgwoodfe0eb172009-12-14 11:35:02 -0800169 if tw.closed {
Robert Griesemer5a1d3322009-12-15 15:33:31 -0800170 err = ErrWriteTooLong
171 return
Christopher Wedgwoodfe0eb172009-12-14 11:35:02 -0800172 }
Robert Griesemer5a1d3322009-12-15 15:33:31 -0800173 overwrite := false
David Symondsd6d0a392009-07-08 17:15:18 -0700174 if int64(len(b)) > tw.nb {
Robert Griesemer5a1d3322009-12-15 15:33:31 -0800175 b = b[0:tw.nb]
176 overwrite = true
David Symondsd6d0a392009-07-08 17:15:18 -0700177 }
Robert Griesemer5a1d3322009-12-15 15:33:31 -0800178 n, err = tw.w.Write(b)
179 tw.nb -= int64(n)
David Symondsd6d0a392009-07-08 17:15:18 -0700180 if err == nil && overwrite {
Robert Griesemer5a1d3322009-12-15 15:33:31 -0800181 err = ErrWriteTooLong
182 return
David Symondsd6d0a392009-07-08 17:15:18 -0700183 }
Robert Griesemer5a1d3322009-12-15 15:33:31 -0800184 tw.err = err
185 return
David Symondsd6d0a392009-07-08 17:15:18 -0700186}
187
Christopher Wedgwoodfe0eb172009-12-14 11:35:02 -0800188// Close closes the tar archive, flushing any unwritten
189// data to the underlying writer.
David Symondsd6d0a392009-07-08 17:15:18 -0700190func (tw *Writer) Close() os.Error {
191 if tw.err != nil || tw.closed {
Robert Griesemer40621d52009-11-09 12:07:39 -0800192 return tw.err
David Symondsd6d0a392009-07-08 17:15:18 -0700193 }
Robert Griesemer5a1d3322009-12-15 15:33:31 -0800194 tw.Flush()
195 tw.closed = true
David Symondsd6d0a392009-07-08 17:15:18 -0700196
197 // trailer: two zero blocks
198 for i := 0; i < 2; i++ {
Robert Griesemer5a1d3322009-12-15 15:33:31 -0800199 _, tw.err = tw.w.Write(zeroBlock)
David Symondsd6d0a392009-07-08 17:15:18 -0700200 if tw.err != nil {
Robert Griesemer40621d52009-11-09 12:07:39 -0800201 break
David Symondsd6d0a392009-07-08 17:15:18 -0700202 }
203 }
Robert Griesemer5a1d3322009-12-15 15:33:31 -0800204 return tw.err
David Symondsd6d0a392009-07-08 17:15:18 -0700205}