github.com/eun/go@v0.0.0-20170811110501-92cfd07a6cfd/src/archive/tar/common.go (about) 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 "strconv" 21 "time" 22 ) 23 24 // BUG: Use of the Uid and Gid fields in Header could overflow on 32-bit 25 // architectures. If a large value is encountered when decoding, the result 26 // stored in Header will be the truncated version. 27 28 // Header type flags. 29 const ( 30 TypeReg = '0' // regular file 31 TypeRegA = '\x00' // regular file 32 TypeLink = '1' // hard link 33 TypeSymlink = '2' // symbolic link 34 TypeChar = '3' // character device node 35 TypeBlock = '4' // block device node 36 TypeDir = '5' // directory 37 TypeFifo = '6' // fifo node 38 TypeCont = '7' // reserved 39 TypeXHeader = 'x' // extended header 40 TypeXGlobalHeader = 'g' // global extended header 41 TypeGNULongName = 'L' // Next file has a long name 42 TypeGNULongLink = 'K' // Next file symlinks to a file w/ a long name 43 TypeGNUSparse = 'S' // sparse file 44 ) 45 46 // A Header represents a single header in a tar archive. 47 // Some fields may not be populated. 48 type Header struct { 49 Name string // name of header file entry 50 Mode int64 // permission and mode bits 51 Uid int // user id of owner 52 Gid int // group id of owner 53 Size int64 // length in bytes 54 ModTime time.Time // modified time 55 Typeflag byte // type of header entry 56 Linkname string // target name of link 57 Uname string // user name of owner 58 Gname string // group name of owner 59 Devmajor int64 // major number of character or block device 60 Devminor int64 // minor number of character or block device 61 AccessTime time.Time // access time 62 ChangeTime time.Time // status change time 63 Xattrs map[string]string 64 } 65 66 // FileInfo returns an os.FileInfo for the Header. 67 func (h *Header) FileInfo() os.FileInfo { 68 return headerFileInfo{h} 69 } 70 71 // allowedFormats determines which formats can be used. The value returned 72 // is the logical OR of multiple possible formats. If the value is 73 // formatUnknown, then the input Header cannot be encoded. 74 // 75 // As a by-product of checking the fields, this function returns paxHdrs, which 76 // contain all fields that could not be directly encoded. 77 func (h *Header) allowedFormats() (format int, paxHdrs map[string]string) { 78 format = formatUSTAR | formatPAX | formatGNU 79 paxHdrs = make(map[string]string) 80 81 verifyString := func(s string, size int, gnuLong bool, paxKey string) { 82 // NUL-terminator is optional for path and linkpath. 83 // Technically, it is required for uname and gname, 84 // but neither GNU nor BSD tar checks for it. 85 tooLong := len(s) > size 86 if !isASCII(s) || (tooLong && !gnuLong) { 87 // TODO(dsnet): GNU supports UTF-8 (without NUL) for strings. 88 format &^= formatGNU // No GNU 89 } 90 if !isASCII(s) || tooLong { 91 // TODO(dsnet): If the path is splittable, it is possible to still 92 // use the USTAR format. 93 format &^= formatUSTAR // No USTAR 94 if paxKey == paxNone { 95 format &^= formatPAX // No PAX 96 } else { 97 paxHdrs[paxKey] = s 98 } 99 } 100 } 101 verifyNumeric := func(n int64, size int, paxKey string) { 102 if !fitsInBase256(size, n) { 103 format &^= formatGNU // No GNU 104 } 105 if !fitsInOctal(size, n) { 106 format &^= formatUSTAR // No USTAR 107 if paxKey == paxNone { 108 format &^= formatPAX // No PAX 109 } else { 110 paxHdrs[paxKey] = strconv.FormatInt(n, 10) 111 } 112 } 113 } 114 verifyTime := func(ts time.Time, size int, ustarField bool, paxKey string) { 115 if ts.IsZero() { 116 return // Always okay 117 } 118 needsNano := ts.Nanosecond() != 0 119 if !fitsInBase256(size, ts.Unix()) || needsNano { 120 format &^= formatGNU // No GNU 121 } 122 if !fitsInOctal(size, ts.Unix()) || needsNano || !ustarField { 123 format &^= formatUSTAR // No USTAR 124 if paxKey == paxNone { 125 format &^= formatPAX // No PAX 126 } else { 127 // TODO(dsnet): Support PAX time here. 128 // paxHdrs[paxKey] = formatPAXTime(ts) 129 } 130 } 131 } 132 133 // TODO(dsnet): Add GNU long name support. 134 const supportGNULong = false 135 136 var blk block 137 var v7 = blk.V7() 138 var ustar = blk.USTAR() 139 verifyString(h.Name, len(v7.Name()), supportGNULong, paxPath) 140 verifyString(h.Linkname, len(v7.LinkName()), supportGNULong, paxLinkpath) 141 verifyString(h.Uname, len(ustar.UserName()), false, paxUname) 142 verifyString(h.Gname, len(ustar.GroupName()), false, paxGname) 143 verifyNumeric(h.Mode, len(v7.Mode()), paxNone) 144 verifyNumeric(int64(h.Uid), len(v7.UID()), paxUid) 145 verifyNumeric(int64(h.Gid), len(v7.GID()), paxGid) 146 verifyNumeric(h.Size, len(v7.Size()), paxSize) 147 verifyNumeric(h.Devmajor, len(ustar.DevMajor()), paxNone) 148 verifyNumeric(h.Devminor, len(ustar.DevMinor()), paxNone) 149 verifyTime(h.ModTime, len(v7.ModTime()), true, paxMtime) 150 // TODO(dsnet): Support atime and ctime fields. 151 // verifyTime(h.AccessTime, len(gnu.AccessTime()), false, paxAtime) 152 // verifyTime(h.ChangeTime, len(gnu.ChangeTime()), false, paxCtime) 153 154 if !isHeaderOnlyType(h.Typeflag) && h.Size < 0 { 155 return formatUnknown, nil 156 } 157 if len(h.Xattrs) > 0 { 158 for k, v := range h.Xattrs { 159 paxHdrs[paxXattr+k] = v 160 } 161 format &= formatPAX // PAX only 162 } 163 for k, v := range paxHdrs { 164 if !validPAXRecord(k, v) { 165 return formatUnknown, nil // Invalid PAX key 166 } 167 } 168 return format, paxHdrs 169 } 170 171 // headerFileInfo implements os.FileInfo. 172 type headerFileInfo struct { 173 h *Header 174 } 175 176 func (fi headerFileInfo) Size() int64 { return fi.h.Size } 177 func (fi headerFileInfo) IsDir() bool { return fi.Mode().IsDir() } 178 func (fi headerFileInfo) ModTime() time.Time { return fi.h.ModTime } 179 func (fi headerFileInfo) Sys() interface{} { return fi.h } 180 181 // Name returns the base name of the file. 182 func (fi headerFileInfo) Name() string { 183 if fi.IsDir() { 184 return path.Base(path.Clean(fi.h.Name)) 185 } 186 return path.Base(fi.h.Name) 187 } 188 189 // Mode returns the permission and mode bits for the headerFileInfo. 190 func (fi headerFileInfo) Mode() (mode os.FileMode) { 191 // Set file permission bits. 192 mode = os.FileMode(fi.h.Mode).Perm() 193 194 // Set setuid, setgid and sticky bits. 195 if fi.h.Mode&c_ISUID != 0 { 196 // setuid 197 mode |= os.ModeSetuid 198 } 199 if fi.h.Mode&c_ISGID != 0 { 200 // setgid 201 mode |= os.ModeSetgid 202 } 203 if fi.h.Mode&c_ISVTX != 0 { 204 // sticky 205 mode |= os.ModeSticky 206 } 207 208 // Set file mode bits. 209 // clear perm, setuid, setgid and sticky bits. 210 m := os.FileMode(fi.h.Mode) &^ 07777 211 if m == c_ISDIR { 212 // directory 213 mode |= os.ModeDir 214 } 215 if m == c_ISFIFO { 216 // named pipe (FIFO) 217 mode |= os.ModeNamedPipe 218 } 219 if m == c_ISLNK { 220 // symbolic link 221 mode |= os.ModeSymlink 222 } 223 if m == c_ISBLK { 224 // device file 225 mode |= os.ModeDevice 226 } 227 if m == c_ISCHR { 228 // Unix character device 229 mode |= os.ModeDevice 230 mode |= os.ModeCharDevice 231 } 232 if m == c_ISSOCK { 233 // Unix domain socket 234 mode |= os.ModeSocket 235 } 236 237 switch fi.h.Typeflag { 238 case TypeSymlink: 239 // symbolic link 240 mode |= os.ModeSymlink 241 case TypeChar: 242 // character device node 243 mode |= os.ModeDevice 244 mode |= os.ModeCharDevice 245 case TypeBlock: 246 // block device node 247 mode |= os.ModeDevice 248 case TypeDir: 249 // directory 250 mode |= os.ModeDir 251 case TypeFifo: 252 // fifo node 253 mode |= os.ModeNamedPipe 254 } 255 256 return mode 257 } 258 259 // sysStat, if non-nil, populates h from system-dependent fields of fi. 260 var sysStat func(fi os.FileInfo, h *Header) error 261 262 const ( 263 // Mode constants from the USTAR spec: 264 // See http://pubs.opengroup.org/onlinepubs/9699919799/utilities/pax.html#tag_20_92_13_06 265 c_ISUID = 04000 // Set uid 266 c_ISGID = 02000 // Set gid 267 c_ISVTX = 01000 // Save text (sticky bit) 268 269 // Common Unix mode constants; these are not defined in any common tar standard. 270 // Header.FileInfo understands these, but FileInfoHeader will never produce these. 271 c_ISDIR = 040000 // Directory 272 c_ISFIFO = 010000 // FIFO 273 c_ISREG = 0100000 // Regular file 274 c_ISLNK = 0120000 // Symbolic link 275 c_ISBLK = 060000 // Block special file 276 c_ISCHR = 020000 // Character special file 277 c_ISSOCK = 0140000 // Socket 278 ) 279 280 // Keywords for the PAX Extended Header 281 const ( 282 paxAtime = "atime" 283 paxCharset = "charset" 284 paxComment = "comment" 285 paxCtime = "ctime" // please note that ctime is not a valid pax header. 286 paxGid = "gid" 287 paxGname = "gname" 288 paxLinkpath = "linkpath" 289 paxMtime = "mtime" 290 paxPath = "path" 291 paxSize = "size" 292 paxUid = "uid" 293 paxUname = "uname" 294 paxXattr = "SCHILY.xattr." 295 paxNone = "" 296 ) 297 298 // FileInfoHeader creates a partially-populated Header from fi. 299 // If fi describes a symlink, FileInfoHeader records link as the link target. 300 // If fi describes a directory, a slash is appended to the name. 301 // Because os.FileInfo's Name method returns only the base name of 302 // the file it describes, it may be necessary to modify the Name field 303 // of the returned header to provide the full path name of the file. 304 func FileInfoHeader(fi os.FileInfo, link string) (*Header, error) { 305 if fi == nil { 306 return nil, errors.New("tar: FileInfo is nil") 307 } 308 fm := fi.Mode() 309 h := &Header{ 310 Name: fi.Name(), 311 ModTime: fi.ModTime(), 312 Mode: int64(fm.Perm()), // or'd with c_IS* constants later 313 } 314 switch { 315 case fm.IsRegular(): 316 h.Typeflag = TypeReg 317 h.Size = fi.Size() 318 case fi.IsDir(): 319 h.Typeflag = TypeDir 320 h.Name += "/" 321 case fm&os.ModeSymlink != 0: 322 h.Typeflag = TypeSymlink 323 h.Linkname = link 324 case fm&os.ModeDevice != 0: 325 if fm&os.ModeCharDevice != 0 { 326 h.Typeflag = TypeChar 327 } else { 328 h.Typeflag = TypeBlock 329 } 330 case fm&os.ModeNamedPipe != 0: 331 h.Typeflag = TypeFifo 332 case fm&os.ModeSocket != 0: 333 return nil, fmt.Errorf("archive/tar: sockets not supported") 334 default: 335 return nil, fmt.Errorf("archive/tar: unknown file mode %v", fm) 336 } 337 if fm&os.ModeSetuid != 0 { 338 h.Mode |= c_ISUID 339 } 340 if fm&os.ModeSetgid != 0 { 341 h.Mode |= c_ISGID 342 } 343 if fm&os.ModeSticky != 0 { 344 h.Mode |= c_ISVTX 345 } 346 // If possible, populate additional fields from OS-specific 347 // FileInfo fields. 348 if sys, ok := fi.Sys().(*Header); ok { 349 // This FileInfo came from a Header (not the OS). Use the 350 // original Header to populate all remaining fields. 351 h.Uid = sys.Uid 352 h.Gid = sys.Gid 353 h.Uname = sys.Uname 354 h.Gname = sys.Gname 355 h.AccessTime = sys.AccessTime 356 h.ChangeTime = sys.ChangeTime 357 if sys.Xattrs != nil { 358 h.Xattrs = make(map[string]string) 359 for k, v := range sys.Xattrs { 360 h.Xattrs[k] = v 361 } 362 } 363 if sys.Typeflag == TypeLink { 364 // hard link 365 h.Typeflag = TypeLink 366 h.Size = 0 367 h.Linkname = sys.Linkname 368 } 369 } 370 if sysStat != nil { 371 return h, sysStat(fi, h) 372 } 373 return h, nil 374 } 375 376 // isHeaderOnlyType checks if the given type flag is of the type that has no 377 // data section even if a size is specified. 378 func isHeaderOnlyType(flag byte) bool { 379 switch flag { 380 case TypeLink, TypeSymlink, TypeChar, TypeBlock, TypeDir, TypeFifo: 381 return true 382 default: 383 return false 384 } 385 }