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
main(int argc,char ** argv)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
pushfilename(const char * fname)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
closeframe(struct dlist * frm)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
reclaim(void)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
pushdir(struct dlist * frm)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
opendirat(int dirfd,const char * entry,struct dlist * frm)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
popdir(boolean_t noerror)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
rm(const char * entry,struct dlist * caller)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
confirm(FILE * fp,const char * q,...)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
memerror(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
checkdir(struct dlist * caller,struct dlist * frmp)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