Skip to content

fix(ext/node): return real OS file descriptors from node:fs APIs#31965

Draft
fraidev wants to merge 2 commits intodenoland:mainfrom
fraidev:fix/node-fs-use-real-file-descriptors
Draft

fix(ext/node): return real OS file descriptors from node:fs APIs#31965
fraidev wants to merge 2 commits intodenoland:mainfrom
fraidev:fix/node-fs-use-real-file-descriptors

Conversation

@fraidev
Copy link
Contributor

@fraidev fraidev commented Jan 28, 2026

Closes #6529
Closes #23012
Closes #31441
Closes #31629

Co-authored-by: @sigmaSd

Needs:

Deno's node:fs polyfills previously returned Deno Resource IDs (RIDs) from fs.open(), which are opaque internal indices. Node.js returns real OS file descriptors (integers like 3, 4, 5...), so code relying on fd semantics, including passing fds between worker threads — would break.

This PR implements a JavaScript-side FD-to-RID mapping layer:

  • fs.open() / fs.openSync() now return real OS file descriptors
  • All fd-consuming APIs (read, write, fstat, fsync, ftruncate, fchmod, fchown, etc.) translate FD → RID before calling internal Deno ops
  • fs.close() unregisters the mapping after closing
  • Cross-worker fd passing works via dup() — when a worker receives an fd it didn't open, getRid() lazily dups it to create a local resource

About Cross-worker fd passing

When a worker receives an fd via postMessage():

  1. The fd integer arrives as a plain number
  2. getRid(fd) doesn't find it in the worker's map
  3. op_node_dup_fd(fd) creates a dup'd copy owned by the worker
  4. The worker can now read/write independently
  5. Closing in the worker only closes the dup, the original fd in the parent is unaffected

This matches Node.js behavior where OS fds are process-wide.

@fraidev fraidev added the ci-draft Run the CI on draft PRs. label Jan 28, 2026
@fraidev fraidev force-pushed the fix/node-fs-use-real-file-descriptors branch 3 times, most recently from 9923810 to 95cca70 Compare February 2, 2026 17:33
@fraidev fraidev force-pushed the fix/node-fs-use-real-file-descriptors branch 3 times, most recently from 6fd1d7e to 92c32cd Compare February 4, 2026 17:37
@lucacasonato
Copy link
Member

About Cross-worker fd passing
[...]
Closing in the worker only closes the dup, the original fd in the parent is unaffected
This matches Node.js behavior where OS fds are process-wide.

these statements seem inconsistent

#[cfg(unix)]
#[op2(fast)]
#[smi]
pub fn op_node_dup_fd(
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is a dangerous op. It allows you to create a Resource for any open fd that Deno has (e.g sqlite DB)

Copy link
Contributor Author

@fraidev fraidev Feb 4, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Indeed, what do you think about separating the internal FDs? A separate set for (SQLite, etc.)

Copy link
Member

@littledivy littledivy Feb 5, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think the FD -> resource map should be in Rust so we can verify the fd exists in the map before calling dup() on it

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

FDs are assigned by the kernel - we can't have a different set for internal tasks.

@fraidev
Copy link
Contributor Author

fraidev commented Feb 4, 2026

About Cross-worker fd passing
[...]
Closing in the worker only closes the dup, the original fd in the parent is unaffected
This matches Node.js behavior where OS fds are process-wide.

these statements seem inconsistent

Why? They are valid for all process (read and write), but their lifetimes are duplicates.

@fraidev fraidev force-pushed the fix/node-fs-use-real-file-descriptors branch 5 times, most recently from edc184e to 38743af Compare February 13, 2026 00:22
@fraidev fraidev force-pushed the fix/node-fs-use-real-file-descriptors branch from 73ffe2d to f756035 Compare February 14, 2026 04:54
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

ci-draft Run the CI on draft PRs.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

gemini cli meta issue gemini-cli some times error node:fs APIs are not actually using file descriptors Support opening raw file descriptors

3 participants