
1: /* provide consistent interface to chown for systems that don't interpret 2: an ID of -1 as meaning `don't change the corresponding ID'. 3: 4: Copyright (C) 1997, 2004, 2005, 2006, 2007 Free Software Foundation, Inc. 5: 6: This program is free software; you can redistribute it and/or modify 7: it under the terms of the GNU General Public License as published by 8: the Free Software Foundation; either version 2, or (at your option) 9: any later version. 10: 11: This program is distributed in the hope that it will be useful, 12: but WITHOUT ANY WARRANTY; without even the implied warranty of 13: MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14: GNU General Public License for more details. 15: 16: You should have received a copy of the GNU General Public License 17: along with this program; if not, write to the Free Software Foundation, 18: Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ 19: 20: /* written by Jim Meyering */ 21: 22: #include <config.h> 23: 24: /* Specification. */ 25: #include <unistd.h> 26: 27: #include <stdbool.h> 28: #include <sys/types.h> 29: #include <sys/stat.h> 30: #include <fcntl.h> 31: #include <errno.h> 32: 33: /* Below we refer to the system's chown(). */ 34: #undef chown 35: 36: /* The results of open() in this file are not used with fchdir, 37: therefore save some unnecessary work in fchdir.c. */ 38: #undef open 39: #undef close 40: 41: /* Provide a more-closely POSIX-conforming version of chown on 42: systems with one or both of the following problems: 43: - chown doesn't treat an ID of -1 as meaning 44: `don't change the corresponding ID'. 45: - chown doesn't dereference symlinks. */ 46: 47: int 48: rpl_chown (const char *file, uid_t uid, gid_t gid) 49: { 50: #if CHOWN_FAILS_TO_HONOR_ID_OF_NEGATIVE_ONE 51: if (gid == (gid_t) -1 || uid == (uid_t) -1) 52: { 53: struct stat file_stats; 54: 55: /* Stat file to get id(s) that should remain unchanged. */ 56: if (stat (file, &file_stats)) 57: return -1; 58: 59: if (gid == (gid_t) -1) 60: gid = file_stats.st_gid; 61: 62: if (uid == (uid_t) -1) 63: uid = file_stats.st_uid; 64: } 65: #endif 66: 67: #if CHOWN_MODIFIES_SYMLINK 68: { 69: /* Handle the case in which the system-supplied chown function 70: does *not* follow symlinks. Instead, it changes permissions 71: on the symlink itself. To work around that, we open the 72: file (but this can fail due to lack of read or write permission) and 73: use fchown on the resulting descriptor. */ 74: int open_flags = O_NONBLOCK | O_NOCTTY; 75: int fd = open (file, O_RDONLY | open_flags); 76: if (0 <= fd 77: || (errno == EACCES 78: && 0 <= (fd = open (file, O_WRONLY | open_flags)))) 79: { 80: int result = fchown (fd, uid, gid); 81: int saved_errno = errno; 82: 83: /* POSIX says fchown can fail with errno == EINVAL on sockets, 84: so fall back on chown in that case. */ 85: struct stat sb; 86: bool fchown_socket_failure = 87: (result != 0 && saved_errno == EINVAL 88: && fstat (fd, &sb) == 0 && S_ISFIFO (sb.st_mode)); 89: 90: close (fd); 91: 92: if (! fchown_socket_failure) 93: { 94: errno = saved_errno; 95: return result; 96: } 97: } 98: else if (errno != EACCES) 99: return -1; 100: } 101: #endif 102: 103: return chown (file, uid, gid); 104: }