|
|
@ -75,7 +75,7 @@ function onFile (srcStat, src, dest, opts, cb) { |
|
|
|
checkDest(dest, (err, resolvedPath) => { |
|
|
|
if (err) return cb(err) |
|
|
|
if (resolvedPath === notExist) { |
|
|
|
return cpFile(srcStat, src, dest, opts, cb) |
|
|
|
return copyFile(srcStat, src, dest, opts, cb) |
|
|
|
} else if (resolvedPath === existsReg) { |
|
|
|
return mayCopyFile(srcStat, src, dest, opts, cb) |
|
|
|
} else { |
|
|
@ -89,30 +89,41 @@ function mayCopyFile (srcStat, src, dest, opts, cb) { |
|
|
|
if (opts.overwrite) { |
|
|
|
fs.unlink(dest, err => { |
|
|
|
if (err) return cb(err) |
|
|
|
return cpFile(srcStat, src, dest, opts, cb) |
|
|
|
return copyFile(srcStat, src, dest, opts, cb) |
|
|
|
}) |
|
|
|
} else if (opts.errorOnExist) { |
|
|
|
return cb(new Error(`'${dest}' already exists`)) |
|
|
|
} else return cb() |
|
|
|
} |
|
|
|
|
|
|
|
function cpFile (srcStat, src, dest, opts, cb) { |
|
|
|
function copyFile (srcStat, src, dest, opts, cb) { |
|
|
|
if (typeof fs.copyFile === 'function') { |
|
|
|
return fs.copyFile(src, dest, err => { |
|
|
|
if (err) return cb(err) |
|
|
|
return handleDestModeAndTimestamps(srcStat, dest, opts, cb) |
|
|
|
}) |
|
|
|
} |
|
|
|
return copyFileFallback(srcStat, src, dest, opts, cb) |
|
|
|
} |
|
|
|
|
|
|
|
function copyFileFallback (srcStat, src, dest, opts, cb) { |
|
|
|
const rs = fs.createReadStream(src) |
|
|
|
const ws = fs.createWriteStream(dest, { mode: srcStat.mode }) |
|
|
|
|
|
|
|
rs.on('error', err => cb(err)) |
|
|
|
ws.on('error', err => cb(err)) |
|
|
|
|
|
|
|
ws.on('open', () => { |
|
|
|
rs.pipe(ws) |
|
|
|
}).once('close', () => { |
|
|
|
fs.chmod(dest, srcStat.mode, err => { |
|
|
|
if (err) return cb(err) |
|
|
|
if (opts.preserveTimestamps) { |
|
|
|
return utimes(dest, srcStat.atime, srcStat.mtime, cb) |
|
|
|
} |
|
|
|
return cb() |
|
|
|
}) |
|
|
|
ws.on('open', () => rs.pipe(ws)) |
|
|
|
.once('close', () => handleDestModeAndTimestamps(srcStat, dest, opts, cb)) |
|
|
|
} |
|
|
|
|
|
|
|
function handleDestModeAndTimestamps (srcStat, dest, opts, cb) { |
|
|
|
fs.chmod(dest, srcStat.mode, err => { |
|
|
|
if (err) return cb(err) |
|
|
|
if (opts.preserveTimestamps) { |
|
|
|
return utimes(dest, srcStat.atime, srcStat.mtime, cb) |
|
|
|
} |
|
|
|
return cb() |
|
|
|
}) |
|
|
|
} |
|
|
|
|
|
|
@ -131,7 +142,7 @@ function onDir (srcStat, src, dest, opts, cb) { |
|
|
|
return mayCopyDir(src, dest, opts, cb) |
|
|
|
} else { |
|
|
|
if (src === resolvedPath) return cb() |
|
|
|
return cpDir(src, dest, opts, cb) |
|
|
|
return copyDir(src, dest, opts, cb) |
|
|
|
} |
|
|
|
}) |
|
|
|
} |
|
|
@ -142,7 +153,7 @@ function mayCopyDir (src, dest, opts, cb) { |
|
|
|
if (!st.isDirectory()) { |
|
|
|
return cb(new Error(`Cannot overwrite non-directory '${dest}' with directory '${src}'.`)) |
|
|
|
} |
|
|
|
return cpDir(src, dest, opts, cb) |
|
|
|
return copyDir(src, dest, opts, cb) |
|
|
|
}) |
|
|
|
} |
|
|
|
|
|
|
@ -151,24 +162,24 @@ function mkDirAndCopy (srcStat, src, dest, opts, cb) { |
|
|
|
if (err) return cb(err) |
|
|
|
fs.chmod(dest, srcStat.mode, err => { |
|
|
|
if (err) return cb(err) |
|
|
|
return cpDir(src, dest, opts, cb) |
|
|
|
return copyDir(src, dest, opts, cb) |
|
|
|
}) |
|
|
|
}) |
|
|
|
} |
|
|
|
|
|
|
|
function cpDir (src, dest, opts, cb) { |
|
|
|
function copyDir (src, dest, opts, cb) { |
|
|
|
fs.readdir(src, (err, items) => { |
|
|
|
if (err) return cb(err) |
|
|
|
return cpDirItems(items, src, dest, opts, cb) |
|
|
|
return copyDirItems(items, src, dest, opts, cb) |
|
|
|
}) |
|
|
|
} |
|
|
|
|
|
|
|
function cpDirItems (items, src, dest, opts, cb) { |
|
|
|
function copyDirItems (items, src, dest, opts, cb) { |
|
|
|
const item = items.pop() |
|
|
|
if (!item) return cb() |
|
|
|
startCopy(path.join(src, item), path.join(dest, item), opts, err => { |
|
|
|
if (err) return cb(err) |
|
|
|
return cpDirItems(items, src, dest, opts, cb) |
|
|
|
return copyDirItems(items, src, dest, opts, cb) |
|
|
|
}) |
|
|
|
} |
|
|
|
|
|
|
@ -201,27 +212,27 @@ function onLink (src, dest, opts, cb) { |
|
|
|
if (st.isDirectory() && isSrcSubdir(resolvedDestPath, resolvedSrcPath)) { |
|
|
|
return cb(new Error(`Cannot overwrite '${resolvedDestPath}' with '${resolvedSrcPath}'.`)) |
|
|
|
} |
|
|
|
return cpLink(resolvedSrcPath, dest, cb) |
|
|
|
return copyLink(resolvedSrcPath, dest, cb) |
|
|
|
}) |
|
|
|
} |
|
|
|
}) |
|
|
|
}) |
|
|
|
} |
|
|
|
|
|
|
|
function cpLink (resolvedSrcPath, dest, cb) { |
|
|
|
function copyLink (resolvedSrcPath, dest, cb) { |
|
|
|
fs.unlink(dest, err => { |
|
|
|
if (err) return cb(err) |
|
|
|
return fs.symlink(resolvedSrcPath, dest, cb) |
|
|
|
}) |
|
|
|
} |
|
|
|
|
|
|
|
// check dest to see if it exists and/or is a symlink
|
|
|
|
// check if dest exists and/or is a symlink
|
|
|
|
function checkDest (dest, cb) { |
|
|
|
fs.readlink(dest, (err, resolvedPath) => { |
|
|
|
if (err) { |
|
|
|
if (err.code === 'ENOENT') return cb(null, notExist) |
|
|
|
|
|
|
|
// dest exists and is a regular file or directory, Windows throws UNKNOWN error.
|
|
|
|
// dest exists and is a regular file or directory, Windows may throw UNKNOWN error.
|
|
|
|
if (err.code === 'EINVAL' || err.code === 'UNKNOWN') return cb(null, existsReg) |
|
|
|
|
|
|
|
return cb(err) |
|
|
|