diff --git a/fs/binfs/fs_binfs.c b/fs/binfs/fs_binfs.c index 5c44347c4e..eaa49b2e6f 100644 --- a/fs/binfs/fs_binfs.c +++ b/fs/binfs/fs_binfs.c @@ -98,6 +98,7 @@ const struct mountpt_operations binfs_operations = NULL, /* sync */ binfs_dup, /* dup */ binfs_fstat, /* fstat */ + NULL, /* fchstat */ NULL, /* truncate */ binfs_opendir, /* opendir */ @@ -113,7 +114,8 @@ const struct mountpt_operations binfs_operations = NULL, /* mkdir */ NULL, /* rmdir */ NULL, /* rename */ - binfs_stat /* stat */ + binfs_stat, /* stat */ + NULL /* chstat */ }; /**************************************************************************** diff --git a/fs/cromfs/fs_cromfs.c b/fs/cromfs/fs_cromfs.c index 644d7823dc..fc9a3f6a00 100644 --- a/fs/cromfs/fs_cromfs.c +++ b/fs/cromfs/fs_cromfs.c @@ -179,6 +179,7 @@ const struct mountpt_operations cromfs_operations = NULL, /* sync */ cromfs_dup, /* dup */ cromfs_fstat, /* fstat */ + NULL, /* fchstat */ NULL, /* truncate */ cromfs_opendir, /* opendir */ @@ -194,7 +195,8 @@ const struct mountpt_operations cromfs_operations = NULL, /* mkdir */ NULL, /* rmdir */ NULL, /* rename */ - cromfs_stat /* stat */ + cromfs_stat, /* stat */ + NULL /* chstat */ }; /* The CROMFS uses a global, in-memory instance of the file system image diff --git a/fs/fat/fs_fat32.c b/fs/fat/fs_fat32.c index 30cbd34bf9..7177d75fa1 100644 --- a/fs/fat/fs_fat32.c +++ b/fs/fat/fs_fat32.c @@ -117,6 +117,7 @@ const struct mountpt_operations fat_operations = fat_sync, /* sync */ fat_dup, /* dup */ fat_fstat, /* fstat */ + NULL, /* fchstat */ fat_truncate, /* truncate */ fat_opendir, /* opendir */ @@ -132,7 +133,8 @@ const struct mountpt_operations fat_operations = fat_mkdir, /* mkdir */ fat_rmdir, /* rmdir */ fat_rename, /* rename */ - fat_stat /* stat */ + fat_stat, /* stat */ + NULL /* chstat */ }; /**************************************************************************** diff --git a/fs/hostfs/hostfs.c b/fs/hostfs/hostfs.c index 4a5b0429b7..2b117598d8 100644 --- a/fs/hostfs/hostfs.c +++ b/fs/hostfs/hostfs.c @@ -131,6 +131,7 @@ const struct mountpt_operations hostfs_operations = hostfs_sync, /* sync */ hostfs_dup, /* dup */ hostfs_fstat, /* fstat */ + NULL, /* fchstat */ hostfs_ftruncate, /* ftruncate */ hostfs_opendir, /* opendir */ @@ -146,7 +147,8 @@ const struct mountpt_operations hostfs_operations = hostfs_mkdir, /* mkdir */ hostfs_rmdir, /* rmdir */ hostfs_rename, /* rename */ - hostfs_stat /* stat */ + hostfs_stat, /* stat */ + NULL /* chstat */ }; /**************************************************************************** diff --git a/fs/inode/inode.h b/fs/inode/inode.h index fc6a0a398e..f9793acd89 100644 --- a/fs/inode/inode.h +++ b/fs/inode/inode.h @@ -242,9 +242,34 @@ int inode_find(FAR struct inode_search_s *desc); * ****************************************************************************/ -struct stat; /* Forward reference */ int inode_stat(FAR struct inode *inode, FAR struct stat *buf, int resolve); +/**************************************************************************** + * 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 vaild 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); + /**************************************************************************** * Name: inode_getpath * diff --git a/fs/littlefs/lfs_vfs.c b/fs/littlefs/lfs_vfs.c index 9cfb15288b..eb77da0ff2 100644 --- a/fs/littlefs/lfs_vfs.c +++ b/fs/littlefs/lfs_vfs.c @@ -141,6 +141,7 @@ const struct mountpt_operations littlefs_operations = littlefs_sync, /* sync */ littlefs_dup, /* dup */ littlefs_fstat, /* fstat */ + NULL, /* fchstat */ littlefs_truncate, /* truncate */ littlefs_opendir, /* opendir */ @@ -156,7 +157,8 @@ const struct mountpt_operations littlefs_operations = littlefs_mkdir, /* mkdir */ littlefs_rmdir, /* rmdir */ littlefs_rename, /* rename */ - littlefs_stat /* stat */ + littlefs_stat, /* stat */ + NULL /* chstat */ }; /**************************************************************************** diff --git a/fs/nfs/nfs_vfsops.c b/fs/nfs/nfs_vfsops.c index 9065a2327c..ca3868eee0 100644 --- a/fs/nfs/nfs_vfsops.c +++ b/fs/nfs/nfs_vfsops.c @@ -180,6 +180,7 @@ const struct mountpt_operations nfs_operations = NULL, /* sync */ nfs_dup, /* dup */ nfs_fstat, /* fstat */ + NULL, /* fchstat */ nfs_truncate, /* truncate */ nfs_opendir, /* opendir */ @@ -195,7 +196,8 @@ const struct mountpt_operations nfs_operations = nfs_mkdir, /* mkdir */ nfs_rmdir, /* rmdir */ nfs_rename, /* rename */ - nfs_stat /* stat */ + nfs_stat, /* stat */ + NULL /* chstat */ }; /**************************************************************************** diff --git a/fs/nxffs/nxffs_initialize.c b/fs/nxffs/nxffs_initialize.c index a2b803b29e..833b839c41 100644 --- a/fs/nxffs/nxffs_initialize.c +++ b/fs/nxffs/nxffs_initialize.c @@ -58,6 +58,7 @@ const struct mountpt_operations nxffs_operations = NULL, /* sync -- No buffered data */ nxffs_dup, /* dup */ nxffs_fstat, /* fstat */ + NULL, /* fchstat */ #ifdef __NO_TRUNCATE_SUPPORT__ nxffs_truncate, /* truncate */ #else @@ -77,7 +78,8 @@ const struct mountpt_operations nxffs_operations = NULL, /* mkdir -- no directories */ NULL, /* rmdir -- no directories */ NULL, /* rename -- cannot rename in place if name is longer */ - nxffs_stat /* stat */ + nxffs_stat, /* stat */ + NULL /* chstat */ }; /**************************************************************************** diff --git a/fs/procfs/fs_procfs.c b/fs/procfs/fs_procfs.c index 6a067d5c78..3c5dace4fd 100644 --- a/fs/procfs/fs_procfs.c +++ b/fs/procfs/fs_procfs.c @@ -249,6 +249,7 @@ const struct mountpt_operations procfs_operations = NULL, /* sync */ procfs_dup, /* dup */ procfs_fstat, /* fstat */ + NULL, /* fchstat */ NULL, /* truncate */ procfs_opendir, /* opendir */ @@ -264,7 +265,8 @@ const struct mountpt_operations procfs_operations = NULL, /* mkdir */ NULL, /* rmdir */ NULL, /* rename */ - procfs_stat /* stat */ + procfs_stat, /* stat */ + NULL /* chstat */ }; /* Level 0 contains the directory of active tasks in addition to other diff --git a/fs/romfs/fs_romfs.c b/fs/romfs/fs_romfs.c index 8be82ee823..c9642f67e9 100644 --- a/fs/romfs/fs_romfs.c +++ b/fs/romfs/fs_romfs.c @@ -106,6 +106,7 @@ const struct mountpt_operations romfs_operations = NULL, /* sync */ romfs_dup, /* dup */ romfs_fstat, /* fstat */ + NULL, /* fchstat */ NULL, /* truncate */ romfs_opendir, /* opendir */ @@ -121,7 +122,8 @@ const struct mountpt_operations romfs_operations = NULL, /* mkdir */ NULL, /* rmdir */ NULL, /* rename */ - romfs_stat /* stat */ + romfs_stat, /* stat */ + NULL /* chstat */ }; /**************************************************************************** diff --git a/fs/smartfs/smartfs_smart.c b/fs/smartfs/smartfs_smart.c index 3ceec3e142..2b632483a1 100644 --- a/fs/smartfs/smartfs_smart.c +++ b/fs/smartfs/smartfs_smart.c @@ -130,6 +130,7 @@ const struct mountpt_operations smartfs_operations = smartfs_sync, /* sync */ smartfs_dup, /* dup */ smartfs_fstat, /* fstat */ + NULL, /* fchstat */ smartfs_truncate, /* truncate */ smartfs_opendir, /* opendir */ @@ -145,7 +146,8 @@ const struct mountpt_operations smartfs_operations = smartfs_mkdir, /* mkdir */ smartfs_rmdir, /* rmdir */ smartfs_rename, /* rename */ - smartfs_stat /* stat */ + smartfs_stat, /* stat */ + NULL /* chstat */ }; /**************************************************************************** diff --git a/fs/spiffs/src/spiffs_vfs.c b/fs/spiffs/src/spiffs_vfs.c index f19dd4c0ca..7c049b36b2 100644 --- a/fs/spiffs/src/spiffs_vfs.c +++ b/fs/spiffs/src/spiffs_vfs.c @@ -140,6 +140,7 @@ const struct mountpt_operations spiffs_operations = spiffs_sync, /* sync */ spiffs_dup, /* dup */ spiffs_fstat, /* fstat */ + NULL, /* fchstat */ spiffs_truncate, /* truncate */ spiffs_opendir, /* opendir */ @@ -156,6 +157,7 @@ const struct mountpt_operations spiffs_operations = spiffs_rmdir, /* rmdir */ spiffs_rename, /* rename */ spiffs_stat, /* stat */ + NULL /* chstat */ }; /**************************************************************************** diff --git a/fs/tmpfs/fs_tmpfs.c b/fs/tmpfs/fs_tmpfs.c index b571e8432f..3610867946 100644 --- a/fs/tmpfs/fs_tmpfs.c +++ b/fs/tmpfs/fs_tmpfs.c @@ -168,6 +168,7 @@ const struct mountpt_operations tmpfs_operations = NULL, /* sync */ tmpfs_dup, /* dup */ tmpfs_fstat, /* fstat */ + NULL, /* fchstat */ tmpfs_truncate, /* truncate */ tmpfs_opendir, /* opendir */ @@ -184,6 +185,7 @@ const struct mountpt_operations tmpfs_operations = tmpfs_rmdir, /* rmdir */ tmpfs_rename, /* rename */ tmpfs_stat, /* stat */ + NULL /* chstat */ }; /**************************************************************************** diff --git a/fs/unionfs/fs_unionfs.c b/fs/unionfs/fs_unionfs.c index 9ead2b1d6f..0b50b4fd1a 100644 --- a/fs/unionfs/fs_unionfs.c +++ b/fs/unionfs/fs_unionfs.c @@ -211,6 +211,7 @@ const struct mountpt_operations unionfs_operations = unionfs_sync, /* sync */ unionfs_dup, /* dup */ unionfs_fstat, /* fstat */ + NULL, /* fchstat */ unionfs_truncate, /* truncate */ unionfs_opendir, /* opendir */ @@ -226,7 +227,8 @@ const struct mountpt_operations unionfs_operations = unionfs_mkdir, /* mkdir */ unionfs_rmdir, /* rmdir */ unionfs_rename, /* rename */ - unionfs_stat /* stat */ + unionfs_stat, /* stat */ + NULL /* chstat */ }; /**************************************************************************** diff --git a/fs/userfs/fs_userfs.c b/fs/userfs/fs_userfs.c index 0ec07fdf2f..abf620443d 100644 --- a/fs/userfs/fs_userfs.c +++ b/fs/userfs/fs_userfs.c @@ -153,6 +153,7 @@ const struct mountpt_operations userfs_operations = userfs_sync, /* sync */ userfs_dup, /* dup */ userfs_fstat, /* fstat */ + NULL, /* fchstat */ userfs_truncate, /* truncate */ userfs_opendir, /* opendir */ @@ -168,7 +169,8 @@ const struct mountpt_operations userfs_operations = userfs_mkdir, /* mkdir */ userfs_rmdir, /* rmdir */ userfs_rename, /* rename */ - userfs_stat /* stat */ + userfs_stat, /* stat */ + NULL /* chstat */ }; /**************************************************************************** diff --git a/fs/vfs/Make.defs b/fs/vfs/Make.defs index daacda4603..fe588c1491 100644 --- a/fs/vfs/Make.defs +++ b/fs/vfs/Make.defs @@ -20,10 +20,11 @@ # Common file/socket descriptor support -CSRCS += fs_close.c fs_dup.c fs_dup2.c fs_fcntl.c fs_epoll.c fs_fstat.c -CSRCS += fs_fstatfs.c fs_ioctl.c fs_lseek.c fs_mkdir.c fs_open.c fs_poll.c -CSRCS += fs_pread.c fs_pwrite.c fs_read.c fs_rename.c fs_rmdir.c fs_select.c -CSRCS += fs_sendfile.c fs_stat.c fs_statfs.c fs_unlink.c fs_write.c +CSRCS += fs_chstat.c fs_close.c fs_dup.c fs_dup2.c fs_fcntl.c fs_epoll.c +CSRCS += fs_fchstat.c fs_fstat.c fs_fstatfs.c fs_ioctl.c fs_lseek.c +CSRCS += fs_mkdir.c fs_open.c fs_poll.c fs_pread.c fs_pwrite.c fs_read.c +CSRCS += fs_rename.c fs_rmdir.c fs_select.c fs_sendfile.c fs_stat.c +CSRCS += fs_statfs.c fs_unlink.c fs_write.c # Certain interfaces are not available if there is no mountpoint support diff --git a/fs/vfs/fs_chstat.c b/fs/vfs/fs_chstat.c new file mode 100644 index 0000000000..dc34591251 --- /dev/null +++ b/fs/vfs/fs_chstat.c @@ -0,0 +1,480 @@ +/**************************************************************************** + * 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 + +#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 & ~07777)) + { + 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: utimes + * + * Description: + * The utimes() function shall set the access and modification times of + * the file pointed to by the path argument to the value of the times + * argument. utimes() function allows time specifications accurate to + * the microsecond. + * + * For utimes(), 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, utimes() 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 utimes(FAR const char *path, const struct timeval times[2]) +{ + struct stat buf; + + if (times != NULL) + { + TIMEVAL_TO_TIMESPEC(×[0], &buf.st_atim); + TIMEVAL_TO_TIMESPEC(×[1], &buf.st_mtim); + } + 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: lutimes + * + * Description: + * The lutimes() system call is similar to utimes() 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 lutimes(FAR const char *path, const struct timeval times[2]) +{ + struct stat buf; + + if (times != NULL) + { + TIMEVAL_TO_TIMESPEC(×[0], &buf.st_atim); + TIMEVAL_TO_TIMESPEC(×[1], &buf.st_mtim); + } + 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 vaild 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; +} diff --git a/fs/vfs/fs_fchstat.c b/fs/vfs/fs_fchstat.c new file mode 100644 index 0000000000..df9a6b87d1 --- /dev/null +++ b/fs/vfs/fs_fchstat.c @@ -0,0 +1,287 @@ +/**************************************************************************** + * fs/vfs/fs_fchstat.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: fchstat + ****************************************************************************/ + +static int fchstat(int fd, FAR struct stat *buf, int flags) +{ + FAR struct file *filep; + int ret; + + /* First, get the file structure. Note that on failure, + * fs_getfilep() will return the errno. + */ + + ret = fs_getfilep(fd, &filep); + if (ret < 0) + { + goto errout; + } + + /* Perform the fchstat operation */ + + ret = file_fchstat(filep, buf, flags); + if (ret >= 0) + { + /* Successfully fchstat'ed the file */ + + return OK; + } + +errout: + set_errno(-ret); + return ERROR; +} + +/**************************************************************************** + * Public Functions + ****************************************************************************/ + +/**************************************************************************** + * Name: file_fchstat + * + * Description: + * file_fchstat() is an internal OS interface. It is functionally similar + * to the combination of fchmod/fchown/futimens standard interface except: + * + * - It does not modify the errno variable, + * - It is not a cancellation point, + * - It does not handle socket descriptors, and + * - It accepts a file structure instance instead of file descriptor. + * + * Input Parameters: + * filep - File structure instance + * buf - The stat to be modified + * flags - The vaild field in buf + * + * Returned Value: + * Upon successful completion, 0 shall be returned. Otherwise, the + * negative errno shall be returned to indicate the error. + * + ****************************************************************************/ + +int file_fchstat(FAR struct file *filep, FAR struct stat *buf, int flags) +{ + FAR struct inode *inode; + int ret; + + DEBUGASSERT(filep != NULL); + + /* Get the inode from the file structure */ + + inode = filep->f_inode; + DEBUGASSERT(inode != NULL); + + /* Adjust and check buf and flags */ + + if ((flags & CH_STAT_MODE) && (buf->st_mode & ~07777)) + { + return -EINVAL; + } + + 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) + { + return -EINVAL; + } + } + + 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) + { + return -EINVAL; + } + } + + /* 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 inode is a file system mountpoint. Verify that the mountpoint + * supports the fchstat() method + */ + + if (inode->u.i_mops && inode->u.i_mops->fchstat) + { + /* Perform the fchstat() operation */ + + ret = inode->u.i_mops->fchstat(filep, buf, flags); + } + else + { + ret = -ENOSYS; + } + } + else +#endif + { + /* The inode is part of the root pseudo file system. */ + + ret = inode_chstat(inode, buf, flags, 0); + } + + return ret; +} + +/**************************************************************************** + * Name: fchmod + * + * Description: + * The fchmod() function shall be equivalent to chmod() except that the + * file whose permissions are changed is specified by the file descriptor. + * + * Input Parameters: + * fd - Specifies the fd to be modified + * mode - Specifies the permission to set + * + * Returned Value: + * Upon successful completion, fchmod() shall return 0. + * Otherwise, it shall return -1 and set errno to indicate the error. + * + ****************************************************************************/ + +int fchmod(int fd, mode_t mode) +{ + struct stat buf; + + buf.st_mode = mode; + + return fchstat(fd, &buf, CH_STAT_MODE); +} + +/**************************************************************************** + * Name: fchown + * + * Description: + * The fchown() function shall be equivalent to chown() except that the + * file whose owner and group are changed is specified by the file + * descriptor. + * + * Input Parameters: + * fd - Specifies the fd to be modified + * owner - Specifies the owner to set + * group - Specifies the group to set + * + * Returned Value: + * Upon successful completion, fchown() shall return 0. + * Otherwise, it shall return -1 and set errno to indicate the error. + * + ****************************************************************************/ + +int fchown(int fd, uid_t owner, gid_t group) +{ + struct stat buf; + + buf.st_uid = owner; + buf.st_gid = group; + + return fchstat(fd, &buf, CH_STAT_UID | CH_STAT_GID); +} + +/**************************************************************************** + * Name: futimens + * + * Description: + * futimens() update the timestamps of a file with nanosecond precision. + * This contrasts with the historical utime(2) and utimes(2), which permit + * only second and microsecond precision, respectively, when setting file + * timestamps. + * + * Input Parameters: + * fd - Specifies the fd to be modified + * times - Specifies the time value to set + * + * Returned Value: + * On success, futimens() return 0. + * On error, -1 is returned and errno is set to indicate the error. + * + ****************************************************************************/ + +int futimens(int fd, FAR 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 fchstat(fd, &buf, CH_STAT_ATIME | CH_STAT_MTIME); +} diff --git a/include/nuttx/fs/fs.h b/include/nuttx/fs/fs.h index edff2656f4..77e4ef0675 100644 --- a/include/nuttx/fs/fs.h +++ b/include/nuttx/fs/fs.h @@ -154,6 +154,16 @@ #define DIRENT_SETPSEUDONODE(f) do (f) |= DIRENTFLAGS_PSEUDONODE; while (0) #define DIRENT_ISPSEUDONODE(f) (((f) & DIRENTFLAGS_PSEUDONODE) != 0) +/* The status change flags. + * These should be or-ed together to figure out what want to change. + */ + +#define CH_STAT_MODE (1 << 0) +#define CH_STAT_UID (1 << 1) +#define CH_STAT_GID (1 << 2) +#define CH_STAT_ATIME (1 << 3) +#define CH_STAT_MTIME (1 << 4) + /* nx_umount() is equivalent to nx_umount2() with flags = 0 */ #define umount(t) umount2(t,0) @@ -276,6 +286,8 @@ struct mountpt_operations int (*sync)(FAR struct file *filep); int (*dup)(FAR const struct file *oldp, FAR struct file *newp); int (*fstat)(FAR const struct file *filep, FAR struct stat *buf); + int (*fchstat)(FAR const struct file *filep, + FAR const struct stat *buf, int flags); int (*truncate)(FAR struct file *filep, off_t length); /* Directory operations */ @@ -307,10 +319,8 @@ struct mountpt_operations FAR const char *newrelpath); int (*stat)(FAR struct inode *mountpt, FAR const char *relpath, FAR struct stat *buf); - - /* NOTE: More operations will be needed here to support: disk usage - * stats file stat(), file attributes, file truncation, etc. - */ + int (*chstat)(FAR struct inode *mountpt, FAR const char *relpath, + FAR const struct stat *buf, int flags); }; #endif /* CONFIG_DISABLE_MOUNTPOINT */ @@ -1358,6 +1368,31 @@ int file_fstat(FAR struct file *filep, FAR struct stat *buf); int nx_stat(FAR const char *path, FAR struct stat *buf, int resolve); +/**************************************************************************** + * Name: file_fchstat + * + * Description: + * file_fchstat() is an internal OS interface. It is functionally similar + * to the combination of fchmod/fchown/futimens standard interface except: + * + * - It does not modify the errno variable, + * - It is not a cancellation point, + * - It does not handle socket descriptors, and + * - It accepts a file structure instance instead of file descriptor. + * + * Input Parameters: + * filep - File structure instance + * buf - The stat to be modified + * flags - The vaild field in buf + * + * Returned Value: + * Upon successful completion, 0 shall be returned. Otherwise, the + * negative errno shall be returned to indicate the error. + * + ****************************************************************************/ + +int file_fchstat(FAR struct file *filep, FAR struct stat *buf, int flags); + /**************************************************************************** * Name: nx_unlink * diff --git a/include/sys/stat.h b/include/sys/stat.h index b1e8bcce21..539188bb3f 100644 --- a/include/sys/stat.h +++ b/include/sys/stat.h @@ -107,6 +107,11 @@ #define S_TYPEISMQ(buf) S_ISMQ((buf)->st_mode) #define S_TYPEISSHM(buf) S_ISSHM((buf)->st_mode) +/* Special value for tv_nsec field of timespec */ + +#define UTIME_NOW ((1l << 30) - 1l) +#define UTIME_OMIT ((1l << 30) - 2l) + /* The following macros are required by POSIX to achieve backward * compatibility with earlier versions of struct stat. */ @@ -162,6 +167,7 @@ int stat(FAR const char *path, FAR struct stat *buf); int lstat(FAR const char *path, FAR struct stat *buf); int fstat(int fd, FAR struct stat *buf); int chmod(FAR const char *path, mode_t mode); +int lchmod(FAR const char *path, mode_t mode); int fchmod(int fd, mode_t mode); int futimens(int fd, const struct timespec times[2]); diff --git a/include/sys/syscall_lookup.h b/include/sys/syscall_lookup.h index 5d55d9f11a..42d781d3a9 100644 --- a/include/sys/syscall_lookup.h +++ b/include/sys/syscall_lookup.h @@ -248,6 +248,15 @@ SYSCALL_LOOKUP(statfs, 2) SYSCALL_LOOKUP(fstatfs, 2) SYSCALL_LOOKUP(telldir, 1) SYSCALL_LOOKUP(sendfile, 4) +SYSCALL_LOOKUP(chmod, 2) +SYSCALL_LOOKUP(lchmod, 2) +SYSCALL_LOOKUP(fchmod, 2) +SYSCALL_LOOKUP(chown, 3) +SYSCALL_LOOKUP(lchown, 3) +SYSCALL_LOOKUP(fchown, 3) +SYSCALL_LOOKUP(utimes, 2) +SYSCALL_LOOKUP(lutimes, 2) +SYSCALL_LOOKUP(futimens, 2) #if defined(CONFIG_FS_RAMMAP) SYSCALL_LOOKUP(munmap, 2) diff --git a/include/sys/time.h b/include/sys/time.h index a9f6c65f30..94fede2ecc 100644 --- a/include/sys/time.h +++ b/include/sys/time.h @@ -372,6 +372,7 @@ int setitimer(int which, FAR const struct itimerval *value, ****************************************************************************/ int utimes(FAR const char *path, const struct timeval times[2]); +int lutimes(FAR const char *path, const struct timeval times[2]); /**************************************************************************** * Name: futimes diff --git a/include/unistd.h b/include/unistd.h index 9095453da0..dacc57e6b8 100644 --- a/include/unistd.h +++ b/include/unistd.h @@ -317,6 +317,7 @@ ssize_t write(int fd, FAR const void *buf, size_t nbytes); ssize_t pread(int fd, FAR void *buf, size_t nbytes, off_t offset); ssize_t pwrite(int fd, FAR const void *buf, size_t nbytes, off_t offset); int ftruncate(int fd, off_t length); +int fchown(int fd, uid_t owner, gid_t group); #ifdef CONFIG_SERIAL_TERMIOS /* Check if a file descriptor corresponds to a terminal I/O file */ @@ -356,6 +357,8 @@ int unlink(FAR const char *pathname); int truncate(FAR const char *path, off_t length); int symlink(FAR const char *path1, FAR const char *path2); ssize_t readlink(FAR const char *path, FAR char *buf, size_t bufsize); +int chown(FAR const char *path, uid_t owner, gid_t group); +int lchown(FAR const char *path, uid_t owner, gid_t group); /* Execution of programs from files */ diff --git a/libs/libc/unistd/Make.defs b/libs/libc/unistd/Make.defs index dd34465791..4529c662b8 100644 --- a/libs/libc/unistd/Make.defs +++ b/libs/libc/unistd/Make.defs @@ -26,9 +26,9 @@ CSRCS += lib_getopt_longonly.c lib_getoptvars.c lib_getoptargp.c CSRCS += lib_getopterrp.c lib_getoptindp.c lib_getoptoptp.c lib_times.c CSRCS += lib_alarm.c lib_fstatvfs.c lib_statvfs.c lib_sleep.c lib_nice.c CSRCS += lib_usleep.c lib_seteuid.c lib_setegid.c lib_geteuid.c lib_getegid.c -CSRCS += lib_setreuid.c lib_setregid.c lib_getrusage.c lib_utime.c lib_utimes.c +CSRCS += lib_setreuid.c lib_setregid.c lib_getrusage.c lib_utime.c CSRCS += lib_setrlimit.c lib_getrlimit.c lib_setpriority.c lib_getpriority.c -CSRCS += lib_futimes.c lib_futimens.c lib_gethostname.c lib_sethostname.c +CSRCS += lib_futimes.c lib_gethostname.c lib_sethostname.c ifneq ($(CONFIG_SCHED_USER_IDENTITY),y) CSRCS += lib_setuid.c lib_setgid.c lib_getuid.c lib_getgid.c diff --git a/libs/libc/unistd/lib_futimens.c b/libs/libc/unistd/lib_futimens.c deleted file mode 100644 index 9f1fc14c07..0000000000 --- a/libs/libc/unistd/lib_futimens.c +++ /dev/null @@ -1,57 +0,0 @@ -/**************************************************************************** - * libs/libc/unistd/lib_futimens.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 - -/**************************************************************************** - * Public Functions - ****************************************************************************/ - -/**************************************************************************** - * Name: futimens - * - * Description: - * futimens() update the timestamps of a file with nanosecond precision. - * This contrasts with the historical utime(2) and utimes(2), which permit - * only second and microsecond precision, respectively, when setting file - * timestamps. - * - * Input Parameters: - * fd - Specifies the fd to be modified - * times - Specifies the time value to set - * - * Returned Value: - * On success, futimens() return 0. - * On error, -1 is returned and errno is set to indicate the error. - * - ****************************************************************************/ - -int futimens(int fd, const struct timespec times[2]) -{ - set_errno(ENOTSUP); - return ERROR; -} diff --git a/libs/libc/unistd/lib_utimes.c b/libs/libc/unistd/lib_utimes.c deleted file mode 100644 index 5d804e3ef5..0000000000 --- a/libs/libc/unistd/lib_utimes.c +++ /dev/null @@ -1,38 +0,0 @@ -/**************************************************************************** - * libs/libc/unistd/lib_utimes.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 - -/**************************************************************************** - * Public Functions - ****************************************************************************/ - -int utimes(FAR const char *path, const struct timeval times[2]) -{ - set_errno(ENOTSUP); - return ERROR; -} diff --git a/syscall/syscall.csv b/syscall/syscall.csv index bfbe88cc80..c202ac9232 100644 --- a/syscall/syscall.csv +++ b/syscall/syscall.csv @@ -9,6 +9,8 @@ "atexit","stdlib.h","defined(CONFIG_SCHED_ATEXIT)","int","void (*)(void)" "bind","sys/socket.h","defined(CONFIG_NET)","int","int","FAR const struct sockaddr *","socklen_t" "boardctl","sys/boardctl.h","defined(CONFIG_LIB_BOARDCTL)","int","unsigned int","uintptr_t" +"chmod","sys/stat.h","","int","FAR const char *","mode_t" +"chown","unistd.h","","int","FAR const char *","uid_t","gid_t" "clearenv","stdlib.h","!defined(CONFIG_DISABLE_ENVIRON)","int" "clock","time.h","","clock_t" "clock_getres","time.h","","int","clockid_t","FAR struct timespec *" @@ -24,12 +26,15 @@ "exec","nuttx/binfmt/binfmt.h","!defined(CONFIG_BINFMT_DISABLE) && !defined(CONFIG_BUILD_KERNEL)","int","FAR const char *","FAR char * const *","FAR const struct symtab_s *","int" "execv","unistd.h","!defined(CONFIG_BINFMT_DISABLE) && defined(CONFIG_LIBC_EXECFUNCS)","int","FAR const char *","FAR char * const []|FAR char * const *" "exit","stdlib.h","","noreturn","int" +"fchmod","sys/stat.h","","int","int","mode_t" +"fchown","unistd.h","","int","int","uid_t","gid_t" "fcntl","fcntl.h","","int","int","int","...","int" "fs_fdopen","nuttx/fs/fs.h","defined(CONFIG_FILE_STREAM)","int","int","int","FAR struct tcb_s *","FAR struct file_struct **" "fstat","sys/stat.h","","int","int","FAR struct stat *" "fstatfs","sys/statfs.h","","int","int","FAR struct statfs *" "fsync","unistd.h","!defined(CONFIG_DISABLE_MOUNTPOINT)","int","int" "ftruncate","unistd.h","!defined(CONFIG_DISABLE_MOUNTPOINT)","int","int","off_t" +"futimens","sys/stat.h","","int","int","const struct timespec [2]|FAR const struct timespec *" "getenv","stdlib.h","!defined(CONFIG_DISABLE_ENVIRON)","FAR char *","FAR const char *" "getgid","unistd.h","defined(CONFIG_SCHED_USER_IDENTITY)","gid_t" "gethostname","unistd.h","","int","FAR char *","size_t" @@ -46,9 +51,12 @@ "insmod","nuttx/module.h","defined(CONFIG_MODULE)","FAR void *","FAR const char *","FAR const char *" "ioctl","sys/ioctl.h","","int","int","int","...","unsigned long" "kill","signal.h","","int","pid_t","int" +"lchmod","sys/stat.h","","int","FAR const char *","mode_t" +"lchown","unistd.h","","int","FAR const char *","uid_t","gid_t" "listen","sys/socket.h","defined(CONFIG_NET)","int","int","int" "lseek","unistd.h","","off_t","int","off_t","int" "lstat","sys/stat.h","","int","FAR const char *","FAR struct stat *" +"lutimes","sys/time.h","","int","FAR const char *","const struct timeval [2]|FAR const struct timeval *" "mkdir","sys/stat.h","!defined(CONFIG_DISABLE_MOUNTPOINT)","int","FAR const char *","mode_t" "mmap","sys/mman.h","","FAR void *","FAR void *","size_t","int","int","int","off_t" "modhandle","nuttx/module.h","defined(CONFIG_MODULE)","FAR void *","FAR const char *" @@ -181,6 +189,7 @@ "unlink","unistd.h","!defined(CONFIG_DISABLE_MOUNTPOINT)","int","FAR const char *" "unsetenv","stdlib.h","!defined(CONFIG_DISABLE_ENVIRON)","int","FAR const char *" "up_assert","nuttx/arch.h","","void","FAR const char *","int" +"utimes","sys/time.h","","int","FAR const char *","const struct timeval [2]|FAR const struct timeval *" "vfork","unistd.h","defined(CONFIG_SCHED_WAITPID) && defined(CONFIG_ARCH_HAVE_VFORK)","pid_t" "wait","sys/wait.h","defined(CONFIG_SCHED_WAITPID) && defined(CONFIG_SCHED_HAVE_PARENT)","pid_t","FAR int *" "waitid","sys/wait.h","defined(CONFIG_SCHED_WAITPID) && defined(CONFIG_SCHED_HAVE_PARENT)","int","idtype_t","id_t"," FAR siginfo_t *","int"