Skip to content

Commit 5e8dfa4

Browse files
committed
Set more portable 'mode' value in portable mode
Fix #235 The `mode` value in `portable` mode will be set to a reasonable default across Unix systems. First, the user must always have read and write permissions. (Ie, a mode of 0o044 becomes 0o644.) Next, the mode is masked against 0o022. (Ie, a file of 0o777 becomes 0o755. 0o666 becomes 0o644.) In practice, this means that all entries will usually have a mode of either 0644 or 0o755, depending on whether the executable bit was set. This avoids a situation where an archive created on a system with a umask of 0o2 creates an archive with files having modes 0o775 and 0o664, whereas an archive created on a system with a umask of 0o22 creates an archive with files having modes 0o755 and 0o644, for the same content. (Especially, for a check-out of the same git repository.) Without this consistency, it's impossible to honor the contract that the same content will produce the same archive, which is necessary for npm to create reprodicible build artifacts that match both in CI and development. This unfortunately still does not address windows, where the executable bit is never set on files. (This bit the mocha project when a publish from a Windows machine did not archive binaries with executable flags set: #210 (comment))
1 parent a2f8899 commit 5e8dfa4

4 files changed

Lines changed: 41 additions & 17 deletions

File tree

README.md

Lines changed: 21 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -284,8 +284,9 @@ The following options are supported:
284284
or `false` to omit it.
285285
- `portable` Omit metadata that is system-specific: `ctime`, `atime`,
286286
`uid`, `gid`, `uname`, `gname`, `dev`, `ino`, and `nlink`. Note
287-
that `mtime` is still included, because this is necessary other
288-
time-based operations.
287+
that `mtime` is still included, because this is necessary for other
288+
time-based operations. Additionally, `mode` is set to a "reasonable
289+
default" for most unix systems, based on a `umask` value of `0o22`.
289290
- `preservePaths` Allow absolute paths. By default, `/` is stripped
290291
from absolute paths. [Alias: `P`]
291292
- `mode` The mode to set on the created file archive
@@ -484,8 +485,9 @@ The following options are supported:
484485
or `false` to omit it.
485486
- `portable` Omit metadata that is system-specific: `ctime`, `atime`,
486487
`uid`, `gid`, `uname`, `gname`, `dev`, `ino`, and `nlink`. Note
487-
that `mtime` is still included, because this is necessary other
488-
time-based operations.
488+
that `mtime` is still included, because this is necessary for other
489+
time-based operations. Additionally, `mode` is set to a "reasonable
490+
default" for most unix systems, based on a `umask` value of `0o22`.
489491
- `preservePaths` Allow absolute paths. By default, `/` is stripped
490492
from absolute paths. [Alias: `P`]
491493
- `maxReadSize` The maximum buffer size for `fs.read()` operations.
@@ -535,8 +537,9 @@ The following options are supported:
535537
or `false` to omit it.
536538
- `portable` Omit metadata that is system-specific: `ctime`, `atime`,
537539
`uid`, `gid`, `uname`, `gname`, `dev`, `ino`, and `nlink`. Note
538-
that `mtime` is still included, because this is necessary other
539-
time-based operations.
540+
that `mtime` is still included, because this is necessary for other
541+
time-based operations. Additionally, `mode` is set to a "reasonable
542+
default" for most unix systems, based on a `umask` value of `0o22`.
540543
- `preservePaths` Allow absolute paths. By default, `/` is stripped
541544
from absolute paths. [Alias: `P`]
542545
- `maxReadSize` The maximum buffer size for `fs.read()` operations.
@@ -582,8 +585,9 @@ The following options are supported:
582585
or `false` to omit it.
583586
- `portable` Omit metadata that is system-specific: `ctime`, `atime`,
584587
`uid`, `gid`, `uname`, `gname`, `dev`, `ino`, and `nlink`. Note
585-
that `mtime` is still included, because this is necessary other
586-
time-based operations.
588+
that `mtime` is still included, because this is necessary for other
589+
time-based operations. Additionally, `mode` is set to a "reasonable
590+
default" for most unix systems, based on a `umask` value of `0o22`.
587591
- `preservePaths` Allow absolute paths. By default, `/` is stripped
588592
from absolute paths.
589593
- `linkCache` A Map object containing the device and inode value for
@@ -795,8 +799,9 @@ It has the following fields:
795799
object.
796800
- `portable` Omit metadata that is system-specific: `ctime`, `atime`,
797801
`uid`, `gid`, `uname`, `gname`, `dev`, `ino`, and `nlink`. Note
798-
that `mtime` is still included, because this is necessary other
799-
time-based operations.
802+
that `mtime` is still included, because this is necessary for other
803+
time-based operations. Additionally, `mode` is set to a "reasonable
804+
default" for most unix systems, based on a `umask` value of `0o22`.
800805
- `myuid` If supported, the uid of the user running the current
801806
process.
802807
- `myuser` The `env.USER` string if set, or `''`. Set as the entry
@@ -834,8 +839,9 @@ The following options are supported:
834839

835840
- `portable` Omit metadata that is system-specific: `ctime`, `atime`,
836841
`uid`, `gid`, `uname`, `gname`, `dev`, `ino`, and `nlink`. Note
837-
that `mtime` is still included, because this is necessary other
838-
time-based operations.
842+
that `mtime` is still included, because this is necessary for other
843+
time-based operations. Additionally, `mode` is set to a "reasonable
844+
default" for most unix systems, based on a `umask` value of `0o22`.
839845
- `maxReadSize` The maximum buffer size for `fs.read()` operations.
840846
Defaults to 1 MB.
841847
- `linkCache` A Map object containing the device and inode value for
@@ -883,8 +889,9 @@ The following options are supported:
883889

884890
- `portable` Omit metadata that is system-specific: `ctime`, `atime`,
885891
`uid`, `gid`, `uname`, `gname`, `dev`, `ino`, and `nlink`. Note
886-
that `mtime` is still included, because this is necessary other
887-
time-based operations.
892+
that `mtime` is still included, because this is necessary for other
893+
time-based operations. Additionally, `mode` is set to a "reasonable
894+
default" for most unix systems, based on a `umask` value of `0o22`.
888895
- `preservePaths` Allow absolute paths. By default, `/` is stripped
889896
from absolute paths.
890897
- `strict` Treat warnings as crash-worthy errors. Default false.

lib/mode-fix.js

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,16 @@
11
'use strict'
2-
module.exports = (mode, isDir) => {
2+
module.exports = (mode, isDir, portable) => {
33
mode &= 0o7777
4+
5+
// in portable mode, use the minimum reasonable umask
6+
// if this system creates files with 0o664 by default
7+
// (as some linux distros do), then we'll write the
8+
// archive with 0o644 instead. Also, don't ever create
9+
// a file that is not readable/writable by the owner.
10+
if (portable) {
11+
mode = (mode | 0o600) &~0o22
12+
}
13+
414
// if dirs are readable, then they should be listable
515
if (isDir) {
616
if (mode & 0o400)

lib/write-entry.js

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -115,7 +115,7 @@ const WriteEntry = warner(class WriteEntry extends MiniPass {
115115
}
116116

117117
[MODE] (mode) {
118-
return modeFix(mode, this.type === 'Directory')
118+
return modeFix(mode, this.type === 'Directory', this.portable)
119119
}
120120

121121
[HEADER] () {
@@ -406,7 +406,7 @@ const WriteEntryTar = warner(class WriteEntryTar extends MiniPass {
406406
}
407407

408408
[MODE] (mode) {
409-
return modeFix(mode, this.type === 'Directory')
409+
return modeFix(mode, this.type === 'Directory', this.portable)
410410
}
411411

412412
write (data) {

test/mode-fix.js

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,3 +7,10 @@ t.equal(mf(0o10644, true), 0o755)
77
t.equal(mf(0o10604, true), 0o705)
88
t.equal(mf(0o10600, true), 0o700)
99
t.equal(mf(0o10066, true), 0o077)
10+
11+
t.equal(mf(0o10664, false, true), 0o644)
12+
t.equal(mf(0o10066, false, true), 0o644)
13+
t.equal(mf(0o10666, true, true), 0o755)
14+
t.equal(mf(0o10604, true, true), 0o705)
15+
t.equal(mf(0o10600, true, true), 0o700)
16+
t.equal(mf(0o10066, true, true), 0o755)

0 commit comments

Comments
 (0)