Socket
Socket
Sign inDemoInstall

fs-extra

Package Overview
Dependencies
Maintainers
2
Versions
96
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

Comparing version 6.0.1 to 7.0.0

8

CHANGELOG.md

@@ -0,1 +1,9 @@

7.0.0 / 2018-07-16
------------------
- **BREAKING:** Refine `copy*()` handling of symlinks to properly detect symlinks that point to the same file. ([#582](https://github.com/jprichardson/node-fs-extra/pull/582))
- Fix bug with copying write-protected directories ([#600](https://github.com/jprichardson/node-fs-extra/pull/600))
- Universalify `fs.lchmod()` ([#596](https://github.com/jprichardson/node-fs-extra/pull/596))
- Add `engines` field to `package.json` ([#580](https://github.com/jprichardson/node-fs-extra/pull/580))
6.0.1 / 2018-05-09

@@ -2,0 +10,0 @@ ------------------

126

lib/copy-sync/copy-sync.js

@@ -9,3 +9,2 @@ 'use strict'

const notExist = Symbol('notExist')
const existsReg = Symbol('existsReg')

@@ -27,3 +26,3 @@ function copySync (src, dest, opts) {

const resolvedDest = checkPaths(src, dest)
const destStat = checkPaths(src, dest)

@@ -34,24 +33,23 @@ if (opts.filter && !opts.filter(src, dest)) return

if (!fs.existsSync(destParent)) mkdirpSync(destParent)
return startCopy(resolvedDest, src, dest, opts)
return startCopy(destStat, src, dest, opts)
}
function startCopy (resolvedDest, src, dest, opts) {
function startCopy (destStat, src, dest, opts) {
if (opts.filter && !opts.filter(src, dest)) return
return getStats(resolvedDest, src, dest, opts)
return getStats(destStat, src, dest, opts)
}
function getStats (resolvedDest, src, dest, opts) {
function getStats (destStat, src, dest, opts) {
const statSync = opts.dereference ? fs.statSync : fs.lstatSync
const st = statSync(src)
const srcStat = statSync(src)
if (st.isDirectory()) return onDir(st, resolvedDest, src, dest, opts)
else if (st.isFile() ||
st.isCharacterDevice() ||
st.isBlockDevice()) return onFile(st, resolvedDest, src, dest, opts)
else if (st.isSymbolicLink()) return onLink(resolvedDest, src, dest, opts)
if (srcStat.isDirectory()) return onDir(srcStat, destStat, src, dest, opts)
else if (srcStat.isFile() ||
srcStat.isCharacterDevice() ||
srcStat.isBlockDevice()) return onFile(srcStat, destStat, src, dest, opts)
else if (srcStat.isSymbolicLink()) return onLink(destStat, src, dest, opts)
}
function onFile (srcStat, resolvedDest, src, dest, opts) {
if (resolvedDest === notExist) return copyFile(srcStat, src, dest, opts)
else if (resolvedDest === existsReg) return mayCopyFile(srcStat, src, dest, opts)
function onFile (srcStat, destStat, src, dest, opts) {
if (destStat === notExist) return copyFile(srcStat, src, dest, opts)
return mayCopyFile(srcStat, src, dest, opts)

@@ -101,19 +99,5 @@ }

function onDir (srcStat, resolvedDest, src, dest, opts) {
if (resolvedDest === notExist) {
if (isSrcSubdir(src, dest)) {
throw new Error(`Cannot copy '${src}' to a subdirectory of itself, '${dest}'.`)
}
return mkDirAndCopy(srcStat, src, dest, opts)
} else if (resolvedDest === existsReg) {
if (isSrcSubdir(src, dest)) {
throw new Error(`Cannot copy '${src}' to a subdirectory of itself, '${dest}'.`)
}
return mayCopyDir(src, dest, opts)
}
return copyDir(src, dest, opts)
}
function mayCopyDir (src, dest, opts) {
if (!fs.statSync(dest).isDirectory()) {
function onDir (srcStat, destStat, src, dest, opts) {
if (destStat === notExist) return mkDirAndCopy(srcStat, src, dest, opts)
if (destStat && !destStat.isDirectory()) {
throw new Error(`Cannot overwrite non-directory '${dest}' with directory '${src}'.`)

@@ -125,5 +109,5 @@ }

function mkDirAndCopy (srcStat, src, dest, opts) {
fs.mkdirSync(dest, srcStat.mode)
fs.chmodSync(dest, srcStat.mode)
return copyDir(src, dest, opts)
fs.mkdirSync(dest)
copyDir(src, dest, opts)
return fs.chmodSync(dest, srcStat.mode)
}

@@ -138,7 +122,7 @@

const destItem = path.join(dest, item)
const resolvedDest = checkPaths(srcItem, destItem)
return startCopy(resolvedDest, srcItem, destItem, opts)
const destStat = checkPaths(srcItem, destItem)
return startCopy(destStat, srcItem, destItem, opts)
}
function onLink (resolvedDest, src, dest, opts) {
function onLink (destStat, src, dest, opts) {
let resolvedSrc = fs.readlinkSync(src)

@@ -150,11 +134,21 @@

if (resolvedDest === notExist || resolvedDest === existsReg) {
// if dest already exists, fs throws error anyway,
// so no need to guard against it here.
if (destStat === notExist) {
return fs.symlinkSync(resolvedSrc, dest)
} else {
let resolvedDest
try {
resolvedDest = fs.readlinkSync(dest)
} catch (err) {
// dest exists and is a regular file or directory,
// Windows may throw UNKNOWN error. If dest already exists,
// fs throws error anyway, so no need to guard against it here.
if (err.code === 'EINVAL' || err.code === 'UNKNOWN') return fs.symlinkSync(resolvedSrc, dest)
throw err
}
if (opts.dereference) {
resolvedDest = path.resolve(process.cwd(), resolvedDest)
}
if (pathsAreIdentical(resolvedSrc, resolvedDest)) return
if (isSrcSubdir(resolvedSrc, resolvedDest)) {
throw new Error(`Cannot copy '${resolvedSrc}' to a subdirectory of itself, '${resolvedDest}'.`)
}

@@ -177,51 +171,31 @@ // prevent copy if src is a subdir of dest since unlinking

// return true if dest is a subdir of src, otherwise false.
// extract dest base dir and check if that is the same as src basename.
function isSrcSubdir (src, dest) {
const srcArray = path.resolve(src).split(path.sep)
const destArray = path.resolve(dest).split(path.sep)
return srcArray.reduce((acc, current, i) => {
return acc && destArray[i] === current
}, true)
return srcArray.reduce((acc, current, i) => acc && destArray[i] === current, true)
}
// check if dest exists and is a symlink.
function checkDest (dest) {
let resolvedPath
function checkStats (src, dest) {
const srcStat = fs.statSync(src)
let destStat
try {
resolvedPath = fs.readlinkSync(dest)
destStat = fs.statSync(dest)
} catch (err) {
if (err.code === 'ENOENT') return notExist
// dest exists and is a regular file or directory, Windows may throw UNKNOWN error.
if (err.code === 'EINVAL' || err.code === 'UNKNOWN') return existsReg
if (err.code === 'ENOENT') return {srcStat, destStat: notExist}
throw err
}
return resolvedPath // dest exists and is a symlink
return {srcStat, destStat}
}
function pathsAreIdentical (src, dest) {
const os = process.platform
const resolvedSrc = path.resolve(src)
const resolvedDest = path.resolve(dest)
// case-insensitive paths
if (os === 'darwin' || os === 'win32') {
return resolvedSrc.toLowerCase() === resolvedDest.toLowerCase()
}
return resolvedSrc === resolvedDest
}
function checkPaths (src, dest) {
const resolvedDest = checkDest(dest)
if (resolvedDest === notExist || resolvedDest === existsReg) {
if (pathsAreIdentical(src, dest)) throw new Error('Source and destination must not be the same.')
return resolvedDest
} else {
// check resolved dest path if dest is a symlink
if (pathsAreIdentical(src, resolvedDest)) throw new Error('Source and destination must not be the same.')
return resolvedDest
const {srcStat, destStat} = checkStats(src, dest)
if (destStat.ino && destStat.ino === srcStat.ino) {
throw new Error('Source and destination must not be the same.')
}
if (srcStat.isDirectory() && isSrcSubdir(src, dest)) {
throw new Error(`Cannot copy '${src}' to a subdirectory of itself, '${dest}'.`)
}
return destStat
}
module.exports = copySync

@@ -10,3 +10,2 @@ 'use strict'

const notExist = Symbol('notExist')
const existsReg = Symbol('existsReg')

@@ -33,17 +32,17 @@ function copy (src, dest, opts, cb) {

checkPaths(src, dest, (err, resolvedDest) => {
checkPaths(src, dest, (err, destStat) => {
if (err) return cb(err)
if (opts.filter) return handleFilter(checkParentDir, resolvedDest, src, dest, opts, cb)
return checkParentDir(resolvedDest, src, dest, opts, cb)
if (opts.filter) return handleFilter(checkParentDir, destStat, src, dest, opts, cb)
return checkParentDir(destStat, src, dest, opts, cb)
})
}
function checkParentDir (resolvedDest, src, dest, opts, cb) {
function checkParentDir (destStat, src, dest, opts, cb) {
const destParent = path.dirname(dest)
pathExists(destParent, (err, dirExists) => {
if (err) return cb(err)
if (dirExists) return startCopy(resolvedDest, src, dest, opts, cb)
if (dirExists) return startCopy(destStat, src, dest, opts, cb)
mkdirp(destParent, err => {
if (err) return cb(err)
return startCopy(resolvedDest, src, dest, opts, cb)
return startCopy(destStat, src, dest, opts, cb)
})

@@ -53,11 +52,6 @@ })

function startCopy (resolvedDest, src, dest, opts, cb) {
if (opts.filter) return handleFilter(getStats, resolvedDest, src, dest, opts, cb)
return getStats(resolvedDest, src, dest, opts, cb)
}
function handleFilter (onInclude, resolvedDest, src, dest, opts, cb) {
function handleFilter (onInclude, destStat, src, dest, opts, cb) {
Promise.resolve(opts.filter(src, dest)).then(include => {
if (include) {
if (resolvedDest) return onInclude(resolvedDest, src, dest, opts, cb)
if (destStat) return onInclude(destStat, src, dest, opts, cb)
return onInclude(src, dest, opts, cb)

@@ -69,18 +63,22 @@ }

function getStats (resolvedDest, src, dest, opts, cb) {
function startCopy (destStat, src, dest, opts, cb) {
if (opts.filter) return handleFilter(getStats, destStat, src, dest, opts, cb)
return getStats(destStat, src, dest, opts, cb)
}
function getStats (destStat, src, dest, opts, cb) {
const stat = opts.dereference ? fs.stat : fs.lstat
stat(src, (err, st) => {
stat(src, (err, srcStat) => {
if (err) return cb(err)
if (st.isDirectory()) return onDir(st, resolvedDest, src, dest, opts, cb)
else if (st.isFile() ||
st.isCharacterDevice() ||
st.isBlockDevice()) return onFile(st, resolvedDest, src, dest, opts, cb)
else if (st.isSymbolicLink()) return onLink(resolvedDest, src, dest, opts, cb)
if (srcStat.isDirectory()) return onDir(srcStat, destStat, src, dest, opts, cb)
else if (srcStat.isFile() ||
srcStat.isCharacterDevice() ||
srcStat.isBlockDevice()) return onFile(srcStat, destStat, src, dest, opts, cb)
else if (srcStat.isSymbolicLink()) return onLink(destStat, src, dest, opts, cb)
})
}
function onFile (srcStat, resolvedDest, src, dest, opts, cb) {
if (resolvedDest === notExist) return copyFile(srcStat, src, dest, opts, cb)
else if (resolvedDest === existsReg) return mayCopyFile(srcStat, src, dest, opts, cb)
function onFile (srcStat, destStat, src, dest, opts, cb) {
if (destStat === notExist) return copyFile(srcStat, src, dest, opts, cb)
return mayCopyFile(srcStat, src, dest, opts, cb)

@@ -130,13 +128,6 @@ }

function onDir (srcStat, resolvedDest, src, dest, opts, cb) {
if (resolvedDest === notExist) {
if (isSrcSubdir(src, dest)) {
return cb(new Error(`Cannot copy '${src}' to a subdirectory of itself, '${dest}'.`))
}
return mkDirAndCopy(srcStat, src, dest, opts, cb)
} else if (resolvedDest === existsReg) {
if (isSrcSubdir(src, dest)) {
return cb(new Error(`Cannot copy '${src}' to a subdirectory of itself, '${dest}'.`))
}
return mayCopyDir(src, dest, opts, cb)
function onDir (srcStat, destStat, src, dest, opts, cb) {
if (destStat === notExist) return mkDirAndCopy(srcStat, src, dest, opts, cb)
if (destStat && !destStat.isDirectory()) {
return cb(new Error(`Cannot overwrite non-directory '${dest}' with directory '${src}'.`))
}

@@ -146,18 +137,8 @@ return copyDir(src, dest, opts, cb)

function mayCopyDir (src, dest, opts, cb) {
fs.stat(dest, (err, st) => {
if (err) return cb(err)
if (!st.isDirectory()) {
return cb(new Error(`Cannot overwrite non-directory '${dest}' with directory '${src}'.`))
}
return copyDir(src, dest, opts, cb)
})
}
function mkDirAndCopy (srcStat, src, dest, opts, cb) {
fs.mkdir(dest, srcStat.mode, err => {
fs.mkdir(dest, err => {
if (err) return cb(err)
fs.chmod(dest, srcStat.mode, err => {
copyDir(src, dest, opts, err => {
if (err) return cb(err)
return copyDir(src, dest, opts, cb)
return fs.chmod(dest, srcStat.mode, cb)
})

@@ -183,5 +164,5 @@ })

const destItem = path.join(dest, item)
checkPaths(srcItem, destItem, (err, resolvedDest) => {
checkPaths(srcItem, destItem, (err, destStat) => {
if (err) return cb(err)
startCopy(resolvedDest, srcItem, destItem, opts, err => {
startCopy(destStat, srcItem, destItem, opts, err => {
if (err) return cb(err)

@@ -193,3 +174,3 @@ return copyDirItems(items, src, dest, opts, cb)

function onLink (resolvedDest, src, dest, opts, cb) {
function onLink (destStat, src, dest, opts, cb) {
fs.readlink(src, (err, resolvedSrc) => {

@@ -202,18 +183,24 @@ if (err) return cb(err)

if (resolvedDest === notExist || resolvedDest === existsReg) {
// if dest already exists, fs throws error anyway,
// so no need to guard against it here.
if (destStat === notExist) {
return fs.symlink(resolvedSrc, dest, cb)
} else {
if (opts.dereference) {
resolvedDest = path.resolve(process.cwd(), resolvedDest)
}
if (pathsAreIdentical(resolvedSrc, resolvedDest)) return cb()
fs.readlink(dest, (err, resolvedDest) => {
if (err) {
// dest exists and is a regular file or directory,
// Windows may throw UNKNOWN error. If dest already exists,
// fs throws error anyway, so no need to guard against it here.
if (err.code === 'EINVAL' || err.code === 'UNKNOWN') return fs.symlink(resolvedSrc, dest, cb)
return cb(err)
}
if (opts.dereference) {
resolvedDest = path.resolve(process.cwd(), resolvedDest)
}
if (isSrcSubdir(resolvedSrc, resolvedDest)) {
return cb(new Error(`Cannot copy '${resolvedSrc}' to a subdirectory of itself, '${resolvedDest}'.`))
}
// prevent copy if src is a subdir of dest since unlinking
// dest in this case would result in removing src contents
// and therefore a broken symlink would be created.
fs.stat(dest, (err, st) => {
if (err) return cb(err)
if (st.isDirectory() && isSrcSubdir(resolvedDest, resolvedSrc)) {
// do not copy if src is a subdir of dest since unlinking
// dest in this case would result in removing src contents
// and therefore a broken symlink would be created.
if (destStat.isDirectory() && isSrcSubdir(resolvedDest, resolvedSrc)) {
return cb(new Error(`Cannot overwrite '${resolvedDest}' with '${resolvedSrc}'.`))

@@ -235,49 +222,32 @@ }

// return true if dest is a subdir of src, otherwise false.
// extract dest base dir and check if that is the same as src basename.
function isSrcSubdir (src, dest) {
const srcArray = path.resolve(src).split(path.sep)
const destArray = path.resolve(dest).split(path.sep)
return srcArray.reduce((acc, current, i) => {
return acc && destArray[i] === current
}, true)
return srcArray.reduce((acc, current, i) => acc && destArray[i] === current, true)
}
// check if dest exists and 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 may throw UNKNOWN error.
if (err.code === 'EINVAL' || err.code === 'UNKNOWN') return cb(null, existsReg)
return cb(err)
}
return cb(null, resolvedPath) // dest exists and is a symlink
function checkStats (src, dest, cb) {
fs.stat(src, (err, srcStat) => {
if (err) return cb(err)
fs.stat(dest, (err, destStat) => {
if (err) {
if (err.code === 'ENOENT') return cb(null, {srcStat, destStat: notExist})
return cb(err)
}
return cb(null, {srcStat, destStat})
})
})
}
function pathsAreIdentical (src, dest) {
const os = process.platform
const resolvedSrc = path.resolve(src)
const resolvedDest = path.resolve(dest)
// case-insensitive paths
if (os === 'darwin' || os === 'win32') {
return resolvedSrc.toLowerCase() === resolvedDest.toLowerCase()
}
return resolvedSrc === resolvedDest
}
function checkPaths (src, dest, cb) {
checkDest(dest, (err, resolvedDest) => {
checkStats(src, dest, (err, stats) => {
if (err) return cb(err)
if (resolvedDest === notExist || resolvedDest === existsReg) {
if (pathsAreIdentical(src, dest)) return cb(new Error('Source and destination must not be the same.'))
return cb(null, resolvedDest)
} else {
// check resolved dest path if dest is a symlink
if (pathsAreIdentical(src, resolvedDest)) return cb(new Error('Source and destination must not be the same.'))
return cb(null, resolvedDest)
const {srcStat, destStat} = stats
if (destStat.ino && destStat.ino === srcStat.ino) {
return cb(new Error('Source and destination must not be the same.'))
}
if (srcStat.isDirectory() && isSrcSubdir(src, dest)) {
return cb(new Error(`Cannot copy '${src}' to a subdirectory of itself, '${dest}'.`))
}
return cb(null, destStat)
})

@@ -284,0 +254,0 @@ }

@@ -22,2 +22,3 @@ 'use strict'

'lchown',
'lchmod',
'link',

@@ -24,0 +25,0 @@ 'lstat',

{
"name": "fs-extra",
"version": "6.0.1",
"version": "7.0.0",
"description": "fs-extra contains methods that aren't included in the vanilla Node.js fs package. Such as mkdir -p, cp -r, and rm -rf.",
"engines": {
"node": ">=6 <7 || >=8"
},
"homepage": "https://github.com/jprichardson/node-fs-extra",

@@ -6,0 +9,0 @@ "repository": {

@@ -117,2 +117,3 @@ Node.js: fs-extra

- [ensureSymlink](docs/ensureSymlink.md)
- [mkdirp](docs/ensureDir.md)
- [mkdirs](docs/ensureDir.md)

@@ -135,2 +136,3 @@ - [move](docs/move.md)

- [ensureSymlinkSync](docs/ensureSymlink-sync.md)
- [mkdirpSync](docs/ensureDir-sync.md)
- [mkdirsSync](docs/ensureDir-sync.md)

@@ -137,0 +139,0 @@ - [moveSync](docs/move-sync.md)

SocketSocket SOC 2 Logo

Product

  • Package Alerts
  • Integrations
  • Docs
  • Pricing
  • FAQ
  • Roadmap
  • Changelog

Packages

Stay in touch

Get open source security insights delivered straight into your inbox.


  • Terms
  • Privacy
  • Security

Made with ⚡️ by Socket Inc