1 /*
2 * Copyright (c) 1988, 1993, 1994
3 * The Regents of the University of California. All rights reserved.
4 *
5 * %sccs.include.redist.c%
6 */
7
8 #ifndef lint
9 static char copyright[] =
10 "@(#) Copyright (c) 1988, 1993, 1994\n\
11 The Regents of the University of California. All rights reserved.\n";
12 #endif /* not lint */
13
14 #ifndef lint
15 static char sccsid[] = "@(#)chown.c 8.8 (Berkeley) 04/04/94";
16 #endif /* not lint */
17
18 #include <sys/param.h>
19 #include <sys/stat.h>
20
21 #include <ctype.h>
22 #include <dirent.h>
23 #include <err.h>
24 #include <errno.h>
25 #include <fts.h>
26 #include <grp.h>
27 #include <pwd.h>
28 #include <stdio.h>
29 #include <stdlib.h>
30 #include <string.h>
31 #include <unistd.h>
32
33 void a_gid __P((char *));
34 void a_uid __P((char *));
35 void chownerr __P((char *));
36 u_long id __P((char *, char *));
37 void usage __P((void));
38
39 uid_t uid;
40 gid_t gid;
41 int Rflag, ischown, fflag;
42 char *gname, *myname;
43
44 int
main(argc,argv)45 main(argc, argv)
46 int argc;
47 char *argv[];
48 {
49 FTS *ftsp;
50 FTSENT *p;
51 int Hflag, Lflag, Pflag, ch, fts_options, hflag, rval;
52 char *cp;
53
54 myname = (cp = rindex(*argv, '/')) ? cp + 1 : *argv;
55 ischown = myname[2] == 'o';
56
57 Hflag = Lflag = Pflag = hflag = 0;
58 while ((ch = getopt(argc, argv, "HLPRfh")) != EOF)
59 switch (ch) {
60 case 'H':
61 Hflag = 1;
62 Lflag = Pflag = 0;
63 break;
64 case 'L':
65 Lflag = 1;
66 Hflag = Pflag = 0;
67 break;
68 case 'P':
69 Pflag = 1;
70 Hflag = Lflag = 0;
71 break;
72 case 'R':
73 Rflag = 1;
74 break;
75 case 'f':
76 fflag = 1;
77 break;
78 case 'h':
79 /*
80 * In System V (and probably POSIX.2) the -h option
81 * causes chown/chgrp to change the owner/group of
82 * the symbolic link. 4.4BSD's symbolic links don't
83 * have owners/groups, so it's an undocumented noop.
84 * Do syntax checking, though.
85 */
86 hflag = 1;
87 break;
88 case '?':
89 default:
90 usage();
91 }
92 argv += optind;
93 argc -= optind;
94
95 if (argc < 2)
96 usage();
97
98 fts_options = FTS_PHYSICAL;
99 if (Rflag) {
100 if (hflag)
101 errx(1,
102 "the -R and -h options may not be specified together.");
103 if (Hflag)
104 fts_options |= FTS_COMFOLLOW;
105 if (Lflag) {
106 fts_options &= ~FTS_PHYSICAL;
107 fts_options |= FTS_LOGICAL;
108 }
109 }
110
111 uid = gid = -1;
112 if (ischown) {
113 #ifdef SUPPORT_DOT
114 if ((cp = strchr(*argv, '.')) != NULL) {
115 *cp++ = '\0';
116 a_gid(cp);
117 } else
118 #endif
119 if ((cp = strchr(*argv, ':')) != NULL) {
120 *cp++ = '\0';
121 a_gid(cp);
122 }
123 a_uid(*argv);
124 } else
125 a_gid(*argv);
126
127 if ((ftsp = fts_open(++argv, fts_options, 0)) == NULL)
128 err(1, NULL);
129
130 for (rval = 0; (p = fts_read(ftsp)) != NULL;) {
131 switch (p->fts_info) {
132 case FTS_D:
133 if (Rflag) /* Change it at FTS_DP. */
134 continue;
135 fts_set(ftsp, p, FTS_SKIP);
136 break;
137 case FTS_DNR: /* Warn, chown, continue. */
138 warnx("%s: %s", p->fts_path, strerror(p->fts_errno));
139 rval = 1;
140 break;
141 case FTS_ERR: /* Warn, continue. */
142 case FTS_NS:
143 warnx("%s: %s", p->fts_path, strerror(p->fts_errno));
144 rval = 1;
145 continue;
146 case FTS_SL: /* Ignore. */
147 case FTS_SLNONE:
148 /*
149 * The only symlinks that end up here are ones that
150 * don't point to anything and ones that we found
151 * doing a physical walk.
152 */
153 continue;
154 default:
155 break;
156 }
157 if (chown(p->fts_accpath, uid, gid) && !fflag) {
158 chownerr(p->fts_path);
159 rval = 1;
160 }
161 }
162 if (errno)
163 err(1, "fts_read");
164 exit(rval);
165 }
166
167 void
a_gid(s)168 a_gid(s)
169 char *s;
170 {
171 struct group *gr;
172
173 if (*s == '\0') /* Argument was "uid[:.]". */
174 return;
175 gname = s;
176 gid = ((gr = getgrnam(s)) == NULL) ? id(s, "group") : gr->gr_gid;
177 }
178
179 void
a_uid(s)180 a_uid(s)
181 char *s;
182 {
183 struct passwd *pw;
184
185 if (*s == '\0') /* Argument was "[:.]gid". */
186 return;
187 uid = ((pw = getpwnam(s)) == NULL) ? id(s, "user") : pw->pw_uid;
188 }
189
190 u_long
id(name,type)191 id(name, type)
192 char *name, *type;
193 {
194 u_long val;
195 char *ep;
196
197 /*
198 * XXX
199 * We know that uid_t's and gid_t's are unsigned longs.
200 */
201 errno = 0;
202 val = strtoul(name, &ep, 10);
203 if (errno)
204 err(1, "%s", name);
205 if (*ep != '\0')
206 errx(1, "%s: illegal %s name", name, type);
207 return (val);
208 }
209
210 void
chownerr(file)211 chownerr(file)
212 char *file;
213 {
214 static int euid = -1, ngroups = -1;
215 int groups[NGROUPS];
216
217 /* Check for chown without being root. */
218 if (errno != EPERM ||
219 uid != -1 && euid == -1 && (euid = geteuid()) != 0) {
220 if (fflag)
221 exit(0);
222 err(1, "%s", file);
223 }
224
225 /* Check group membership; kernel just returns EPERM. */
226 if (gid != -1 && ngroups == -1) {
227 ngroups = getgroups(NGROUPS, groups);
228 while (--ngroups >= 0 && gid != groups[ngroups]);
229 if (ngroups < 0) {
230 if (fflag)
231 exit(0);
232 errx(1, "you are not a member of group %s", gname);
233 }
234 }
235
236 if (!fflag)
237 warn("%s", file);
238 }
239
240 void
usage()241 usage()
242 {
243 (void)fprintf(stderr,
244 "usage: %s [-R [-H | -L | -P]] [-f] %s file ...\n",
245 myname, ischown ? "[owner][:group]" : "group");
246 exit(1);
247 }
248