"Fossies" - the Fresh Open Source Software Archive

Member "Atom/resources/app/apm/node_modules/npm/lib/utils/gently-rm.js" (8 Mar 2017, 8854 Bytes) of archive /windows/misc/atom-windows.zip:


As a special service "Fossies" has tried to format the requested source page into HTML format using (guessed) Javascript source code syntax highlighting (style: standard) with prefixed line numbers and code folding option. Alternatively you can here view or download the uninterpreted source code file.

    1 // only remove the thing if it's a symlink into a specific folder. This is
    2 // a very common use-case of npm's, but not so common elsewhere.
    3 
    4 exports = module.exports = gentlyRm
    5 
    6 var resolve = require('path').resolve
    7 var dirname = require('path').dirname
    8 var normalize = require('path').normalize
    9 var validate = require('aproba')
   10 var log = require('npmlog')
   11 var lstat = require('graceful-fs').lstat
   12 var readlink = require('graceful-fs').readlink
   13 var isInside = require('path-is-inside')
   14 var vacuum = require('fs-vacuum')
   15 var chain = require('slide').chain
   16 var asyncMap = require('slide').asyncMap
   17 var readCmdShim = require('read-cmd-shim')
   18 var iferr = require('iferr')
   19 var npm = require('../npm.js')
   20 
   21 function gentlyRm (target, gently, base, cb) {
   22   if (!cb) {
   23     cb = base
   24     base = undefined
   25   }
   26 
   27   if (!cb) {
   28     cb = gently
   29     gently = false
   30   }
   31 
   32   log.silly(
   33     'gentlyRm',
   34     target,
   35     'is being', gently ? 'gently removed' : 'purged',
   36     base ? 'from base ' + base : ''
   37   )
   38 
   39   // never rm the root, prefix, or bin dirs
   40   //
   41   // globals included because of `npm link` -- as far as the package
   42   // requesting the link is concerned, the linked package is always
   43   // installed globally
   44   var prefixes = [
   45     npm.prefix,
   46     npm.globalPrefix,
   47     npm.dir,
   48     npm.root,
   49     npm.globalDir,
   50     npm.bin,
   51     npm.globalBin
   52   ]
   53 
   54   var targetPath = normalize(resolve(npm.prefix, target))
   55   if (prefixes.indexOf(targetPath) !== -1) {
   56     log.verbose('gentlyRm', targetPath, "is part of npm and can't be removed")
   57     return cb(new Error('May not delete: ' + targetPath))
   58   }
   59   var options = { log: log.silly.bind(log, 'vacuum-fs') }
   60   if (npm.config.get('force') || !gently) options.purge = true
   61   if (base) options.base = normalize(resolve(npm.prefix, base))
   62 
   63   if (!gently) {
   64     log.verbose('gentlyRm', "don't care about contents; nuking", targetPath)
   65     return vacuum(targetPath, options, cb)
   66   }
   67 
   68   var parent = options.base = options.base || normalize(npm.prefix)
   69 
   70   // Do all the async work we'll need to do in order to tell if this is a
   71   // safe operation
   72   chain([
   73     [isEverInside, parent, prefixes],
   74     [readLinkOrShim, targetPath],
   75     [isEverInside, targetPath, prefixes],
   76     [isEverInside, targetPath, [parent]]
   77   ], function (er, results) {
   78     if (er) {
   79       if (er.code === 'ENOENT') return cb()
   80       return cb(er)
   81     }
   82     var parentInfo = {
   83       path: parent,
   84       managed: results[0]
   85     }
   86     var targetInfo = {
   87       path: targetPath,
   88       symlink: results[1],
   89       managed: results[2],
   90       inParent: results[3]
   91     }
   92 
   93     isSafeToRm(parentInfo, targetInfo, iferr(cb, thenRemove))
   94 
   95     function thenRemove (toRemove, removeBase) {
   96       if (!toRemove) return cb()
   97       if (removeBase) options.base = removeBase
   98       log.verbose('gentlyRm', options.purge ? 'Purging' : 'Vacuuming',
   99         toRemove, 'up to', options.base)
  100       return vacuum(toRemove, options, cb)
  101     }
  102   })
  103 }
  104 
  105 exports._isSafeToRm = isSafeToRm
  106 function isSafeToRm (parent, target, cb) {
  107   log.silly('gentlyRm', 'parent.path =', parent.path)
  108   log.silly('gentlyRm', 'parent.managed =',
  109     parent.managed && parent.managed.target + ' is in ' + parent.managed.path)
  110   log.silly('gentlyRm', 'target.path = ', target.path)
  111   log.silly('gentlyRm', 'target.symlink =', target.symlink)
  112   log.silly('gentlyRm', 'target.managed =',
  113     target.managed && target.managed.target + ' is in ' + target.managed.path)
  114   log.silly('gentlyRm', 'target.inParent = ', target.inParent)
  115 
  116   // The parent directory or something it symlinks to must eventually be in
  117   // a folder that npm maintains.
  118   if (!parent.managed) {
  119     log.verbose('gentlyRm', parent.path,
  120       'is not contained in any diretory npm is known to control or ' +
  121       'any place they link to')
  122     return cb(clobberFail(target.path, 'containing path ' + parent.path +
  123       " isn't under npm's control"))
  124   }
  125 
  126   // The target or something it symlinks to must eventually be in the parent
  127   // or something the parent symlinks to
  128   if (target.inParent) {
  129     var actualTarget = target.inParent.target
  130     var targetsParent = target.inParent.path
  131     // if the target.path was what we found in some version of parent, remove
  132     // using that parent as the base
  133     if (target.path === actualTarget) {
  134       return cb(null, target.path, targetsParent)
  135     } else {
  136       // If something the target.path links to was what was found, just
  137       // remove target.path in the location it was found.
  138       return cb(null, target.path, dirname(target.path))
  139     }
  140   }
  141 
  142   // If the target is in a managed directory and is in a symlink, but was
  143   // not in our parent that usually means someone else installed a bin file
  144   // with the same name as one of our bin files.
  145   if (target.managed && target.symlink) {
  146     log.warn('gentlyRm', 'not removing', target.path,
  147       "as it wasn't installed by", parent.path)
  148     return cb()
  149   }
  150 
  151   if (target.symlink) {
  152     return cb(clobberFail(target.path, target.symlink +
  153       ' symlink target is not controlled by npm ' + parent.path))
  154   } else {
  155     return cb(clobberFail(target.path, 'is outside ' + parent.path +
  156       ' and not a link'))
  157   }
  158 }
  159 
  160 function clobberFail (target, msg) {
  161   validate('SS', arguments)
  162   var er = new Error('Refusing to delete ' + target + ': ' + msg)
  163   er.code = 'EEXIST'
  164   er.path = target
  165   return er
  166 }
  167 
  168 function isENOENT (err) {
  169   return err && err.code === 'ENOENT'
  170 }
  171 
  172 function notENOENT (err) {
  173   return !isENOENT(err)
  174 }
  175 
  176 function skipENOENT (cb) {
  177   return function (err, value) {
  178     if (isENOENT(err)) {
  179       return cb(null, false)
  180     } else {
  181       return cb(err, value)
  182     }
  183   }
  184 }
  185 
  186 function errorsToValues (fn) {
  187   return function () {
  188     var args = Array.prototype.slice.call(arguments)
  189     var cb = args.pop()
  190     args.push(function (err, value) {
  191       if (err) {
  192         return cb(null, err)
  193       } else {
  194         return cb(null, value)
  195       }
  196     })
  197     fn.apply(null, args)
  198   }
  199 }
  200 
  201 function isNotError (value) {
  202   return !(value instanceof Error)
  203 }
  204 
  205 exports._isEverInside = isEverInside
  206 // return the first of path, where target (or anything it symlinks to)
  207 // isInside the path (or anything it symlinks to)
  208 function isEverInside (target, paths, cb) {
  209   validate('SAF', arguments)
  210   asyncMap(paths, errorsToValues(readAllLinks), iferr(cb, function (resolvedPaths) {
  211     var errorFree = resolvedPaths.filter(isNotError)
  212     if (errorFree.length === 0) {
  213       var badErrors = resolvedPaths.filter(notENOENT)
  214       if (badErrors.length === 0) {
  215         return cb(null, false)
  216       } else {
  217         return cb(badErrors[0])
  218       }
  219     }
  220     readAllLinks(target, iferr(skipENOENT(cb), function (targets) {
  221       cb(null, areAnyInsideAny(targets, errorFree))
  222     }))
  223   }))
  224 }
  225 
  226 exports._areAnyInsideAny = areAnyInsideAny
  227 // Return the first path found that any target is inside
  228 function areAnyInsideAny (targets, paths) {
  229   validate('AA', arguments)
  230   var toCheck = []
  231   paths.forEach(function (path) {
  232     targets.forEach(function (target) {
  233       toCheck.push([target, path])
  234     })
  235   })
  236   for (var ii = 0; ii < toCheck.length; ++ii) {
  237     var target = toCheck[ii][0]
  238     var path = toCheck[ii][1]
  239     var inside = isInside(target, path)
  240     if (!inside) log.silly('isEverInside', target, 'is not inside', path)
  241     if (inside && path) return inside && path && {target: target, path: path}
  242   }
  243   return false
  244 }
  245 
  246 exports._readAllLinks = readAllLinks
  247 // resolves chains of symlinks of unlimited depth, returning a list of paths
  248 // it's seen in the process when it hits either a symlink cycle or a
  249 // non-symlink
  250 function readAllLinks (path, cb) {
  251   validate('SF', arguments)
  252   var seen = {}
  253   _readAllLinks(path)
  254 
  255   function _readAllLinks (path) {
  256     if (seen[path]) return cb(null, Object.keys(seen))
  257     seen[path] = true
  258     resolveSymlink(path, iferr(cb, _readAllLinks))
  259   }
  260 }
  261 
  262 exports._resolveSymlink = resolveSymlink
  263 var resolvedPaths = {}
  264 function resolveSymlink (symlink, cb) {
  265   validate('SF', arguments)
  266   var cached = resolvedPaths[symlink]
  267   if (cached) return cb(null, cached)
  268 
  269   readLinkOrShim(symlink, iferr(cb, function (symlinkTarget) {
  270     if (symlinkTarget) {
  271       resolvedPaths[symlink] = resolve(dirname(symlink), symlinkTarget)
  272     } else {
  273       resolvedPaths[symlink] = symlink
  274     }
  275     return cb(null, resolvedPaths[symlink])
  276   }))
  277 }
  278 
  279 exports._readLinkOrShim = readLinkOrShim
  280 function readLinkOrShim (path, cb) {
  281   validate('SF', arguments)
  282   lstat(path, iferr(cb, function (stat) {
  283     if (stat.isSymbolicLink()) {
  284       readlink(path, cb)
  285     } else {
  286       readCmdShim(path, function (er, source) {
  287         if (!er) return cb(null, source)
  288         // lstat wouldn't return an error on these, so we don't either.
  289         if (er.code === 'ENOTASHIM' || er.code === 'EISDIR') {
  290           return cb(null, null)
  291         } else {
  292           return cb(er)
  293         }
  294       })
  295     }
  296   }))
  297 }