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 (the "License"). 6 * You may not use this file except in compliance with the License. 7 * 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. 12 * 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] 18 * 19 * CDDL HEADER END 20 */ 21 22 /* 23 * Copyright 2007 Sun Microsystems, Inc. All rights reserved. 24 * Use is subject to license terms. 25 */ 26 27 /* Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T */ 28 /* All Rights Reserved */ 29 30 #pragma ident "%Z%%M% %I% %E% SMI" 31 32 /* 33 * rm [-fiRr] file ... 34 */ 35 36 #include <sys/param.h> 37 #include <sys/stat.h> 38 #include <dirent.h> 39 #include <errno.h> 40 #include <fcntl.h> 41 #include <langinfo.h> 42 #include <limits.h> 43 #include <locale.h> 44 #include <stdarg.h> 45 #include <stdio.h> 46 #include <stdlib.h> 47 #include <string.h> 48 #include <unistd.h> 49 #include <values.h> 50 51 #define E_OK 010 /* make __accessat() use effective ids */ 52 53 #define DIR_CANTCLOSE 1 54 55 static struct stat rootdir; 56 57 struct dlist { 58 int fd; /* Stores directory fd */ 59 int flags; /* DIR_* Flags */ 60 DIR *dp; /* Open directory (opened with fd) */ 61 long diroff; /* Saved directory offset when closing */ 62 struct dlist *up; /* Up one step in the tree (toward "/") */ 63 struct dlist *down; /* Down one step in the tree */ 64 ino_t ino; /* st_ino of directory */ 65 dev_t dev; /* st_dev of directory */ 66 int pathend; /* Offset of name end in the pathbuffer */ 67 }; 68 69 static struct dlist top = { 70 (int)AT_FDCWD, 71 DIR_CANTCLOSE, 72 }; 73 74 static char yeschr[SCHAR_MAX + 2]; 75 static char nochr[SCHAR_MAX + 2]; 76 77 static struct dlist *cur, *rec; 78 79 static int rm(const char *, struct dlist *); 80 static int confirm(FILE *, const char *, ...); 81 static void memerror(void); 82 static int checkdir(struct dlist *, struct dlist *); 83 static int errcnt; 84 static boolean_t silent, interactive, recursive, ontty; 85 86 static char *pathbuf; 87 static size_t pathbuflen; 88 89 static int maxfds = MAXINT; 90 static int nfds; 91 92 extern int __accessat(int, const char *, int); 93 94 int 95 main(int argc, char **argv) 96 { 97 int errflg = 0; 98 int c; 99 100 (void) setlocale(LC_ALL, ""); 101 #if !defined(TEXT_DOMAIN) /* Should be defined by cc -D */ 102 #define TEXT_DOMAIN "SYS_TEST" /* Use this only if it weren't */ 103 #endif 104 (void) textdomain(TEXT_DOMAIN); 105 106 (void) strncpy(yeschr, nl_langinfo(YESSTR), SCHAR_MAX + 1); 107 (void) strncpy(nochr, nl_langinfo(NOSTR), SCHAR_MAX + 1); 108 109 while ((c = getopt(argc, argv, "frRi")) != EOF) 110 switch (c) { 111 case 'f': 112 silent = B_TRUE; 113 #ifdef XPG4 114 interactive = B_FALSE; 115 #endif 116 break; 117 case 'i': 118 interactive = B_TRUE; 119 #ifdef XPG4 120 silent = B_FALSE; 121 #endif 122 break; 123 case 'r': 124 case 'R': 125 recursive = B_TRUE; 126 break; 127 case '?': 128 errflg = 1; 129 break; 130 } 131 132 /* 133 * For BSD compatibility allow '-' to delimit the end 134 * of options. However, if options were already explicitly 135 * terminated with '--', then treat '-' literally: otherwise, 136 * "rm -- -" won't remove '-'. 137 */ 138 if (optind < argc && 139 strcmp(argv[optind], "-") == 0 && 140 strcmp(argv[optind - 1], "--") != 0) 141 optind++; 142 143 argc -= optind; 144 argv = &argv[optind]; 145 146 if ((argc < 1 && !silent) || errflg) { 147 (void) fprintf(stderr, gettext("usage: rm [-fiRr] file ...\n")); 148 exit(2); 149 } 150 151 ontty = isatty(STDIN_FILENO) != 0; 152 153 if (recursive && stat("/", &rootdir) != 0) { 154 (void) fprintf(stderr, 155 gettext("rm: cannot stat root directory: %s\n"), 156 strerror(errno)); 157 exit(2); 158 } 159 160 for (; *argv != NULL; argv++) { 161 char *p = strrchr(*argv, '/'); 162 if (p == NULL) 163 p = *argv; 164 else 165 p = p + 1; 166 if (strcmp(p, ".") == 0 || strcmp(p, "..") == 0) { 167 (void) fprintf(stderr, 168 gettext("rm of %s is not allowed\n"), *argv); 169 errcnt++; 170 continue; 171 } 172 /* Retry when we can't walk back up. */ 173 while (rm(*argv, rec = cur = &top) != 0) 174 ; 175 } 176 177 return (errcnt != 0 ? 2 : 0); 178 } 179 180 static void 181 pushfilename(const char *fname) 182 { 183 char *p; 184 const char *q = fname; 185 186 if (cur == &top) { 187 p = pathbuf; 188 } else { 189 p = pathbuf + cur->up->pathend; 190 *p++ = '/'; 191 } 192 while (*q != '\0') { 193 if (p - pathbuf + 2 >= pathbuflen) { 194 char *np; 195 pathbuflen += MAXPATHLEN; 196 np = realloc(pathbuf, pathbuflen); 197 if (np == NULL) 198 memerror(); 199 p = np + (p - pathbuf); 200 pathbuf = np; 201 } 202 *p++ = *q++; 203 } 204 *p = '\0'; 205 cur->pathend = p - pathbuf; 206 } 207 208 static void 209 closeframe(struct dlist *frm) 210 { 211 if (frm->dp != NULL) { 212 (void) closedir(frm->dp); 213 nfds--; 214 frm->dp = NULL; 215 frm->fd = -1; 216 } 217 } 218 219 static int 220 reclaim(void) 221 { 222 while (rec != NULL && (rec->flags & DIR_CANTCLOSE) != 0) 223 rec = rec->down; 224 if (rec == NULL || rec == cur || rec->dp == NULL) 225 return (-1); 226 rec->diroff = telldir(rec->dp); 227 closeframe(rec); 228 rec = rec->down; 229 return (0); 230 } 231 232 static void 233 pushdir(struct dlist *frm) 234 { 235 frm->up = cur; 236 frm->down = NULL; 237 cur->down = frm; 238 cur = frm; 239 } 240 241 static int 242 opendirat(int dirfd, const char *entry, struct dlist *frm) 243 { 244 int fd; 245 246 if (nfds >= maxfds) 247 (void) reclaim(); 248 249 while ((fd = openat(dirfd, entry, O_RDONLY|O_NONBLOCK)) == -1 && 250 errno == EMFILE) { 251 if (nfds < maxfds) 252 maxfds = nfds; 253 if (reclaim() != 0) 254 return (-1); 255 } 256 if (fd < 0) 257 return (-1); 258 frm->fd = fd; 259 frm->dp = fdopendir(fd); 260 if (frm->dp == NULL) { 261 (void) close(fd); 262 return (-1); 263 } 264 nfds++; 265 return (0); 266 } 267 268 /* 269 * Since we never pop the top frame, cur->up can never be NULL. 270 * If we pop beyond a frame we closed, we try to reopen "..". 271 */ 272 static int 273 popdir(boolean_t noerror) 274 { 275 struct stat buf; 276 int ret = noerror ? 0 : -1; 277 pathbuf[cur->up->pathend] = '\0'; 278 279 if (noerror && cur->up->fd == -1) { 280 rec = cur->up; 281 if (opendirat(cur->fd, "..", rec) != 0 || 282 fstat(rec->fd, &buf) != 0) { 283 (void) fprintf(stderr, 284 gettext("rm: cannot reopen %s: %s\n"), 285 pathbuf, strerror(errno)); 286 exit(2); 287 } 288 if (rec->ino != buf.st_ino || rec->dev != buf.st_dev) { 289 (void) fprintf(stderr, gettext("rm: WARNING: " 290 "The directory %s was moved or linked to " 291 "another directory during the execution of rm\n"), 292 pathbuf); 293 closeframe(rec); 294 ret = -1; 295 } else { 296 /* If telldir failed, we take it from the top. */ 297 if (rec->diroff != -1) 298 seekdir(rec->dp, rec->diroff); 299 } 300 } else if (rec == cur) 301 rec = cur->up; 302 closeframe(cur); 303 cur = cur->up; 304 cur->down = NULL; 305 return (ret); 306 } 307 308 /* 309 * The stack frame of this function is minimized so that we can 310 * recurse quite a bit before we overflow the stack; around 311 * 30,000-40,000 nested directories can be removed with the default 312 * stack limit. 313 */ 314 static int 315 rm(const char *entry, struct dlist *caller) 316 { 317 struct dlist frame; 318 int flag; 319 struct stat temp; 320 struct dirent *dent; 321 int err; 322 323 /* 324 * Construct the pathname: note that the entry may live in memory 325 * allocated by readdir and that after return from recursion 326 * the memory is no longer valid. So after the recursive rm() 327 * call, we use the global pathbuf instead of the entry argument. 328 */ 329 pushfilename(entry); 330 331 if (fstatat(caller->fd, entry, &temp, AT_SYMLINK_NOFOLLOW) != 0) { 332 if (!silent) { 333 (void) fprintf(stderr, "rm: %s: %s\n", pathbuf, 334 strerror(errno)); 335 errcnt++; 336 } 337 return (0); 338 } 339 340 if (S_ISDIR(temp.st_mode)) { 341 /* 342 * If "-r" wasn't specified, trying to remove directories 343 * is an error. 344 */ 345 if (!recursive) { 346 (void) fprintf(stderr, 347 gettext("rm: %s is a directory\n"), pathbuf); 348 errcnt++; 349 return (0); 350 } 351 352 if (temp.st_ino == rootdir.st_ino && 353 temp.st_dev == rootdir.st_dev) { 354 (void) fprintf(stderr, 355 gettext("rm of %s is not allowed\n"), "/"); 356 errcnt++; 357 return (0); 358 } 359 /* 360 * TRANSLATION_NOTE - The following message will contain the 361 * first character of the strings for "yes" and "no" defined 362 * in the file "nl_langinfo.po". After substitution, the 363 * message will appear as follows: 364 * rm: examine files in directory <directoryname> (y/n)? 365 * where <directoryname> is the directory to be removed 366 * 367 */ 368 if (interactive && !confirm(stderr, 369 gettext("rm: examine files in directory %s (%s/%s)? "), 370 pathbuf, yeschr, nochr)) { 371 return (0); 372 } 373 374 frame.dev = temp.st_dev; 375 frame.ino = temp.st_ino; 376 frame.flags = 0; 377 flag = AT_REMOVEDIR; 378 379 #ifdef XPG4 380 /* 381 * XCU4 and POSIX.2: If not interactive, check to see whether 382 * or not directory is readable or writable and if not, 383 * prompt user for response. 384 */ 385 if (ontty && !interactive && !silent && 386 __accessat(caller->fd, entry, W_OK|X_OK|E_OK) != 0 && 387 !confirm(stderr, 388 gettext("rm: examine files in directory %s (%s/%s)? "), 389 pathbuf, yeschr, nochr)) { 390 return (0); 391 } 392 #endif 393 if (opendirat(caller->fd, entry, &frame) == -1) { 394 err = errno; 395 396 if (interactive) { 397 /* 398 * Print an error message that 399 * we could not read the directory 400 * as the user wanted to examine 401 * files in the directory. Only 402 * affect the error status if 403 * user doesn't want to remove the 404 * directory as we still may be able 405 * remove the directory successfully. 406 */ 407 (void) fprintf(stderr, gettext( 408 "rm: cannot read directory %s: %s\n"), 409 pathbuf, strerror(err)); 410 411 /* 412 * TRANSLATION_NOTE - The following message will contain the 413 * first character of the strings for "yes" and "no" defined 414 * in the file "nl_langinfo.po". After substitution, the 415 * message will appear as follows: 416 * rm: remove <filename> (y/n)? 417 * For example, in German, this will appear as 418 * rm: l�schen <filename> (j/n)? 419 * where j=ja, n=nein, <filename>=the file to be removed 420 */ 421 if (!confirm(stderr, 422 gettext("rm: remove %s (%s/%s)? "), 423 pathbuf, yeschr, nochr)) { 424 errcnt++; 425 return (0); 426 } 427 } 428 /* If it's empty we may still be able to rm it */ 429 if (unlinkat(caller->fd, entry, flag) == 0) 430 return (0); 431 if (interactive) 432 err = errno; 433 (void) fprintf(stderr, 434 interactive ? 435 gettext("rm: Unable to remove directory %s: %s\n") : 436 gettext("rm: cannot read directory %s: %s\n"), 437 pathbuf, strerror(err)); 438 errcnt++; 439 return (0); 440 } 441 442 /* 443 * There is a race condition here too; if we open a directory 444 * we have to make sure it's still the same directory we 445 * stat'ed and checked against root earlier. Let's check. 446 */ 447 if (fstat(frame.fd, &temp) != 0 || 448 frame.ino != temp.st_ino || 449 frame.dev != temp.st_dev) { 450 (void) fprintf(stderr, 451 gettext("rm: %s: directory renamed\n"), pathbuf); 452 closeframe(&frame); 453 errcnt++; 454 return (0); 455 } 456 457 if (caller != &top) { 458 if (checkdir(caller, &frame) != 0) { 459 closeframe(&frame); 460 goto unlinkit; 461 } 462 } 463 pushdir(&frame); 464 465 /* 466 * rm() only returns -1 if popdir failed at some point; 467 * frame.dp is no longer reliable and we must drop out. 468 */ 469 while ((dent = readdir(frame.dp)) != NULL) { 470 if (strcmp(dent->d_name, ".") == 0 || 471 strcmp(dent->d_name, "..") == 0) 472 continue; 473 474 if (rm(dent->d_name, &frame) != 0) 475 break; 476 } 477 478 if (popdir(dent == NULL) != 0) 479 return (-1); 480 481 /* 482 * We recursed and the subdirectory may have set the CANTCLOSE 483 * flag; we need to clear it except for &top. 484 * Recursion may have invalidated entry because of closedir(). 485 */ 486 if (caller != &top) { 487 caller->flags &= ~DIR_CANTCLOSE; 488 entry = &pathbuf[caller->up->pathend + 1]; 489 } 490 } else { 491 flag = 0; 492 } 493 unlinkit: 494 /* 495 * If interactive, ask for acknowledgement. 496 */ 497 if (interactive) { 498 if (!confirm(stderr, gettext("rm: remove %s (%s/%s)? "), 499 pathbuf, yeschr, nochr)) { 500 return (0); 501 } 502 } else if (!silent && flag == 0) { 503 /* 504 * If not silent, and stdin is a terminal, and there's 505 * no write access, and the file isn't a symbolic link, 506 * ask for permission. If flag is set, then we know it's 507 * a directory so we skip this test as it was done above. 508 * 509 * TRANSLATION_NOTE - The following message will contain the 510 * first character of the strings for "yes" and "no" defined 511 * in the file "nl_langinfo.po". After substitution, the 512 * message will appear as follows: 513 * rm: <filename>: override protection XXX (y/n)? 514 * where XXX is the permission mode bits of the file in octal 515 * and <filename> is the file to be removed 516 * 517 */ 518 if (ontty && !S_ISLNK(temp.st_mode) && 519 __accessat(caller->fd, entry, W_OK|E_OK) != 0 && 520 !confirm(stdout, 521 gettext("rm: %s: override protection %o (%s/%s)? "), 522 pathbuf, temp.st_mode & 0777, yeschr, nochr)) { 523 return (0); 524 } 525 } 526 527 if (unlinkat(caller->fd, entry, flag) != 0) { 528 err = errno; 529 if (err == ENOENT) 530 return (0); 531 532 if (flag != 0) { 533 if (err == EINVAL) { 534 (void) fprintf(stderr, gettext( 535 "rm: Cannot remove any directory in the " 536 "path of the current working directory\n" 537 "%s\n"), pathbuf); 538 } else { 539 if (err == EEXIST) 540 err = ENOTEMPTY; 541 (void) fprintf(stderr, 542 gettext("rm: Unable to remove directory %s:" 543 " %s\n"), pathbuf, strerror(err)); 544 } 545 } else { 546 #ifndef XPG4 547 if (!silent || interactive) { 548 #endif 549 550 (void) fprintf(stderr, 551 gettext("rm: %s not removed: %s\n"), 552 pathbuf, strerror(err)); 553 #ifndef XPG4 554 } 555 #endif 556 } 557 errcnt++; 558 } 559 return (0); 560 } 561 562 static int 563 yes(void) 564 { 565 int i, b; 566 char ans[SCHAR_MAX + 1]; 567 568 for (i = 0; ; i++) { 569 b = getchar(); 570 if (b == '\n' || b == '\0' || b == EOF) { 571 ans[i] = 0; 572 break; 573 } 574 if (i < SCHAR_MAX) 575 ans[i] = (char)b; 576 } 577 if (i >= SCHAR_MAX) { 578 i = SCHAR_MAX; 579 ans[SCHAR_MAX] = 0; 580 } 581 if ((i == 0) | (strncmp(yeschr, ans, i))) 582 return (0); 583 return (1); 584 } 585 586 static int 587 confirm(FILE *fp, const char *q, ...) 588 { 589 va_list ap; 590 591 va_start(ap, q); 592 (void) vfprintf(fp, q, ap); 593 va_end(ap); 594 return (yes()); 595 } 596 597 static void 598 memerror(void) 599 { 600 (void) fprintf(stderr, gettext("rm: Insufficient memory.\n")); 601 exit(1); 602 } 603 604 /* 605 * If we can't stat "..", it's either not there or we can't search 606 * the current directory; in that case we can't return back through 607 * "..", so we need to keep the parent open. 608 * Check that we came from "..", if not then this directory entry is an 609 * additional link and there is risk of a filesystem cycle and we also 610 * can't go back up through ".." and we keep the directory open. 611 */ 612 static int 613 checkdir(struct dlist *caller, struct dlist *frmp) 614 { 615 struct stat up; 616 struct dlist *ptr; 617 618 if (fstatat(frmp->fd, "..", &up, 0) != 0) { 619 caller->flags |= DIR_CANTCLOSE; 620 return (0); 621 } else if (up.st_ino == caller->ino && up.st_dev == caller->dev) { 622 return (0); 623 } 624 625 /* Directory hard link, check cycle */ 626 for (ptr = caller; ptr != NULL; ptr = ptr->up) { 627 if (frmp->dev == ptr->dev && frmp->ino == ptr->ino) { 628 (void) fprintf(stderr, 629 gettext("rm: cycle detected for %s\n"), pathbuf); 630 errcnt++; 631 return (-1); 632 } 633 } 634 caller->flags |= DIR_CANTCLOSE; 635 return (0); 636 } 637