summaryrefslogtreecommitdiff
path: root/na/fs/fd.cc
diff options
context:
space:
mode:
Diffstat (limited to 'na/fs/fd.cc')
-rw-r--r--na/fs/fd.cc216
1 files changed, 216 insertions, 0 deletions
diff --git a/na/fs/fd.cc b/na/fs/fd.cc
new file mode 100644
index 0000000..5fc8ab0
--- /dev/null
+++ b/na/fs/fd.cc
@@ -0,0 +1,216 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+/* Copyright (C) 2026 Richard Qin */
+
+/*
+ * fd.cc — File descriptor table management
+ */
+
+#include <lume/fdtable.h>
+#include <lume/file.h>
+#include <lume/slab.h>
+#include <lume/errno.h>
+
+/* ================================================================== */
+/* Bitmap helpers */
+/* ================================================================== */
+
+static inline bool bit_test(const uint64* bmap, int bit) {
+ return (bmap[bit / 64] >> (bit % 64)) & 1;
+}
+static inline void bit_set(uint64* bmap, int bit) {
+ bmap[bit / 64] |= (1ULL << (bit % 64));
+}
+static inline void bit_clear(uint64* bmap, int bit) {
+ bmap[bit / 64] &= ~(1ULL << (bit % 64));
+}
+
+/* ================================================================== */
+/* alloc_files_struct */
+/* ================================================================== */
+
+files_struct* alloc_files_struct()
+{
+ auto* files = new files_struct{};
+ if (!files) return nullptr;
+
+ files->count.store(1);
+ files->next_fd = 0;
+ files->max_fds = NR_OPEN_DEFAULT;
+
+ for (int i = 0; i < NR_OPEN_DEFAULT; i++)
+ files->fd_array[i] = nullptr;
+
+ int bmap_words = (NR_OPEN_DEFAULT + 63) / 64;
+ for (int i = 0; i < bmap_words; i++) {
+ files->close_on_exec_init[i] = 0;
+ files->open_fds_init[i] = 0;
+ }
+
+ return files;
+}
+
+/* ================================================================== */
+/* put_files_struct — drop reference, free on last */
+/* ================================================================== */
+
+void put_files_struct(files_struct* files)
+{
+ if (!files) return;
+
+ int old = files->count.fetch_sub(1);
+ if (old > 1) return;
+
+ /* Close all open fds */
+ for (int i = 0; i < files->max_fds; i++) {
+ if (files->fd_array[i]) {
+ fput(files->fd_array[i]);
+ files->fd_array[i] = nullptr;
+ }
+ }
+
+ delete files;
+}
+
+/* ================================================================== */
+/* dup_fd — duplicate entire fd table (for fork) */
+/* ================================================================== */
+
+files_struct* dup_fd(files_struct* old_files)
+{
+ auto* new_files = alloc_files_struct();
+ if (!new_files) return nullptr;
+
+ LockGuard guard(old_files->file_lock);
+
+ for (int i = 0; i < old_files->max_fds; i++) {
+ file* fp = old_files->fd_array[i];
+ if (fp) {
+ get_file(fp);
+ new_files->fd_array[i] = fp;
+ }
+ }
+
+ /* Copy bitmaps */
+ int bmap_words = (NR_OPEN_DEFAULT + 63) / 64;
+ for (int i = 0; i < bmap_words; i++) {
+ new_files->open_fds_init[i] = old_files->open_fds_init[i];
+ new_files->close_on_exec_init[i] = old_files->close_on_exec_init[i];
+ }
+
+ new_files->next_fd = old_files->next_fd;
+
+ return new_files;
+}
+
+/* ================================================================== */
+/* alloc_fd — find lowest free fd >= start */
+/* ================================================================== */
+
+int alloc_fd(files_struct* files, unsigned int start)
+{
+ LockGuard guard(files->file_lock);
+
+ for (int fd = static_cast<int>(start); fd < files->max_fds; fd++) {
+ if (!bit_test(files->open_fds_init, fd)) {
+ bit_set(files->open_fds_init, fd);
+ files->fd_array[fd] = nullptr; /* placeholder, fd_install sets real file */
+ if (fd >= files->next_fd)
+ files->next_fd = fd + 1;
+ return fd;
+ }
+ }
+
+ return -EMFILE;
+}
+
+/* ================================================================== */
+/* fd_install — place a file* at a given fd */
+/* ================================================================== */
+
+void fd_install(files_struct* files, int fd, file* fp)
+{
+ LockGuard guard(files->file_lock);
+ files->fd_array[fd] = fp;
+}
+
+/* ================================================================== */
+/* fcheck — look up file by fd (no refcount bump) */
+/* ================================================================== */
+
+file* fcheck(files_struct* files, int fd)
+{
+ if (fd < 0 || fd >= files->max_fds) return nullptr;
+ return files->fd_array[fd];
+}
+
+/* ================================================================== */
+/* close_fd */
+/* ================================================================== */
+
+int close_fd(files_struct* files, int fd)
+{
+ file* fp;
+ {
+ LockGuard guard(files->file_lock);
+ if (fd < 0 || fd >= files->max_fds) return -EBADF;
+
+ fp = files->fd_array[fd];
+ if (!fp) return -EBADF;
+
+ files->fd_array[fd] = nullptr;
+ bit_clear(files->open_fds_init, fd);
+ bit_clear(files->close_on_exec_init, fd);
+ }
+
+ fput(fp);
+ return 0;
+}
+
+/* ================================================================== */
+/* do_dup2 — duplicate oldfd to newfd */
+/* ================================================================== */
+
+int do_dup2(files_struct* files, int oldfd, int newfd, int flags)
+{
+ if (oldfd == newfd) return -EINVAL;
+
+ file* existing = nullptr;
+ {
+ LockGuard guard(files->file_lock);
+
+ if (oldfd < 0 || oldfd >= files->max_fds) return -EBADF;
+ if (newfd < 0 || newfd >= files->max_fds) return -EBADF;
+
+ file* old_fp = files->fd_array[oldfd];
+ if (!old_fp) return -EBADF;
+
+ existing = files->fd_array[newfd];
+
+ get_file(old_fp);
+ files->fd_array[newfd] = old_fp;
+ bit_set(files->open_fds_init, newfd);
+
+ if (flags & O_CLOEXEC)
+ bit_set(files->close_on_exec_init, newfd);
+ else
+ bit_clear(files->close_on_exec_init, newfd);
+ }
+
+ if (existing)
+ fput(existing);
+
+ return newfd;
+}
+
+/* ================================================================== */
+/* do_close_on_exec — close all fds with CLOEXEC set */
+/* ================================================================== */
+
+void do_close_on_exec(files_struct* files)
+{
+ for (int fd = 0; fd < files->max_fds; fd++) {
+ if (bit_test(files->close_on_exec_init, fd)) {
+ close_fd(files, fd);
+ }
+ }
+}