(root)/
tar-1.35/
gnu/
lchmod.c
       1  /* Implement lchmod on platforms where it does not work correctly.
       2  
       3     Copyright 2020-2023 Free Software Foundation, Inc.
       4  
       5     This program is free software: you can redistribute it and/or modify
       6     it under the terms of the GNU General Public License as published by
       7     the Free Software Foundation, either version 3 of the License, or
       8     (at your option) any later version.
       9  
      10     This program is distributed in the hope that it will be useful,
      11     but WITHOUT ANY WARRANTY; without even the implied warranty of
      12     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
      13     GNU General Public License for more details.
      14  
      15     You should have received a copy of the GNU General Public License
      16     along with this program.  If not, see <https://www.gnu.org/licenses/>.  */
      17  
      18  /* written by Paul Eggert */
      19  
      20  #include <config.h>
      21  
      22  /* Specification.  */
      23  #include <sys/stat.h>
      24  
      25  #include <errno.h>
      26  #include <fcntl.h>
      27  #include <stdio.h>
      28  #include <string.h>
      29  #include <unistd.h>
      30  
      31  #include <intprops.h>
      32  
      33  /* Work like chmod, except when FILE is a symbolic link.
      34     In that case, on systems where permissions on symbolic links are unsupported
      35     (such as Linux), set errno to EOPNOTSUPP and return -1.  */
      36  
      37  int
      38  lchmod (char const *file, mode_t mode)
      39  {
      40    char readlink_buf[1];
      41  
      42  #ifdef O_PATH
      43    /* Open a file descriptor with O_NOFOLLOW, to make sure we don't
      44       follow symbolic links, if /proc is mounted.  O_PATH is used to
      45       avoid a failure if the file is not readable.
      46       Cf. <https://sourceware.org/bugzilla/show_bug.cgi?id=14578>  */
      47    int fd = open (file, O_PATH | O_NOFOLLOW | O_CLOEXEC);
      48    if (fd < 0)
      49      return fd;
      50  
      51    int err;
      52    if (0 <= readlinkat (fd, "", readlink_buf, sizeof readlink_buf))
      53      err = EOPNOTSUPP;
      54    else if (errno == EINVAL)
      55      {
      56        static char const fmt[] = "/proc/self/fd/%d";
      57        char buf[sizeof fmt - sizeof "%d" + INT_BUFSIZE_BOUND (int)];
      58        sprintf (buf, fmt, fd);
      59        err = chmod (buf, mode) == 0 ? 0 : errno == ENOENT ? -1 : errno;
      60      }
      61    else
      62      err = errno == ENOENT ? -1 : errno;
      63  
      64    close (fd);
      65  
      66    errno = err;
      67    if (0 <= err)
      68      return err == 0 ? 0 : -1;
      69  #endif
      70  
      71    size_t len = strlen (file);
      72    if (len && file[len - 1] == '/')
      73      {
      74        struct stat st;
      75        if (lstat (file, &st) < 0)
      76          return -1;
      77        if (!S_ISDIR (st.st_mode))
      78          {
      79            errno = ENOTDIR;
      80            return -1;
      81          }
      82      }
      83  
      84    /* O_PATH + /proc is not supported.  */
      85  
      86    if (0 <= readlink (file, readlink_buf, sizeof readlink_buf))
      87      {
      88        errno = EOPNOTSUPP;
      89        return -1;
      90      }
      91  
      92    /* Fall back on chmod, despite a possible race.  */
      93    return chmod (file, mode);
      94  }