10Sstevel@tonic-gate /*
20Sstevel@tonic-gate * CDDL HEADER START
30Sstevel@tonic-gate *
40Sstevel@tonic-gate * The contents of this file are subject to the terms of the
52473Sas145665 * Common Development and Distribution License (the "License").
62473Sas145665 * You may not use this file except in compliance with the License.
70Sstevel@tonic-gate *
80Sstevel@tonic-gate * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
90Sstevel@tonic-gate * or http://www.opensolaris.org/os/licensing.
100Sstevel@tonic-gate * See the License for the specific language governing permissions
110Sstevel@tonic-gate * and limitations under the License.
120Sstevel@tonic-gate *
130Sstevel@tonic-gate * When distributing Covered Code, include this CDDL HEADER in each
140Sstevel@tonic-gate * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
150Sstevel@tonic-gate * If applicable, add the following below this CDDL HEADER, with the
160Sstevel@tonic-gate * fields enclosed by brackets "[]" replaced with your own identifying
170Sstevel@tonic-gate * information: Portions Copyright [yyyy] [name of copyright owner]
180Sstevel@tonic-gate *
190Sstevel@tonic-gate * CDDL HEADER END
200Sstevel@tonic-gate */
210Sstevel@tonic-gate
220Sstevel@tonic-gate /*
23*9880SSumanth.Naropanth@Sun.COM * Copyright 2009 Sun Microsystems, Inc. All rights reserved.
240Sstevel@tonic-gate * Use is subject to license terms.
250Sstevel@tonic-gate */
260Sstevel@tonic-gate
272790Sas145665 /* Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T */
282790Sas145665 /* All Rights Reserved */
292790Sas145665
300Sstevel@tonic-gate /*
310Sstevel@tonic-gate * rm [-fiRr] file ...
320Sstevel@tonic-gate */
330Sstevel@tonic-gate
343855Ssn199410 #include <sys/param.h>
350Sstevel@tonic-gate #include <sys/stat.h>
360Sstevel@tonic-gate #include <dirent.h>
373855Ssn199410 #include <errno.h>
383855Ssn199410 #include <fcntl.h>
393855Ssn199410 #include <langinfo.h>
400Sstevel@tonic-gate #include <limits.h>
410Sstevel@tonic-gate #include <locale.h>
423855Ssn199410 #include <stdarg.h>
433855Ssn199410 #include <stdio.h>
440Sstevel@tonic-gate #include <stdlib.h>
453855Ssn199410 #include <string.h>
463855Ssn199410 #include <unistd.h>
473855Ssn199410 #include <values.h>
484774Sas145665 #include "getresponse.h"
490Sstevel@tonic-gate
503855Ssn199410 #define DIR_CANTCLOSE 1
510Sstevel@tonic-gate
523855Ssn199410 static struct stat rootdir;
530Sstevel@tonic-gate
543855Ssn199410 struct dlist {
553855Ssn199410 int fd; /* Stores directory fd */
563855Ssn199410 int flags; /* DIR_* Flags */
573855Ssn199410 DIR *dp; /* Open directory (opened with fd) */
583855Ssn199410 long diroff; /* Saved directory offset when closing */
593855Ssn199410 struct dlist *up; /* Up one step in the tree (toward "/") */
603855Ssn199410 struct dlist *down; /* Down one step in the tree */
613855Ssn199410 ino_t ino; /* st_ino of directory */
623855Ssn199410 dev_t dev; /* st_dev of directory */
633855Ssn199410 int pathend; /* Offset of name end in the pathbuffer */
640Sstevel@tonic-gate };
650Sstevel@tonic-gate
663855Ssn199410 static struct dlist top = {
673855Ssn199410 (int)AT_FDCWD,
683855Ssn199410 DIR_CANTCLOSE,
693855Ssn199410 };
703855Ssn199410
713855Ssn199410 static struct dlist *cur, *rec;
723855Ssn199410
733855Ssn199410 static int rm(const char *, struct dlist *);
743855Ssn199410 static int confirm(FILE *, const char *, ...);
753855Ssn199410 static void memerror(void);
763855Ssn199410 static int checkdir(struct dlist *, struct dlist *);
773855Ssn199410 static int errcnt;
783855Ssn199410 static boolean_t silent, interactive, recursive, ontty;
793855Ssn199410
803855Ssn199410 static char *pathbuf;
814054Ssn199410 static size_t pathbuflen = MAXPATHLEN;
823855Ssn199410
833855Ssn199410 static int maxfds = MAXINT;
843855Ssn199410 static int nfds;
853855Ssn199410
860Sstevel@tonic-gate int
main(int argc,char ** argv)873855Ssn199410 main(int argc, char **argv)
880Sstevel@tonic-gate {
893855Ssn199410 int errflg = 0;
903855Ssn199410 int c;
910Sstevel@tonic-gate
920Sstevel@tonic-gate (void) setlocale(LC_ALL, "");
930Sstevel@tonic-gate #if !defined(TEXT_DOMAIN) /* Should be defined by cc -D */
940Sstevel@tonic-gate #define TEXT_DOMAIN "SYS_TEST" /* Use this only if it weren't */
950Sstevel@tonic-gate #endif
960Sstevel@tonic-gate (void) textdomain(TEXT_DOMAIN);
970Sstevel@tonic-gate
980Sstevel@tonic-gate while ((c = getopt(argc, argv, "frRi")) != EOF)
990Sstevel@tonic-gate switch (c) {
1000Sstevel@tonic-gate case 'f':
1013855Ssn199410 silent = B_TRUE;
1020Sstevel@tonic-gate #ifdef XPG4
1033855Ssn199410 interactive = B_FALSE;
1040Sstevel@tonic-gate #endif
1050Sstevel@tonic-gate break;
1060Sstevel@tonic-gate case 'i':
1073855Ssn199410 interactive = B_TRUE;
1080Sstevel@tonic-gate #ifdef XPG4
1093855Ssn199410 silent = B_FALSE;
1100Sstevel@tonic-gate #endif
1110Sstevel@tonic-gate break;
1120Sstevel@tonic-gate case 'r':
1130Sstevel@tonic-gate case 'R':
1143855Ssn199410 recursive = B_TRUE;
1150Sstevel@tonic-gate break;
1160Sstevel@tonic-gate case '?':
1170Sstevel@tonic-gate errflg = 1;
1180Sstevel@tonic-gate break;
1190Sstevel@tonic-gate }
1200Sstevel@tonic-gate
1210Sstevel@tonic-gate /*
1220Sstevel@tonic-gate * For BSD compatibility allow '-' to delimit the end
1230Sstevel@tonic-gate * of options. However, if options were already explicitly
1240Sstevel@tonic-gate * terminated with '--', then treat '-' literally: otherwise,
1250Sstevel@tonic-gate * "rm -- -" won't remove '-'.
1260Sstevel@tonic-gate */
1270Sstevel@tonic-gate if (optind < argc &&
1280Sstevel@tonic-gate strcmp(argv[optind], "-") == 0 &&
1290Sstevel@tonic-gate strcmp(argv[optind - 1], "--") != 0)
1300Sstevel@tonic-gate optind++;
1310Sstevel@tonic-gate
1320Sstevel@tonic-gate argc -= optind;
1330Sstevel@tonic-gate argv = &argv[optind];
1340Sstevel@tonic-gate
1350Sstevel@tonic-gate if ((argc < 1 && !silent) || errflg) {
1363855Ssn199410 (void) fprintf(stderr, gettext("usage: rm [-fiRr] file ...\n"));
1373855Ssn199410 exit(2);
1383855Ssn199410 }
1393855Ssn199410
1403855Ssn199410 ontty = isatty(STDIN_FILENO) != 0;
1413855Ssn199410
1423855Ssn199410 if (recursive && stat("/", &rootdir) != 0) {
1430Sstevel@tonic-gate (void) fprintf(stderr,
1443855Ssn199410 gettext("rm: cannot stat root directory: %s\n"),
1453855Ssn199410 strerror(errno));
1460Sstevel@tonic-gate exit(2);
1470Sstevel@tonic-gate }
1480Sstevel@tonic-gate
1494054Ssn199410 pathbuf = malloc(pathbuflen);
1504054Ssn199410 if (pathbuf == NULL)
1514054Ssn199410 memerror();
1524054Ssn199410
1534774Sas145665 if (init_yes() < 0) {
1544774Sas145665 (void) fprintf(stderr, gettext(ERR_MSG_INIT_YES),
1554774Sas145665 strerror(errno));
1564774Sas145665 exit(2);
1574774Sas145665 }
1584774Sas145665
1593855Ssn199410 for (; *argv != NULL; argv++) {
1603855Ssn199410 char *p = strrchr(*argv, '/');
1613855Ssn199410 if (p == NULL)
1623855Ssn199410 p = *argv;
1633855Ssn199410 else
1643855Ssn199410 p = p + 1;
1653855Ssn199410 if (strcmp(p, ".") == 0 || strcmp(p, "..") == 0) {
1663855Ssn199410 (void) fprintf(stderr,
1673855Ssn199410 gettext("rm of %s is not allowed\n"), *argv);
1683855Ssn199410 errcnt++;
1693855Ssn199410 continue;
1703855Ssn199410 }
1713855Ssn199410 /* Retry when we can't walk back up. */
1723855Ssn199410 while (rm(*argv, rec = cur = &top) != 0)
1733068Ssn199410 ;
1740Sstevel@tonic-gate }
1752875Ssn199410
1763855Ssn199410 return (errcnt != 0 ? 2 : 0);
1773855Ssn199410 }
1783855Ssn199410
1793855Ssn199410 static void
pushfilename(const char * fname)1803855Ssn199410 pushfilename(const char *fname)
1813855Ssn199410 {
1823855Ssn199410 char *p;
1833855Ssn199410 const char *q = fname;
1843855Ssn199410
1853855Ssn199410 if (cur == &top) {
1863855Ssn199410 p = pathbuf;
1873855Ssn199410 } else {
1883855Ssn199410 p = pathbuf + cur->up->pathend;
1893855Ssn199410 *p++ = '/';
1903855Ssn199410 }
1913855Ssn199410 while (*q != '\0') {
1923855Ssn199410 if (p - pathbuf + 2 >= pathbuflen) {
1933855Ssn199410 char *np;
1943855Ssn199410 pathbuflen += MAXPATHLEN;
1953855Ssn199410 np = realloc(pathbuf, pathbuflen);
1963855Ssn199410 if (np == NULL)
1973855Ssn199410 memerror();
1983855Ssn199410 p = np + (p - pathbuf);
1993855Ssn199410 pathbuf = np;
2003855Ssn199410 }
2013855Ssn199410 *p++ = *q++;
2023855Ssn199410 }
2033855Ssn199410 *p = '\0';
2043855Ssn199410 cur->pathend = p - pathbuf;
2053855Ssn199410 }
2063855Ssn199410
2073855Ssn199410 static void
closeframe(struct dlist * frm)2083855Ssn199410 closeframe(struct dlist *frm)
2093855Ssn199410 {
2103855Ssn199410 if (frm->dp != NULL) {
2113855Ssn199410 (void) closedir(frm->dp);
2123855Ssn199410 nfds--;
2133855Ssn199410 frm->dp = NULL;
2143855Ssn199410 frm->fd = -1;
2153855Ssn199410 }
2163855Ssn199410 }
2173855Ssn199410
2183855Ssn199410 static int
reclaim(void)2193855Ssn199410 reclaim(void)
2203855Ssn199410 {
2213855Ssn199410 while (rec != NULL && (rec->flags & DIR_CANTCLOSE) != 0)
2223855Ssn199410 rec = rec->down;
2233855Ssn199410 if (rec == NULL || rec == cur || rec->dp == NULL)
2243855Ssn199410 return (-1);
2253855Ssn199410 rec->diroff = telldir(rec->dp);
2263855Ssn199410 closeframe(rec);
2273855Ssn199410 rec = rec->down;
2283855Ssn199410 return (0);
2293855Ssn199410 }
2303855Ssn199410
2313855Ssn199410 static void
pushdir(struct dlist * frm)2323855Ssn199410 pushdir(struct dlist *frm)
2333855Ssn199410 {
2343855Ssn199410 frm->up = cur;
2353855Ssn199410 frm->down = NULL;
2363855Ssn199410 cur->down = frm;
2373855Ssn199410 cur = frm;
2380Sstevel@tonic-gate }
2390Sstevel@tonic-gate
2403068Ssn199410 static int
opendirat(int dirfd,const char * entry,struct dlist * frm)2413855Ssn199410 opendirat(int dirfd, const char *entry, struct dlist *frm)
2423855Ssn199410 {
2433855Ssn199410 int fd;
2443855Ssn199410
2453855Ssn199410 if (nfds >= maxfds)
2463855Ssn199410 (void) reclaim();
2473855Ssn199410
2483855Ssn199410 while ((fd = openat(dirfd, entry, O_RDONLY|O_NONBLOCK)) == -1 &&
2493855Ssn199410 errno == EMFILE) {
2503855Ssn199410 if (nfds < maxfds)
2513855Ssn199410 maxfds = nfds;
2523855Ssn199410 if (reclaim() != 0)
2533855Ssn199410 return (-1);
2543855Ssn199410 }
2553855Ssn199410 if (fd < 0)
2563855Ssn199410 return (-1);
2573855Ssn199410 frm->fd = fd;
2583855Ssn199410 frm->dp = fdopendir(fd);
2593855Ssn199410 if (frm->dp == NULL) {
2603855Ssn199410 (void) close(fd);
2613855Ssn199410 return (-1);
2623855Ssn199410 }
2633855Ssn199410 nfds++;
2643855Ssn199410 return (0);
2653855Ssn199410 }
2663855Ssn199410
2673855Ssn199410 /*
2683855Ssn199410 * Since we never pop the top frame, cur->up can never be NULL.
2693855Ssn199410 * If we pop beyond a frame we closed, we try to reopen "..".
2703855Ssn199410 */
2713855Ssn199410 static int
popdir(boolean_t noerror)2723855Ssn199410 popdir(boolean_t noerror)
2730Sstevel@tonic-gate {
2743855Ssn199410 struct stat buf;
2753855Ssn199410 int ret = noerror ? 0 : -1;
2763855Ssn199410 pathbuf[cur->up->pathend] = '\0';
2773855Ssn199410
2783855Ssn199410 if (noerror && cur->up->fd == -1) {
2793855Ssn199410 rec = cur->up;
2803855Ssn199410 if (opendirat(cur->fd, "..", rec) != 0 ||
2813855Ssn199410 fstat(rec->fd, &buf) != 0) {
2823855Ssn199410 (void) fprintf(stderr,
2833855Ssn199410 gettext("rm: cannot reopen %s: %s\n"),
2843855Ssn199410 pathbuf, strerror(errno));
2853855Ssn199410 exit(2);
2863855Ssn199410 }
2873855Ssn199410 if (rec->ino != buf.st_ino || rec->dev != buf.st_dev) {
2883855Ssn199410 (void) fprintf(stderr, gettext("rm: WARNING: "
2893855Ssn199410 "The directory %s was moved or linked to "
2903855Ssn199410 "another directory during the execution of rm\n"),
2913855Ssn199410 pathbuf);
2923855Ssn199410 closeframe(rec);
2933855Ssn199410 ret = -1;
2943855Ssn199410 } else {
2953855Ssn199410 /* If telldir failed, we take it from the top. */
2963855Ssn199410 if (rec->diroff != -1)
2973855Ssn199410 seekdir(rec->dp, rec->diroff);
2983855Ssn199410 }
2993855Ssn199410 } else if (rec == cur)
3003855Ssn199410 rec = cur->up;
3013855Ssn199410 closeframe(cur);
3023855Ssn199410 cur = cur->up;
3033855Ssn199410 cur->down = NULL;
3043855Ssn199410 return (ret);
3053855Ssn199410 }
3063855Ssn199410
3073855Ssn199410 /*
3083855Ssn199410 * The stack frame of this function is minimized so that we can
3093855Ssn199410 * recurse quite a bit before we overflow the stack; around
3103855Ssn199410 * 30,000-40,000 nested directories can be removed with the default
3113855Ssn199410 * stack limit.
3123855Ssn199410 */
3133855Ssn199410 static int
rm(const char * entry,struct dlist * caller)3143855Ssn199410 rm(const char *entry, struct dlist *caller)
3153855Ssn199410 {
3163855Ssn199410 struct dlist frame;
3173855Ssn199410 int flag;
3183855Ssn199410 struct stat temp;
3193855Ssn199410 struct dirent *dent;
3203855Ssn199410 int err;
3210Sstevel@tonic-gate
3220Sstevel@tonic-gate /*
3233855Ssn199410 * Construct the pathname: note that the entry may live in memory
3243855Ssn199410 * allocated by readdir and that after return from recursion
3253855Ssn199410 * the memory is no longer valid. So after the recursive rm()
3263855Ssn199410 * call, we use the global pathbuf instead of the entry argument.
3270Sstevel@tonic-gate */
3283855Ssn199410 pushfilename(entry);
3293855Ssn199410
3303855Ssn199410 if (fstatat(caller->fd, entry, &temp, AT_SYMLINK_NOFOLLOW) != 0) {
3310Sstevel@tonic-gate if (!silent) {
3323855Ssn199410 (void) fprintf(stderr, "rm: %s: %s\n", pathbuf,
3333855Ssn199410 strerror(errno));
3343855Ssn199410 errcnt++;
3350Sstevel@tonic-gate }
3363068Ssn199410 return (0);
3370Sstevel@tonic-gate }
3380Sstevel@tonic-gate
3393855Ssn199410 if (S_ISDIR(temp.st_mode)) {
3400Sstevel@tonic-gate /*
3410Sstevel@tonic-gate * If "-r" wasn't specified, trying to remove directories
3420Sstevel@tonic-gate * is an error.
3430Sstevel@tonic-gate */
3440Sstevel@tonic-gate if (!recursive) {
3450Sstevel@tonic-gate (void) fprintf(stderr,
3463855Ssn199410 gettext("rm: %s is a directory\n"), pathbuf);
3473855Ssn199410 errcnt++;
3483855Ssn199410 return (0);
3493855Ssn199410 }
3503855Ssn199410
3513855Ssn199410 if (temp.st_ino == rootdir.st_ino &&
3523855Ssn199410 temp.st_dev == rootdir.st_dev) {
3533855Ssn199410 (void) fprintf(stderr,
3543855Ssn199410 gettext("rm of %s is not allowed\n"), "/");
3553855Ssn199410 errcnt++;
3563855Ssn199410 return (0);
3573855Ssn199410 }
3583855Ssn199410 /*
3593855Ssn199410 * TRANSLATION_NOTE - The following message will contain the
3603855Ssn199410 * first character of the strings for "yes" and "no" defined
3613855Ssn199410 * in the file "nl_langinfo.po". After substitution, the
3623855Ssn199410 * message will appear as follows:
3633855Ssn199410 * rm: examine files in directory <directoryname> (y/n)?
3643855Ssn199410 * where <directoryname> is the directory to be removed
3653855Ssn199410 *
3663855Ssn199410 */
3673855Ssn199410 if (interactive && !confirm(stderr,
3683855Ssn199410 gettext("rm: examine files in directory %s (%s/%s)? "),
3694774Sas145665 pathbuf, yesstr, nostr)) {
3703068Ssn199410 return (0);
3710Sstevel@tonic-gate }
3720Sstevel@tonic-gate
3733855Ssn199410 frame.dev = temp.st_dev;
3743855Ssn199410 frame.ino = temp.st_ino;
3753855Ssn199410 frame.flags = 0;
3763855Ssn199410 flag = AT_REMOVEDIR;
3773855Ssn199410
3783855Ssn199410 #ifdef XPG4
3793855Ssn199410 /*
3803855Ssn199410 * XCU4 and POSIX.2: If not interactive, check to see whether
3813855Ssn199410 * or not directory is readable or writable and if not,
3823855Ssn199410 * prompt user for response.
3833855Ssn199410 */
3843855Ssn199410 if (ontty && !interactive && !silent &&
385*9880SSumanth.Naropanth@Sun.COM faccessat(caller->fd, entry, W_OK|X_OK, AT_EACCESS) != 0 &&
3863855Ssn199410 !confirm(stderr,
3873855Ssn199410 gettext("rm: examine files in directory %s (%s/%s)? "),
3884774Sas145665 pathbuf, yesstr, nostr)) {
3893855Ssn199410 return (0);
3903855Ssn199410 }
3913855Ssn199410 #endif
3923855Ssn199410 if (opendirat(caller->fd, entry, &frame) == -1) {
3933855Ssn199410 err = errno;
3943855Ssn199410
3953855Ssn199410 if (interactive) {
3963855Ssn199410 /*
3973855Ssn199410 * Print an error message that
3983855Ssn199410 * we could not read the directory
3993855Ssn199410 * as the user wanted to examine
4003855Ssn199410 * files in the directory. Only
4013855Ssn199410 * affect the error status if
4023855Ssn199410 * user doesn't want to remove the
4033855Ssn199410 * directory as we still may be able
4043855Ssn199410 * remove the directory successfully.
4053855Ssn199410 */
4063855Ssn199410 (void) fprintf(stderr, gettext(
4073855Ssn199410 "rm: cannot read directory %s: %s\n"),
4083855Ssn199410 pathbuf, strerror(err));
4093855Ssn199410
4103855Ssn199410 /*
4113855Ssn199410 * TRANSLATION_NOTE - The following message will contain the
4123855Ssn199410 * first character of the strings for "yes" and "no" defined
4133855Ssn199410 * in the file "nl_langinfo.po". After substitution, the
4143855Ssn199410 * message will appear as follows:
4153855Ssn199410 * rm: remove <filename> (y/n)?
4163855Ssn199410 * For example, in German, this will appear as
4173855Ssn199410 * rm: l�schen <filename> (j/n)?
4183855Ssn199410 * where j=ja, n=nein, <filename>=the file to be removed
4193855Ssn199410 */
4203855Ssn199410 if (!confirm(stderr,
4213855Ssn199410 gettext("rm: remove %s (%s/%s)? "),
4224774Sas145665 pathbuf, yesstr, nostr)) {
4233855Ssn199410 errcnt++;
4243855Ssn199410 return (0);
4253855Ssn199410 }
4263855Ssn199410 }
4273855Ssn199410 /* If it's empty we may still be able to rm it */
4283855Ssn199410 if (unlinkat(caller->fd, entry, flag) == 0)
4293855Ssn199410 return (0);
4303855Ssn199410 if (interactive)
4313855Ssn199410 err = errno;
4323855Ssn199410 (void) fprintf(stderr,
4333855Ssn199410 interactive ?
4343855Ssn199410 gettext("rm: Unable to remove directory %s: %s\n") :
4353855Ssn199410 gettext("rm: cannot read directory %s: %s\n"),
4363855Ssn199410 pathbuf, strerror(err));
4373855Ssn199410 errcnt++;
4383855Ssn199410 return (0);
4390Sstevel@tonic-gate }
4400Sstevel@tonic-gate
4413855Ssn199410 /*
4423855Ssn199410 * There is a race condition here too; if we open a directory
4433855Ssn199410 * we have to make sure it's still the same directory we
4443855Ssn199410 * stat'ed and checked against root earlier. Let's check.
4453855Ssn199410 */
4463855Ssn199410 if (fstat(frame.fd, &temp) != 0 ||
4473855Ssn199410 frame.ino != temp.st_ino ||
4483855Ssn199410 frame.dev != temp.st_dev) {
4493855Ssn199410 (void) fprintf(stderr,
4503855Ssn199410 gettext("rm: %s: directory renamed\n"), pathbuf);
4513855Ssn199410 closeframe(&frame);
4523855Ssn199410 errcnt++;
4533855Ssn199410 return (0);
4543855Ssn199410 }
4553855Ssn199410
4563855Ssn199410 if (caller != &top) {
4573855Ssn199410 if (checkdir(caller, &frame) != 0) {
4583855Ssn199410 closeframe(&frame);
4593855Ssn199410 goto unlinkit;
4603855Ssn199410 }
4613855Ssn199410 }
4623855Ssn199410 pushdir(&frame);
4632875Ssn199410
4643855Ssn199410 /*
4653855Ssn199410 * rm() only returns -1 if popdir failed at some point;
4663855Ssn199410 * frame.dp is no longer reliable and we must drop out.
4673855Ssn199410 */
4683855Ssn199410 while ((dent = readdir(frame.dp)) != NULL) {
4693855Ssn199410 if (strcmp(dent->d_name, ".") == 0 ||
4703855Ssn199410 strcmp(dent->d_name, "..") == 0)
4713855Ssn199410 continue;
4723855Ssn199410
4733855Ssn199410 if (rm(dent->d_name, &frame) != 0)
4743855Ssn199410 break;
4753855Ssn199410 }
4760Sstevel@tonic-gate
4773855Ssn199410 if (popdir(dent == NULL) != 0)
4783855Ssn199410 return (-1);
4793855Ssn199410
4803855Ssn199410 /*
4813855Ssn199410 * We recursed and the subdirectory may have set the CANTCLOSE
4823855Ssn199410 * flag; we need to clear it except for &top.
4833855Ssn199410 * Recursion may have invalidated entry because of closedir().
4843855Ssn199410 */
4853855Ssn199410 if (caller != &top) {
4863855Ssn199410 caller->flags &= ~DIR_CANTCLOSE;
4873855Ssn199410 entry = &pathbuf[caller->up->pathend + 1];
4883855Ssn199410 }
4893855Ssn199410 } else {
4903855Ssn199410 flag = 0;
4913855Ssn199410 }
4923855Ssn199410 unlinkit:
4930Sstevel@tonic-gate /*
4940Sstevel@tonic-gate * If interactive, ask for acknowledgement.
4950Sstevel@tonic-gate */
4960Sstevel@tonic-gate if (interactive) {
4973855Ssn199410 if (!confirm(stderr, gettext("rm: remove %s (%s/%s)? "),
4984774Sas145665 pathbuf, yesstr, nostr)) {
4993068Ssn199410 return (0);
5000Sstevel@tonic-gate }
5013855Ssn199410 } else if (!silent && flag == 0) {
5020Sstevel@tonic-gate /*
5030Sstevel@tonic-gate * If not silent, and stdin is a terminal, and there's
5040Sstevel@tonic-gate * no write access, and the file isn't a symbolic link,
5053855Ssn199410 * ask for permission. If flag is set, then we know it's
5063855Ssn199410 * a directory so we skip this test as it was done above.
5070Sstevel@tonic-gate *
5080Sstevel@tonic-gate * TRANSLATION_NOTE - The following message will contain the
5090Sstevel@tonic-gate * first character of the strings for "yes" and "no" defined
5100Sstevel@tonic-gate * in the file "nl_langinfo.po". After substitution, the
5110Sstevel@tonic-gate * message will appear as follows:
5123855Ssn199410 * rm: <filename>: override protection XXX (y/n)?
5130Sstevel@tonic-gate * where XXX is the permission mode bits of the file in octal
5140Sstevel@tonic-gate * and <filename> is the file to be removed
5150Sstevel@tonic-gate *
5160Sstevel@tonic-gate */
5173855Ssn199410 if (ontty && !S_ISLNK(temp.st_mode) &&
518*9880SSumanth.Naropanth@Sun.COM faccessat(caller->fd, entry, W_OK, AT_EACCESS) != 0 &&
5193855Ssn199410 !confirm(stdout,
5203855Ssn199410 gettext("rm: %s: override protection %o (%s/%s)? "),
5214774Sas145665 pathbuf, temp.st_mode & 0777, yesstr, nostr)) {
5223855Ssn199410 return (0);
5230Sstevel@tonic-gate }
5240Sstevel@tonic-gate }
5250Sstevel@tonic-gate
5263855Ssn199410 if (unlinkat(caller->fd, entry, flag) != 0) {
5273855Ssn199410 err = errno;
5283855Ssn199410 if (err == ENOENT)
5293855Ssn199410 return (0);
5300Sstevel@tonic-gate
5313855Ssn199410 if (flag != 0) {
5323855Ssn199410 if (err == EINVAL) {
5333855Ssn199410 (void) fprintf(stderr, gettext(
5343855Ssn199410 "rm: Cannot remove any directory in the "
5353855Ssn199410 "path of the current working directory\n"
5363855Ssn199410 "%s\n"), pathbuf);
5373855Ssn199410 } else {
5383855Ssn199410 if (err == EEXIST)
5393855Ssn199410 err = ENOTEMPTY;
5403855Ssn199410 (void) fprintf(stderr,
5413855Ssn199410 gettext("rm: Unable to remove directory %s:"
5423855Ssn199410 " %s\n"), pathbuf, strerror(err));
5433855Ssn199410 }
5443855Ssn199410 } else {
5453855Ssn199410 #ifndef XPG4
5463855Ssn199410 if (!silent || interactive) {
5473855Ssn199410 #endif
5483855Ssn199410
5493855Ssn199410 (void) fprintf(stderr,
5503855Ssn199410 gettext("rm: %s not removed: %s\n"),
5513855Ssn199410 pathbuf, strerror(err));
5523855Ssn199410 #ifndef XPG4
5533855Ssn199410 }
5543855Ssn199410 #endif
5550Sstevel@tonic-gate }
5563855Ssn199410 errcnt++;
5570Sstevel@tonic-gate }
5583068Ssn199410 return (0);
5590Sstevel@tonic-gate }
5600Sstevel@tonic-gate
5613068Ssn199410 static int
confirm(FILE * fp,const char * q,...)5623855Ssn199410 confirm(FILE *fp, const char *q, ...)
5630Sstevel@tonic-gate {
5643855Ssn199410 va_list ap;
5650Sstevel@tonic-gate
5663855Ssn199410 va_start(ap, q);
5673855Ssn199410 (void) vfprintf(fp, q, ap);
5683855Ssn199410 va_end(ap);
5693855Ssn199410 return (yes());
5700Sstevel@tonic-gate }
5710Sstevel@tonic-gate
5720Sstevel@tonic-gate static void
memerror(void)5733855Ssn199410 memerror(void)
5740Sstevel@tonic-gate {
5753855Ssn199410 (void) fprintf(stderr, gettext("rm: Insufficient memory.\n"));
5763855Ssn199410 exit(1);
5770Sstevel@tonic-gate }
5780Sstevel@tonic-gate
5790Sstevel@tonic-gate /*
5803855Ssn199410 * If we can't stat "..", it's either not there or we can't search
5813855Ssn199410 * the current directory; in that case we can't return back through
5823855Ssn199410 * "..", so we need to keep the parent open.
5833855Ssn199410 * Check that we came from "..", if not then this directory entry is an
5843855Ssn199410 * additional link and there is risk of a filesystem cycle and we also
5853855Ssn199410 * can't go back up through ".." and we keep the directory open.
5860Sstevel@tonic-gate */
5873855Ssn199410 static int
checkdir(struct dlist * caller,struct dlist * frmp)5883855Ssn199410 checkdir(struct dlist *caller, struct dlist *frmp)
5890Sstevel@tonic-gate {
5903855Ssn199410 struct stat up;
5913855Ssn199410 struct dlist *ptr;
5920Sstevel@tonic-gate
5933855Ssn199410 if (fstatat(frmp->fd, "..", &up, 0) != 0) {
5943855Ssn199410 caller->flags |= DIR_CANTCLOSE;
5953855Ssn199410 return (0);
5963855Ssn199410 } else if (up.st_ino == caller->ino && up.st_dev == caller->dev) {
5973855Ssn199410 return (0);
5980Sstevel@tonic-gate }
5990Sstevel@tonic-gate
6003855Ssn199410 /* Directory hard link, check cycle */
6013855Ssn199410 for (ptr = caller; ptr != NULL; ptr = ptr->up) {
6023855Ssn199410 if (frmp->dev == ptr->dev && frmp->ino == ptr->ino) {
6030Sstevel@tonic-gate (void) fprintf(stderr,
6043855Ssn199410 gettext("rm: cycle detected for %s\n"), pathbuf);
6053855Ssn199410 errcnt++;
6063855Ssn199410 return (-1);
6070Sstevel@tonic-gate }
6080Sstevel@tonic-gate }
6093855Ssn199410 caller->flags |= DIR_CANTCLOSE;
6103855Ssn199410 return (0);
6110Sstevel@tonic-gate }
612