17c478bd9Sstevel@tonic-gate /* 27c478bd9Sstevel@tonic-gate * CDDL HEADER START 37c478bd9Sstevel@tonic-gate * 47c478bd9Sstevel@tonic-gate * The contents of this file are subject to the terms of the 579033acbSas145665 * Common Development and Distribution License (the "License"). 679033acbSas145665 * You may not use this file except in compliance with the License. 77c478bd9Sstevel@tonic-gate * 87c478bd9Sstevel@tonic-gate * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 97c478bd9Sstevel@tonic-gate * or http://www.opensolaris.org/os/licensing. 107c478bd9Sstevel@tonic-gate * See the License for the specific language governing permissions 117c478bd9Sstevel@tonic-gate * and limitations under the License. 127c478bd9Sstevel@tonic-gate * 137c478bd9Sstevel@tonic-gate * When distributing Covered Code, include this CDDL HEADER in each 147c478bd9Sstevel@tonic-gate * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 157c478bd9Sstevel@tonic-gate * If applicable, add the following below this CDDL HEADER, with the 167c478bd9Sstevel@tonic-gate * fields enclosed by brackets "[]" replaced with your own identifying 177c478bd9Sstevel@tonic-gate * information: Portions Copyright [yyyy] [name of copyright owner] 187c478bd9Sstevel@tonic-gate * 197c478bd9Sstevel@tonic-gate * CDDL HEADER END 207c478bd9Sstevel@tonic-gate */ 217c478bd9Sstevel@tonic-gate 227c478bd9Sstevel@tonic-gate /* 2348011479Ssn199410 * Copyright 2007 Sun Microsystems, Inc. All rights reserved. 247c478bd9Sstevel@tonic-gate * Use is subject to license terms. 257c478bd9Sstevel@tonic-gate */ 267c478bd9Sstevel@tonic-gate 27014a7923Sas145665 /* Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T */ 28014a7923Sas145665 /* All Rights Reserved */ 29014a7923Sas145665 307c478bd9Sstevel@tonic-gate #pragma ident "%Z%%M% %I% %E% SMI" 317c478bd9Sstevel@tonic-gate 327c478bd9Sstevel@tonic-gate /* 337c478bd9Sstevel@tonic-gate * rm [-fiRr] file ... 347c478bd9Sstevel@tonic-gate */ 357c478bd9Sstevel@tonic-gate 3648011479Ssn199410 #include <sys/param.h> 377c478bd9Sstevel@tonic-gate #include <sys/stat.h> 387c478bd9Sstevel@tonic-gate #include <dirent.h> 3948011479Ssn199410 #include <errno.h> 4048011479Ssn199410 #include <fcntl.h> 4148011479Ssn199410 #include <langinfo.h> 427c478bd9Sstevel@tonic-gate #include <limits.h> 437c478bd9Sstevel@tonic-gate #include <locale.h> 4448011479Ssn199410 #include <stdarg.h> 4548011479Ssn199410 #include <stdio.h> 467c478bd9Sstevel@tonic-gate #include <stdlib.h> 4748011479Ssn199410 #include <string.h> 4848011479Ssn199410 #include <unistd.h> 4948011479Ssn199410 #include <values.h> 507c478bd9Sstevel@tonic-gate 5148011479Ssn199410 #define E_OK 010 /* make __accessat() use effective ids */ 527c478bd9Sstevel@tonic-gate 5348011479Ssn199410 #define DIR_CANTCLOSE 1 547c478bd9Sstevel@tonic-gate 5548011479Ssn199410 static struct stat rootdir; 5648011479Ssn199410 5748011479Ssn199410 struct dlist { 5848011479Ssn199410 int fd; /* Stores directory fd */ 5948011479Ssn199410 int flags; /* DIR_* Flags */ 6048011479Ssn199410 DIR *dp; /* Open directory (opened with fd) */ 6148011479Ssn199410 long diroff; /* Saved directory offset when closing */ 6248011479Ssn199410 struct dlist *up; /* Up one step in the tree (toward "/") */ 6348011479Ssn199410 struct dlist *down; /* Down one step in the tree */ 6448011479Ssn199410 ino_t ino; /* st_ino of directory */ 6548011479Ssn199410 dev_t dev; /* st_dev of directory */ 6648011479Ssn199410 int pathend; /* Offset of name end in the pathbuffer */ 6748011479Ssn199410 }; 6848011479Ssn199410 6948011479Ssn199410 static struct dlist top = { 7048011479Ssn199410 (int)AT_FDCWD, 7148011479Ssn199410 DIR_CANTCLOSE, 7248011479Ssn199410 }; 737c478bd9Sstevel@tonic-gate 747c478bd9Sstevel@tonic-gate static char yeschr[SCHAR_MAX + 2]; 757c478bd9Sstevel@tonic-gate static char nochr[SCHAR_MAX + 2]; 767c478bd9Sstevel@tonic-gate 7748011479Ssn199410 static struct dlist *cur, *rec; 787c478bd9Sstevel@tonic-gate 7948011479Ssn199410 static int rm(const char *, struct dlist *); 8048011479Ssn199410 static int confirm(FILE *, const char *, ...); 8148011479Ssn199410 static void memerror(void); 8248011479Ssn199410 static int checkdir(struct dlist *, struct dlist *); 8348011479Ssn199410 static int errcnt; 8448011479Ssn199410 static boolean_t silent, interactive, recursive, ontty; 857c478bd9Sstevel@tonic-gate 8648011479Ssn199410 static char *pathbuf; 87*131f4edeSsn199410 static size_t pathbuflen = MAXPATHLEN; 887c478bd9Sstevel@tonic-gate 8948011479Ssn199410 static int maxfds = MAXINT; 9048011479Ssn199410 static int nfds; 917c478bd9Sstevel@tonic-gate 9248011479Ssn199410 extern int __accessat(int, const char *, int); 937c478bd9Sstevel@tonic-gate 947c478bd9Sstevel@tonic-gate int 9548011479Ssn199410 main(int argc, char **argv) 967c478bd9Sstevel@tonic-gate { 977c478bd9Sstevel@tonic-gate int errflg = 0; 987c478bd9Sstevel@tonic-gate int c; 997c478bd9Sstevel@tonic-gate 1007c478bd9Sstevel@tonic-gate (void) setlocale(LC_ALL, ""); 1017c478bd9Sstevel@tonic-gate #if !defined(TEXT_DOMAIN) /* Should be defined by cc -D */ 1027c478bd9Sstevel@tonic-gate #define TEXT_DOMAIN "SYS_TEST" /* Use this only if it weren't */ 1037c478bd9Sstevel@tonic-gate #endif 1047c478bd9Sstevel@tonic-gate (void) textdomain(TEXT_DOMAIN); 1057c478bd9Sstevel@tonic-gate 1067c478bd9Sstevel@tonic-gate (void) strncpy(yeschr, nl_langinfo(YESSTR), SCHAR_MAX + 1); 1077c478bd9Sstevel@tonic-gate (void) strncpy(nochr, nl_langinfo(NOSTR), SCHAR_MAX + 1); 1087c478bd9Sstevel@tonic-gate 1097c478bd9Sstevel@tonic-gate while ((c = getopt(argc, argv, "frRi")) != EOF) 1107c478bd9Sstevel@tonic-gate switch (c) { 1117c478bd9Sstevel@tonic-gate case 'f': 11248011479Ssn199410 silent = B_TRUE; 1137c478bd9Sstevel@tonic-gate #ifdef XPG4 11448011479Ssn199410 interactive = B_FALSE; 1157c478bd9Sstevel@tonic-gate #endif 1167c478bd9Sstevel@tonic-gate break; 1177c478bd9Sstevel@tonic-gate case 'i': 11848011479Ssn199410 interactive = B_TRUE; 1197c478bd9Sstevel@tonic-gate #ifdef XPG4 12048011479Ssn199410 silent = B_FALSE; 1217c478bd9Sstevel@tonic-gate #endif 1227c478bd9Sstevel@tonic-gate break; 1237c478bd9Sstevel@tonic-gate case 'r': 1247c478bd9Sstevel@tonic-gate case 'R': 12548011479Ssn199410 recursive = B_TRUE; 1267c478bd9Sstevel@tonic-gate break; 1277c478bd9Sstevel@tonic-gate case '?': 1287c478bd9Sstevel@tonic-gate errflg = 1; 1297c478bd9Sstevel@tonic-gate break; 1307c478bd9Sstevel@tonic-gate } 1317c478bd9Sstevel@tonic-gate 1327c478bd9Sstevel@tonic-gate /* 1337c478bd9Sstevel@tonic-gate * For BSD compatibility allow '-' to delimit the end 1347c478bd9Sstevel@tonic-gate * of options. However, if options were already explicitly 1357c478bd9Sstevel@tonic-gate * terminated with '--', then treat '-' literally: otherwise, 1367c478bd9Sstevel@tonic-gate * "rm -- -" won't remove '-'. 1377c478bd9Sstevel@tonic-gate */ 1387c478bd9Sstevel@tonic-gate if (optind < argc && 1397c478bd9Sstevel@tonic-gate strcmp(argv[optind], "-") == 0 && 1407c478bd9Sstevel@tonic-gate strcmp(argv[optind - 1], "--") != 0) 1417c478bd9Sstevel@tonic-gate optind++; 1427c478bd9Sstevel@tonic-gate 1437c478bd9Sstevel@tonic-gate argc -= optind; 1447c478bd9Sstevel@tonic-gate argv = &argv[optind]; 1457c478bd9Sstevel@tonic-gate 1467c478bd9Sstevel@tonic-gate if ((argc < 1 && !silent) || errflg) { 14748011479Ssn199410 (void) fprintf(stderr, gettext("usage: rm [-fiRr] file ...\n")); 14848011479Ssn199410 exit(2); 14948011479Ssn199410 } 15048011479Ssn199410 15148011479Ssn199410 ontty = isatty(STDIN_FILENO) != 0; 15248011479Ssn199410 15348011479Ssn199410 if (recursive && stat("/", &rootdir) != 0) { 1547c478bd9Sstevel@tonic-gate (void) fprintf(stderr, 15548011479Ssn199410 gettext("rm: cannot stat root directory: %s\n"), 15648011479Ssn199410 strerror(errno)); 1577c478bd9Sstevel@tonic-gate exit(2); 1587c478bd9Sstevel@tonic-gate } 1597c478bd9Sstevel@tonic-gate 160*131f4edeSsn199410 pathbuf = malloc(pathbuflen); 161*131f4edeSsn199410 if (pathbuf == NULL) 162*131f4edeSsn199410 memerror(); 163*131f4edeSsn199410 16448011479Ssn199410 for (; *argv != NULL; argv++) { 16548011479Ssn199410 char *p = strrchr(*argv, '/'); 16648011479Ssn199410 if (p == NULL) 16748011479Ssn199410 p = *argv; 16848011479Ssn199410 else 16948011479Ssn199410 p = p + 1; 17048011479Ssn199410 if (strcmp(p, ".") == 0 || strcmp(p, "..") == 0) { 17148011479Ssn199410 (void) fprintf(stderr, 17248011479Ssn199410 gettext("rm of %s is not allowed\n"), *argv); 17348011479Ssn199410 errcnt++; 17448011479Ssn199410 continue; 17548011479Ssn199410 } 17648011479Ssn199410 /* Retry when we can't walk back up. */ 17748011479Ssn199410 while (rm(*argv, rec = cur = &top) != 0) 178996aa816Ssn199410 ; 1797c478bd9Sstevel@tonic-gate } 18012a9e0efSsn199410 18148011479Ssn199410 return (errcnt != 0 ? 2 : 0); 18248011479Ssn199410 } 18348011479Ssn199410 18448011479Ssn199410 static void 18548011479Ssn199410 pushfilename(const char *fname) 18648011479Ssn199410 { 18748011479Ssn199410 char *p; 18848011479Ssn199410 const char *q = fname; 18948011479Ssn199410 19048011479Ssn199410 if (cur == &top) { 19148011479Ssn199410 p = pathbuf; 19248011479Ssn199410 } else { 19348011479Ssn199410 p = pathbuf + cur->up->pathend; 19448011479Ssn199410 *p++ = '/'; 19548011479Ssn199410 } 19648011479Ssn199410 while (*q != '\0') { 19748011479Ssn199410 if (p - pathbuf + 2 >= pathbuflen) { 19848011479Ssn199410 char *np; 19948011479Ssn199410 pathbuflen += MAXPATHLEN; 20048011479Ssn199410 np = realloc(pathbuf, pathbuflen); 20148011479Ssn199410 if (np == NULL) 20248011479Ssn199410 memerror(); 20348011479Ssn199410 p = np + (p - pathbuf); 20448011479Ssn199410 pathbuf = np; 20548011479Ssn199410 } 20648011479Ssn199410 *p++ = *q++; 20748011479Ssn199410 } 20848011479Ssn199410 *p = '\0'; 20948011479Ssn199410 cur->pathend = p - pathbuf; 21048011479Ssn199410 } 21148011479Ssn199410 21248011479Ssn199410 static void 21348011479Ssn199410 closeframe(struct dlist *frm) 21448011479Ssn199410 { 21548011479Ssn199410 if (frm->dp != NULL) { 21648011479Ssn199410 (void) closedir(frm->dp); 21748011479Ssn199410 nfds--; 21848011479Ssn199410 frm->dp = NULL; 21948011479Ssn199410 frm->fd = -1; 22048011479Ssn199410 } 2217c478bd9Sstevel@tonic-gate } 2227c478bd9Sstevel@tonic-gate 223996aa816Ssn199410 static int 22448011479Ssn199410 reclaim(void) 2257c478bd9Sstevel@tonic-gate { 22648011479Ssn199410 while (rec != NULL && (rec->flags & DIR_CANTCLOSE) != 0) 22748011479Ssn199410 rec = rec->down; 22848011479Ssn199410 if (rec == NULL || rec == cur || rec->dp == NULL) 22948011479Ssn199410 return (-1); 23048011479Ssn199410 rec->diroff = telldir(rec->dp); 23148011479Ssn199410 closeframe(rec); 23248011479Ssn199410 rec = rec->down; 23348011479Ssn199410 return (0); 23448011479Ssn199410 } 23548011479Ssn199410 23648011479Ssn199410 static void 23748011479Ssn199410 pushdir(struct dlist *frm) 23848011479Ssn199410 { 23948011479Ssn199410 frm->up = cur; 24048011479Ssn199410 frm->down = NULL; 24148011479Ssn199410 cur->down = frm; 24248011479Ssn199410 cur = frm; 24348011479Ssn199410 } 24448011479Ssn199410 24548011479Ssn199410 static int 24648011479Ssn199410 opendirat(int dirfd, const char *entry, struct dlist *frm) 24748011479Ssn199410 { 24848011479Ssn199410 int fd; 24948011479Ssn199410 25048011479Ssn199410 if (nfds >= maxfds) 25148011479Ssn199410 (void) reclaim(); 25248011479Ssn199410 25348011479Ssn199410 while ((fd = openat(dirfd, entry, O_RDONLY|O_NONBLOCK)) == -1 && 25448011479Ssn199410 errno == EMFILE) { 25548011479Ssn199410 if (nfds < maxfds) 25648011479Ssn199410 maxfds = nfds; 25748011479Ssn199410 if (reclaim() != 0) 25848011479Ssn199410 return (-1); 25948011479Ssn199410 } 26048011479Ssn199410 if (fd < 0) 26148011479Ssn199410 return (-1); 26248011479Ssn199410 frm->fd = fd; 26348011479Ssn199410 frm->dp = fdopendir(fd); 26448011479Ssn199410 if (frm->dp == NULL) { 26548011479Ssn199410 (void) close(fd); 26648011479Ssn199410 return (-1); 26748011479Ssn199410 } 26848011479Ssn199410 nfds++; 26948011479Ssn199410 return (0); 27048011479Ssn199410 } 2717c478bd9Sstevel@tonic-gate 2727c478bd9Sstevel@tonic-gate /* 27348011479Ssn199410 * Since we never pop the top frame, cur->up can never be NULL. 27448011479Ssn199410 * If we pop beyond a frame we closed, we try to reopen "..". 2757c478bd9Sstevel@tonic-gate */ 27648011479Ssn199410 static int 27748011479Ssn199410 popdir(boolean_t noerror) 27848011479Ssn199410 { 27948011479Ssn199410 struct stat buf; 28048011479Ssn199410 int ret = noerror ? 0 : -1; 28148011479Ssn199410 pathbuf[cur->up->pathend] = '\0'; 28248011479Ssn199410 28348011479Ssn199410 if (noerror && cur->up->fd == -1) { 28448011479Ssn199410 rec = cur->up; 28548011479Ssn199410 if (opendirat(cur->fd, "..", rec) != 0 || 28648011479Ssn199410 fstat(rec->fd, &buf) != 0) { 28748011479Ssn199410 (void) fprintf(stderr, 28848011479Ssn199410 gettext("rm: cannot reopen %s: %s\n"), 28948011479Ssn199410 pathbuf, strerror(errno)); 29048011479Ssn199410 exit(2); 29148011479Ssn199410 } 29248011479Ssn199410 if (rec->ino != buf.st_ino || rec->dev != buf.st_dev) { 29348011479Ssn199410 (void) fprintf(stderr, gettext("rm: WARNING: " 29448011479Ssn199410 "The directory %s was moved or linked to " 29548011479Ssn199410 "another directory during the execution of rm\n"), 29648011479Ssn199410 pathbuf); 29748011479Ssn199410 closeframe(rec); 29848011479Ssn199410 ret = -1; 29948011479Ssn199410 } else { 30048011479Ssn199410 /* If telldir failed, we take it from the top. */ 30148011479Ssn199410 if (rec->diroff != -1) 30248011479Ssn199410 seekdir(rec->dp, rec->diroff); 30348011479Ssn199410 } 30448011479Ssn199410 } else if (rec == cur) 30548011479Ssn199410 rec = cur->up; 30648011479Ssn199410 closeframe(cur); 30748011479Ssn199410 cur = cur->up; 30848011479Ssn199410 cur->down = NULL; 30948011479Ssn199410 return (ret); 31048011479Ssn199410 } 31148011479Ssn199410 31248011479Ssn199410 /* 31348011479Ssn199410 * The stack frame of this function is minimized so that we can 31448011479Ssn199410 * recurse quite a bit before we overflow the stack; around 31548011479Ssn199410 * 30,000-40,000 nested directories can be removed with the default 31648011479Ssn199410 * stack limit. 31748011479Ssn199410 */ 31848011479Ssn199410 static int 31948011479Ssn199410 rm(const char *entry, struct dlist *caller) 32048011479Ssn199410 { 32148011479Ssn199410 struct dlist frame; 32248011479Ssn199410 int flag; 32348011479Ssn199410 struct stat temp; 32448011479Ssn199410 struct dirent *dent; 32548011479Ssn199410 int err; 32648011479Ssn199410 32748011479Ssn199410 /* 32848011479Ssn199410 * Construct the pathname: note that the entry may live in memory 32948011479Ssn199410 * allocated by readdir and that after return from recursion 33048011479Ssn199410 * the memory is no longer valid. So after the recursive rm() 33148011479Ssn199410 * call, we use the global pathbuf instead of the entry argument. 33248011479Ssn199410 */ 33348011479Ssn199410 pushfilename(entry); 33448011479Ssn199410 33548011479Ssn199410 if (fstatat(caller->fd, entry, &temp, AT_SYMLINK_NOFOLLOW) != 0) { 3367c478bd9Sstevel@tonic-gate if (!silent) { 33748011479Ssn199410 (void) fprintf(stderr, "rm: %s: %s\n", pathbuf, 33848011479Ssn199410 strerror(errno)); 33948011479Ssn199410 errcnt++; 3407c478bd9Sstevel@tonic-gate } 341996aa816Ssn199410 return (0); 3427c478bd9Sstevel@tonic-gate } 3437c478bd9Sstevel@tonic-gate 34448011479Ssn199410 if (S_ISDIR(temp.st_mode)) { 3457c478bd9Sstevel@tonic-gate /* 3467c478bd9Sstevel@tonic-gate * If "-r" wasn't specified, trying to remove directories 3477c478bd9Sstevel@tonic-gate * is an error. 3487c478bd9Sstevel@tonic-gate */ 3497c478bd9Sstevel@tonic-gate if (!recursive) { 3507c478bd9Sstevel@tonic-gate (void) fprintf(stderr, 35148011479Ssn199410 gettext("rm: %s is a directory\n"), pathbuf); 35248011479Ssn199410 errcnt++; 353996aa816Ssn199410 return (0); 3547c478bd9Sstevel@tonic-gate } 3557c478bd9Sstevel@tonic-gate 35648011479Ssn199410 if (temp.st_ino == rootdir.st_ino && 35748011479Ssn199410 temp.st_dev == rootdir.st_dev) { 3587c478bd9Sstevel@tonic-gate (void) fprintf(stderr, 35948011479Ssn199410 gettext("rm of %s is not allowed\n"), "/"); 36048011479Ssn199410 errcnt++; 361996aa816Ssn199410 return (0); 3627c478bd9Sstevel@tonic-gate } 3637c478bd9Sstevel@tonic-gate /* 3647c478bd9Sstevel@tonic-gate * TRANSLATION_NOTE - The following message will contain the 3657c478bd9Sstevel@tonic-gate * first character of the strings for "yes" and "no" defined 3667c478bd9Sstevel@tonic-gate * in the file "nl_langinfo.po". After substitution, the 3677c478bd9Sstevel@tonic-gate * message will appear as follows: 3687c478bd9Sstevel@tonic-gate * rm: examine files in directory <directoryname> (y/n)? 3697c478bd9Sstevel@tonic-gate * where <directoryname> is the directory to be removed 3707c478bd9Sstevel@tonic-gate * 3717c478bd9Sstevel@tonic-gate */ 37248011479Ssn199410 if (interactive && !confirm(stderr, 3737c478bd9Sstevel@tonic-gate gettext("rm: examine files in directory %s (%s/%s)? "), 37448011479Ssn199410 pathbuf, yeschr, nochr)) { 37548011479Ssn199410 return (0); 3767c478bd9Sstevel@tonic-gate } 3777c478bd9Sstevel@tonic-gate 37848011479Ssn199410 frame.dev = temp.st_dev; 37948011479Ssn199410 frame.ino = temp.st_ino; 38048011479Ssn199410 frame.flags = 0; 38148011479Ssn199410 flag = AT_REMOVEDIR; 38248011479Ssn199410 3837c478bd9Sstevel@tonic-gate #ifdef XPG4 3847c478bd9Sstevel@tonic-gate /* 38548011479Ssn199410 * XCU4 and POSIX.2: If not interactive, check to see whether 3867c478bd9Sstevel@tonic-gate * or not directory is readable or writable and if not, 3877c478bd9Sstevel@tonic-gate * prompt user for response. 3887c478bd9Sstevel@tonic-gate */ 38948011479Ssn199410 if (ontty && !interactive && !silent && 39048011479Ssn199410 __accessat(caller->fd, entry, W_OK|X_OK|E_OK) != 0 && 39148011479Ssn199410 !confirm(stderr, 39248011479Ssn199410 gettext("rm: examine files in directory %s (%s/%s)? "), 39348011479Ssn199410 pathbuf, yeschr, nochr)) { 39448011479Ssn199410 return (0); 3957c478bd9Sstevel@tonic-gate } 3967c478bd9Sstevel@tonic-gate #endif 39748011479Ssn199410 if (opendirat(caller->fd, entry, &frame) == -1) { 39848011479Ssn199410 err = errno; 3997c478bd9Sstevel@tonic-gate 4007c478bd9Sstevel@tonic-gate if (interactive) { 4017c478bd9Sstevel@tonic-gate /* 4027c478bd9Sstevel@tonic-gate * Print an error message that 4037c478bd9Sstevel@tonic-gate * we could not read the directory 4047c478bd9Sstevel@tonic-gate * as the user wanted to examine 4057c478bd9Sstevel@tonic-gate * files in the directory. Only 4067c478bd9Sstevel@tonic-gate * affect the error status if 4077c478bd9Sstevel@tonic-gate * user doesn't want to remove the 4087c478bd9Sstevel@tonic-gate * directory as we still may be able 4097c478bd9Sstevel@tonic-gate * remove the directory successfully. 4107c478bd9Sstevel@tonic-gate */ 4117c478bd9Sstevel@tonic-gate (void) fprintf(stderr, gettext( 41248011479Ssn199410 "rm: cannot read directory %s: %s\n"), 41348011479Ssn199410 pathbuf, strerror(err)); 41448011479Ssn199410 41548011479Ssn199410 /* 41648011479Ssn199410 * TRANSLATION_NOTE - The following message will contain the 41748011479Ssn199410 * first character of the strings for "yes" and "no" defined 41848011479Ssn199410 * in the file "nl_langinfo.po". After substitution, the 41948011479Ssn199410 * message will appear as follows: 42048011479Ssn199410 * rm: remove <filename> (y/n)? 42148011479Ssn199410 * For example, in German, this will appear as 42248011479Ssn199410 * rm: l�schen <filename> (j/n)? 42348011479Ssn199410 * where j=ja, n=nein, <filename>=the file to be removed 42448011479Ssn199410 */ 42548011479Ssn199410 if (!confirm(stderr, 42648011479Ssn199410 gettext("rm: remove %s (%s/%s)? "), 42748011479Ssn199410 pathbuf, yeschr, nochr)) { 42848011479Ssn199410 errcnt++; 42948011479Ssn199410 return (0); 4307c478bd9Sstevel@tonic-gate } 4317c478bd9Sstevel@tonic-gate } 43248011479Ssn199410 /* If it's empty we may still be able to rm it */ 43348011479Ssn199410 if (unlinkat(caller->fd, entry, flag) == 0) 43448011479Ssn199410 return (0); 43548011479Ssn199410 if (interactive) 43648011479Ssn199410 err = errno; 43748011479Ssn199410 (void) fprintf(stderr, 43848011479Ssn199410 interactive ? 43948011479Ssn199410 gettext("rm: Unable to remove directory %s: %s\n") : 44048011479Ssn199410 gettext("rm: cannot read directory %s: %s\n"), 44148011479Ssn199410 pathbuf, strerror(err)); 44248011479Ssn199410 errcnt++; 44348011479Ssn199410 return (0); 44448011479Ssn199410 } 4457c478bd9Sstevel@tonic-gate 4467c478bd9Sstevel@tonic-gate /* 44748011479Ssn199410 * There is a race condition here too; if we open a directory 44848011479Ssn199410 * we have to make sure it's still the same directory we 44948011479Ssn199410 * stat'ed and checked against root earlier. Let's check. 4507c478bd9Sstevel@tonic-gate */ 45148011479Ssn199410 if (fstat(frame.fd, &temp) != 0 || 45248011479Ssn199410 frame.ino != temp.st_ino || 45348011479Ssn199410 frame.dev != temp.st_dev) { 45448011479Ssn199410 (void) fprintf(stderr, 45548011479Ssn199410 gettext("rm: %s: directory renamed\n"), pathbuf); 45648011479Ssn199410 closeframe(&frame); 45748011479Ssn199410 errcnt++; 45848011479Ssn199410 return (0); 4597c478bd9Sstevel@tonic-gate } 4607c478bd9Sstevel@tonic-gate 46148011479Ssn199410 if (caller != &top) { 46248011479Ssn199410 if (checkdir(caller, &frame) != 0) { 46348011479Ssn199410 closeframe(&frame); 46448011479Ssn199410 goto unlinkit; 4657c478bd9Sstevel@tonic-gate } 46648011479Ssn199410 } 46748011479Ssn199410 pushdir(&frame); 4687c478bd9Sstevel@tonic-gate 4697c478bd9Sstevel@tonic-gate /* 47048011479Ssn199410 * rm() only returns -1 if popdir failed at some point; 47148011479Ssn199410 * frame.dp is no longer reliable and we must drop out. 4727c478bd9Sstevel@tonic-gate */ 47348011479Ssn199410 while ((dent = readdir(frame.dp)) != NULL) { 47448011479Ssn199410 if (strcmp(dent->d_name, ".") == 0 || 47548011479Ssn199410 strcmp(dent->d_name, "..") == 0) 4767c478bd9Sstevel@tonic-gate continue; 4777c478bd9Sstevel@tonic-gate 47848011479Ssn199410 if (rm(dent->d_name, &frame) != 0) 479996aa816Ssn199410 break; 4807c478bd9Sstevel@tonic-gate } 4817c478bd9Sstevel@tonic-gate 48248011479Ssn199410 if (popdir(dent == NULL) != 0) 483996aa816Ssn199410 return (-1); 4847c478bd9Sstevel@tonic-gate 4857c478bd9Sstevel@tonic-gate /* 48648011479Ssn199410 * We recursed and the subdirectory may have set the CANTCLOSE 48748011479Ssn199410 * flag; we need to clear it except for &top. 48848011479Ssn199410 * Recursion may have invalidated entry because of closedir(). 4897c478bd9Sstevel@tonic-gate */ 49048011479Ssn199410 if (caller != &top) { 49148011479Ssn199410 caller->flags &= ~DIR_CANTCLOSE; 49248011479Ssn199410 entry = &pathbuf[caller->up->pathend + 1]; 4937c478bd9Sstevel@tonic-gate } 49448011479Ssn199410 } else { 49548011479Ssn199410 flag = 0; 4967c478bd9Sstevel@tonic-gate } 49748011479Ssn199410 unlinkit: 4987c478bd9Sstevel@tonic-gate /* 4997c478bd9Sstevel@tonic-gate * If interactive, ask for acknowledgement. 5007c478bd9Sstevel@tonic-gate */ 5017c478bd9Sstevel@tonic-gate if (interactive) { 50248011479Ssn199410 if (!confirm(stderr, gettext("rm: remove %s (%s/%s)? "), 50348011479Ssn199410 pathbuf, yeschr, nochr)) { 50448011479Ssn199410 return (0); 5057c478bd9Sstevel@tonic-gate } 50648011479Ssn199410 } else if (!silent && flag == 0) { 50748011479Ssn199410 /* 50848011479Ssn199410 * If not silent, and stdin is a terminal, and there's 50948011479Ssn199410 * no write access, and the file isn't a symbolic link, 51048011479Ssn199410 * ask for permission. If flag is set, then we know it's 51148011479Ssn199410 * a directory so we skip this test as it was done above. 51248011479Ssn199410 * 51348011479Ssn199410 * TRANSLATION_NOTE - The following message will contain the 51448011479Ssn199410 * first character of the strings for "yes" and "no" defined 51548011479Ssn199410 * in the file "nl_langinfo.po". After substitution, the 51648011479Ssn199410 * message will appear as follows: 51748011479Ssn199410 * rm: <filename>: override protection XXX (y/n)? 51848011479Ssn199410 * where XXX is the permission mode bits of the file in octal 51948011479Ssn199410 * and <filename> is the file to be removed 52048011479Ssn199410 * 52148011479Ssn199410 */ 52248011479Ssn199410 if (ontty && !S_ISLNK(temp.st_mode) && 52348011479Ssn199410 __accessat(caller->fd, entry, W_OK|E_OK) != 0 && 52448011479Ssn199410 !confirm(stdout, 52548011479Ssn199410 gettext("rm: %s: override protection %o (%s/%s)? "), 52648011479Ssn199410 pathbuf, temp.st_mode & 0777, yeschr, nochr)) { 52748011479Ssn199410 return (0); 5287c478bd9Sstevel@tonic-gate } 5297c478bd9Sstevel@tonic-gate } 5307c478bd9Sstevel@tonic-gate 53148011479Ssn199410 if (unlinkat(caller->fd, entry, flag) != 0) { 53248011479Ssn199410 err = errno; 53348011479Ssn199410 if (err == ENOENT) 53448011479Ssn199410 return (0); 53548011479Ssn199410 53648011479Ssn199410 if (flag != 0) { 53748011479Ssn199410 if (err == EINVAL) { 53848011479Ssn199410 (void) fprintf(stderr, gettext( 53948011479Ssn199410 "rm: Cannot remove any directory in the " 54048011479Ssn199410 "path of the current working directory\n" 54148011479Ssn199410 "%s\n"), pathbuf); 54248011479Ssn199410 } else { 54348011479Ssn199410 if (err == EEXIST) 54448011479Ssn199410 err = ENOTEMPTY; 54548011479Ssn199410 (void) fprintf(stderr, 54648011479Ssn199410 gettext("rm: Unable to remove directory %s:" 54748011479Ssn199410 " %s\n"), pathbuf, strerror(err)); 54848011479Ssn199410 } 54948011479Ssn199410 } else { 55048011479Ssn199410 #ifndef XPG4 55148011479Ssn199410 if (!silent || interactive) { 55248011479Ssn199410 #endif 55348011479Ssn199410 55448011479Ssn199410 (void) fprintf(stderr, 55548011479Ssn199410 gettext("rm: %s not removed: %s\n"), 55648011479Ssn199410 pathbuf, strerror(err)); 55748011479Ssn199410 #ifndef XPG4 55848011479Ssn199410 } 55948011479Ssn199410 #endif 56048011479Ssn199410 } 56148011479Ssn199410 errcnt++; 56248011479Ssn199410 } 56348011479Ssn199410 return (0); 56448011479Ssn199410 } 5657c478bd9Sstevel@tonic-gate 5667c478bd9Sstevel@tonic-gate static int 5677c478bd9Sstevel@tonic-gate yes(void) 5687c478bd9Sstevel@tonic-gate { 5697c478bd9Sstevel@tonic-gate int i, b; 5707c478bd9Sstevel@tonic-gate char ans[SCHAR_MAX + 1]; 5717c478bd9Sstevel@tonic-gate 5727c478bd9Sstevel@tonic-gate for (i = 0; ; i++) { 5737c478bd9Sstevel@tonic-gate b = getchar(); 5747c478bd9Sstevel@tonic-gate if (b == '\n' || b == '\0' || b == EOF) { 5757c478bd9Sstevel@tonic-gate ans[i] = 0; 5767c478bd9Sstevel@tonic-gate break; 5777c478bd9Sstevel@tonic-gate } 5787c478bd9Sstevel@tonic-gate if (i < SCHAR_MAX) 57948011479Ssn199410 ans[i] = (char)b; 5807c478bd9Sstevel@tonic-gate } 5817c478bd9Sstevel@tonic-gate if (i >= SCHAR_MAX) { 5827c478bd9Sstevel@tonic-gate i = SCHAR_MAX; 5837c478bd9Sstevel@tonic-gate ans[SCHAR_MAX] = 0; 5847c478bd9Sstevel@tonic-gate } 5857c478bd9Sstevel@tonic-gate if ((i == 0) | (strncmp(yeschr, ans, i))) 5867c478bd9Sstevel@tonic-gate return (0); 5877c478bd9Sstevel@tonic-gate return (1); 5887c478bd9Sstevel@tonic-gate } 5897c478bd9Sstevel@tonic-gate 5907c478bd9Sstevel@tonic-gate static int 59148011479Ssn199410 confirm(FILE *fp, const char *q, ...) 5927c478bd9Sstevel@tonic-gate { 59348011479Ssn199410 va_list ap; 5947c478bd9Sstevel@tonic-gate 59548011479Ssn199410 va_start(ap, q); 59648011479Ssn199410 (void) vfprintf(fp, q, ap); 59748011479Ssn199410 va_end(ap); 59848011479Ssn199410 return (yes()); 5997c478bd9Sstevel@tonic-gate } 6007c478bd9Sstevel@tonic-gate 60148011479Ssn199410 static void 60248011479Ssn199410 memerror(void) 60348011479Ssn199410 { 60448011479Ssn199410 (void) fprintf(stderr, gettext("rm: Insufficient memory.\n")); 60548011479Ssn199410 exit(1); 60648011479Ssn199410 } 6077c478bd9Sstevel@tonic-gate 6087c478bd9Sstevel@tonic-gate /* 60948011479Ssn199410 * If we can't stat "..", it's either not there or we can't search 61048011479Ssn199410 * the current directory; in that case we can't return back through 61148011479Ssn199410 * "..", so we need to keep the parent open. 61248011479Ssn199410 * Check that we came from "..", if not then this directory entry is an 61348011479Ssn199410 * additional link and there is risk of a filesystem cycle and we also 61448011479Ssn199410 * can't go back up through ".." and we keep the directory open. 6157c478bd9Sstevel@tonic-gate */ 61648011479Ssn199410 static int 61748011479Ssn199410 checkdir(struct dlist *caller, struct dlist *frmp) 61848011479Ssn199410 { 61948011479Ssn199410 struct stat up; 62048011479Ssn199410 struct dlist *ptr; 62148011479Ssn199410 62248011479Ssn199410 if (fstatat(frmp->fd, "..", &up, 0) != 0) { 62348011479Ssn199410 caller->flags |= DIR_CANTCLOSE; 62448011479Ssn199410 return (0); 62548011479Ssn199410 } else if (up.st_ino == caller->ino && up.st_dev == caller->dev) { 6267c478bd9Sstevel@tonic-gate return (0); 6277c478bd9Sstevel@tonic-gate } 6287c478bd9Sstevel@tonic-gate 62948011479Ssn199410 /* Directory hard link, check cycle */ 63048011479Ssn199410 for (ptr = caller; ptr != NULL; ptr = ptr->up) { 63148011479Ssn199410 if (frmp->dev == ptr->dev && frmp->ino == ptr->ino) { 6327c478bd9Sstevel@tonic-gate (void) fprintf(stderr, 63348011479Ssn199410 gettext("rm: cycle detected for %s\n"), pathbuf); 63448011479Ssn199410 errcnt++; 63548011479Ssn199410 return (-1); 6367c478bd9Sstevel@tonic-gate } 6377c478bd9Sstevel@tonic-gate } 63848011479Ssn199410 caller->flags |= DIR_CANTCLOSE; 639996aa816Ssn199410 return (0); 6407c478bd9Sstevel@tonic-gate } 641