Skip to content

Commit

Permalink
Fix: Windows symlink permissions check (#619)
Browse files Browse the repository at this point in the history
  • Loading branch information
emmercm authored Aug 25, 2023
1 parent cb59eb1 commit 1dbfc9e
Show file tree
Hide file tree
Showing 5 changed files with 74 additions and 0 deletions.
44 changes: 44 additions & 0 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,7 @@
"fast-glob": "3.2.12",
"figlet": "1.6.0",
"graceful-fs": "4.2.11",
"is-admin": "4.0.0",
"junk": "4.0.1",
"micromatch": "4.0.5",
"moment": "2.29.4",
Expand Down
12 changes: 12 additions & 0 deletions src/igir.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,10 @@
import async from 'async';
import isAdmin from 'is-admin';

import Logger from './console/logger.js';
import ProgressBar, { ProgressBarSymbol } from './console/progressBar.js';
import ProgressBarCLI from './console/progressBarCLI.js';
import Constants from './constants.js';
import CandidateCombiner from './modules/candidateCombiner.js';
import CandidateGenerator from './modules/candidateGenerator.js';
import CandidatePatchGenerator from './modules/candidatePatchGenerator.js';
Expand All @@ -22,6 +24,7 @@ import ROMHeaderProcessor from './modules/romHeaderProcessor.js';
import ROMScanner from './modules/romScanner.js';
import StatusGenerator from './modules/statusGenerator.js';
import ArrayPoly from './polyfill/arrayPoly.js';
import FsPoly from './polyfill/fsPoly.js';
import DATStatus from './types/datStatus.js';
import File from './types/files/file.js';
import DAT from './types/logiqx/dat.js';
Expand All @@ -42,6 +45,15 @@ export default class Igir {
}

async main(): Promise<void> {
// Windows 10 may require admin privileges to symlink at all
// @see https://github.com/nodejs/node/issues/18518
if (this.options.shouldSymlink() && process.platform === 'win32' && !await FsPoly.canSymlink(Constants.GLOBAL_TEMP_DIR)) {
if (!await isAdmin()) {
throw new Error(`${Constants.COMMAND_NAME} does not have permissions to create symlinks, please try running as administrator`);
}
throw new Error(`${Constants.COMMAND_NAME} does not have permissions to create symlinks`);
}

// Scan and process input files
let dats = await this.processDATScanner();
const indexedRoms = await this.processROMScanner();
Expand Down
2 changes: 2 additions & 0 deletions src/modules/candidateWriter.ts
Original file line number Diff line number Diff line change
Expand Up @@ -338,6 +338,7 @@ export default class CandidateWriter extends Module {
const writtenTest = await this.testWrittenRaw(dat, outputFilePath, outputRomFile);
if (writtenTest) {
this.progressBar.logError(`${dat.getNameShort()}: ${releaseCandidate.getName()}: written file ${writtenTest}: ${outputFilePath}`);
return;
}
}
this.enqueueFileDeletion(inputRomFile);
Expand Down Expand Up @@ -455,6 +456,7 @@ export default class CandidateWriter extends Module {
await fsPoly.symlink(sourcePath, targetPath);
} catch (e) {
this.progressBar.logError(`${dat.getNameShort()}: ${inputRomFile.toString()}: failed to symlink ${sourcePath} to ${targetPath}: ${e}`);
return;
}

if (this.options.shouldTest()) {
Expand Down
15 changes: 15 additions & 0 deletions src/polyfill/fsPoly.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,21 @@ export default class FsPoly {
.map((info) => info.mounted)
.sort((a, b) => b.split(/[\\/]/).length - a.split(/[\\/]/).length);

static async canSymlink(tempDir: string): Promise<boolean> {
const source = await this.mktemp(path.join(tempDir, 'source'));
await this.touch(source);
const target = await this.mktemp(path.join(tempDir, 'target'));
try {
await this.symlink(source, target);
return await this.exists(target);
} catch (e) {
return false;
} finally {
await this.rm(source, { force: true });
await this.rm(target, { force: true });
}
}

static async copyDir(src: string, dest: string): Promise<void> {
await this.mkdir(dest, { recursive: true });
const entries = await util.promisify(fs.readdir)(src, { withFileTypes: true });
Expand Down

0 comments on commit 1dbfc9e

Please sign in to comment.