Home | History | Annotate | Line # | Download | only in import
chown.c revision 1.1.1.1.4.2
      1  1.1.1.1.4.2  perseant /* provide consistent interface to chown for systems that don't interpret
      2  1.1.1.1.4.2  perseant    an ID of -1 as meaning "don't change the corresponding ID".
      3  1.1.1.1.4.2  perseant 
      4  1.1.1.1.4.2  perseant    Copyright (C) 1997, 2004-2007, 2009-2022 Free Software Foundation, Inc.
      5  1.1.1.1.4.2  perseant 
      6  1.1.1.1.4.2  perseant    This file is free software: you can redistribute it and/or modify
      7  1.1.1.1.4.2  perseant    it under the terms of the GNU Lesser General Public License as
      8  1.1.1.1.4.2  perseant    published by the Free Software Foundation; either version 2.1 of the
      9  1.1.1.1.4.2  perseant    License, or (at your option) any later version.
     10  1.1.1.1.4.2  perseant 
     11  1.1.1.1.4.2  perseant    This file is distributed in the hope that it will be useful,
     12  1.1.1.1.4.2  perseant    but WITHOUT ANY WARRANTY; without even the implied warranty of
     13  1.1.1.1.4.2  perseant    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
     14  1.1.1.1.4.2  perseant    GNU Lesser General Public License for more details.
     15  1.1.1.1.4.2  perseant 
     16  1.1.1.1.4.2  perseant    You should have received a copy of the GNU Lesser General Public License
     17  1.1.1.1.4.2  perseant    along with this program.  If not, see <https://www.gnu.org/licenses/>.  */
     18  1.1.1.1.4.2  perseant 
     19  1.1.1.1.4.2  perseant /* written by Jim Meyering */
     20  1.1.1.1.4.2  perseant 
     21  1.1.1.1.4.2  perseant #include <config.h>
     22  1.1.1.1.4.2  perseant 
     23  1.1.1.1.4.2  perseant /* Specification.  */
     24  1.1.1.1.4.2  perseant #include <unistd.h>
     25  1.1.1.1.4.2  perseant 
     26  1.1.1.1.4.2  perseant #include <errno.h>
     27  1.1.1.1.4.2  perseant #include <fcntl.h>
     28  1.1.1.1.4.2  perseant #include <stdbool.h>
     29  1.1.1.1.4.2  perseant #include <string.h>
     30  1.1.1.1.4.2  perseant #include <sys/stat.h>
     31  1.1.1.1.4.2  perseant 
     32  1.1.1.1.4.2  perseant #if !HAVE_CHOWN
     33  1.1.1.1.4.2  perseant 
     34  1.1.1.1.4.2  perseant /* Simple stub that always fails with ENOSYS, for mingw.  */
     35  1.1.1.1.4.2  perseant int
     36  1.1.1.1.4.2  perseant chown (_GL_UNUSED const char *file, _GL_UNUSED uid_t uid,
     37  1.1.1.1.4.2  perseant        _GL_UNUSED gid_t gid)
     38  1.1.1.1.4.2  perseant {
     39  1.1.1.1.4.2  perseant   errno = ENOSYS;
     40  1.1.1.1.4.2  perseant   return -1;
     41  1.1.1.1.4.2  perseant }
     42  1.1.1.1.4.2  perseant 
     43  1.1.1.1.4.2  perseant #else /* HAVE_CHOWN */
     44  1.1.1.1.4.2  perseant 
     45  1.1.1.1.4.2  perseant /* Below we refer to the system's chown().  */
     46  1.1.1.1.4.2  perseant # undef chown
     47  1.1.1.1.4.2  perseant 
     48  1.1.1.1.4.2  perseant /* Provide a more-closely POSIX-conforming version of chown on
     49  1.1.1.1.4.2  perseant    systems with one or both of the following problems:
     50  1.1.1.1.4.2  perseant    - chown doesn't treat an ID of -1 as meaning
     51  1.1.1.1.4.2  perseant    "don't change the corresponding ID".
     52  1.1.1.1.4.2  perseant    - chown doesn't dereference symlinks.  */
     53  1.1.1.1.4.2  perseant 
     54  1.1.1.1.4.2  perseant int
     55  1.1.1.1.4.2  perseant rpl_chown (const char *file, uid_t uid, gid_t gid)
     56  1.1.1.1.4.2  perseant {
     57  1.1.1.1.4.2  perseant   struct stat st;
     58  1.1.1.1.4.2  perseant   bool stat_valid = false;
     59  1.1.1.1.4.2  perseant   int result;
     60  1.1.1.1.4.2  perseant 
     61  1.1.1.1.4.2  perseant # if CHOWN_CHANGE_TIME_BUG
     62  1.1.1.1.4.2  perseant   if (gid != (gid_t) -1 || uid != (uid_t) -1)
     63  1.1.1.1.4.2  perseant     {
     64  1.1.1.1.4.2  perseant       if (stat (file, &st))
     65  1.1.1.1.4.2  perseant         return -1;
     66  1.1.1.1.4.2  perseant       stat_valid = true;
     67  1.1.1.1.4.2  perseant     }
     68  1.1.1.1.4.2  perseant # endif
     69  1.1.1.1.4.2  perseant 
     70  1.1.1.1.4.2  perseant # if CHOWN_FAILS_TO_HONOR_ID_OF_NEGATIVE_ONE
     71  1.1.1.1.4.2  perseant   if (gid == (gid_t) -1 || uid == (uid_t) -1)
     72  1.1.1.1.4.2  perseant     {
     73  1.1.1.1.4.2  perseant       /* Stat file to get id(s) that should remain unchanged.  */
     74  1.1.1.1.4.2  perseant       if (!stat_valid && stat (file, &st))
     75  1.1.1.1.4.2  perseant         return -1;
     76  1.1.1.1.4.2  perseant       if (gid == (gid_t) -1)
     77  1.1.1.1.4.2  perseant         gid = st.st_gid;
     78  1.1.1.1.4.2  perseant       if (uid == (uid_t) -1)
     79  1.1.1.1.4.2  perseant         uid = st.st_uid;
     80  1.1.1.1.4.2  perseant     }
     81  1.1.1.1.4.2  perseant # endif
     82  1.1.1.1.4.2  perseant 
     83  1.1.1.1.4.2  perseant # if CHOWN_MODIFIES_SYMLINK
     84  1.1.1.1.4.2  perseant   {
     85  1.1.1.1.4.2  perseant     /* Handle the case in which the system-supplied chown function
     86  1.1.1.1.4.2  perseant        does *not* follow symlinks.  Instead, it changes permissions
     87  1.1.1.1.4.2  perseant        on the symlink itself.  To work around that, we open the
     88  1.1.1.1.4.2  perseant        file (but this can fail due to lack of read or write permission) and
     89  1.1.1.1.4.2  perseant        use fchown on the resulting descriptor.  */
     90  1.1.1.1.4.2  perseant     int open_flags = O_NONBLOCK | O_NOCTTY | O_CLOEXEC;
     91  1.1.1.1.4.2  perseant     int fd = open (file, O_RDONLY | open_flags);
     92  1.1.1.1.4.2  perseant     if (0 <= fd
     93  1.1.1.1.4.2  perseant         || (errno == EACCES
     94  1.1.1.1.4.2  perseant             && 0 <= (fd = open (file, O_WRONLY | open_flags))))
     95  1.1.1.1.4.2  perseant       {
     96  1.1.1.1.4.2  perseant         int saved_errno;
     97  1.1.1.1.4.2  perseant         bool fchown_socket_failure;
     98  1.1.1.1.4.2  perseant 
     99  1.1.1.1.4.2  perseant         result = fchown (fd, uid, gid);
    100  1.1.1.1.4.2  perseant         saved_errno = errno;
    101  1.1.1.1.4.2  perseant 
    102  1.1.1.1.4.2  perseant         /* POSIX says fchown can fail with errno == EINVAL on sockets
    103  1.1.1.1.4.2  perseant            and pipes, so fall back on chown in that case.  */
    104  1.1.1.1.4.2  perseant         fchown_socket_failure =
    105  1.1.1.1.4.2  perseant           (result != 0 && saved_errno == EINVAL
    106  1.1.1.1.4.2  perseant            && fstat (fd, &st) == 0
    107  1.1.1.1.4.2  perseant            && (S_ISFIFO (st.st_mode) || S_ISSOCK (st.st_mode)));
    108  1.1.1.1.4.2  perseant 
    109  1.1.1.1.4.2  perseant         close (fd);
    110  1.1.1.1.4.2  perseant 
    111  1.1.1.1.4.2  perseant         if (! fchown_socket_failure)
    112  1.1.1.1.4.2  perseant           {
    113  1.1.1.1.4.2  perseant             errno = saved_errno;
    114  1.1.1.1.4.2  perseant             return result;
    115  1.1.1.1.4.2  perseant           }
    116  1.1.1.1.4.2  perseant       }
    117  1.1.1.1.4.2  perseant     else if (errno != EACCES)
    118  1.1.1.1.4.2  perseant       return -1;
    119  1.1.1.1.4.2  perseant   }
    120  1.1.1.1.4.2  perseant # endif
    121  1.1.1.1.4.2  perseant 
    122  1.1.1.1.4.2  perseant # if CHOWN_TRAILING_SLASH_BUG
    123  1.1.1.1.4.2  perseant   if (!stat_valid)
    124  1.1.1.1.4.2  perseant     {
    125  1.1.1.1.4.2  perseant       size_t len = strlen (file);
    126  1.1.1.1.4.2  perseant       if (len && file[len - 1] == '/' && stat (file, &st))
    127  1.1.1.1.4.2  perseant         return -1;
    128  1.1.1.1.4.2  perseant     }
    129  1.1.1.1.4.2  perseant # endif
    130  1.1.1.1.4.2  perseant 
    131  1.1.1.1.4.2  perseant   result = chown (file, uid, gid);
    132  1.1.1.1.4.2  perseant 
    133  1.1.1.1.4.2  perseant # if CHOWN_CHANGE_TIME_BUG
    134  1.1.1.1.4.2  perseant   if (result == 0 && stat_valid
    135  1.1.1.1.4.2  perseant       && (uid == st.st_uid || uid == (uid_t) -1)
    136  1.1.1.1.4.2  perseant       && (gid == st.st_gid || gid == (gid_t) -1))
    137  1.1.1.1.4.2  perseant     {
    138  1.1.1.1.4.2  perseant       /* No change in ownership, but at least one argument was not -1,
    139  1.1.1.1.4.2  perseant          so we are required to update ctime.  Since chown succeeded,
    140  1.1.1.1.4.2  perseant          we assume that chmod will do likewise.  Fortunately, on all
    141  1.1.1.1.4.2  perseant          known systems where a 'no-op' chown skips the ctime update, a
    142  1.1.1.1.4.2  perseant          'no-op' chmod still does the trick.  */
    143  1.1.1.1.4.2  perseant       result = chmod (file, st.st_mode & (S_IRWXU | S_IRWXG | S_IRWXO
    144  1.1.1.1.4.2  perseant                                           | S_ISUID | S_ISGID | S_ISVTX));
    145  1.1.1.1.4.2  perseant     }
    146  1.1.1.1.4.2  perseant # endif
    147  1.1.1.1.4.2  perseant 
    148  1.1.1.1.4.2  perseant   return result;
    149  1.1.1.1.4.2  perseant }
    150  1.1.1.1.4.2  perseant 
    151  1.1.1.1.4.2  perseant #endif /* HAVE_CHOWN */
    152