|
|
|
// field paths that every tar file must have.
|
|
|
|
// header is padded to 512 bytes.
|
|
|
|
var f = 0
|
|
|
|
, fields = {}
|
|
|
|
, path = fields.path = f++
|
|
|
|
, mode = fields.mode = f++
|
|
|
|
, uid = fields.uid = f++
|
|
|
|
, gid = fields.gid = f++
|
|
|
|
, size = fields.size = f++
|
|
|
|
, mtime = fields.mtime = f++
|
|
|
|
, cksum = fields.cksum = f++
|
|
|
|
, type = fields.type = f++
|
|
|
|
, linkpath = fields.linkpath = f++
|
|
|
|
, headerSize = 512
|
|
|
|
, blockSize = 512
|
|
|
|
, fieldSize = []
|
|
|
|
|
|
|
|
fieldSize[path] = 100
|
|
|
|
fieldSize[mode] = 8
|
|
|
|
fieldSize[uid] = 8
|
|
|
|
fieldSize[gid] = 8
|
|
|
|
fieldSize[size] = 12
|
|
|
|
fieldSize[mtime] = 12
|
|
|
|
fieldSize[cksum] = 8
|
|
|
|
fieldSize[type] = 1
|
|
|
|
fieldSize[linkpath] = 100
|
|
|
|
|
|
|
|
// "ustar\0" may introduce another bunch of headers.
|
|
|
|
// these are optional, and will be nulled out if not present.
|
|
|
|
|
|
|
|
var ustar = fields.ustar = f++
|
|
|
|
, ustarver = fields.ustarver = f++
|
|
|
|
, uname = fields.uname = f++
|
|
|
|
, gname = fields.gname = f++
|
|
|
|
, devmaj = fields.devmaj = f++
|
|
|
|
, devmin = fields.devmin = f++
|
|
|
|
, prefix = fields.prefix = f++
|
|
|
|
, fill = fields.fill = f++
|
|
|
|
|
|
|
|
// terminate fields.
|
|
|
|
fields[f] = null
|
|
|
|
|
|
|
|
fieldSize[ustar] = 6
|
|
|
|
fieldSize[ustarver] = 2
|
|
|
|
fieldSize[uname] = 32
|
|
|
|
fieldSize[gname] = 32
|
|
|
|
fieldSize[devmaj] = 8
|
|
|
|
fieldSize[devmin] = 8
|
|
|
|
fieldSize[prefix] = 155
|
|
|
|
fieldSize[fill] = 12
|
|
|
|
|
|
|
|
// nb: prefix field may in fact be 130 bytes of prefix,
|
|
|
|
// a null char, 12 bytes for atime, 12 bytes for ctime.
|
|
|
|
//
|
|
|
|
// To recognize this format:
|
|
|
|
// 1. prefix[130] === ' ' or '\0'
|
|
|
|
// 2. atime and ctime are octal numeric values
|
|
|
|
// 3. atime and ctime have ' ' in their last byte
|
|
|
|
|
|
|
|
var fieldEnds = {}
|
|
|
|
, fieldOffs = {}
|
|
|
|
, fe = 0
|
|
|
|
for (var i = 0; i < f; i ++) {
|
|
|
|
fieldOffs[i] = fe
|
|
|
|
fieldEnds[i] = (fe += fieldSize[i])
|
|
|
|
}
|
|
|
|
|
|
|
|
// build a translation table of field paths.
|
|
|
|
Object.keys(fields).forEach(function (f) {
|
|
|
|
if (fields[f] !== null) fields[fields[f]] = f
|
|
|
|
})
|
|
|
|
|
|
|
|
// different values of the 'type' field
|
|
|
|
// paths match the values of Stats.isX() functions, where appropriate
|
|
|
|
var types =
|
|
|
|
{ 0: "File"
|
|
|
|
, "\0": "OldFile" // like 0
|
|
|
|
, "": "OldFile"
|
|
|
|
, 1: "Link"
|
|
|
|
, 2: "SymbolicLink"
|
|
|
|
, 3: "CharacterDevice"
|
|
|
|
, 4: "BlockDevice"
|
|
|
|
, 5: "Directory"
|
|
|
|
, 6: "FIFO"
|
|
|
|
, 7: "ContiguousFile" // like 0
|
|
|
|
// posix headers
|
|
|
|
, g: "GlobalExtendedHeader" // k=v for the rest of the archive
|
|
|
|
, x: "ExtendedHeader" // k=v for the next file
|
|
|
|
// vendor-specific stuff
|
|
|
|
, A: "SolarisACL" // skip
|
|
|
|
, D: "GNUDumpDir" // like 5, but with data, which should be skipped
|
|
|
|
, I: "Inode" // metadata only, skip
|
|
|
|
, K: "NextFileHasLongLinkpath" // data = link path of next file
|
|
|
|
, L: "NextFileHasLongPath" // data = path of next file
|
|
|
|
, M: "ContinuationFile" // skip
|
|
|
|
, N: "OldGnuLongPath" // like L
|
|
|
|
, S: "SparseFile" // skip
|
|
|
|
, V: "TapeVolumeHeader" // skip
|
|
|
|
, X: "OldExtendedHeader" // like x
|
|
|
|
}
|
|
|
|
|
|
|
|
Object.keys(types).forEach(function (t) {
|
|
|
|
types[types[t]] = types[types[t]] || t
|
|
|
|
})
|
|
|
|
|
|
|
|
// values for the mode field
|
|
|
|
var modes =
|
|
|
|
{ suid: 04000 // set uid on extraction
|
|
|
|
, sgid: 02000 // set gid on extraction
|
|
|
|
, svtx: 01000 // set restricted deletion flag on dirs on extraction
|
|
|
|
, uread: 0400
|
|
|
|
, uwrite: 0200
|
|
|
|
, uexec: 0100
|
|
|
|
, gread: 040
|
|
|
|
, gwrite: 020
|
|
|
|
, gexec: 010
|
|
|
|
, oread: 4
|
|
|
|
, owrite: 2
|
|
|
|
, oexec: 1
|
|
|
|
, all: 07777
|
|
|
|
}
|
|
|
|
|
|
|
|
var numeric =
|
|
|
|
{ mode: true
|
|
|
|
, uid: true
|
|
|
|
, gid: true
|
|
|
|
, size: true
|
|
|
|
, mtime: true
|
|
|
|
, devmaj: true
|
|
|
|
, devmin: true
|
|
|
|
, cksum: true
|
|
|
|
, atime: true
|
|
|
|
, ctime: true
|
|
|
|
, dev: true
|
|
|
|
, ino: true
|
|
|
|
, nlink: true
|
|
|
|
}
|
|
|
|
|
|
|
|
Object.keys(modes).forEach(function (t) {
|
|
|
|
modes[modes[t]] = modes[modes[t]] || t
|
|
|
|
})
|
|
|
|
|
|
|
|
var knownExtended =
|
|
|
|
{ atime: true
|
|
|
|
, charset: true
|
|
|
|
, comment: true
|
|
|
|
, ctime: true
|
|
|
|
, gid: true
|
|
|
|
, gname: true
|
|
|
|
, linkpath: true
|
|
|
|
, mtime: true
|
|
|
|
, path: true
|
|
|
|
, realtime: true
|
|
|
|
, security: true
|
|
|
|
, size: true
|
|
|
|
, uid: true
|
|
|
|
, uname: true }
|
|
|
|
|
|
|
|
|
|
|
|
exports.fields = fields
|
|
|
|
exports.fieldSize = fieldSize
|
|
|
|
exports.fieldOffs = fieldOffs
|
|
|
|
exports.fieldEnds = fieldEnds
|
|
|
|
exports.types = types
|
|
|
|
exports.modes = modes
|
|
|
|
exports.numeric = numeric
|
|
|
|
exports.headerSize = headerSize
|
|
|
|
exports.blockSize = blockSize
|
|
|
|
exports.knownExtended = knownExtended
|
|
|
|
|
|
|
|
exports.Pack = require("./lib/pack.js")
|
|
|
|
exports.Parse = require("./lib/parse.js")
|
|
|
|
exports.Extract = require("./lib/extract.js")
|