8322 nl: misleading-indentation
[unleashed/tickless.git] / usr / src / cmd / chgrp / chgrp.c
bloba1fe7fefbf316201dfea0c46555757c88ac112b1
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.
27 * Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T
28 * All Rights Reserved
32 * University Copyright- Copyright (c) 1982, 1986, 1988
33 * The Regents of the University of California
34 * All Rights Reserved
36 * University Acknowledgment- Portions of this document are derived from
37 * software developed by the University of California, Berkeley, and its
38 * contributors.
41 #pragma ident "%Z%%M% %I% %E% SMI"
44 * chgrp [-fhR] gid file ...
45 * chgrp -R [-f] [-H|-L|-P] gid file ...
46 * chgrp -s [-fhR] groupsid file ...
47 * chgrp -s -R [-f] [-H|-L|-P] groupsid file ...
50 #include <stdio.h>
51 #include <ctype.h>
52 #include <sys/types.h>
53 #include <sys/stat.h>
54 #include <sys/avl.h>
55 #include <grp.h>
56 #include <dirent.h>
57 #include <unistd.h>
58 #include <stdlib.h>
59 #include <locale.h>
60 #include <libcmdutils.h>
61 #include <errno.h>
62 #include <strings.h>
63 #include <aclutils.h>
65 static struct group *gr;
66 static struct stat stbuf;
67 static struct stat stbuf2;
68 static gid_t gid;
69 static int hflag = 0,
70 fflag = 0,
71 rflag = 0,
72 Hflag = 0,
73 Lflag = 0,
74 Pflag = 0,
75 sflag = 0;
76 static int status = 0; /* total number of errors received */
78 static avl_tree_t *tree; /* search tree to store inode data */
80 static void usage(void);
81 static int isnumber(char *);
82 static int Perror(char *);
83 static void chgrpr(char *, gid_t);
85 #ifdef XPG4
87 * Check to see if we are to follow symlinks specified on the command line.
88 * This assumes we've already checked to make sure neither -h or -P was
89 * specified, so we are just looking to see if -R -L, or -R -H was specified,
90 * or, since -R has the same behavior as -R -L, if -R was specified by itself.
91 * Therefore, all we really need to check for is if -R was specified.
93 #define FOLLOW_CL_LINKS (rflag)
94 #else
96 * Check to see if we are to follow symlinks specified on the command line.
97 * This assumes we've already checked to make sure neither -h or -P was
98 * specified, so we are just looking to see if -R -L, or -R -H was specified.
99 * Note: -R by itself will change the group of a directory referenced by a
100 * symlink however it will not follow the symlink to any other part of the
101 * file hierarchy.
103 #define FOLLOW_CL_LINKS (rflag && (Hflag || Lflag))
104 #endif
106 #ifdef XPG4
108 * Follow symlinks when traversing directories. Since -R behaves the
109 * same as -R -L, we always want to follow symlinks to other parts
110 * of the file hierarchy unless -H was specified.
112 #define FOLLOW_D_LINKS (!Hflag)
113 #else
115 * Follow symlinks when traversing directories. Only follow symlinks
116 * to other parts of the file hierarchy if -L was specified.
118 #define FOLLOW_D_LINKS (Lflag)
119 #endif
121 #define CHOWN(f, u, g) if (chown(f, u, g) < 0) { \
122 status += Perror(f); \
125 #define LCHOWN(f, u, g) if (lchown(f, u, g) < 0) { \
126 status += Perror(f); \
129 * We're ignoring errors here because preserving the SET[UG]ID bits is just
130 * a courtesy. This is only used on directories.
132 #define SETUGID_PRESERVE(dir, mode) \
133 if (((mode) & (S_ISGID|S_ISUID)) != 0) \
134 (void) chmod((dir), (mode) & ~S_IFMT)
136 extern int optind;
140 main(int argc, char *argv[])
142 int c;
144 /* set the locale for only the messages system (all else is clean) */
146 (void) setlocale(LC_ALL, "");
147 #if !defined(TEXT_DOMAIN) /* Should be defined by cc -D */
148 #define TEXT_DOMAIN "SYS_TEST" /* Use this only if it weren't */
149 #endif
150 (void) textdomain(TEXT_DOMAIN);
152 while ((c = getopt(argc, argv, "RhfHLPs")) != EOF)
153 switch (c) {
154 case 'R':
155 rflag++;
156 break;
157 case 'h':
158 hflag++;
159 break;
160 case 'f':
161 fflag++;
162 break;
163 case 'H':
165 * If more than one of -H, -L, and -P
166 * are specified, only the last option
167 * specified determines the behavior of
168 * chgrp. In addition, make [-H|-L]
169 * mutually exclusive of -h.
171 Lflag = Pflag = 0;
172 Hflag++;
173 break;
174 case 'L':
175 Hflag = Pflag = 0;
176 Lflag++;
177 break;
178 case 'P':
179 Hflag = Lflag = 0;
180 Pflag++;
181 break;
182 case 's':
183 sflag++;
184 break;
185 default:
186 usage();
190 * Check for sufficient arguments
191 * or a usage error.
193 argc -= optind;
194 argv = &argv[optind];
196 if ((argc < 2) ||
197 ((Hflag || Lflag || Pflag) && !rflag) ||
198 ((Hflag || Lflag || Pflag) && hflag)) {
199 usage();
202 if (sflag) {
203 if (sid_to_id(argv[0], B_FALSE, &gid)) {
204 (void) fprintf(stderr, gettext(
205 "chgrp: invalid group sid %s\n"), argv[0]);
206 exit(2);
208 } else if ((gr = getgrnam(argv[0])) != NULL) {
209 gid = gr->gr_gid;
210 } else {
211 if (isnumber(argv[0])) {
212 errno = 0;
213 /* gid is an int */
214 gid = (gid_t)strtoul(argv[0], NULL, 10);
215 if (errno != 0) {
216 if (errno == ERANGE) {
217 (void) fprintf(stderr, gettext(
218 "chgrp: group id is too large\n"));
219 exit(2);
220 } else {
221 (void) fprintf(stderr, gettext(
222 "chgrp: invalid group id\n"));
223 exit(2);
226 } else {
227 (void) fprintf(stderr, "chgrp: ");
228 (void) fprintf(stderr, gettext("unknown group: %s\n"),
229 argv[0]);
230 exit(2);
234 for (c = 1; c < argc; c++) {
235 tree = NULL;
236 if (lstat(argv[c], &stbuf) < 0) {
237 status += Perror(argv[c]);
238 continue;
240 if (rflag && ((stbuf.st_mode & S_IFMT) == S_IFLNK)) {
241 if (hflag || Pflag) {
243 * Change the group id of the symbolic link
244 * specified on the command line.
245 * Don't follow the symbolic link to
246 * any other part of the file hierarchy.
248 LCHOWN(argv[c], -1, gid);
249 } else {
250 if (stat(argv[c], &stbuf2) < 0) {
251 status += Perror(argv[c]);
252 continue;
255 * We know that we are to change the
256 * group of the file referenced by the
257 * symlink specified on the command line.
258 * Now check to see if we are to follow
259 * the symlink to any other part of the
260 * file hierarchy.
262 if (FOLLOW_CL_LINKS) {
263 if ((stbuf2.st_mode & S_IFMT)
264 == S_IFDIR) {
266 * We are following symlinks so
267 * traverse into the directory.
268 * Add this node to the search
269 * tree so we don't get into an
270 * endless loop.
272 if (add_tnode(&tree,
273 stbuf2.st_dev,
274 stbuf2.st_ino) == 1) {
275 chgrpr(argv[c], gid);
277 * Try to restore the
278 * SET[UG]ID bits.
280 SETUGID_PRESERVE(
281 argv[c],
282 stbuf2.st_mode &
283 ~S_IFMT);
284 } else {
286 * Error occurred.
287 * rc can't be 0
288 * as this is the first
289 * node to be added to
290 * the search tree.
292 status += Perror(
293 argv[c]);
295 } else {
297 * Change the group id of the
298 * file referenced by the
299 * symbolic link.
301 CHOWN(argv[c], -1, gid);
303 } else {
305 * Change the group id of the file
306 * referenced by the symbolic link.
308 CHOWN(argv[c], -1, gid);
310 if ((stbuf2.st_mode & S_IFMT)
311 == S_IFDIR) {
312 /* Reset the SET[UG]ID bits. */
313 SETUGID_PRESERVE(argv[c],
314 stbuf2.st_mode & ~S_IFMT);
318 } else if (rflag && ((stbuf.st_mode & S_IFMT) == S_IFDIR)) {
320 * Add this node to the search tree so we don't
321 * get into a endless loop.
323 if (add_tnode(&tree, stbuf.st_dev,
324 stbuf.st_ino) == 1) {
325 chgrpr(argv[c], gid);
327 /* Restore the SET[UG]ID bits. */
328 SETUGID_PRESERVE(argv[c],
329 stbuf.st_mode & ~S_IFMT);
330 } else {
332 * An error occurred while trying
333 * to add the node to the tree.
334 * Continue on with next file
335 * specified. Note: rc shouldn't
336 * be 0 as this was the first node
337 * being added to the search tree.
339 status += Perror(argv[c]);
341 } else {
342 if (hflag || Pflag) {
343 LCHOWN(argv[c], -1, gid);
344 } else {
345 CHOWN(argv[c], -1, gid);
347 /* If a directory, reset the SET[UG]ID bits. */
348 if ((stbuf.st_mode & S_IFMT) == S_IFDIR) {
349 SETUGID_PRESERVE(argv[c],
350 stbuf.st_mode & ~S_IFMT);
354 return (status);
358 * chgrpr() - recursive chown()
360 * Recursively chowns the input directory then its contents. rflag must
361 * have been set if chgrpr() is called. The input directory should not
362 * be a sym link (this is handled in the calling routine). In
363 * addition, the calling routine should have already added the input
364 * directory to the search tree so we do not get into endless loops.
365 * Note: chgrpr() doesn't need a return value as errors are reported
366 * through the global "status" variable.
368 static void
369 chgrpr(char *dir, gid_t gid)
371 struct dirent *dp;
372 DIR *dirp;
373 struct stat st, st2;
374 char savedir[1024];
376 if (getcwd(savedir, 1024) == 0) {
377 (void) fprintf(stderr, "chgrp: ");
378 (void) fprintf(stderr, gettext("%s\n"), savedir);
379 exit(255);
383 * Attempt to chown the directory, however don't return if we
384 * can't as we still may be able to chown the contents of the
385 * directory. Note: the calling routine resets the SUID bits
386 * on this directory so we don't have to perform an extra 'stat'.
388 CHOWN(dir, -1, gid);
390 if (chdir(dir) < 0) {
391 status += Perror(dir);
392 return;
394 if ((dirp = opendir(".")) == NULL) {
395 status += Perror(dir);
396 return;
398 for (dp = readdir(dirp); dp != NULL; dp = readdir(dirp)) {
399 if ((strcmp(dp->d_name, ".") == 0) ||
400 (strcmp(dp->d_name, "..") == 0)) {
401 continue; /* skip "." and ".." */
403 if (lstat(dp->d_name, &st) < 0) {
404 status += Perror(dp->d_name);
405 continue;
407 if ((st.st_mode & S_IFMT) == S_IFLNK) {
408 if (hflag || Pflag) {
410 * Change the group id of the symbolic link
411 * encountered while traversing the
412 * directory. Don't follow the symbolic
413 * link to any other part of the file
414 * hierarchy.
416 LCHOWN(dp->d_name, -1, gid);
417 } else {
418 if (stat(dp->d_name, &st2) < 0) {
419 status += Perror(dp->d_name);
420 continue;
423 * We know that we are to change the
424 * group of the file referenced by the
425 * symlink encountered while traversing
426 * the directory. Now check to see if we
427 * are to follow the symlink to any other
428 * part of the file hierarchy.
430 if (FOLLOW_D_LINKS) {
431 if ((st2.st_mode & S_IFMT) == S_IFDIR) {
433 * We are following symlinks so
434 * traverse into the directory.
435 * Add this node to the search
436 * tree so we don't get into an
437 * endless loop.
439 int rc;
440 if ((rc = add_tnode(&tree,
441 st2.st_dev,
442 st2.st_ino)) == 1) {
443 chgrpr(dp->d_name, gid);
446 * Restore SET[UG]ID
447 * bits.
449 SETUGID_PRESERVE(
450 dp->d_name,
451 st2.st_mode &
452 ~S_IFMT);
453 } else if (rc == 0) {
454 /* already visited */
455 continue;
456 } else {
458 * An error occurred
459 * while trying to add
460 * the node to the tree.
462 status += Perror(
463 dp->d_name);
464 continue;
466 } else {
468 * Change the group id of the
469 * file referenced by the
470 * symbolic link.
472 CHOWN(dp->d_name, -1, gid);
475 } else {
477 * Change the group id of the file
478 * referenced by the symbolic link.
480 CHOWN(dp->d_name, -1, gid);
482 if ((st2.st_mode & S_IFMT) == S_IFDIR) {
483 /* Restore SET[UG]ID bits. */
484 SETUGID_PRESERVE(dp->d_name,
485 st2.st_mode & ~S_IFMT);
489 } else if ((st.st_mode & S_IFMT) == S_IFDIR) {
491 * Add this node to the search tree so we don't
492 * get into a endless loop.
494 int rc;
495 if ((rc = add_tnode(&tree, st.st_dev,
496 st.st_ino)) == 1) {
497 chgrpr(dp->d_name, gid);
499 /* Restore the SET[UG]ID bits. */
500 SETUGID_PRESERVE(dp->d_name,
501 st.st_mode & ~S_IFMT);
502 } else if (rc == 0) {
503 /* already visited */
504 continue;
505 } else {
507 * An error occurred while trying
508 * to add the node to the search tree.
510 status += Perror(dp->d_name);
511 continue;
513 } else {
514 CHOWN(dp->d_name, -1, gid);
517 (void) closedir(dirp);
518 if (chdir(savedir) < 0) {
519 (void) fprintf(stderr, "chgrp: ");
520 (void) fprintf(stderr, gettext("can't change back to %s\n"),
521 savedir);
522 exit(255);
526 static int
527 isnumber(char *s)
529 int c;
531 while ((c = *s++) != '\0')
532 if (!isdigit(c))
533 return (0);
534 return (1);
538 static int
539 Perror(char *s)
541 if (!fflag) {
542 (void) fprintf(stderr, "chgrp: ");
543 perror(s);
545 return (!fflag);
549 static void
550 usage(void)
552 (void) fprintf(stderr, gettext(
553 "usage:\n"
554 "\tchgrp [-fhR] group file ...\n"
555 "\tchgrp -R [-f] [-H|-L|-P] group file ...\n"
556 "\tchgrp -s [-fhR] groupsid file ...\n"
557 "\tchgrp -s -R [-f] [-H|-L|-P] groupsid file ...\n"));
558 exit(2);