Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with
or
.
Download ZIP
Browse files

Meticulous permission setting in cache.

Never ever end up with root-owned crud in a user-owned home directory.
Hat-tip: http://honnix.com/blog/archives/711
  • Loading branch information...
commit ebd0b32510f48f5773b9dd2e36ffc0bf0ecf4768 1 parent 9673c9e
@isaacs isaacs authored
View
68 lib/cache.js
@@ -384,17 +384,67 @@ function addLocalTarball (p, name, cb) {
})
}
+// to maintain the cache dir's permissions consistently.
+var cacheStat = null
+function getCacheStat (cb) {
+ if (cacheStat) return cb(null, cacheStat)
+ fs.stat(npm.cache, function (er, st) {
+ if (er) return makeCacheDir(cb)
+ if (!st.isDirectory()) {
+ return log.er(cb, "invalid cache directory: "+npm.cache)(er)
+ }
+ return cb(null, cacheStat = st)
+ })
+}
+
+function makeCacheDir (cb) {
+ if (!process.getuid) return mkdir(npm.cache, DMODE, cb)
+
+ var uid = +process.getuid()
+ , gid = +process.getgid()
+
+ if (uid === 0) {
+ if (process.env.SUDO_UID) uid = +process.env.SUDO_UID
+ if (process.env.SUDO_GID) gid = +process.env.SUDO_GID
+ }
+ if (uid !== 0 || !process.env.HOME) {
+ cacheStat = {uid: uid, gid: gid}
+ return mkdir(npm.cache, DMODE, uid, gid, function (er) {
+ return cb(er, cacheStat)
+ })
+ }
+ fs.stat(process.env.HOME, function (er, st) {
+ if (er) return log.er(cb, "homeless?")(er)
+ cacheStat = st
+ log.silly([st.uid, st.gid], "uid, gid for cache dir")
+ return mkdir(npm.cache, DMODE, st.uid, st.gid, function (er) {
+ return cb(er, cacheStat)
+ })
+ })
+}
+
+
+
+
function addPlacedTarball (p, name, cb) {
if (!cb) cb = name, name = ""
+ getCacheStat(function (er, cs) {
+ if (er) return cb(er)
+ return addPlacedTarball_(p, name, cs.uid, cs.gid, cb)
+ })
+}
+
+function addPlacedTarball_ (p, name, uid, gid, cb) {
// now we know it's in place already as .cache/name/ver/package.tgz
// unpack to .cache/name/ver/package/, read the package.json,
// and fire cb with the json data.
var target = path.dirname(p)
, folder = path.join(target, "package")
, json = path.join(target, "package.json")
+
rm(folder, function (er) {
if (er) return log.er(cb, "Could not remove "+folder)(er)
- tar.unpack(p, folder, function (er) {
+ tar.unpack(p, folder, null, null, uid, gid, function (er) {
if (er) return log.er(cb, "Could not unpack "+p+" to "+target)(er)
// calculate the sha of the file that we just unpacked.
// this is so that the data is available when publishing.
@@ -410,6 +460,8 @@ function addPlacedTarball (p, name, cb) {
asyncMap([json, p], function (f, cb) {
log.verbose(FMODE.toString(8), "chmod "+f)
fs.chmod(f, FMODE, cb)
+ }, function (f, cb) {
+ fs.chown(f, uid, gid, cb)
}, function (er) {
cb(er, data)
})
@@ -447,8 +499,18 @@ function addLocalDirectory (p, name, cb) {
function addTmpTarball (tgz, name, cb) {
if (!cb) cb = name, name = ""
- var contents = path.join(path.dirname(tgz),"contents")
- tar.unpack(tgz, path.resolve(contents, "package"), function (er) {
+ getCacheStat(function (er, cs) {
+ if (er) return cb(er)
+ return addTmpTarball_(tgz, name, cs.uid, cs.gid, cb)
+ })
+}
+
+function addTmpTarball_ (tgz, name, uid, gid, cb) {
+ var contents = path.resolve(path.dirname(tgz), "contents")
+ tar.unpack( tgz, path.resolve(contents, "package")
+ , null, null
+ , uid, gid
+ , function (er) {
if (er) return log.er(cb, "couldn't unpack "+tgz+" to "+contents)(er)
fs.readdir(contents, function (er, folder) {
if (er) return log.er(cb, "couldn't readdir "+contents)(er)
View
25 lib/utils/mkdir-p.js
@@ -39,6 +39,10 @@ function mkdir (ensure, mode, uid, gid, cb_) {
cbs.forEach(function (c) { c(er) })
}
+ if (uid === null && gid === null) {
+ return mkdir_(ensure, mode, uid, gid, cb)
+ }
+
uidNumber(uid, gid, function (er, uid, gid) {
if (er) return cb(er)
mkdir_(ensure, mode, uid, gid, cb)
@@ -56,6 +60,7 @@ function mkdir_ (ensure, mode, uid, gid, cb) {
return walkDirs(ensure, mode, uid, gid, cb)
})
}
+
function done (ensure, mode, uid, gid, cb) {
// now the directory has been created.
// chown it to the desired uid/gid
@@ -67,9 +72,12 @@ function done (ensure, mode, uid, gid, cb) {
fs.chown(ensure, uid, gid, cb)
})
}
+
function walkDirs (ensure, mode, uid, gid, cb) {
var dirs = ensure.split("/")
, walker = []
+ , foundUID = null
+ , foundGID = null
walker.push(dirs.shift()) // gobble the "/" first
;(function S (d) {
if (d === undefined) {
@@ -79,17 +87,28 @@ function walkDirs (ensure, mode, uid, gid, cb) {
var dir = walker.join("/")
fs.stat(dir, function STATCB (er, s) {
if (er) {
+ if (foundUID) uid = foundUID
+ if (foundGID) gid = foundGID
fs.mkdir(dir, mode, function MKDIRCB (er, s) {
if (er && er.message.indexOf("EEXIST") === 0) {
// handle stat race
return fs.stat(dir, STATCB)
}
if (er) return cb(er)
- S(dirs.shift())
+ if (uid && gid) {
+ fs.chown(dir, uid, gid, function (er) {
+ S(dirs.shift())
+ })
+ } else {
+ S(dirs.shift())
+ }
})
} else {
- if (s.isDirectory()) S(dirs.shift())
- else cb(new Error("Failed to mkdir "+dir+": File exists"))
+ if (s.isDirectory()) {
+ if (uid === null && typeof s.uid === "number") foundUID = s.uid
+ if (gid === null && typeof s.gid === "number") foundGID = s.gid
+ S(dirs.shift())
+ } else cb(new Error("Failed to mkdir "+dir+": File exists"))
}
})
})(dirs.shift())
View
34 lib/utils/npm-registry-client/get.js
@@ -7,6 +7,7 @@ var GET = require("./request").GET
, path = require("path")
, log = require("../log")
, mkdir = require("../mkdir-p")
+ , cacheStat = null
function get (project, version, timeout, nofollow, cb) {
if (typeof cb !== "function") cb = nofollow, nofollow = false
@@ -66,9 +67,36 @@ function get_ (uri, timeout, cache, stat, data, nofollow, cb) {
cb(er, data, raw, response)
}
- mkdir(path.dirname(cache), 0755, function (er) {
- if (er) return saved()
- fs.writeFile(cache, JSON.stringify(data), saved)
+ saveToCache(cache, data, saved)
+ })
+}
+
+function saveToCache (cache, data, saved) {
+ if (cacheStat) {
+ return saveToCache_(cache, data, cacheStat.uid, cacheStat.gid, saved)
+ }
+ fs.stat(npm.cache, function (er, st) {
+ if (er) {
+ return fs.stat(process.env.HOME, function (er, st) {
+ // if this fails, oh well.
+ if (er) return saved()
+ cacheStat = st
+ return saveToCache(cache, data, saved)
+ })
+ }
+ cacheStat = st || { uid: null, gid: null }
+ return saveToCache(cache, data, saved)
+ })
+}
+
+function saveToCache_ (cache, data, uid, gid, saved) {
+ mkdir(path.dirname(cache), 0755, uid, gid, function (er) {
+ if (er) return saved()
+ fs.writeFile(cache, JSON.stringify(data), function (er) {
+ if (er || uid === null || gid === null) {
+ return saved()
+ }
+ fs.chown(cache, uid, gid, saved)
})
})
}
View
6 lib/utils/tar.js
@@ -134,7 +134,10 @@ function unpack_ ( tarball, unpackTarget, dMode, fMode, uid, gid, cb ) {
// cp the gzip of the tarball, pipe the stdout into tar's stdin
// gzip {tarball} --decompress --stdout \
// | tar -mvxpf - --strip-components=1 -C {unpackTarget}
- gunzTarPerm(tarball, tmp, dMode, fMode, uid, gid, function (er, folder) {
+ gunzTarPerm( tarball, tmp
+ , dMode, fMode
+ , uid, gid
+ , function (er, folder) {
if (er) return cb(er)
log.verbose(folder, "gunzed")
rm(unpackTarget, function (er) {
@@ -161,7 +164,6 @@ function gunzTarPerm (tarball, tmp, dMode, fMode, uid, gid, cb) {
if (!dMode) dMode = DMODE
if (!fMode) fMode = FMODE
log.silly([dMode.toString(8), fMode.toString(8)], "gunzTarPerm modes")
- //log.warn("gunzTarPerm")
//console.error(npm.config.get("gzipbin")+" --decompress --stdout "
// +tarball+" | "+npm.config.get("tar")+" -mvxpf - --no-same-owner -C "
// +tmp)
Please sign in to comment.
Something went wrong with that request. Please try again.