480 lines
13 KiB
C
480 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_MODE) && (buf->st_mode & ~0177777))
|
|
{
|
|
goto errout;
|
|
}
|
|
|
|
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;
|
|
|
|
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;
|
|
|
|
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;
|
|
}
|