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