1// 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
5// Package tar implements access to tar archives.
6// It aims to cover most of the variations, including those produced
7// by GNU and BSD tars.
8//
9// References:
10//   http://www.freebsd.org/cgi/man.cgi?query=tar&sektion=5
11//   http://www.gnu.org/software/tar/manual/html_node/Standard.html
12//   http://pubs.opengroup.org/onlinepubs/9699919799/utilities/pax.html
13package tar
14
15import (
16	"bytes"
17	"errors"
18	"fmt"
19	"os"
20	"path"
21	"time"
22)
23
24const (
25	blockSize = 512
26
27	// Types
28	TypeReg           = '0'    // regular file
29	TypeRegA          = '\x00' // regular file
30	TypeLink          = '1'    // hard link
31	TypeSymlink       = '2'    // symbolic link
32	TypeChar          = '3'    // character device node
33	TypeBlock         = '4'    // block device node
34	TypeDir           = '5'    // directory
35	TypeFifo          = '6'    // fifo node
36	TypeCont          = '7'    // reserved
37	TypeXHeader       = 'x'    // extended header
38	TypeXGlobalHeader = 'g'    // global extended header
39	TypeGNULongName   = 'L'    // Next file has a long name
40	TypeGNULongLink   = 'K'    // Next file symlinks to a file w/ a long name
41	TypeGNUSparse     = 'S'    // sparse file
42)
43
44// A Header represents a single header in a tar archive.
45// Some fields may not be populated.
46type Header struct {
47	Name         string    // name of header file entry
48	Mode         int64     // permission and mode bits
49	Uid          int       // user id of owner
50	Gid          int       // group id of owner
51	Size         int64     // length in bytes
52	ModTime      time.Time // modified time
53	Typeflag     byte      // type of header entry
54	Linkname     string    // target name of link
55	Uname        string    // user name of owner
56	Gname        string    // group name of owner
57	Devmajor     int64     // major number of character or block device
58	Devminor     int64     // minor number of character or block device
59	AccessTime   time.Time // access time
60	ChangeTime   time.Time // status change time
61	CreationTime time.Time // creation time
62	Xattrs       map[string]string
63	Winheaders   map[string]string
64}
65
66// File name constants from the tar spec.
67const (
68	fileNameSize       = 100 // Maximum number of bytes in a standard tar name.
69	fileNamePrefixSize = 155 // Maximum number of ustar extension bytes.
70)
71
72// FileInfo returns an os.FileInfo for the Header.
73func (h *Header) FileInfo() os.FileInfo {
74	return headerFileInfo{h}
75}
76
77// headerFileInfo implements os.FileInfo.
78type headerFileInfo struct {
79	h *Header
80}
81
82func (fi headerFileInfo) Size() int64        { return fi.h.Size }
83func (fi headerFileInfo) IsDir() bool        { return fi.Mode().IsDir() }
84func (fi headerFileInfo) ModTime() time.Time { return fi.h.ModTime }
85func (fi headerFileInfo) Sys() interface{}   { return fi.h }
86
87// Name returns the base name of the file.
88func (fi headerFileInfo) Name() string {
89	if fi.IsDir() {
90		return path.Base(path.Clean(fi.h.Name))
91	}
92	return path.Base(fi.h.Name)
93}
94
95// Mode returns the permission and mode bits for the headerFileInfo.
96func (fi headerFileInfo) Mode() (mode os.FileMode) {
97	// Set file permission bits.
98	mode = os.FileMode(fi.h.Mode).Perm()
99
100	// Set setuid, setgid and sticky bits.
101	if fi.h.Mode&c_ISUID != 0 {
102		// setuid
103		mode |= os.ModeSetuid
104	}
105	if fi.h.Mode&c_ISGID != 0 {
106		// setgid
107		mode |= os.ModeSetgid
108	}
109	if fi.h.Mode&c_ISVTX != 0 {
110		// sticky
111		mode |= os.ModeSticky
112	}
113
114	// Set file mode bits.
115	// clear perm, setuid, setgid and sticky bits.
116	m := os.FileMode(fi.h.Mode) &^ 07777
117	if m == c_ISDIR {
118		// directory
119		mode |= os.ModeDir
120	}
121	if m == c_ISFIFO {
122		// named pipe (FIFO)
123		mode |= os.ModeNamedPipe
124	}
125	if m == c_ISLNK {
126		// symbolic link
127		mode |= os.ModeSymlink
128	}
129	if m == c_ISBLK {
130		// device file
131		mode |= os.ModeDevice
132	}
133	if m == c_ISCHR {
134		// Unix character device
135		mode |= os.ModeDevice
136		mode |= os.ModeCharDevice
137	}
138	if m == c_ISSOCK {
139		// Unix domain socket
140		mode |= os.ModeSocket
141	}
142
143	switch fi.h.Typeflag {
144	case TypeSymlink:
145		// symbolic link
146		mode |= os.ModeSymlink
147	case TypeChar:
148		// character device node
149		mode |= os.ModeDevice
150		mode |= os.ModeCharDevice
151	case TypeBlock:
152		// block device node
153		mode |= os.ModeDevice
154	case TypeDir:
155		// directory
156		mode |= os.ModeDir
157	case TypeFifo:
158		// fifo node
159		mode |= os.ModeNamedPipe
160	}
161
162	return mode
163}
164
165// sysStat, if non-nil, populates h from system-dependent fields of fi.
166var sysStat func(fi os.FileInfo, h *Header) error
167
168// Mode constants from the tar spec.
169const (
170	c_ISUID  = 04000   // Set uid
171	c_ISGID  = 02000   // Set gid
172	c_ISVTX  = 01000   // Save text (sticky bit)
173	c_ISDIR  = 040000  // Directory
174	c_ISFIFO = 010000  // FIFO
175	c_ISREG  = 0100000 // Regular file
176	c_ISLNK  = 0120000 // Symbolic link
177	c_ISBLK  = 060000  // Block special file
178	c_ISCHR  = 020000  // Character special file
179	c_ISSOCK = 0140000 // Socket
180)
181
182// Keywords for the PAX Extended Header
183const (
184	paxAtime        = "atime"
185	paxCharset      = "charset"
186	paxComment      = "comment"
187	paxCtime        = "ctime" // please note that ctime is not a valid pax header.
188	paxCreationTime = "LIBARCHIVE.creationtime"
189	paxGid          = "gid"
190	paxGname        = "gname"
191	paxLinkpath     = "linkpath"
192	paxMtime        = "mtime"
193	paxPath         = "path"
194	paxSize         = "size"
195	paxUid          = "uid"
196	paxUname        = "uname"
197	paxXattr        = "SCHILY.xattr."
198	paxWindows      = "MSWINDOWS."
199	paxNone         = ""
200)
201
202// FileInfoHeader creates a partially-populated Header from fi.
203// If fi describes a symlink, FileInfoHeader records link as the link target.
204// If fi describes a directory, a slash is appended to the name.
205// Because os.FileInfo's Name method returns only the base name of
206// the file it describes, it may be necessary to modify the Name field
207// of the returned header to provide the full path name of the file.
208func FileInfoHeader(fi os.FileInfo, link string) (*Header, error) {
209	if fi == nil {
210		return nil, errors.New("tar: FileInfo is nil")
211	}
212	fm := fi.Mode()
213	h := &Header{
214		Name:    fi.Name(),
215		ModTime: fi.ModTime(),
216		Mode:    int64(fm.Perm()), // or'd with c_IS* constants later
217	}
218	switch {
219	case fm.IsRegular():
220		h.Mode |= c_ISREG
221		h.Typeflag = TypeReg
222		h.Size = fi.Size()
223	case fi.IsDir():
224		h.Typeflag = TypeDir
225		h.Mode |= c_ISDIR
226		h.Name += "/"
227	case fm&os.ModeSymlink != 0:
228		h.Typeflag = TypeSymlink
229		h.Mode |= c_ISLNK
230		h.Linkname = link
231	case fm&os.ModeDevice != 0:
232		if fm&os.ModeCharDevice != 0 {
233			h.Mode |= c_ISCHR
234			h.Typeflag = TypeChar
235		} else {
236			h.Mode |= c_ISBLK
237			h.Typeflag = TypeBlock
238		}
239	case fm&os.ModeNamedPipe != 0:
240		h.Typeflag = TypeFifo
241		h.Mode |= c_ISFIFO
242	case fm&os.ModeSocket != 0:
243		h.Mode |= c_ISSOCK
244	default:
245		return nil, fmt.Errorf("archive/tar: unknown file mode %v", fm)
246	}
247	if fm&os.ModeSetuid != 0 {
248		h.Mode |= c_ISUID
249	}
250	if fm&os.ModeSetgid != 0 {
251		h.Mode |= c_ISGID
252	}
253	if fm&os.ModeSticky != 0 {
254		h.Mode |= c_ISVTX
255	}
256	// If possible, populate additional fields from OS-specific
257	// FileInfo fields.
258	if sys, ok := fi.Sys().(*Header); ok {
259		// This FileInfo came from a Header (not the OS). Use the
260		// original Header to populate all remaining fields.
261		h.Uid = sys.Uid
262		h.Gid = sys.Gid
263		h.Uname = sys.Uname
264		h.Gname = sys.Gname
265		h.AccessTime = sys.AccessTime
266		h.ChangeTime = sys.ChangeTime
267		if sys.Xattrs != nil {
268			h.Xattrs = make(map[string]string)
269			for k, v := range sys.Xattrs {
270				h.Xattrs[k] = v
271			}
272		}
273		if sys.Typeflag == TypeLink {
274			// hard link
275			h.Typeflag = TypeLink
276			h.Size = 0
277			h.Linkname = sys.Linkname
278		}
279	}
280	if sysStat != nil {
281		return h, sysStat(fi, h)
282	}
283	return h, nil
284}
285
286var zeroBlock = make([]byte, blockSize)
287
288// POSIX specifies a sum of the unsigned byte values, but the Sun tar uses signed byte values.
289// We compute and return both.
290func checksum(header []byte) (unsigned int64, signed int64) {
291	for i := 0; i < len(header); i++ {
292		if i == 148 {
293			// The chksum field (header[148:156]) is special: it should be treated as space bytes.
294			unsigned += ' ' * 8
295			signed += ' ' * 8
296			i += 7
297			continue
298		}
299		unsigned += int64(header[i])
300		signed += int64(int8(header[i]))
301	}
302	return
303}
304
305type slicer []byte
306
307func (sp *slicer) next(n int) (b []byte) {
308	s := *sp
309	b, *sp = s[0:n], s[n:]
310	return
311}
312
313func isASCII(s string) bool {
314	for _, c := range s {
315		if c >= 0x80 {
316			return false
317		}
318	}
319	return true
320}
321
322func toASCII(s string) string {
323	if isASCII(s) {
324		return s
325	}
326	var buf bytes.Buffer
327	for _, c := range s {
328		if c < 0x80 {
329			buf.WriteByte(byte(c))
330		}
331	}
332	return buf.String()
333}
334
335// isHeaderOnlyType checks if the given type flag is of the type that has no
336// data section even if a size is specified.
337func isHeaderOnlyType(flag byte) bool {
338	switch flag {
339	case TypeLink, TypeSymlink, TypeChar, TypeBlock, TypeDir, TypeFifo:
340		return true
341	default:
342		return false
343	}
344}
345