--- /dev/null
+module.exports = chmodr
+chmodr.sync = chmodrSync
+
+var fs = require("fs")
+var path = require("path")
+
+function chmodr (p, mode, cb) {
+ fs.lstat(p, function (er, stats) {
+ if (er)
+ return cb(er)
+ if (stats.isSymbolicLink())
+ return cb()
+ if (stats.isDirectory())
+ return chmodrDir(p, mode, cb)
+ return fs.chmod(p, mode, cb)
+ })
+}
+
+function chmodrDir (p, mode, cb) {
+ fs.readdir(p, function (er, children) {
+ if (er)
+ return cb(er)
+
+ if (!children.length)
+ return fs.chmod(p, dirMode(mode), cb)
+
+ var len = children.length
+ var errState = null
+ children.forEach(function (child) {
+ chmodr(path.resolve(p, child), mode, then)
+ })
+
+ // return first error, but not until all are finished,
+ // so we don't keep performing FS operations after the cb
+ function then (er) {
+ len = len - 1
+ if (er && !errState)
+ errState = er
+ if (len === 0) {
+ if (errState)
+ return cb(errState)
+ else
+ return fs.chmod(p, dirMode(mode), cb)
+ }
+ }
+ })
+}
+
+function chmodrSync (p, mode) {
+ var stats = fs.lstatSync(p)
+ if (stats.isSymbolicLink())
+ return
+ if (stats.isDirectory())
+ return chmodrDirSync(p, mode)
+ else
+ return fs.chmodSync(p, mode)
+}
+
+function chmodrDirSync (p, mode) {
+ fs.readdirSync(p).forEach(function (child) {
+ chmodrSync(path.resolve(p, child), mode)
+ })
+ return fs.chmodSync(p, dirMode(mode))
+}
+
+// If a party has r, add x
+// so that dirs are listable
+function dirMode(mode) {
+ if (mode & 0400)
+ mode |= 0100
+ if (mode & 040)
+ mode |= 010
+ if (mode & 04)
+ mode |= 01
+ return mode
+}