audiobookshelf/server/utils/filePerms.js
2022-02-10 17:05:15 -06:00

85 lines
2.1 KiB
JavaScript

const fs = require('fs-extra')
const Path = require('path')
const Logger = require('../Logger')
// Modified from:
// https://github.com/isaacs/chmodr/blob/master/chmodr.js
// If a party has r, add x
// so that dirs are listable
const dirMode = mode => {
if (mode & 0o400)
mode |= 0o100
if (mode & 0o40)
mode |= 0o10
if (mode & 0o4)
mode |= 0o1
return mode
}
const chmodrKid = (p, child, mode, uid, gid, cb) => {
if (typeof child === 'string')
return fs.lstat(Path.resolve(p, child), (er, stats) => {
if (er)
return cb(er)
stats.name = child
chmodrKid(p, stats, mode, uid, gid, cb)
})
if (child.isDirectory()) {
chmodr(Path.resolve(p, child.name), mode, uid, gid, er => {
if (er)
return cb(er)
var _path = Path.resolve(p, child.name)
fs.chmod(_path, dirMode(mode)).then(() => {
fs.chown(_path, uid, gid, cb)
})
})
} else {
var _path = Path.resolve(p, child.name)
fs.chmod(_path, mode).then(() => {
fs.chown(_path, uid, gid, cb)
})
}
}
const chmodr = (p, mode, uid, gid, cb) => {
fs.readdir(p, { withFileTypes: true }, (er, children) => {
// any error other than ENOTDIR means it's not readable, or
// doesn't exist. give up.
if (er && er.code !== 'ENOTDIR') return cb(er)
if (er) {
return fs.chmod(p, mode).then(() => {
fs.chown(p, uid, gid, cb)
})
}
if (!children.length) {
return fs.chmod(p, dirMode(mode)).then(() => {
fs.chown(p, uid, gid, cb)
})
}
let len = children.length
let errState = null
const then = er => {
if (errState) return
if (er) return cb(errState = er)
if (--len === 0) {
return fs.chmod(p, dirMode(mode)).then(() => {
fs.chown(p, uid, gid, cb)
})
}
}
children.forEach(child => chmodrKid(p, child, mode, uid, gid, then))
})
}
module.exports = (path, mode, uid, gid) => {
return new Promise((resolve) => {
Logger.debug(`[FilePerms] Setting permission "${mode}" for uid ${uid} and gid ${gid} | "${path}"`)
chmodr(path, mode, uid, gid, resolve)
})
}