metro-memory-fs: add lstat() and fstat()

Reviewed By: mjesun

Differential Revision: D7257967

fbshipit-source-id: 56c59ca088bfd71fc111efdfda2f5393d6abbd9b
This commit is contained in:
Jean Lauliac 2018-03-16 08:42:05 -07:00 committed by Facebook Github Bot
parent bfecccd180
commit 23bd78220b
2 changed files with 82 additions and 17 deletions

View File

@ -317,13 +317,46 @@ it('gives the real path via a symlinked directory', () => {
expect(fs.realpathSync('/baz/foo.txt')).toEqual('/glo/foo.txt');
});
it('gives stat about a regular file', () => {
fs.writeFileSync('/foo.txt', 'test');
const st = fs.statSync('/foo.txt');
expect(st.isFile()).toBe(true);
expect(st.isDirectory()).toBe(false);
expect(st.isSymbolicLink()).toBe(false);
expect(st.size).toBe(4);
describe('stat', () => {
it('works for a regular file', () => {
fs.writeFileSync('/foo.txt', 'test');
const st = fs.statSync('/foo.txt');
expect(st.isFile()).toBe(true);
expect(st.isDirectory()).toBe(false);
expect(st.isSymbolicLink()).toBe(false);
expect(st.size).toBe(4);
});
it('works for a symlinked file', () => {
fs.writeFileSync('/foo.txt', 'test');
fs.symlinkSync('foo.txt', '/bar.txt');
const st = fs.statSync('/bar.txt');
expect(st.isFile()).toBe(true);
expect(st.isDirectory()).toBe(false);
expect(st.isSymbolicLink()).toBe(false);
expect(st.size).toBe(4);
});
});
describe('lstat', () => {
it('works for a regular file', () => {
fs.writeFileSync('/foo.txt', 'test');
const st = fs.lstatSync('/foo.txt');
expect(st.isFile()).toBe(true);
expect(st.isDirectory()).toBe(false);
expect(st.isSymbolicLink()).toBe(false);
expect(st.size).toBe(4);
});
it('works for a symlink', () => {
const linkStr = 'foo/bar/baz.txt';
fs.symlinkSync(linkStr, '/bar.txt');
const st = fs.lstatSync('/bar.txt');
expect(st.isFile()).toBe(false);
expect(st.isDirectory()).toBe(false);
expect(st.isSymbolicLink()).toBe(true);
expect(st.size).toBe(linkStr.length);
});
});
it('able to list files of a directory', () => {

View File

@ -83,6 +83,8 @@ const FLAGS_SPECS: {
const ASYNC_FUNC_NAMES = [
'close',
'fstat',
'lstat',
'open',
'read',
'readdir',
@ -204,10 +206,7 @@ class MemoryFs {
length: number,
position: ?number,
): number => {
const desc = this._fds.get(fd);
if (desc == null) {
throw makeError('EBADF', null, 'file descriptor is not open');
}
const desc = this._getDesc(fd);
if (!desc.readable) {
throw makeError('EBADF', null, 'file descriptor cannot be written to');
}
@ -417,6 +416,22 @@ class MemoryFs {
return new Stats(node);
};
lstatSync = (filePath: string | Buffer) => {
filePath = pathStr(filePath);
const {node} = this._resolve(filePath, {
keepFinalSymlink: true,
});
if (node == null) {
throw makeError('ENOENT', filePath, 'no such file or directory');
}
return new Stats(node);
};
fstatSync = (fd: number) => {
const desc = this._getDesc(fd);
return new Stats(desc.node);
};
createReadStream = (
filePath: string | Buffer,
options?:
@ -540,7 +555,14 @@ class MemoryFs {
* Implemented according with
* http://man7.org/linux/man-pages/man7/path_resolution.7.html
*/
_resolve(originalFilePath: string): Resolution {
_resolve(
originalFilePath: string,
options?: {keepFinalSymlink: boolean},
): Resolution {
let keepFinalSymlink = false;
if (options != null) {
({keepFinalSymlink} = options);
}
let filePath = originalFilePath;
let drive = '';
if (path === path.win32 && filePath.match(/^[a-zA-Z]:\\/)) {
@ -562,6 +584,7 @@ class MemoryFs {
nodePath: [['', this._root]],
entNames,
symlinkCount: 0,
keepFinalSymlink,
};
while (context.entNames.length > 0) {
const entName = context.entNames.shift();
@ -597,7 +620,11 @@ class MemoryFs {
return;
}
const childNode = entries.get(entName);
if (childNode == null || childNode.type !== 'symbolicLink') {
if (
childNode == null ||
childNode.type !== 'symbolicLink' ||
(context.keepFinalSymlink && context.entNames.length === 0)
) {
context.node = childNode;
context.nodePath.push([entName, childNode]);
return;
@ -623,10 +650,7 @@ class MemoryFs {
length: number,
position: ?number,
): number {
const desc = this._fds.get(fd);
if (desc == null) {
throw makeError('EBADF', null, 'file descriptor is not open');
}
const desc = this._getDesc(fd);
if (!desc.writable) {
throw makeError('EBADF', null, 'file descriptor cannot be written to');
}
@ -655,6 +679,14 @@ class MemoryFs {
this._fds.set(fd, desc);
return fd;
}
_getDesc(fd: number): Descriptor {
const desc = this._fds.get(fd);
if (desc == null) {
throw makeError('EBADF', null, 'file descriptor is not open');
}
return desc;
}
}
class Stats {