Merge commit '00f1a4f432b3d8aad1aa270e91c44c57f03ef407'
[unleashed.git] / usr / src / cmd / chown / chown.c
blobd9609a51ca374f1d8278714fd1835778dfc2d681
1 /*
2 * CDDL HEADER START
4 * The contents of this file are subject to the terms of the
5 * Common Development and Distribution License (the "License").
6 * You may not use this file except in compliance with the License.
8 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9 * or http://www.opensolaris.org/os/licensing.
10 * See the License for the specific language governing permissions
11 * and limitations under the License.
13 * When distributing Covered Code, include this CDDL HEADER in each
14 * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15 * If applicable, add the following below this CDDL HEADER, with the
16 * fields enclosed by brackets "[]" replaced with your own identifying
17 * information: Portions Copyright [yyyy] [name of copyright owner]
19 * CDDL HEADER END
22 * Copyright 2008 Sun Microsystems, Inc. All rights reserved.
23 * Use is subject to license terms.
26 /* Copyright (c) 1983, 1984, 1985, 1986, 1987, 1988, 1989 AT&T */
27 /* All Rights Reserved */
30 * Portions of this source code were derived from Berkeley 4.3 BSD
31 * under license from the Regents of the University of California.
34 #pragma ident "%Z%%M% %I% %E% SMI"
37 * chown [-fhR] uid[:gid] file ...
38 * chown -R [-f] [-H|-L|-P] uid[:gid] file ...
39 * chown -s [-fhR] ownersid[:groupsid] file ...
40 * chown -s -R [-f] [-H|-L|-P] ownersid[:groupsid] file ...
43 #include <stdio.h>
44 #include <stdlib.h>
45 #include <ctype.h>
46 #include <sys/types.h>
47 #include <dirent.h>
48 #include <string.h>
49 #include <sys/stat.h>
50 #include <sys/avl.h>
51 #include <pwd.h>
52 #include <grp.h>
53 #include <unistd.h>
54 #include <locale.h>
55 #include <errno.h>
56 #include <libcmdutils.h>
57 #include <aclutils.h>
59 static struct passwd *pwd;
60 static struct group *grp;
61 static struct stat stbuf;
62 static uid_t uid = (uid_t)-1;
63 static gid_t gid = (gid_t)-1;
64 static int status = 0; /* total number of errors received */
65 static int hflag = 0,
66 rflag = 0,
67 fflag = 0,
68 Hflag = 0,
69 Lflag = 0,
70 Pflag = 0,
71 sflag = 0;
72 static avl_tree_t *tree;
74 static int Perror(char *);
75 static int isnumber(char *);
76 static void chownr(char *, uid_t, gid_t);
77 static void usage();
79 #ifdef XPG4
81 * Check to see if we are to follow symlinks specified on the command line.
82 * This assumes we've already checked to make sure neither -h or -P was
83 * specified, so we are just looking to see if -R -H, or -R -L was specified,
84 * or, since -R has the same behavior as -R -L, if -R was specified by itself.
85 * Therefore, all we really need to check for is if -R was specified.
87 #define FOLLOW_CL_LINKS (rflag)
88 #else
90 * Check to see if we are to follow symlinks specified on the command line.
91 * This assumes we've already checked to make sure neither -h or -P was
92 * specified, so we are just looking to see if -R -H, or -R -L was specified.
93 * Note: -R by itself will change the ownership of a directory referenced by a
94 * symlink however it will now follow the symlink to any other part of the
95 * file hierarchy.
97 #define FOLLOW_CL_LINKS (rflag && (Hflag || Lflag))
98 #endif
100 #ifdef XPG4
102 * Follow symlinks when traversing directories. Since -R behaves the
103 * same as -R -L, we always want to follow symlinks to other parts
104 * of the file hierarchy unless -H was specified.
106 #define FOLLOW_D_LINKS (!Hflag)
107 #else
109 * Follow symlinks when traversing directories. Only follow symlinks
110 * to other parts of the file hierarchy if -L was specified.
112 #define FOLLOW_D_LINKS (Lflag)
113 #endif
115 #define CHOWN(f, u, g) if (chown(f, u, g) < 0) { \
116 status += Perror(f); \
118 #define LCHOWN(f, u, g) if (lchown(f, u, g) < 0) { \
119 status += Perror(f); \
124 main(int argc, char *argv[])
126 int c;
127 int ch;
128 char *grpp; /* pointer to group name arg */
129 extern int optind;
130 int errflg = 0;
132 (void) setlocale(LC_ALL, "");
133 #if !defined(TEXT_DOMAIN) /* Should be defined by cc -D */
134 #define TEXT_DOMAIN "SYS_TEST" /* Use this only if it weren't */
135 #endif
136 (void) textdomain(TEXT_DOMAIN);
138 while ((ch = getopt(argc, argv, "hRfHLPs")) != EOF) {
139 switch (ch) {
140 case 'h':
141 hflag++;
142 break;
144 case 'R':
145 rflag++;
146 break;
148 case 'f':
149 fflag++;
150 break;
152 case 'H':
154 * If more than one of -H, -L, and -P
155 * are specified, only the last option
156 * specified determines the behavior of
157 * chown.
159 Lflag = Pflag = 0;
160 Hflag++;
161 break;
163 case 'L':
164 Hflag = Pflag = 0;
165 Lflag++;
166 break;
168 case 'P':
169 Hflag = Lflag = 0;
170 Pflag++;
171 break;
173 case 's':
174 sflag++;
175 break;
177 default:
178 errflg++;
179 break;
183 * Check for sufficient arguments
184 * or a usage error.
187 argc -= optind;
188 argv = &argv[optind];
190 if (errflg || (argc < 2) ||
191 ((Hflag || Lflag || Pflag) && !rflag) ||
192 ((Hflag || Lflag || Pflag) && hflag)) {
193 usage();
197 * POSIX.2
198 * Check for owner[:group]
200 if ((grpp = strchr(argv[0], ':')) != NULL) {
201 *grpp++ = 0;
203 if (sflag) {
204 if (sid_to_id(grpp, B_FALSE, &gid)) {
205 (void) fprintf(stderr, gettext(
206 "chown: invalid owning group sid %s\n"),
207 grpp);
208 exit(2);
210 } else if ((grp = getgrnam(grpp)) != NULL) {
211 gid = grp->gr_gid;
212 } else {
213 if (isnumber(grpp)) {
214 errno = 0;
215 gid = (gid_t)strtoul(grpp, NULL, 10);
216 if (errno != 0) {
217 if (errno == ERANGE) {
218 (void) fprintf(stderr, gettext(
219 "chown: group id too large\n"));
220 exit(2);
221 } else {
222 (void) fprintf(stderr, gettext(
223 "chown: invalid group id\n"));
224 exit(2);
227 } else {
228 (void) fprintf(stderr, gettext(
229 "chown: unknown group id %s\n"), grpp);
230 exit(2);
235 if (sflag) {
236 if (sid_to_id(argv[0], B_TRUE, &uid)) {
237 (void) fprintf(stderr, gettext(
238 "chown: invalid owner sid %s\n"), argv[0]);
239 exit(2);
241 } else if ((pwd = getpwnam(argv[0])) != NULL) {
242 uid = pwd->pw_uid;
243 } else {
244 if (isnumber(argv[0])) {
245 errno = 0;
246 uid = (uid_t)strtoul(argv[0], NULL, 10);
247 if (errno != 0) {
248 if (errno == ERANGE) {
249 (void) fprintf(stderr, gettext(
250 "chown: user id too large\n"));
251 exit(2);
252 } else {
253 (void) fprintf(stderr, gettext(
254 "chown: invalid user id\n"));
255 exit(2);
258 } else {
259 (void) fprintf(stderr, gettext(
260 "chown: unknown user id %s\n"), argv[0]);
261 exit(2);
265 for (c = 1; c < argc; c++) {
266 tree = NULL;
267 if (lstat(argv[c], &stbuf) < 0) {
268 status += Perror(argv[c]);
269 continue;
271 if (rflag && ((stbuf.st_mode & S_IFMT) == S_IFLNK)) {
272 if (hflag || Pflag) {
274 * Change the ownership of the symlink
275 * specified on the command line.
276 * Don't follow the symbolic link to
277 * any other part of the file hierarchy.
279 LCHOWN(argv[c], uid, gid);
280 } else {
281 struct stat stbuf2;
282 if (stat(argv[c], &stbuf2) < 0) {
283 status += Perror(argv[c]);
284 continue;
287 * We know that we are to change the
288 * ownership of the file referenced by the
289 * symlink specified on the command line.
290 * Now check to see if we are to follow
291 * the symlink to any other part of the
292 * file hierarchy.
294 if (FOLLOW_CL_LINKS) {
295 if ((stbuf2.st_mode & S_IFMT)
296 == S_IFDIR) {
298 * We are following symlinks so
299 * traverse into the directory.
300 * Add this node to the search
301 * tree so we don't get into an
302 * endless loop.
304 if (add_tnode(&tree,
305 stbuf2.st_dev,
306 stbuf2.st_ino) == 1) {
307 chownr(argv[c],
308 uid, gid);
309 } else {
311 * Error occurred.
312 * rc can't be 0
313 * as this is the first
314 * node to be added to
315 * the search tree.
317 status += Perror(
318 argv[c]);
320 } else {
322 * Change the user ID of the
323 * file referenced by the
324 * symlink.
326 CHOWN(argv[c], uid, gid);
328 } else {
330 * Change the user ID of the file
331 * referenced by the symbolic link.
333 CHOWN(argv[c], uid, gid);
336 } else if (rflag && ((stbuf.st_mode & S_IFMT) == S_IFDIR)) {
338 * Add this node to the search tree so we don't
339 * get into a endless loop.
341 if (add_tnode(&tree, stbuf.st_dev,
342 stbuf.st_ino) == 1) {
343 chownr(argv[c], uid, gid);
344 } else {
346 * An error occurred while trying
347 * to add the node to the tree.
348 * Continue on with next file
349 * specified. Note: rc shouldn't
350 * be 0 as this was the first node
351 * being added to the search tree.
353 status += Perror(argv[c]);
355 } else if (hflag || Pflag) {
356 LCHOWN(argv[c], uid, gid);
357 } else {
358 CHOWN(argv[c], uid, gid);
361 return (status);
365 * chownr() - recursive chown()
367 * Recursively chowns the input directory then its contents. rflag must
368 * have been set if chownr() is called. The input directory should not
369 * be a sym link (this is handled in the calling routine). In
370 * addition, the calling routine should have already added the input
371 * directory to the search tree so we do not get into endless loops.
372 * Note: chownr() doesn't need a return value as errors are reported
373 * through the global "status" variable.
375 static void
376 chownr(char *dir, uid_t uid, gid_t gid)
378 DIR *dirp;
379 struct dirent *dp;
380 struct stat st, st2;
381 char savedir[1024];
383 if (getcwd(savedir, 1024) == NULL) {
384 (void) Perror("getcwd");
385 exit(255);
389 * Attempt to chown the directory, however don't return if we
390 * can't as we still may be able to chown the contents of the
391 * directory. Note: the calling routine resets the SUID bits
392 * on this directory so we don't have to perform an extra 'stat'.
394 CHOWN(dir, uid, gid);
396 if (chdir(dir) < 0) {
397 status += Perror(dir);
398 return;
400 if ((dirp = opendir(".")) == NULL) {
401 status += Perror(dir);
402 return;
404 for (dp = readdir(dirp); dp != NULL; dp = readdir(dirp)) {
405 if (strcmp(dp->d_name, ".") == 0 || /* skip . and .. */
406 strcmp(dp->d_name, "..") == 0) {
407 continue;
409 if (lstat(dp->d_name, &st) < 0) {
410 status += Perror(dp->d_name);
411 continue;
413 if ((st.st_mode & S_IFMT) == S_IFLNK) {
414 if (hflag || Pflag) {
416 * Change the ownership of the symbolic link
417 * encountered while traversing the
418 * directory. Don't follow the symbolic
419 * link to any other part of the file
420 * hierarchy.
422 LCHOWN(dp->d_name, uid, gid);
423 } else {
424 if (stat(dp->d_name, &st2) < 0) {
425 status += Perror(dp->d_name);
426 continue;
429 * We know that we are to change the
430 * ownership of the file referenced by the
431 * symlink encountered while traversing
432 * the directory. Now check to see if we
433 * are to follow the symlink to any other
434 * part of the file hierarchy.
436 if (FOLLOW_D_LINKS) {
437 if ((st2.st_mode & S_IFMT) == S_IFDIR) {
439 * We are following symlinks so
440 * traverse into the directory.
441 * Add this node to the search
442 * tree so we don't get into an
443 * endless loop.
445 int rc;
446 if ((rc = add_tnode(&tree,
447 st2.st_dev,
448 st2.st_ino)) == 1) {
449 chownr(dp->d_name,
450 uid, gid);
451 } else if (rc == 0) {
452 /* already visited */
453 continue;
454 } else {
456 * An error occurred
457 * while trying to add
458 * the node to the tree.
460 status += Perror(
461 dp->d_name);
462 continue;
464 } else {
466 * Change the user id of the
467 * file referenced by the
468 * symbolic link.
470 CHOWN(dp->d_name, uid, gid);
472 } else {
474 * Change the user id of the file
475 * referenced by the symbolic link.
477 CHOWN(dp->d_name, uid, gid);
480 } else if ((st.st_mode & S_IFMT) == S_IFDIR) {
482 * Add this node to the search tree so we don't
483 * get into a endless loop.
485 int rc;
486 if ((rc = add_tnode(&tree, st.st_dev,
487 st.st_ino)) == 1) {
488 chownr(dp->d_name, uid, gid);
489 } else if (rc == 0) {
490 /* already visited */
491 continue;
492 } else {
494 * An error occurred while trying
495 * to add the node to the search tree.
497 status += Perror(dp->d_name);
498 continue;
500 } else {
501 CHOWN(dp->d_name, uid, gid);
505 (void) closedir(dirp);
506 if (chdir(savedir) < 0) {
507 (void) fprintf(stderr, gettext(
508 "chown: can't change back to %s\n"), savedir);
509 exit(255);
513 static int
514 isnumber(char *s)
516 int c;
518 while ((c = *s++) != '\0')
519 if (!isdigit(c))
520 return (0);
521 return (1);
524 static int
525 Perror(char *s)
527 if (!fflag) {
528 (void) fprintf(stderr, "chown: ");
529 perror(s);
531 return (!fflag);
534 static void
535 usage()
537 (void) fprintf(stderr, gettext(
538 "usage:\n"
539 "\tchown [-fhR] owner[:group] file...\n"
540 "\tchown -R [-f] [-H|-L|-P] owner[:group] file...\n"
541 "\tchown -s [-fhR] ownersid[:groupsid] file...\n"
542 "\tchown -s -R [-f] [-H|-L|-P] ownersid[:groupsid] file...\n"));
543 exit(2);