nuttx/fs/vfs/fs_chstat.c
zhengyu16 ebfe22bfb9 fs: rename PSEUDOFS_SOFTLINKS to FS_LINKS
The link support is no longer limited to the pseudo file system and now
covers both soft (symbolic) links and hard links across the VFS.  Rename
the configuration option PSEUDOFS_SOFTLINKS to the more accurate FS_LINKS
and update all references in the source, headers, Kconfig, documentation
and board defconfigs accordingly.

This is a configuration rename; any out-of-tree defconfig that still
selects PSEUDOFS_SOFTLINKS must be updated to FS_LINKS.

Signed-off-by: zhengyu16 <zhengyu16@xiaomi.com>
2026-08-27 01:12:33 +08:00

521 lines
14 KiB
C

/****************************************************************************
* fs/vfs/fs_chstat.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 <sys/stat.h>
#include <unistd.h>
#include <assert.h>
#include <errno.h>
#include <nuttx/fs/fs.h>
#include <nuttx/sched.h>
#include "inode/inode.h"
/****************************************************************************
* Private Functions
****************************************************************************/
/****************************************************************************
* Name: chstat_recursive
****************************************************************************/
static int chstat_recursive(FAR const char *path,
FAR const struct stat *buf,
int flags, int resolve)
{
struct inode_search_s desc;
FAR struct inode *inode;
int ret;
/* Get an inode for this path */
SETUP_SEARCH(&desc, path, true);
ret = inode_find(&desc);
if (ret < 0)
{
/* This name does not refer to an inode in the pseudo file system and
* there is no mountpoint that includes in this path.
*/
goto errout_with_search;
}
/* Get the search results */
inode = desc.node;
DEBUGASSERT(inode != NULL);
ret = inode_checkpathperm(inode, 0, 0);
if (ret < 0)
{
inode_release(inode);
goto errout_with_search;
}
/* The way we handle the chstat depends on the type of inode that we
* are dealing with.
*/
#ifndef CONFIG_DISABLE_MOUNTPOINT
if (INODE_IS_MOUNTPT(inode))
{
/* The node is a file system mointpoint. Verify that the mountpoint
* supports the chstat() method
*/
if (inode->u.i_mops && inode->u.i_mops->chstat)
{
/* Perform the chstat() operation */
ret = inode->u.i_mops->chstat(inode, desc.relpath, buf, flags);
}
else
{
ret = -ENOSYS;
}
}
else
#endif
{
/* The node is part of the root pseudo file system. This path may
* recurse if soft links are supported in the pseudo file system.
*/
ret = inode_chstat(inode, buf, flags, resolve);
}
inode_release(inode);
errout_with_search:
RELEASE_SEARCH(&desc);
return ret;
}
/****************************************************************************
* Name: fchstat
****************************************************************************/
static int chstat(FAR const char *path,
FAR struct stat *buf, int flags, int resolve)
{
int ret = -EINVAL;
/* Adjust and check buf and flags */
if ((flags & CH_STAT_UID) && buf->st_uid == -1)
{
flags &= ~CH_STAT_UID;
}
if ((flags & CH_STAT_GID) && buf->st_gid == -1)
{
flags &= ~CH_STAT_GID;
}
clock_gettime(CLOCK_REALTIME, &buf->st_ctim);
if (flags & CH_STAT_ATIME)
{
if (buf->st_atim.tv_nsec == UTIME_OMIT)
{
flags &= ~CH_STAT_ATIME;
}
else if (buf->st_atim.tv_nsec == UTIME_NOW)
{
buf->st_atim = buf->st_ctim;
}
else if (buf->st_atim.tv_nsec >= 1000000000)
{
goto errout;
}
}
if (flags & CH_STAT_MTIME)
{
if (buf->st_mtim.tv_nsec == UTIME_OMIT)
{
flags &= ~CH_STAT_MTIME;
}
else if (buf->st_mtim.tv_nsec == UTIME_NOW)
{
buf->st_mtim = buf->st_ctim;
}
else if (buf->st_mtim.tv_nsec >= 1000000000)
{
goto errout;
}
}
/* Perform the chstat operation */
ret = chstat_recursive(path, buf, flags, resolve);
if (ret >= 0)
{
/* Successfully chstat'ed the file */
return OK;
}
errout:
set_errno(-ret);
return ERROR;
}
/****************************************************************************
* Public Functions
****************************************************************************/
/****************************************************************************
* Name: chmod
*
* Description:
* The chmod() function changes S_ISUID, S_ISGID, S_ISVTX and the file
* permission bits of the file named by the pathname pointed to by the
* path argument to the corresponding bits in the mode argument. The
* effective user ID of the process must match the owner of the file or
* the process must have appropriate privileges in order to do this.
*
* Input Parameters:
* path - Specifies the file to be modified
* mode - Specifies the permission to set
*
* Returned Value:
* Upon successful completion, chmod() shall return 0.
* Otherwise, it shall return -1 and set errno to indicate the error.
*
****************************************************************************/
int chmod(FAR const char *path, mode_t mode)
{
struct stat buf;
buf.st_mode = mode & 0777;
return chstat(path, &buf, CH_STAT_MODE, 1);
}
/****************************************************************************
* Name: lchmod
*
* Description:
* The lchmod() system call is similar to chmod() but does not follow
* the symbolic links.
*
* Input Parameters:
* path - Specifies the file to be modified
* mode - Specifies the permission to set
*
* Returned Value:
* Upon successful completion, lchmod() shall return 0.
* Otherwise, it shall return -1 and set errno to indicate the error.
*
****************************************************************************/
int lchmod(FAR const char *path, mode_t mode)
{
struct stat buf;
buf.st_mode = mode & 0777;
return chstat(path, &buf, CH_STAT_MODE, 0);
}
/****************************************************************************
* Name: chown
*
* Description:
* The chown() function shall change the user and group ownership of a
* file. Only processes with an effective user ID equal to the user ID
* of the file or with appropriate privileges may change the ownership
* of a file.
*
* Input Parameters:
* path - Specifies the file to be modified
* owner - Specifies the owner to set
* group - Specifies the group to set
*
* Returned Value:
* Upon successful completion, chown() shall return 0.
* Otherwise, it shall return -1 and set errno to indicate the error.
*
****************************************************************************/
int chown(FAR const char *path, uid_t owner, gid_t group)
{
struct stat buf;
buf.st_uid = owner;
buf.st_gid = group;
return chstat(path, &buf, CH_STAT_UID | CH_STAT_GID, 1);
}
/****************************************************************************
* Name: lchown
*
* Description:
* The lchown() system call is similar to chown() but does not follow
* the symbolic links.
*
* Input Parameters:
* path - Specifies the file to be modified
* owner - Specifies the owner to set
* group - Specifies the group to set
*
* Returned Value:
* Upon successful completion, lchown() shall return 0.
* Otherwise, it shall return -1 and set errno to indicate the error.
*
****************************************************************************/
int lchown(FAR const char *path, uid_t owner, gid_t group)
{
struct stat buf;
buf.st_uid = owner;
buf.st_gid = group;
return chstat(path, &buf, CH_STAT_UID | CH_STAT_GID, 0);
}
/****************************************************************************
* Name: utimens
*
* Description:
* The utimens() function shall set the access and modification times of
* the file pointed to by the path argument to the value of the times
* argument. utimens() function allows time specifications accurate to
* the microsecond.
*
* For utimens(), the times argument is an array of timeval structures.
* The first array member represents the date and time of last access,
* and the second member represents the date and time of last
* modification. The times in the timeval structure are measured in
* seconds and microseconds since the Epoch, although rounding toward
* the nearest second may occur.
*
* If the times argument is a null pointer, the access and modification
* times of the file shall be set to the current time. The effective
* user ID of the process shall match the owner of the file, has write
* access to the file or appropriate privileges to use this call in this
* manner. Upon completion, utimens() shall mark the time of the last
* file status change, st_ctime, for update.
*
* Input Parameters:
* path - Specifies the file to be modified
* times - Specifies the time value to set
*
* Returned Value:
* Upon successful completion, 0 shall be returned. Otherwise, -1 shall
* be returned and errno shall be set to indicate the error, and the file
* times shall not be affected.
*
****************************************************************************/
int utimens(FAR const char *path, const struct timespec times[2])
{
struct stat buf;
if (times != NULL)
{
buf.st_atim = times[0];
buf.st_mtim = times[1];
}
else
{
buf.st_atim.tv_nsec = UTIME_NOW;
buf.st_mtim.tv_nsec = UTIME_NOW;
}
return chstat(path, &buf, CH_STAT_ATIME | CH_STAT_MTIME, 1);
}
/****************************************************************************
* Name: lutimens
*
* Description:
* The lutimens() system call is similar to utimens() but does not follow
* the symbolic links.
*
* Input Parameters:
* path - Specifies the file to be modified
* times - Specifies the time value to set
*
* Returned Value:
* Upon successful completion, 0 shall be returned. Otherwise, -1 shall
* be returned and errno shall be set to indicate the error, and the file
* times shall not be affected.
*
****************************************************************************/
int lutimens(FAR const char *path, const struct timespec times[2])
{
struct stat buf;
if (times != NULL)
{
buf.st_atim = times[0];
buf.st_mtim = times[1];
}
else
{
buf.st_atim.tv_nsec = UTIME_NOW;
buf.st_mtim.tv_nsec = UTIME_NOW;
}
return chstat(path, &buf, CH_STAT_ATIME | CH_STAT_MTIME, 0);
}
/****************************************************************************
* Name: inode_chstat
*
* Description:
* The inode_chstat() function will change information about an 'inode'
* in the pseudo file system according the area pointed to by 'buf'.
*
* The 'buf' argument is a pointer to a stat structure, as defined in
* <sys/stat.h>, which information is placed concerning the file.
*
* Input Parameters:
* inode - The inode of interest
* buf - The caller provide location in which to apply information
* about the inode.
* flags - The valid field in buf
* resolve - Whether to resolve the symbolic link
*
* Returned Value:
* Zero (OK) returned on success. Otherwise, a negated errno value is
* returned to indicate the nature of the failure.
*
****************************************************************************/
int inode_chstat(FAR struct inode *inode,
FAR const struct stat *buf, int flags, int resolve)
{
#ifdef CONFIG_SCHED_USER_IDENTITY
FAR struct tcb_s *rtcb;
uid_t euid;
#endif
DEBUGASSERT(inode != NULL && buf != NULL);
#ifdef CONFIG_FS_LINKS
/* Handle softlinks differently. Just call chstat() recursively on the
* target of the softlink.
*/
if (INODE_IS_SOFTLINK(inode))
{
if (resolve)
{
/* Increment the link counter. This is necessary to avoid
* infinite recursion if loops are encountered in the
* traversal. If we encounter more SYMLOOP_MAX symbolic links
* at any time during the traversal, error out.
*
* NOTE: That inode_search() will automatically skip over
* consecutive, intermediate symbolic links. Those numbers
* will not be included in the total.
*/
if (resolve > SYMLOOP_MAX)
{
return -ELOOP;
}
/* chstat() the target of the soft link. */
return chstat_recursive(inode->u.i_link, buf, flags, ++resolve);
}
}
else if (INODE_IS_HARDLINK(inode))
{
/* The inode is a hard link. The actual inode is referenced
* by the i_private field.
*/
DEBUGASSERT(inode->i_private != NULL);
inode = inode->i_private;
}
#endif
#ifdef CONFIG_SCHED_USER_IDENTITY
rtcb = nxsched_self();
if ((rtcb->flags & TCB_FLAG_TTYPE_MASK) != TCB_FLAG_TTYPE_KERNEL &&
rtcb->group != NULL)
{
euid = rtcb->group->tg_euid;
if ((flags & (CH_STAT_UID | CH_STAT_GID)) != 0 && euid != 0)
{
return -EPERM;
}
#ifdef CONFIG_PSEUDOFS_ATTRIBUTES
if ((flags & CH_STAT_MODE) != 0 &&
euid != 0 && euid != inode->i_owner)
{
return -EPERM;
}
#endif
}
#endif
#ifdef CONFIG_PSEUDOFS_ATTRIBUTES
if (flags & CH_STAT_MODE)
{
inode->i_mode = buf->st_mode;
}
if (flags & CH_STAT_UID)
{
inode->i_owner = buf->st_uid;
}
if (flags & CH_STAT_GID)
{
inode->i_group = buf->st_gid;
}
if (flags & CH_STAT_ATIME)
{
inode->i_atime = buf->st_atim;
}
if (flags & CH_STAT_MTIME)
{
inode->i_mtime = buf->st_mtim;
}
inode->i_ctime = buf->st_ctim;
#endif
return OK;
}