From 80bb164c3e62d78064021841be3a6174328f4eba Mon Sep 17 00:00:00 2001 From: Julien Roncaglia Date: Tue, 21 Mar 2017 00:26:44 +0100 Subject: [PATCH] Use the windows CopyFile API for copying files --- package.json | 2 ++ src/util/fs.js | 78 ++++++++++++++++++++++++++++++++++---------------- yarn.lock | 46 +++++++++++++++++++++++++++-- 3 files changed, 100 insertions(+), 26 deletions(-) diff --git a/package.json b/package.json index 8e7e3bc7fc..c3ea695cbe 100644 --- a/package.json +++ b/package.json @@ -15,6 +15,7 @@ "death": "^1.0.0", "debug": "^2.2.0", "detect-indent": "^5.0.0", + "ffi": "^2.2.0", "ini": "^1.3.4", "inquirer": "^3.0.1", "invariant": "^2.2.0", @@ -29,6 +30,7 @@ "object-path": "^0.11.2", "proper-lockfile": "^2.0.0", "read": "^1.0.7", + "ref-wchar": "^1.0.2", "request": "^2.81.0", "request-capture-har": "^1.2.2", "rimraf": "^2.5.0", diff --git a/src/util/fs.js b/src/util/fs.js index 1382eba3f6..1c40741f35 100644 --- a/src/util/fs.js +++ b/src/util/fs.js @@ -452,6 +452,57 @@ export function copy(src: string, dest: string, reporter: Reporter): Promise new Promise((resolve, reject) => { + const readStream = fs.createReadStream(data.src); + const writeStream = fs.createWriteStream(data.dest, {mode: data.mode}); + + reporter.verbose(reporter.lang('verboseFileCopy', data.src, data.dest)); + + readStream.on('error', reject); + writeStream.on('error', reject); + + writeStream.on('open', function() { + readStream.pipe(writeStream); + }); + + writeStream.once('finish', function() { + fs.utimes(data.dest, data.atime, data.mtime, function(err) { + if (err) { + reject(err); + } else { + resolve(); + } + }); + }); +}); + +if (process.platform === 'win32') { + const ffi = require('ffi'); + const wchar_t = require('ref-wchar'); + + const kernel32 = ffi.Library('kernel32', { + 'CopyFileW': ['uint32', [wchar_t.string, wchar_t.string, 'uint32']], + }); + + const longFileNamePrefix = '\\\\?\\'; + + copyFile = (reporter: Reporter, data: CopyFileAction) => new Promise((resolve, reject) => { + reporter.verbose(reporter.lang('verboseFileCopy', data.src, data.dest)); + + kernel32.CopyFileW.async(longFileNamePrefix + data.src, longFileNamePrefix + data.dest, 0, function(err, res) { + if (err) { + reject(err); + } + + if (res === 0) { + reject(new Error('CopyFileW failed')); + } + + resolve(); + }); + }); +} + export async function copyBulk( queue: CopyQueue, reporter: Reporter, @@ -490,31 +541,10 @@ export async function copyBulk( } const cleanup = () => delete currentlyWriting[data.dest]; - return currentlyWriting[data.dest] = new Promise((resolve, reject) => { - const readStream = fs.createReadStream(data.src); - const writeStream = fs.createWriteStream(data.dest, {mode: data.mode}); - - reporter.verbose(reporter.lang('verboseFileCopy', data.src, data.dest)); - - readStream.on('error', reject); - writeStream.on('error', reject); - - writeStream.on('open', function() { - readStream.pipe(writeStream); - }); - writeStream.once('finish', function() { - fs.utimes(data.dest, data.atime, data.mtime, function(err) { - if (err) { - reject(err); - } else { - events.onProgress(data.dest); - cleanup(); - resolve(); - } - }); - }); - }).then((arg) => { + return currentlyWriting[data.dest] = copyFile(reporter, data) + .then((arg) => { + events.onProgress(data.dest); cleanup(); return arg; }).catch((arg) => { diff --git a/yarn.lock b/yarn.lock index 032aac4ccb..73a7277b76 100644 --- a/yarn.lock +++ b/yarn.lock @@ -785,6 +785,10 @@ binary-extensions@^1.0.0: version "1.8.0" resolved "https://registry.yarnpkg.com/binary-extensions/-/binary-extensions-1.8.0.tgz#48ec8d16df4377eae5fa5884682480af4d95c774" +bindings@1, bindings@~1.2.0: + version "1.2.1" + resolved "https://registry.yarnpkg.com/bindings/-/bindings-1.2.1.tgz#14ad6113812d2d37d72e67b4cacb4bb726505f11" + bl@^1.0.0: version "1.2.0" resolved "https://registry.yarnpkg.com/bl/-/bl-1.2.0.tgz#1397e7ec42c5f5dc387470c500e34a9f6be9ea98" @@ -1231,7 +1235,7 @@ debug-fabulous@0.0.X: lazy-debug-legacy "0.0.X" object-assign "4.1.0" -debug@2.X, debug@^2.1.1, debug@^2.2.0: +debug@2, debug@2.X, debug@^2.1.1, debug@^2.2.0: version "2.6.1" resolved "https://registry.yarnpkg.com/debug/-/debug-2.6.1.tgz#79855090ba2c4e3115cc7d8769491d58f0491351" dependencies: @@ -1695,6 +1699,16 @@ fb-watchman@^2.0.0: dependencies: bser "^2.0.0" +ffi@^2.2.0: + version "2.2.0" + resolved "https://registry.yarnpkg.com/ffi/-/ffi-2.2.0.tgz#bf18b04666a29f71227ed56895d5430af47042fa" + dependencies: + bindings "~1.2.0" + debug "2" + nan "2" + ref "1" + ref-struct "1" + figures@^1.3.5: version "1.7.0" resolved "https://registry.yarnpkg.com/figures/-/figures-1.7.0.tgz#cbe1e3affcf1cd44b80cadfed28dc793a9701d2e" @@ -2268,6 +2282,12 @@ iconv-lite@0.4.13: version "0.4.13" resolved "https://registry.yarnpkg.com/iconv-lite/-/iconv-lite-0.4.13.tgz#1f88aba4ab0b1508e8312acc39345f36e992e2f2" +iconv@2: + version "2.2.3" + resolved "https://registry.yarnpkg.com/iconv/-/iconv-2.2.3.tgz#e084d60eeb7d73da7f0a9c096e4c8abe090bfaed" + dependencies: + nan "^2.3.5" + ieee754@^1.1.4: version "1.1.8" resolved "https://registry.yarnpkg.com/ieee754/-/ieee754-1.1.8.tgz#be33d40ac10ef1926701f6f08a2d86fbfd1ad3e4" @@ -3258,7 +3278,7 @@ mute-stream@0.0.7, mute-stream@~0.0.4: version "0.0.7" resolved "https://registry.yarnpkg.com/mute-stream/-/mute-stream-0.0.7.tgz#3075ce93bc21b8fab43e1bc4da7e8115ed1e7bab" -nan@^2.3.0: +nan@2, nan@^2.3.0, nan@^2.3.5: version "2.5.1" resolved "https://registry.yarnpkg.com/nan/-/nan-2.5.1.tgz#d5b01691253326a97a2bbee9e61c55d8d60351e2" @@ -3814,6 +3834,28 @@ rechoir@^0.6.2: dependencies: resolve "^1.1.6" +ref-struct@1: + version "1.1.0" + resolved "https://registry.yarnpkg.com/ref-struct/-/ref-struct-1.1.0.tgz#5d5ee65ad41cefc3a5c5feb40587261e479edc13" + dependencies: + debug "2" + ref "1" + +ref-wchar@^1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/ref-wchar/-/ref-wchar-1.0.2.tgz#a709aab2f461ac8f3de6d3dcb4d18495f8d93d1e" + dependencies: + iconv "2" + ref "^1.3.1" + +ref@1, ref@^1.3.1: + version "1.3.4" + resolved "https://registry.yarnpkg.com/ref/-/ref-1.3.4.tgz#724d2bf8ac85f8c8db194d3d85be6efe416bc1e5" + dependencies: + bindings "1" + debug "2" + nan "2" + regenerate@^1.2.1: version "1.3.2" resolved "https://registry.yarnpkg.com/regenerate/-/regenerate-1.3.2.tgz#d1941c67bad437e1be76433add5b385f95b19260"