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