text: /****************************************************************************
   * 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 <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: 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 & ~0177777))
      {
        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);
  }
  