xref: /original-bsd/usr.sbin/chown/chown.c (revision 7e5c8007)
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
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
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
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
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
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
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