Skip to content

Commit

Permalink
Prevent dangling file handles.
Browse files Browse the repository at this point in the history
  • Loading branch information
emmercm committed Nov 5, 2022
1 parent fb4b21d commit 9b6c3c4
Show file tree
Hide file tree
Showing 8 changed files with 39 additions and 22 deletions.
2 changes: 1 addition & 1 deletion src/modules/candidateGenerator.ts
Original file line number Diff line number Diff line change
Expand Up @@ -78,7 +78,7 @@ export default class CandidateGenerator {
}
}

await this.progressBar.logInfo(`${dat.getName()}: Found ${releaseCandidates.length} candidates for ${parent}`);
await this.progressBar.logDebug(`${dat.getName()}: Found ${releaseCandidates.length} candidates for ${parent.getName()}`);
output.set(parent, releaseCandidates);
}

Expand Down
22 changes: 11 additions & 11 deletions src/modules/datScanner.ts
Original file line number Diff line number Diff line change
Expand Up @@ -65,20 +65,20 @@ export default class DATScanner extends Scanner {
return results.sort((a, b) => a.getNameShort().localeCompare(b.getNameShort()));
}

private async parseDatFile(datFile: File): Promise<DataFile | void> {
try {
await this.progressBar.logDebug(`${datFile.toString()}: parsing XML`);
return await datFile.extractToStream(async (stream) => {
private async parseDatFile(datFile: File): Promise<DataFile | undefined> {
await this.progressBar.logDebug(`${datFile.toString()}: parsing XML`);
return datFile.extractToStream(async (stream) => {
try {
const xmlContents = await bufferPoly.fromReadable(stream);
return xml2js.parseStringPromise(xmlContents.toString(), {
return await xml2js.parseStringPromise(xmlContents.toString(), {
mergeAttrs: true,
explicitArray: false,
});
});
} catch (err) {
const message = (err as Error).message.split('\n').join(', ');
await this.progressBar.logError(`Failed to parse DAT ${datFile.toString()} : ${message}`);
return Promise.resolve();
}
} catch (e) {
const message = (e as Error).message.split('\n').join(', ');
await this.progressBar.logError(`Failed to parse DAT ${datFile.toString()} : ${message}`);
return undefined;
}
});
}
}
15 changes: 11 additions & 4 deletions src/modules/outputCleaner.ts
Original file line number Diff line number Diff line change
Expand Up @@ -50,24 +50,31 @@ export default class OutputCleaner {
await this.progressBar.reset(filesToClean.length);

try {
await OutputCleaner.trashOrDelete(filesToClean);
await this.progressBar.logInfo(`Cleaning ${filesToClean.length.toLocaleString()} files`);
await this.trashOrDelete(filesToClean);
} catch (e) {
await this.progressBar.logError(`Failed to clean unmatched files in ${outputDir} : ${e}`);
}

try {
const emptyDirs = await OutputCleaner.getEmptyDirs(outputDir);
await OutputCleaner.trashOrDelete(emptyDirs);
await this.progressBar.logInfo(`Cleaning ${emptyDirs.length.toLocaleString()} empty directories`);
await this.trashOrDelete(emptyDirs);
} catch (e) {
await this.progressBar.logError(`Failed to clean empty directories in ${outputDir} : ${e}`);
}

return filesToClean.length;
}

private static async trashOrDelete(filePaths: string[]): Promise<void> {
private async trashOrDelete(filePaths: string[]): Promise<void> {
// Prefer recycling...
await trash(filePaths);
const CHUNK_SIZE = 100;
/* eslint-disable no-await-in-loop */
for (let i = 0; i < filePaths.length; i += CHUNK_SIZE) {
await this.progressBar.update(i);
await trash(filePaths.slice(i, i + CHUNK_SIZE));
}

// ...but if that doesn't work, delete the leftovers
await Promise.all(filePaths.map(async (filePath) => {
Expand Down
1 change: 1 addition & 0 deletions src/types/archives/zip.ts
Original file line number Diff line number Diff line change
Expand Up @@ -80,6 +80,7 @@ export default class Zip extends Archive {
return reject(callbackErr);
}
});
writeStream.on('error', (err) => reject(err));
readStream.pipe(writeStream);
}),
);
Expand Down
16 changes: 10 additions & 6 deletions src/types/files/file.ts
Original file line number Diff line number Diff line change
Expand Up @@ -151,9 +151,11 @@ export default class File {
`${path.basename(this.getFilePath())}.temp`,
));
await fsPromises.copyFile(this.getFilePath(), temp);
const result = await callback(temp);
await fsPoly.rm(temp);
return result;
try {
return await callback(temp);
} finally {
await fsPoly.rm(temp);
}
}

async extractToStream<T>(
Expand All @@ -180,9 +182,11 @@ export default class File {
callback: (stream: Readable) => (Promise<T> | T),
): Promise<T> {
const stream = fs.createReadStream(filePath, { start });
const result = await callback(stream);
stream.destroy();
return result;
try {
return await callback(stream);
} finally {
stream.destroy();
}
}

async withFileName(fileNameWithoutExt: string): Promise<File> {
Expand Down
1 change: 1 addition & 0 deletions src/types/patches/bpsPatch.ts
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,7 @@ export default class BPSPatch extends Patch {
// Skip header info
const header = await fp.readNext(4);
if (header.toString() !== 'BPS1') {
await fp.close();
throw new Error(`BPS patch header is invalid: ${this.getFile().toString()}`);
}
await BPSPatch.readNumber(fp); // source size
Expand Down
1 change: 1 addition & 0 deletions src/types/patches/ipsPatch.ts
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@ export default class IPSPatch extends Patch {

const header = (await fp.readNext(5)).toString();
if (header !== 'PATCH') {
await fp.close();
throw new Error(`IPS patch header is invalid: ${this.getFile().toString()}`);
}

Expand Down
3 changes: 3 additions & 0 deletions src/types/patches/ppfPatch.ts
Original file line number Diff line number Diff line change
Expand Up @@ -29,11 +29,13 @@ export default class PPFPatch extends Patch {

const header = (await fp.readNext(5)).toString();
if (!header.startsWith('PPF')) {
await fp.close();
throw new Error(`PPF patch header is invalid: ${this.getFile().toString()}`);
}
const encoding = (await fp.readNext(1))[0];
const version = encoding + 1;
if (!header.endsWith(`${version}0`)) {
await fp.close();
throw new Error(`PPF patch header is invalid: ${this.getFile().toString()}`);
}

Expand All @@ -50,6 +52,7 @@ export default class PPFPatch extends Patch {
undoDataAvailable = (await fp.readNext(1)).readUInt8() === 0x01;
await fp.readNext(1); // dummy
} else {
await fp.close();
throw new Error(`PPF v${version} isn't supported: ${this.getFile().toString()}`);
}

Expand Down

0 comments on commit 9b6c3c4

Please sign in to comment.