Skip to content

Commit 322230d

Browse files
trivikrrichardlau
authored andcommitted
vfs: support writeFileSync with virtual fds
Route fs.writeFileSync() and fs.appendFileSync() calls with VFS-owned file descriptors through the virtual file handle instead of falling back to the native fs binding. This preserves descriptor semantics for both memory-backed VFS files and RealFSProvider handles. Signed-off-by: Kamat, Trivikram <16024985+trivikr@users.noreply.github.com> Assisted-by: openai:gpt-5.5 PR-URL: #64165Fixes: #64164 Reviewed-By: Matteo Collina <matteo.collina@gmail.com> Reviewed-By: Gürgün Dayıoğlu <hey@gurgun.day> Reviewed-By: James M Snell <jasnell@gmail.com>
1 parent aeb539a commit 322230d

2 files changed

Lines changed: 93 additions & 4 deletions

File tree

‎lib/internal/vfs/setup.js‎

Lines changed: 34 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ const {
99
}=primordials;
1010

1111
const{ Buffer }=require('buffer');
12+
const{ isArrayBufferView }=require('internal/util/types');
1213
const{ resolve, sep }=require('path');
1314
const{ fileURLToPath,URL}=require('internal/url');
1415
const{ kEmptyObject }=require('internal/util');
@@ -46,6 +47,31 @@ function noopFd(fd) {
4647
returnundefined;
4748
}
4849

50+
functiontoWriteBuffer(data,options){
51+
if(Buffer.isBuffer(data))returndata;
52+
if(isArrayBufferView(data)){
53+
returnBuffer.from(data.buffer,data.byteOffset,data.byteLength);
54+
}
55+
constencoding=typeofoptions==='string' ? options : options?.encoding;
56+
returnBuffer.from(data,encoding||'utf8');
57+
}
58+
59+
functionwriteFileSyncFd(fd,data,options){
60+
constvfd=getVirtualFd(fd);
61+
if(vfd===undefined)returnundefined;
62+
63+
constbuffer=toWriteBuffer(data,options);
64+
letoffset=0;
65+
letlength=buffer.byteLength;
66+
while(length>0){
67+
constwritten=vfd.entry.writeSync(buffer,offset,length,null);
68+
offset+=written;
69+
length-=written;
70+
}
71+
72+
returntrue;
73+
}
74+
4975
// Registry of active VFS instances.
5076
constactiveVFSList=[];
5177

@@ -288,10 +314,14 @@ function createVfsHandlers() {
288314

289315
// ==================== Sync path-based write ops ====================
290316

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(typeofpath==='number')returnwriteFileSyncFd(path,data,options);
319+
returnvfsOpVoid(path,(vfs,n)=>vfs.writeFileSync(n,data,options));
320+
},
321+
appendFileSync(path,data,options){
322+
if(typeofpath==='number')returnwriteFileSyncFd(path,data,options);
323+
returnvfsOpVoid(path,(vfs,n)=>vfs.appendFileSync(n,data,options));
324+
},
295325
mkdirSync: (path,options)=>
296326
vfsOp(path,(vfs,n)=>({result: vfs.mkdirSync(n,options)})),
297327
rmdirSync: (path)=>vfsOpVoid(path,(vfs,n)=>vfs.rmdirSync(n)),

‎test/parallel/test-vfs-fs-writeFileSync.js‎

Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,4 +26,63 @@ assert.strictEqual(fs.readFileSync(target, 'utf8'), 'fresh more');
2626
fs.writeFileSync(target,Buffer.from('binary'));
2727
assert.strictEqual(fs.readFileSync(target,'utf8'),'binary');
2828

29+
// writeFileSync via a VFS fd writes through the open descriptor.
30+
{
31+
constfdTarget=path.join(mountPoint,'src/fd.txt');
32+
constfd=fs.openSync(fdTarget,'w+');
33+
try{
34+
fs.writeFileSync(fd,'hello');
35+
fs.writeFileSync(fd,newUint8Array(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+
constfdTarget=path.join(mountPoint,'src/append-fd.txt');
45+
fs.writeFileSync(fdTarget,'start');
46+
constfd=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+
2955
myVfs.unmount();
56+
57+
// writeFileSync via a RealFSProvider fd remains tied to the open descriptor
58+
// after the backing path is renamed.
59+
{
60+
constroot=path.join('/tmp','vfs-real-writeFileSync-'+process.pid);
61+
constrealMountPoint=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+
constrealVfs=vfs
68+
.create(newvfs.RealFSProvider(root),{emitExperimentalWarning: false})
69+
.mount(realMountPoint);
70+
try{
71+
constmountedFile=path.join(realMountPoint,'a.txt');
72+
fs.writeFileSync(path.join(root,'a.txt'),'old');
73+
constfd=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+
}

0 commit comments

Comments
 (0)