This is an automated email from the ASF dual-hosted git repository.

xiaoxiang781216 pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/nuttx.git

commit c68dfe8a93f7999622779882bf02633dd888a4d5
Author: Marco Casaroli <[email protected]>
AuthorDate: Sat Oct 24 01:29:16 2026 +0200

    fs/vfs: Fail an open of a directory for writing or with O_TMPFILE.
    
    open() with O_DIRECTORY opened the directory whatever the access mode.
    O_TMPFILE includes O_DIRECTORY, so open(dir, O_RDWR | O_TMPFILE) gave a
    directory descriptor instead of an unnamed file: open() returned success
    and every write() to it failed.  A program that uses mkstemp() only when
    O_TMPFILE fails then could not write its temporary file.
    
    Now an O_DIRECTORY open with write access does not open the directory.
    It checks the type of the path (the stat method of a mounted file system,
    the inode type in the pseudo file system) and fails: with ENOTDIR for a
    path that is not a directory, else with EOPNOTSUPP for O_TMPFILE, as
    Linux does for a file system without it (no file system makes an unnamed
    file), else with EISDIR, as POSIX specifies for a directory opened with
    O_WRONLY or O_RDWR.
    
    Assisted-by: Claude Code:claude-opus-5-5
    Signed-off-by: Marco Casaroli <[email protected]>
---
 fs/vfs/fs_open.c | 65 +++++++++++++++++++++++++++++++++++++++++++++++++++++++-
 1 file changed, 64 insertions(+), 1 deletion(-)

diff --git a/fs/vfs/fs_open.c b/fs/vfs/fs_open.c
index 7218785955b..c671afe8e79 100644
--- a/fs/vfs/fs_open.c
+++ b/fs/vfs/fs_open.c
@@ -48,6 +48,47 @@
  * Private Functions
  ****************************************************************************/
 
+/****************************************************************************
+ * Name: file_checkdir
+ *
+ * Description:
+ *   Check that the path of an open with O_DIRECTORY is a directory, without
+ *   opening the directory.
+ *
+ * Returned Value:
+ *   Zero (OK) if the path is a directory, -ENOTDIR if it is not, or the
+ *   negated errno value of the stat method of the file system.
+ *
+ ****************************************************************************/
+
+static int file_checkdir(FAR struct inode *inode, FAR const char *relpath)
+{
+#ifndef CONFIG_DISABLE_MOUNTPOINT
+  if (INODE_IS_MOUNTPT(inode))
+    {
+      struct stat buf;
+      int ret;
+
+      if (inode->u.i_mops == NULL || inode->u.i_mops->stat == NULL)
+        {
+          return -ENOSYS;
+        }
+
+      ret = inode->u.i_mops->stat(inode, relpath, &buf);
+      if (ret < 0)
+        {
+          return ret;
+        }
+
+      return S_ISDIR(buf.st_mode) ? OK : -ENOTDIR;
+    }
+#else
+  UNUSED(relpath);
+#endif
+
+  return INODE_IS_PSEUDODIR(inode) ? OK : -ENOTDIR;
+}
+
 /****************************************************************************
  * Name: file_vopen
  *
@@ -239,7 +280,29 @@ static int file_vopen(FAR struct file *filep, FAR const 
char *path,
 
   if (oflags & O_DIRECTORY)
     {
-      ret = dir_allocate(filep, desc.relpath);
+      if ((oflags & O_ACCMODE) == O_RDONLY)
+        {
+          ret = dir_allocate(filep, desc.relpath);
+        }
+      else
+        {
+          /* A directory cannot be opened for writing.  O_TMPFILE (it
+           * includes O_DIRECTORY) asks for an unnamed regular file in the
+           * directory.  No file system makes one: fail as Linux does for a
+           * file system without it, so that the caller can use mkstemp()
+           * and unlink().
+           */
+
+          ret = file_checkdir(inode, desc.relpath);
+          if (ret >= 0)
+            {
+#ifdef __O_TMPFILE
+              ret = (oflags & __O_TMPFILE) != 0 ? -EOPNOTSUPP : -EISDIR;
+#else
+              ret = -EISDIR;
+#endif
+            }
+        }
     }
 #ifndef CONFIG_DISABLE_MOUNTPOINT
   else if (INODE_IS_MOUNTPT(inode))

Reply via email to