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 | |
a61ba0b2 | 15 | static char sccsid[] = "@(#)chown.c 8.8 (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 | |
da2fcce6 | 99 | fts_options = FTS_PHYSICAL; |
c6bbda50 KB |
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; | |
c3729774 | 138 | case FTS_DNR: /* Warn, chown, continue. */ |
8cd86009 | 139 | warnx("%s: %s", p->fts_path, strerror(p->fts_errno)); |
c3729774 KB |
140 | rval = 1; |
141 | break; | |
142 | case FTS_ERR: /* Warn, continue. */ | |
da2fcce6 | 143 | case FTS_NS: |
8cd86009 | 144 | warnx("%s: %s", p->fts_path, strerror(p->fts_errno)); |
c6bbda50 | 145 | rval = 1; |
1c8f2f3c | 146 | continue; |
c3729774 | 147 | case FTS_SL: /* Ignore. */ |
c6bbda50 KB |
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; |
c3729774 KB |
155 | default: |
156 | break; | |
a7bd3fcd | 157 | } |
c6bbda50 | 158 | if (chown(p->fts_accpath, uid, gid) && !fflag) { |
7c4a71e0 | 159 | chownerr(p->fts_path); |
c6bbda50 KB |
160 | rval = 1; |
161 | } | |
c439a634 | 162 | } |
84f17227 KB |
163 | if (errno) |
164 | err(1, "fts_read"); | |
c6bbda50 | 165 | exit(rval); |
69e53de7 | 166 | } |
702e3fe5 | 167 | |
c6bbda50 | 168 | void |
0594ed17 | 169 | a_gid(s) |
c6bbda50 | 170 | char *s; |
702e3fe5 | 171 | { |
0594ed17 | 172 | struct group *gr; |
7445173f | 173 | |
c6bbda50 | 174 | if (*s == '\0') /* Argument was "uid[:.]". */ |
7445173f | 175 | return; |
0594ed17 | 176 | gname = s; |
c6bbda50 | 177 | gid = ((gr = getgrnam(s)) == NULL) ? id(s, "group") : gr->gr_gid; |
c76e0466 SL |
178 | } |
179 | ||
c6bbda50 | 180 | void |
0594ed17 | 181 | a_uid(s) |
c6bbda50 | 182 | char *s; |
c76e0466 | 183 | { |
0594ed17 | 184 | struct passwd *pw; |
c76e0466 | 185 | |
c6bbda50 | 186 | if (*s == '\0') /* Argument was "[:.]gid". */ |
7445173f | 187 | return; |
c6bbda50 | 188 | uid = ((pw = getpwnam(s)) == NULL) ? id(s, "user") : pw->pw_uid; |
c76e0466 SL |
189 | } |
190 | ||
c6bbda50 KB |
191 | u_long |
192 | id(name, type) | |
193 | char *name, *type; | |
194 | { | |
195 | u_long val; | |
196 | char *ep; | |
197 | ||
198 | /* | |
199 | * XXX | |
200 | * We know that uid_t's and gid_t's are unsigned longs. | |
201 | */ | |
202 | errno = 0; | |
203 | val = strtoul(name, &ep, 10); | |
204 | if (errno) | |
205 | err(1, "%s", name); | |
206 | if (*ep != '\0') | |
207 | errx(1, "%s: illegal %s name", name, type); | |
208 | return (val); | |
209 | } | |
210 | ||
211 | void | |
2a151187 KB |
212 | chownerr(file) |
213 | char *file; | |
214 | { | |
215 | static int euid = -1, ngroups = -1; | |
c6bbda50 | 216 | int groups[NGROUPS]; |
2a151187 | 217 | |
c6bbda50 KB |
218 | /* Check for chown without being root. */ |
219 | if (errno != EPERM || | |
220 | uid != -1 && euid == -1 && (euid = geteuid()) != 0) { | |
2a151187 KB |
221 | if (fflag) |
222 | exit(0); | |
c6bbda50 | 223 | err(1, "%s", file); |
2a151187 | 224 | } |
2a151187 | 225 | |
c6bbda50 KB |
226 | /* Check group membership; kernel just returns EPERM. */ |
227 | if (gid != -1 && ngroups == -1) { | |
2a151187 KB |
228 | ngroups = getgroups(NGROUPS, groups); |
229 | while (--ngroups >= 0 && gid != groups[ngroups]); | |
230 | if (ngroups < 0) { | |
231 | if (fflag) | |
232 | exit(0); | |
c6bbda50 | 233 | errx(1, "you are not a member of group %s", gname); |
2a151187 KB |
234 | } |
235 | } | |
2a151187 | 236 | |
c6bbda50 KB |
237 | if (!fflag) |
238 | warn("%s", file); | |
7445173f | 239 | } |
c76e0466 | 240 | |
c6bbda50 | 241 | void |
7445173f KB |
242 | usage() |
243 | { | |
c6bbda50 KB |
244 | (void)fprintf(stderr, |
245 | "usage: %s [-R [-H | -L | -P]] [-f] %s file ...\n", | |
246 | myname, ischown ? "[owner][:group]" : "group"); | |
a7bd3fcd | 247 | exit(1); |
702e3fe5 | 248 | } |