diff options
Diffstat (limited to 'src/remove.c')
-rw-r--r-- | src/remove.c | 97 |
1 files changed, 30 insertions, 67 deletions
diff --git a/src/remove.c b/src/remove.c index f44a9821..cdbbec5b 100644 --- a/src/remove.c +++ b/src/remove.c @@ -1,5 +1,5 @@ /* remove.c -- core functions for removing files and directories - Copyright (C) 1988-2012 Free Software Foundation, Inc. + Copyright (C) 1988-2013 Free Software Foundation, Inc. This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by @@ -88,20 +88,6 @@ cache_stat_init (struct stat *st) return st; } -/* Return true if *ST has been statted. */ -static inline bool -cache_statted (struct stat *st) -{ - return (st->st_size != -1); -} - -/* Return true if *ST has been statted successfully. */ -static inline bool -cache_stat_ok (struct stat *st) -{ - return (0 <= st->st_size); -} - /* Return 1 if FILE is an unwritable non-symlink, 0 if it is writable or some other type of file, -1 and set errno if there is some problem in determining the answer. @@ -204,6 +190,13 @@ prompt (FTS const *fts, FTSENT const *ent, bool is_dir, int dirent_type = is_dir ? DT_DIR : DT_UNKNOWN; int write_protected = 0; + bool is_empty = false; + if (is_empty_p) + { + is_empty = is_empty_dir (fd_cwd, filename); + *is_empty_p = is_empty ? T_YES : T_NO; + } + /* When nonzero, this indicates that we failed to remove a child entry, either because the user declined an interactive prompt, or due to some other failure, like permissions. */ @@ -252,7 +245,10 @@ prompt (FTS const *fts, FTSENT const *ent, bool is_dir, break; case DT_DIR: - if (!x->recursive) + /* Unless we're either deleting directories or deleting + recursively, we want to raise an EISDIR error rather than + prompting the user */ + if ( ! (x->recursive || (x->remove_empty_directories && is_empty))) { write_protected = -1; wp_errno = EISDIR; @@ -268,15 +264,6 @@ prompt (FTS const *fts, FTSENT const *ent, bool is_dir, return RM_ERROR; } - bool is_empty; - if (is_empty_p) - { - is_empty = is_empty_dir (fd_cwd, filename); - *is_empty_p = is_empty ? T_YES : T_NO; - } - else - is_empty = false; - /* Issue the prompt. */ if (dirent_type == DT_DIR && mode == PA_DESCEND_INTO_DIR @@ -311,36 +298,6 @@ prompt (FTS const *fts, FTSENT const *ent, bool is_dir, return RM_OK; } -/* Return true if FILENAME is a directory (and not a symlink to a directory). - Otherwise, including the case in which lstat fails, return false. - *ST is FILENAME's tstatus. - Do not modify errno. */ -static inline bool -is_dir_lstat (int fd_cwd, char const *filename, struct stat *st) -{ - int saved_errno = errno; - bool is_dir = - (cache_fstatat (fd_cwd, filename, st, AT_SYMLINK_NOFOLLOW) == 0 - && S_ISDIR (st->st_mode)); - errno = saved_errno; - return is_dir; -} - -/* Return true if FILENAME is a non-directory. - Otherwise, including the case in which lstat fails, return false. - *ST is FILENAME's tstatus. - Do not modify errno. */ -static inline bool -is_nondir_lstat (int fd_cwd, char const *filename, struct stat *st) -{ - int saved_errno = errno; - bool is_non_dir = - (cache_fstatat (fd_cwd, filename, st, AT_SYMLINK_NOFOLLOW) == 0 - && !S_ISDIR (st->st_mode)); - errno = saved_errno; - return is_non_dir; -} - /* When a function like unlink, rmdir, or fstatat fails with an errno value of ERRNUM, return true if the specified file system object is guaranteed not to exist; otherwise, return false. */ @@ -435,11 +392,15 @@ excise (FTS *fts, FTSENT *ent, struct rm_options const *x, bool is_dir) if (ignorable_missing (x, errno)) return RM_OK; - /* When failing to rmdir an unreadable directory, the typical - errno value is EISDIR, but that is not as useful to the user - as the errno value from the failed open (probably EPERM). - Use the earlier, more descriptive errno value. */ - if (ent->fts_info == FTS_DNR) + /* When failing to rmdir an unreadable directory, we see errno values + like EISDIR or ENOTDIR (or, on Solaris 10, EEXIST), but they would be + meaningless in a diagnostic. When that happens and the errno value + from the failed open is EPERM or EACCES, use the earlier, more + descriptive errno value. */ + if (ent->fts_info == FTS_DNR + && (errno == ENOTEMPTY || errno == EISDIR || errno == ENOTDIR + || errno == EEXIST) + && (ent->fts_errno == EPERM || ent->fts_errno == EACCES)) errno = ent->fts_errno; error (0, errno, _("cannot remove %s"), quote (ent->fts_path)); mark_ancestor_dirs (ent); @@ -458,11 +419,16 @@ rm_fts (FTS *fts, FTSENT *ent, struct rm_options const *x) switch (ent->fts_info) { case FTS_D: /* preorder directory */ - if (! x->recursive) + if (! x->recursive + && !(x->remove_empty_directories + && is_empty_dir (fts->fts_cwd_fd, ent->fts_accpath))) { - /* This is the first (pre-order) encounter with a directory. - Not recursive, so arrange to skip contents. */ - error (0, EISDIR, _("cannot remove %s"), quote (ent->fts_path)); + /* This is the first (pre-order) encounter with a directory + that we cannot delete. + Not recursive, and it's not an empty directory (if we're removing + them) so arrange to skip contents. */ + int err = x->remove_empty_directories ? ENOTEMPTY : EISDIR; + error (0, err, _("cannot remove %s"), quote (ent->fts_path)); mark_ancestor_dirs (ent); fts_skip_tree (fts, ent); return RM_ERROR; @@ -471,9 +437,6 @@ rm_fts (FTS *fts, FTSENT *ent, struct rm_options const *x) /* Perform checks that can apply only for command-line arguments. */ if (ent->fts_level == FTS_ROOTLEVEL) { - if (strip_trailing_slashes (ent->fts_path)) - ent->fts_pathlen = strlen (ent->fts_path); - /* If the basename of a command line argument is "." or "..", diagnose it and do nothing more with that argument. */ if (dot_or_dotdot (last_component (ent->fts_accpath))) |