Skip to content

Commit 7facbd0

Browse files
authored
feat(fslib): add fchmod support (#4466)
1 parent 235f5a7 commit 7facbd0

File tree

16 files changed

+230
-4
lines changed

16 files changed

+230
-4
lines changed

.pnp.cjs

Lines changed: 40 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

.yarn/versions/64fa7941.yml

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
releases:
2+
"@yarnpkg/cli": patch
3+
"@yarnpkg/fslib": minor
4+
"@yarnpkg/pnp": patch
5+
"@yarnpkg/pnpify": patch
6+
7+
declined:
8+
- "@yarnpkg/esbuild-plugin-pnp"
9+
- "@yarnpkg/plugin-compat"
10+
- "@yarnpkg/plugin-constraints"
11+
- "@yarnpkg/plugin-dlx"
12+
- "@yarnpkg/plugin-essentials"
13+
- "@yarnpkg/plugin-exec"
14+
- "@yarnpkg/plugin-file"
15+
- "@yarnpkg/plugin-git"
16+
- "@yarnpkg/plugin-github"
17+
- "@yarnpkg/plugin-http"
18+
- "@yarnpkg/plugin-init"
19+
- "@yarnpkg/plugin-interactive-tools"
20+
- "@yarnpkg/plugin-link"
21+
- "@yarnpkg/plugin-nm"
22+
- "@yarnpkg/plugin-npm"
23+
- "@yarnpkg/plugin-npm-cli"
24+
- "@yarnpkg/plugin-pack"
25+
- "@yarnpkg/plugin-patch"
26+
- "@yarnpkg/plugin-pnp"
27+
- "@yarnpkg/plugin-pnpm"
28+
- "@yarnpkg/plugin-stage"
29+
- "@yarnpkg/plugin-typescript"
30+
- "@yarnpkg/plugin-version"
31+
- "@yarnpkg/plugin-workspace-tools"
32+
- vscode-zipfs
33+
- "@yarnpkg/builder"
34+
- "@yarnpkg/core"
35+
- "@yarnpkg/doctor"
36+
- "@yarnpkg/json-proxy"
37+
- "@yarnpkg/nm"
38+
- "@yarnpkg/sdks"
39+
- "@yarnpkg/shell"

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,7 @@ The following changes only affect people writing Yarn plugins:
3838
### Compatibility
3939

4040
- The patched filesystem now supports `ftruncate`.
41+
- The patched filesystem now supports `fchmod`.
4142
- Updates the PnP compatibility layer for TypeScript 4.8 Beta
4243

4344
## 3.2.1

packages/yarnpkg-core/sources/worker-zip/index.js

Lines changed: 1 addition & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

packages/yarnpkg-fslib/sources/FakeFS.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -189,6 +189,8 @@ export abstract class FakeFS<P extends Path> {
189189

190190
abstract chmodPromise(p: P, mask: number): Promise<void>;
191191
abstract chmodSync(p: P, mask: number): void;
192+
abstract fchmodPromise(fd: number, mask: number): Promise<void>;
193+
abstract fchmodSync(fd: number, mask: number): void;
192194

193195
abstract chownPromise(p: P, uid: number, gid: number): Promise<void>;
194196
abstract chownSync(p: P, uid: number, gid: number): void;

packages/yarnpkg-fslib/sources/NoFS.ts

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -126,6 +126,14 @@ export class NoFS extends FakeFS<PortablePath> {
126126
throw makeError();
127127
}
128128

129+
async fchmodPromise(): Promise<never> {
130+
throw makeError();
131+
}
132+
133+
fchmodSync(): never {
134+
throw makeError();
135+
}
136+
129137
async chmodPromise(): Promise<never> {
130138
throw makeError();
131139
}

packages/yarnpkg-fslib/sources/NodeFS.ts

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -230,6 +230,16 @@ export class NodeFS extends BasePortableFakeFS {
230230
}
231231
}
232232

233+
async fchmodPromise(fd: number, mask: number): Promise<void> {
234+
return await new Promise<void>((resolve, reject) => {
235+
this.realFs.fchmod(fd, mask, this.makeCallback(resolve, reject));
236+
});
237+
}
238+
239+
fchmodSync(fd: number, mask: number): void {
240+
return this.realFs.fchmodSync(fd, mask);
241+
}
242+
233243
async chmodPromise(p: PortablePath, mask: number) {
234244
return await new Promise<void>((resolve, reject) => {
235245
this.realFs.chmod(npath.fromPortablePath(p), mask, this.makeCallback(resolve, reject));

packages/yarnpkg-fslib/sources/ProxiedFS.ts

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -156,6 +156,14 @@ export abstract class ProxiedFS<P extends Path, IP extends Path> extends FakeFS<
156156
return this.baseFs.lstatSync(this.mapToBase(p), opts);
157157
}
158158

159+
async fchmodPromise(fd: number, mask: number): Promise<void> {
160+
return this.baseFs.fchmodPromise(fd, mask);
161+
}
162+
163+
fchmodSync(fd: number, mask: number): void {
164+
return this.baseFs.fchmodSync(fd, mask);
165+
}
166+
159167
async chmodPromise(p: P, mask: number) {
160168
return this.baseFs.chmodPromise(this.mapToBase(p), mask);
161169
}

packages/yarnpkg-fslib/sources/ZipFS.ts

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -983,6 +983,14 @@ export class ZipFS extends BasePortableFakeFS {
983983
}
984984
}
985985

986+
async fchmodPromise(fd: number, mask: number): Promise<void> {
987+
return this.chmodPromise(this.fdToPath(fd, `fchmod`), mask);
988+
}
989+
990+
fchmodSync(fd: number, mask: number): void {
991+
return this.chmodSync(this.fdToPath(fd, `fchmodSync`), mask);
992+
}
993+
986994
async chmodPromise(p: PortablePath, mask: number) {
987995
return this.chmodSync(p, mask);
988996
}

packages/yarnpkg-fslib/sources/ZipOpenFS.ts

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -457,6 +457,30 @@ export class ZipOpenFS extends BasePortableFakeFS {
457457
});
458458
}
459459

460+
async fchmodPromise(fd: number, mask: number): Promise<void> {
461+
if ((fd & ZIP_FD) === 0)
462+
return this.baseFs.fchmodPromise(fd, mask);
463+
464+
const entry = this.fdMap.get(fd);
465+
if (typeof entry === `undefined`)
466+
throw errors.EBADF(`fchmod`);
467+
468+
const [zipFs, realFd] = entry;
469+
return zipFs.fchmodPromise(realFd, mask);
470+
}
471+
472+
fchmodSync(fd: number, mask: number): void {
473+
if ((fd & ZIP_FD) === 0)
474+
return this.baseFs.fchmodSync(fd, mask);
475+
476+
const entry = this.fdMap.get(fd);
477+
if (typeof entry === `undefined`)
478+
throw errors.EBADF(`fchmodSync`);
479+
480+
const [zipFs, realFd] = entry;
481+
return zipFs.fchmodSync(realFd, mask);
482+
}
483+
460484
async chmodPromise(p: PortablePath, mask: number) {
461485
return await this.makeCallPromise(p, async () => {
462486
return await this.baseFs.chmodPromise(p, mask);

packages/yarnpkg-fslib/sources/patchFs.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ const SYNC_IMPLEMENTATIONS = new Set([
1111
`createReadStream`,
1212
`createWriteStream`,
1313
`chmodSync`,
14+
`fchmodSync`,
1415
`chownSync`,
1516
`closeSync`,
1617
`copyFileSync`,
@@ -44,6 +45,7 @@ const SYNC_IMPLEMENTATIONS = new Set([
4445
const ASYNC_IMPLEMENTATIONS = new Set([
4546
`accessPromise`,
4647
`appendFilePromise`,
48+
`fchmodPromise`,
4749
`chmodPromise`,
4850
`chownPromise`,
4951
`closePromise`,

packages/yarnpkg-fslib/tests/NodeFS.test.ts

Lines changed: 30 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,10 @@
1-
import {NodeFS} from '../sources/NodeFS';
2-
import {xfs, PortablePath} from '../sources';
1+
import {NodeFS} from '../sources/NodeFS';
2+
import {xfs, PortablePath, ppath, Filename} from '../sources';
33

44
const nodeFs = new NodeFS();
55

6+
const ifNotWin32It = process.platform !== `win32` ? it : it.skip;
7+
68
describe(`NodeFS`, () => {
79
describe(`copyPromise`, () => {
810
it(`should support copying files`, async () => {
@@ -65,4 +67,30 @@ describe(`NodeFS`, () => {
6567
});
6668
});
6769
});
70+
71+
ifNotWin32It(`should support fchmodPromise`, async () => {
72+
await xfs.mktempPromise(async dir => {
73+
const p = ppath.join(dir, `foo.txt` as Filename);
74+
await nodeFs.writeFilePromise(p, ``);
75+
76+
const fd = await nodeFs.openPromise(p, `w`);
77+
await nodeFs.fchmodPromise(fd, 0o744);
78+
await nodeFs.closePromise(fd);
79+
80+
expect((await nodeFs.statPromise(p)).mode & 0o777).toBe(0o744);
81+
});
82+
});
83+
84+
ifNotWin32It(`should support fchmodSync`, () => {
85+
xfs.mktempSync(dir => {
86+
const p = ppath.join(dir, `bar.txt` as Filename);
87+
nodeFs.writeFileSync(p, ``);
88+
89+
const fd = nodeFs.openSync(p, `w`);
90+
nodeFs.fchmodSync(fd, 0o744);
91+
nodeFs.closeSync(fd);
92+
93+
expect((nodeFs.statSync(p)).mode & 0o777).toBe(0o744);
94+
});
95+
});
6896
});

packages/yarnpkg-fslib/tests/ZipFS.test.ts

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -813,6 +813,18 @@ describe(`ZipFS`, () => {
813813
zipFs.discardAndClose();
814814
});
815815

816+
it(`should support fchmodSync`, () => {
817+
const zipFs = new ZipFS(null, {libzip: getLibzipSync()});
818+
819+
zipFs.writeFileSync(`/foo.txt` as Filename, `foo`);
820+
const fd = zipFs.openSync(`/foo.txt` as Filename, `rw`);
821+
zipFs.fchmodSync(fd, 0o754);
822+
zipFs.closeSync(fd);
823+
expect(zipFs.statSync(`/foo.txt` as Filename).mode & 0o777).toBe(0o754);
824+
825+
zipFs.discardAndClose();
826+
});
827+
816828
it(`should support writeFile mode`, async () => {
817829
const zipFs = new ZipFS(null, {libzip: getLibzipSync()});
818830

packages/yarnpkg-fslib/tests/patchedFs.test.ts

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,8 @@ import {statUtils, ZipFS, ZipOpenFS} from '../sources';
1212

1313
import {ZIP_FILE1, ZIP_DIR1} from './ZipOpenFS.test';
1414

15+
const ifNotWin32It = process.platform !== `win32` ? it : it.skip;
16+
1517
describe(`patchedFs`, () => {
1618
it(`in case of no error, give null: fs.stat`, done => {
1719
const file = npath.join(__dirname, `patchedFs.test.ts` as Filename);
@@ -257,4 +259,37 @@ describe(`patchedFs`, () => {
257259
patchedFs.closeSync(fd);
258260
}
259261
});
262+
263+
ifNotWin32It(`should support fchmodSync`, async () => {
264+
await xfs.mktempPromise(async dir => {
265+
const patchedFs = extendFs(fs, new PosixFS(new NodeFS()));
266+
const p = npath.join(npath.fromPortablePath(dir), `foo.txt`);
267+
268+
const fd = patchedFs.openSync(p, `w`);
269+
patchedFs.fchmodSync(fd, 0o744);
270+
patchedFs.closeSync(fd);
271+
272+
expect((patchedFs.statSync(p)).mode & 0o777).toBe(0o744);
273+
});
274+
});
275+
276+
ifNotWin32It(`should support fchmod`, async () => {
277+
const patchedFs = extendFs(fs, new PosixFS(new NodeFS()));
278+
279+
await xfs.mktempPromise(async dir => {
280+
const p = npath.join(npath.fromPortablePath(dir), `foo.txt`);
281+
282+
const fd = patchedFs.openSync(p, `w`);
283+
284+
await new Promise<void>((resolve, reject) => {
285+
patchedFs.fchmod(fd, 0o744, err => {
286+
err ? reject(err) : resolve();
287+
});
288+
});
289+
290+
patchedFs.closeSync(fd);
291+
292+
expect((patchedFs.statSync(p)).mode & 0o777).toBe(0o744);
293+
});
294+
});
260295
});

packages/yarnpkg-pnp/sources/hook.js

Lines changed: 1 addition & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

packages/yarnpkg-pnpify/sources/NodeModulesFS.ts

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -135,6 +135,7 @@ export class PortableNodeModulesFS extends FakeFS<PortablePath> {
135135
return pnpPath.resolvedPath;
136136
}
137137

138+
private resolveDirOrFilePath(p: number): number;
138139
private resolveDirOrFilePath(p: PortablePath): PortablePath;
139140
private resolveDirOrFilePath(p: FSPath<PortablePath>): FSPath<PortablePath>;
140141
private resolveDirOrFilePath(p: FSPath<PortablePath>): FSPath<PortablePath> {
@@ -361,6 +362,14 @@ export class PortableNodeModulesFS extends FakeFS<PortablePath> {
361362
});
362363
}
363364

365+
async fchmodPromise(fd: number, mask: number): Promise<void> {
366+
return this.baseFs.fchmodPromise(this.resolveDirOrFilePath(fd), mask);
367+
}
368+
369+
fchmodSync(fd: number, mask: number): void {
370+
return this.baseFs.fchmodSync(this.resolveDirOrFilePath(fd), mask);
371+
}
372+
364373
async chmodPromise(p: PortablePath, mask: number) {
365374
return await this.baseFs.chmodPromise(this.resolveDirOrFilePath(p), mask);
366375
}

0 commit comments

Comments
 (0)