Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: support unixfs metadata and formatting it #14

Merged
merged 6 commits into from
Dec 6, 2019
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
66 changes: 66 additions & 0 deletions src/files/format-mode.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
'use strict'

const S_ISUID = parseInt('4000', 8) // set UID bit
const S_ISGID = parseInt('2000', 8) // set-group-ID bit (see below)
const S_ISVTX = parseInt('1000', 8) // sticky bit (see below)
// const S_IRWXU = parseInt('700', 8) // mask for file owner permissions
const S_IRUSR = parseInt('400', 8) // owner has read permission
const S_IWUSR = parseInt('200', 8) // owner has write permission
const S_IXUSR = parseInt('100', 8) // owner has execute permission
// const S_IRWXG = parseInt('70', 8) // mask for group permissions
const S_IRGRP = parseInt('40', 8) // group has read permission
const S_IWGRP = parseInt('20', 8) // group has write permission
const S_IXGRP = parseInt('10', 8) // group has execute permission
// const S_IRWXO = parseInt('7', 8) // mask for permissions for others (not in group)
const S_IROTH = parseInt('4', 8) // others have read permission
const S_IWOTH = parseInt('2', 8) // others have write permission
const S_IXOTH = parseInt('1', 8) // others have execute permission

function checkPermission (mode, perm, type, output) {
if ((mode & perm) === perm) {
output.push(type)
} else {
output.push('-')
}
}

function formatMode (mode, isDirectory) {
const output = []

if (isDirectory) {
output.push('d')
} else {
output.push('-')
}

checkPermission(mode, S_IRUSR, 'r', output)
checkPermission(mode, S_IWUSR, 'w', output)

if ((mode & S_ISUID) === S_ISUID) {
output.push('s')
} else {
checkPermission(mode, S_IXUSR, 'x', output)
}

checkPermission(mode, S_IRGRP, 'r', output)
checkPermission(mode, S_IWGRP, 'w', output)

if ((mode & S_ISGID) === S_ISGID) {
output.push('s')
} else {
checkPermission(mode, S_IXGRP, 'x', output)
}

checkPermission(mode, S_IROTH, 'r', output)
checkPermission(mode, S_IWOTH, 'w', output)

if ((mode & S_ISVTX) === S_ISVTX) {
output.push('t')
} else {
checkPermission(mode, S_IXOTH, 'x', output)
}

return output.join('')
}

module.exports = formatMode
19 changes: 19 additions & 0 deletions src/files/format-mtime.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
'use strict'

function formatMtime (mtime) {
if (mtime === undefined) {
return '-'
}

return new Date(mtime * 1000).toLocaleDateString(Intl.DateTimeFormat().resolvedOptions().locale, {
year: 'numeric',
month: 'short',
day: 'numeric',
hour: '2-digit',
minute: '2-digit',
second: '2-digit',
timeZoneName: 'short'
})
}

module.exports = formatMtime
56 changes: 50 additions & 6 deletions src/files/glob-source.js
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,10 @@ const kindOf = require('kind-of')
* @param {Boolean} [options.hidden] Include .dot files in matched paths
* @param {Array<String>} [options.ignore] Glob paths to ignore
* @param {Boolean} [options.followSymlinks] follow symlinks
* @param {Boolean} [options.preserveMode] preserve mode
* @param {Boolean} [options.preserveMtime] preserve mtime
* @param {Boolean} [options.mode] mode to use - if preserveMode is true this will be ignored
* @param {Boolean} [options.mtime] mtime to use - if preserveMtime is true this will be ignored
* @yields {Object} File objects in the form `{ path: String, content: AsyncIterator<Buffer> }`
*/
module.exports = async function * globSource (paths, options) {
Expand Down Expand Up @@ -47,21 +51,47 @@ module.exports = async function * globSource (paths, options) {
const stat = await fs.stat(absolutePath)
const prefix = Path.dirname(absolutePath)

for await (const entry of toGlobSource({ path, type: stat.isDirectory() ? 'dir' : 'file', prefix }, globSourceOptions)) {
yield entry
let mode = options.mode

if (options.preserveMode) {
mode = stat.mode
}

let mtime = options.mtime

if (options.preserveMtime) {
mtime = parseInt(stat.mtimeMs / 1000)
}

for await (const entry of toGlobSource({
path,
type: stat.isDirectory() ? 'dir' : 'file',
prefix,
mode,
mtime,
preserveMode: options.preserveMode,
preserveMtime: options.preserveMtime
}, globSourceOptions)) {
yield {
...entry,
mode,
mtime
}
}
}
}

async function * toGlobSource ({ path, type, prefix }, options) {
async function * toGlobSource ({ path, type, prefix, mode, mtime, preserveMode, preserveMtime }, options) {
options = options || {}

const baseName = Path.basename(path)

if (type === 'file') {
yield {
path: baseName.replace(prefix, ''),
content: fs.createReadStream(Path.isAbsolute(path) ? path : Path.join(process.cwd(), path))
content: fs.createReadStream(Path.isAbsolute(path) ? path : Path.join(process.cwd(), path)),
mode,
mtime
}

return
Expand All @@ -77,15 +107,29 @@ async function * toGlobSource ({ path, type, prefix }, options) {

const globOptions = Object.assign({}, options.glob, {
cwd: path,
nodir: true,
nodir: false,
realpath: false,
absolute: true
})

for await (const p of glob(path, '**/*', globOptions)) {
if (preserveMode || preserveMtime) {
const stat = await fs.stat(p)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In the future we might get some perf wins by parallel mapping these.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Possibly - our bottlenecks typically aren't from reading paths from the filesystem but it's worth measuring before jumping to conclusions. Like I just did. :trollface:


if (options.preserveMode) {
mode = stat.mode
}

if (options.preserveMtime) {
mtime = parseInt(stat.mtimeMs / 1000)
}
}

yield {
path: toPosix(p.replace(prefix, '')),
content: fs.createReadStream(p)
content: fs.createReadStream(p),
achingbrain marked this conversation as resolved.
Show resolved Hide resolved
mode,
mtime
achingbrain marked this conversation as resolved.
Show resolved Hide resolved
}
}
}
Expand Down
6 changes: 5 additions & 1 deletion src/files/normalise-input.js
Original file line number Diff line number Diff line change
Expand Up @@ -211,7 +211,11 @@ module.exports = function normaliseInput (input) {
}

function toFileObject (input) {
const obj = { path: input.path || '' }
const obj = {
path: input.path || '',
mode: input.mode,
mtime: input.mtime
}

if (input.content) {
obj.content = toAsyncIterable(input.content)
Expand Down