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