xref: /original-bsd/usr.sbin/chown/chown.c (revision 4b77e540)
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.7 (Berkeley) 04/01/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
45 main(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_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_DNR:			/* Warn, chown, continue. */
139 			warnx("%s: %s", p->fts_path, strerror(p->fts_errno));
140 			rval = 1;
141 			break;
142 		case FTS_ERR:			/* Warn, continue. */
143 		case FTS_NS:
144 			warnx("%s: %s", p->fts_path, strerror(p->fts_errno));
145 			rval = 1;
146 			continue;
147 		case FTS_SL:			/* Ignore. */
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 		default:
156 			break;
157 		}
158 		if (chown(p->fts_accpath, uid, gid) && !fflag) {
159 			chownerr(p->fts_path);
160 			rval = 1;
161 		}
162 	}
163 	if (errno)
164 		err(1, "fts_read");
165 	exit(rval);
166 }
167 
168 void
169 a_gid(s)
170 	char *s;
171 {
172 	struct group *gr;
173 
174 	if (*s == '\0')			/* Argument was "uid[:.]". */
175 		return;
176 	gname = s;
177 	gid = ((gr = getgrnam(s)) == NULL) ? id(s, "group") : gr->gr_gid;
178 }
179 
180 void
181 a_uid(s)
182 	char *s;
183 {
184 	struct passwd *pw;
185 
186 	if (*s == '\0')			/* Argument was "[:.]gid". */
187 		return;
188 	uid = ((pw = getpwnam(s)) == NULL) ? id(s, "user") : pw->pw_uid;
189 }
190 
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
212 chownerr(file)
213 	char *file;
214 {
215 	static int euid = -1, ngroups = -1;
216 	int groups[NGROUPS];
217 
218 	/* Check for chown without being root. */
219 	if (errno != EPERM ||
220 	    uid != -1 && euid == -1 && (euid = geteuid()) != 0) {
221 		if (fflag)
222 			exit(0);
223 		err(1, "%s", file);
224 	}
225 
226 	/* Check group membership; kernel just returns EPERM. */
227 	if (gid != -1 && ngroups == -1) {
228 		ngroups = getgroups(NGROUPS, groups);
229 		while (--ngroups >= 0 && gid != groups[ngroups]);
230 		if (ngroups < 0) {
231 			if (fflag)
232 				exit(0);
233 			errx(1, "you are not a member of group %s", gname);
234 		}
235 	}
236 
237 	if (!fflag)
238 		warn("%s", file);
239 }
240 
241 void
242 usage()
243 {
244 	(void)fprintf(stderr,
245 	    "usage: %s [-R [-H | -L | -P]] [-f] %s file ...\n",
246 	    myname, ischown ? "[owner][:group]" : "group");
247 	exit(1);
248 }
249