1 |
/* $MidnightBSD$ */ |
2 |
/* |
3 |
* Copyright (c) 1988, 1993, 1994 |
4 |
* The Regents of the University of California. All rights reserved. |
5 |
* |
6 |
* Redistribution and use in source and binary forms, with or without |
7 |
* modification, are permitted provided that the following conditions |
8 |
* are met: |
9 |
* 1. Redistributions of source code must retain the above copyright |
10 |
* notice, this list of conditions and the following disclaimer. |
11 |
* 2. Redistributions in binary form must reproduce the above copyright |
12 |
* notice, this list of conditions and the following disclaimer in the |
13 |
* documentation and/or other materials provided with the distribution. |
14 |
* 4. Neither the name of the University nor the names of its contributors |
15 |
* may be used to endorse or promote products derived from this software |
16 |
* without specific prior written permission. |
17 |
* |
18 |
* THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND |
19 |
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE |
20 |
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE |
21 |
* ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE |
22 |
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL |
23 |
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS |
24 |
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) |
25 |
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT |
26 |
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY |
27 |
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF |
28 |
* SUCH DAMAGE. |
29 |
*/ |
30 |
|
31 |
#if 0 |
32 |
#ifndef lint |
33 |
static const char copyright[] = |
34 |
"@(#) Copyright (c) 1988, 1993, 1994\n\ |
35 |
The Regents of the University of California. All rights reserved.\n"; |
36 |
#endif /* not lint */ |
37 |
|
38 |
#ifndef lint |
39 |
static char sccsid[] = "@(#)chown.c 8.8 (Berkeley) 4/4/94"; |
40 |
#endif /* not lint */ |
41 |
#endif |
42 |
|
43 |
#include <sys/cdefs.h> |
44 |
__FBSDID("$FreeBSD: stable/10/usr.sbin/chown/chown.c 283875 2015-06-01 09:04:57Z smh $"); |
45 |
|
46 |
#include <sys/param.h> |
47 |
#include <sys/stat.h> |
48 |
|
49 |
#include <err.h> |
50 |
#include <errno.h> |
51 |
#include <fcntl.h> |
52 |
#include <fts.h> |
53 |
#include <grp.h> |
54 |
#include <libgen.h> |
55 |
#include <pwd.h> |
56 |
#include <stdint.h> |
57 |
#include <stdio.h> |
58 |
#include <stdlib.h> |
59 |
#include <string.h> |
60 |
#include <unistd.h> |
61 |
|
62 |
static void a_gid(const char *); |
63 |
static void a_uid(const char *); |
64 |
static void chownerr(const char *); |
65 |
static uid_t id(const char *, const char *); |
66 |
static void usage(void); |
67 |
|
68 |
static uid_t uid; |
69 |
static gid_t gid; |
70 |
static int ischown; |
71 |
static const char *gname; |
72 |
|
73 |
int |
74 |
main(int argc, char **argv) |
75 |
{ |
76 |
FTS *ftsp; |
77 |
FTSENT *p; |
78 |
int Hflag, Lflag, Rflag, fflag, hflag, vflag, xflag; |
79 |
int ch, fts_options, rval; |
80 |
char *cp; |
81 |
|
82 |
ischown = (strcmp(basename(argv[0]), "chown") == 0); |
83 |
|
84 |
Hflag = Lflag = Rflag = fflag = hflag = vflag = xflag = 0; |
85 |
while ((ch = getopt(argc, argv, "HLPRfhvx")) != -1) |
86 |
switch (ch) { |
87 |
case 'H': |
88 |
Hflag = 1; |
89 |
Lflag = 0; |
90 |
break; |
91 |
case 'L': |
92 |
Lflag = 1; |
93 |
Hflag = 0; |
94 |
break; |
95 |
case 'P': |
96 |
Hflag = Lflag = 0; |
97 |
break; |
98 |
case 'R': |
99 |
Rflag = 1; |
100 |
break; |
101 |
case 'f': |
102 |
fflag = 1; |
103 |
break; |
104 |
case 'h': |
105 |
hflag = 1; |
106 |
break; |
107 |
case 'v': |
108 |
vflag++; |
109 |
break; |
110 |
case 'x': |
111 |
xflag = 1; |
112 |
break; |
113 |
case '?': |
114 |
default: |
115 |
usage(); |
116 |
} |
117 |
argv += optind; |
118 |
argc -= optind; |
119 |
|
120 |
if (argc < 2) |
121 |
usage(); |
122 |
|
123 |
if (Rflag) { |
124 |
if (hflag && (Hflag || Lflag)) |
125 |
errx(1, "the -R%c and -h options may not be " |
126 |
"specified together", Hflag ? 'H' : 'L'); |
127 |
if (Lflag) { |
128 |
fts_options = FTS_LOGICAL; |
129 |
} else { |
130 |
fts_options = FTS_PHYSICAL; |
131 |
|
132 |
if (Hflag) { |
133 |
fts_options |= FTS_COMFOLLOW; |
134 |
} |
135 |
} |
136 |
} else if (hflag) { |
137 |
fts_options = FTS_PHYSICAL; |
138 |
} else { |
139 |
fts_options = FTS_LOGICAL; |
140 |
} |
141 |
|
142 |
if (xflag) |
143 |
fts_options |= FTS_XDEV; |
144 |
|
145 |
uid = (uid_t)-1; |
146 |
gid = (gid_t)-1; |
147 |
if (ischown) { |
148 |
if ((cp = strchr(*argv, ':')) != NULL) { |
149 |
*cp++ = '\0'; |
150 |
a_gid(cp); |
151 |
} |
152 |
#ifdef SUPPORT_DOT |
153 |
else if ((cp = strchr(*argv, '.')) != NULL) { |
154 |
warnx("separation of user and group with a period is deprecated"); |
155 |
*cp++ = '\0'; |
156 |
a_gid(cp); |
157 |
} |
158 |
#endif |
159 |
a_uid(*argv); |
160 |
} else |
161 |
a_gid(*argv); |
162 |
|
163 |
if ((ftsp = fts_open(++argv, fts_options, 0)) == NULL) |
164 |
err(1, NULL); |
165 |
|
166 |
for (rval = 0; (p = fts_read(ftsp)) != NULL;) { |
167 |
int atflag; |
168 |
|
169 |
if ((fts_options & FTS_LOGICAL) || |
170 |
((fts_options & FTS_COMFOLLOW) && |
171 |
p->fts_level == FTS_ROOTLEVEL)) |
172 |
atflag = 0; |
173 |
else |
174 |
atflag = AT_SYMLINK_NOFOLLOW; |
175 |
|
176 |
switch (p->fts_info) { |
177 |
case FTS_D: /* Change it at FTS_DP. */ |
178 |
if (!Rflag) |
179 |
fts_set(ftsp, p, FTS_SKIP); |
180 |
continue; |
181 |
case FTS_DNR: /* Warn, chown. */ |
182 |
warnx("%s: %s", p->fts_path, strerror(p->fts_errno)); |
183 |
rval = 1; |
184 |
break; |
185 |
case FTS_ERR: /* Warn, continue. */ |
186 |
case FTS_NS: |
187 |
warnx("%s: %s", p->fts_path, strerror(p->fts_errno)); |
188 |
rval = 1; |
189 |
continue; |
190 |
default: |
191 |
break; |
192 |
} |
193 |
if ((uid == (uid_t)-1 || uid == p->fts_statp->st_uid) && |
194 |
(gid == (gid_t)-1 || gid == p->fts_statp->st_gid)) |
195 |
continue; |
196 |
if (fchownat(AT_FDCWD, p->fts_accpath, uid, gid, atflag) |
197 |
== -1 && !fflag) { |
198 |
chownerr(p->fts_path); |
199 |
rval = 1; |
200 |
} else if (vflag) { |
201 |
printf("%s", p->fts_path); |
202 |
if (vflag > 1) { |
203 |
if (ischown) { |
204 |
printf(": %ju:%ju -> %ju:%ju", |
205 |
(uintmax_t) |
206 |
p->fts_statp->st_uid, |
207 |
(uintmax_t) |
208 |
p->fts_statp->st_gid, |
209 |
(uid == (uid_t)-1) ? |
210 |
(uintmax_t) |
211 |
p->fts_statp->st_uid : |
212 |
(uintmax_t)uid, |
213 |
(gid == (gid_t)-1) ? |
214 |
(uintmax_t) |
215 |
p->fts_statp->st_gid : |
216 |
(uintmax_t)gid); |
217 |
} else { |
218 |
printf(": %ju -> %ju", |
219 |
(uintmax_t) |
220 |
p->fts_statp->st_gid, |
221 |
(gid == (gid_t)-1) ? |
222 |
(uintmax_t) |
223 |
p->fts_statp->st_gid : |
224 |
(uintmax_t)gid); |
225 |
} |
226 |
} |
227 |
printf("\n"); |
228 |
} |
229 |
} |
230 |
if (errno) |
231 |
err(1, "fts_read"); |
232 |
exit(rval); |
233 |
} |
234 |
|
235 |
static void |
236 |
a_gid(const char *s) |
237 |
{ |
238 |
struct group *gr; |
239 |
|
240 |
if (*s == '\0') /* Argument was "uid[:.]". */ |
241 |
return; |
242 |
gname = s; |
243 |
gid = ((gr = getgrnam(s)) != NULL) ? gr->gr_gid : id(s, "group"); |
244 |
} |
245 |
|
246 |
static void |
247 |
a_uid(const char *s) |
248 |
{ |
249 |
struct passwd *pw; |
250 |
|
251 |
if (*s == '\0') /* Argument was "[:.]gid". */ |
252 |
return; |
253 |
uid = ((pw = getpwnam(s)) != NULL) ? pw->pw_uid : id(s, "user"); |
254 |
} |
255 |
|
256 |
static uid_t |
257 |
id(const char *name, const char *type) |
258 |
{ |
259 |
uid_t val; |
260 |
char *ep; |
261 |
|
262 |
/* |
263 |
* XXX |
264 |
* We know that uid_t's and gid_t's are unsigned longs. |
265 |
*/ |
266 |
errno = 0; |
267 |
val = strtoul(name, &ep, 10); |
268 |
if (errno || *ep != '\0') |
269 |
errx(1, "%s: illegal %s name", name, type); |
270 |
return (val); |
271 |
} |
272 |
|
273 |
static void |
274 |
chownerr(const char *file) |
275 |
{ |
276 |
static uid_t euid = -1; |
277 |
static int ngroups = -1; |
278 |
static long ngroups_max; |
279 |
gid_t *groups; |
280 |
|
281 |
/* Check for chown without being root. */ |
282 |
if (errno != EPERM || (uid != (uid_t)-1 && |
283 |
euid == (uid_t)-1 && (euid = geteuid()) != 0)) { |
284 |
warn("%s", file); |
285 |
return; |
286 |
} |
287 |
|
288 |
/* Check group membership; kernel just returns EPERM. */ |
289 |
if (gid != (gid_t)-1 && ngroups == -1 && |
290 |
euid == (uid_t)-1 && (euid = geteuid()) != 0) { |
291 |
ngroups_max = sysconf(_SC_NGROUPS_MAX) + 1; |
292 |
if ((groups = malloc(sizeof(gid_t) * ngroups_max)) == NULL) |
293 |
err(1, "malloc"); |
294 |
ngroups = getgroups(ngroups_max, groups); |
295 |
while (--ngroups >= 0 && gid != groups[ngroups]); |
296 |
free(groups); |
297 |
if (ngroups < 0) { |
298 |
warnx("you are not a member of group %s", gname); |
299 |
return; |
300 |
} |
301 |
} |
302 |
warn("%s", file); |
303 |
} |
304 |
|
305 |
static void |
306 |
usage(void) |
307 |
{ |
308 |
|
309 |
if (ischown) |
310 |
(void)fprintf(stderr, "%s\n%s\n", |
311 |
"usage: chown [-fhvx] [-R [-H | -L | -P]] owner[:group]" |
312 |
" file ...", |
313 |
" chown [-fhvx] [-R [-H | -L | -P]] :group file ..."); |
314 |
else |
315 |
(void)fprintf(stderr, "%s\n", |
316 |
"usage: chgrp [-fhvx] [-R [-H | -L | -P]] group file ..."); |
317 |
exit(1); |
318 |
} |