You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
143 lines
3.9 KiB
143 lines
3.9 KiB
'use strict' |
|
const Header = require('./header.js') |
|
const path = require('path') |
|
|
|
class Pax { |
|
constructor (obj, global) { |
|
this.atime = obj.atime || null |
|
this.charset = obj.charset || null |
|
this.comment = obj.comment || null |
|
this.ctime = obj.ctime || null |
|
this.gid = obj.gid || null |
|
this.gname = obj.gname || null |
|
this.linkpath = obj.linkpath || null |
|
this.mtime = obj.mtime || null |
|
this.path = obj.path || null |
|
this.size = obj.size || null |
|
this.uid = obj.uid || null |
|
this.uname = obj.uname || null |
|
this.dev = obj.dev || null |
|
this.ino = obj.ino || null |
|
this.nlink = obj.nlink || null |
|
this.global = global || false |
|
} |
|
|
|
encode () { |
|
const body = this.encodeBody() |
|
if (body === '') |
|
return null |
|
|
|
const bodyLen = Buffer.byteLength(body) |
|
// round up to 512 bytes |
|
// add 512 for header |
|
const bufLen = 512 * Math.ceil(1 + bodyLen / 512) |
|
const buf = Buffer.allocUnsafe(bufLen) |
|
|
|
// 0-fill the header section, it might not hit every field |
|
for (let i = 0; i < 512; i++) |
|
buf[i] = 0 |
|
|
|
new Header({ |
|
// XXX split the path |
|
// then the path should be PaxHeader + basename, but less than 99, |
|
// prepend with the dirname |
|
path: ('PaxHeader/' + path.basename(this.path)).slice(0, 99), |
|
mode: this.mode || 0o644, |
|
uid: this.uid || null, |
|
gid: this.gid || null, |
|
size: bodyLen, |
|
mtime: this.mtime || null, |
|
type: this.global ? 'GlobalExtendedHeader' : 'ExtendedHeader', |
|
linkpath: '', |
|
uname: this.uname || '', |
|
gname: this.gname || '', |
|
devmaj: 0, |
|
devmin: 0, |
|
atime: this.atime || null, |
|
ctime: this.ctime || null, |
|
}).encode(buf) |
|
|
|
buf.write(body, 512, bodyLen, 'utf8') |
|
|
|
// null pad after the body |
|
for (let i = bodyLen + 512; i < buf.length; i++) |
|
buf[i] = 0 |
|
|
|
return buf |
|
} |
|
|
|
encodeBody () { |
|
return ( |
|
this.encodeField('path') + |
|
this.encodeField('ctime') + |
|
this.encodeField('atime') + |
|
this.encodeField('dev') + |
|
this.encodeField('ino') + |
|
this.encodeField('nlink') + |
|
this.encodeField('charset') + |
|
this.encodeField('comment') + |
|
this.encodeField('gid') + |
|
this.encodeField('gname') + |
|
this.encodeField('linkpath') + |
|
this.encodeField('mtime') + |
|
this.encodeField('size') + |
|
this.encodeField('uid') + |
|
this.encodeField('uname') |
|
) |
|
} |
|
|
|
encodeField (field) { |
|
if (this[field] === null || this[field] === undefined) |
|
return '' |
|
const v = this[field] instanceof Date ? this[field].getTime() / 1000 |
|
: this[field] |
|
const s = ' ' + |
|
(field === 'dev' || field === 'ino' || field === 'nlink' |
|
? 'SCHILY.' : '') + |
|
field + '=' + v + '\n' |
|
const byteLen = Buffer.byteLength(s) |
|
// the digits includes the length of the digits in ascii base-10 |
|
// so if it's 9 characters, then adding 1 for the 9 makes it 10 |
|
// which makes it 11 chars. |
|
let digits = Math.floor(Math.log(byteLen) / Math.log(10)) + 1 |
|
if (byteLen + digits >= Math.pow(10, digits)) |
|
digits += 1 |
|
const len = digits + byteLen |
|
return len + s |
|
} |
|
} |
|
|
|
Pax.parse = (string, ex, g) => new Pax(merge(parseKV(string), ex), g) |
|
|
|
const merge = (a, b) => |
|
b ? Object.keys(a).reduce((s, k) => (s[k] = a[k], s), b) : a |
|
|
|
const parseKV = string => |
|
string |
|
.replace(/\n$/, '') |
|
.split('\n') |
|
.reduce(parseKVLine, Object.create(null)) |
|
|
|
const parseKVLine = (set, line) => { |
|
const n = parseInt(line, 10) |
|
|
|
// XXX Values with \n in them will fail this. |
|
// Refactor to not be a naive line-by-line parse. |
|
if (n !== Buffer.byteLength(line) + 1) |
|
return set |
|
|
|
line = line.substr((n + ' ').length) |
|
const kv = line.split('=') |
|
const k = kv.shift().replace(/^SCHILY\.(dev|ino|nlink)/, '$1') |
|
if (!k) |
|
return set |
|
|
|
const v = kv.join('=') |
|
set[k] = /^([A-Z]+\.)?([mac]|birth|creation)time$/.test(k) |
|
? new Date(v * 1000) |
|
: /^[0-9]+$/.test(v) ? +v |
|
: v |
|
return set |
|
} |
|
|
|
module.exports = Pax
|
|
|