fs/vfs: Separate file descriptors from file descriptions

This patch is a rework of the NuttX file descriptor implementation. The
goal is two-fold:

1. Improve POSIX compliance. The old implementation tied file description
to inode only, not the file struct. POSIX however dictates otherwise.
2. Fix a bug with descriptor duplication (dup2() and dup3()). There is
an existing race condition with this POSIX API that currently results
in a kernel side crash.

The crash occurs when a partially open / closed file descriptor is
duplicated. The reason for the crash is that even if the descriptor is
closed, the file might still be in use by the kernel (due to e.g. ongoing
write to file). The open file data is changed by file_dup3() and this
causes a crash in the device / drivers themselves as they lose access to
the inode and private data.

The fix is done by separating struct file into file and file descriptor
structs. The file struct can live on even if the descriptor is closed,
fixing the crash. This also fixes the POSIX issue, as two descriptors
can now point to the same file.

Signed-off-by: Ville Juven <ville.juven@unikie.com>
Signed-off-by: dongjiuzhu1 <dongjiuzhu1@xiaomi.com>
This commit is contained in:
Ville Juven
2025-06-07 14:05:34 +08:00
committed by Xiang Xiao
parent a12d21e830
commit b8e30b54ec
29 changed files with 837 additions and 753 deletions
+5 -5
View File
@@ -1252,7 +1252,6 @@ static ssize_t proc_groupfd(FAR struct proc_file_s *procfile,
size_t buflen, off_t offset)
{
FAR struct task_group_s *group = tcb->group;
FAR struct file *filep;
FAR char *path;
size_t remaining;
size_t linesize;
@@ -1263,7 +1262,7 @@ static ssize_t proc_groupfd(FAR struct proc_file_s *procfile,
DEBUGASSERT(group != NULL);
count = files_countlist(&group->tg_filelist);
count = fdlist_count(&group->tg_fdlist);
if (count == 0)
{
return 0;
@@ -1303,11 +1302,12 @@ static ssize_t proc_groupfd(FAR struct proc_file_s *procfile,
for (i = 0; i < count; i++)
{
filep = files_fget(&group->tg_filelist, i);
FAR struct file *filep;
FAR struct fd *fdp;
/* Is there an inode associated with the file descriptor? */
if (filep == NULL)
if (fdlist_get2(&group->tg_fdlist, i, &filep, &fdp) < 0)
{
continue;
}
@@ -1327,7 +1327,7 @@ static ssize_t proc_groupfd(FAR struct proc_file_s *procfile,
#if CONFIG_FS_BACKTRACE > 0
linesize += backtrace_format(procfile->line + linesize,
STATUS_LINELEN - linesize,
filep->f_backtrace,
fdp->f_backtrace,
CONFIG_FS_BACKTRACE);
#endif
procfile->line[linesize - 2] = '\n';