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 13 package tar 14 15 import ( 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. 28 const ( 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. 47 type 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. 66 func (h *Header) FileInfo() os.FileInfo { 67 return headerFileInfo{h} 68 } 69 70 // headerFileInfo implements os.FileInfo. 71 type headerFileInfo struct { 72 h *Header 73 } 74 75 func (fi headerFileInfo) Size() int64 { return fi.h.Size } 76 func (fi headerFileInfo) IsDir() bool { return fi.Mode().IsDir() } 77 func (fi headerFileInfo) ModTime() time.Time { return fi.h.ModTime } 78 func (fi headerFileInfo) Sys() interface{} { return fi.h } 79 80 // Name returns the base name of the file. 81 func (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. 89 func (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. 159 var sysStat func(fi os.FileInfo, h *Header) error 160 161 // Mode constants from the tar spec. 162 const ( 163 c_ISUID = 04000 // Set uid 164 c_ISGID = 02000 // Set gid 165 c_ISVTX = 01000 // Save text (sticky bit) 166 c_ISDIR = 040000 // Directory 167 c_ISFIFO = 010000 // FIFO 168 c_ISREG = 0100000 // Regular file 169 c_ISLNK = 0120000 // Symbolic link 170 c_ISBLK = 060000 // Block special file 171 c_ISCHR = 020000 // Character special file 172 c_ISSOCK = 0140000 // Socket 173 ) 174 175 // Keywords for the PAX Extended Header 176 const ( 177 paxAtime = "atime" 178 paxCharset = "charset" 179 paxComment = "comment" 180 paxCtime = "ctime" // please note that ctime is not a valid pax header. 181 paxGid = "gid" 182 paxGname = "gname" 183 paxLinkpath = "linkpath" 184 paxMtime = "mtime" 185 paxPath = "path" 186 paxSize = "size" 187 paxUid = "uid" 188 paxUname = "uname" 189 paxXattr = "SCHILY.xattr." 190 paxNone = "" 191 ) 192 193 // FileInfoHeader creates a partially-populated Header from fi. 194 // If fi describes a symlink, FileInfoHeader records link as the link target. 195 // If fi describes a directory, a slash is appended to the name. 196 // Because os.FileInfo's Name method returns only the base name of 197 // the file it describes, it may be necessary to modify the Name field 198 // of the returned header to provide the full path name of the file. 199 func FileInfoHeader(fi os.FileInfo, link string) (*Header, error) { 200 if fi == nil { 201 return nil, errors.New("tar: FileInfo is nil") 202 } 203 fm := fi.Mode() 204 h := &Header{ 205 Name: fi.Name(), 206 ModTime: fi.ModTime(), 207 Mode: int64(fm.Perm()), // or'd with c_IS* constants later 208 } 209 switch { 210 case fm.IsRegular(): 211 h.Mode |= c_ISREG 212 h.Typeflag = TypeReg 213 h.Size = fi.Size() 214 case fi.IsDir(): 215 h.Typeflag = TypeDir 216 h.Mode |= c_ISDIR 217 h.Name += "/" 218 case fm&os.ModeSymlink != 0: 219 h.Typeflag = TypeSymlink 220 h.Mode |= c_ISLNK 221 h.Linkname = link 222 case fm&os.ModeDevice != 0: 223 if fm&os.ModeCharDevice != 0 { 224 h.Mode |= c_ISCHR 225 h.Typeflag = TypeChar 226 } else { 227 h.Mode |= c_ISBLK 228 h.Typeflag = TypeBlock 229 } 230 case fm&os.ModeNamedPipe != 0: 231 h.Typeflag = TypeFifo 232 h.Mode |= c_ISFIFO 233 case fm&os.ModeSocket != 0: 234 h.Mode |= c_ISSOCK 235 default: 236 return nil, fmt.Errorf("archive/tar: unknown file mode %v", fm) 237 } 238 if fm&os.ModeSetuid != 0 { 239 h.Mode |= c_ISUID 240 } 241 if fm&os.ModeSetgid != 0 { 242 h.Mode |= c_ISGID 243 } 244 if fm&os.ModeSticky != 0 { 245 h.Mode |= c_ISVTX 246 } 247 // If possible, populate additional fields from OS-specific 248 // FileInfo fields. 249 if sys, ok := fi.Sys().(*Header); ok { 250 // This FileInfo came from a Header (not the OS). Use the 251 // original Header to populate all remaining fields. 252 h.Uid = sys.Uid 253 h.Gid = sys.Gid 254 h.Uname = sys.Uname 255 h.Gname = sys.Gname 256 h.AccessTime = sys.AccessTime 257 h.ChangeTime = sys.ChangeTime 258 if sys.Xattrs != nil { 259 h.Xattrs = make(map[string]string) 260 for k, v := range sys.Xattrs { 261 h.Xattrs[k] = v 262 } 263 } 264 if sys.Typeflag == TypeLink { 265 // hard link 266 h.Typeflag = TypeLink 267 h.Size = 0 268 h.Linkname = sys.Linkname 269 } 270 } 271 if sysStat != nil { 272 return h, sysStat(fi, h) 273 } 274 return h, nil 275 } 276 277 // isHeaderOnlyType checks if the given type flag is of the type that has no 278 // data section even if a size is specified. 279 func isHeaderOnlyType(flag byte) bool { 280 switch flag { 281 case TypeLink, TypeSymlink, TypeChar, TypeBlock, TypeDir, TypeFifo: 282 return true 283 default: 284 return false 285 } 286 } 287