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
23const (
24	blockSize = 512
25
26	// Types
27	TypeReg           = '0'    // regular file
28	TypeRegA          = '\x00' // regular file
29	TypeLink          = '1'    // hard link
30	TypeSymlink       = '2'    // symbolic link
31	TypeChar          = '3'    // character device node
32	TypeBlock         = '4'    // block device node
33	TypeDir           = '5'    // directory
34	TypeFifo          = '6'    // fifo node
35	TypeCont          = '7'    // reserved
36	TypeXHeader       = 'x'    // extended header
37	TypeXGlobalHeader = 'g'    // global extended header
38	TypeGNULongName   = 'L'    // Next file has a long name
39	TypeGNULongLink   = 'K'    // Next file symlinks to a file w/ a long name
40)
41
42// A Header represents a single header in a tar archive.
43// Some fields may not be populated.
44type Header struct {
45	Name       string    // name of header file entry
46	Mode       int64     // permission and mode bits
47	Uid        int       // user id of owner
48	Gid        int       // group id of owner
49	Size       int64     // length in bytes
50	ModTime    time.Time // modified time
51	Typeflag   byte      // type of header entry
52	Linkname   string    // target name of link
53	Uname      string    // user name of owner
54	Gname      string    // group name of owner
55	Devmajor   int64     // major number of character or block device
56	Devminor   int64     // minor number of character or block device
57	AccessTime time.Time // access time
58	ChangeTime time.Time // status change time
59}
60
61// File name constants from the tar spec.
62const (
63	fileNameSize       = 100 // Maximum number of bytes in a standard tar name.
64	fileNamePrefixSize = 155 // Maximum number of ustar extension bytes.
65)
66
67// FileInfo returns an os.FileInfo for the Header.
68func (h *Header) FileInfo() os.FileInfo {
69	return headerFileInfo{h}
70}
71
72// headerFileInfo implements os.FileInfo.
73type headerFileInfo struct {
74	h *Header
75}
76
77func (fi headerFileInfo) Size() int64        { return fi.h.Size }
78func (fi headerFileInfo) IsDir() bool        { return fi.Mode().IsDir() }
79func (fi headerFileInfo) ModTime() time.Time { return fi.h.ModTime }
80func (fi headerFileInfo) Sys() interface{}   { return fi.h }
81
82// Name returns the base name of the file.
83func (fi headerFileInfo) Name() string {
84	if fi.IsDir() {
85		return path.Clean(fi.h.Name)
86	}
87	return fi.h.Name
88}
89
90// Mode returns the permission and mode bits for the headerFileInfo.
91func (fi headerFileInfo) Mode() (mode os.FileMode) {
92	// Set file permission bits.
93	mode = os.FileMode(fi.h.Mode).Perm()
94
95	// Set setuid, setgid and sticky bits.
96	if fi.h.Mode&c_ISUID != 0 {
97		// setuid
98		mode |= os.ModeSetuid
99	}
100	if fi.h.Mode&c_ISGID != 0 {
101		// setgid
102		mode |= os.ModeSetgid
103	}
104	if fi.h.Mode&c_ISVTX != 0 {
105		// sticky
106		mode |= os.ModeSticky
107	}
108
109	// Set file mode bits.
110	// clear perm, setuid, setgid and sticky bits.
111	m := os.FileMode(fi.h.Mode) &^ 07777
112	if m == c_ISDIR {
113		// directory
114		mode |= os.ModeDir
115	}
116	if m == c_ISFIFO {
117		// named pipe (FIFO)
118		mode |= os.ModeNamedPipe
119	}
120	if m == c_ISLNK {
121		// symbolic link
122		mode |= os.ModeSymlink
123	}
124	if m == c_ISBLK {
125		// device file
126		mode |= os.ModeDevice
127	}
128	if m == c_ISCHR {
129		// Unix character device
130		mode |= os.ModeDevice
131		mode |= os.ModeCharDevice
132	}
133	if m == c_ISSOCK {
134		// Unix domain socket
135		mode |= os.ModeSocket
136	}
137
138	switch fi.h.Typeflag {
139	case TypeLink, TypeSymlink:
140		// hard link, symbolic link
141		mode |= os.ModeSymlink
142	case TypeChar:
143		// character device node
144		mode |= os.ModeDevice
145		mode |= os.ModeCharDevice
146	case TypeBlock:
147		// block device node
148		mode |= os.ModeDevice
149	case TypeDir:
150		// directory
151		mode |= os.ModeDir
152	case TypeFifo:
153		// fifo node
154		mode |= os.ModeNamedPipe
155	}
156
157	return mode
158}
159
160// sysStat, if non-nil, populates h from system-dependent fields of fi.
161var sysStat func(fi os.FileInfo, h *Header) error
162
163// Mode constants from the tar spec.
164const (
165	c_ISUID  = 04000   // Set uid
166	c_ISGID  = 02000   // Set gid
167	c_ISVTX  = 01000   // Save text (sticky bit)
168	c_ISDIR  = 040000  // Directory
169	c_ISFIFO = 010000  // FIFO
170	c_ISREG  = 0100000 // Regular file
171	c_ISLNK  = 0120000 // Symbolic link
172	c_ISBLK  = 060000  // Block special file
173	c_ISCHR  = 020000  // Character special file
174	c_ISSOCK = 0140000 // Socket
175)
176
177// FileInfoHeader creates a partially-populated Header from fi.
178// If fi describes a symlink, FileInfoHeader records link as the link target.
179// If fi describes a directory, a slash is appended to the name.
180func FileInfoHeader(fi os.FileInfo, link string) (*Header, error) {
181	if fi == nil {
182		return nil, errors.New("tar: FileInfo is nil")
183	}
184	fm := fi.Mode()
185	h := &Header{
186		Name:    fi.Name(),
187		ModTime: fi.ModTime(),
188		Mode:    int64(fm.Perm()), // or'd with c_IS* constants later
189	}
190	switch {
191	case fm.IsRegular():
192		h.Mode |= c_ISREG
193		h.Typeflag = TypeReg
194		h.Size = fi.Size()
195	case fi.IsDir():
196		h.Typeflag = TypeDir
197		h.Mode |= c_ISDIR
198		h.Name += "/"
199	case fm&os.ModeSymlink != 0:
200		h.Typeflag = TypeSymlink
201		h.Mode |= c_ISLNK
202		h.Linkname = link
203	case fm&os.ModeDevice != 0:
204		if fm&os.ModeCharDevice != 0 {
205			h.Mode |= c_ISCHR
206			h.Typeflag = TypeChar
207		} else {
208			h.Mode |= c_ISBLK
209			h.Typeflag = TypeBlock
210		}
211	case fm&os.ModeNamedPipe != 0:
212		h.Typeflag = TypeFifo
213		h.Mode |= c_ISFIFO
214	case fm&os.ModeSocket != 0:
215		h.Mode |= c_ISSOCK
216	default:
217		return nil, fmt.Errorf("archive/tar: unknown file mode %v", fm)
218	}
219	if fm&os.ModeSetuid != 0 {
220		h.Mode |= c_ISUID
221	}
222	if fm&os.ModeSetgid != 0 {
223		h.Mode |= c_ISGID
224	}
225	if fm&os.ModeSticky != 0 {
226		h.Mode |= c_ISVTX
227	}
228	if sysStat != nil {
229		return h, sysStat(fi, h)
230	}
231	return h, nil
232}
233
234var zeroBlock = make([]byte, blockSize)
235
236// POSIX specifies a sum of the unsigned byte values, but the Sun tar uses signed byte values.
237// We compute and return both.
238func checksum(header []byte) (unsigned int64, signed int64) {
239	for i := 0; i < len(header); i++ {
240		if i == 148 {
241			// The chksum field (header[148:156]) is special: it should be treated as space bytes.
242			unsigned += ' ' * 8
243			signed += ' ' * 8
244			i += 7
245			continue
246		}
247		unsigned += int64(header[i])
248		signed += int64(int8(header[i]))
249	}
250	return
251}
252
253type slicer []byte
254
255func (sp *slicer) next(n int) (b []byte) {
256	s := *sp
257	b, *sp = s[0:n], s[n:]
258	return
259}
260