Skip to content
This repository has been archived by the owner on Aug 11, 2022. It is now read-only.

Commit

Permalink
gently-rm: swap checks & match target, not source
Browse files Browse the repository at this point in the history
Fixes #7579.

We want to know two things with gently-rm:

1. Is the parent directory under npm's control?
(1a. Is the thing to be deleted under that parent directory if so?)
2. Is the thing to be deleted under npm's control?

There are some additional complications, but that's the core of it, so
to make clearer what was going on, I rearranged the code to prioritize
answering those two questions, and added a bunch of comments explaining
what's happening in an effort that that would make clearer *why* the
code is written the way it is.

When npm removes a file or directory, it verifies whether the thing
being removed is under npm's control. This isn't something that can be
ascertained directly, so gentlyRm checks to see if there's a plausible
case that this file was something that was originally installed by npm.
Mostly it just verifies whether either the deletion target or the parent
directory (which could be passed in as the base, or could be the prefix
otherwise) is under one of the paths that npm might have reason to mess
with.

It will also deal with a certain amount of symbolic link trickery, but
symlink verification was overly aggressive before, and has been eased up
some.
  • Loading branch information
othiym23 committed Mar 12, 2015
1 parent 5156149 commit fb0ac26
Showing 1 changed file with 43 additions and 30 deletions.
73 changes: 43 additions & 30 deletions lib/utils/gently-rm.js
Original file line number Diff line number Diff line change
Expand Up @@ -26,8 +26,10 @@ function gentlyRm (path, gently, base, cb) {
gently = false
}

// never rm the root, prefix, or bin dirs.
// just a safety precaution.
// never rm the root, prefix, or bin dirs
//
// globals included because of `npm link` -- as far as the package requesting
// the link is concerned, the linked package is always installed globally
var prefixes = [
npm.prefix,
npm.globalPrefix,
Expand All @@ -54,61 +56,72 @@ function gentlyRm (path, gently, base, cb) {
}

var parent = options.base = normalize(base ? base : npm.prefix)
log.verbose("gentlyRm", "verifying that", parent, "is managed by npm")

// is the parent directory managed by npm?
log.silly("gentlyRm", "verifying", parent, "is an npm working directory")
some(prefixes, isManaged(parent), function (er, matched) {
if (er) return cb(er)

if (!matched) {
log.verbose("gentlyRm", parent, "is not managed by npm")
return clobberFail(resolved, parent, cb)
}
log.silly("gentlyRm", "containing path", parent, "is under npm's control, in", matched)

log.silly("gentlyRm", parent, "is managed by npm")

// is the target directly contained within the (now known to be
// managed) parent?
if (isInside(resolved, parent)) {
log.silly("gentlyRm", resolved, "is under", parent)
log.verbose("gentlyRm", "vacuuming", resolved, "up to", parent)
return vacuum(resolved, options, cb)
}

log.silly("gentlyRm", resolved, "is not under", parent)
log.silly("gentlyRm", "checking to see if", resolved, "is a link")
lstat(resolved, function (er, stat) {
if (er) {
if (er.code === "ENOENT") return cb(null)
return cb(er)
}

if (!stat.isSymbolicLink()) {
log.verbose("gentlyRm", resolved, "is outside", parent, "and not a link")
return clobberFail(resolved, parent, cb)
// the target isn't directly within the parent, but is it itself managed?
log.silly("gentlyRm", "verifying", resolved, "is an npm working directory")
some(prefixes, isManaged(resolved), function (er, matched) {
if (er) return cb(er)

if (matched) {
log.silly("gentlyRm", resolved, "is under npm's control, in", matched)
options.base = matched
log.verbose("gentlyRm", "removing", resolved, 'with base', options.base)
return vacuum(resolved, options, cb)
}
log.verbose("gentlyRm", resolved, "is not under npm's control")

log.silly("gentlyRm", resolved, "is a link")
readlink(resolved, function (er, link) {
// the target isn't managed directly, but maybe it's a link...
log.silly("gentlyRm", "checking to see if", resolved, "is a link")
lstat(resolved, function (er, stat) {
if (er) {
// race conditions are common when unbuilding
if (er.code === "ENOENT") return cb(null)
return cb(er)
}

var source = resolve(dirname(resolved), link)
if (isInside(source, parent)) {
log.silly("gentlyRm", source, "inside", parent)
log.verbose("gentlyRm", "vacuuming", resolved)
return vacuum(resolved, options, cb)
if (!stat.isSymbolicLink()) {
log.verbose("gentlyRm", resolved, "is outside", parent, "and not a link")
return clobberFail(resolved, parent, cb)
}

log.silly("gentlyRm", "checking to see if", source, "is managed by npm")
some(prefixes, isManaged(source), function (er, matched) {
if (er) return cb(er)
// ...and maybe the link source, when read...
log.silly("gentlyRm", resolved, "is a link")
readlink(resolved, function (er, link) {
if (er) {
// race conditions are common when unbuilding
if (er.code === "ENOENT") return cb(null)
return cb(er)
}

if (matched) {
log.silly("gentlyRm", source, "is under", matched)
log.verbose("gentlyRm", "removing", resolved)
vacuum(resolved, options, cb)
// ...is inside the managed parent
var source = resolve(dirname(resolved), link)
if (isInside(source, parent)) {
log.silly("gentlyRm", source, "symlink target", resolved, "is inside", parent)
log.verbose("gentlyRm", "vacuuming", resolved)
return vacuum(resolved, options, cb)
}

log.verbose("gentlyRm", source, "is not managed by npm")
log.verbose("gentlyRm", resolved, "is not under npm's control")
return clobberFail(path, parent, cb)
})
})
Expand Down

0 comments on commit fb0ac26

Please sign in to comment.