Skip to content

Commit 96cbce4

Browse files
authored
feat: add support for O_SYMLINK (#944)
1 parent f6d988d commit 96cbce4

File tree

3 files changed

+55
-3
lines changed

3 files changed

+55
-3
lines changed

src/__tests__/volume.test.ts

Lines changed: 44 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,9 @@ import { Volume, filenameToSteps, StatWatcher } from '../volume';
66
import hasBigInt from './hasBigInt';
77
import { tryGetChild, tryGetChildNode } from './util';
88
import { genRndStr6 } from '../node/util';
9+
import { constants } from '../constants';
10+
11+
const { O_RDWR, O_SYMLINK } = constants;
912

1013
describe('volume', () => {
1114
describe('filenameToSteps(filename): string[]', () => {
@@ -484,13 +487,26 @@ describe('volume', () => {
484487
});
485488
});
486489
describe('.read(fd, buffer, offset, length, position, callback)', () => {
487-
xit('...', () => {});
490+
const vol = new Volume();
491+
const data = 'trololo';
492+
const fileNode = (vol as any).createLink(vol.root, 'text.txt').getNode();
493+
fileNode.setString(data);
494+
vol.symlinkSync('/text.txt', '/link.txt');
495+
496+
it('Attempt to read from a symlink should throw EPERM', () => {
497+
const fd = vol.openSync('/link.txt', O_SYMLINK);
498+
expect(vol.fstatSync(fd).isSymbolicLink()).toBe(true);
499+
const buf = Buffer.alloc(10);
500+
const fn = () => vol.readSync(fd, buf, 0, 10, 0);
501+
expect(fn).toThrowError('EPERM');
502+
});
488503
});
489504
describe('.readFileSync(path[, options])', () => {
490505
const vol = new Volume();
491506
const data = 'trololo';
492507
const fileNode = (vol as any).createLink(vol.root, 'text.txt').getNode();
493508
fileNode.setString(data);
509+
494510
it('Read file at root (/text.txt)', () => {
495511
const buf = vol.readFileSync('/text.txt');
496512
const str = buf.toString();
@@ -584,6 +600,16 @@ describe('volume', () => {
584600
vol.closeSync(fd);
585601
expect(vol.readFileSync('/overwrite.txt', 'utf8')).toBe('mArmagedon');
586602
});
603+
it('Attempt to write to a symlink should throw EBADF', () => {
604+
const data = 'asdfasdf asdfasdf asdf';
605+
vol.writeFileSync('/file.txt', data);
606+
vol.symlinkSync('/file.txt', '/link.txt');
607+
608+
const fd = vol.openSync('/link.txt', O_SYMLINK | O_RDWR);
609+
expect(vol.fstatSync(fd).isSymbolicLink()).toBe(true);
610+
const fn = () => vol.writeSync(fd, 'hello');
611+
expect(fn).toThrowError('EBADF');
612+
});
587613
});
588614
describe('.write(fd, buffer, offset, length, position, callback)', () => {
589615
it('Simple write to a file descriptor', done => {
@@ -834,6 +860,8 @@ describe('volume', () => {
834860
const data = '(function(){})();';
835861
dojo.getNode().setString(data);
836862

863+
vol.symlinkSync('/dojo.js', '/link.js');
864+
837865
it('Returns basic file stats', () => {
838866
const fd = vol.openSync('/dojo.js', 'r');
839867
const stats = vol.fstatSync(fd);
@@ -855,6 +883,21 @@ describe('volume', () => {
855883
expect(() => vol.fstatSync(fd, { bigint: true })).toThrowError();
856884
}
857885
});
886+
it('Returns stats about regular file for fd opened without O_SYMLINK', () => {
887+
const fd = vol.openSync('/link.js', 0);
888+
const stats = vol.fstatSync(fd);
889+
expect(stats).toBeInstanceOf(Stats);
890+
expect(stats.size).toBe(data.length);
891+
expect(stats.isFile()).toBe(true);
892+
expect(stats.isDirectory()).toBe(false);
893+
});
894+
it('Returns stats about symlink itself for fd opened with O_SYMLINK', () => {
895+
const fd = vol.openSync('/link.js', O_SYMLINK);
896+
const stats = vol.fstatSync(fd);
897+
expect(stats.isSymbolicLink()).toBe(true);
898+
expect(stats.isFile()).toBe(false);
899+
expect(stats.size).toBe(0);
900+
});
858901
});
859902
describe('.fstat(fd, callback)', () => {
860903
xit('...', () => {});

src/constants.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ export const constants = {
1919
O_NOATIME: 262144,
2020
O_NOFOLLOW: 131072,
2121
O_SYNC: 1052672,
22+
O_SYMLINK: 2097152,
2223
O_DIRECT: 16384,
2324
O_NONBLOCK: 2048,
2425
S_IRWXU: 448,

src/volume.ts

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -68,6 +68,7 @@ const {
6868
O_TRUNC,
6969
O_APPEND,
7070
O_DIRECTORY,
71+
O_SYMLINK,
7172
F_OK,
7273
COPYFILE_EXCL,
7374
COPYFILE_FICLONE_FORCE,
@@ -95,6 +96,7 @@ const kMinPoolSpace = 128;
9596

9697
// ---------------------------------------- Error messages
9798

99+
const EPERM = 'EPERM';
98100
const ENOENT = 'ENOENT';
99101
const EBADF = 'EBADF';
100102
const EINVAL = 'EINVAL';
@@ -703,7 +705,7 @@ export class Volume implements FsCallbackApi {
703705
const modeNum = modeToNumber(mode);
704706
const fileName = pathToFilename(path);
705707
const flagsNum = flagsToNumber(flags);
706-
return this.openBase(fileName, flagsNum, modeNum);
708+
return this.openBase(fileName, flagsNum, modeNum, !(flagsNum & O_SYMLINK));
707709
}
708710

709711
open(path: PathLike, flags: TFlags, /* ... */ callback: TCallback<number>);
@@ -722,7 +724,7 @@ export class Volume implements FsCallbackApi {
722724
const fileName = pathToFilename(path);
723725
const flagsNum = flagsToNumber(flags);
724726

725-
this.wrapAsync(this.openBase, [fileName, flagsNum, modeNum], callback);
727+
this.wrapAsync(this.openBase, [fileName, flagsNum, modeNum, !(flagsNum & O_SYMLINK)], callback);
726728
}
727729

728730
private closeFile(file: File) {
@@ -762,6 +764,9 @@ export class Volume implements FsCallbackApi {
762764
position: number,
763765
): number {
764766
const file = this.getFileByFdOrThrow(fd);
767+
if (file.node.isSymlink()) {
768+
throw createError(EPERM, 'read', file.link.getPath());
769+
}
765770
return file.read(buffer, Number(offset), Number(length), position);
766771
}
767772

@@ -851,6 +856,9 @@ export class Volume implements FsCallbackApi {
851856

852857
private writeBase(fd: number, buf: Buffer, offset?: number, length?: number, position?: number | null): number {
853858
const file = this.getFileByFdOrThrow(fd, 'write');
859+
if (file.node.isSymlink()) {
860+
throw createError(EBADF, 'write', file.link.getPath());
861+
}
854862
return file.write(buf, offset, length, position);
855863
}
856864

0 commit comments

Comments
 (0)
pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy