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))
