GeezerVison is showing github.com — vfs: support writeFileSync with virtual fds · nodejs/node@322230d
File treeExpand file treeCollapse file tree Open diff view settings Expand file treeCollapse file tree Open diff view settings Collapse file Copy file name to clipboardExpand all lines: lib/internal/vfs/setup.js +34-4Lines changed: 34 additions & 4 deletions | Original file line number | Diff line number | Diff line change |
|---|
| 9 | 9 | } = primordials; | 10 | 10 |
| 11 | 11 | const { Buffer } = require('buffer'); | | 12 | +const { isArrayBufferView } = require('internal/util/types'); | 12 | 13 | const { resolve, sep } = require('path'); | 13 | 14 | const { fileURLToPath, URL } = require('internal/url'); | 14 | 15 | const { kEmptyObject } = require('internal/util'); | @@ -46,6 +47,31 @@ function noopFd(fd) { | 46 | 47 | return undefined; | 47 | 48 | } | 48 | 49 |
| | 50 | +function toWriteBuffer(data, options) { | | 51 | + if (Buffer.isBuffer(data)) return data; | | 52 | + if (isArrayBufferView(data)) { | | 53 | + return Buffer.from(data.buffer, data.byteOffset, data.byteLength); | | 54 | + } | | 55 | + const encoding = typeof options === 'string' ? options : options?.encoding; | | 56 | + return Buffer.from(data, encoding || 'utf8'); | | 57 | +} | | 58 | + | | 59 | +function writeFileSyncFd(fd, data, options) { | | 60 | + const vfd = getVirtualFd(fd); | | 61 | + if (vfd === undefined) return undefined; | | 62 | + | | 63 | + const buffer = toWriteBuffer(data, options); | | 64 | + let offset = 0; | | 65 | + let length = buffer.byteLength; | | 66 | + while (length > 0) { | | 67 | + const written = vfd.entry.writeSync(buffer, offset, length, null); | | 68 | + offset += written; | | 69 | + length -= written; | | 70 | + } | | 71 | + | | 72 | + return true; | | 73 | +} | | 74 | + | 49 | 75 | // Registry of active VFS instances. | 50 | 76 | const activeVFSList = []; | 51 | 77 |
| @@ -288,10 +314,14 @@ function createVfsHandlers() { | 288 | 314 |
| 289 | 315 | // ==================== Sync path-based write ops ==================== | 290 | 316 |
| 291 | | - writeFileSync: (path, data, options) => | 292 | | - vfsOpVoid(path, (vfs, n) => vfs.writeFileSync(n, data, options)), | 293 | | - appendFileSync: (path, data, options) => | 294 | | - vfsOpVoid(path, (vfs, n) => vfs.appendFileSync(n, data, options)), | | 317 | + writeFileSync(path, data, options) { | | 318 | + if (typeof path === 'number') return writeFileSyncFd(path, data, options); | | 319 | + return vfsOpVoid(path, (vfs, n) => vfs.writeFileSync(n, data, options)); | | 320 | + }, | | 321 | + appendFileSync(path, data, options) { | | 322 | + if (typeof path === 'number') return writeFileSyncFd(path, data, options); | | 323 | + return vfsOpVoid(path, (vfs, n) => vfs.appendFileSync(n, data, options)); | | 324 | + }, | 295 | 325 | mkdirSync: (path, options) => | 296 | 326 | vfsOp(path, (vfs, n) => ({ result: vfs.mkdirSync(n, options) })), | 297 | 327 | rmdirSync: (path) => vfsOpVoid(path, (vfs, n) => vfs.rmdirSync(n)), | |
Collapse file Copy file name to clipboardExpand all lines: test/parallel/test-vfs-fs-writeFileSync.js +59Lines changed: 59 additions & 0 deletions | Original file line number | Diff line number | Diff line change |
|---|
@@ -26,4 +26,63 @@ assert.strictEqual(fs.readFileSync(target, 'utf8'), 'fresh more'); | 26 | 26 | fs.writeFileSync(target, Buffer.from('binary')); | 27 | 27 | assert.strictEqual(fs.readFileSync(target, 'utf8'), 'binary'); | 28 | 28 |
| | 29 | +// writeFileSync via a VFS fd writes through the open descriptor. | | 30 | +{ | | 31 | + const fdTarget = path.join(mountPoint, 'src/fd.txt'); | | 32 | + const fd = fs.openSync(fdTarget, 'w+'); | | 33 | + try { | | 34 | + fs.writeFileSync(fd, 'hello'); | | 35 | + fs.writeFileSync(fd, new Uint8Array(Buffer.from(' world'))); | | 36 | + assert.strictEqual(fs.readFileSync(fdTarget, 'utf8'), 'hello world'); | | 37 | + } finally { | | 38 | + fs.closeSync(fd); | | 39 | + } | | 40 | +} | | 41 | + | | 42 | +// appendFileSync via a VFS fd follows normal fd write semantics. | | 43 | +{ | | 44 | + const fdTarget = path.join(mountPoint, 'src/append-fd.txt'); | | 45 | + fs.writeFileSync(fdTarget, 'start'); | | 46 | + const fd = fs.openSync(fdTarget, 'a'); | | 47 | + try { | | 48 | + fs.appendFileSync(fd, ' end'); | | 49 | + assert.strictEqual(fs.readFileSync(fdTarget, 'utf8'), 'start end'); | | 50 | + } finally { | | 51 | + fs.closeSync(fd); | | 52 | + } | | 53 | +} | | 54 | + | 29 | 55 | myVfs.unmount(); | | 56 | + | | 57 | +// writeFileSync via a RealFSProvider fd remains tied to the open descriptor | | 58 | +// after the backing path is renamed. | | 59 | +{ | | 60 | + const root = path.join('/tmp', 'vfs-real-writeFileSync-' + process.pid); | | 61 | + const realMountPoint = path.join('/tmp', 'vfs-real-writeFileSync-mount-' + process.pid); | | 62 | + fs.rmSync(root, { recursive: true, force: true }); | | 63 | + fs.rmSync(realMountPoint, { recursive: true, force: true }); | | 64 | + fs.mkdirSync(root, { recursive: true }); | | 65 | + fs.mkdirSync(realMountPoint, { recursive: true }); | | 66 | + | | 67 | + const realVfs = vfs | | 68 | + .create(new vfs.RealFSProvider(root), { emitExperimentalWarning: false }) | | 69 | + .mount(realMountPoint); | | 70 | + try { | | 71 | + const mountedFile = path.join(realMountPoint, 'a.txt'); | | 72 | + fs.writeFileSync(path.join(root, 'a.txt'), 'old'); | | 73 | + const fd = fs.openSync(mountedFile, 'r+'); | | 74 | + try { | | 75 | + fs.renameSync(path.join(root, 'a.txt'), path.join(root, 'b.txt')); | | 76 | + fs.writeFileSync(path.join(root, 'a.txt'), 'new'); | | 77 | + fs.writeFileSync(fd, 'updated'); | | 78 | + assert.strictEqual(fs.readFileSync(path.join(root, 'b.txt'), 'utf8'), 'updated'); | | 79 | + assert.strictEqual(fs.readFileSync(path.join(root, 'a.txt'), 'utf8'), 'new'); | | 80 | + } finally { | | 81 | + fs.closeSync(fd); | | 82 | + } | | 83 | + } finally { | | 84 | + realVfs.unmount(); | | 85 | + fs.rmSync(root, { recursive: true, force: true }); | | 86 | + fs.rmSync(realMountPoint, { recursive: true, force: true }); | | 87 | + } | | 88 | +} |
|