Skip to content

Commit

Permalink
fix: support directories named __proto__ (#945)
Browse files Browse the repository at this point in the history
* fix(#938): replace `children` plain object with Map

* test: apply code review suggestion from @G-Rath

* chore: appy `prettier`

---------

Co-authored-by: Gareth Jones <[email protected]>
  • Loading branch information
SukkaW and G-Rath authored Sep 15, 2023
1 parent a16834f commit 8d92a0b
Show file tree
Hide file tree
Showing 3 changed files with 33 additions and 22 deletions.
13 changes: 13 additions & 0 deletions src/__tests__/volume/mkdirSync.test.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import { create } from '../util';
import type Stats from '../../Stats';

describe('mkdirSync', () => {
it('can create a directory', () => {
Expand Down Expand Up @@ -50,4 +51,16 @@ describe('mkdirSync', () => {
expect(error).toBeInstanceOf(Error);
expect(error.message).toMatchSnapshot();
});

/**
* See issue #938
* https://github.com/streamich/memfs/issues/938
*/
it('can create a directory with name "__proto__"', () => {
const vol = create();

vol.mkdirSync('/__proto__');

expect(vol.statSync('/__proto__').isDirectory()).toBe(true);
});
});
20 changes: 9 additions & 11 deletions src/node.ts
Original file line number Diff line number Diff line change
Expand Up @@ -308,7 +308,7 @@ export class Link extends EventEmitter {

parent: Link;

children: { [child: string]: Link | undefined } = {};
children = new Map<string, Link | undefined>();

// Path to this node as Array: ['usr', 'bin', 'node'].
private _steps: string[] = [];
Expand All @@ -331,7 +331,7 @@ export class Link extends EventEmitter {
// Recursively sync children steps, e.g. in case of dir rename
set steps(val) {
this._steps = val;
for (const [child, link] of Object.entries(this.children)) {
for (const [child, link] of this.children.entries()) {
if (child === '.' || child === '..') {
continue;
}
Expand Down Expand Up @@ -361,7 +361,7 @@ export class Link extends EventEmitter {
link.setNode(node);

if (node.isDirectory()) {
link.children['.'] = link;
link.children.set('.', link);
link.getNode().nlink++;
}

Expand All @@ -371,13 +371,13 @@ export class Link extends EventEmitter {
}

setChild(name: string, link: Link = new Link(this.vol, this, name)): Link {
this.children[name] = link;
this.children.set(name, link);
link.parent = this;
this.length++;

const node = link.getNode();
if (node.isDirectory()) {
link.children['..'] = this;
link.children.set('..', this);
this.getNode().nlink++;
}

Expand All @@ -390,10 +390,10 @@ export class Link extends EventEmitter {
deleteChild(link: Link) {
const node = link.getNode();
if (node.isDirectory()) {
delete link.children['..'];
link.children.delete('..');
this.getNode().nlink--;
}
delete this.children[link.getName()];
this.children.delete(link.getName());
this.length--;

this.getNode().mtime = new Date();
Expand All @@ -402,9 +402,7 @@ export class Link extends EventEmitter {

getChild(name: string): Link | undefined {
this.getNode().mtime = new Date();
if (Object.hasOwnProperty.call(this.children, name)) {
return this.children[name];
}
return this.children.get(name);
}

getPath(): string {
Expand Down Expand Up @@ -446,7 +444,7 @@ export class Link extends EventEmitter {
return {
steps: this.steps,
ino: this.ino,
children: Object.keys(this.children),
children: Array.from(this.children.keys()),
};
}

Expand Down
22 changes: 11 additions & 11 deletions src/volume.ts
Original file line number Diff line number Diff line change
Expand Up @@ -528,11 +528,11 @@ export class Volume implements FsCallbackApi {
let children = link.children;

if (link.getNode().isFile()) {
children = { [link.getName()]: link.parent.getChild(link.getName()) };
children = new Map([[link.getName(), link.parent.getChild(link.getName())]]);
link = link.parent;
}

for (const name in children) {
for (const name of children.keys()) {
if (name === '.' || name === '..') {
continue;
}
Expand Down Expand Up @@ -568,7 +568,7 @@ export class Volume implements FsCallbackApi {
const links: Link[] = [];

if (paths) {
if (!(paths instanceof Array)) paths = [paths];
if (!Array.isArray(paths)) paths = [paths];
for (const path of paths) {
const filename = pathToFilename(path);
const link = this.getResolvedLink(filename);
Expand Down Expand Up @@ -1346,7 +1346,7 @@ export class Volume implements FsCallbackApi {

if (options.withFileTypes) {
const list: Dirent[] = [];
for (const name in link.children) {
for (const name of link.children.keys()) {
const child = link.getChild(name);

if (!child || name === '.' || name === '..') {
Expand All @@ -1365,7 +1365,7 @@ export class Volume implements FsCallbackApi {
}

const list: TDataOut[] = [];
for (const name in link.children) {
for (const name of link.children.keys()) {
if (name === '.' || name === '..') {
continue;
}
Expand Down Expand Up @@ -2406,23 +2406,23 @@ export class FSWatcher extends EventEmitter {
removers.forEach(r => r());
this._listenerRemovers.delete(ino);
}
Object.entries(curLink.children).forEach(([name, childLink]) => {
for (const [name, childLink] of curLink.children.entries()) {
if (childLink && name !== '.' && name !== '..') {
removeLinkNodeListeners(childLink);
}
});
}
};
removeLinkNodeListeners(l);

this.emit('change', 'rename', relative(this._filename, l.getPath()));
};

// children nodes changed
Object.entries(link.children).forEach(([name, childLink]) => {
for (const [name, childLink] of link.children.entries()) {
if (childLink && name !== '.' && name !== '..') {
watchLinkNodeChanged(childLink);
}
});
}
// link children add/remove
link.on('child:add', onLinkChildAdd);
link.on('child:delete', onLinkChildDelete);
Expand All @@ -2434,11 +2434,11 @@ export class FSWatcher extends EventEmitter {
});

if (recursive) {
Object.entries(link.children).forEach(([name, childLink]) => {
for (const [name, childLink] of link.children.entries()) {
if (childLink && name !== '.' && name !== '..') {
watchLinkChildrenChanged(childLink);
}
});
}
}
};
watchLinkNodeChanged(this._link);
Expand Down

0 comments on commit 8d92a0b

Please sign in to comment.