Skip to content
This repository has been archived by the owner on Jan 13, 2024. It is now read-only.

fix: copyFile and copyFileSync patch #1484

Merged
merged 4 commits into from
Feb 3, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
103 changes: 90 additions & 13 deletions prelude/bootstrap.js
Original file line number Diff line number Diff line change
Expand Up @@ -158,19 +158,28 @@ function createMountpoint(interior, exterior) {
mountpoints.push({ interior, exterior });
}

function copyFileSync(source, target) {
let targetFile = target;

// If target is a directory, a new file with the same name will be created
if (fs.existsSync(target)) {
if (fs.lstatSync(target).isDirectory()) {
targetFile = path.join(target, path.basename(source));
}
const DEFAULT_COPY_CHUNK_SIZE = 10 * 1024 * 1024; // 10 MB
function copyInChunks(
source,
target,
chunkSize = DEFAULT_COPY_CHUNK_SIZE,
fs_ = fs
) {
const sourceFile = fs_.openSync(source, 'r');
const targetFile = fs_.openSync(target, 'w');

let bytesRead = 1;
while (bytesRead > 0) {
const buffer = Buffer.alloc(chunkSize);
bytesRead = fs_.readSync(sourceFile, buffer, 0, chunkSize);
fs_.writeSync(targetFile, buffer, 0, bytesRead);
}

fs.writeFileSync(targetFile, fs.readFileSync(source));
fs_.closeSync(sourceFile);
fs_.closeSync(targetFile);
}

// TODO: replace this with fs.cpSync when we drop Node < 16
function copyFolderRecursiveSync(source, target) {
let files = [];

Expand All @@ -188,7 +197,10 @@ function copyFolderRecursiveSync(source, target) {
if (fs.lstatSync(curSource).isDirectory()) {
copyFolderRecursiveSync(curSource, targetFolder);
} else {
copyFileSync(curSource, targetFolder);
fs.copyFileSync(
curSource,
path.join(targetFolder, path.basename(curSource))
);
}
});
}
Expand Down Expand Up @@ -572,6 +584,8 @@ function payloadFileSync(pointer) {
mkdirSync: fs.mkdirSync,
mkdir: fs.mkdir,
createReadStream: fs.createReadStream,
copyFileSync: fs.copyFileSync,
copyFile: fs.copyFile,
};

ancestor.realpathSync.native = fs.realpathSync;
Expand Down Expand Up @@ -1066,6 +1080,67 @@ function payloadFileSync(pointer) {
});
};

fs.copyFile = function copyFile(src, dest, flags, callback) {
if (!insideSnapshot(path.resolve(src))) {
ancestor.copyFile(src, dest, flags, callback);
return;
}
if (typeof flags === 'function') {
callback = flags;
flags = 0;
} else if (typeof callback !== 'function') {
throw new TypeError('Callback must be a function');
}

function _streamCopy() {
fs.createReadStream(src)
.on('error', callback)
.pipe(fs.createWriteStream(dest))
.on('error', callback)
.on('finish', callback);
}

if (flags & fs.constants.COPYFILE_EXCL) {
fs.stat(dest, (statError) => {
if (!statError) {
callback(
Object.assign(new Error('File already exists'), {
code: 'EEXIST',
})
);
return;
}
if (statError.code !== 'ENOENT') {
callback(statError);
return;
}
_streamCopy();
});
} else {
_streamCopy();
}
};

fs.copyFileSync = function copyFileSync(src, dest, flags) {
if (!insideSnapshot(path.resolve(src))) {
ancestor.copyFileSync(src, dest, flags);
return;
}

if (flags & fs.constants.COPYFILE_EXCL) {
try {
fs.statSync(dest);
} catch (statError) {
if (statError.code !== 'ENOENT') throw statError;
copyInChunks(src, dest, DEFAULT_COPY_CHUNK_SIZE, fs);
return;
}

throw Object.assign(new Error('File already exists'), { code: 'EEXIST' });
}
copyInChunks(src, dest, DEFAULT_COPY_CHUNK_SIZE, fs);
};

// ///////////////////////////////////////////////////////////////
// writeFile /////////////////////////////////////////////////////
// ///////////////////////////////////////////////////////////////
Expand Down Expand Up @@ -1556,6 +1631,7 @@ function payloadFileSync(pointer) {
lstat: fs.promises.lstat,
fstat: fs.promises.fstat,
access: fs.promises.access,
copyFile: fs.promises.copyFile,
};

fs.promises.open = async function open(path_) {
Expand Down Expand Up @@ -1602,6 +1678,7 @@ function payloadFileSync(pointer) {

// this one use promisify on purpose
fs.promises.readdir = util.promisify(fs.readdir);
fs.promises.copyFile = util.promisify(fs.copyFile);

/*
fs.promises.read = util.promisify(fs.read);
Expand Down Expand Up @@ -2093,6 +2170,8 @@ function payloadFileSync(pointer) {
// Example: /tmp/pkg/<hash>
const tmpFolder = path.join(tmpdir(), 'pkg', hash);

createDirRecursively(tmpFolder);

// Example: moduleFolder = /snapshot/appname/node_modules/sharp/build/Release
const parts = moduleFolder.split(path.sep);
const mIndex = parts.indexOf('node_modules') + 1;
Expand All @@ -2110,14 +2189,12 @@ function payloadFileSync(pointer) {
// here we copy all files from the snapshot module folder to temporary folder
// we keep the module folder structure to prevent issues with modules that are statically
// linked using relative paths (Fix #1075)
createDirRecursively(tmpFolder);
copyFolderRecursiveSync(modulePkgFolder, tmpFolder);

// Example: /tmp/pkg/<hash>/sharp/build/Release/sharp.node
newPath = path.join(tmpFolder, modulePackagePath, moduleBaseName);
} else {
createDirRecursively(tmpFolder);
copyFileSync(modulePath, tmpFolder);
fs.copyFileSync(modulePath, path.join(tmpFolder, moduleBaseName));

// load the copied file in the temporary folder
newPath = path.join(tmpFolder, moduleBaseName);
Expand Down
3 changes: 3 additions & 0 deletions test/test-420-copy-from-snapshot/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
output
sync.json
async.json
19 changes: 19 additions & 0 deletions test/test-420-copy-from-snapshot/copy.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
#!/usr/bin/env node

'use strict';

const fs = require('fs');
const path = require('path');

const testPath = path.resolve(__dirname, 'input/test.json');
console.log(fs.readFileSync(testPath, 'utf8'));

const syncPath = path.resolve(process.cwd(), 'output/sync.json');
fs.copyFileSync(testPath, syncPath);
console.log(fs.readFileSync(syncPath, 'utf8'));

const asyncPath = path.resolve(process.cwd(), 'output/async.json');
fs.copyFile(testPath, asyncPath, (err) => {
if (err) throw err;
console.log(fs.readFileSync(asyncPath, 'utf8'));
});
3 changes: 3 additions & 0 deletions test/test-420-copy-from-snapshot/input/test.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
{
"key": "value"
}
30 changes: 30 additions & 0 deletions test/test-420-copy-from-snapshot/main.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
#!/usr/bin/env node

'use strict';

const path = require('path');
const assert = require('assert');
const utils = require('../utils.js');

assert(!module.parent);
assert(__dirname === process.cwd());

const target = process.argv[2] || 'host';
const input = './copy.js';
const output = './output/test-output.exe';

utils.mkdirp.sync(path.dirname(output));
utils.pkg.sync(['--target', target, '--output', output, '.']);

let left, right;
left = utils.spawn.sync('node', [path.basename(input)], {
cwd: path.dirname(input),
});

right = utils.spawn.sync(output, [], {
cwd: path.dirname(input),
});

assert.strictEqual(left, right);
utils.vacuum.sync(path.dirname(output));
utils.vacuum.sync(path.join(__dirname, '/*sync.json'));
8 changes: 8 additions & 0 deletions test/test-420-copy-from-snapshot/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
{
"bin": "copy.js",
"pkg": {
"assets": [
"input/**/*"
]
}
}