Skip to content

Commit 2d83d5b

Browse files
isaacsruyadorno
authored andcommittedJan 7, 2021
Add the noChmod option
This allows a way to suppress the call to process.umask() while still being as compliant as possible with the modes as defined in the tarball entries. Re: npm/cli#1103 PR-URL: #270 Credit: @isaacs Close: #270 Reviewed-by: @ruyadorno
1 parent 73ec0f7 commit 2d83d5b

File tree

3 files changed

+63
-4
lines changed

3 files changed

+63
-4
lines changed
 

‎README.md

+14-1
Original file line numberDiff line numberDiff line change
@@ -302,7 +302,6 @@ The following options are supported:
302302
- `mtime` Set to a `Date` object to force a specific `mtime` for
303303
everything added to the archive. Overridden by `noMtime`.
304304

305-
306305
The following options are mostly internal, but can be modified in some
307306
advanced use cases, such as re-using caches between runs.
308307

@@ -396,6 +395,13 @@ The following options are supported:
396395
the `filter` option described above.)
397396
- `onentry` A function that gets called with `(entry)` for each entry
398397
that passes the filter.
398+
- `onwarn` A function that will get called with `(code, message, data)` for
399+
any warnings encountered. (See "Warnings and Errors")
400+
- `noChmod` Set to true to omit calling `fs.chmod()` to ensure that the
401+
extracted file matches the entry mode. This also suppresses the call to
402+
`process.umask()` to determine the default umask value, since tar will
403+
extract with whatever mode is provided, and let the process `umask` apply
404+
normally.
399405

400406
The following options are mostly internal, but can be modified in some
401407
advanced use cases, such as re-using caches between runs.
@@ -451,6 +457,8 @@ The following options are supported:
451457
the call to `onentry`. Set `noResume: true` to suppress this
452458
behavior. Note that by opting into this, the stream will never
453459
complete until the entry data is consumed.
460+
- `onwarn` A function that will get called with `(code, message, data)` for
461+
any warnings encountered. (See "Warnings and Errors")
454462

455463
### tar.u(options, fileList, callback) [alias: tar.update]
456464

@@ -708,6 +716,11 @@ Most unpack errors will cause a `warn` event to be emitted. If the
708716
that passes the filter.
709717
- `onwarn` A function that will get called with `(code, message, data)` for
710718
any warnings encountered. (See "Warnings and Errors")
719+
- `noChmod` Set to true to omit calling `fs.chmod()` to ensure that the
720+
extracted file matches the entry mode. This also suppresses the call to
721+
`process.umask()` to determine the default umask value, since tar will
722+
extract with whatever mode is provided, and let the process `umask` apply
723+
normally.
711724

712725
### class tar.Unpack.Sync
713726

‎lib/unpack.js

+7-3
Original file line numberDiff line numberDiff line change
@@ -169,11 +169,14 @@ class Unpack extends Parser {
169169

170170
this.cwd = path.resolve(opt.cwd || process.cwd())
171171
this.strip = +opt.strip || 0
172-
this.processUmask = process.umask()
172+
// if we're not chmodding, then we don't need the process umask
173+
this.processUmask = opt.noChmod ? 0 : process.umask()
173174
this.umask = typeof opt.umask === 'number' ? opt.umask : this.processUmask
175+
174176
// default mode for dirs created as parents
175177
this.dmode = opt.dmode || (0o0777 & (~this.umask))
176178
this.fmode = opt.fmode || (0o0666 & (~this.umask))
179+
177180
this.on('entry', entry => this[ONENTRY](entry))
178181
}
179182

@@ -299,6 +302,7 @@ class Unpack extends Parser {
299302
cache: this.dirCache,
300303
cwd: this.cwd,
301304
mode: mode,
305+
noChmod: this.noChmod,
302306
}, cb)
303307
}
304308

@@ -475,7 +479,7 @@ class Unpack extends Parser {
475479

476480
else if (st.isDirectory()) {
477481
if (entry.type === 'Directory') {
478-
if (!entry.mode || (st.mode & 0o7777) === entry.mode)
482+
if (!this.noChmod && (!entry.mode || (st.mode & 0o7777) === entry.mode))
479483
this[MAKEFS](null, entry, done)
480484
else {
481485
fs.chmod(entry.absolute, entry.mode,
@@ -538,7 +542,7 @@ class UnpackSync extends Unpack {
538542
try {
539543
if (st.isDirectory()) {
540544
if (entry.type === 'Directory') {
541-
if (entry.mode && (st.mode & 0o7777) !== entry.mode)
545+
if (!this.noChmod && entry.mode && (st.mode & 0o7777) !== entry.mode)
542546
fs.chmodSync(entry.absolute, entry.mode)
543547
} else
544548
fs.rmdirSync(entry.absolute)

‎test/unpack.js

+42
Original file line numberDiff line numberDiff line change
@@ -1916,6 +1916,48 @@ t.test('use explicit chmod when required by umask', t => {
19161916
})
19171917
})
19181918

1919+
t.test('dont use explicit chmod if noChmod flag set', t => {
1920+
process.umask(0o022)
1921+
const { umask } = process
1922+
t.teardown(() => process.umask = umask)
1923+
process.umask = () => {
1924+
throw new Error('should not call process.umask()')
1925+
}
1926+
1927+
const basedir = path.resolve(unpackdir, 'umask-no-chmod')
1928+
1929+
const data = makeTar([
1930+
{
1931+
path: 'x/y/z',
1932+
mode: 0o775,
1933+
type: 'Directory',
1934+
},
1935+
'',
1936+
'',
1937+
])
1938+
1939+
const check = t => {
1940+
const st = fs.statSync(basedir + '/x/y/z')
1941+
t.equal(st.mode & 0o777, 0o755)
1942+
rimraf.sync(basedir)
1943+
t.end()
1944+
}
1945+
1946+
t.test('async', t => {
1947+
mkdirp.sync(basedir)
1948+
const unpack = new Unpack({ cwd: basedir, noChmod: true })
1949+
unpack.on('close', _ => check(t))
1950+
unpack.end(data)
1951+
})
1952+
1953+
return t.test('sync', t => {
1954+
mkdirp.sync(basedir)
1955+
const unpack = new Unpack.Sync({ cwd: basedir, noChmod: true})
1956+
unpack.end(data)
1957+
check(t)
1958+
})
1959+
})
1960+
19191961
t.test('chown implicit dirs and also the entries', t => {
19201962
const basedir = path.resolve(unpackdir, 'chownr')
19211963

0 commit comments

Comments
 (0)