
1: /* Change the ownership and mode bits of a directory. 2: 3: Copyright (C) 2006 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 2, or (at your option) 8: 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, write to the Free Software Foundation, 17: Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ 18: 19: /* Written by Paul Eggert. */ 20: 21: #include <config.h> 22: 23: #include "dirchownmod.h" 24: 25: #include <errno.h> 26: #include <sys/types.h> 27: #include <sys/stat.h> 28: #include <unistd.h> 29: 30: #include "lchmod.h" 31: #include "lchown.h" 32: #include "stat-macros.h" 33: 34: #ifndef HAVE_FCHMOD 35: # define HAVE_FCHMOD 0 36: # undef fchmod 37: # define fchmod(fd, mode) (-1) 38: #endif 39: 40: /* Change the ownership and mode bits of a directory. If FD is 41: nonnegative, it should be a file descriptor associated with the 42: directory; close it before returning. DIR is the name of the 43: directory. 44: 45: If MKDIR_MODE is not (mode_t) -1, mkdir (DIR, MKDIR_MODE) has just 46: been executed successfully with umask zero, so DIR should be a 47: directory (not a symbolic link). 48: 49: First, set the file's owner to OWNER and group to GROUP, but leave 50: the owner alone if OWNER is (uid_t) -1, and similarly for GROUP. 51: 52: Then, set the file's mode bits to MODE, except preserve any of the 53: bits that correspond to zero bits in MODE_BITS. In other words, 54: MODE_BITS is a mask that specifies which of the file's mode bits 55: should be set or cleared. MODE should be a subset of MODE_BITS, 56: which in turn should be a subset of CHMOD_MODE_BITS. 57: 58: This implementation assumes the current umask is zero. 59: 60: Return 0 if successful, -1 (setting errno) otherwise. Unsuccessful 61: calls may do the chown but not the chmod. */ 62: 63: int 64: dirchownmod (int fd, char const *dir, mode_t mkdir_mode, 65: uid_t owner, gid_t group, 66: mode_t mode, mode_t mode_bits) 67: { 68: struct stat st; 69: int result = (fd < 0 ? stat (dir, &st) : fstat (fd, &st)); 70: 71: if (result == 0) 72: { 73: mode_t dir_mode = st.st_mode; 74: 75: /* Check whether DIR is a directory. If FD is nonnegative, this 76: check avoids changing the ownership and mode bits of the 77: wrong file in many cases. This doesn't fix all the race 78: conditions, but it is better than nothing. */ 79: if (! S_ISDIR (dir_mode)) 80: { 81: errno = ENOTDIR; 82: result = -1; 83: } 84: else 85: { 86: /* If at least one of the S_IXUGO bits are set, chown might 87: clear the S_ISUID and S_SGID bits. Keep track of any 88: file mode bits whose values are indeterminate due to this 89: issue. */ 90: mode_t indeterminate = 0; 91: 92: /* On some systems, chown clears S_ISUID and S_ISGID, so do 93: chown before chmod. On older System V hosts, ordinary 94: users can give their files away via chown; don't worry 95: about that here, since users shouldn't do that. */ 96: 97: if ((owner != (uid_t) -1 && owner != st.st_uid) 98: || (group != (gid_t) -1 && group != st.st_gid)) 99: { 100: result = (0 <= fd 101: ? fchown (fd, owner, group) 102: : mkdir_mode != (mode_t) -1 103: ? lchown (dir, owner, group) 104: : chown (dir, owner, group)); 105: 106: /* Either the user cares about an indeterminate bit and 107: it'll be set properly by chmod below, or the user 108: doesn't care and it's OK to use the bit's pre-chown 109: value. So there's no need to re-stat DIR here. */ 110: 111: if (result == 0 && (dir_mode & S_IXUGO)) 112: indeterminate = dir_mode & (S_ISUID | S_ISGID); 113: } 114: 115: /* If the file mode bits might not be right, use chmod to 116: change them. Don't change bits the user doesn't care 117: about. */ 118: if (result == 0 && (((dir_mode ^ mode) | indeterminate) & mode_bits)) 119: { 120: mode_t chmod_mode = 121: mode | (dir_mode & CHMOD_MODE_BITS & ~mode_bits); 122: result = (HAVE_FCHMOD && 0 <= fd 123: ? fchmod (fd, chmod_mode) 124: : mkdir_mode != (mode_t) -1 125: ? lchmod (dir, chmod_mode) 126: : chmod (dir, chmod_mode)); 127: } 128: } 129: } 130: 131: if (0 <= fd) 132: { 133: if (result == 0) 134: result = close (fd); 135: else 136: { 137: int e = errno; 138: close (fd); 139: errno = e; 140: } 141: } 142: 143: return result; 144: }