1*0Sstevel@tonic-gate /* 2*0Sstevel@tonic-gate * CDDL HEADER START 3*0Sstevel@tonic-gate * 4*0Sstevel@tonic-gate * The contents of this file are subject to the terms of the 5*0Sstevel@tonic-gate * Common Development and Distribution License, Version 1.0 only 6*0Sstevel@tonic-gate * (the "License"). You may not use this file except in compliance 7*0Sstevel@tonic-gate * with the License. 8*0Sstevel@tonic-gate * 9*0Sstevel@tonic-gate * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 10*0Sstevel@tonic-gate * or http://www.opensolaris.org/os/licensing. 11*0Sstevel@tonic-gate * See the License for the specific language governing permissions 12*0Sstevel@tonic-gate * and limitations under the License. 13*0Sstevel@tonic-gate * 14*0Sstevel@tonic-gate * When distributing Covered Code, include this CDDL HEADER in each 15*0Sstevel@tonic-gate * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 16*0Sstevel@tonic-gate * If applicable, add the following below this CDDL HEADER, with the 17*0Sstevel@tonic-gate * fields enclosed by brackets "[]" replaced with your own identifying 18*0Sstevel@tonic-gate * information: Portions Copyright [yyyy] [name of copyright owner] 19*0Sstevel@tonic-gate * 20*0Sstevel@tonic-gate * CDDL HEADER END 21*0Sstevel@tonic-gate */ 22*0Sstevel@tonic-gate /* 23*0Sstevel@tonic-gate * Copyright 2004 Sun Microsystems, Inc. All rights reserved. 24*0Sstevel@tonic-gate * Use is subject to license terms. 25*0Sstevel@tonic-gate */ 26*0Sstevel@tonic-gate 27*0Sstevel@tonic-gate /* 28*0Sstevel@tonic-gate * Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T 29*0Sstevel@tonic-gate * All Rights Reserved 30*0Sstevel@tonic-gate */ 31*0Sstevel@tonic-gate 32*0Sstevel@tonic-gate /* 33*0Sstevel@tonic-gate * University Copyright- Copyright (c) 1982, 1986, 1988 34*0Sstevel@tonic-gate * The Regents of the University of California 35*0Sstevel@tonic-gate * All Rights Reserved 36*0Sstevel@tonic-gate * 37*0Sstevel@tonic-gate * University Acknowledgment- Portions of this document are derived from 38*0Sstevel@tonic-gate * software developed by the University of California, Berkeley, and its 39*0Sstevel@tonic-gate * contributors. 40*0Sstevel@tonic-gate */ 41*0Sstevel@tonic-gate 42*0Sstevel@tonic-gate #pragma ident "%Z%%M% %I% %E% SMI" 43*0Sstevel@tonic-gate 44*0Sstevel@tonic-gate /* 45*0Sstevel@tonic-gate * chgrp [-fhR] gid file ... 46*0Sstevel@tonic-gate * chgrp -R [-f] [-H|-L|-P] gid file ... 47*0Sstevel@tonic-gate */ 48*0Sstevel@tonic-gate 49*0Sstevel@tonic-gate #include <stdio.h> 50*0Sstevel@tonic-gate #include <ctype.h> 51*0Sstevel@tonic-gate #include <sys/types.h> 52*0Sstevel@tonic-gate #include <sys/stat.h> 53*0Sstevel@tonic-gate #include <sys/avl.h> 54*0Sstevel@tonic-gate #include <grp.h> 55*0Sstevel@tonic-gate #include <dirent.h> 56*0Sstevel@tonic-gate #include <unistd.h> 57*0Sstevel@tonic-gate #include <stdlib.h> 58*0Sstevel@tonic-gate #include <locale.h> 59*0Sstevel@tonic-gate #include <libcmdutils.h> 60*0Sstevel@tonic-gate #include <errno.h> 61*0Sstevel@tonic-gate #include <strings.h> 62*0Sstevel@tonic-gate 63*0Sstevel@tonic-gate static struct group *gr; 64*0Sstevel@tonic-gate static struct stat stbuf; 65*0Sstevel@tonic-gate static struct stat stbuf2; 66*0Sstevel@tonic-gate static gid_t gid; 67*0Sstevel@tonic-gate static int hflag = 0, 68*0Sstevel@tonic-gate fflag = 0, 69*0Sstevel@tonic-gate rflag = 0, 70*0Sstevel@tonic-gate Hflag = 0, 71*0Sstevel@tonic-gate Lflag = 0, 72*0Sstevel@tonic-gate Pflag = 0; 73*0Sstevel@tonic-gate static int status = 0; /* total number of errors received */ 74*0Sstevel@tonic-gate 75*0Sstevel@tonic-gate static avl_tree_t *tree; /* search tree to store inode data */ 76*0Sstevel@tonic-gate 77*0Sstevel@tonic-gate static void usage(void); 78*0Sstevel@tonic-gate static int isnumber(char *); 79*0Sstevel@tonic-gate static int Perror(char *); 80*0Sstevel@tonic-gate static void chgrpr(char *, gid_t); 81*0Sstevel@tonic-gate 82*0Sstevel@tonic-gate #ifdef XPG4 83*0Sstevel@tonic-gate /* 84*0Sstevel@tonic-gate * Check to see if we are to follow symlinks specified on the command line. 85*0Sstevel@tonic-gate * This assumes we've already checked to make sure neither -h or -P was 86*0Sstevel@tonic-gate * specified, so we are just looking to see if -R -L, or -R -H was specified, 87*0Sstevel@tonic-gate * or, since -R has the same behavior as -R -L, if -R was specified by itself. 88*0Sstevel@tonic-gate * Therefore, all we really need to check for is if -R was specified. 89*0Sstevel@tonic-gate */ 90*0Sstevel@tonic-gate #define FOLLOW_CL_LINKS (rflag) 91*0Sstevel@tonic-gate #else 92*0Sstevel@tonic-gate /* 93*0Sstevel@tonic-gate * Check to see if we are to follow symlinks specified on the command line. 94*0Sstevel@tonic-gate * This assumes we've already checked to make sure neither -h or -P was 95*0Sstevel@tonic-gate * specified, so we are just looking to see if -R -L, or -R -H was specified. 96*0Sstevel@tonic-gate * Note: -R by itself will change the group of a directory referenced by a 97*0Sstevel@tonic-gate * symlink however it will not follow the symlink to any other part of the 98*0Sstevel@tonic-gate * file hierarchy. 99*0Sstevel@tonic-gate */ 100*0Sstevel@tonic-gate #define FOLLOW_CL_LINKS (rflag && (Hflag || Lflag)) 101*0Sstevel@tonic-gate #endif 102*0Sstevel@tonic-gate 103*0Sstevel@tonic-gate #ifdef XPG4 104*0Sstevel@tonic-gate /* 105*0Sstevel@tonic-gate * Follow symlinks when traversing directories. Since -R behaves the 106*0Sstevel@tonic-gate * same as -R -L, we always want to follow symlinks to other parts 107*0Sstevel@tonic-gate * of the file hierarchy unless -H was specified. 108*0Sstevel@tonic-gate */ 109*0Sstevel@tonic-gate #define FOLLOW_D_LINKS (!Hflag) 110*0Sstevel@tonic-gate #else 111*0Sstevel@tonic-gate /* 112*0Sstevel@tonic-gate * Follow symlinks when traversing directories. Only follow symlinks 113*0Sstevel@tonic-gate * to other parts of the file hierarchy if -L was specified. 114*0Sstevel@tonic-gate */ 115*0Sstevel@tonic-gate #define FOLLOW_D_LINKS (Lflag) 116*0Sstevel@tonic-gate #endif 117*0Sstevel@tonic-gate 118*0Sstevel@tonic-gate #define CHOWN(f, u, g) if (chown(f, u, g) < 0) { \ 119*0Sstevel@tonic-gate status += Perror(f); \ 120*0Sstevel@tonic-gate } 121*0Sstevel@tonic-gate 122*0Sstevel@tonic-gate #define LCHOWN(f, u, g) if (lchown(f, u, g) < 0) { \ 123*0Sstevel@tonic-gate status += Perror(f); \ 124*0Sstevel@tonic-gate } 125*0Sstevel@tonic-gate /* 126*0Sstevel@tonic-gate * We're ignoring errors here because preserving the SET[UG]ID bits is just 127*0Sstevel@tonic-gate * a courtesy. This is only used on directories. 128*0Sstevel@tonic-gate */ 129*0Sstevel@tonic-gate #define SETUGID_PRESERVE(dir, mode) \ 130*0Sstevel@tonic-gate if (((mode) & (S_ISGID|S_ISUID)) != 0) \ 131*0Sstevel@tonic-gate (void) chmod((dir), (mode) & ~S_IFMT) 132*0Sstevel@tonic-gate 133*0Sstevel@tonic-gate extern int optind; 134*0Sstevel@tonic-gate 135*0Sstevel@tonic-gate 136*0Sstevel@tonic-gate int 137*0Sstevel@tonic-gate main(int argc, char *argv[]) 138*0Sstevel@tonic-gate { 139*0Sstevel@tonic-gate int c; 140*0Sstevel@tonic-gate 141*0Sstevel@tonic-gate /* set the locale for only the messages system (all else is clean) */ 142*0Sstevel@tonic-gate 143*0Sstevel@tonic-gate (void) setlocale(LC_ALL, ""); 144*0Sstevel@tonic-gate #if !defined(TEXT_DOMAIN) /* Should be defined by cc -D */ 145*0Sstevel@tonic-gate #define TEXT_DOMAIN "SYS_TEST" /* Use this only if it weren't */ 146*0Sstevel@tonic-gate #endif 147*0Sstevel@tonic-gate (void) textdomain(TEXT_DOMAIN); 148*0Sstevel@tonic-gate 149*0Sstevel@tonic-gate while ((c = getopt(argc, argv, "RhfHLP")) != EOF) 150*0Sstevel@tonic-gate switch (c) { 151*0Sstevel@tonic-gate case 'R': 152*0Sstevel@tonic-gate rflag++; 153*0Sstevel@tonic-gate break; 154*0Sstevel@tonic-gate case 'h': 155*0Sstevel@tonic-gate hflag++; 156*0Sstevel@tonic-gate break; 157*0Sstevel@tonic-gate case 'f': 158*0Sstevel@tonic-gate fflag++; 159*0Sstevel@tonic-gate break; 160*0Sstevel@tonic-gate case 'H': 161*0Sstevel@tonic-gate /* 162*0Sstevel@tonic-gate * If more than one of -H, -L, and -P 163*0Sstevel@tonic-gate * are specified, only the last option 164*0Sstevel@tonic-gate * specified determines the behavior of 165*0Sstevel@tonic-gate * chgrp. In addition, make [-H|-L] 166*0Sstevel@tonic-gate * mutually exclusive of -h. 167*0Sstevel@tonic-gate */ 168*0Sstevel@tonic-gate Lflag = Pflag = 0; 169*0Sstevel@tonic-gate Hflag++; 170*0Sstevel@tonic-gate break; 171*0Sstevel@tonic-gate case 'L': 172*0Sstevel@tonic-gate Hflag = Pflag = 0; 173*0Sstevel@tonic-gate Lflag++; 174*0Sstevel@tonic-gate break; 175*0Sstevel@tonic-gate case 'P': 176*0Sstevel@tonic-gate Hflag = Lflag = 0; 177*0Sstevel@tonic-gate Pflag++; 178*0Sstevel@tonic-gate break; 179*0Sstevel@tonic-gate default: 180*0Sstevel@tonic-gate usage(); 181*0Sstevel@tonic-gate } 182*0Sstevel@tonic-gate 183*0Sstevel@tonic-gate /* 184*0Sstevel@tonic-gate * Check for sufficient arguments 185*0Sstevel@tonic-gate * or a usage error. 186*0Sstevel@tonic-gate */ 187*0Sstevel@tonic-gate argc -= optind; 188*0Sstevel@tonic-gate argv = &argv[optind]; 189*0Sstevel@tonic-gate 190*0Sstevel@tonic-gate if ((argc < 2) || 191*0Sstevel@tonic-gate ((Hflag || Lflag || Pflag) && !rflag) || 192*0Sstevel@tonic-gate ((Hflag || Lflag || Pflag) && hflag)) { 193*0Sstevel@tonic-gate usage(); 194*0Sstevel@tonic-gate } 195*0Sstevel@tonic-gate 196*0Sstevel@tonic-gate if (isnumber(argv[0])) { 197*0Sstevel@tonic-gate errno = 0; 198*0Sstevel@tonic-gate gid = (gid_t)strtol(argv[0], NULL, 10); /* gid is an int */ 199*0Sstevel@tonic-gate if (errno != 0) { 200*0Sstevel@tonic-gate if (errno == ERANGE) { 201*0Sstevel@tonic-gate (void) fprintf(stderr, gettext( 202*0Sstevel@tonic-gate "chgrp: group id is too large\n")); 203*0Sstevel@tonic-gate exit(2); 204*0Sstevel@tonic-gate } else { 205*0Sstevel@tonic-gate (void) fprintf(stderr, gettext( 206*0Sstevel@tonic-gate "chgrp: invalid group id\n")); 207*0Sstevel@tonic-gate exit(2); 208*0Sstevel@tonic-gate } 209*0Sstevel@tonic-gate } 210*0Sstevel@tonic-gate } else { 211*0Sstevel@tonic-gate if ((gr = getgrnam(argv[0])) == NULL) { 212*0Sstevel@tonic-gate (void) fprintf(stderr, "chgrp: "); 213*0Sstevel@tonic-gate (void) fprintf(stderr, gettext("unknown group: %s\n"), 214*0Sstevel@tonic-gate argv[0]); 215*0Sstevel@tonic-gate exit(2); 216*0Sstevel@tonic-gate } 217*0Sstevel@tonic-gate gid = gr->gr_gid; 218*0Sstevel@tonic-gate } 219*0Sstevel@tonic-gate 220*0Sstevel@tonic-gate for (c = 1; c < argc; c++) { 221*0Sstevel@tonic-gate tree = NULL; 222*0Sstevel@tonic-gate if (lstat(argv[c], &stbuf) < 0) { 223*0Sstevel@tonic-gate status += Perror(argv[c]); 224*0Sstevel@tonic-gate continue; 225*0Sstevel@tonic-gate } 226*0Sstevel@tonic-gate if (rflag && ((stbuf.st_mode & S_IFMT) == S_IFLNK)) { 227*0Sstevel@tonic-gate if (hflag || Pflag) { 228*0Sstevel@tonic-gate /* 229*0Sstevel@tonic-gate * Change the group id of the symbolic link 230*0Sstevel@tonic-gate * specified on the command line. 231*0Sstevel@tonic-gate * Don't follow the symbolic link to 232*0Sstevel@tonic-gate * any other part of the file hierarchy. 233*0Sstevel@tonic-gate */ 234*0Sstevel@tonic-gate LCHOWN(argv[c], -1, gid); 235*0Sstevel@tonic-gate } else { 236*0Sstevel@tonic-gate if (stat(argv[c], &stbuf2) < 0) { 237*0Sstevel@tonic-gate status += Perror(argv[c]); 238*0Sstevel@tonic-gate continue; 239*0Sstevel@tonic-gate } 240*0Sstevel@tonic-gate /* 241*0Sstevel@tonic-gate * We know that we are to change the 242*0Sstevel@tonic-gate * group of the file referenced by the 243*0Sstevel@tonic-gate * symlink specified on the command line. 244*0Sstevel@tonic-gate * Now check to see if we are to follow 245*0Sstevel@tonic-gate * the symlink to any other part of the 246*0Sstevel@tonic-gate * file hierarchy. 247*0Sstevel@tonic-gate */ 248*0Sstevel@tonic-gate if (FOLLOW_CL_LINKS) { 249*0Sstevel@tonic-gate if ((stbuf2.st_mode & S_IFMT) 250*0Sstevel@tonic-gate == S_IFDIR) { 251*0Sstevel@tonic-gate /* 252*0Sstevel@tonic-gate * We are following symlinks so 253*0Sstevel@tonic-gate * traverse into the directory. 254*0Sstevel@tonic-gate * Add this node to the search 255*0Sstevel@tonic-gate * tree so we don't get into an 256*0Sstevel@tonic-gate * endless loop. 257*0Sstevel@tonic-gate */ 258*0Sstevel@tonic-gate if (add_tnode(&tree, 259*0Sstevel@tonic-gate stbuf2.st_dev, 260*0Sstevel@tonic-gate stbuf2.st_ino) == 1) { 261*0Sstevel@tonic-gate chgrpr(argv[c], gid); 262*0Sstevel@tonic-gate /* 263*0Sstevel@tonic-gate * Try to restore the 264*0Sstevel@tonic-gate * SET[UG]ID bits. 265*0Sstevel@tonic-gate */ 266*0Sstevel@tonic-gate SETUGID_PRESERVE( 267*0Sstevel@tonic-gate argv[c], 268*0Sstevel@tonic-gate stbuf2.st_mode & 269*0Sstevel@tonic-gate ~S_IFMT); 270*0Sstevel@tonic-gate } else { 271*0Sstevel@tonic-gate /* 272*0Sstevel@tonic-gate * Error occurred. 273*0Sstevel@tonic-gate * rc can't be 0 274*0Sstevel@tonic-gate * as this is the first 275*0Sstevel@tonic-gate * node to be added to 276*0Sstevel@tonic-gate * the search tree. 277*0Sstevel@tonic-gate */ 278*0Sstevel@tonic-gate status += Perror( 279*0Sstevel@tonic-gate argv[c]); 280*0Sstevel@tonic-gate } 281*0Sstevel@tonic-gate } else { 282*0Sstevel@tonic-gate /* 283*0Sstevel@tonic-gate * Change the group id of the 284*0Sstevel@tonic-gate * file referenced by the 285*0Sstevel@tonic-gate * symbolic link. 286*0Sstevel@tonic-gate */ 287*0Sstevel@tonic-gate CHOWN(argv[c], -1, gid); 288*0Sstevel@tonic-gate } 289*0Sstevel@tonic-gate } else { 290*0Sstevel@tonic-gate /* 291*0Sstevel@tonic-gate * Change the group id of the file 292*0Sstevel@tonic-gate * referenced by the symbolic link. 293*0Sstevel@tonic-gate */ 294*0Sstevel@tonic-gate CHOWN(argv[c], -1, gid); 295*0Sstevel@tonic-gate 296*0Sstevel@tonic-gate if ((stbuf2.st_mode & S_IFMT) 297*0Sstevel@tonic-gate == S_IFDIR) { 298*0Sstevel@tonic-gate /* Reset the SET[UG]ID bits. */ 299*0Sstevel@tonic-gate SETUGID_PRESERVE(argv[c], 300*0Sstevel@tonic-gate stbuf2.st_mode & ~S_IFMT); 301*0Sstevel@tonic-gate } 302*0Sstevel@tonic-gate } 303*0Sstevel@tonic-gate } 304*0Sstevel@tonic-gate } else if (rflag && ((stbuf.st_mode & S_IFMT) == S_IFDIR)) { 305*0Sstevel@tonic-gate /* 306*0Sstevel@tonic-gate * Add this node to the search tree so we don't 307*0Sstevel@tonic-gate * get into a endless loop. 308*0Sstevel@tonic-gate */ 309*0Sstevel@tonic-gate if (add_tnode(&tree, stbuf.st_dev, 310*0Sstevel@tonic-gate stbuf.st_ino) == 1) { 311*0Sstevel@tonic-gate chgrpr(argv[c], gid); 312*0Sstevel@tonic-gate 313*0Sstevel@tonic-gate /* Restore the SET[UG]ID bits. */ 314*0Sstevel@tonic-gate SETUGID_PRESERVE(argv[c], 315*0Sstevel@tonic-gate stbuf.st_mode & ~S_IFMT); 316*0Sstevel@tonic-gate } else { 317*0Sstevel@tonic-gate /* 318*0Sstevel@tonic-gate * An error occurred while trying 319*0Sstevel@tonic-gate * to add the node to the tree. 320*0Sstevel@tonic-gate * Continue on with next file 321*0Sstevel@tonic-gate * specified. Note: rc shouldn't 322*0Sstevel@tonic-gate * be 0 as this was the first node 323*0Sstevel@tonic-gate * being added to the search tree. 324*0Sstevel@tonic-gate */ 325*0Sstevel@tonic-gate status += Perror(argv[c]); 326*0Sstevel@tonic-gate } 327*0Sstevel@tonic-gate } else { 328*0Sstevel@tonic-gate if (hflag || Pflag) { 329*0Sstevel@tonic-gate LCHOWN(argv[c], -1, gid); 330*0Sstevel@tonic-gate } else { 331*0Sstevel@tonic-gate CHOWN(argv[c], -1, gid); 332*0Sstevel@tonic-gate } 333*0Sstevel@tonic-gate /* If a directory, reset the SET[UG]ID bits. */ 334*0Sstevel@tonic-gate if ((stbuf.st_mode & S_IFMT) == S_IFDIR) { 335*0Sstevel@tonic-gate SETUGID_PRESERVE(argv[c], 336*0Sstevel@tonic-gate stbuf.st_mode & ~S_IFMT); 337*0Sstevel@tonic-gate } 338*0Sstevel@tonic-gate } 339*0Sstevel@tonic-gate } 340*0Sstevel@tonic-gate return (status); 341*0Sstevel@tonic-gate } 342*0Sstevel@tonic-gate 343*0Sstevel@tonic-gate /* 344*0Sstevel@tonic-gate * chgrpr() - recursive chown() 345*0Sstevel@tonic-gate * 346*0Sstevel@tonic-gate * Recursively chowns the input directory then its contents. rflag must 347*0Sstevel@tonic-gate * have been set if chgrpr() is called. The input directory should not 348*0Sstevel@tonic-gate * be a sym link (this is handled in the calling routine). In 349*0Sstevel@tonic-gate * addition, the calling routine should have already added the input 350*0Sstevel@tonic-gate * directory to the search tree so we do not get into endless loops. 351*0Sstevel@tonic-gate * Note: chgrpr() doesn't need a return value as errors are reported 352*0Sstevel@tonic-gate * through the global "status" variable. 353*0Sstevel@tonic-gate */ 354*0Sstevel@tonic-gate static void 355*0Sstevel@tonic-gate chgrpr(char *dir, gid_t gid) 356*0Sstevel@tonic-gate { 357*0Sstevel@tonic-gate struct dirent *dp; 358*0Sstevel@tonic-gate DIR *dirp; 359*0Sstevel@tonic-gate struct stat st, st2; 360*0Sstevel@tonic-gate char savedir[1024]; 361*0Sstevel@tonic-gate 362*0Sstevel@tonic-gate if (getcwd(savedir, 1024) == 0) { 363*0Sstevel@tonic-gate (void) fprintf(stderr, "chgrp: "); 364*0Sstevel@tonic-gate (void) fprintf(stderr, gettext("%s\n"), savedir); 365*0Sstevel@tonic-gate exit(255); 366*0Sstevel@tonic-gate } 367*0Sstevel@tonic-gate 368*0Sstevel@tonic-gate /* 369*0Sstevel@tonic-gate * Attempt to chown the directory, however don't return if we 370*0Sstevel@tonic-gate * can't as we still may be able to chown the contents of the 371*0Sstevel@tonic-gate * directory. Note: the calling routine resets the SUID bits 372*0Sstevel@tonic-gate * on this directory so we don't have to perform an extra 'stat'. 373*0Sstevel@tonic-gate */ 374*0Sstevel@tonic-gate CHOWN(dir, -1, gid); 375*0Sstevel@tonic-gate 376*0Sstevel@tonic-gate if (chdir(dir) < 0) { 377*0Sstevel@tonic-gate status += Perror(dir); 378*0Sstevel@tonic-gate return; 379*0Sstevel@tonic-gate } 380*0Sstevel@tonic-gate if ((dirp = opendir(".")) == NULL) { 381*0Sstevel@tonic-gate status += Perror(dir); 382*0Sstevel@tonic-gate return; 383*0Sstevel@tonic-gate } 384*0Sstevel@tonic-gate for (dp = readdir(dirp); dp != NULL; dp = readdir(dirp)) { 385*0Sstevel@tonic-gate if ((strcmp(dp->d_name, ".") == 0) || 386*0Sstevel@tonic-gate (strcmp(dp->d_name, "..") == 0)) { 387*0Sstevel@tonic-gate continue; /* skip "." and ".." */ 388*0Sstevel@tonic-gate } 389*0Sstevel@tonic-gate if (lstat(dp->d_name, &st) < 0) { 390*0Sstevel@tonic-gate status += Perror(dp->d_name); 391*0Sstevel@tonic-gate continue; 392*0Sstevel@tonic-gate } 393*0Sstevel@tonic-gate if ((st.st_mode & S_IFMT) == S_IFLNK) { 394*0Sstevel@tonic-gate if (hflag || Pflag) { 395*0Sstevel@tonic-gate /* 396*0Sstevel@tonic-gate * Change the group id of the symbolic link 397*0Sstevel@tonic-gate * encountered while traversing the 398*0Sstevel@tonic-gate * directory. Don't follow the symbolic 399*0Sstevel@tonic-gate * link to any other part of the file 400*0Sstevel@tonic-gate * hierarchy. 401*0Sstevel@tonic-gate */ 402*0Sstevel@tonic-gate LCHOWN(dp->d_name, -1, gid); 403*0Sstevel@tonic-gate } else { 404*0Sstevel@tonic-gate if (stat(dp->d_name, &st2) < 0) { 405*0Sstevel@tonic-gate status += Perror(dp->d_name); 406*0Sstevel@tonic-gate continue; 407*0Sstevel@tonic-gate } 408*0Sstevel@tonic-gate /* 409*0Sstevel@tonic-gate * We know that we are to change the 410*0Sstevel@tonic-gate * group of the file referenced by the 411*0Sstevel@tonic-gate * symlink encountered while traversing 412*0Sstevel@tonic-gate * the directory. Now check to see if we 413*0Sstevel@tonic-gate * are to follow the symlink to any other 414*0Sstevel@tonic-gate * part of the file hierarchy. 415*0Sstevel@tonic-gate */ 416*0Sstevel@tonic-gate if (FOLLOW_D_LINKS) { 417*0Sstevel@tonic-gate if ((st2.st_mode & S_IFMT) == S_IFDIR) { 418*0Sstevel@tonic-gate /* 419*0Sstevel@tonic-gate * We are following symlinks so 420*0Sstevel@tonic-gate * traverse into the directory. 421*0Sstevel@tonic-gate * Add this node to the search 422*0Sstevel@tonic-gate * tree so we don't get into an 423*0Sstevel@tonic-gate * endless loop. 424*0Sstevel@tonic-gate */ 425*0Sstevel@tonic-gate int rc; 426*0Sstevel@tonic-gate if ((rc = add_tnode(&tree, 427*0Sstevel@tonic-gate st2.st_dev, 428*0Sstevel@tonic-gate st2.st_ino)) == 1) { 429*0Sstevel@tonic-gate chgrpr(dp->d_name, gid); 430*0Sstevel@tonic-gate 431*0Sstevel@tonic-gate /* 432*0Sstevel@tonic-gate * Restore SET[UG]ID 433*0Sstevel@tonic-gate * bits. 434*0Sstevel@tonic-gate */ 435*0Sstevel@tonic-gate SETUGID_PRESERVE( 436*0Sstevel@tonic-gate dp->d_name, 437*0Sstevel@tonic-gate st2.st_mode & 438*0Sstevel@tonic-gate ~S_IFMT); 439*0Sstevel@tonic-gate } else if (rc == 0) { 440*0Sstevel@tonic-gate /* already visited */ 441*0Sstevel@tonic-gate continue; 442*0Sstevel@tonic-gate } else { 443*0Sstevel@tonic-gate /* 444*0Sstevel@tonic-gate * An error occurred 445*0Sstevel@tonic-gate * while trying to add 446*0Sstevel@tonic-gate * the node to the tree. 447*0Sstevel@tonic-gate */ 448*0Sstevel@tonic-gate status += Perror( 449*0Sstevel@tonic-gate dp->d_name); 450*0Sstevel@tonic-gate continue; 451*0Sstevel@tonic-gate } 452*0Sstevel@tonic-gate } else { 453*0Sstevel@tonic-gate /* 454*0Sstevel@tonic-gate * Change the group id of the 455*0Sstevel@tonic-gate * file referenced by the 456*0Sstevel@tonic-gate * symbolic link. 457*0Sstevel@tonic-gate */ 458*0Sstevel@tonic-gate CHOWN(dp->d_name, -1, gid); 459*0Sstevel@tonic-gate 460*0Sstevel@tonic-gate } 461*0Sstevel@tonic-gate } else { 462*0Sstevel@tonic-gate /* 463*0Sstevel@tonic-gate * Change the group id of the file 464*0Sstevel@tonic-gate * referenced by the symbolic link. 465*0Sstevel@tonic-gate */ 466*0Sstevel@tonic-gate CHOWN(dp->d_name, -1, gid); 467*0Sstevel@tonic-gate 468*0Sstevel@tonic-gate if ((st2.st_mode & S_IFMT) == S_IFDIR) { 469*0Sstevel@tonic-gate /* Restore SET[UG]ID bits. */ 470*0Sstevel@tonic-gate SETUGID_PRESERVE(dp->d_name, 471*0Sstevel@tonic-gate st2.st_mode & ~S_IFMT); 472*0Sstevel@tonic-gate } 473*0Sstevel@tonic-gate } 474*0Sstevel@tonic-gate } 475*0Sstevel@tonic-gate } else if ((st.st_mode & S_IFMT) == S_IFDIR) { 476*0Sstevel@tonic-gate /* 477*0Sstevel@tonic-gate * Add this node to the search tree so we don't 478*0Sstevel@tonic-gate * get into a endless loop. 479*0Sstevel@tonic-gate */ 480*0Sstevel@tonic-gate int rc; 481*0Sstevel@tonic-gate if ((rc = add_tnode(&tree, st.st_dev, 482*0Sstevel@tonic-gate st.st_ino)) == 1) { 483*0Sstevel@tonic-gate chgrpr(dp->d_name, gid); 484*0Sstevel@tonic-gate 485*0Sstevel@tonic-gate /* Restore the SET[UG]ID bits. */ 486*0Sstevel@tonic-gate SETUGID_PRESERVE(dp->d_name, 487*0Sstevel@tonic-gate st.st_mode & ~S_IFMT); 488*0Sstevel@tonic-gate } else if (rc == 0) { 489*0Sstevel@tonic-gate /* already visited */ 490*0Sstevel@tonic-gate continue; 491*0Sstevel@tonic-gate } else { 492*0Sstevel@tonic-gate /* 493*0Sstevel@tonic-gate * An error occurred while trying 494*0Sstevel@tonic-gate * to add the node to the search tree. 495*0Sstevel@tonic-gate */ 496*0Sstevel@tonic-gate status += Perror(dp->d_name); 497*0Sstevel@tonic-gate continue; 498*0Sstevel@tonic-gate } 499*0Sstevel@tonic-gate } else { 500*0Sstevel@tonic-gate CHOWN(dp->d_name, -1, gid); 501*0Sstevel@tonic-gate } 502*0Sstevel@tonic-gate } 503*0Sstevel@tonic-gate (void) closedir(dirp); 504*0Sstevel@tonic-gate if (chdir(savedir) < 0) { 505*0Sstevel@tonic-gate (void) fprintf(stderr, "chgrp: "); 506*0Sstevel@tonic-gate (void) fprintf(stderr, gettext("can't change back to %s\n"), 507*0Sstevel@tonic-gate savedir); 508*0Sstevel@tonic-gate exit(255); 509*0Sstevel@tonic-gate } 510*0Sstevel@tonic-gate } 511*0Sstevel@tonic-gate 512*0Sstevel@tonic-gate static int 513*0Sstevel@tonic-gate isnumber(char *s) 514*0Sstevel@tonic-gate { 515*0Sstevel@tonic-gate int c; 516*0Sstevel@tonic-gate 517*0Sstevel@tonic-gate while ((c = *s++) != '\0') 518*0Sstevel@tonic-gate if (!isdigit(c)) 519*0Sstevel@tonic-gate return (0); 520*0Sstevel@tonic-gate return (1); 521*0Sstevel@tonic-gate } 522*0Sstevel@tonic-gate 523*0Sstevel@tonic-gate 524*0Sstevel@tonic-gate static int 525*0Sstevel@tonic-gate Perror(char *s) 526*0Sstevel@tonic-gate { 527*0Sstevel@tonic-gate if (!fflag) { 528*0Sstevel@tonic-gate (void) fprintf(stderr, "chgrp: "); 529*0Sstevel@tonic-gate perror(s); 530*0Sstevel@tonic-gate } 531*0Sstevel@tonic-gate return (!fflag); 532*0Sstevel@tonic-gate } 533*0Sstevel@tonic-gate 534*0Sstevel@tonic-gate 535*0Sstevel@tonic-gate static void 536*0Sstevel@tonic-gate usage(void) 537*0Sstevel@tonic-gate { 538*0Sstevel@tonic-gate (void) fprintf(stderr, gettext( 539*0Sstevel@tonic-gate "usage:\n" 540*0Sstevel@tonic-gate "\tchgrp [-fhR] group file ...\n" 541*0Sstevel@tonic-gate "\tchgrp -R [-f] [-H|-L|-P] group file ...\n")); 542*0Sstevel@tonic-gate exit(2); 543*0Sstevel@tonic-gate } 544