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 5 commits
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
4 changes: 2 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -33,14 +33,14 @@
"is-electron": "^2.2.0",
"is-pull-stream": "0.0.0",
"is-stream": "^2.0.0",
"it-glob": "0.0.4",
"it-glob": "0.0.7",
"kind-of": "^6.0.2",
"pull-stream-to-async-iterator": "^1.0.2",
"readable-stream": "^3.4.0"
},
"devDependencies": {
"aegir": "^20.3.0",
"async-iterator-all": "^1.0.0",
"it-all": "^1.0.1",
"chai": "^4.2.0",
"chai-as-promised": "^7.1.1",
"dirty-chai": "^2.0.1",
Expand Down
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
60 changes: 53 additions & 7 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,49 @@ 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)
}

if (stat.isDirectory()) {
yield {
path: `/${Path.basename(path)}`,
mode,
mtime
}
}

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

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))
path: `/${baseName.replace(prefix, '')}`,
content: fs.createReadStream(Path.isAbsolute(path) ? path : Path.join(process.cwd(), path)),
mode,
mtime
}

return
Expand All @@ -77,15 +109,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)) {
const stat = await fs.stat(p)

if (preserveMode || preserveMtime) {
if (preserveMode) {
mode = stat.mode
}

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

yield {
path: toPosix(p.replace(prefix, '')),
content: fs.createReadStream(p)
content: stat.isFile() ? fs.createReadStream(p) : undefined,
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
58 changes: 58 additions & 0 deletions test/files/format-mode.spec.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
'use strict'

/* eslint-env mocha */
const chai = require('chai')
const dirtyChai = require('dirty-chai')
const formatMode = require('../../src/files/format-mode')

chai.use(dirtyChai)
const expect = chai.expect

describe('format-mode', function () {
it('formats mode for directories', function () {
expect(formatMode(parseInt('0777', 8), true)).to.equal('drwxrwxrwx')
})

it('formats mode for files', function () {
expect(formatMode(parseInt('0777', 8), false)).to.equal('-rwxrwxrwx')
})

it('setgid, setuid and stick bit', function () {
expect(formatMode(parseInt('1777', 8), false)).to.equal('-rwxrwxrwt')
expect(formatMode(parseInt('2777', 8), false)).to.equal('-rwxrwsrwx')
expect(formatMode(parseInt('4777', 8), false)).to.equal('-rwsrwxrwx')
expect(formatMode(parseInt('5777', 8), false)).to.equal('-rwsrwxrwt')
expect(formatMode(parseInt('6777', 8), false)).to.equal('-rwsrwsrwx')
expect(formatMode(parseInt('7777', 8), false)).to.equal('-rwsrwsrwt')
})

it('formats user', function () {
expect(formatMode(parseInt('0100', 8), false)).to.equal('---x------')
expect(formatMode(parseInt('0200', 8), false)).to.equal('--w-------')
expect(formatMode(parseInt('0300', 8), false)).to.equal('--wx------')
expect(formatMode(parseInt('0400', 8), false)).to.equal('-r--------')
expect(formatMode(parseInt('0500', 8), false)).to.equal('-r-x------')
expect(formatMode(parseInt('0600', 8), false)).to.equal('-rw-------')
expect(formatMode(parseInt('0700', 8), false)).to.equal('-rwx------')
})

it('formats group', function () {
expect(formatMode(parseInt('0010', 8), false)).to.equal('------x---')
expect(formatMode(parseInt('0020', 8), false)).to.equal('-----w----')
expect(formatMode(parseInt('0030', 8), false)).to.equal('-----wx---')
expect(formatMode(parseInt('0040', 8), false)).to.equal('----r-----')
expect(formatMode(parseInt('0050', 8), false)).to.equal('----r-x---')
expect(formatMode(parseInt('0060', 8), false)).to.equal('----rw----')
expect(formatMode(parseInt('0070', 8), false)).to.equal('----rwx---')
})

it('formats other', function () {
expect(formatMode(parseInt('0001', 8), false)).to.equal('---------x')
expect(formatMode(parseInt('0002', 8), false)).to.equal('--------w-')
expect(formatMode(parseInt('0003', 8), false)).to.equal('--------wx')
expect(formatMode(parseInt('0004', 8), false)).to.equal('-------r--')
expect(formatMode(parseInt('0005', 8), false)).to.equal('-------r-x')
expect(formatMode(parseInt('0006', 8), false)).to.equal('-------rw-')
expect(formatMode(parseInt('0007', 8), false)).to.equal('-------rwx')
})
})
15 changes: 15 additions & 0 deletions test/files/format-mtime.spec.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
'use strict'

/* eslint-env mocha */
const chai = require('chai')
const dirtyChai = require('dirty-chai')
const formatMtime = require('../../src/files/format-mtime')

chai.use(dirtyChai)
const expect = chai.expect

describe('format-mtime', function () {
it('formats mtime', function () {
expect((new Date(formatMtime(0))).getTime()).to.equal(0)
})
})
Loading