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 /* 23*b2b3ca14SSumanth Naropanth * Copyright 2009 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 /* 317c478bd9Sstevel@tonic-gate * rm [-fiRr] file ... 327c478bd9Sstevel@tonic-gate */ 337c478bd9Sstevel@tonic-gate 3448011479Ssn199410 #include <sys/param.h> 357c478bd9Sstevel@tonic-gate #include <sys/stat.h> 367c478bd9Sstevel@tonic-gate #include <dirent.h> 3748011479Ssn199410 #include <errno.h> 3848011479Ssn199410 #include <fcntl.h> 3948011479Ssn199410 #include <langinfo.h> 407c478bd9Sstevel@tonic-gate #include <limits.h> 417c478bd9Sstevel@tonic-gate #include <locale.h> 4248011479Ssn199410 #include <stdarg.h> 4348011479Ssn199410 #include <stdio.h> 447c478bd9Sstevel@tonic-gate #include <stdlib.h> 4548011479Ssn199410 #include <string.h> 4648011479Ssn199410 #include <unistd.h> 4748011479Ssn199410 #include <values.h> 483d63ea05Sas145665 #include "getresponse.h" 497c478bd9Sstevel@tonic-gate 5048011479Ssn199410 #define DIR_CANTCLOSE 1 517c478bd9Sstevel@tonic-gate 5248011479Ssn199410 static struct stat rootdir; 5348011479Ssn199410 5448011479Ssn199410 struct dlist { 5548011479Ssn199410 int fd; /* Stores directory fd */ 5648011479Ssn199410 int flags; /* DIR_* Flags */ 5748011479Ssn199410 DIR *dp; /* Open directory (opened with fd) */ 5848011479Ssn199410 long diroff; /* Saved directory offset when closing */ 5948011479Ssn199410 struct dlist *up; /* Up one step in the tree (toward "/") */ 6048011479Ssn199410 struct dlist *down; /* Down one step in the tree */ 6148011479Ssn199410 ino_t ino; /* st_ino of directory */ 6248011479Ssn199410 dev_t dev; /* st_dev of directory */ 6348011479Ssn199410 int pathend; /* Offset of name end in the pathbuffer */ 6448011479Ssn199410 }; 6548011479Ssn199410 6648011479Ssn199410 static struct dlist top = { 6748011479Ssn199410 (int)AT_FDCWD, 6848011479Ssn199410 DIR_CANTCLOSE, 6948011479Ssn199410 }; 707c478bd9Sstevel@tonic-gate 7148011479Ssn199410 static struct dlist *cur, *rec; 727c478bd9Sstevel@tonic-gate 7348011479Ssn199410 static int rm(const char *, struct dlist *); 7448011479Ssn199410 static int confirm(FILE *, const char *, ...); 7548011479Ssn199410 static void memerror(void); 7648011479Ssn199410 static int checkdir(struct dlist *, struct dlist *); 7748011479Ssn199410 static int errcnt; 7848011479Ssn199410 static boolean_t silent, interactive, recursive, ontty; 797c478bd9Sstevel@tonic-gate 8048011479Ssn199410 static char *pathbuf; 81131f4edeSsn199410 static size_t pathbuflen = MAXPATHLEN; 827c478bd9Sstevel@tonic-gate 8348011479Ssn199410 static int maxfds = MAXINT; 8448011479Ssn199410 static int nfds; 857c478bd9Sstevel@tonic-gate 867c478bd9Sstevel@tonic-gate int 8748011479Ssn199410 main(int argc, char **argv) 887c478bd9Sstevel@tonic-gate { 897c478bd9Sstevel@tonic-gate int errflg = 0; 907c478bd9Sstevel@tonic-gate int c; 917c478bd9Sstevel@tonic-gate 927c478bd9Sstevel@tonic-gate (void) setlocale(LC_ALL, ""); 937c478bd9Sstevel@tonic-gate #if !defined(TEXT_DOMAIN) /* Should be defined by cc -D */ 947c478bd9Sstevel@tonic-gate #define TEXT_DOMAIN "SYS_TEST" /* Use this only if it weren't */ 957c478bd9Sstevel@tonic-gate #endif 967c478bd9Sstevel@tonic-gate (void) textdomain(TEXT_DOMAIN); 977c478bd9Sstevel@tonic-gate 987c478bd9Sstevel@tonic-gate while ((c = getopt(argc, argv, "frRi")) != EOF) 997c478bd9Sstevel@tonic-gate switch (c) { 1007c478bd9Sstevel@tonic-gate case 'f': 10148011479Ssn199410 silent = B_TRUE; 1027c478bd9Sstevel@tonic-gate #ifdef XPG4 10348011479Ssn199410 interactive = B_FALSE; 1047c478bd9Sstevel@tonic-gate #endif 1057c478bd9Sstevel@tonic-gate break; 1067c478bd9Sstevel@tonic-gate case 'i': 10748011479Ssn199410 interactive = B_TRUE; 1087c478bd9Sstevel@tonic-gate #ifdef XPG4 10948011479Ssn199410 silent = B_FALSE; 1107c478bd9Sstevel@tonic-gate #endif 1117c478bd9Sstevel@tonic-gate break; 1127c478bd9Sstevel@tonic-gate case 'r': 1137c478bd9Sstevel@tonic-gate case 'R': 11448011479Ssn199410 recursive = B_TRUE; 1157c478bd9Sstevel@tonic-gate break; 1167c478bd9Sstevel@tonic-gate case '?': 1177c478bd9Sstevel@tonic-gate errflg = 1; 1187c478bd9Sstevel@tonic-gate break; 1197c478bd9Sstevel@tonic-gate } 1207c478bd9Sstevel@tonic-gate 1217c478bd9Sstevel@tonic-gate /* 1227c478bd9Sstevel@tonic-gate * For BSD compatibility allow '-' to delimit the end 1237c478bd9Sstevel@tonic-gate * of options. However, if options were already explicitly 1247c478bd9Sstevel@tonic-gate * terminated with '--', then treat '-' literally: otherwise, 1257c478bd9Sstevel@tonic-gate * "rm -- -" won't remove '-'. 1267c478bd9Sstevel@tonic-gate */ 1277c478bd9Sstevel@tonic-gate if (optind < argc && 1287c478bd9Sstevel@tonic-gate strcmp(argv[optind], "-") == 0 && 1297c478bd9Sstevel@tonic-gate strcmp(argv[optind - 1], "--") != 0) 1307c478bd9Sstevel@tonic-gate optind++; 1317c478bd9Sstevel@tonic-gate 1327c478bd9Sstevel@tonic-gate argc -= optind; 1337c478bd9Sstevel@tonic-gate argv = &argv[optind]; 1347c478bd9Sstevel@tonic-gate 1357c478bd9Sstevel@tonic-gate if ((argc < 1 && !silent) || errflg) { 13648011479Ssn199410 (void) fprintf(stderr, gettext("usage: rm [-fiRr] file ...\n")); 13748011479Ssn199410 exit(2); 13848011479Ssn199410 } 13948011479Ssn199410 14048011479Ssn199410 ontty = isatty(STDIN_FILENO) != 0; 14148011479Ssn199410 14248011479Ssn199410 if (recursive && stat("/", &rootdir) != 0) { 1437c478bd9Sstevel@tonic-gate (void) fprintf(stderr, 14448011479Ssn199410 gettext("rm: cannot stat root directory: %s\n"), 14548011479Ssn199410 strerror(errno)); 1467c478bd9Sstevel@tonic-gate exit(2); 1477c478bd9Sstevel@tonic-gate } 1487c478bd9Sstevel@tonic-gate 149131f4edeSsn199410 pathbuf = malloc(pathbuflen); 150131f4edeSsn199410 if (pathbuf == NULL) 151131f4edeSsn199410 memerror(); 152131f4edeSsn199410 1533d63ea05Sas145665 if (init_yes() < 0) { 1543d63ea05Sas145665 (void) fprintf(stderr, gettext(ERR_MSG_INIT_YES), 1553d63ea05Sas145665 strerror(errno)); 1563d63ea05Sas145665 exit(2); 1573d63ea05Sas145665 } 1583d63ea05Sas145665 15948011479Ssn199410 for (; *argv != NULL; argv++) { 16048011479Ssn199410 char *p = strrchr(*argv, '/'); 16148011479Ssn199410 if (p == NULL) 16248011479Ssn199410 p = *argv; 16348011479Ssn199410 else 16448011479Ssn199410 p = p + 1; 16548011479Ssn199410 if (strcmp(p, ".") == 0 || strcmp(p, "..") == 0) { 16648011479Ssn199410 (void) fprintf(stderr, 16748011479Ssn199410 gettext("rm of %s is not allowed\n"), *argv); 16848011479Ssn199410 errcnt++; 16948011479Ssn199410 continue; 17048011479Ssn199410 } 17148011479Ssn199410 /* Retry when we can't walk back up. */ 17248011479Ssn199410 while (rm(*argv, rec = cur = &top) != 0) 173996aa816Ssn199410 ; 1747c478bd9Sstevel@tonic-gate } 17512a9e0efSsn199410 17648011479Ssn199410 return (errcnt != 0 ? 2 : 0); 17748011479Ssn199410 } 17848011479Ssn199410 17948011479Ssn199410 static void 18048011479Ssn199410 pushfilename(const char *fname) 18148011479Ssn199410 { 18248011479Ssn199410 char *p; 18348011479Ssn199410 const char *q = fname; 18448011479Ssn199410 18548011479Ssn199410 if (cur == &top) { 18648011479Ssn199410 p = pathbuf; 18748011479Ssn199410 } else { 18848011479Ssn199410 p = pathbuf + cur->up->pathend; 18948011479Ssn199410 *p++ = '/'; 19048011479Ssn199410 } 19148011479Ssn199410 while (*q != '\0') { 19248011479Ssn199410 if (p - pathbuf + 2 >= pathbuflen) { 19348011479Ssn199410 char *np; 19448011479Ssn199410 pathbuflen += MAXPATHLEN; 19548011479Ssn199410 np = realloc(pathbuf, pathbuflen); 19648011479Ssn199410 if (np == NULL) 19748011479Ssn199410 memerror(); 19848011479Ssn199410 p = np + (p - pathbuf); 19948011479Ssn199410 pathbuf = np; 20048011479Ssn199410 } 20148011479Ssn199410 *p++ = *q++; 20248011479Ssn199410 } 20348011479Ssn199410 *p = '\0'; 20448011479Ssn199410 cur->pathend = p - pathbuf; 20548011479Ssn199410 } 20648011479Ssn199410 20748011479Ssn199410 static void 20848011479Ssn199410 closeframe(struct dlist *frm) 20948011479Ssn199410 { 21048011479Ssn199410 if (frm->dp != NULL) { 21148011479Ssn199410 (void) closedir(frm->dp); 21248011479Ssn199410 nfds--; 21348011479Ssn199410 frm->dp = NULL; 21448011479Ssn199410 frm->fd = -1; 21548011479Ssn199410 } 2167c478bd9Sstevel@tonic-gate } 2177c478bd9Sstevel@tonic-gate 218996aa816Ssn199410 static int 21948011479Ssn199410 reclaim(void) 2207c478bd9Sstevel@tonic-gate { 22148011479Ssn199410 while (rec != NULL && (rec->flags & DIR_CANTCLOSE) != 0) 22248011479Ssn199410 rec = rec->down; 22348011479Ssn199410 if (rec == NULL || rec == cur || rec->dp == NULL) 22448011479Ssn199410 return (-1); 22548011479Ssn199410 rec->diroff = telldir(rec->dp); 22648011479Ssn199410 closeframe(rec); 22748011479Ssn199410 rec = rec->down; 22848011479Ssn199410 return (0); 22948011479Ssn199410 } 23048011479Ssn199410 23148011479Ssn199410 static void 23248011479Ssn199410 pushdir(struct dlist *frm) 23348011479Ssn199410 { 23448011479Ssn199410 frm->up = cur; 23548011479Ssn199410 frm->down = NULL; 23648011479Ssn199410 cur->down = frm; 23748011479Ssn199410 cur = frm; 23848011479Ssn199410 } 23948011479Ssn199410 24048011479Ssn199410 static int 24148011479Ssn199410 opendirat(int dirfd, const char *entry, struct dlist *frm) 24248011479Ssn199410 { 24348011479Ssn199410 int fd; 24448011479Ssn199410 24548011479Ssn199410 if (nfds >= maxfds) 24648011479Ssn199410 (void) reclaim(); 24748011479Ssn199410 24848011479Ssn199410 while ((fd = openat(dirfd, entry, O_RDONLY|O_NONBLOCK)) == -1 && 24948011479Ssn199410 errno == EMFILE) { 25048011479Ssn199410 if (nfds < maxfds) 25148011479Ssn199410 maxfds = nfds; 25248011479Ssn199410 if (reclaim() != 0) 25348011479Ssn199410 return (-1); 25448011479Ssn199410 } 25548011479Ssn199410 if (fd < 0) 25648011479Ssn199410 return (-1); 25748011479Ssn199410 frm->fd = fd; 25848011479Ssn199410 frm->dp = fdopendir(fd); 25948011479Ssn199410 if (frm->dp == NULL) { 26048011479Ssn199410 (void) close(fd); 26148011479Ssn199410 return (-1); 26248011479Ssn199410 } 26348011479Ssn199410 nfds++; 26448011479Ssn199410 return (0); 26548011479Ssn199410 } 2667c478bd9Sstevel@tonic-gate 2677c478bd9Sstevel@tonic-gate /* 26848011479Ssn199410 * Since we never pop the top frame, cur->up can never be NULL. 26948011479Ssn199410 * If we pop beyond a frame we closed, we try to reopen "..". 2707c478bd9Sstevel@tonic-gate */ 27148011479Ssn199410 static int 27248011479Ssn199410 popdir(boolean_t noerror) 27348011479Ssn199410 { 27448011479Ssn199410 struct stat buf; 27548011479Ssn199410 int ret = noerror ? 0 : -1; 27648011479Ssn199410 pathbuf[cur->up->pathend] = '\0'; 27748011479Ssn199410 27848011479Ssn199410 if (noerror && cur->up->fd == -1) { 27948011479Ssn199410 rec = cur->up; 28048011479Ssn199410 if (opendirat(cur->fd, "..", rec) != 0 || 28148011479Ssn199410 fstat(rec->fd, &buf) != 0) { 28248011479Ssn199410 (void) fprintf(stderr, 28348011479Ssn199410 gettext("rm: cannot reopen %s: %s\n"), 28448011479Ssn199410 pathbuf, strerror(errno)); 28548011479Ssn199410 exit(2); 28648011479Ssn199410 } 28748011479Ssn199410 if (rec->ino != buf.st_ino || rec->dev != buf.st_dev) { 28848011479Ssn199410 (void) fprintf(stderr, gettext("rm: WARNING: " 28948011479Ssn199410 "The directory %s was moved or linked to " 29048011479Ssn199410 "another directory during the execution of rm\n"), 29148011479Ssn199410 pathbuf); 29248011479Ssn199410 closeframe(rec); 29348011479Ssn199410 ret = -1; 29448011479Ssn199410 } else { 29548011479Ssn199410 /* If telldir failed, we take it from the top. */ 29648011479Ssn199410 if (rec->diroff != -1) 29748011479Ssn199410 seekdir(rec->dp, rec->diroff); 29848011479Ssn199410 } 29948011479Ssn199410 } else if (rec == cur) 30048011479Ssn199410 rec = cur->up; 30148011479Ssn199410 closeframe(cur); 30248011479Ssn199410 cur = cur->up; 30348011479Ssn199410 cur->down = NULL; 30448011479Ssn199410 return (ret); 30548011479Ssn199410 } 30648011479Ssn199410 30748011479Ssn199410 /* 30848011479Ssn199410 * The stack frame of this function is minimized so that we can 30948011479Ssn199410 * recurse quite a bit before we overflow the stack; around 31048011479Ssn199410 * 30,000-40,000 nested directories can be removed with the default 31148011479Ssn199410 * stack limit. 31248011479Ssn199410 */ 31348011479Ssn199410 static int 31448011479Ssn199410 rm(const char *entry, struct dlist *caller) 31548011479Ssn199410 { 31648011479Ssn199410 struct dlist frame; 31748011479Ssn199410 int flag; 31848011479Ssn199410 struct stat temp; 31948011479Ssn199410 struct dirent *dent; 32048011479Ssn199410 int err; 32148011479Ssn199410 32248011479Ssn199410 /* 32348011479Ssn199410 * Construct the pathname: note that the entry may live in memory 32448011479Ssn199410 * allocated by readdir and that after return from recursion 32548011479Ssn199410 * the memory is no longer valid. So after the recursive rm() 32648011479Ssn199410 * call, we use the global pathbuf instead of the entry argument. 32748011479Ssn199410 */ 32848011479Ssn199410 pushfilename(entry); 32948011479Ssn199410 33048011479Ssn199410 if (fstatat(caller->fd, entry, &temp, AT_SYMLINK_NOFOLLOW) != 0) { 3317c478bd9Sstevel@tonic-gate if (!silent) { 33248011479Ssn199410 (void) fprintf(stderr, "rm: %s: %s\n", pathbuf, 33348011479Ssn199410 strerror(errno)); 33448011479Ssn199410 errcnt++; 3357c478bd9Sstevel@tonic-gate } 336996aa816Ssn199410 return (0); 3377c478bd9Sstevel@tonic-gate } 3387c478bd9Sstevel@tonic-gate 33948011479Ssn199410 if (S_ISDIR(temp.st_mode)) { 3407c478bd9Sstevel@tonic-gate /* 3417c478bd9Sstevel@tonic-gate * If "-r" wasn't specified, trying to remove directories 3427c478bd9Sstevel@tonic-gate * is an error. 3437c478bd9Sstevel@tonic-gate */ 3447c478bd9Sstevel@tonic-gate if (!recursive) { 3457c478bd9Sstevel@tonic-gate (void) fprintf(stderr, 34648011479Ssn199410 gettext("rm: %s is a directory\n"), pathbuf); 34748011479Ssn199410 errcnt++; 348996aa816Ssn199410 return (0); 3497c478bd9Sstevel@tonic-gate } 3507c478bd9Sstevel@tonic-gate 35148011479Ssn199410 if (temp.st_ino == rootdir.st_ino && 35248011479Ssn199410 temp.st_dev == rootdir.st_dev) { 3537c478bd9Sstevel@tonic-gate (void) fprintf(stderr, 35448011479Ssn199410 gettext("rm of %s is not allowed\n"), "/"); 35548011479Ssn199410 errcnt++; 356996aa816Ssn199410 return (0); 3577c478bd9Sstevel@tonic-gate } 3587c478bd9Sstevel@tonic-gate /* 3597c478bd9Sstevel@tonic-gate * TRANSLATION_NOTE - The following message will contain the 3607c478bd9Sstevel@tonic-gate * first character of the strings for "yes" and "no" defined 3617c478bd9Sstevel@tonic-gate * in the file "nl_langinfo.po". After substitution, the 3627c478bd9Sstevel@tonic-gate * message will appear as follows: 3637c478bd9Sstevel@tonic-gate * rm: examine files in directory <directoryname> (y/n)? 3647c478bd9Sstevel@tonic-gate * where <directoryname> is the directory to be removed 3657c478bd9Sstevel@tonic-gate * 3667c478bd9Sstevel@tonic-gate */ 36748011479Ssn199410 if (interactive && !confirm(stderr, 3687c478bd9Sstevel@tonic-gate gettext("rm: examine files in directory %s (%s/%s)? "), 3693d63ea05Sas145665 pathbuf, yesstr, nostr)) { 37048011479Ssn199410 return (0); 3717c478bd9Sstevel@tonic-gate } 3727c478bd9Sstevel@tonic-gate 37348011479Ssn199410 frame.dev = temp.st_dev; 37448011479Ssn199410 frame.ino = temp.st_ino; 37548011479Ssn199410 frame.flags = 0; 37648011479Ssn199410 flag = AT_REMOVEDIR; 37748011479Ssn199410 3787c478bd9Sstevel@tonic-gate #ifdef XPG4 3797c478bd9Sstevel@tonic-gate /* 38048011479Ssn199410 * XCU4 and POSIX.2: If not interactive, check to see whether 3817c478bd9Sstevel@tonic-gate * or not directory is readable or writable and if not, 3827c478bd9Sstevel@tonic-gate * prompt user for response. 3837c478bd9Sstevel@tonic-gate */ 38448011479Ssn199410 if (ontty && !interactive && !silent && 385*b2b3ca14SSumanth Naropanth faccessat(caller->fd, entry, W_OK|X_OK, AT_EACCESS) != 0 && 38648011479Ssn199410 !confirm(stderr, 38748011479Ssn199410 gettext("rm: examine files in directory %s (%s/%s)? "), 3883d63ea05Sas145665 pathbuf, yesstr, nostr)) { 38948011479Ssn199410 return (0); 3907c478bd9Sstevel@tonic-gate } 3917c478bd9Sstevel@tonic-gate #endif 39248011479Ssn199410 if (opendirat(caller->fd, entry, &frame) == -1) { 39348011479Ssn199410 err = errno; 3947c478bd9Sstevel@tonic-gate 3957c478bd9Sstevel@tonic-gate if (interactive) { 3967c478bd9Sstevel@tonic-gate /* 3977c478bd9Sstevel@tonic-gate * Print an error message that 3987c478bd9Sstevel@tonic-gate * we could not read the directory 3997c478bd9Sstevel@tonic-gate * as the user wanted to examine 4007c478bd9Sstevel@tonic-gate * files in the directory. Only 4017c478bd9Sstevel@tonic-gate * affect the error status if 4027c478bd9Sstevel@tonic-gate * user doesn't want to remove the 4037c478bd9Sstevel@tonic-gate * directory as we still may be able 4047c478bd9Sstevel@tonic-gate * remove the directory successfully. 4057c478bd9Sstevel@tonic-gate */ 4067c478bd9Sstevel@tonic-gate (void) fprintf(stderr, gettext( 40748011479Ssn199410 "rm: cannot read directory %s: %s\n"), 40848011479Ssn199410 pathbuf, strerror(err)); 40948011479Ssn199410 41048011479Ssn199410 /* 41148011479Ssn199410 * TRANSLATION_NOTE - The following message will contain the 41248011479Ssn199410 * first character of the strings for "yes" and "no" defined 41348011479Ssn199410 * in the file "nl_langinfo.po". After substitution, the 41448011479Ssn199410 * message will appear as follows: 41548011479Ssn199410 * rm: remove <filename> (y/n)? 41648011479Ssn199410 * For example, in German, this will appear as 41748011479Ssn199410 * rm: l�schen <filename> (j/n)? 41848011479Ssn199410 * where j=ja, n=nein, <filename>=the file to be removed 41948011479Ssn199410 */ 42048011479Ssn199410 if (!confirm(stderr, 42148011479Ssn199410 gettext("rm: remove %s (%s/%s)? "), 4223d63ea05Sas145665 pathbuf, yesstr, nostr)) { 42348011479Ssn199410 errcnt++; 42448011479Ssn199410 return (0); 4257c478bd9Sstevel@tonic-gate } 4267c478bd9Sstevel@tonic-gate } 42748011479Ssn199410 /* If it's empty we may still be able to rm it */ 42848011479Ssn199410 if (unlinkat(caller->fd, entry, flag) == 0) 42948011479Ssn199410 return (0); 43048011479Ssn199410 if (interactive) 43148011479Ssn199410 err = errno; 43248011479Ssn199410 (void) fprintf(stderr, 43348011479Ssn199410 interactive ? 43448011479Ssn199410 gettext("rm: Unable to remove directory %s: %s\n") : 43548011479Ssn199410 gettext("rm: cannot read directory %s: %s\n"), 43648011479Ssn199410 pathbuf, strerror(err)); 43748011479Ssn199410 errcnt++; 43848011479Ssn199410 return (0); 43948011479Ssn199410 } 4407c478bd9Sstevel@tonic-gate 4417c478bd9Sstevel@tonic-gate /* 44248011479Ssn199410 * There is a race condition here too; if we open a directory 44348011479Ssn199410 * we have to make sure it's still the same directory we 44448011479Ssn199410 * stat'ed and checked against root earlier. Let's check. 4457c478bd9Sstevel@tonic-gate */ 44648011479Ssn199410 if (fstat(frame.fd, &temp) != 0 || 44748011479Ssn199410 frame.ino != temp.st_ino || 44848011479Ssn199410 frame.dev != temp.st_dev) { 44948011479Ssn199410 (void) fprintf(stderr, 45048011479Ssn199410 gettext("rm: %s: directory renamed\n"), pathbuf); 45148011479Ssn199410 closeframe(&frame); 45248011479Ssn199410 errcnt++; 45348011479Ssn199410 return (0); 4547c478bd9Sstevel@tonic-gate } 4557c478bd9Sstevel@tonic-gate 45648011479Ssn199410 if (caller != &top) { 45748011479Ssn199410 if (checkdir(caller, &frame) != 0) { 45848011479Ssn199410 closeframe(&frame); 45948011479Ssn199410 goto unlinkit; 4607c478bd9Sstevel@tonic-gate } 46148011479Ssn199410 } 46248011479Ssn199410 pushdir(&frame); 4637c478bd9Sstevel@tonic-gate 4647c478bd9Sstevel@tonic-gate /* 46548011479Ssn199410 * rm() only returns -1 if popdir failed at some point; 46648011479Ssn199410 * frame.dp is no longer reliable and we must drop out. 4677c478bd9Sstevel@tonic-gate */ 46848011479Ssn199410 while ((dent = readdir(frame.dp)) != NULL) { 46948011479Ssn199410 if (strcmp(dent->d_name, ".") == 0 || 47048011479Ssn199410 strcmp(dent->d_name, "..") == 0) 4717c478bd9Sstevel@tonic-gate continue; 4727c478bd9Sstevel@tonic-gate 47348011479Ssn199410 if (rm(dent->d_name, &frame) != 0) 474996aa816Ssn199410 break; 4757c478bd9Sstevel@tonic-gate } 4767c478bd9Sstevel@tonic-gate 47748011479Ssn199410 if (popdir(dent == NULL) != 0) 478996aa816Ssn199410 return (-1); 4797c478bd9Sstevel@tonic-gate 4807c478bd9Sstevel@tonic-gate /* 48148011479Ssn199410 * We recursed and the subdirectory may have set the CANTCLOSE 48248011479Ssn199410 * flag; we need to clear it except for &top. 48348011479Ssn199410 * Recursion may have invalidated entry because of closedir(). 4847c478bd9Sstevel@tonic-gate */ 48548011479Ssn199410 if (caller != &top) { 48648011479Ssn199410 caller->flags &= ~DIR_CANTCLOSE; 48748011479Ssn199410 entry = &pathbuf[caller->up->pathend + 1]; 4887c478bd9Sstevel@tonic-gate } 48948011479Ssn199410 } else { 49048011479Ssn199410 flag = 0; 4917c478bd9Sstevel@tonic-gate } 49248011479Ssn199410 unlinkit: 4937c478bd9Sstevel@tonic-gate /* 4947c478bd9Sstevel@tonic-gate * If interactive, ask for acknowledgement. 4957c478bd9Sstevel@tonic-gate */ 4967c478bd9Sstevel@tonic-gate if (interactive) { 49748011479Ssn199410 if (!confirm(stderr, gettext("rm: remove %s (%s/%s)? "), 4983d63ea05Sas145665 pathbuf, yesstr, nostr)) { 49948011479Ssn199410 return (0); 5007c478bd9Sstevel@tonic-gate } 50148011479Ssn199410 } else if (!silent && flag == 0) { 50248011479Ssn199410 /* 50348011479Ssn199410 * If not silent, and stdin is a terminal, and there's 50448011479Ssn199410 * no write access, and the file isn't a symbolic link, 50548011479Ssn199410 * ask for permission. If flag is set, then we know it's 50648011479Ssn199410 * a directory so we skip this test as it was done above. 50748011479Ssn199410 * 50848011479Ssn199410 * TRANSLATION_NOTE - The following message will contain the 50948011479Ssn199410 * first character of the strings for "yes" and "no" defined 51048011479Ssn199410 * in the file "nl_langinfo.po". After substitution, the 51148011479Ssn199410 * message will appear as follows: 51248011479Ssn199410 * rm: <filename>: override protection XXX (y/n)? 51348011479Ssn199410 * where XXX is the permission mode bits of the file in octal 51448011479Ssn199410 * and <filename> is the file to be removed 51548011479Ssn199410 * 51648011479Ssn199410 */ 51748011479Ssn199410 if (ontty && !S_ISLNK(temp.st_mode) && 518*b2b3ca14SSumanth Naropanth faccessat(caller->fd, entry, W_OK, AT_EACCESS) != 0 && 51948011479Ssn199410 !confirm(stdout, 52048011479Ssn199410 gettext("rm: %s: override protection %o (%s/%s)? "), 5213d63ea05Sas145665 pathbuf, temp.st_mode & 0777, yesstr, nostr)) { 52248011479Ssn199410 return (0); 5237c478bd9Sstevel@tonic-gate } 5247c478bd9Sstevel@tonic-gate } 5257c478bd9Sstevel@tonic-gate 52648011479Ssn199410 if (unlinkat(caller->fd, entry, flag) != 0) { 52748011479Ssn199410 err = errno; 52848011479Ssn199410 if (err == ENOENT) 52948011479Ssn199410 return (0); 53048011479Ssn199410 53148011479Ssn199410 if (flag != 0) { 53248011479Ssn199410 if (err == EINVAL) { 53348011479Ssn199410 (void) fprintf(stderr, gettext( 53448011479Ssn199410 "rm: Cannot remove any directory in the " 53548011479Ssn199410 "path of the current working directory\n" 53648011479Ssn199410 "%s\n"), pathbuf); 53748011479Ssn199410 } else { 53848011479Ssn199410 if (err == EEXIST) 53948011479Ssn199410 err = ENOTEMPTY; 54048011479Ssn199410 (void) fprintf(stderr, 54148011479Ssn199410 gettext("rm: Unable to remove directory %s:" 54248011479Ssn199410 " %s\n"), pathbuf, strerror(err)); 54348011479Ssn199410 } 54448011479Ssn199410 } else { 54548011479Ssn199410 #ifndef XPG4 54648011479Ssn199410 if (!silent || interactive) { 54748011479Ssn199410 #endif 54848011479Ssn199410 54948011479Ssn199410 (void) fprintf(stderr, 55048011479Ssn199410 gettext("rm: %s not removed: %s\n"), 55148011479Ssn199410 pathbuf, strerror(err)); 55248011479Ssn199410 #ifndef XPG4 55348011479Ssn199410 } 55448011479Ssn199410 #endif 55548011479Ssn199410 } 55648011479Ssn199410 errcnt++; 55748011479Ssn199410 } 55848011479Ssn199410 return (0); 55948011479Ssn199410 } 5607c478bd9Sstevel@tonic-gate 5617c478bd9Sstevel@tonic-gate static int 56248011479Ssn199410 confirm(FILE *fp, const char *q, ...) 5637c478bd9Sstevel@tonic-gate { 56448011479Ssn199410 va_list ap; 5657c478bd9Sstevel@tonic-gate 56648011479Ssn199410 va_start(ap, q); 56748011479Ssn199410 (void) vfprintf(fp, q, ap); 56848011479Ssn199410 va_end(ap); 56948011479Ssn199410 return (yes()); 5707c478bd9Sstevel@tonic-gate } 5717c478bd9Sstevel@tonic-gate 57248011479Ssn199410 static void 57348011479Ssn199410 memerror(void) 57448011479Ssn199410 { 57548011479Ssn199410 (void) fprintf(stderr, gettext("rm: Insufficient memory.\n")); 57648011479Ssn199410 exit(1); 57748011479Ssn199410 } 5787c478bd9Sstevel@tonic-gate 5797c478bd9Sstevel@tonic-gate /* 58048011479Ssn199410 * If we can't stat "..", it's either not there or we can't search 58148011479Ssn199410 * the current directory; in that case we can't return back through 58248011479Ssn199410 * "..", so we need to keep the parent open. 58348011479Ssn199410 * Check that we came from "..", if not then this directory entry is an 58448011479Ssn199410 * additional link and there is risk of a filesystem cycle and we also 58548011479Ssn199410 * can't go back up through ".." and we keep the directory open. 5867c478bd9Sstevel@tonic-gate */ 58748011479Ssn199410 static int 58848011479Ssn199410 checkdir(struct dlist *caller, struct dlist *frmp) 58948011479Ssn199410 { 59048011479Ssn199410 struct stat up; 59148011479Ssn199410 struct dlist *ptr; 59248011479Ssn199410 59348011479Ssn199410 if (fstatat(frmp->fd, "..", &up, 0) != 0) { 59448011479Ssn199410 caller->flags |= DIR_CANTCLOSE; 59548011479Ssn199410 return (0); 59648011479Ssn199410 } else if (up.st_ino == caller->ino && up.st_dev == caller->dev) { 5977c478bd9Sstevel@tonic-gate return (0); 5987c478bd9Sstevel@tonic-gate } 5997c478bd9Sstevel@tonic-gate 60048011479Ssn199410 /* Directory hard link, check cycle */ 60148011479Ssn199410 for (ptr = caller; ptr != NULL; ptr = ptr->up) { 60248011479Ssn199410 if (frmp->dev == ptr->dev && frmp->ino == ptr->ino) { 6037c478bd9Sstevel@tonic-gate (void) fprintf(stderr, 60448011479Ssn199410 gettext("rm: cycle detected for %s\n"), pathbuf); 60548011479Ssn199410 errcnt++; 60648011479Ssn199410 return (-1); 6077c478bd9Sstevel@tonic-gate } 6087c478bd9Sstevel@tonic-gate } 60948011479Ssn199410 caller->flags |= DIR_CANTCLOSE; 610996aa816Ssn199410 return (0); 6117c478bd9Sstevel@tonic-gate } 612