From 23bd78220ba3c64d172bfaf76ddd5c2f90ef0f99 Mon Sep 17 00:00:00 2001 From: Jean Lauliac Date: Fri, 16 Mar 2018 08:42:05 -0700 Subject: [PATCH] metro-memory-fs: add lstat() and fstat() Reviewed By: mjesun Differential Revision: D7257967 fbshipit-source-id: 56c59ca088bfd71fc111efdfda2f5393d6abbd9b --- .../src/__tests__/index-test.js | 47 ++++++++++++++--- packages/metro-memory-fs/src/index.js | 52 +++++++++++++++---- 2 files changed, 82 insertions(+), 17 deletions(-) diff --git a/packages/metro-memory-fs/src/__tests__/index-test.js b/packages/metro-memory-fs/src/__tests__/index-test.js index cebaad22..7956207d 100644 --- a/packages/metro-memory-fs/src/__tests__/index-test.js +++ b/packages/metro-memory-fs/src/__tests__/index-test.js @@ -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', () => { diff --git a/packages/metro-memory-fs/src/index.js b/packages/metro-memory-fs/src/index.js index d78dad71..82c4069c 100644 --- a/packages/metro-memory-fs/src/index.js +++ b/packages/metro-memory-fs/src/index.js @@ -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 {