Blame lib/chown.c

Packit Service a2489d
/* provide consistent interface to chown for systems that don't interpret
Packit Service a2489d
   an ID of -1 as meaning "don't change the corresponding ID".
Packit Service a2489d
Packit Service a2489d
   Copyright (C) 1997, 2004-2007, 2009-2018 Free Software Foundation, Inc.
Packit Service a2489d
Packit Service a2489d
   This program is free software: you can redistribute it and/or modify
Packit Service a2489d
   it under the terms of the GNU General Public License as published by
Packit Service a2489d
   the Free Software Foundation; either version 3 of the License, or
Packit Service a2489d
   (at your option) any later version.
Packit Service a2489d
Packit Service a2489d
   This program is distributed in the hope that it will be useful,
Packit Service a2489d
   but WITHOUT ANY WARRANTY; without even the implied warranty of
Packit Service a2489d
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
Packit Service a2489d
   GNU General Public License for more details.
Packit Service a2489d
Packit Service a2489d
   You should have received a copy of the GNU General Public License
Packit Service a2489d
   along with this program.  If not, see <https://www.gnu.org/licenses/>.  */
Packit Service a2489d
Packit Service a2489d
/* written by Jim Meyering */
Packit Service a2489d
Packit Service a2489d
#include <config.h>
Packit Service a2489d
Packit Service a2489d
/* Specification.  */
Packit Service a2489d
#include <unistd.h>
Packit Service a2489d
Packit Service a2489d
#include <errno.h>
Packit Service a2489d
#include <fcntl.h>
Packit Service a2489d
#include <stdbool.h>
Packit Service a2489d
#include <string.h>
Packit Service a2489d
#include <sys/stat.h>
Packit Service a2489d
Packit Service a2489d
#if !HAVE_CHOWN
Packit Service a2489d
Packit Service a2489d
/* Simple stub that always fails with ENOSYS, for mingw.  */
Packit Service a2489d
int
Packit Service a2489d
chown (const char *file _GL_UNUSED, uid_t uid _GL_UNUSED,
Packit Service a2489d
       gid_t gid _GL_UNUSED)
Packit Service a2489d
{
Packit Service a2489d
  errno = ENOSYS;
Packit Service a2489d
  return -1;
Packit Service a2489d
}
Packit Service a2489d
Packit Service a2489d
#else /* HAVE_CHOWN */
Packit Service a2489d
Packit Service a2489d
/* Below we refer to the system's chown().  */
Packit Service a2489d
# undef chown
Packit Service a2489d
Packit Service a2489d
/* Provide a more-closely POSIX-conforming version of chown on
Packit Service a2489d
   systems with one or both of the following problems:
Packit Service a2489d
   - chown doesn't treat an ID of -1 as meaning
Packit Service a2489d
   "don't change the corresponding ID".
Packit Service a2489d
   - chown doesn't dereference symlinks.  */
Packit Service a2489d
Packit Service a2489d
int
Packit Service a2489d
rpl_chown (const char *file, uid_t uid, gid_t gid)
Packit Service a2489d
{
Packit Service a2489d
  struct stat st;
Packit Service a2489d
  bool stat_valid = false;
Packit Service a2489d
  int result;
Packit Service a2489d
Packit Service a2489d
# if CHOWN_CHANGE_TIME_BUG
Packit Service a2489d
  if (gid != (gid_t) -1 || uid != (uid_t) -1)
Packit Service a2489d
    {
Packit Service a2489d
      if (stat (file, &st))
Packit Service a2489d
        return -1;
Packit Service a2489d
      stat_valid = true;
Packit Service a2489d
    }
Packit Service a2489d
# endif
Packit Service a2489d
Packit Service a2489d
# if CHOWN_FAILS_TO_HONOR_ID_OF_NEGATIVE_ONE
Packit Service a2489d
  if (gid == (gid_t) -1 || uid == (uid_t) -1)
Packit Service a2489d
    {
Packit Service a2489d
      /* Stat file to get id(s) that should remain unchanged.  */
Packit Service a2489d
      if (!stat_valid && stat (file, &st))
Packit Service a2489d
        return -1;
Packit Service a2489d
      if (gid == (gid_t) -1)
Packit Service a2489d
        gid = st.st_gid;
Packit Service a2489d
      if (uid == (uid_t) -1)
Packit Service a2489d
        uid = st.st_uid;
Packit Service a2489d
    }
Packit Service a2489d
# endif
Packit Service a2489d
Packit Service a2489d
# if CHOWN_MODIFIES_SYMLINK
Packit Service a2489d
  {
Packit Service a2489d
    /* Handle the case in which the system-supplied chown function
Packit Service a2489d
       does *not* follow symlinks.  Instead, it changes permissions
Packit Service a2489d
       on the symlink itself.  To work around that, we open the
Packit Service a2489d
       file (but this can fail due to lack of read or write permission) and
Packit Service a2489d
       use fchown on the resulting descriptor.  */
Packit Service a2489d
    int open_flags = O_NONBLOCK | O_NOCTTY;
Packit Service a2489d
    int fd = open (file, O_RDONLY | open_flags);
Packit Service a2489d
    if (0 <= fd
Packit Service a2489d
        || (errno == EACCES
Packit Service a2489d
            && 0 <= (fd = open (file, O_WRONLY | open_flags))))
Packit Service a2489d
      {
Packit Service a2489d
        int saved_errno;
Packit Service a2489d
        bool fchown_socket_failure;
Packit Service a2489d
Packit Service a2489d
        result = fchown (fd, uid, gid);
Packit Service a2489d
        saved_errno = errno;
Packit Service a2489d
Packit Service a2489d
        /* POSIX says fchown can fail with errno == EINVAL on sockets
Packit Service a2489d
           and pipes, so fall back on chown in that case.  */
Packit Service a2489d
        fchown_socket_failure =
Packit Service a2489d
          (result != 0 && saved_errno == EINVAL
Packit Service a2489d
           && fstat (fd, &st) == 0
Packit Service a2489d
           && (S_ISFIFO (st.st_mode) || S_ISSOCK (st.st_mode)));
Packit Service a2489d
Packit Service a2489d
        close (fd);
Packit Service a2489d
Packit Service a2489d
        if (! fchown_socket_failure)
Packit Service a2489d
          {
Packit Service a2489d
            errno = saved_errno;
Packit Service a2489d
            return result;
Packit Service a2489d
          }
Packit Service a2489d
      }
Packit Service a2489d
    else if (errno != EACCES)
Packit Service a2489d
      return -1;
Packit Service a2489d
  }
Packit Service a2489d
# endif
Packit Service a2489d
Packit Service a2489d
# if CHOWN_TRAILING_SLASH_BUG
Packit Service a2489d
  if (!stat_valid)
Packit Service a2489d
    {
Packit Service a2489d
      size_t len = strlen (file);
Packit Service a2489d
      if (len && file[len - 1] == '/' && stat (file, &st))
Packit Service a2489d
        return -1;
Packit Service a2489d
    }
Packit Service a2489d
# endif
Packit Service a2489d
Packit Service a2489d
  result = chown (file, uid, gid);
Packit Service a2489d
Packit Service a2489d
# if CHOWN_CHANGE_TIME_BUG
Packit Service a2489d
  if (result == 0 && stat_valid
Packit Service a2489d
      && (uid == st.st_uid || uid == (uid_t) -1)
Packit Service a2489d
      && (gid == st.st_gid || gid == (gid_t) -1))
Packit Service a2489d
    {
Packit Service a2489d
      /* No change in ownership, but at least one argument was not -1,
Packit Service a2489d
         so we are required to update ctime.  Since chown succeeded,
Packit Service a2489d
         we assume that chmod will do likewise.  Fortunately, on all
Packit Service a2489d
         known systems where a 'no-op' chown skips the ctime update, a
Packit Service a2489d
         'no-op' chmod still does the trick.  */
Packit Service a2489d
      result = chmod (file, st.st_mode & (S_IRWXU | S_IRWXG | S_IRWXO
Packit Service a2489d
                                          | S_ISUID | S_ISGID | S_ISVTX));
Packit Service a2489d
    }
Packit Service a2489d
# endif
Packit Service a2489d
Packit Service a2489d
  return result;
Packit Service a2489d
}
Packit Service a2489d
Packit Service a2489d
#endif /* HAVE_CHOWN */