/**************************************************************************** * 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 #include #include #include #include #include #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 * , 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; }