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	"errors"
17	"fmt"
18	"os"
19	"path"
20	"time"
21)
22
23// BUG: Use of the Uid and Gid fields in Header could overflow on 32-bit
24// architectures. If a large value is encountered when decoding, the result
25// stored in Header will be the truncated version.
26
27// Header type flags.
28const (
29	TypeReg           = '0'    // regular file
30	TypeRegA          = '\x00' // regular file
31	TypeLink          = '1'    // hard link
32	TypeSymlink       = '2'    // symbolic link
33	TypeChar          = '3'    // character device node
34	TypeBlock         = '4'    // block device node
35	TypeDir           = '5'    // directory
36	TypeFifo          = '6'    // fifo node
37	TypeCont          = '7'    // reserved
38	TypeXHeader       = 'x'    // extended header
39	TypeXGlobalHeader = 'g'    // global extended header
40	TypeGNULongName   = 'L'    // Next file has a long name
41	TypeGNULongLink   = 'K'    // Next file symlinks to a file w/ a long name
42	TypeGNUSparse     = 'S'    // sparse file
43)
44
45// A Header represents a single header in a tar archive.
46// Some fields may not be populated.
47type Header struct {
48	Name       string    // name of header file entry
49	Mode       int64     // permission and mode bits
50	Uid        int       // user id of owner
51	Gid        int       // group id of owner
52	Size       int64     // length in bytes
53	ModTime    time.Time // modified time
54	Typeflag   byte      // type of header entry
55	Linkname   string    // target name of link
56	Uname      string    // user name of owner
57	Gname      string    // group name of owner
58	Devmajor   int64     // major number of character or block device
59	Devminor   int64     // minor number of character or block device
60	AccessTime time.Time // access time
61	ChangeTime time.Time // status change time
62	Xattrs     map[string]string
63}
64
65// FileInfo returns an os.FileInfo for the Header.
66func (h *Header) FileInfo() os.FileInfo {
67	return headerFileInfo{h}
68}
69
70// headerFileInfo implements os.FileInfo.
71type headerFileInfo struct {
72	h *Header
73}
74
75func (fi headerFileInfo) Size() int64        { return fi.h.Size }
76func (fi headerFileInfo) IsDir() bool        { return fi.Mode().IsDir() }
77func (fi headerFileInfo) ModTime() time.Time { return fi.h.ModTime }
78func (fi headerFileInfo) Sys() interface{}   { return fi.h }
79
80// Name returns the base name of the file.
81func (fi headerFileInfo) Name() string {
82	if fi.IsDir() {
83		return path.Base(path.Clean(fi.h.Name))
84	}
85	return path.Base(fi.h.Name)
86}
87
88// Mode returns the permission and mode bits for the headerFileInfo.
89func (fi headerFileInfo) Mode() (mode os.FileMode) {
90	// Set file permission bits.
91	mode = os.FileMode(fi.h.Mode).Perm()
92
93	// Set setuid, setgid and sticky bits.
94	if fi.h.Mode&c_ISUID != 0 {
95		// setuid
96		mode |= os.ModeSetuid
97	}
98	if fi.h.Mode&c_ISGID != 0 {
99		// setgid
100		mode |= os.ModeSetgid
101	}
102	if fi.h.Mode&c_ISVTX != 0 {
103		// sticky
104		mode |= os.ModeSticky
105	}
106
107	// Set file mode bits.
108	// clear perm, setuid, setgid and sticky bits.
109	m := os.FileMode(fi.h.Mode) &^ 07777
110	if m == c_ISDIR {
111		// directory
112		mode |= os.ModeDir
113	}
114	if m == c_ISFIFO {
115		// named pipe (FIFO)
116		mode |= os.ModeNamedPipe
117	}
118	if m == c_ISLNK {
119		// symbolic link
120		mode |= os.ModeSymlink
121	}
122	if m == c_ISBLK {
123		// device file
124		mode |= os.ModeDevice
125	}
126	if m == c_ISCHR {
127		// Unix character device
128		mode |= os.ModeDevice
129		mode |= os.ModeCharDevice
130	}
131	if m == c_ISSOCK {
132		// Unix domain socket
133		mode |= os.ModeSocket
134	}
135
136	switch fi.h.Typeflag {
137	case TypeSymlink:
138		// symbolic link
139		mode |= os.ModeSymlink
140	case TypeChar:
141		// character device node
142		mode |= os.ModeDevice
143		mode |= os.ModeCharDevice
144	case TypeBlock:
145		// block device node
146		mode |= os.ModeDevice
147	case TypeDir:
148		// directory
149		mode |= os.ModeDir
150	case TypeFifo:
151		// fifo node
152		mode |= os.ModeNamedPipe
153	}
154
155	return mode
156}
157
158// sysStat, if non-nil, populates h from system-dependent fields of fi.
159var sysStat func(fi os.FileInfo, h *Header) error
160
161const (
162	// Mode constants from the USTAR spec:
163	// See http://pubs.opengroup.org/onlinepubs/9699919799/utilities/pax.html#tag_20_92_13_06
164	c_ISUID = 04000 // Set uid
165	c_ISGID = 02000 // Set gid
166	c_ISVTX = 01000 // Save text (sticky bit)
167
168	// Common Unix mode constants; these are not defined in any common tar standard.
169	// Header.FileInfo understands these, but FileInfoHeader will never produce these.
170	c_ISDIR  = 040000  // Directory
171	c_ISFIFO = 010000  // FIFO
172	c_ISREG  = 0100000 // Regular file
173	c_ISLNK  = 0120000 // Symbolic link
174	c_ISBLK  = 060000  // Block special file
175	c_ISCHR  = 020000  // Character special file
176	c_ISSOCK = 0140000 // Socket
177)
178
179// Keywords for the PAX Extended Header
180const (
181	paxAtime    = "atime"
182	paxCharset  = "charset"
183	paxComment  = "comment"
184	paxCtime    = "ctime" // please note that ctime is not a valid pax header.
185	paxGid      = "gid"
186	paxGname    = "gname"
187	paxLinkpath = "linkpath"
188	paxMtime    = "mtime"
189	paxPath     = "path"
190	paxSize     = "size"
191	paxUid      = "uid"
192	paxUname    = "uname"
193	paxXattr    = "SCHILY.xattr."
194	paxNone     = ""
195)
196
197// FileInfoHeader creates a partially-populated Header from fi.
198// If fi describes a symlink, FileInfoHeader records link as the link target.
199// If fi describes a directory, a slash is appended to the name.
200// Because os.FileInfo's Name method returns only the base name of
201// the file it describes, it may be necessary to modify the Name field
202// of the returned header to provide the full path name of the file.
203func FileInfoHeader(fi os.FileInfo, link string) (*Header, error) {
204	if fi == nil {
205		return nil, errors.New("tar: FileInfo is nil")
206	}
207	fm := fi.Mode()
208	h := &Header{
209		Name:    fi.Name(),
210		ModTime: fi.ModTime(),
211		Mode:    int64(fm.Perm()), // or'd with c_IS* constants later
212	}
213	switch {
214	case fm.IsRegular():
215		h.Typeflag = TypeReg
216		h.Size = fi.Size()
217	case fi.IsDir():
218		h.Typeflag = TypeDir
219		h.Name += "/"
220	case fm&os.ModeSymlink != 0:
221		h.Typeflag = TypeSymlink
222		h.Linkname = link
223	case fm&os.ModeDevice != 0:
224		if fm&os.ModeCharDevice != 0 {
225			h.Typeflag = TypeChar
226		} else {
227			h.Typeflag = TypeBlock
228		}
229	case fm&os.ModeNamedPipe != 0:
230		h.Typeflag = TypeFifo
231	case fm&os.ModeSocket != 0:
232		return nil, fmt.Errorf("archive/tar: sockets not supported")
233	default:
234		return nil, fmt.Errorf("archive/tar: unknown file mode %v", fm)
235	}
236	if fm&os.ModeSetuid != 0 {
237		h.Mode |= c_ISUID
238	}
239	if fm&os.ModeSetgid != 0 {
240		h.Mode |= c_ISGID
241	}
242	if fm&os.ModeSticky != 0 {
243		h.Mode |= c_ISVTX
244	}
245	// If possible, populate additional fields from OS-specific
246	// FileInfo fields.
247	if sys, ok := fi.Sys().(*Header); ok {
248		// This FileInfo came from a Header (not the OS). Use the
249		// original Header to populate all remaining fields.
250		h.Uid = sys.Uid
251		h.Gid = sys.Gid
252		h.Uname = sys.Uname
253		h.Gname = sys.Gname
254		h.AccessTime = sys.AccessTime
255		h.ChangeTime = sys.ChangeTime
256		if sys.Xattrs != nil {
257			h.Xattrs = make(map[string]string)
258			for k, v := range sys.Xattrs {
259				h.Xattrs[k] = v
260			}
261		}
262		if sys.Typeflag == TypeLink {
263			// hard link
264			h.Typeflag = TypeLink
265			h.Size = 0
266			h.Linkname = sys.Linkname
267		}
268	}
269	if sysStat != nil {
270		return h, sysStat(fi, h)
271	}
272	return h, nil
273}
274
275// isHeaderOnlyType checks if the given type flag is of the type that has no
276// data section even if a size is specified.
277func isHeaderOnlyType(flag byte) bool {
278	switch flag {
279	case TypeLink, TypeSymlink, TypeChar, TypeBlock, TypeDir, TypeFifo:
280		return true
281	default:
282		return false
283	}
284}
285