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