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