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