POSIX 1003.2B/D9 symbolic links
[unix-history] / usr / src / usr.sbin / chown / chown.c
... / ...
CommitLineData
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
9static 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
15static char sccsid[] = "@(#)chown.c 8.2 (Berkeley) %G%";
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
33void a_gid __P((char *));
34void a_uid __P((char *));
35void chownerr __P((char *));
36u_long id __P((char *, char *));
37void usage __P((void));
38
39uid_t uid;
40gid_t gid;
41int Rflag, ischown, fflag;
42char *gname, *myname;
43
44int
45main(argc, argv)
46 int argc;
47 char *argv[];
48{
49 extern int optind;
50 FTS *ftsp;
51 FTSENT *p;
52 int Hflag, Lflag, Pflag, ch, fts_options, hflag, rval;
53 char *cp;
54
55 myname = (cp = rindex(*argv, '/')) ? cp + 1 : *argv;
56 ischown = myname[2] == 'o';
57
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;
73 case 'R':
74 Rflag = 1;
75 break;
76 case 'f':
77 fflag = 1;
78 break;
79 case 'h':
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 */
87 hflag = 1;
88 break;
89 case '?':
90 default:
91 usage();
92 }
93 argv += optind;
94 argc -= optind;
95
96 if (argc < 2)
97 usage();
98
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
112 uid = gid = -1;
113 if (ischown) {
114#ifdef SUPPORT_DOT
115 if ((cp = strchr(*argv, '.')) != NULL) {
116 *cp++ = '\0';
117 a_gid(cp);
118 } else
119#endif
120 if ((cp = strchr(*argv, ':')) != NULL) {
121 *cp++ = '\0';
122 a_gid(cp);
123 }
124 a_uid(*argv);
125 } else
126 a_gid(*argv);
127
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. */
135 continue;
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;
146 continue;
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 */
154 continue;
155 }
156 if (chown(p->fts_accpath, uid, gid) && !fflag) {
157 chownerr(p->fts_path);
158 rval = 1;
159 }
160 }
161 exit(rval);
162}
163
164void
165a_gid(s)
166 char *s;
167{
168 struct group *gr;
169
170 if (*s == '\0') /* Argument was "uid[:.]". */
171 return;
172 gname = s;
173 gid = ((gr = getgrnam(s)) == NULL) ? id(s, "group") : gr->gr_gid;
174}
175
176void
177a_uid(s)
178 char *s;
179{
180 struct passwd *pw;
181
182 if (*s == '\0') /* Argument was "[:.]gid". */
183 return;
184 uid = ((pw = getpwnam(s)) == NULL) ? id(s, "user") : pw->pw_uid;
185}
186
187u_long
188id(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
207void
208chownerr(file)
209 char *file;
210{
211 static int euid = -1, ngroups = -1;
212 int groups[NGROUPS];
213
214 /* Check for chown without being root. */
215 if (errno != EPERM ||
216 uid != -1 && euid == -1 && (euid = geteuid()) != 0) {
217 if (fflag)
218 exit(0);
219 err(1, "%s", file);
220 }
221
222 /* Check group membership; kernel just returns EPERM. */
223 if (gid != -1 && ngroups == -1) {
224 ngroups = getgroups(NGROUPS, groups);
225 while (--ngroups >= 0 && gid != groups[ngroups]);
226 if (ngroups < 0) {
227 if (fflag)
228 exit(0);
229 errx(1, "you are not a member of group %s", gname);
230 }
231 }
232
233 if (!fflag)
234 warn("%s", file);
235}
236
237void
238usage()
239{
240 (void)fprintf(stderr,
241 "usage: %s [-R [-H | -L | -P]] [-f] %s file ...\n",
242 myname, ischown ? "[owner][:group]" : "group");
243 exit(1);
244}