incubator-nuttx/fs/vfs/fs_chstat.c

475 lines
13 KiB
C

/****************************************************************************
* fs/vfs/fs_chstat.c
*
* 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 "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);
/* 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)
{
DEBUGASSERT(inode != NULL && buf != NULL);
#ifdef CONFIG_PSEUDOFS_SOFTLINKS
/* 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);
}
}
#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;
}