-
Notifications
You must be signed in to change notification settings - Fork 9
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat: add withOwner and withOwnerSync methods (#21)
- Loading branch information
1 parent
55679fa
commit 40ee281
Showing
12 changed files
with
512 additions
and
38 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,92 @@ | ||
const { dirname, resolve } = require('path') | ||
|
||
const fileURLToPath = require('./file-url-to-path/index.js') | ||
const fs = require('../fs.js') | ||
|
||
// given a path, find the owner of the nearest parent | ||
const find = (path) => { | ||
// if we have no getuid, permissions are irrelevant on this platform | ||
if (!process.getuid) { | ||
return {} | ||
} | ||
|
||
// fs methods accept URL objects with a scheme of file: so we need to unwrap | ||
// those into an actual path string before we can resolve it | ||
const resolved = path != null && path.href && path.origin | ||
? resolve(fileURLToPath(path)) | ||
: resolve(path) | ||
|
||
let stat | ||
|
||
try { | ||
stat = fs.lstatSync(resolved) | ||
} finally { | ||
// if we got a stat, return its contents | ||
if (stat) { | ||
return { uid: stat.uid, gid: stat.gid } | ||
} | ||
|
||
// try the parent directory | ||
if (resolved !== dirname(resolved)) { | ||
return find(dirname(resolved)) | ||
} | ||
|
||
// no more parents, never got a stat, just return an empty object | ||
return {} | ||
} | ||
} | ||
|
||
// given a path, uid, and gid update the ownership of the path if necessary | ||
const update = (path, uid, gid) => { | ||
// nothing to update, just exit | ||
if (uid === undefined && gid === undefined) { | ||
return | ||
} | ||
|
||
try { | ||
// see if the permissions are already the same, if they are we don't | ||
// need to do anything, so return early | ||
const stat = fs.statSync(path) | ||
if (uid === stat.uid && gid === stat.gid) { | ||
return | ||
} | ||
} catch (err) {} | ||
|
||
try { | ||
fs.chownSync(path, uid, gid) | ||
} catch (err) {} | ||
} | ||
|
||
// accepts a `path` and the `owner` property of an options object and normalizes | ||
// it into an object with numerical `uid` and `gid` | ||
const validate = (path, input) => { | ||
let uid | ||
let gid | ||
|
||
if (typeof input === 'string' || typeof input === 'number') { | ||
uid = input | ||
gid = input | ||
} else if (input && typeof input === 'object') { | ||
uid = input.uid | ||
gid = input.gid | ||
} | ||
|
||
if (uid === 'inherit' || gid === 'inherit') { | ||
const owner = find(path) | ||
if (uid === 'inherit') { | ||
uid = owner.uid | ||
} | ||
|
||
if (gid === 'inherit') { | ||
gid = owner.gid | ||
} | ||
} | ||
|
||
return { uid, gid } | ||
} | ||
|
||
module.exports = { | ||
find, | ||
update, | ||
validate, | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,22 +1,16 @@ | ||
const fs = require('./fs.js') | ||
const getOptions = require('./common/get-options.js') | ||
const owner = require('./common/owner.js') | ||
const withOwner = require('./with-owner.js') | ||
|
||
const copyFile = async (src, dest, opts) => { | ||
const options = getOptions(opts, { | ||
copy: ['mode', 'owner'], | ||
copy: ['mode'], | ||
wrap: 'mode', | ||
}) | ||
|
||
const { uid, gid } = await owner.validate(dest, options.owner) | ||
|
||
// the node core method as of 16.5.0 does not support the mode being in an | ||
// object, so we have to pass the mode value directly | ||
const result = await fs.copyFile(src, dest, options.mode) | ||
|
||
await owner.update(dest, uid, gid) | ||
|
||
return result | ||
return withOwner(dest, () => fs.copyFile(src, dest, options.mode), opts) | ||
} | ||
|
||
module.exports = copyFile |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,8 +1,14 @@ | ||
const fs = require('fs') | ||
const promisify = require('@gar/promisify') | ||
|
||
// this module returns the core fs module wrapped in a proxy that promisifies | ||
const isLower = (s) => s === s.toLowerCase() && s !== s.toUpperCase() | ||
|
||
const fsSync = Object.fromEntries(Object.entries(fs).filter(([k, v]) => | ||
typeof v === 'function' && (k.endsWith('Sync') || !isLower(k[0])) | ||
)) | ||
|
||
// this module returns the core fs async fns wrapped in a proxy that promisifies | ||
// method calls within the getter. we keep it in a separate module so that the | ||
// overridden methods have a consistent way to get to promisified fs methods | ||
// without creating a circular dependency | ||
module.exports = promisify(fs) | ||
// without creating a circular dependency. the ctors and sync methods are kept untouched | ||
module.exports = { ...promisify(fs), ...fsSync } |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,21 @@ | ||
const getOptions = require('./common/get-options.js') | ||
const owner = require('./common/owner-sync.js') | ||
|
||
const withOwnerSync = (path, fn, opts) => { | ||
const options = getOptions(opts, { | ||
copy: ['owner'], | ||
}) | ||
|
||
const { uid, gid } = owner.validate(path, options.owner) | ||
|
||
const result = fn({ uid, gid }) | ||
|
||
owner.update(path, uid, gid) | ||
if (typeof result === 'string') { | ||
owner.update(result, uid, gid) | ||
} | ||
|
||
return result | ||
} | ||
|
||
module.exports = withOwnerSync |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,21 @@ | ||
const getOptions = require('./common/get-options.js') | ||
const owner = require('./common/owner.js') | ||
|
||
const withOwner = async (path, fn, opts) => { | ||
const options = getOptions(opts, { | ||
copy: ['owner'], | ||
}) | ||
|
||
const { uid, gid } = await owner.validate(path, options.owner) | ||
|
||
const result = await fn({ uid, gid }) | ||
|
||
await Promise.all([ | ||
owner.update(path, uid, gid), | ||
typeof result === 'string' ? owner.update(result, uid, gid) : null, | ||
]) | ||
|
||
return result | ||
} | ||
|
||
module.exports = withOwner |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,19 +1,14 @@ | ||
const fs = require('./fs.js') | ||
const getOptions = require('./common/get-options.js') | ||
const owner = require('./common/owner.js') | ||
const withOwner = require('./with-owner.js') | ||
|
||
const writeFile = async (file, data, opts) => { | ||
const options = getOptions(opts, { | ||
copy: ['encoding', 'mode', 'flag', 'signal', 'owner'], | ||
copy: ['encoding', 'mode', 'flag', 'signal'], | ||
wrap: 'encoding', | ||
}) | ||
const { uid, gid } = await owner.validate(file, options.owner) | ||
|
||
const result = await fs.writeFile(file, data, options) | ||
|
||
await owner.update(file, uid, gid) | ||
|
||
return result | ||
return withOwner(file, () => fs.writeFile(file, data, options), opts) | ||
} | ||
|
||
module.exports = writeFile |
Oops, something went wrong.