Skip to content

Commit 5067a82

Browse files
committed
vfs: add ZipProvider
Add a node:vfs provider backed by a node:zlib ZIP archive - a ZipBuffer held in memory or a ZipFile on disk - that exposes the archive's members as a virtual filesystem tree. Directories are recognized both explicitly (a "name/" entry) and implicitly (any entry under "name/"), and a file opened for writing commits its content as a new archive entry when its handle is closed. The provider is read-only unless the backing archive is writable, and offers both asynchronous and synchronous operations. Available as vfs.ZipProvider. Signed-off-by: Philipp Dunkel <pip@pipobscure.com>
1 parent a4aa3c0 commit 5067a82

6 files changed

Lines changed: 1559 additions & 4 deletions

File tree

‎doc/api/vfs.md‎

Lines changed: 59 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -38,10 +38,12 @@ Mounting a VFS only redirects supported [`node:fs`][] calls whose resolved paths
3838
are under the mount point. It does not prevent code from using other paths or
3939
other Node.js APIs to access resources available to the process.
4040
[`RealFSProvider`][] maps VFS paths under its configured root and rejects paths
41-
that resolve outside that root, but that check is not a security boundary. Do
42-
not rely on VFS to run untrusted code; use operating-system-level isolation,
43-
such as separate users, containers, or platform sandboxes, when a security
44-
boundary is required.
41+
that resolve outside that root, but that check is not a security boundary.
42+
[`ZipProvider`][] has no real file-system paths of its own to escape; its
43+
entries only ever exist within the archive's own namespace. Do not rely on VFS
44+
to run untrusted code; use operating-system-level isolation, such as separate
45+
users, containers, or platform sandboxes, when a security boundary is
46+
required.
4547

4648
## Basic usage
4749

@@ -302,6 +304,55 @@ added: v26.4.0
302304

303305
The resolved absolute path used as the root.
304306

307+
## Class: `ZipProvider`
308+
309+
<!-- YAML
310+
added: REPLACEME
311+
-->
312+
313+
A provider that exposes the entries of a ZIP archive - either a
314+
[`zlib.ZipBuffer`][] (in memory) or a [`zlib.ZipFile`][] (on disk) - through
315+
the VFS API. `provider.readonly` reflects the archive's own
316+
[`zipFile.writable`][] flag: a `ZipBuffer` is always writable, and a
317+
`ZipFile` is writable only when opened with `{ writable: true }`.
318+
319+
Directories are recognized both explicitly (an entry whose name ends in `/`)
320+
and implicitly (any entry name starting with `"<dir>/"`). `readdir()` does
321+
not support `{ recursive: true }`. Because a ZIP member cannot be edited or
322+
read in place - only fully written or fully decompressed - a file opened for
323+
writing only commits its content (as a new archive entry) when the handle is
324+
closed.
325+
326+
Every method has a synchronous counterpart (`openSync()`, `statSync()`,
327+
`readdirSync()`, and so on), backed by the equally complete synchronous
328+
surface [`zlib.ZipBuffer`][]/[`zlib.ZipFile`][] expose. As with those, the
329+
synchronous methods here block the Node.js event loop and further JavaScript
330+
execution until the operation - including any deflate/inflate pass -
331+
completes.
332+
333+
```cjs
334+
const vfs = require('node:vfs');
335+
const zlib = require('node:zlib');
336+
const { readFileSync } = require('node:fs');
337+
338+
async function main() {
339+
const zip = new zlib.ZipBuffer(readFileSync('archive.zip'));
340+
const archiveVfs = vfs.create(new vfs.ZipProvider(zip));
341+
342+
console.log(await archiveVfs.promises.readdir('/'));
343+
await archiveVfs.promises.writeFile('/new.txt', 'hello');
344+
}
345+
main();
346+
```
347+
348+
### `new ZipProvider(source)`
349+
350+
<!-- YAML
351+
added: REPLACEME
352+
-->
353+
354+
* `source` {zlib.ZipBuffer|zlib.ZipFile} An already-open archive.
355+
305356
## Implementation details
306357

307358
### `Stats` objects
@@ -320,6 +371,10 @@ fields use synthetic but stable values:
320371
[`RealFSProvider`]: #class-realfsprovider
321372
[`VirtualFileSystem`]: #class-virtualfilesystem
322373
[`VirtualProvider`]: #class-virtualprovider
374+
[`ZipProvider`]: #class-zipprovider
323375
[`fs.BigIntStats`]: fs.md#class-fsbigintstats
324376
[`fs.Stats`]: fs.md#class-fsstats
325377
[`node:fs`]: fs.md
378+
[`zipFile.writable`]: zlib.md#zipfilewritable
379+
[`zlib.ZipBuffer`]: zlib.md#class-zlibzipbuffer
380+
[`zlib.ZipFile`]: zlib.md#class-zlibzipfile

0 commit comments

Comments
 (0)