Skip to content

Instantly share code, notes, and snippets.

@retrohacker
Created February 6, 2020 18:00
Show Gist options
  • Save retrohacker/f5ec254eeef99a3f7200cbe32d28e5a3 to your computer and use it in GitHub Desktop.
Save retrohacker/f5ec254eeef99a3f7200cbe32d28e5a3 to your computer and use it in GitHub Desktop.
Modify a tarball's header content
const fs = require('fs');
const Header = require('tar/lib/header');
const fd = fs.openSync('./service.tar', 'r+');
const childProcess = require('child_process');
const args = require('yargs')
.option('u', {
alias: 'user',
demandOption: true,
describe: 'the user to set permission bits to',
type: 'string'
})
.argv
const file = args._[0];
if(!file) {
throw new Error('filename is required');
}
// Ensure file exists
fs.statSync(file);
// Resolve username to uid/gid/group
const user = args.user;
const uid = childProcess.execSync(`id -u ${user}`).toString().trim();
const gid = childProcess.execSync(`id -g ${user}`).toString().trim();
const group = childProcess.execSync(`getent group ${gid}`).toString().trim().split(':')[0];
console.log(user, uid, group, gid);
// Tar stores uid and gid in octal
const uidString = Number(uid).toString(8);
const gidString = Number(uid).toString(8);
// Tar blocks are 512 bytes each. Anything that doesn't fill a block gets zero
// padded.
let block = 0;
let BLOCK_SIZE = 512;
const buffer = Buffer.alloc(BLOCK_SIZE);
let exit = false;
while(true) {
const bytesRead = fs.readSync(fd, buffer, 0, BLOCK_SIZE, block * BLOCK_SIZE);
const header = new Header(buffer);
if(header.nullBlock === false && header.cksumValid === false) {
console.log(header);
throw new Error('Invalid checksum!')
}
if(header.nullBlock === true && exit === true) {
break;
}
if(header.nullBlock === true) {
exit = true;
continue;
}
const oldUid = header.uid;
const oldGid = header.gid;
// Zero out the uid/user/gid/group blocks in the header
buffer.fill(0, 108, 124) // uid and gid
buffer.fill(0, 265, 329) // username and groupname
buffer.fill(0, 148, 156) // chksum
buffer.write(uidString, 108, 8, 'ascii');
buffer.write(gidString, 116, 8, 'ascii');
buffer.write(user, 265, 32, 'ascii');
buffer.write(group, 297, 32, 'ascii');
// Recompute chksum
let sum = 256
for (let i = 0; i < 0 + 512; i++) {
sum += buffer[i]
}
buffer.write(sum.toString(8), 148, 8, 'ascii');
const newHeader = new Header(buffer);
console.log(`${header.path} user: ${header.uid} => ${newHeader.uid} && group: ${header.gid} => ${newHeader.gid} && cksum: ${header.cksum} => ${newHeader.cksum}`);
// Update the header on disk
fs.writeSync(fd, buffer, 0, BLOCK_SIZE, block * BLOCK_SIZE);
let seekTo = ( block * BLOCK_SIZE ) + header.size;
while(block * BLOCK_SIZE < seekTo) {
block = block + 1;
}
block = block + 1;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment