Skip to content
This repository was archived by the owner on Oct 28, 2024. It is now read-only.

Commit cef7d8d

Browse files
committed
Fix zip files created on Windows being unusable on Linux/MacOS (missing executable bits on directories)
from thejoshwolfe/yazl#59
1 parent a271452 commit cef7d8d

File tree

2 files changed

+27
-6
lines changed

2 files changed

+27
-6
lines changed

index.js

Lines changed: 12 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -83,7 +83,7 @@ class Entry {
8383
this.isDirectory = isDirectory;
8484
this.state = Entry.WAITING_FOR_METADATA;
8585
this.setLastModDate(options.mtime ?? new Date());
86-
this.setFileAttributesMode(options.mode ?? (isDirectory ? 0o40775 : 0o100664));
86+
this.setFileAttributesMode(options.mode ?? 0o000664, isDirectory);
8787
if (isDirectory) {
8888
this.crcAndFileSizeKnown = true;
8989
this.crc32 = 0;
@@ -139,10 +139,19 @@ class Entry {
139139
/**
140140
* @param {number} mode
141141
*/
142-
setFileAttributesMode(mode) {
142+
setFileAttributesMode(mode, isDirectory) {
143143
if ((mode & 0xffff) !== mode) {
144144
throw new Error(`invalid mode. expected: 0 <= ${mode} <= 65535`);
145145
}
146+
if (isDirectory) {
147+
// https://github.com/thejoshwolfe/yazl/pull/59
148+
// Set executable bit on directories if any other bits are set for that user/group/all
149+
// Fixes creating unusable zip files on platforms that do not use an executable bit
150+
mode |= ((mode >> 1) | (mode >> 2)) & 0o000111;
151+
mode |= 0o040000; // S_IFDIR
152+
} else {
153+
mode |= 0o100000; // S_IFREG
154+
}
146155
// http://unix.stackexchange.com/questions/14705/the-zip-formats-external-file-attribute/14727#14727
147156
this.externalFileAttributes = (mode << 16) >>> 0;
148157
}
@@ -381,7 +390,7 @@ class ZipFile extends EventEmitter {
381390
}
382391
entry.uncompressedSize = stats.size;
383392
if (options.mtime == null) entry.setLastModDate(stats.mtime);
384-
if (options.mode == null) entry.setFileAttributesMode(stats.mode);
393+
if (options.mode == null) entry.setFileAttributesMode(stats.mode, false);
385394
entry.setFileDataPumpFunction(() => {
386395
const readStream = createReadStream(realPath);
387396
entry.state = Entry.FILE_DATA_IN_PROGRESS;

test/test.js

Lines changed: 15 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -87,7 +87,7 @@ const filename = fileURLToPath(import.meta.url);
8787
zipfile.addBuffer(Buffer.from('buffer'), 'b.txt');
8888
zipfile.addReadStream(new BufferListStream().append('stream'), 'c.txt');
8989
zipfile.addEmptyDirectory('d/');
90-
zipfile.addEmptyDirectory('e');
90+
zipfile.addEmptyDirectory('e', { mode: 0o000644 });
9191
zipfile.end(function (finalSize) {
9292
if (finalSize !== -1) throw new Error('finalSize should be unknown');
9393
zipfile.outputStream.pipe(new BufferListStream(function (err, data) {
@@ -96,9 +96,21 @@ const filename = fileURLToPath(import.meta.url);
9696
if (err) throw err;
9797
const entryNames = ['a.txt', 'b.txt', 'c.txt', 'd/', 'e/'];
9898
zipfile.on('entry', function (entry) {
99+
const { fileName } = entry;
99100
const expectedName = entryNames.shift();
100-
if (entry.fileName !== expectedName) {
101-
throw new Error(`unexpected entry fileName: ${entry.fileName}, expected: ${expectedName}`);
101+
if (fileName !== expectedName) {
102+
throw new Error(`unexpected entry fileName: ${fileName}, expected: ${expectedName}`);
103+
}
104+
const mode = entry.externalFileAttributes >>> 16;
105+
if (fileName.endsWith('/')) {
106+
if ((mode & 0o040000) === 0) {
107+
throw new Error(`directory expected to have S_IFDIR, found ${mode.toString(8)}`);
108+
}
109+
if ((mode & 0o000111) === 0) {
110+
throw new Error(`directory expected to have executable flags, found ${mode.toString(8)}`);
111+
}
112+
} else if ((mode & 0o100000) === 0) {
113+
throw new Error(`file expected to have S_IFREG, found ${mode.toString(8)}`);
102114
}
103115
});
104116
zipfile.on('end', function () {

0 commit comments

Comments
 (0)