nuttx/fs/inode/fs_inodereserve.c
Megha Rajput c5619cb3fe fs/inode: propagate inode search errors
inode_reserve() previously continued processing all negative return
values from inode_search(). Only -ENOENT indicates that the target
inode is absent and creation may continue.

Propagate other search errors through the existing cleanup path to
avoid continuing inode creation with invalid insertion metadata.
Assisted-by: GitHub Copilot
Signed-off-by: Megha Rajput <i.meghar.2408@gmail.com>
2026-09-04 13:50:54 -03:00

296 lines
7.8 KiB
C

/****************************************************************************
* fs/inode/fs_inodereserve.c
*
* SPDX-License-Identifier: Apache-2.0
*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership. The
* ASF licenses this file to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance with the
* License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations
* under the License.
*
****************************************************************************/
/****************************************************************************
* Included Files
****************************************************************************/
#include <nuttx/config.h>
#include <assert.h>
#include <errno.h>
#include <nuttx/kmalloc.h>
#include <nuttx/sched.h>
#include <nuttx/fs/fs.h>
#include "inode/inode.h"
#include "fs_heap.h"
/****************************************************************************
* Private Data
****************************************************************************/
static ino_t g_ino;
/****************************************************************************
* Private Functions
****************************************************************************/
/****************************************************************************
* Name: inode_namelen
****************************************************************************/
static int inode_namelen(FAR const char *name)
{
FAR const char *tmp = name;
while (*tmp && *tmp != '/')
{
tmp++;
}
return tmp - name;
}
/****************************************************************************
* Name: inode_namecpy
****************************************************************************/
static void inode_namecpy(FAR char *dest, FAR const char *src)
{
while (*src && *src != '/')
{
*dest++ = *src++;
}
*dest = '\0';
}
/****************************************************************************
* Name: inode_alloc
****************************************************************************/
static FAR struct inode *inode_alloc(FAR const char *name, mode_t mode)
{
FAR struct inode *inode;
int namelen;
#if defined(CONFIG_PSEUDOFS_ATTRIBUTES) && defined(CONFIG_SCHED_USER_IDENTITY)
FAR struct tcb_s *rtcb;
#endif
namelen = inode_namelen(name);
inode = fs_heap_zalloc(FSNODE_SIZE(namelen));
if (inode)
{
inode->i_ino = g_ino++;
atomic_set(&inode->i_crefs, 1);
#ifdef CONFIG_PSEUDOFS_ATTRIBUTES
inode->i_mode = mode;
clock_gettime(CLOCK_REALTIME, &inode->i_atime);
inode->i_mtime = inode->i_atime;
inode->i_ctime = inode->i_atime;
# if defined(CONFIG_SCHED_USER_IDENTITY)
rtcb = nxsched_self();
if (rtcb != NULL && rtcb->group != NULL)
{
inode->i_owner = rtcb->group->tg_euid;
inode->i_group = rtcb->group->tg_egid;
}
# endif
#endif
inode_namecpy(inode->i_name, name);
}
return inode;
}
/****************************************************************************
* Name: inode_insert
****************************************************************************/
static void inode_insert(FAR struct inode *inode,
FAR struct inode *peer,
FAR struct inode *parent)
{
/* If peer is non-null, then new node simply goes to the right
* of that peer node.
*/
if (peer)
{
inode->i_peer = peer->i_peer;
inode->i_parent = parent;
peer->i_peer = inode;
}
/* Then it must go at the head of parent's list of children. */
else
{
DEBUGASSERT(parent != NULL);
inode->i_peer = parent->i_child;
inode->i_parent = parent;
parent->i_child = inode;
}
}
/****************************************************************************
* Public Functions
****************************************************************************/
/****************************************************************************
* Name: inode_root_reserve
*
* Description:
* Reserve the root inode for the pseudo file system.
*
****************************************************************************/
void inode_root_reserve(void)
{
g_root_inode = inode_alloc("", 0777);
}
/****************************************************************************
* Name: inode_reserve
*
* Description:
* Reserve an (initialized) inode the pseudo file system. The initial
* reference count on the new inode is zero.
*
* Input Parameters:
* path - The path to the inode to create
* mode - inmode privileges
* inode - The location to return the inode pointer
*
* Returned Value:
* Zero on success (with the inode point in 'inode'); A negated errno
* value is returned on failure:
*
* EINVAL - 'path' is invalid for this operation
* EEXIST - An inode already exists at 'path'
* ENOMEM - Failed to allocate in-memory resources for the operation
*
* Assumptions:
* Caller must hold the inode semaphore
*
****************************************************************************/
int inode_reserve(FAR const char *path,
mode_t mode, FAR struct inode **inode)
{
struct inode_search_s desc;
FAR struct inode *left;
FAR struct inode *parent;
FAR const char *name;
int ret;
/* Assume failure */
DEBUGASSERT(path != NULL && inode != NULL);
*inode = NULL;
if (path[0] == '\0')
{
return -EINVAL;
}
/* Find the location to insert the new subtree */
SETUP_SEARCH(&desc, path, false);
ret = inode_search(&desc);
if (ret != -ENOENT)
{
/* It is an error if the node already exists in the tree (or if it
* lies within a mountpoint, we don't distinguish here).
*/
if (ret >= 0)
{
ret = -EEXIST;
}
goto errout_with_search;
}
/* Now we now where to insert the subtree */
name = desc.path;
left = desc.peer;
parent = desc.parent;
if (parent != NULL)
{
/* Traverse ancestors (X_OK), then require write on the parent.
* Caller holds the inode tree lock.
*/
ret = inode_checkpathperm(parent, W_OK, INODE_CHECK_LOCKED);
if (ret < 0)
{
goto errout_with_search;
}
}
for (; ; )
{
FAR struct inode *node;
/* Create a new node -- we need to know if this is the
* the leaf node or some intermediary. We can find this
* by looking at the next name.
*/
FAR const char *nextname = inode_nextname(name);
if (*nextname != '\0')
{
/* Insert an operationless node */
node = inode_alloc(name, 0777);
if (node != NULL)
{
inode_insert(node, left, parent);
/* Set up for the next time through the loop */
name = nextname;
left = NULL;
parent = node;
continue;
}
}
else
{
node = inode_alloc(name, mode);
if (node != NULL)
{
inode_insert(node, left, parent);
*inode = node;
ret = OK;
break;
}
}
/* We get here on failures to allocate node memory */
ret = -ENOMEM;
break;
}
errout_with_search:
RELEASE_SEARCH(&desc);
return ret;
}