tgz: handle addEntryToPack to error

This commit is contained in:
Girish Ramakrishnan
2024-07-18 13:40:36 +02:00
parent b3c301fc2a
commit 885aac69c5
2 changed files with 52 additions and 41 deletions

View File

@@ -34,7 +34,7 @@ function getBackupFilePath(backupConfig, remotePath) {
return path.join(rootPath, remotePath + fileType);
}
function addToPack(pack, header, options) {
function addEntryToPack(pack, header, options) {
assert.strictEqual(typeof pack, 'object');
assert.strictEqual(typeof header, 'object');
assert.strictEqual(typeof options, 'object'); // { input }
@@ -56,6 +56,52 @@ function addToPack(pack, header, options) {
});
}
async function addPathToPack(pack, localPath, dataLayout) {
assert.strictEqual(typeof pack, 'object');
assert(dataLayout instanceof DataLayout, 'dataLayout must be a DataLayout');
assert.strictEqual(typeof localPath, 'string');
const queue = [ localPath ];
while (queue.length) {
// if (pack.destroyed || outStream.destroyed) break;
const dir = queue.shift();
debug(`tarPack: processing ${dir}`);
const [readdirError, entries] = await safe(fs.promises.readdir(dir, { withFileTypes: true }));
if (!entries) {
debug(`tarPack: skipping directory ${dir}: ${readdirError.message}`);
continue;
}
const subdirs = [];
for (const entry of entries) {
const abspath = path.join(dir, entry.name);
const headerName = dataLayout.toRemotePath(abspath);
if (entry.isFile()) {
const [openError, handle] = await safe(fs.promises.open(abspath, 'r'));
if (!handle) { debug(`tarPack: skipping file, could not open ${abspath}: ${openError.message}`); continue; }
const [statError, stat] = await safe(handle.stat());
if (!stat) { debug(`tarPack: skipping file, could not stat ${abspath}: ${statError.message}`); continue; }
const header = { name: headerName, type: 'file', mode: stat.mode, size: stat.size, uid: process.getuid(), gid: process.getgid() };
if (stat.size > 8589934590 || entry.name.length > 99) header.pax = { size: stat.size };
const input = handle.createReadStream({ autoClose: true });
await addEntryToPack(pack, header, { input });
} else if (entry.isDirectory()) {
const header = { name: headerName, type: 'directory', uid: process.getuid(), gid: process.getgid() };
subdirs.push(abspath);
await addEntryToPack(pack, header, { /* options */ });
} else if (entry.isSymbolicLink()) {
const [readlinkError, target] = await safe(fs.promises.readlink(abspath));
if (!target) { debug(`tarPack: skipping link, could not readlink ${abspath}: ${readlinkError.message}`); continue; }
const header = { name: headerName, type: 'symlink', linkname: target, uid: process.getuid(), gid: process.getgid() };
await addEntryToPack(pack, header, { /* options */ });
} else {
debug(`tarPack: ignoring unknown type ${entry.name} ${entry.type}`);
}
}
queue.unshift(...subdirs); // add to front of queue and in order of readdir listing
}
}
async function tarPack(dataLayout, encryption, uploader, progressCallback) {
assert(dataLayout instanceof DataLayout, 'dataLayout must be a DataLayout');
assert.strictEqual(typeof encryption, 'object');
@@ -81,48 +127,11 @@ async function tarPack(dataLayout, encryption, uploader, progressCallback) {
}
for (const localPath of dataLayout.localPaths()) {
const queue = [ localPath ];
while (queue.length) {
// if (pack.destroyed || outStream.destroyed) break;
const dir = queue.shift();
debug(`tarPack: processing ${dir}`);
const [readdirError, entries] = await safe(fs.promises.readdir(dir, { withFileTypes: true }));
if (!entries) {
debug(`tarPack: skipping directory ${dir}: ${readdirError.message}`);
continue;
}
const subdirs = [];
for (const entry of entries) {
const abspath = path.join(dir, entry.name);
const headerName = dataLayout.toRemotePath(abspath);
if (entry.isFile()) {
const [openError, handle] = await safe(fs.promises.open(abspath, 'r'));
if (!handle) { debug(`tarPack: skipping file, could not open ${abspath}: ${openError.message}`); continue; }
const [statError, stat] = await safe(handle.stat());
if (!stat) { debug(`tarPack: skipping file, could not stat ${abspath}: ${statError.message}`); continue; }
const header = { name: headerName, type: 'file', mode: stat.mode, size: stat.size, uid: process.getuid(), gid: process.getgid() };
if (stat.size > 8589934590 || entry.name.length > 99) header.pax = { size: stat.size };
const input = handle.createReadStream({ autoClose: true });
await addToPack(pack, header, { input });
} else if (entry.isDirectory()) {
const header = { name: headerName, type: 'directory', uid: process.getuid(), gid: process.getgid() };
await addToPack(pack, header);
subdirs.push(abspath);
} else if (entry.isSymbolicLink()) {
const [readlinkError, target] = await safe(fs.promises.readlink(abspath));
if (!target) { debug(`tarPack: skipping link, could not readlink ${abspath}: ${readlinkError.message}`); continue; }
const header = { name: headerName, type: 'symlink', linkname: target, uid: process.getuid(), gid: process.getgid() };
await addToPack(pack, header);
} else {
debug(`tarPack: ignoring unknown type ${entry.name} ${entry.type}`);
}
}
queue.unshift(...subdirs); // add to front of queue and in order of readdir listing
}
const [error] = await safe(addPathToPack(pack, localPath, dataLayout), { debug });
if (error) break; // the pipeline will error and we will retry the whole packing all over
}
pack.finalize();
pack.finalize(); // harmless to call if already in error state
const [error] = await pipeline; // already wrapped in safe()
if (error) throw new BoxError(BoxError.EXTERNAL_ERROR, `tarPack pipeline error: ${error.message}`);