~ubuntu-branches/ubuntu/utopic/golang/utopic

« back to all changes in this revision

Viewing changes to src/pkg/archive/tar/writer.go

  • Committer: Package Import Robot
  • Author(s): James Page
  • Date: 2013-08-20 14:06:23 UTC
  • mfrom: (14.1.23 saucy-proposed)
  • Revision ID: package-import@ubuntu.com-20130820140623-b414jfxi3m0qkmrq
Tags: 2:1.1.2-2ubuntu1
* Merge from Debian unstable (LP: #1211749, #1202027). Remaining changes:
  - 016-armhf-elf-header.patch: Use correct ELF header for armhf binaries.
  - d/control,control.cross: Update Breaks/Replaces for Ubuntu
    versions to ensure smooth upgrades, regenerate control file.

Show diffs side-by-side

added added

removed removed

Lines of Context:
8
8
// - catch more errors (no first header, etc.)
9
9
 
10
10
import (
 
11
        "bytes"
11
12
        "errors"
12
13
        "fmt"
13
14
        "io"
 
15
        "os"
 
16
        "path"
14
17
        "strconv"
 
18
        "strings"
 
19
        "time"
15
20
)
16
21
 
17
22
var (
18
23
        ErrWriteTooLong    = errors.New("archive/tar: write too long")
19
24
        ErrFieldTooLong    = errors.New("archive/tar: header field too long")
20
25
        ErrWriteAfterClose = errors.New("archive/tar: write after close")
 
26
        errNameTooLong     = errors.New("archive/tar: name too long")
21
27
)
22
28
 
23
29
// A Writer provides sequential writing of a tar archive in POSIX.1 format.
24
30
// A tar archive consists of a sequence of files.
25
31
// Call WriteHeader to begin a new file, and then call Write to supply that file's data,
26
32
// writing at most hdr.Size bytes in total.
27
 
//
28
 
// Example:
29
 
//      tw := tar.NewWriter(w)
30
 
//      hdr := new(Header)
31
 
//      hdr.Size = length of data in bytes
32
 
//      // populate other hdr fields as desired
33
 
//      if err := tw.WriteHeader(hdr); err != nil {
34
 
//              // handle error
35
 
//      }
36
 
//      io.Copy(tw, data)
37
 
//      tw.Close()
38
33
type Writer struct {
39
34
        w          io.Writer
40
35
        err        error
110
105
        b[0] |= 0x80 // highest bit indicates binary format
111
106
}
112
107
 
 
108
var (
 
109
        minTime = time.Unix(0, 0)
 
110
        // There is room for 11 octal digits (33 bits) of mtime.
 
111
        maxTime = minTime.Add((1<<33 - 1) * time.Second)
 
112
)
 
113
 
113
114
// WriteHeader writes hdr and prepares to accept the file's contents.
114
115
// WriteHeader calls Flush if it is not the first header.
115
116
// Calling after a Close will return ErrWriteAfterClose.
123
124
        if tw.err != nil {
124
125
                return tw.err
125
126
        }
126
 
 
 
127
        // Decide whether or not to use PAX extensions
 
128
        // TODO(shanemhansen): we might want to use PAX headers for
 
129
        // subsecond time resolution, but for now let's just capture
 
130
        // the long name/long symlink use case.
 
131
        suffix := hdr.Name
 
132
        prefix := ""
 
133
        if len(hdr.Name) > fileNameSize || len(hdr.Linkname) > fileNameSize {
 
134
                var err error
 
135
                prefix, suffix, err = tw.splitUSTARLongName(hdr.Name)
 
136
                // Either we were unable to pack the long name into ustar format
 
137
                // or the link name is too long; use PAX headers.
 
138
                if err == errNameTooLong || len(hdr.Linkname) > fileNameSize {
 
139
                        if err := tw.writePAXHeader(hdr); err != nil {
 
140
                                return err
 
141
                        }
 
142
                } else if err != nil {
 
143
                        return err
 
144
                }
 
145
        }
127
146
        tw.nb = int64(hdr.Size)
128
147
        tw.pad = -tw.nb & (blockSize - 1) // blockSize is a power of two
129
148
 
130
149
        header := make([]byte, blockSize)
131
150
        s := slicer(header)
132
 
 
133
 
        // TODO(dsymonds): handle names longer than 100 chars
134
 
        copy(s.next(100), []byte(hdr.Name))
135
 
 
136
 
        tw.octal(s.next(8), hdr.Mode)              // 100:108
137
 
        tw.numeric(s.next(8), int64(hdr.Uid))      // 108:116
138
 
        tw.numeric(s.next(8), int64(hdr.Gid))      // 116:124
139
 
        tw.numeric(s.next(12), hdr.Size)           // 124:136
140
 
        tw.numeric(s.next(12), hdr.ModTime.Unix()) // 136:148
141
 
        s.next(8)                                  // chksum (148:156)
142
 
        s.next(1)[0] = hdr.Typeflag                // 156:157
143
 
        tw.cString(s.next(100), hdr.Linkname)      // linkname (157:257)
144
 
        copy(s.next(8), []byte("ustar\x0000"))     // 257:265
145
 
        tw.cString(s.next(32), hdr.Uname)          // 265:297
146
 
        tw.cString(s.next(32), hdr.Gname)          // 297:329
147
 
        tw.numeric(s.next(8), hdr.Devmajor)        // 329:337
148
 
        tw.numeric(s.next(8), hdr.Devminor)        // 337:345
149
 
 
 
151
        tw.cString(s.next(fileNameSize), suffix)
 
152
 
 
153
        // Handle out of range ModTime carefully.
 
154
        var modTime int64
 
155
        if !hdr.ModTime.Before(minTime) && !hdr.ModTime.After(maxTime) {
 
156
                modTime = hdr.ModTime.Unix()
 
157
        }
 
158
 
 
159
        tw.octal(s.next(8), hdr.Mode)          // 100:108
 
160
        tw.numeric(s.next(8), int64(hdr.Uid))  // 108:116
 
161
        tw.numeric(s.next(8), int64(hdr.Gid))  // 116:124
 
162
        tw.numeric(s.next(12), hdr.Size)       // 124:136
 
163
        tw.numeric(s.next(12), modTime)        // 136:148
 
164
        s.next(8)                              // chksum (148:156)
 
165
        s.next(1)[0] = hdr.Typeflag            // 156:157
 
166
        tw.cString(s.next(100), hdr.Linkname)  // linkname (157:257)
 
167
        copy(s.next(8), []byte("ustar\x0000")) // 257:265
 
168
        tw.cString(s.next(32), hdr.Uname)      // 265:297
 
169
        tw.cString(s.next(32), hdr.Gname)      // 297:329
 
170
        tw.numeric(s.next(8), hdr.Devmajor)    // 329:337
 
171
        tw.numeric(s.next(8), hdr.Devminor)    // 337:345
 
172
        tw.cString(s.next(155), prefix)        // 345:500
150
173
        // Use the GNU magic instead of POSIX magic if we used any GNU extensions.
151
174
        if tw.usedBinary {
152
175
                copy(header[257:265], []byte("ustar  \x00"))
153
176
        }
 
177
        // Use the ustar magic if we used ustar long names.
 
178
        if len(prefix) > 0 {
 
179
                copy(header[257:265], []byte("ustar\000"))
 
180
        }
154
181
 
155
182
        // The chksum field is terminated by a NUL and a space.
156
183
        // This is different from the other octal fields.
168
195
        return tw.err
169
196
}
170
197
 
 
198
// writeUSTARLongName splits a USTAR long name hdr.Name.
 
199
// name must be < 256 characters. errNameTooLong is returned
 
200
// if hdr.Name can't be split. The splitting heuristic
 
201
// is compatible with gnu tar.
 
202
func (tw *Writer) splitUSTARLongName(name string) (prefix, suffix string, err error) {
 
203
        length := len(name)
 
204
        if length > fileNamePrefixSize+1 {
 
205
                length = fileNamePrefixSize + 1
 
206
        } else if name[length-1] == '/' {
 
207
                length--
 
208
        }
 
209
        i := strings.LastIndex(name[:length], "/")
 
210
        nlen := length - i - 1
 
211
        if i <= 0 || nlen > fileNameSize || nlen == 0 {
 
212
                err = errNameTooLong
 
213
                return
 
214
        }
 
215
        prefix, suffix = name[:i], name[i+1:]
 
216
        return
 
217
}
 
218
 
 
219
// writePaxHeader writes an extended pax header to the
 
220
// archive.
 
221
func (tw *Writer) writePAXHeader(hdr *Header) error {
 
222
        // Prepare extended header
 
223
        ext := new(Header)
 
224
        ext.Typeflag = TypeXHeader
 
225
        // Setting ModTime is required for reader parsing to
 
226
        // succeed, and seems harmless enough.
 
227
        ext.ModTime = hdr.ModTime
 
228
        // The spec asks that we namespace our pseudo files
 
229
        // with the current pid.
 
230
        pid := os.Getpid()
 
231
        dir, file := path.Split(hdr.Name)
 
232
        ext.Name = path.Join(dir,
 
233
                fmt.Sprintf("PaxHeaders.%d", pid), file)[0:100]
 
234
        // Construct the body
 
235
        var buf bytes.Buffer
 
236
        if len(hdr.Name) > fileNameSize {
 
237
                fmt.Fprint(&buf, paxHeader("path="+hdr.Name))
 
238
        }
 
239
        if len(hdr.Linkname) > fileNameSize {
 
240
                fmt.Fprint(&buf, paxHeader("linkpath="+hdr.Linkname))
 
241
        }
 
242
        ext.Size = int64(len(buf.Bytes()))
 
243
        if err := tw.WriteHeader(ext); err != nil {
 
244
                return err
 
245
        }
 
246
        if _, err := tw.Write(buf.Bytes()); err != nil {
 
247
                return err
 
248
        }
 
249
        if err := tw.Flush(); err != nil {
 
250
                return err
 
251
        }
 
252
        return nil
 
253
}
 
254
 
 
255
// paxHeader formats a single pax record, prefixing it with the appropriate length
 
256
func paxHeader(msg string) string {
 
257
        const padding = 2 // Extra padding for space and newline
 
258
        size := len(msg) + padding
 
259
        size += len(strconv.Itoa(size))
 
260
        record := fmt.Sprintf("%d %s\n", size, msg)
 
261
        if len(record) != size {
 
262
                // Final adjustment if adding size increased
 
263
                // the number of digits in size
 
264
                size = len(record)
 
265
                record = fmt.Sprintf("%d %s\n", size, msg)
 
266
        }
 
267
        return record
 
268
}
 
269
 
171
270
// Write writes to the current entry in the tar archive.
172
271
// Write returns the error ErrWriteTooLong if more than
173
272
// hdr.Size bytes are written after WriteHeader.