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