LCOV - code coverage report
Current view: top level - src - remove.c (source / functions) Hit Total Coverage
Test: coreutils.info Lines: 303 523 57.9 %
Date: 2018-01-30 Functions: 29 39 74.4 %

          Line data    Source code
       1             : /* remove.c -- core functions for removing files and directories
       2             :    Copyright (C) 88, 90, 91, 1994-2008 Free Software Foundation, Inc.
       3             : 
       4             :    This program is free software: you can redistribute it and/or modify
       5             :    it under the terms of the GNU General Public License as published by
       6             :    the Free Software Foundation, either version 3 of the License, or
       7             :    (at your option) any later version.
       8             : 
       9             :    This program is distributed in the hope that it will be useful,
      10             :    but WITHOUT ANY WARRANTY; without even the implied warranty of
      11             :    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
      12             :    GNU General Public License for more details.
      13             : 
      14             :    You should have received a copy of the GNU General Public License
      15             :    along with this program.  If not, see <http://www.gnu.org/licenses/>.  */
      16             : 
      17             : /* Extracted from rm.c and librarified, then rewritten by Jim Meyering.  */
      18             : 
      19             : #include <config.h>
      20             : #include <stdio.h>
      21             : #include <sys/types.h>
      22             : #include <setjmp.h>
      23             : #include <assert.h>
      24             : 
      25             : #include "system.h"
      26             : #include "cycle-check.h"
      27             : #include "dirfd.h"
      28             : #include "error.h"
      29             : #include "euidaccess.h"
      30             : #include "euidaccess-stat.h"
      31             : #include "file-type.h"
      32             : #include "hash.h"
      33             : #include "hash-pjw.h"
      34             : #include "lstat.h"
      35             : #include "obstack.h"
      36             : #include "quote.h"
      37             : #include "remove.h"
      38             : #include "root-dev-ino.h"
      39             : #include "unlinkdir.h"
      40             : #include "write-any-file.h"
      41             : #include "yesno.h"
      42             : 
      43             : /* Avoid shadowing warnings because these are functions declared
      44             :    in dirname.h as well as locals used below.  */
      45             : #define dir_name rm_dir_name
      46             : #define dir_len rm_dir_len
      47             : 
      48             : #define obstack_chunk_alloc malloc
      49             : #define obstack_chunk_free free
      50             : 
      51             : /* This is the maximum number of consecutive readdir/unlink calls that
      52             :    can be made (with no intervening rewinddir or closedir/opendir) before
      53             :    triggering a bug that makes readdir return NULL even though some
      54             :    directory entries have not been processed.  The bug afflicts SunOS's
      55             :    readdir when applied to ufs file systems and Darwin 6.5's (and OSX
      56             :    v.10.3.8's) HFS+.  This maximum is conservative in that demonstrating
      57             :    the problem requires a directory containing at least 16 deletable
      58             :    entries (which doesn't count . and ..).
      59             :    This problem also affects Darwin 7.9.0 (aka MacOS X 10.3.9) on HFS+
      60             :    and NFS-mounted file systems, but not vfat ones.  */
      61             : enum
      62             :   {
      63             :     CONSECUTIVE_READDIR_UNLINK_THRESHOLD = 10
      64             :   };
      65             : 
      66             : /* FIXME: in 2009, or whenever Darwin 7.9.0 (aka MacOS X 10.3.9) is no
      67             :    longer relevant, remove this work-around code.  Then, there will be
      68             :    no need to perform the extra rewinddir call, ever.  */
      69             : #define NEED_REWIND(readdir_unlink_count) \
      70             :   (CONSECUTIVE_READDIR_UNLINK_THRESHOLD <= (readdir_unlink_count))
      71             : 
      72             : enum Ternary
      73             :   {
      74             :     T_UNKNOWN = 2,
      75             :     T_NO,
      76             :     T_YES
      77             :   };
      78             : typedef enum Ternary Ternary;
      79             : 
      80             : /* The prompt function may be called twice for a given directory.
      81             :    The first time, we ask whether to descend into it, and the
      82             :    second time, we ask whether to remove it.  */
      83             : enum Prompt_action
      84             :   {
      85             :     PA_DESCEND_INTO_DIR = 2,
      86             :     PA_REMOVE_DIR
      87             :   };
      88             : 
      89             : /* Initial capacity of per-directory hash table of entries that have
      90             :    been processed but not been deleted.  */
      91             : enum { HT_UNREMOVABLE_INITIAL_CAPACITY = 13 };
      92             : 
      93             : /* An entry in the active directory stack.
      94             :    Each entry corresponds to an `active' directory.  */
      95             : struct AD_ent
      96             : {
      97             :   /* For a given active directory, this is the set of names of
      98             :      entries in that directory that could/should not be removed.
      99             :      For example, `.' and `..', as well as files/dirs for which
     100             :      unlink/rmdir failed e.g., due to access restrictions.  */
     101             :   Hash_table *unremovable;
     102             : 
     103             :   /* Record the status for a given active directory; we need to know
     104             :      whether an entry was not removed, either because of an error or
     105             :      because the user declined.  */
     106             :   enum RM_status status;
     107             : 
     108             :   /* The directory's dev/ino.  Used to ensure that a malicious user does
     109             :      not replace a directory we're about to process with a symlink to
     110             :      some other directory.  */
     111             :   struct dev_ino dev_ino;
     112             : };
     113             : 
     114             : /* D_TYPE(D) is the type of directory entry D if known, DT_UNKNOWN
     115             :    otherwise.  */
     116             : #if HAVE_STRUCT_DIRENT_D_TYPE
     117             : # define D_TYPE(d) ((d)->d_type)
     118             : #else
     119             : # define D_TYPE(d) DT_UNKNOWN
     120             : 
     121             : /* Any int values will do here, so long as they're distinct.
     122             :    Undef any existing macros out of the way.  */
     123             : # undef DT_UNKNOWN
     124             : # undef DT_DIR
     125             : # undef DT_LNK
     126             : # define DT_UNKNOWN 0
     127             : # define DT_DIR 1
     128             : # define DT_LNK 2
     129             : #endif
     130             : 
     131             : extern char *program_name;
     132             : 
     133             : struct dirstack_state
     134             : {
     135             :   /* The name of the directory (starting with and relative to a command
     136             :      line argument) being processed.  When a subdirectory is entered, a new
     137             :      component is appended (pushed).  Remove (pop) the top component
     138             :      upon chdir'ing out of a directory.  This is used to form the full
     139             :      name of the current directory or a file therein, when necessary.  */
     140             :   struct obstack dir_stack;
     141             : 
     142             :   /* Stack of lengths of directory names (including trailing slash)
     143             :      appended to dir_stack.  We have to have a separate stack of lengths
     144             :      (rather than just popping back to previous slash) because the first
     145             :      element pushed onto the dir stack may contain slashes.  */
     146             :   struct obstack len_stack;
     147             : 
     148             :   /* Stack of active directory entries.
     149             :      The first `active' directory is the initial working directory.
     150             :      Additional active dirs are pushed onto the stack as we `chdir'
     151             :      into each directory to be processed.  When finished with the
     152             :      hierarchy under a directory, pop the active dir stack.  */
     153             :   struct obstack Active_dir;
     154             : 
     155             :   /* Used to detect cycles.  */
     156             :   struct cycle_check_state cycle_check_state;
     157             : 
     158             :   /* Target of a longjmp in case rm has to stop processing the current
     159             :      command-line argument.  This happens 1) when rm detects a directory
     160             :      cycle or 2) when it has processed one or more directories, but then
     161             :      is unable to return to the initial working directory to process
     162             :      additional `.'-relative command-line arguments.  */
     163             :   jmp_buf current_arg_jumpbuf;
     164             : };
     165             : typedef struct dirstack_state Dirstack_state;
     166             : 
     167             : /* A static buffer and its allocated size, these variables are used by
     168             :    xfull_filename and full_filename to form full, relative file names.  */
     169             : static char *g_buf;
     170             : static size_t g_n_allocated;
     171             : 
     172             : /* Like fstatat, but cache the result.  If ST->st_size is -1, the
     173             :    status has not been gotten yet.  If less than -1, fstatat failed
     174             :    with errno == ST->st_ino.  Otherwise, the status has already
     175             :    been gotten, so return 0.  */
     176             : static int
     177          72 : cache_fstatat (int fd, char const *file, struct stat *st, int flag)
     178             : {
     179          72 :   if (st->st_size == -1 && fstatat (fd, file, st, flag) != 0)
     180             :     {
     181          13 :       st->st_size = -2;
     182          13 :       st->st_ino = errno;
     183             :     }
     184          72 :   if (0 <= st->st_size)
     185          59 :     return 0;
     186          13 :   errno = (int) st->st_ino;
     187          13 :   return -1;
     188             : }
     189             : 
     190             : /* Initialize a fstatat cache *ST.  Return ST for convenience.  */
     191             : static inline struct stat *
     192          66 : cache_stat_init (struct stat *st)
     193             : {
     194          66 :   st->st_size = -1;
     195          66 :   return st;
     196             : }
     197             : 
     198             : /* Return true if *ST has been statted.  */
     199             : static inline bool
     200             : cache_statted (struct stat *st)
     201             : {
     202             :   return (st->st_size != -1);
     203             : }
     204             : 
     205             : /* Return true if *ST has been statted successfully.  */
     206             : static inline bool
     207           4 : cache_stat_ok (struct stat *st)
     208             : {
     209           4 :   return (0 <= st->st_size);
     210             : }
     211             : 
     212             : 
     213             : static void
     214           0 : hash_freer (void *x)
     215             : {
     216           0 :   free (x);
     217           0 : }
     218             : 
     219             : static bool
     220           0 : hash_compare_strings (void const *x, void const *y)
     221             : {
     222           0 :   return STREQ (x, y) ? true : false;
     223             : }
     224             : 
     225             : static inline void
     226           2 : push_dir (Dirstack_state *ds, const char *dir_name)
     227             : {
     228           2 :   size_t len = strlen (dir_name);
     229             : 
     230             :   /* Don't copy trailing slashes.  */
     231           4 :   while (1 < len && dir_name[len - 1] == '/')
     232           0 :     --len;
     233             : 
     234             :   /* Append the string onto the stack.  */
     235           2 :   obstack_grow (&ds->dir_stack, dir_name, len);
     236             : 
     237             :   /* Append a trailing slash.  */
     238           2 :   obstack_1grow (&ds->dir_stack, '/');
     239             : 
     240             :   /* Add one for the slash.  */
     241           2 :   ++len;
     242             : 
     243             :   /* Push the length (including slash) onto its stack.  */
     244           2 :   obstack_grow (&ds->len_stack, &len, sizeof (len));
     245           2 : }
     246             : 
     247             : /* Return the entry name of the directory on the top of the stack
     248             :    in malloc'd storage.  */
     249             : static inline char *
     250           2 : top_dir (Dirstack_state const *ds)
     251             : {
     252           2 :   size_t n_lengths = obstack_object_size (&ds->len_stack) / sizeof (size_t);
     253           2 :   size_t *length = obstack_base (&ds->len_stack);
     254           2 :   size_t top_len = length[n_lengths - 1];
     255           2 :   char const *p = obstack_next_free (&ds->dir_stack) - top_len;
     256           2 :   char *q = xmalloc (top_len);
     257           2 :   memcpy (q, p, top_len - 1);
     258           2 :   q[top_len - 1] = 0;
     259           2 :   return q;
     260             : }
     261             : 
     262             : static inline void
     263           2 : pop_dir (Dirstack_state *ds)
     264             : {
     265           2 :   size_t n_lengths = obstack_object_size (&ds->len_stack) / sizeof (size_t);
     266           2 :   size_t *length = obstack_base (&ds->len_stack);
     267             : 
     268           2 :   assert (n_lengths > 0);
     269           2 :   size_t top_len = length[n_lengths - 1];
     270           2 :   assert (top_len >= 2);
     271             : 
     272             :   /* Pop the specified length of file name.  */
     273           2 :   assert (obstack_object_size (&ds->dir_stack) >= top_len);
     274           2 :   obstack_blank (&ds->dir_stack, -top_len);
     275             : 
     276             :   /* Pop the length stack, too.  */
     277           2 :   assert (obstack_object_size (&ds->len_stack) >= sizeof (size_t));
     278           2 :   obstack_blank (&ds->len_stack, -(int) sizeof (size_t));
     279           2 : }
     280             : 
     281             : /* Copy the SRC_LEN bytes of data beginning at SRC into the DST_LEN-byte
     282             :    buffer, DST, so that the last source byte is at the end of the destination
     283             :    buffer.  If SRC_LEN is longer than DST_LEN, then set *TRUNCATED.
     284             :    Set *RESULT to point to the beginning of (the portion of) the source data
     285             :    in DST.  Return the number of bytes remaining in the destination buffer.  */
     286             : 
     287             : static size_t
     288           0 : right_justify (char *dst, size_t dst_len, const char *src, size_t src_len,
     289             :                char **result, bool *truncated)
     290             : {
     291             :   const char *sp;
     292             :   char *dp;
     293             : 
     294           0 :   if (src_len <= dst_len)
     295             :     {
     296           0 :       sp = src;
     297           0 :       dp = dst + (dst_len - src_len);
     298           0 :       *truncated = false;
     299             :     }
     300             :   else
     301             :     {
     302           0 :       sp = src + (src_len - dst_len);
     303           0 :       dp = dst;
     304           0 :       src_len = dst_len;
     305           0 :       *truncated = true;
     306             :     }
     307             : 
     308           0 :   *result = memcpy (dp, sp, src_len);
     309           0 :   return dst_len - src_len;
     310             : }
     311             : 
     312             : /* Using the global directory name obstack, create the full name of FILENAME.
     313             :    Return it in sometimes-realloc'd space that should not be freed by the
     314             :    caller.  Realloc as necessary.  If realloc fails, return NULL.  */
     315             : 
     316             : static char *
     317          86 : full_filename0 (Dirstack_state const *ds, const char *filename)
     318             : {
     319          86 :   size_t dir_len = obstack_object_size (&ds->dir_stack);
     320          86 :   char *dir_name = obstack_base (&ds->dir_stack);
     321          86 :   size_t filename_len = strlen (filename);
     322          86 :   size_t n_bytes_needed = dir_len + filename_len + 1;
     323             : 
     324          86 :   if (g_n_allocated < n_bytes_needed)
     325             :     {
     326          38 :       char *new_buf = realloc (g_buf, n_bytes_needed);
     327          38 :       if (new_buf == NULL)
     328           0 :         return NULL;
     329             : 
     330          38 :       g_buf = new_buf;
     331          38 :       g_n_allocated = n_bytes_needed;
     332             :     }
     333             : 
     334          86 :   if (STREQ (filename, ".") && dir_len)
     335           0 :     {
     336             :       /* FILENAME is just `.' and dir_len is nonzero.
     337             :          Copy the directory part, omitting the trailing slash,
     338             :          and append a trailing zero byte.  */
     339           0 :       char *p = mempcpy (g_buf, dir_name, dir_len - 1);
     340           0 :       *p = 0;
     341             :     }
     342             :   else
     343             :     {
     344             :       /* Copy the directory part, including trailing slash, and then
     345             :          append the filename part, including a trailing zero byte.  */
     346          86 :       memcpy (mempcpy (g_buf, dir_name, dir_len), filename, filename_len + 1);
     347          86 :       assert (strlen (g_buf) + 1 == n_bytes_needed);
     348             :     }
     349             : 
     350          86 :   return g_buf;
     351             : }
     352             : 
     353             : /* Using the global directory name obstack, create the full name of FILENAME.
     354             :    Return it in sometimes-realloc'd space that should not be freed by the
     355             :    caller.  Realloc as necessary.  If realloc fails, die.  */
     356             : 
     357             : static char *
     358          33 : xfull_filename (Dirstack_state const *ds, const char *filename)
     359             : {
     360          33 :   char *buf = full_filename0 (ds, filename);
     361          33 :   if (buf == NULL)
     362           0 :     xalloc_die ();
     363          33 :   return buf;
     364             : }
     365             : 
     366             : /* Using the global directory name obstack, create the full name FILENAME.
     367             :    Return it in sometimes-realloc'd space that should not be freed by the
     368             :    caller.  Realloc as necessary.  If realloc fails, use a static buffer
     369             :    and put as long a suffix in that buffer as possible.  Be careful not
     370             :    to change errno.  */
     371             : 
     372             : #define full_filename(Filename) full_filename_ (ds, Filename)
     373             : static char *
     374          53 : full_filename_ (Dirstack_state const *ds, const char *filename)
     375             : {
     376          53 :   int saved_errno = errno;
     377          53 :   char *full_name = full_filename0 (ds, filename);
     378          53 :   if (full_name)
     379             :     {
     380          53 :       errno = saved_errno;
     381          53 :       return full_name;
     382             :     }
     383             : 
     384             :   {
     385             : #define SBUF_SIZE 512
     386             : #define ELLIPSES_PREFIX "[...]"
     387             :     static char static_buf[SBUF_SIZE];
     388             :     bool file_truncated;
     389             :     bool dir_truncated;
     390             :     size_t n_bytes_remaining;
     391             :     char *p;
     392           0 :     char *dir_name = obstack_base (&ds->dir_stack);
     393           0 :     size_t dir_len = obstack_object_size (&ds->dir_stack);
     394             : 
     395           0 :     free (g_buf);
     396           0 :     n_bytes_remaining = right_justify (static_buf, SBUF_SIZE, filename,
     397           0 :                                        strlen (filename) + 1, &p,
     398             :                                        &file_truncated);
     399           0 :     right_justify (static_buf, n_bytes_remaining, dir_name, dir_len,
     400             :                    &p, &dir_truncated);
     401           0 :     if (file_truncated || dir_truncated)
     402             :       {
     403           0 :         memcpy (static_buf, ELLIPSES_PREFIX,
     404             :                 sizeof (ELLIPSES_PREFIX) - 1);
     405             :       }
     406           0 :     errno = saved_errno;
     407           0 :     return p;
     408             :   }
     409             : }
     410             : 
     411             : static inline size_t
     412         187 : AD_stack_height (Dirstack_state const *ds)
     413             : {
     414         187 :   return obstack_object_size (&ds->Active_dir) / sizeof (struct AD_ent);
     415             : }
     416             : 
     417             : static inline struct AD_ent *
     418         189 : AD_stack_top (Dirstack_state const *ds)
     419             : {
     420         189 :   return (struct AD_ent *)
     421         189 :     ((char *) obstack_next_free (&ds->Active_dir) - sizeof (struct AD_ent));
     422             : }
     423             : 
     424             : static void
     425          61 : AD_stack_pop (Dirstack_state *ds)
     426             : {
     427          61 :   assert (0 < AD_stack_height (ds));
     428             : 
     429             :   /* operate on Active_dir.  pop and free top entry */
     430          61 :   struct AD_ent *top = AD_stack_top (ds);
     431          61 :   if (top->unremovable)
     432           0 :     hash_free (top->unremovable);
     433          61 :   obstack_blank (&ds->Active_dir, -(int) sizeof (struct AD_ent));
     434          61 : }
     435             : 
     436             : static void
     437           2 : AD_stack_clear (Dirstack_state *ds)
     438             : {
     439           4 :   while (0 < AD_stack_height (ds))
     440             :     {
     441           0 :       AD_stack_pop (ds);
     442             :     }
     443           2 : }
     444             : 
     445             : static Dirstack_state *
     446          59 : ds_init (void)
     447             : {
     448          59 :   Dirstack_state *ds = xmalloc (sizeof *ds);
     449          59 :   obstack_init (&ds->dir_stack);
     450          59 :   obstack_init (&ds->len_stack);
     451          59 :   obstack_init (&ds->Active_dir);
     452          59 :   return ds;
     453             : }
     454             : 
     455             : static void
     456          59 : ds_clear (Dirstack_state *ds)
     457             : {
     458          59 :   obstack_free (&ds->dir_stack, obstack_finish (&ds->dir_stack));
     459          59 :   obstack_free (&ds->len_stack, obstack_finish (&ds->len_stack));
     460         175 :   while (0 < AD_stack_height (ds))
     461          57 :     AD_stack_pop (ds);
     462          59 :   obstack_free (&ds->Active_dir, obstack_finish (&ds->Active_dir));
     463          59 : }
     464             : 
     465             : static void
     466          59 : ds_free (Dirstack_state *ds)
     467             : {
     468          59 :   obstack_free (&ds->dir_stack, NULL);
     469          59 :   obstack_free (&ds->len_stack, NULL);
     470          59 :   obstack_free (&ds->Active_dir, NULL);
     471          59 :   free (ds);
     472          59 : }
     473             : 
     474             : /* Pop the active directory (AD) stack and prepare to move `up' one level,
     475             :    safely.  Moving `up' usually means opening `..', but when we've just
     476             :    finished recursively processing a command-line directory argument,
     477             :    there's nothing left on the stack, so set *FDP to AT_FDCWD in that case.
     478             :    The idea is to return with *FDP opened on the parent directory,
     479             :    assuming there are entries in that directory that we need to remove.
     480             : 
     481             :    Note that we must not call opendir (or fdopendir) just yet, since
     482             :    the caller must first remove the directory we're coming from.
     483             :    That is because some file system implementations cache readdir
     484             :    results at opendir time; so calling opendir, rmdir, readdir would
     485             :    return an entry for the just-removed directory.
     486             : 
     487             :    Whenever using chdir '..' (virtually, now, via openat), verify
     488             :    that the post-chdir dev/ino numbers for `.' match the saved ones.
     489             :    If any system call fails or if dev/ino don't match, then give a
     490             :    diagnostic and longjump out.
     491             :    Return the name (in malloc'd storage) of the
     492             :    directory (usually now empty) from which we're coming, and which
     493             :    corresponds to the input value of DIRP.
     494             : 
     495             :    Finally, note that while this function's name is no longer as
     496             :    accurate as it once was (it no longer calls chdir), it does open
     497             :    the destination directory.  */
     498             : static char *
     499           2 : AD_pop_and_chdir (DIR *dirp, int *fdp, Dirstack_state *ds)
     500             : {
     501           2 :   struct AD_ent *leaf_dir_ent = AD_stack_top(ds);
     502           2 :   struct dev_ino leaf_dev_ino = leaf_dir_ent->dev_ino;
     503           2 :   enum RM_status old_status = leaf_dir_ent->status;
     504             :   struct AD_ent *top;
     505             : 
     506             :   /* Get the name of the current (but soon to be `previous') directory
     507             :      from the top of the stack.  */
     508           2 :   char *prev_dir = top_dir (ds);
     509             : 
     510           2 :   AD_stack_pop (ds);
     511           2 :   pop_dir (ds);
     512           2 :   top = AD_stack_top (ds);
     513             : 
     514             :   /* If the directory we're about to leave (and try to rmdir)
     515             :      is the one whose dev_ino is being used to detect a cycle,
     516             :      reset cycle_check_state.dev_ino to that of the parent.
     517             :      Otherwise, once that directory is removed, its dev_ino
     518             :      could be reused in the creation (by some other process)
     519             :      of a directory that this rm process would encounter,
     520             :      which would result in a false-positive cycle indication.  */
     521           2 :   CYCLE_CHECK_REFLECT_CHDIR_UP (&ds->cycle_check_state,
     522             :                                 top->dev_ino, leaf_dev_ino);
     523             : 
     524             :   /* Propagate any failure to parent.  */
     525           2 :   UPDATE_STATUS (top->status, old_status);
     526             : 
     527           2 :   assert (AD_stack_height (ds));
     528             : 
     529           2 :   if (1 < AD_stack_height (ds))
     530             :     {
     531             :       struct stat sb;
     532           0 :       int fd = openat (dirfd (dirp), "..", O_RDONLY);
     533           0 :       if (closedir (dirp) != 0)
     534             :         {
     535           0 :           error (0, errno, _("FATAL: failed to close directory %s"),
     536           0 :                  quote (full_filename (prev_dir)));
     537           0 :           goto next_cmdline_arg;
     538             :         }
     539             : 
     540             :       /* The above fails with EACCES when DIRP is readable but not
     541             :          searchable, when using Solaris' openat.  Without this openat
     542             :          call, tests/rm2 would fail to remove directories a/2 and a/3.  */
     543           0 :       if (fd < 0)
     544           0 :         fd = openat (AT_FDCWD, xfull_filename (ds, "."), O_RDONLY);
     545             : 
     546           0 :       if (fd < 0)
     547             :         {
     548           0 :           error (0, errno, _("FATAL: cannot open .. from %s"),
     549           0 :                  quote (full_filename (prev_dir)));
     550           0 :           goto next_cmdline_arg;
     551             :         }
     552             : 
     553           0 :       if (fstat (fd, &sb))
     554             :         {
     555           0 :           error (0, errno,
     556             :                  _("FATAL: cannot ensure %s (returned to via ..) is safe"),
     557           0 :                  quote (full_filename (".")));
     558           0 :           goto close_and_next;
     559             :         }
     560             : 
     561             :       /*  Ensure that post-chdir dev/ino match the stored ones.  */
     562           0 :       if ( ! SAME_INODE (sb, top->dev_ino))
     563             :         {
     564           0 :           error (0, 0, _("FATAL: directory %s changed dev/ino"),
     565           0 :                  quote (full_filename (".")));
     566           0 :         close_and_next:;
     567           0 :           close (fd);
     568             : 
     569           0 :         next_cmdline_arg:;
     570           0 :           free (prev_dir);
     571           0 :           longjmp (ds->current_arg_jumpbuf, 1);
     572             :         }
     573           0 :       *fdp = fd;
     574             :     }
     575             :   else
     576             :     {
     577           2 :       if (closedir (dirp) != 0)
     578             :         {
     579           0 :           error (0, errno, _("FATAL: failed to close directory %s"),
     580           0 :                  quote (full_filename (prev_dir)));
     581           0 :           goto next_cmdline_arg;
     582             :         }
     583           2 :       *fdp = AT_FDCWD;
     584             :     }
     585             : 
     586           2 :   return prev_dir;
     587             : }
     588             : 
     589             : /* Initialize *HT if it is NULL.  Return *HT.  */
     590             : static Hash_table *
     591           0 : AD_ensure_initialized (Hash_table **ht)
     592             : {
     593           0 :   if (*ht == NULL)
     594             :     {
     595           0 :       *ht = hash_initialize (HT_UNREMOVABLE_INITIAL_CAPACITY, NULL, hash_pjw,
     596             :                              hash_compare_strings, hash_freer);
     597           0 :       if (*ht == NULL)
     598           0 :         xalloc_die ();
     599             :     }
     600             : 
     601           0 :   return *ht;
     602             : }
     603             : 
     604             : /* Initialize *HT if it is NULL.
     605             :    Insert FILENAME into HT.  */
     606             : static void
     607           0 : AD_mark_helper (Hash_table **ht, char *filename)
     608             : {
     609           0 :   void *ent = hash_insert (AD_ensure_initialized (ht), filename);
     610           0 :   if (ent == NULL)
     611           0 :     xalloc_die ();
     612             :   else
     613             :     {
     614           0 :       if (ent != filename)
     615           0 :         free (filename);
     616             :     }
     617           0 : }
     618             : 
     619             : /* Mark FILENAME (in current directory) as unremovable.  */
     620             : static void
     621           0 : AD_mark_as_unremovable (Dirstack_state *ds, char const *filename)
     622             : {
     623           0 :   AD_mark_helper (&AD_stack_top(ds)->unremovable, xstrdup (filename));
     624           0 : }
     625             : 
     626             : /* Mark the current directory as unremovable.  I.e., mark the entry
     627             :    in the parent directory corresponding to `.'.
     628             :    This happens e.g., when an opendir fails and the only name
     629             :    the caller has conveniently at hand is `.'.  */
     630             : static void
     631           0 : AD_mark_current_as_unremovable (Dirstack_state *ds)
     632             : {
     633           0 :   struct AD_ent *top = AD_stack_top (ds);
     634           0 :   char *curr = top_dir (ds);
     635             : 
     636           0 :   assert (1 < AD_stack_height (ds));
     637             : 
     638           0 :   --top;
     639           0 :   AD_mark_helper (&top->unremovable, curr);
     640           0 : }
     641             : 
     642             : /* Push an initial dummy entry onto the stack.
     643             :    This will always be the bottommost entry on the stack.  */
     644             : static void
     645          59 : AD_push_initial (Dirstack_state *ds)
     646             : {
     647             :   struct AD_ent *top;
     648             : 
     649             :   /* Extend the stack.  */
     650          59 :   obstack_blank (&ds->Active_dir, sizeof (struct AD_ent));
     651             : 
     652             :   /* Fill in the new values.  */
     653          59 :   top = AD_stack_top (ds);
     654          59 :   top->unremovable = NULL;
     655             : 
     656             :   /* These should never be used.
     657             :      Give them values that might look suspicious
     658             :      in a debugger or in a diagnostic.  */
     659          59 :   top->dev_ino.st_dev = TYPE_MAXIMUM (dev_t);
     660          59 :   top->dev_ino.st_ino = TYPE_MAXIMUM (ino_t);
     661          59 : }
     662             : 
     663             : /* Push info about the current working directory (".") onto the
     664             :    active directory stack.  DIR is the ./-relative name through
     665             :    which we've just `chdir'd to this directory.  DIR_SB_FROM_PARENT
     666             :    is the result of calling lstat on DIR from the parent of DIR.
     667             :    Longjump out (skipping the entire command line argument we're
     668             :    dealing with) if `fstat (FD_CWD, ...' fails or if someone has
     669             :    replaced DIR with e.g., a symlink to some other directory.  */
     670             : static void
     671           2 : AD_push (int fd_cwd, Dirstack_state *ds, char const *dir,
     672             :          struct stat const *dir_sb_from_parent)
     673             : {
     674             :   struct AD_ent *top;
     675             : 
     676           2 :   push_dir (ds, dir);
     677             : 
     678             :   /* If our uses of openat are guaranteed not to
     679             :      follow a symlink, then we can skip this check.  */
     680             :   if (! HAVE_WORKING_O_NOFOLLOW)
     681             :     {
     682             :       struct stat sb;
     683             :       if (fstat (fd_cwd, &sb) != 0)
     684             :         {
     685             :           error (0, errno, _("FATAL: cannot enter directory %s"),
     686             :                  quote (full_filename (".")));
     687             :           longjmp (ds->current_arg_jumpbuf, 1);
     688             :         }
     689             : 
     690             :       if ( ! SAME_INODE (sb, *dir_sb_from_parent))
     691             :         {
     692             :           error (0, 0,
     693             :                  _("FATAL: just-changed-to directory %s changed dev/ino"),
     694             :                  quote (full_filename (".")));
     695             :           longjmp (ds->current_arg_jumpbuf, 1);
     696             :         }
     697             :     }
     698             : 
     699           2 :   if (cycle_check (&ds->cycle_check_state, dir_sb_from_parent))
     700             :     {
     701           0 :       error (0, 0, _("\
     702             : WARNING: Circular directory structure.\n\
     703             : This almost certainly means that you have a corrupted file system.\n\
     704             : NOTIFY YOUR SYSTEM MANAGER.\n\
     705             : The following directory is part of the cycle:\n  %s\n"),
     706           0 :              quote (full_filename (".")));
     707           0 :       longjmp (ds->current_arg_jumpbuf, 1);
     708             :     }
     709             : 
     710             :   /* Extend the stack.  */
     711           2 :   obstack_blank (&ds->Active_dir, sizeof (struct AD_ent));
     712             : 
     713             :   /* The active directory stack must be one larger than the length stack.  */
     714           2 :   assert (AD_stack_height (ds) ==
     715             :           1 + obstack_object_size (&ds->len_stack) / sizeof (size_t));
     716             : 
     717             :   /* Fill in the new values.  */
     718           2 :   top = AD_stack_top (ds);
     719           2 :   top->dev_ino.st_dev = dir_sb_from_parent->st_dev;
     720           2 :   top->dev_ino.st_ino = dir_sb_from_parent->st_ino;
     721           2 :   top->unremovable = NULL;
     722           2 : }
     723             : 
     724             : static inline bool
     725           0 : AD_is_removable (Dirstack_state const *ds, char const *file)
     726             : {
     727           0 :   struct AD_ent *top = AD_stack_top (ds);
     728           0 :   return ! (top->unremovable && hash_lookup (top->unremovable, file));
     729             : }
     730             : 
     731             : /* Return 1 if FILE is an unwritable non-symlink,
     732             :    0 if it is writable or some other type of file,
     733             :    -1 and set errno if there is some problem in determining the answer.
     734             :    Set *BUF to the file status.
     735             :    This is to avoid calling euidaccess when FILE is a symlink.  */
     736             : static int
     737          45 : write_protected_non_symlink (int fd_cwd,
     738             :                              char const *file,
     739             :                              Dirstack_state const *ds,
     740             :                              struct stat *buf)
     741             : {
     742          45 :   if (can_write_any_file ())
     743           0 :     return 0;
     744          45 :   if (cache_fstatat (fd_cwd, file, buf, AT_SYMLINK_NOFOLLOW) != 0)
     745           9 :     return -1;
     746          36 :   if (S_ISLNK (buf->st_mode))
     747           3 :     return 0;
     748             :   /* Here, we know FILE is not a symbolic link.  */
     749             : 
     750             :   /* In order to be reentrant -- i.e., to avoid changing the working
     751             :      directory, and at the same time to be able to deal with alternate
     752             :      access control mechanisms (ACLs, xattr-style attributes) and
     753             :      arbitrarily deep trees -- we need a function like eaccessat, i.e.,
     754             :      like Solaris' eaccess, but fd-relative, in the spirit of openat.  */
     755             : 
     756             :   /* In the absence of a native eaccessat function, here are some of
     757             :      the implementation choices [#4 and #5 were suggested by Paul Eggert]:
     758             :      1) call openat with O_WRONLY|O_NOCTTY
     759             :         Disadvantage: may create the file and doesn't work for directory,
     760             :         may mistakenly report `unwritable' for EROFS or ACLs even though
     761             :         perm bits say the file is writable.
     762             : 
     763             :      2) fake eaccessat (save_cwd, fchdir, call euidaccess, restore_cwd)
     764             :         Disadvantage: changes working directory (not reentrant) and can't
     765             :         work if save_cwd fails.
     766             : 
     767             :      3) if (euidaccess (xfull_filename (file), W_OK) == 0)
     768             :         Disadvantage: doesn't work if xfull_filename is too long.
     769             :         Inefficient for very deep trees (O(Depth^2)).
     770             : 
     771             :      4) If the full pathname is sufficiently short (say, less than
     772             :         PATH_MAX or 8192 bytes, whichever is shorter):
     773             :         use method (3) (i.e., euidaccess (xfull_filename (file), W_OK));
     774             :         Otherwise: vfork, fchdir in the child, run euidaccess in the
     775             :         child, then the child exits with a status that tells the parent
     776             :         whether euidaccess succeeded.
     777             : 
     778             :         This avoids the O(N**2) algorithm of method (3), and it also avoids
     779             :         the failure-due-to-too-long-file-names of method (3), but it's fast
     780             :         in the normal shallow case.  It also avoids the lack-of-reentrancy
     781             :         and the save_cwd problems.
     782             :         Disadvantage; it uses a process slot for very-long file names,
     783             :         and would be very slow for hierarchies with many such files.
     784             : 
     785             :      5) If the full file name is sufficiently short (say, less than
     786             :         PATH_MAX or 8192 bytes, whichever is shorter):
     787             :         use method (3) (i.e., euidaccess (xfull_filename (file), W_OK));
     788             :         Otherwise: look just at the file bits.  Perhaps issue a warning
     789             :         the first time this occurs.
     790             : 
     791             :         This is like (4), except for the "Otherwise" case where it isn't as
     792             :         "perfect" as (4) but is considerably faster.  It conforms to current
     793             :         POSIX, and is uniformly better than what Solaris and FreeBSD do (they
     794             :         mess up with long file names). */
     795             : 
     796             :   {
     797             :     /* This implements #5: */
     798          33 :     size_t file_name_len
     799          33 :       = obstack_object_size (&ds->dir_stack) + strlen (file);
     800             : 
     801          33 :     if (MIN (PATH_MAX, 8192) <= file_name_len)
     802           0 :       return ! euidaccess_stat (buf, W_OK);
     803          33 :     if (euidaccess (xfull_filename (ds, file), W_OK) == 0)
     804          23 :       return 0;
     805          10 :     if (errno == EACCES)
     806             :       {
     807          10 :         errno = 0;
     808          10 :         return 1;
     809             :       }
     810             : 
     811             :     /* Perhaps some other process has removed the file, or perhaps this
     812             :        is a buggy NFS client.  */
     813           0 :     return -1;
     814             :   }
     815             : }
     816             : 
     817             : /* Prompt whether to remove FILENAME, if required via a combination of
     818             :    the options specified by X and/or file attributes.  If the file may
     819             :    be removed, return RM_OK.  If the user declines to remove the file,
     820             :    return RM_USER_DECLINED.  If not ignoring missing files and we
     821             :    cannot lstat FILENAME, then return RM_ERROR.
     822             : 
     823             :    *PDIRENT_TYPE is the type of the directory entry; update it to DT_DIR
     824             :    or DT_LNK as needed.  *SBUF is the file's status.
     825             : 
     826             :    Depending on MODE, ask whether to `descend into' or to `remove' the
     827             :    directory FILENAME.  MODE is ignored when FILENAME is not a directory.
     828             :    Set *IS_EMPTY to T_YES if FILENAME is an empty directory, and it is
     829             :    appropriate to try to remove it with rmdir (e.g. recursive mode).
     830             :    Don't even try to set *IS_EMPTY when MODE == PA_REMOVE_DIR.  */
     831             : static enum RM_status
     832          61 : prompt (int fd_cwd, Dirstack_state const *ds, char const *filename,
     833             :         int *pdirent_type, struct stat *sbuf,
     834             :         struct rm_options const *x, enum Prompt_action mode,
     835             :         Ternary *is_empty)
     836             : {
     837          61 :   int write_protected = 0;
     838          61 :   int dirent_type = *pdirent_type;
     839             : 
     840          61 :   *is_empty = T_UNKNOWN;
     841             : 
     842          61 :   if (x->interactive == RMI_NEVER)
     843           3 :     return RM_OK;
     844             : 
     845          58 :   int wp_errno = 0;
     846             : 
     847          58 :   if (!x->ignore_missing_files
     848          58 :       && ((x->interactive == RMI_ALWAYS) || x->stdin_tty)
     849          45 :       && dirent_type != DT_LNK)
     850             :     {
     851          45 :       write_protected = write_protected_non_symlink (fd_cwd, filename, ds, sbuf);
     852          45 :       wp_errno = errno;
     853             :     }
     854             : 
     855          58 :   if (write_protected || x->interactive == RMI_ALWAYS)
     856             :     {
     857          24 :       if (0 <= write_protected && dirent_type == DT_UNKNOWN)
     858             :         {
     859          15 :           if (cache_fstatat (fd_cwd, filename, sbuf, AT_SYMLINK_NOFOLLOW) == 0)
     860             :             {
     861          15 :               if (S_ISLNK (sbuf->st_mode))
     862           2 :                 dirent_type = DT_LNK;
     863          13 :               else if (S_ISDIR (sbuf->st_mode))
     864          11 :                 dirent_type = DT_DIR;
     865             :               /* Otherwise it doesn't matter, so leave it DT_UNKNOWN.  */
     866          15 :               *pdirent_type = dirent_type;
     867             :             }
     868             :           else
     869             :             {
     870             :               /* This happens, e.g., with `rm '''.  */
     871           0 :               write_protected = -1;
     872           0 :               wp_errno = errno;
     873             :             }
     874             :         }
     875             : 
     876          24 :       if (0 <= write_protected)
     877          15 :         switch (dirent_type)
     878             :           {
     879           2 :           case DT_LNK:
     880             :             /* Using permissions doesn't make sense for symlinks.  */
     881           2 :             if (x->interactive != RMI_ALWAYS)
     882           0 :               return RM_OK;
     883           2 :             break;
     884             : 
     885          11 :           case DT_DIR:
     886          11 :             if (!x->recursive)
     887             :               {
     888          10 :                 write_protected = -1;
     889          10 :                 wp_errno = EISDIR;
     890             :               }
     891          11 :             break;
     892             :           }
     893             : 
     894          24 :       char const *quoted_name = quote (full_filename (filename));
     895             : 
     896          24 :       if (write_protected < 0)
     897             :         {
     898          19 :           error (0, wp_errno, _("cannot remove %s"), quoted_name);
     899          19 :           return RM_ERROR;
     900             :         }
     901             : 
     902             :       /* Issue the prompt.  */
     903             :       /* FIXME: use a variant of error (instead of fprintf) that doesn't
     904             :          append a newline.  Then we won't have to declare program_name in
     905             :          this file.  */
     906           5 :       if (dirent_type == DT_DIR
     907           1 :           && mode == PA_DESCEND_INTO_DIR
     908           1 :           && ((*is_empty = (is_empty_dir (fd_cwd, filename) ? T_YES : T_NO))
     909             :               == T_NO))
     910           1 :         fprintf (stderr,
     911             :                  (write_protected
     912             :                   ? _("%s: descend into write-protected directory %s? ")
     913             :                   : _("%s: descend into directory %s? ")),
     914             :                  program_name, quoted_name);
     915             :       else
     916             :         {
     917           4 :           if (cache_fstatat (fd_cwd, filename, sbuf, AT_SYMLINK_NOFOLLOW) != 0)
     918             :             {
     919           0 :               error (0, errno, _("cannot remove %s"), quoted_name);
     920           0 :               return RM_ERROR;
     921             :             }
     922             : 
     923             :           /* TRANSLATORS: You may find it more convenient to translate
     924             :              the equivalent of _("%s: remove %s (write-protected) %s? ").
     925             :              It should avoid grammatical problems with the output
     926             :              of file_type.  */
     927           4 :           fprintf (stderr,
     928             :                    (write_protected
     929             :                     ? _("%s: remove write-protected %s %s? ")
     930             :                     : _("%s: remove %s %s? ")),
     931             :                    program_name, file_type (sbuf), quoted_name);
     932             :         }
     933             : 
     934           5 :       if (!yesno ())
     935           5 :         return RM_USER_DECLINED;
     936             :     }
     937          34 :   return RM_OK;
     938             : }
     939             : 
     940             : /* Return true if FILENAME is a directory (and not a symlink to a directory).
     941             :    Otherwise, including the case in which lstat fails, return false.
     942             :    *ST is FILENAME's tstatus.
     943             :    Do not modify errno.  */
     944             : static inline bool
     945           0 : is_dir_lstat (int fd_cwd, char const *filename, struct stat *st)
     946             : {
     947           0 :   int saved_errno = errno;
     948           0 :   bool is_dir =
     949           0 :     (cache_fstatat (fd_cwd, filename, st, AT_SYMLINK_NOFOLLOW) == 0
     950           0 :      && S_ISDIR (st->st_mode));
     951           0 :   errno = saved_errno;
     952           0 :   return is_dir;
     953             : }
     954             : 
     955             : /* Return true if FILENAME is a non-directory.
     956             :    Otherwise, including the case in which lstat fails, return false.
     957             :    *ST is FILENAME's tstatus.
     958             :    Do not modify errno.  */
     959             : static inline bool
     960           0 : is_nondir_lstat (int fd_cwd, char const *filename, struct stat *st)
     961             : {
     962           0 :   int saved_errno = errno;
     963           0 :   bool is_non_dir =
     964           0 :     (cache_fstatat (fd_cwd, filename, st, AT_SYMLINK_NOFOLLOW) == 0
     965           0 :      && !S_ISDIR (st->st_mode));
     966           0 :   errno = saved_errno;
     967           0 :   return is_non_dir;
     968             : }
     969             : 
     970             : #define DO_UNLINK(Fd_cwd, Filename, X)                                  \
     971             :   do                                                                    \
     972             :     {                                                                   \
     973             :       if (unlinkat (Fd_cwd, Filename, 0) == 0)                          \
     974             :         {                                                               \
     975             :           if ((X)->verbose)                                          \
     976             :             printf (_("removed %s\n"), quote (full_filename (Filename))); \
     977             :           return RM_OK;                                                 \
     978             :         }                                                               \
     979             :                                                                         \
     980             :       if (ignorable_missing (X, errno))                                 \
     981             :         return RM_OK;                                                   \
     982             :     }                                                                   \
     983             :   while (0)
     984             : 
     985             : #define DO_RMDIR(Fd_cwd, Filename, X)                   \
     986             :   do                                                    \
     987             :     {                                                   \
     988             :       if (unlinkat (Fd_cwd, Filename, AT_REMOVEDIR) == 0) /* rmdir */ \
     989             :         {                                               \
     990             :           if ((X)->verbose)                          \
     991             :             printf (_("removed directory: %s\n"),     \
     992             :                     quote (full_filename (Filename)));  \
     993             :           return RM_OK;                                 \
     994             :         }                                               \
     995             :                                                         \
     996             :       if (ignorable_missing (X, errno))                 \
     997             :         return RM_OK;                                   \
     998             :                                                         \
     999             :       if (errno == ENOTEMPTY || errno == EEXIST)        \
    1000             :         return RM_NONEMPTY_DIR;                         \
    1001             :     }                                                   \
    1002             :   while (0)
    1003             : 
    1004             : /* When a function like unlink, rmdir, or fstatat fails with an errno
    1005             :    value of ERRNUM, return true if the specified file system object
    1006             :    is guaranteed not to exist;  otherwise, return false.  */
    1007             : static inline bool
    1008           5 : nonexistent_file_errno (int errnum)
    1009             : {
    1010             :   /* Do not include ELOOP here, since the specified file may indeed
    1011             :      exist, but be (in)accessible only via too long a symlink chain.
    1012             :      Likewise for ENAMETOOLONG, since rm -f ./././.../foo may fail
    1013             :      if the "..." part expands to a long enough sequence of "./"s,
    1014             :      even though ./foo does indeed exist.  */
    1015             : 
    1016           5 :   switch (errnum)
    1017             :     {
    1018           1 :     case ENOENT:
    1019             :     case ENOTDIR:
    1020           1 :       return true;
    1021           4 :     default:
    1022           4 :       return false;
    1023             :     }
    1024             : }
    1025             : 
    1026             : /* Encapsulate the test for whether the errno value, ERRNUM, is ignorable.  */
    1027             : static inline bool
    1028          62 : ignorable_missing (struct rm_options const *x, int errnum)
    1029             : {
    1030          62 :   return x->ignore_missing_files && nonexistent_file_errno (errnum);
    1031             : }
    1032             : 
    1033             : /* Remove the file or directory specified by FILENAME.
    1034             :    Return RM_OK if it is removed, and RM_ERROR or RM_USER_DECLINED if not.
    1035             :    But if FILENAME specifies a non-empty directory, return RM_NONEMPTY_DIR. */
    1036             : 
    1037             : static enum RM_status
    1038          59 : remove_entry (int fd_cwd, Dirstack_state const *ds, char const *filename,
    1039             :               int dirent_type_arg, struct stat *st,
    1040             :               struct rm_options const *x)
    1041             : {
    1042             :   Ternary is_empty_directory;
    1043          59 :   enum RM_status s = prompt (fd_cwd, ds, filename, &dirent_type_arg, st, x,
    1044             :                              PA_DESCEND_INTO_DIR,
    1045             :                              &is_empty_directory);
    1046          59 :   int dirent_type = dirent_type_arg;
    1047          59 :   if (s != RM_OK)
    1048          24 :     return s;
    1049             : 
    1050             :   /* Why bother with the following if/else block?  Because on systems with
    1051             :      an unlink function that *can* unlink directories, we must determine the
    1052             :      type of each entry before removing it.  Otherwise, we'd risk unlinking
    1053             :      an entire directory tree simply by unlinking a single directory;  then
    1054             :      all the storage associated with that hierarchy would not be freed until
    1055             :      the next fsck.  Not nice.  To avoid that, on such slightly losing
    1056             :      systems, we need to call lstat to determine the type of each entry,
    1057             :      and that represents extra overhead that -- it turns out -- we can
    1058             :      avoid on non-losing systems, since there, unlink will never remove
    1059             :      a directory.  Also, on systems where unlink may unlink directories,
    1060             :      we're forced to allow a race condition: we lstat a non-directory, then
    1061             :      go to unlink it, but in the mean time, a malicious someone could have
    1062             :      replaced it with a directory.  */
    1063             : 
    1064             :   if (cannot_unlink_dir ())
    1065             :     {
    1066          35 :       if (dirent_type == DT_DIR && ! x->recursive)
    1067             :         {
    1068           0 :           error (0, EISDIR, _("cannot remove %s"),
    1069           0 :                  quote (full_filename (filename)));
    1070           0 :           return RM_ERROR;
    1071             :         }
    1072             : 
    1073             :       /* is_empty_directory is set iff it's ok to use rmdir.
    1074             :          Note that it's set only in interactive mode -- in which case it's
    1075             :          an optimization that arranges so that the user is asked just
    1076             :          once whether to remove the directory.  */
    1077          35 :       if (is_empty_directory == T_YES)
    1078           0 :         DO_RMDIR (fd_cwd, filename, x);
    1079             : 
    1080             :       /* If we happen to know that FILENAME is a directory, return now
    1081             :          and let the caller remove it -- this saves the overhead of a failed
    1082             :          unlink call.  If FILENAME is a command-line argument, then
    1083             :          DIRENT_TYPE is DT_UNKNOWN so we'll first try to unlink it.
    1084             :          Using unlink here is ok, because it cannot remove a
    1085             :          directory.  */
    1086          35 :       if (dirent_type == DT_DIR)
    1087           0 :         return RM_NONEMPTY_DIR;
    1088             : 
    1089          35 :       DO_UNLINK (fd_cwd, filename, x);
    1090             : 
    1091             :       /* Upon a failed attempt to unlink a directory, most non-Linux systems
    1092             :          set errno to the POSIX-required value EPERM.  In that case, change
    1093             :          errno to EISDIR so that we emit a better diagnostic.  */
    1094          30 :       if (! x->recursive && errno == EPERM && is_dir_lstat (fd_cwd,
    1095             :                                                             filename, st))
    1096           0 :         errno = EISDIR;
    1097             : 
    1098          30 :       if (! x->recursive
    1099           2 :           || (cache_stat_ok (st) && !S_ISDIR (st->st_mode))
    1100           2 :           || ((errno == EACCES || errno == EPERM)
    1101           0 :               && is_nondir_lstat (fd_cwd, filename, st))
    1102             :           )
    1103             :         {
    1104          28 :           if (ignorable_missing (x, errno))
    1105           0 :             return RM_OK;
    1106             : 
    1107             :           /* Either --recursive is not in effect, or the file cannot be a
    1108             :              directory.  Report the unlink problem and fail.  */
    1109          28 :           error (0, errno, _("cannot remove %s"),
    1110          28 :                  quote (full_filename (filename)));
    1111          28 :           return RM_ERROR;
    1112             :         }
    1113           2 :       assert (!cache_stat_ok (st) || S_ISDIR (st->st_mode));
    1114             :     }
    1115             :   else
    1116             :     {
    1117             :       /* If we don't already know whether FILENAME is a directory,
    1118             :          find out now.  Then, if it's a non-directory, we can use
    1119             :          unlink on it.  */
    1120             : 
    1121             :       if (dirent_type == DT_UNKNOWN)
    1122             :         {
    1123             :           if (fstatat (fd_cwd, filename, st, AT_SYMLINK_NOFOLLOW))
    1124             :             {
    1125             :               if (ignorable_missing (x, errno))
    1126             :                 return RM_OK;
    1127             : 
    1128             :               error (0, errno, _("cannot remove %s"),
    1129             :                      quote (full_filename (filename)));
    1130             :               return RM_ERROR;
    1131             :             }
    1132             : 
    1133             :           if (S_ISDIR (st->st_mode))
    1134             :             dirent_type = DT_DIR;
    1135             :         }
    1136             : 
    1137             :       if (dirent_type != DT_DIR)
    1138             :         {
    1139             :           /* At this point, barring race conditions, FILENAME is known
    1140             :              to be a non-directory, so it's ok to try to unlink it.  */
    1141             :           DO_UNLINK (fd_cwd, filename, x);
    1142             : 
    1143             :           /* unlink failed with some other error code.  report it.  */
    1144             :           error (0, errno, _("cannot remove %s"),
    1145             :                  quote (full_filename (filename)));
    1146             :           return RM_ERROR;
    1147             :         }
    1148             : 
    1149             :       if (! x->recursive)
    1150             :         {
    1151             :           error (0, EISDIR, _("cannot remove %s"),
    1152             :                  quote (full_filename (filename)));
    1153             :           return RM_ERROR;
    1154             :         }
    1155             : 
    1156             :       if (is_empty_directory == T_YES)
    1157             :         {
    1158             :           DO_RMDIR (fd_cwd, filename, x);
    1159             :           /* Don't diagnose any failure here.
    1160             :              It'll be detected when the caller tries another way.  */
    1161             :         }
    1162             :     }
    1163             : 
    1164           2 :   return RM_NONEMPTY_DIR;
    1165             : }
    1166             : 
    1167             : /* Given FD_CWD, the file descriptor for an open directory,
    1168             :    open its subdirectory F (F is already `known' to be a directory,
    1169             :    so if it is no longer one, someone is playing games), return a DIR*
    1170             :    pointer for F, and put F's `stat' data in *SUBDIR_SB.
    1171             :    Upon failure give a diagnostic and return NULL.
    1172             :    If PREV_ERRNO is nonzero, it is the errno value from a preceding failed
    1173             :    unlink- or rmdir-like system call -- use that value instead of ENOTDIR
    1174             :    if an opened file turns out not to be a directory.  This is important
    1175             :    when the preceding non-dir-unlink failed due to e.g., EPERM or EACCES.
    1176             :    The caller must use a nonnnull CWD_ERRNO the first
    1177             :    time this function is called for each command-line-specified directory.
    1178             :    If CWD_ERRNO is not null, set *CWD_ERRNO to the appropriate error number
    1179             :    if this function fails to restore the initial working directory.
    1180             :    If it is null, report an error and exit if the working directory
    1181             :    isn't restored.  */
    1182             : static DIR *
    1183           2 : fd_to_subdirp (int fd_cwd, char const *f,
    1184             :                int prev_errno,
    1185             :                struct stat *subdir_sb,
    1186             :                int *cwd_errno ATTRIBUTE_UNUSED)
    1187             : {
    1188           2 :   int open_flags = O_RDONLY | O_NOCTTY | O_NOFOLLOW | O_NONBLOCK;
    1189           2 :   int fd_sub = openat_permissive (fd_cwd, f, open_flags, 0, cwd_errno);
    1190             :   int saved_errno;
    1191             : 
    1192             :   /* Record dev/ino of F.  We may compare them against saved values
    1193             :      to thwart any attempt to subvert the traversal.  They are also used
    1194             :      to detect directory cycles.  */
    1195           2 :   if (fd_sub < 0)
    1196           0 :     return NULL;
    1197           2 :   else if (fstat (fd_sub, subdir_sb) != 0)
    1198           0 :     saved_errno = errno;
    1199           2 :   else if (S_ISDIR (subdir_sb->st_mode))
    1200             :     {
    1201           2 :       DIR *subdir_dirp = fdopendir (fd_sub);
    1202           2 :       if (subdir_dirp)
    1203           2 :         return subdir_dirp;
    1204           0 :       saved_errno = errno;
    1205             :     }
    1206             :   else
    1207           0 :     saved_errno = (prev_errno ? prev_errno : ENOTDIR);
    1208             : 
    1209           0 :   close (fd_sub);
    1210           0 :   errno = saved_errno;
    1211           0 :   return NULL;
    1212             : }
    1213             : 
    1214             : /* Remove entries in the directory open on DIRP
    1215             :    Upon finding a directory that is both non-empty and that can be chdir'd
    1216             :    into, return RM_OK and set *SUBDIR and fill in SUBDIR_SB, where
    1217             :    SUBDIR is the malloc'd name of the subdirectory if the chdir succeeded,
    1218             :    NULL otherwise (e.g., if opendir failed or if there was no subdirectory).
    1219             :    Likewise, SUBDIR_SB is the result of calling lstat on SUBDIR.
    1220             :    Return RM_OK if all entries are removed.  Return RM_ERROR if any
    1221             :    entry cannot be removed.  Otherwise, return RM_USER_DECLINED if
    1222             :    the user declines to remove at least one entry.  Remove as much as
    1223             :    possible, continuing even if we fail to remove some entries.  */
    1224             : static enum RM_status
    1225           2 : remove_cwd_entries (DIR **dirp,
    1226             :                     Dirstack_state *ds, char **subdir, struct stat *subdir_sb,
    1227             :                     struct rm_options const *x)
    1228             : {
    1229           2 :   struct AD_ent *top = AD_stack_top (ds);
    1230           2 :   enum RM_status status = top->status;
    1231           2 :   size_t n_unlinked_since_opendir_or_last_rewind = 0;
    1232             : 
    1233           2 :   assert (VALID_STATUS (status));
    1234           2 :   *subdir = NULL;
    1235             : 
    1236             :   while (1)
    1237           0 :     {
    1238             :       struct dirent const *dp;
    1239             :       enum RM_status tmp_status;
    1240             :       const char *f;
    1241             : 
    1242             :       /* Set errno to zero so we can distinguish between a readdir failure
    1243             :          and when readdir simply finds that there are no more entries.  */
    1244           2 :       errno = 0;
    1245           2 :       dp = readdir_ignoring_dot_and_dotdot (*dirp);
    1246           2 :       if (dp == NULL)
    1247             :         {
    1248           2 :           if (errno)
    1249             :             {
    1250             :               /* fall through */
    1251             :             }
    1252           2 :           else if (NEED_REWIND (n_unlinked_since_opendir_or_last_rewind))
    1253             :             {
    1254             :               /* Call rewinddir if we've called unlink or rmdir so many times
    1255             :                  (since the opendir or the previous rewinddir) that this
    1256             :                  NULL-return may be the symptom of a buggy readdir.  */
    1257           0 :               rewinddir (*dirp);
    1258           0 :               n_unlinked_since_opendir_or_last_rewind = 0;
    1259           0 :               continue;
    1260             :             }
    1261           2 :           break;
    1262             :         }
    1263             : 
    1264           0 :       f = dp->d_name;
    1265             : 
    1266             :       /* Skip files we've already tried/failed to remove.  */
    1267           0 :       if ( ! AD_is_removable (ds, f))
    1268           0 :         continue;
    1269             : 
    1270             :       /* Pass dp->d_type info to remove_entry so the non-glibc
    1271             :          case can decide whether to use unlink or chdir.
    1272             :          Systems without the d_type member will have to endure
    1273             :          the performance hit of first calling lstat F. */
    1274           0 :       cache_stat_init (subdir_sb);
    1275           0 :       tmp_status = remove_entry (dirfd (*dirp), ds, f,
    1276           0 :                                  D_TYPE (dp), subdir_sb, x);
    1277           0 :       switch (tmp_status)
    1278             :         {
    1279           0 :         case RM_OK:
    1280             :           /* Count how many files we've unlinked since the initial
    1281             :              opendir or the last rewinddir.  On buggy systems, if you
    1282             :              remove too many, readdir returns NULL even though there
    1283             :              remain unprocessed directory entries.  */
    1284           0 :           ++n_unlinked_since_opendir_or_last_rewind;
    1285           0 :           break;
    1286             : 
    1287           0 :         case RM_ERROR:
    1288             :         case RM_USER_DECLINED:
    1289           0 :           AD_mark_as_unremovable (ds, f);
    1290           0 :           UPDATE_STATUS (status, tmp_status);
    1291           0 :           break;
    1292             : 
    1293           0 :         case RM_NONEMPTY_DIR:
    1294             :           {
    1295           0 :             DIR *subdir_dirp = fd_to_subdirp (dirfd (*dirp), f,
    1296           0 :                                               errno, subdir_sb, NULL);
    1297           0 :             if (subdir_dirp == NULL)
    1298             :               {
    1299           0 :                 status = RM_ERROR;
    1300             : 
    1301             :                 /* CAUTION: this test and diagnostic are identical to
    1302             :                    those following the other use of fd_to_subdirp.  */
    1303           0 :                 if (ignorable_missing (x, errno))
    1304             :                   {
    1305             :                     /* With -f, don't report "file not found".  */
    1306             :                   }
    1307             :                 else
    1308             :                   {
    1309             :                     /* Upon fd_to_subdirp failure, try to remove F directly,
    1310             :                        in case it's just an empty directory.  */
    1311           0 :                     int saved_errno = errno;
    1312           0 :                     if (unlinkat (dirfd (*dirp), f, AT_REMOVEDIR) == 0)
    1313           0 :                       status = RM_OK;
    1314             :                     else
    1315           0 :                       error (0, saved_errno,
    1316           0 :                              _("cannot remove %s"), quote (full_filename (f)));
    1317             :                   }
    1318             : 
    1319           0 :                 if (status == RM_ERROR)
    1320           0 :                   AD_mark_as_unremovable (ds, f);
    1321           0 :                 break;
    1322             :               }
    1323             : 
    1324           0 :             *subdir = xstrdup (f);
    1325           0 :             if (closedir (*dirp) != 0)
    1326             :               {
    1327           0 :                 error (0, 0, _("failed to close directory %s"),
    1328           0 :                        quote (full_filename (".")));
    1329           0 :                 status = RM_ERROR;
    1330             :               }
    1331           0 :             *dirp = subdir_dirp;
    1332             : 
    1333           0 :             break;
    1334             :           }
    1335             :         }
    1336             : 
    1337             :       /* Record status for this directory.  */
    1338           0 :       UPDATE_STATUS (top->status, status);
    1339             : 
    1340           0 :       if (*subdir)
    1341           0 :         break;
    1342             :     }
    1343             : 
    1344             :   /* Ensure that *dirp is not NULL and that its file descriptor is valid.  */
    1345           2 :   assert (*dirp != NULL);
    1346           2 :   assert (0 <= fcntl (dirfd (*dirp), F_GETFD));
    1347             : 
    1348           2 :   return status;
    1349             : }
    1350             : 
    1351             : /* Do this after each call to AD_push or AD_push_initial.
    1352             :    Because the status = RM_OK bit is too remove-specific to
    1353             :    go into the general-purpose AD_* package.  */
    1354             : #define AD_INIT_OTHER_MEMBERS()                 \
    1355             :   do                                            \
    1356             :     {                                           \
    1357             :       AD_stack_top(ds)->status = RM_OK;              \
    1358             :     }                                           \
    1359             :   while (0)
    1360             : 
    1361             : /*  Remove the hierarchy rooted at DIR.
    1362             :     Do that by changing into DIR, then removing its contents, then
    1363             :     returning to the original working directory and removing DIR itself.
    1364             :     Don't use recursion.  Be careful when using chdir ".." that we
    1365             :     return to the same directory from which we came, if necessary.
    1366             :     Return an RM_status value to indicate success or failure.  */
    1367             : 
    1368             : static enum RM_status
    1369           2 : remove_dir (int fd_cwd, Dirstack_state *ds, char const *dir,
    1370             :             struct stat *dir_st,
    1371             :             struct rm_options const *x, int *cwd_errno)
    1372             : {
    1373             :   enum RM_status status;
    1374           2 :   dev_t current_dev = dir_st->st_dev;
    1375             : 
    1376             :   /* There is a race condition in that an attacker could replace the nonempty
    1377             :      directory, DIR, with a symlink between the preceding call to rmdir
    1378             :      (unlinkat, in our caller) and fd_to_subdirp's openat call.  But on most
    1379             :      systems, even those without openat, this isn't a problem, since we ensure
    1380             :      that opening a symlink will fail, when that is possible.  Otherwise,
    1381             :      fd_to_subdirp's fstat, along with the `fstat' and the dev/ino
    1382             :      comparison in AD_push ensure that we detect it and fail.  */
    1383             : 
    1384           2 :   DIR *dirp = fd_to_subdirp (fd_cwd, dir, 0, dir_st, cwd_errno);
    1385             : 
    1386           2 :   if (dirp == NULL)
    1387             :     {
    1388             :       /* CAUTION: this test and diagnostic are identical to
    1389             :          those following the other use of fd_to_subdirp.  */
    1390           0 :       if (ignorable_missing (x, errno))
    1391             :         {
    1392             :           /* With -f, don't report "file not found".  */
    1393             :         }
    1394             :       else
    1395             :         {
    1396             :           /* Upon fd_to_subdirp failure, try to remove DIR directly,
    1397             :              in case it's just an empty directory.  */
    1398           0 :           int saved_errno = errno;
    1399           0 :           if (unlinkat (fd_cwd, dir, AT_REMOVEDIR) == 0)
    1400           0 :             return RM_OK;
    1401             : 
    1402           0 :           error (0, saved_errno,
    1403           0 :                  _("cannot remove %s"), quote (full_filename (dir)));
    1404             :         }
    1405             : 
    1406           0 :       return RM_ERROR;
    1407             :     }
    1408             : 
    1409           2 :   if (ROOT_DEV_INO_CHECK (x->root_dev_ino, dir_st))
    1410             :     {
    1411           0 :       ROOT_DEV_INO_WARN (full_filename (dir));
    1412           0 :       status = RM_ERROR;
    1413           0 :       goto closedir_and_return;
    1414             :     }
    1415             : 
    1416           2 :   AD_push (dirfd (dirp), ds, dir, dir_st);
    1417           2 :   AD_INIT_OTHER_MEMBERS ();
    1418             : 
    1419           2 :   status = RM_OK;
    1420             : 
    1421             :   while (1)
    1422           0 :     {
    1423           2 :       char *subdir = NULL;
    1424             :       struct stat subdir_sb;
    1425             :       enum RM_status tmp_status;
    1426             : 
    1427           2 :       tmp_status = remove_cwd_entries (&dirp, ds, &subdir, &subdir_sb, x);
    1428             : 
    1429           2 :       if (tmp_status != RM_OK)
    1430             :         {
    1431           0 :           UPDATE_STATUS (status, tmp_status);
    1432           0 :           AD_mark_current_as_unremovable (ds);
    1433             :         }
    1434           2 :       if (subdir)
    1435             :         {
    1436           0 :           if ( ! x->one_file_system
    1437           0 :                || subdir_sb.st_dev == current_dev)
    1438             :             {
    1439           0 :               AD_push (dirfd (dirp), ds, subdir, &subdir_sb);
    1440           0 :               AD_INIT_OTHER_MEMBERS ();
    1441           0 :               free (subdir);
    1442           0 :               continue;
    1443             :             }
    1444             : 
    1445             :           /* Here, --one-file-system is in effect, and with remove_cwd_entries'
    1446             :              traversal into the current directory, (known as SUBDIR, from ..),
    1447             :              DIRP's device number is different from CURRENT_DEV.  Arrange not
    1448             :              to do anything more with this hierarchy.  */
    1449           0 :           error (0, 0, _("skipping %s, since it's on a different device"),
    1450           0 :                  quote (full_filename (subdir)));
    1451           0 :           free (subdir);
    1452           0 :           AD_mark_current_as_unremovable (ds);
    1453           0 :           tmp_status = RM_ERROR;
    1454           0 :           UPDATE_STATUS (status, tmp_status);
    1455             :         }
    1456             : 
    1457             :       /* Execution reaches this point when we've removed the last
    1458             :          removable entry from the current directory -- or, with
    1459             :          --one-file-system, when the current directory is on a
    1460             :          different file system.  */
    1461             :       {
    1462             :         int fd;
    1463             :         /* The name of the directory that we have just processed,
    1464             :            nominally removing all of its contents.  */
    1465           2 :         char *empty_dir = AD_pop_and_chdir (dirp, &fd, ds);
    1466           2 :         dirp = NULL;
    1467           2 :         assert (fd != AT_FDCWD || AD_stack_height (ds) == 1);
    1468             : 
    1469             :         /* Try to remove EMPTY_DIR only if remove_cwd_entries succeeded.  */
    1470           2 :         if (tmp_status == RM_OK)
    1471             :           {
    1472             :             struct stat empty_st;
    1473             :             Ternary is_empty;
    1474           2 :             int dirent_type = DT_DIR;
    1475           2 :             enum RM_status s = prompt (fd, ds, empty_dir, &dirent_type,
    1476             :                                        cache_stat_init (&empty_st), x,
    1477             :                                        PA_REMOVE_DIR, &is_empty);
    1478             : 
    1479           2 :             if (s != RM_OK)
    1480             :               {
    1481           0 :                 free (empty_dir);
    1482           0 :                 status = s;
    1483           0 :                 if (fd != AT_FDCWD)
    1484           0 :                   close (fd);
    1485           0 :                 goto closedir_and_return;
    1486             :               }
    1487             : 
    1488           2 :             if (unlinkat (fd, empty_dir, AT_REMOVEDIR) == 0)
    1489             :               {
    1490           2 :                 if (x->verbose)
    1491           0 :                   printf (_("removed directory: %s\n"),
    1492           0 :                           quote (full_filename (empty_dir)));
    1493             :               }
    1494             :             else
    1495             :               {
    1496           0 :                 error (0, errno, _("cannot remove directory %s"),
    1497           0 :                        quote (full_filename (empty_dir)));
    1498           0 :                 AD_mark_as_unremovable (ds, empty_dir);
    1499           0 :                 status = RM_ERROR;
    1500           0 :                 UPDATE_STATUS (AD_stack_top(ds)->status, status);
    1501             :               }
    1502             :           }
    1503             : 
    1504           2 :         free (empty_dir);
    1505             : 
    1506           2 :         if (fd == AT_FDCWD)
    1507           2 :           break;
    1508             : 
    1509           0 :         dirp = fdopendir (fd);
    1510           0 :         if (dirp == NULL)
    1511             :           {
    1512           0 :             error (0, errno, _("FATAL: cannot return to .. from %s"),
    1513           0 :                    quote (full_filename (".")));
    1514           0 :             close (fd);
    1515           0 :             longjmp (ds->current_arg_jumpbuf, 1);
    1516             :           }
    1517             :       }
    1518             :     }
    1519             : 
    1520             :   /* If the first/final hash table of unremovable entries was used,
    1521             :      free it here.  */
    1522           2 :   AD_stack_pop (ds);
    1523             : 
    1524           2 :  closedir_and_return:;
    1525           2 :   if (dirp != NULL && closedir (dirp) != 0)
    1526             :     {
    1527           0 :       error (0, 0, _("failed to close directory %s"),
    1528           0 :              quote (full_filename (".")));
    1529           0 :       status = RM_ERROR;
    1530             :     }
    1531             : 
    1532           2 :   return status;
    1533             : }
    1534             : 
    1535             : /* Remove the file or directory specified by FILENAME.
    1536             :    Return RM_OK if it is removed, and RM_ERROR or RM_USER_DECLINED if not.  */
    1537             : 
    1538             : static enum RM_status
    1539          80 : rm_1 (Dirstack_state *ds, char const *filename,
    1540             :       struct rm_options const *x, int *cwd_errno)
    1541             : {
    1542          80 :   char const *base = last_component (filename);
    1543          80 :   if (dot_or_dotdot (base))
    1544             :     {
    1545          16 :       error (0, 0, _(base == filename
    1546             :                      ? "cannot remove directory %s"
    1547             :                      : "cannot remove %s directory %s"),
    1548             :              quote_n (0, base), quote_n (1, filename));
    1549          16 :       return RM_ERROR;
    1550             :     }
    1551             : 
    1552             :   struct stat st;
    1553          64 :   cache_stat_init (&st);
    1554          64 :   cycle_check_init (&ds->cycle_check_state);
    1555          64 :   if (x->root_dev_ino)
    1556             :     {
    1557           8 :       if (cache_fstatat (AT_FDCWD, filename, &st, AT_SYMLINK_NOFOLLOW) != 0)
    1558             :         {
    1559           4 :           if (ignorable_missing (x, errno))
    1560           1 :             return RM_OK;
    1561           3 :           error (0, errno, _("cannot remove %s"), quote (filename));
    1562           3 :           return RM_ERROR;
    1563             :         }
    1564           4 :       if (SAME_INODE (st, *(x->root_dev_ino)))
    1565             :         {
    1566           1 :           error (0, 0, _("cannot remove root directory %s"), quote (filename));
    1567           1 :           return RM_ERROR;
    1568             :         }
    1569             :     }
    1570             : 
    1571          59 :   AD_push_initial (ds);
    1572          59 :   AD_INIT_OTHER_MEMBERS ();
    1573             : 
    1574          59 :   enum RM_status status = remove_entry (AT_FDCWD, ds, filename,
    1575             :                                         DT_UNKNOWN, &st, x);
    1576          59 :   if (status == RM_NONEMPTY_DIR)
    1577             :     {
    1578             :       /* In the event that remove_dir->remove_cwd_entries detects
    1579             :          a directory cycle, arrange to fail, give up on this FILE, but
    1580             :          continue on with any other arguments.  */
    1581           2 :       if (setjmp (ds->current_arg_jumpbuf))
    1582           0 :         status = RM_ERROR;
    1583             :       else
    1584           2 :         status = remove_dir (AT_FDCWD, ds, filename, &st, x, cwd_errno);
    1585             : 
    1586           2 :       AD_stack_clear (ds);
    1587             :     }
    1588             : 
    1589          59 :   ds_clear (ds);
    1590          59 :   return status;
    1591             : }
    1592             : 
    1593             : /* Remove all files and/or directories specified by N_FILES and FILE.
    1594             :    Apply the options in X.  */
    1595             : extern enum RM_status
    1596          59 : rm (size_t n_files, char const *const *file, struct rm_options const *x)
    1597             : {
    1598          59 :   enum RM_status status = RM_OK;
    1599          59 :   Dirstack_state *ds = ds_init ();
    1600          59 :   int cwd_errno = 0;
    1601             :   size_t i;
    1602             : 
    1603         139 :   for (i = 0; i < n_files; i++)
    1604             :     {
    1605          80 :       if (cwd_errno && IS_RELATIVE_FILE_NAME (file[i]))
    1606             :         {
    1607           0 :           error (0, 0, _("cannot remove relative-named %s"), quote (file[i]));
    1608           0 :           status = RM_ERROR;
    1609             :         }
    1610             :       else
    1611             :         {
    1612          80 :           enum RM_status s = rm_1 (ds, file[i], x, &cwd_errno);
    1613          80 :           assert (VALID_STATUS (s));
    1614          80 :           UPDATE_STATUS (status, s);
    1615             :         }
    1616             :     }
    1617             : 
    1618          59 :   if (x->require_restore_cwd && cwd_errno)
    1619             :     {
    1620           0 :       error (0, cwd_errno,
    1621             :              _("cannot restore current working directory"));
    1622           0 :       status = RM_ERROR;
    1623             :     }
    1624             : 
    1625          59 :   ds_free (ds);
    1626             : 
    1627          59 :   return status;
    1628             : }

Generated by: LCOV version 1.10