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