xref: /minix3/sbin/chown/chown.c (revision e39e890e081e80fafba29a21d53b6984038a7aaa)
1*e39e890eSLionel Sambuc /*	$NetBSD: chown.c,v 1.8 2012/10/24 01:12:51 enami Exp $	*/
2*e39e890eSLionel Sambuc 
3*e39e890eSLionel Sambuc /*
4*e39e890eSLionel Sambuc  * Copyright (c) 1988, 1993, 1994, 2003
5*e39e890eSLionel Sambuc  *	The Regents of the University of California.  All rights reserved.
6*e39e890eSLionel Sambuc  *
7*e39e890eSLionel Sambuc  * Redistribution and use in source and binary forms, with or without
8*e39e890eSLionel Sambuc  * modification, are permitted provided that the following conditions
9*e39e890eSLionel Sambuc  * are met:
10*e39e890eSLionel Sambuc  * 1. Redistributions of source code must retain the above copyright
11*e39e890eSLionel Sambuc  *    notice, this list of conditions and the following disclaimer.
12*e39e890eSLionel Sambuc  * 2. Redistributions in binary form must reproduce the above copyright
13*e39e890eSLionel Sambuc  *    notice, this list of conditions and the following disclaimer in the
14*e39e890eSLionel Sambuc  *    documentation and/or other materials provided with the distribution.
15*e39e890eSLionel Sambuc  * 3. Neither the name of the University nor the names of its contributors
16*e39e890eSLionel Sambuc  *    may be used to endorse or promote products derived from this software
17*e39e890eSLionel Sambuc  *    without specific prior written permission.
18*e39e890eSLionel Sambuc  *
19*e39e890eSLionel Sambuc  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
20*e39e890eSLionel Sambuc  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
21*e39e890eSLionel Sambuc  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
22*e39e890eSLionel Sambuc  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
23*e39e890eSLionel Sambuc  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
24*e39e890eSLionel Sambuc  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
25*e39e890eSLionel Sambuc  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
26*e39e890eSLionel Sambuc  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
27*e39e890eSLionel Sambuc  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
28*e39e890eSLionel Sambuc  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
29*e39e890eSLionel Sambuc  * SUCH DAMAGE.
30*e39e890eSLionel Sambuc  */
31*e39e890eSLionel Sambuc 
32*e39e890eSLionel Sambuc #include <sys/cdefs.h>
33*e39e890eSLionel Sambuc #ifndef lint
34*e39e890eSLionel Sambuc __COPYRIGHT("@(#) Copyright (c) 1988, 1993, 1994, 2003\
35*e39e890eSLionel Sambuc  The Regents of the University of California.  All rights reserved.");
36*e39e890eSLionel Sambuc #endif /* not lint */
37*e39e890eSLionel Sambuc 
38*e39e890eSLionel Sambuc #ifndef lint
39*e39e890eSLionel Sambuc #if 0
40*e39e890eSLionel Sambuc static char sccsid[] = "@(#)chown.c	8.8 (Berkeley) 4/4/94";
41*e39e890eSLionel Sambuc #else
42*e39e890eSLionel Sambuc __RCSID("$NetBSD: chown.c,v 1.8 2012/10/24 01:12:51 enami Exp $");
43*e39e890eSLionel Sambuc #endif
44*e39e890eSLionel Sambuc #endif /* not lint */
45*e39e890eSLionel Sambuc 
46*e39e890eSLionel Sambuc #include <sys/types.h>
47*e39e890eSLionel Sambuc #include <sys/stat.h>
48*e39e890eSLionel Sambuc 
49*e39e890eSLionel Sambuc #include <ctype.h>
50*e39e890eSLionel Sambuc #include <dirent.h>
51*e39e890eSLionel Sambuc #include <err.h>
52*e39e890eSLionel Sambuc #include <errno.h>
53*e39e890eSLionel Sambuc #include <locale.h>
54*e39e890eSLionel Sambuc #include <fts.h>
55*e39e890eSLionel Sambuc #include <grp.h>
56*e39e890eSLionel Sambuc #include <pwd.h>
57*e39e890eSLionel Sambuc #include <stdio.h>
58*e39e890eSLionel Sambuc #include <stdlib.h>
59*e39e890eSLionel Sambuc #include <string.h>
60*e39e890eSLionel Sambuc #include <unistd.h>
61*e39e890eSLionel Sambuc #include <getopt.h>
62*e39e890eSLionel Sambuc 
63*e39e890eSLionel Sambuc static void	a_gid(const char *);
64*e39e890eSLionel Sambuc static void	a_uid(const char *);
65*e39e890eSLionel Sambuc static id_t	id(const char *, const char *);
66*e39e890eSLionel Sambuc __dead static void	usage(void);
67*e39e890eSLionel Sambuc 
68*e39e890eSLionel Sambuc static uid_t uid;
69*e39e890eSLionel Sambuc static gid_t gid;
70*e39e890eSLionel Sambuc static int ischown;
71*e39e890eSLionel Sambuc static const char *myname;
72*e39e890eSLionel Sambuc 
73*e39e890eSLionel Sambuc struct option chown_longopts[] = {
74*e39e890eSLionel Sambuc 	{ "reference",		required_argument,	0,
75*e39e890eSLionel Sambuc 						1 },
76*e39e890eSLionel Sambuc 	{ NULL,			0,			0,
77*e39e890eSLionel Sambuc 						0 },
78*e39e890eSLionel Sambuc };
79*e39e890eSLionel Sambuc 
80*e39e890eSLionel Sambuc int
main(int argc,char ** argv)81*e39e890eSLionel Sambuc main(int argc, char **argv)
82*e39e890eSLionel Sambuc {
83*e39e890eSLionel Sambuc 	FTS *ftsp;
84*e39e890eSLionel Sambuc 	FTSENT *p;
85*e39e890eSLionel Sambuc 	int Hflag, Lflag, Rflag, ch, fflag, fts_options, hflag, rval, vflag;
86*e39e890eSLionel Sambuc 	char *cp, *reference;
87*e39e890eSLionel Sambuc 	int (*change_owner)(const char *, uid_t, gid_t);
88*e39e890eSLionel Sambuc 
89*e39e890eSLionel Sambuc 	setprogname(*argv);
90*e39e890eSLionel Sambuc 
91*e39e890eSLionel Sambuc 	(void)setlocale(LC_ALL, "");
92*e39e890eSLionel Sambuc 
93*e39e890eSLionel Sambuc 	myname = getprogname();
94*e39e890eSLionel Sambuc 	ischown = (myname[2] == 'o');
95*e39e890eSLionel Sambuc 	reference = NULL;
96*e39e890eSLionel Sambuc 
97*e39e890eSLionel Sambuc 	Hflag = Lflag = Rflag = fflag = hflag = vflag = 0;
98*e39e890eSLionel Sambuc 	while ((ch = getopt_long(argc, argv, "HLPRfhv",
99*e39e890eSLionel Sambuc 	    chown_longopts, NULL)) != -1)
100*e39e890eSLionel Sambuc 		switch (ch) {
101*e39e890eSLionel Sambuc 		case 1:
102*e39e890eSLionel Sambuc 			reference = optarg;
103*e39e890eSLionel Sambuc 			break;
104*e39e890eSLionel Sambuc 		case 'H':
105*e39e890eSLionel Sambuc 			Hflag = 1;
106*e39e890eSLionel Sambuc 			Lflag = 0;
107*e39e890eSLionel Sambuc 			break;
108*e39e890eSLionel Sambuc 		case 'L':
109*e39e890eSLionel Sambuc 			Lflag = 1;
110*e39e890eSLionel Sambuc 			Hflag = 0;
111*e39e890eSLionel Sambuc 			break;
112*e39e890eSLionel Sambuc 		case 'P':
113*e39e890eSLionel Sambuc 			Hflag = Lflag = 0;
114*e39e890eSLionel Sambuc 			break;
115*e39e890eSLionel Sambuc 		case 'R':
116*e39e890eSLionel Sambuc 			Rflag = 1;
117*e39e890eSLionel Sambuc 			break;
118*e39e890eSLionel Sambuc 		case 'f':
119*e39e890eSLionel Sambuc 			fflag = 1;
120*e39e890eSLionel Sambuc 			break;
121*e39e890eSLionel Sambuc 		case 'h':
122*e39e890eSLionel Sambuc 			/*
123*e39e890eSLionel Sambuc 			 * In System V the -h option causes chown/chgrp to
124*e39e890eSLionel Sambuc 			 * change the owner/group of the symbolic link.
125*e39e890eSLionel Sambuc 			 * 4.4BSD's symbolic links didn't have owners/groups,
126*e39e890eSLionel Sambuc 			 * so it was an undocumented noop.
127*e39e890eSLionel Sambuc 			 * In NetBSD 1.3, lchown(2) is introduced.
128*e39e890eSLionel Sambuc 			 */
129*e39e890eSLionel Sambuc 			hflag = 1;
130*e39e890eSLionel Sambuc 			break;
131*e39e890eSLionel Sambuc 		case 'v':
132*e39e890eSLionel Sambuc 			vflag = 1;
133*e39e890eSLionel Sambuc 			break;
134*e39e890eSLionel Sambuc 		case '?':
135*e39e890eSLionel Sambuc 		default:
136*e39e890eSLionel Sambuc 			usage();
137*e39e890eSLionel Sambuc 		}
138*e39e890eSLionel Sambuc 	argv += optind;
139*e39e890eSLionel Sambuc 	argc -= optind;
140*e39e890eSLionel Sambuc 
141*e39e890eSLionel Sambuc 	if (argc == 0 || (argc == 1 && reference == NULL))
142*e39e890eSLionel Sambuc 		usage();
143*e39e890eSLionel Sambuc 
144*e39e890eSLionel Sambuc 	fts_options = FTS_PHYSICAL;
145*e39e890eSLionel Sambuc 	if (Rflag) {
146*e39e890eSLionel Sambuc 		if (Hflag)
147*e39e890eSLionel Sambuc 			fts_options |= FTS_COMFOLLOW;
148*e39e890eSLionel Sambuc 		if (Lflag) {
149*e39e890eSLionel Sambuc 			if (hflag)
150*e39e890eSLionel Sambuc 				errx(EXIT_FAILURE,
151*e39e890eSLionel Sambuc 				    "the -L and -h options "
152*e39e890eSLionel Sambuc 				    "may not be specified together.");
153*e39e890eSLionel Sambuc 			fts_options &= ~FTS_PHYSICAL;
154*e39e890eSLionel Sambuc 			fts_options |= FTS_LOGICAL;
155*e39e890eSLionel Sambuc 		}
156*e39e890eSLionel Sambuc 	} else if (!hflag)
157*e39e890eSLionel Sambuc 		fts_options |= FTS_COMFOLLOW;
158*e39e890eSLionel Sambuc 
159*e39e890eSLionel Sambuc 	uid = (uid_t)-1;
160*e39e890eSLionel Sambuc 	gid = (gid_t)-1;
161*e39e890eSLionel Sambuc 	if (reference == NULL) {
162*e39e890eSLionel Sambuc 		if (ischown) {
163*e39e890eSLionel Sambuc 			if ((cp = strchr(*argv, ':')) != NULL) {
164*e39e890eSLionel Sambuc 				*cp++ = '\0';
165*e39e890eSLionel Sambuc 				a_gid(cp);
166*e39e890eSLionel Sambuc 			}
167*e39e890eSLionel Sambuc #ifdef SUPPORT_DOT
168*e39e890eSLionel Sambuc 			else if ((cp = strrchr(*argv, '.')) != NULL) {
169*e39e890eSLionel Sambuc 				if (uid_from_user(*argv, &uid) == -1) {
170*e39e890eSLionel Sambuc 					*cp++ = '\0';
171*e39e890eSLionel Sambuc 					a_gid(cp);
172*e39e890eSLionel Sambuc 				}
173*e39e890eSLionel Sambuc 			}
174*e39e890eSLionel Sambuc #endif
175*e39e890eSLionel Sambuc 			a_uid(*argv);
176*e39e890eSLionel Sambuc 		} else
177*e39e890eSLionel Sambuc 			a_gid(*argv);
178*e39e890eSLionel Sambuc 		argv++;
179*e39e890eSLionel Sambuc 	} else {
180*e39e890eSLionel Sambuc 		struct stat st;
181*e39e890eSLionel Sambuc 
182*e39e890eSLionel Sambuc 		if (stat(reference, &st) == -1)
183*e39e890eSLionel Sambuc 			err(EXIT_FAILURE, "Cannot stat `%s'", reference);
184*e39e890eSLionel Sambuc 		if (ischown)
185*e39e890eSLionel Sambuc 			uid = st.st_uid;
186*e39e890eSLionel Sambuc 		gid = st.st_gid;
187*e39e890eSLionel Sambuc 	}
188*e39e890eSLionel Sambuc 
189*e39e890eSLionel Sambuc 	if ((ftsp = fts_open(argv, fts_options, NULL)) == NULL)
190*e39e890eSLionel Sambuc 		err(EXIT_FAILURE, "fts_open");
191*e39e890eSLionel Sambuc 
192*e39e890eSLionel Sambuc 	for (rval = EXIT_SUCCESS; (p = fts_read(ftsp)) != NULL;) {
193*e39e890eSLionel Sambuc 		change_owner = chown;
194*e39e890eSLionel Sambuc 		switch (p->fts_info) {
195*e39e890eSLionel Sambuc 		case FTS_D:
196*e39e890eSLionel Sambuc 			if (!Rflag)		/* Change it at FTS_DP. */
197*e39e890eSLionel Sambuc 				fts_set(ftsp, p, FTS_SKIP);
198*e39e890eSLionel Sambuc 			continue;
199*e39e890eSLionel Sambuc 		case FTS_DNR:			/* Warn, chown, continue. */
200*e39e890eSLionel Sambuc 			warnx("%s: %s", p->fts_path, strerror(p->fts_errno));
201*e39e890eSLionel Sambuc 			rval = EXIT_FAILURE;
202*e39e890eSLionel Sambuc 			break;
203*e39e890eSLionel Sambuc 		case FTS_ERR:			/* Warn, continue. */
204*e39e890eSLionel Sambuc 		case FTS_NS:
205*e39e890eSLionel Sambuc 			warnx("%s: %s", p->fts_path, strerror(p->fts_errno));
206*e39e890eSLionel Sambuc 			rval = EXIT_FAILURE;
207*e39e890eSLionel Sambuc 			continue;
208*e39e890eSLionel Sambuc 		case FTS_SL:			/* Ignore unless -h. */
209*e39e890eSLionel Sambuc 			/*
210*e39e890eSLionel Sambuc 			 * All symlinks we found while doing a physical
211*e39e890eSLionel Sambuc 			 * walk end up here.
212*e39e890eSLionel Sambuc 			 */
213*e39e890eSLionel Sambuc 			if (!hflag)
214*e39e890eSLionel Sambuc 				continue;
215*e39e890eSLionel Sambuc 			/*
216*e39e890eSLionel Sambuc 			 * Note that if we follow a symlink, fts_info is
217*e39e890eSLionel Sambuc 			 * not FTS_SL but FTS_F or whatever.  And we should
218*e39e890eSLionel Sambuc 			 * use lchown only for FTS_SL and should use chown
219*e39e890eSLionel Sambuc 			 * for others.
220*e39e890eSLionel Sambuc 			 */
221*e39e890eSLionel Sambuc 			change_owner = lchown;
222*e39e890eSLionel Sambuc 			break;
223*e39e890eSLionel Sambuc 		case FTS_SLNONE:		/* Ignore. */
224*e39e890eSLionel Sambuc 			/*
225*e39e890eSLionel Sambuc 			 * The only symlinks that end up here are ones that
226*e39e890eSLionel Sambuc 			 * don't point to anything.  Note that if we are
227*e39e890eSLionel Sambuc 			 * doing a phisycal walk, we never reach here unless
228*e39e890eSLionel Sambuc 			 * we asked to follow explicitly.
229*e39e890eSLionel Sambuc 			 */
230*e39e890eSLionel Sambuc 			continue;
231*e39e890eSLionel Sambuc 		default:
232*e39e890eSLionel Sambuc 			break;
233*e39e890eSLionel Sambuc 		}
234*e39e890eSLionel Sambuc 
235*e39e890eSLionel Sambuc 		if ((*change_owner)(p->fts_accpath, uid, gid) && !fflag) {
236*e39e890eSLionel Sambuc 			warn("%s", p->fts_path);
237*e39e890eSLionel Sambuc 			rval = EXIT_FAILURE;
238*e39e890eSLionel Sambuc 		} else {
239*e39e890eSLionel Sambuc 			if (vflag)
240*e39e890eSLionel Sambuc 				printf("%s\n", p->fts_path);
241*e39e890eSLionel Sambuc 		}
242*e39e890eSLionel Sambuc 	}
243*e39e890eSLionel Sambuc 	if (errno)
244*e39e890eSLionel Sambuc 		err(EXIT_FAILURE, "fts_read");
245*e39e890eSLionel Sambuc 	exit(rval);
246*e39e890eSLionel Sambuc 	/* NOTREACHED */
247*e39e890eSLionel Sambuc }
248*e39e890eSLionel Sambuc 
249*e39e890eSLionel Sambuc static void
a_gid(const char * s)250*e39e890eSLionel Sambuc a_gid(const char *s)
251*e39e890eSLionel Sambuc {
252*e39e890eSLionel Sambuc 	struct group *gr;
253*e39e890eSLionel Sambuc 
254*e39e890eSLionel Sambuc 	if (*s == '\0')			/* Argument was "uid[:.]". */
255*e39e890eSLionel Sambuc 		return;
256*e39e890eSLionel Sambuc 	gr = *s == '#' ? NULL : getgrnam(s);
257*e39e890eSLionel Sambuc 	if (gr == NULL)
258*e39e890eSLionel Sambuc 		gid = id(s, "group");
259*e39e890eSLionel Sambuc 	else
260*e39e890eSLionel Sambuc 		gid = gr->gr_gid;
261*e39e890eSLionel Sambuc 	return;
262*e39e890eSLionel Sambuc }
263*e39e890eSLionel Sambuc 
264*e39e890eSLionel Sambuc static void
a_uid(const char * s)265*e39e890eSLionel Sambuc a_uid(const char *s)
266*e39e890eSLionel Sambuc {
267*e39e890eSLionel Sambuc 	if (*s == '\0')			/* Argument was "[:.]gid". */
268*e39e890eSLionel Sambuc 		return;
269*e39e890eSLionel Sambuc 	if (*s == '#' || uid_from_user(s, &uid) == -1) {
270*e39e890eSLionel Sambuc 		uid = id(s, "user");
271*e39e890eSLionel Sambuc 	}
272*e39e890eSLionel Sambuc 	return;
273*e39e890eSLionel Sambuc }
274*e39e890eSLionel Sambuc 
275*e39e890eSLionel Sambuc static id_t
id(const char * name,const char * type)276*e39e890eSLionel Sambuc id(const char *name, const char *type)
277*e39e890eSLionel Sambuc {
278*e39e890eSLionel Sambuc 	id_t val;
279*e39e890eSLionel Sambuc 	char *ep;
280*e39e890eSLionel Sambuc 
281*e39e890eSLionel Sambuc 	errno = 0;
282*e39e890eSLionel Sambuc 	if (*name == '#')
283*e39e890eSLionel Sambuc 		name++;
284*e39e890eSLionel Sambuc 	val = (id_t)strtoul(name, &ep, 10);
285*e39e890eSLionel Sambuc 	if (errno)
286*e39e890eSLionel Sambuc 		err(EXIT_FAILURE, "%s", name);
287*e39e890eSLionel Sambuc 	if (*ep != '\0')
288*e39e890eSLionel Sambuc 		errx(EXIT_FAILURE, "%s: invalid %s name", name, type);
289*e39e890eSLionel Sambuc 	return (val);
290*e39e890eSLionel Sambuc }
291*e39e890eSLionel Sambuc 
292*e39e890eSLionel Sambuc static void
usage(void)293*e39e890eSLionel Sambuc usage(void)
294*e39e890eSLionel Sambuc {
295*e39e890eSLionel Sambuc 
296*e39e890eSLionel Sambuc 	(void)fprintf(stderr,
297*e39e890eSLionel Sambuc 	    "Usage: %s [-R [-H | -L | -P]] [-fhv] %s file ...\n"
298*e39e890eSLionel Sambuc 	    "\t%s [-R [-H | -L | -P]] [-fhv] --reference=rfile file ...\n",
299*e39e890eSLionel Sambuc 	    myname, ischown ? "owner:group|owner|:group" : "group",
300*e39e890eSLionel Sambuc 	    myname);
301*e39e890eSLionel Sambuc 	exit(EXIT_FAILURE);
302*e39e890eSLionel Sambuc }
303