4 * The contents of this file are subject to the terms of the
5 * Common Development and Distribution License (the "License").
6 * You may not use this file except in compliance with the License.
8 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9 * or http://www.opensolaris.org/os/licensing.
10 * See the License for the specific language governing permissions
11 * and limitations under the License.
13 * When distributing Covered Code, include this CDDL HEADER in each
14 * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15 * If applicable, add the following below this CDDL HEADER, with the
16 * fields enclosed by brackets "[]" replaced with your own identifying
17 * information: Portions Copyright [yyyy] [name of copyright owner]
23 * Copyright 2009 Sun Microsystems, Inc. All rights reserved.
24 * Use is subject to license terms.
27 /* Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T */
28 /* All Rights Reserved */
34 #include <sys/param.h>
48 #include "getresponse.h"
50 #define DIR_CANTCLOSE 1
52 static struct stat rootdir
;
55 int fd
; /* Stores directory fd */
56 int flags
; /* DIR_* Flags */
57 DIR *dp
; /* Open directory (opened with fd) */
58 long diroff
; /* Saved directory offset when closing */
59 struct dlist
*up
; /* Up one step in the tree (toward "/") */
60 struct dlist
*down
; /* Down one step in the tree */
61 ino_t ino
; /* st_ino of directory */
62 dev_t dev
; /* st_dev of directory */
63 int pathend
; /* Offset of name end in the pathbuffer */
66 static struct dlist top
= {
71 static struct dlist
*cur
, *rec
;
73 static int rm(const char *, struct dlist
*);
74 static int confirm(FILE *, const char *, ...);
75 static void memerror(void);
76 static int checkdir(struct dlist
*, struct dlist
*);
78 static boolean_t silent
, interactive
, recursive
, ontty
;
81 static size_t pathbuflen
= MAXPATHLEN
;
83 static int maxfds
= MAXINT
;
87 main(int argc
, char **argv
)
92 (void) setlocale(LC_ALL
, "");
93 #if !defined(TEXT_DOMAIN) /* Should be defined by cc -D */
94 #define TEXT_DOMAIN "SYS_TEST" /* Use this only if it weren't */
96 (void) textdomain(TEXT_DOMAIN
);
98 while ((c
= getopt(argc
, argv
, "frRi")) != EOF
)
103 interactive
= B_FALSE
;
107 interactive
= B_TRUE
;
122 * For BSD compatibility allow '-' to delimit the end
123 * of options. However, if options were already explicitly
124 * terminated with '--', then treat '-' literally: otherwise,
125 * "rm -- -" won't remove '-'.
128 strcmp(argv
[optind
], "-") == 0 &&
129 strcmp(argv
[optind
- 1], "--") != 0)
133 argv
= &argv
[optind
];
135 if ((argc
< 1 && !silent
) || errflg
) {
136 (void) fprintf(stderr
, gettext("usage: rm [-fiRr] file ...\n"));
140 ontty
= isatty(STDIN_FILENO
) != 0;
142 if (recursive
&& stat("/", &rootdir
) != 0) {
143 (void) fprintf(stderr
,
144 gettext("rm: cannot stat root directory: %s\n"),
149 pathbuf
= malloc(pathbuflen
);
153 if (init_yes() < 0) {
154 (void) fprintf(stderr
, gettext(ERR_MSG_INIT_YES
),
159 for (; *argv
!= NULL
; argv
++) {
160 char *p
= strrchr(*argv
, '/');
165 if (strcmp(p
, ".") == 0 || strcmp(p
, "..") == 0) {
166 (void) fprintf(stderr
,
167 gettext("rm of %s is not allowed\n"), *argv
);
171 /* Retry when we can't walk back up. */
172 while (rm(*argv
, rec
= cur
= &top
) != 0)
176 return (errcnt
!= 0 ? 2 : 0);
180 pushfilename(const char *fname
)
183 const char *q
= fname
;
188 p
= pathbuf
+ cur
->up
->pathend
;
192 if (p
- pathbuf
+ 2 >= pathbuflen
) {
194 pathbuflen
+= MAXPATHLEN
;
195 np
= realloc(pathbuf
, pathbuflen
);
198 p
= np
+ (p
- pathbuf
);
204 cur
->pathend
= p
- pathbuf
;
208 closeframe(struct dlist
*frm
)
210 if (frm
->dp
!= NULL
) {
211 (void) closedir(frm
->dp
);
221 while (rec
!= NULL
&& (rec
->flags
& DIR_CANTCLOSE
) != 0)
223 if (rec
== NULL
|| rec
== cur
|| rec
->dp
== NULL
)
225 rec
->diroff
= telldir(rec
->dp
);
232 pushdir(struct dlist
*frm
)
241 opendirat(int dirfd
, const char *entry
, struct dlist
*frm
)
248 while ((fd
= openat(dirfd
, entry
, O_RDONLY
|O_NONBLOCK
)) == -1 &&
258 frm
->dp
= fdopendir(fd
);
259 if (frm
->dp
== NULL
) {
268 * Since we never pop the top frame, cur->up can never be NULL.
269 * If we pop beyond a frame we closed, we try to reopen "..".
272 popdir(boolean_t noerror
)
275 int ret
= noerror
? 0 : -1;
276 pathbuf
[cur
->up
->pathend
] = '\0';
278 if (noerror
&& cur
->up
->fd
== -1) {
280 if (opendirat(cur
->fd
, "..", rec
) != 0 ||
281 fstat(rec
->fd
, &buf
) != 0) {
282 (void) fprintf(stderr
,
283 gettext("rm: cannot reopen %s: %s\n"),
284 pathbuf
, strerror(errno
));
287 if (rec
->ino
!= buf
.st_ino
|| rec
->dev
!= buf
.st_dev
) {
288 (void) fprintf(stderr
, gettext("rm: WARNING: "
289 "The directory %s was moved or linked to "
290 "another directory during the execution of rm\n"),
295 /* If telldir failed, we take it from the top. */
296 if (rec
->diroff
!= -1)
297 seekdir(rec
->dp
, rec
->diroff
);
299 } else if (rec
== cur
)
308 * The stack frame of this function is minimized so that we can
309 * recurse quite a bit before we overflow the stack; around
310 * 30,000-40,000 nested directories can be removed with the default
314 rm(const char *entry
, struct dlist
*caller
)
323 * Construct the pathname: note that the entry may live in memory
324 * allocated by readdir and that after return from recursion
325 * the memory is no longer valid. So after the recursive rm()
326 * call, we use the global pathbuf instead of the entry argument.
330 if (fstatat(caller
->fd
, entry
, &temp
, AT_SYMLINK_NOFOLLOW
) != 0) {
332 (void) fprintf(stderr
, "rm: %s: %s\n", pathbuf
,
339 if (S_ISDIR(temp
.st_mode
)) {
341 * If "-r" wasn't specified, trying to remove directories
345 (void) fprintf(stderr
,
346 gettext("rm: %s is a directory\n"), pathbuf
);
351 if (temp
.st_ino
== rootdir
.st_ino
&&
352 temp
.st_dev
== rootdir
.st_dev
) {
353 (void) fprintf(stderr
,
354 gettext("rm of %s is not allowed\n"), "/");
359 * TRANSLATION_NOTE - The following message will contain the
360 * first character of the strings for "yes" and "no" defined
361 * in the file "nl_langinfo.po". After substitution, the
362 * message will appear as follows:
363 * rm: examine files in directory <directoryname> (y/n)?
364 * where <directoryname> is the directory to be removed
367 if (interactive
&& !confirm(stderr
,
368 gettext("rm: examine files in directory %s (%s/%s)? "),
369 pathbuf
, yesstr
, nostr
)) {
373 frame
.dev
= temp
.st_dev
;
374 frame
.ino
= temp
.st_ino
;
380 * XCU4 and POSIX.2: If not interactive, check to see whether
381 * or not directory is readable or writable and if not,
382 * prompt user for response.
384 if (ontty
&& !interactive
&& !silent
&&
385 faccessat(caller
->fd
, entry
, W_OK
|X_OK
, AT_EACCESS
) != 0 &&
387 gettext("rm: examine files in directory %s (%s/%s)? "),
388 pathbuf
, yesstr
, nostr
)) {
392 if (opendirat(caller
->fd
, entry
, &frame
) == -1) {
397 * Print an error message that
398 * we could not read the directory
399 * as the user wanted to examine
400 * files in the directory. Only
401 * affect the error status if
402 * user doesn't want to remove the
403 * directory as we still may be able
404 * remove the directory successfully.
406 (void) fprintf(stderr
, gettext(
407 "rm: cannot read directory %s: %s\n"),
408 pathbuf
, strerror(err
));
411 * TRANSLATION_NOTE - The following message will contain the
412 * first character of the strings for "yes" and "no" defined
413 * in the file "nl_langinfo.po". After substitution, the
414 * message will appear as follows:
415 * rm: remove <filename> (y/n)?
416 * For example, in German, this will appear as
417 * rm: löschen <filename> (j/n)?
418 * where j=ja, n=nein, <filename>=the file to be removed
421 gettext("rm: remove %s (%s/%s)? "),
422 pathbuf
, yesstr
, nostr
)) {
427 /* If it's empty we may still be able to rm it */
428 if (unlinkat(caller
->fd
, entry
, flag
) == 0)
432 (void) fprintf(stderr
,
434 gettext("rm: Unable to remove directory %s: %s\n") :
435 gettext("rm: cannot read directory %s: %s\n"),
436 pathbuf
, strerror(err
));
442 * There is a race condition here too; if we open a directory
443 * we have to make sure it's still the same directory we
444 * stat'ed and checked against root earlier. Let's check.
446 if (fstat(frame
.fd
, &temp
) != 0 ||
447 frame
.ino
!= temp
.st_ino
||
448 frame
.dev
!= temp
.st_dev
) {
449 (void) fprintf(stderr
,
450 gettext("rm: %s: directory renamed\n"), pathbuf
);
456 if (caller
!= &top
) {
457 if (checkdir(caller
, &frame
) != 0) {
465 * rm() only returns -1 if popdir failed at some point;
466 * frame.dp is no longer reliable and we must drop out.
468 while ((dent
= readdir(frame
.dp
)) != NULL
) {
469 if (strcmp(dent
->d_name
, ".") == 0 ||
470 strcmp(dent
->d_name
, "..") == 0)
473 if (rm(dent
->d_name
, &frame
) != 0)
477 if (popdir(dent
== NULL
) != 0)
481 * We recursed and the subdirectory may have set the CANTCLOSE
482 * flag; we need to clear it except for &top.
483 * Recursion may have invalidated entry because of closedir().
485 if (caller
!= &top
) {
486 caller
->flags
&= ~DIR_CANTCLOSE
;
487 entry
= &pathbuf
[caller
->up
->pathend
+ 1];
494 * If interactive, ask for acknowledgement.
497 if (!confirm(stderr
, gettext("rm: remove %s (%s/%s)? "),
498 pathbuf
, yesstr
, nostr
)) {
501 } else if (!silent
&& flag
== 0) {
503 * If not silent, and stdin is a terminal, and there's
504 * no write access, and the file isn't a symbolic link,
505 * ask for permission. If flag is set, then we know it's
506 * a directory so we skip this test as it was done above.
508 * TRANSLATION_NOTE - The following message will contain the
509 * first character of the strings for "yes" and "no" defined
510 * in the file "nl_langinfo.po". After substitution, the
511 * message will appear as follows:
512 * rm: <filename>: override protection XXX (y/n)?
513 * where XXX is the permission mode bits of the file in octal
514 * and <filename> is the file to be removed
517 if (ontty
&& !S_ISLNK(temp
.st_mode
) &&
518 faccessat(caller
->fd
, entry
, W_OK
, AT_EACCESS
) != 0 &&
520 gettext("rm: %s: override protection %o (%s/%s)? "),
521 pathbuf
, temp
.st_mode
& 0777, yesstr
, nostr
)) {
526 if (unlinkat(caller
->fd
, entry
, flag
) != 0) {
533 (void) fprintf(stderr
, gettext(
534 "rm: Cannot remove any directory in the "
535 "path of the current working directory\n"
540 (void) fprintf(stderr
,
541 gettext("rm: Unable to remove directory %s:"
542 " %s\n"), pathbuf
, strerror(err
));
546 if (!silent
|| interactive
) {
549 (void) fprintf(stderr
,
550 gettext("rm: %s not removed: %s\n"),
551 pathbuf
, strerror(err
));
562 confirm(FILE *fp
, const char *q
, ...)
567 (void) vfprintf(fp
, q
, ap
);
575 (void) fprintf(stderr
, gettext("rm: Insufficient memory.\n"));
580 * If we can't stat "..", it's either not there or we can't search
581 * the current directory; in that case we can't return back through
582 * "..", so we need to keep the parent open.
583 * Check that we came from "..", if not then this directory entry is an
584 * additional link and there is risk of a filesystem cycle and we also
585 * can't go back up through ".." and we keep the directory open.
588 checkdir(struct dlist
*caller
, struct dlist
*frmp
)
593 if (fstatat(frmp
->fd
, "..", &up
, 0) != 0) {
594 caller
->flags
|= DIR_CANTCLOSE
;
596 } else if (up
.st_ino
== caller
->ino
&& up
.st_dev
== caller
->dev
) {
600 /* Directory hard link, check cycle */
601 for (ptr
= caller
; ptr
!= NULL
; ptr
= ptr
->up
) {
602 if (frmp
->dev
== ptr
->dev
&& frmp
->ino
== ptr
->ino
) {
603 (void) fprintf(stderr
,
604 gettext("rm: cycle detected for %s\n"), pathbuf
);
609 caller
->flags
|= DIR_CANTCLOSE
;