Exemplo n.º 1
0
/* optionp_stat() implements the stat operation when the -P option is
 * in effect (this is also the default).  That option makes us examine
 * the symbolic link itself, not the thing it points to.
 */
int 
optionp_stat(const char *name, struct stat *p)
{
  assert ((state.cwd_dir_fd >= 0) || (state.cwd_dir_fd==AT_FDCWD));
  set_stat_placeholders(p);
  return fstatat(state.cwd_dir_fd, name, p, AT_SYMLINK_NOFOLLOW);
}
Exemplo n.º 2
0
/* optionh_stat() implements the stat operation when the -H option is
 * in effect.
 * 
 * If the item to be examined is a command-line argument, we follow
 * symbolic links.  If the stat() call fails on the command-line item,
 * we fall back on the properties of the symbolic link.
 *
 * If the item to be examined is not a command-line argument, we
 * examine the link itself.
 */
int 
optionh_stat(const char *name, struct stat *p)
{
  if (AT_FDCWD != state.cwd_dir_fd)
    assert (state.cwd_dir_fd >= 0);
  set_stat_placeholders(p);
  if (0 == state.curdepth) 
    {
      /* This file is from the command line; deference the link (if it
       * is a link).  
       */
      int rv;
      rv = fstatat(state.cwd_dir_fd, name, p, 0);
      if (0 == rv)
	return 0;		/* success */
      else
	return fallback_stat(name, p, rv);
    }
  else
    {
      /* Not a file on the command line; do not dereference the link.
       */
      return fstatat(state.cwd_dir_fd, name, p, AT_SYMLINK_NOFOLLOW);
    }
}
Exemplo n.º 3
0
/* optionl_stat() implements the stat operation when the -L option is
 * in effect.  That option makes us examine the thing the symbolic
 * link points to, not the symbolic link itself.
 */
int 
optionl_stat(const char *name, struct stat *p)
{
  int rv;
  if (AT_FDCWD != state.cwd_dir_fd)
    assert (state.cwd_dir_fd >= 0);
  
  set_stat_placeholders(p);
  rv = fstatat(state.cwd_dir_fd, name, p, 0);
  if (0 == rv)
    return 0;			/* normal case. */
  else
    return fallback_stat(name, p, rv);
}
Exemplo n.º 4
0
/* Get the stat information for a file, if it is 
 * not already known. 
 */
int
get_statinfo (const char *pathname, const char *name, struct stat *p)
{
  /* Set markers in fields so we have a good idea if the implementation
   * didn't bother to set them (e.g., NetBSD st_birthtimespec for MS-DOS 
   * files)
   */
  if (!state.have_stat)
    {
      set_stat_placeholders(p);
      if (0 == (*options.xstat) (name, p))
	{
	  if (00000 == p->st_mode)
	    {
	      /* Savannah bug #16378. */
	      error(0, 0, _("Warning: file %s appears to have mode 0000"),
		    quotearg_n_style(0, options.err_quoting_style, name));
	    }
	}
      else
	{
	  if (!options.ignore_readdir_race || (errno != ENOENT) )
	    {
	      error (0, errno, "%s",
		     safely_quote_err_filename(0, pathname));
	      state.exit_status = 1;
	    }
	  return -1;
	}
    }
  state.have_stat = true;
  state.have_type = true;
  state.type = p->st_mode;

  return 0;
}
Exemplo n.º 5
0
/* Safely perform a change in directory.  We do this by calling
 * lstat() on the subdirectory, using chdir() to move into it, and
 * then lstat()ing ".".  We compare the results of the two stat calls
 * to see if they are consistent.  If not, we sound the alarm.
 *
 * If following_links() is true, we do follow symbolic links.
 */
static enum SafeChdirStatus
safely_chdir_lstat (const char *dest,
		    enum TraversalDirection direction,
		    struct stat *statbuf_dest,
		    enum ChdirSymlinkHandling symlink_follow_option,
		    bool *did_stat)
{
  struct stat statbuf_arrived;
  int rv, dotfd=-1;
  int saved_errno;		/* specific_dirname() changes errno. */
  bool rv_set = false;
  bool statflag = false;
  int tries = 0;
  enum WdSanityCheckFatality isfatal = RETRY_IF_SANITY_CHECK_FAILS;

  saved_errno = errno = 0;

  dotfd = open_cloexec (".", O_RDONLY
#if defined O_LARGEFILE
			|O_LARGEFILE
#endif
			);

  /* We jump back to here if wd_sanity_check()
   * recoverably triggers an alert.
   */
 retry:
  ++tries;

  if (dotfd >= 0)
    {
      /* Stat the directory we're going to. */
      set_stat_placeholders (statbuf_dest);
      if (0 == options.xstat (dest, statbuf_dest))
	{
	  statflag = true;

#ifdef S_ISLNK
	  /* symlink_follow_option might be set to SymlinkFollowOk, which
	   * would allow us to chdir() into a symbolic link.  This is
	   * only useful for the case where the directory we're
	   * chdir()ing into is the basename of a command line
	   * argument, for example where "foo/bar/baz" is specified on
	   * the command line.  When -P is in effect (the default),
	   * baz will not be followed if it is a symlink, but if bar
	   * is a symlink, it _should_ be followed.  Hence we need the
	   * ability to override the policy set by following_links().
	   */
	  if (!following_links () && S_ISLNK(statbuf_dest->st_mode))
	    {
	      /* We're not supposed to be following links, but this is
	       * a link.  Check symlink_follow_option to see if we should
	       * make a special exception.
	       */
	      if (symlink_follow_option == SymlinkFollowOk)
		{
		  /* We need to re-stat() the file so that the
		   * sanity check can pass.
		   */
		  if (0 != stat (dest, statbuf_dest))
		    {
		      rv = SafeChdirFailNonexistent;
		      rv_set = true;
		      saved_errno = errno;
		      goto fail;
		    }
		  statflag = true;
		}
	      else
		{
		  /* Not following symlinks, so the attempt to
		   * chdir() into a symlink should be prevented.
		   */
		  rv = SafeChdirFailSymlink;
		  rv_set = true;
		  saved_errno = 0;	/* silence the error message */
		  goto fail;
		}
	    }
#endif
#ifdef S_ISDIR
	  /* Although the immediately following chdir() would detect
	   * the fact that this is not a directory for us, this would
	   * result in an extra system call that fails.  Anybody
	   * examining the system-call trace should ideally not be
	   * concerned that something is actually failing.
	   */
	  if (!S_ISDIR(statbuf_dest->st_mode))
	    {
	      rv = SafeChdirFailNotDir;
	      rv_set = true;
	      saved_errno = 0;	/* silence the error message */
	      goto fail;
	    }
#endif

	  if (options.debug_options & DebugSearch)
	    fprintf (stderr, "safely_chdir(): chdir(\"%s\")\n", dest);

	  if (0 == chdir (dest))
	    {
	      /* check we ended up where we wanted to go */
	      bool changed = false;
	      if (!wd_sanity_check (".", program_name, ".",
				    statbuf_dest->st_dev,
				    statbuf_dest->st_ino,
				    &statbuf_arrived,
				    0, __LINE__, direction,
				    isfatal,
				    &changed))
		{
		  /* Only allow one failure. */
		  if (RETRY_IF_SANITY_CHECK_FAILS == isfatal)
		    {
		      if (0 == fchdir (dotfd))
			{
			  isfatal = FATAL_IF_SANITY_CHECK_FAILS;
			  goto retry;
			}
		      else
			{
			  /* Failed to return to original directory,
			   * but we know that the current working
			   * directory is not the one that we intend
			   * to be in.  Since fchdir() failed, we
			   * can't recover from this and so this error
			   * is fatal.
			   */
			  error (EXIT_FAILURE, errno,
				 _("failed to return to parent directory"));
			}
		    }
		  else
		    {
		      /* XXX: not sure what to use as an excuse here. */
		      rv = SafeChdirFailNonexistent;
		      rv_set = true;
		      saved_errno = 0;
		      goto fail;
		    }
		}

	      close (dotfd);
	      return SafeChdirOK;
	    }
	  else
	    {
	      saved_errno = errno;
	      if (ENOENT == saved_errno)
		{
		  rv = SafeChdirFailNonexistent;
		  rv_set = true;
		  if (options.ignore_readdir_race)
		    errno = 0;	/* don't issue err msg */
		}
	      else if (ENOTDIR == saved_errno)
		{
		  /* This can happen if the we stat a directory,
		   * and then file system activity changes it into
		   * a non-directory.
		   */
		  saved_errno = 0;	/* don't issue err msg */
		  rv = SafeChdirFailNotDir;
		  rv_set = true;
		}
	      else
		{
		  rv = SafeChdirFailChdirFailed;
		  rv_set = true;
		}
	      goto fail;
	    }
	}
      else
	{
	  saved_errno = errno;
	  rv = SafeChdirFailStat;
	  rv_set = true;

	  if ( (ENOENT == saved_errno) || (0 == state.curdepth))
	    saved_errno = 0;	/* don't issue err msg */
	  goto fail;
	}
    }
  else
    {
      /* We do not have read permissions on "." */
      rv = SafeChdirFailWouldBeUnableToReturn;
      rv_set = true;
      goto fail;
    }

  /* This is the success path, so we clear errno.  The caller probably
   * won't be calling error() anyway.
   */
  saved_errno = 0;

  /* We use the same exit path for success or failure.
   * which has occurred is recorded in RV.
   */
 fail:
  /* We do not call error() as this would result in a duplicate error
   * message when the caller does the same thing.
   */
  if (saved_errno)
    errno = saved_errno;

  if (dotfd >= 0)
    {
      close (dotfd);
      dotfd = -1;
    }

  *did_stat = statflag;
  assert (rv_set);
  return rv;
}
Exemplo n.º 6
0
/* Examine the results of the stat() of a directory from before we
 * entered or left it, with the results of stat()ing it afterward.  If
 * these are different, the file system tree has been modified while we
 * were traversing it.  That might be an attempt to use a race
 * condition to persuade find to do something it didn't intend
 * (e.g. an attempt by an ordinary user to exploit the fact that root
 * sometimes runs find on the whole file system).  However, this can
 * also happen if automount is running (certainly on Solaris).  With
 * automount, moving into a directory can cause a file system to be
 * mounted there.
 *
 * To cope sensibly with this, we will raise an error if we see the
 * device number change unless we are chdir()ing into a subdirectory,
 * and the directory we moved into has been mounted or unmounted "recently".
 * Here "recently" means since we started "find" or we last re-read
 * the /etc/mnttab file.
 *
 * If the device number does not change but the inode does, that is a
 * problem.
 *
 * If the device number and inode are both the same, we are happy.
 *
 * If a file system is (un)mounted as we chdir() into the directory, that
 * may mean that we're now examining a section of the file system that might
 * have been excluded from consideration (via -prune or -quit for example).
 * Hence we print a warning message to indicate that the output of find
 * might be inconsistent due to the change in the file system.
 */
static bool
wd_sanity_check (const char *thing_to_stat,
		const char *progname,
		const char *what,
		dev_t old_dev,
		ino_t old_ino,
		struct stat *newinfo,
		int parent,
		int line_no,
		enum TraversalDirection direction,
		enum WdSanityCheckFatality isfatal,
		bool *changed) /* output parameter */
{
  const char *fstype;
  char *specific_what = NULL;
  int silent = 0;
  const char *current_dir = ".";

  *changed = false;

  set_stat_placeholders (newinfo);
  if ((*options.xstat) (current_dir, newinfo) != 0)
    fatal_target_file_error (errno, thing_to_stat);

  if (old_dev != newinfo->st_dev)
    {
      *changed = true;
      specific_what = specific_dirname (what);
      fstype = filesystem_type (newinfo, current_dir);
      silent = fs_likely_to_be_automounted (fstype);

      /* This condition is rare, so once we are here it is
       * reasonable to perform an expensive computation to
       * determine if we should continue or fail.
       */
      if (TraversingDown == direction)
	{
#ifdef STAT_MOUNTPOINTS
	  isfatal = dirchange_is_fatal (specific_what,isfatal,silent,newinfo);
#else
	  (void) silent;
	  isfatal = RETRY_IF_SANITY_CHECK_FAILS;
#endif
	}

      switch (isfatal)
	{
	case FATAL_IF_SANITY_CHECK_FAILS:
	  {
	    fstype = filesystem_type (newinfo, current_dir);
	    error (EXIT_FAILURE, 0,
		   _("%s%s changed during execution of %s (old device number %ld, new device number %ld, file system type is %s) [ref %ld]"),
		   safely_quote_err_filename (0, specific_what),
		   parent ? "/.." : "",
		   safely_quote_err_filename (1, progname),
		   (long) old_dev,
		   (long) newinfo->st_dev,
		   fstype,
		   (long)line_no);
	    /*NOTREACHED*/
	    return false;
	  }

	case NON_FATAL_IF_SANITY_CHECK_FAILS:
	  {
	    /* Since the device has changed under us, the inode number
	     * will almost certainly also be different. However, we have
	     * already decided that this is not a problem.  Hence we return
	     * without checking the inode number.
	     */
	    free (specific_what);
	    return true;
	  }

	case RETRY_IF_SANITY_CHECK_FAILS:
	  return false;
	}
    }

  /* Device number was the same, check if the inode has changed. */
  if (old_ino != newinfo->st_ino)
    {
      *changed = true;
      specific_what = specific_dirname (what);
      fstype = filesystem_type (newinfo, current_dir);

      error ((isfatal == FATAL_IF_SANITY_CHECK_FAILS) ? 1 : 0,
	     0,			/* no relevant errno value */
	     _("%s%s changed during execution of %s "
	       "(old inode number %" PRIuMAX ", new inode number %" PRIuMAX
	       ", file system type is %s) [ref %ld]"),
	     safely_quote_err_filename (0, specific_what),
	     parent ? "/.." : "",
	     safely_quote_err_filename (1, progname),
	     (uintmax_t) old_ino,
	     (uintmax_t) newinfo->st_ino,
	     fstype,
	     (long)line_no);
      free (specific_what);
      return false;
    }

  return true;
}
Exemplo n.º 7
0
/* CAUTION: this is the entry point for the oldfind executable, which is not the binary that
 * will actually get installed.   See ftsfind.c. */
int
main (int argc, char **argv)
{
  int i;
  int end_of_leading_options = 0; /* First arg after any -H/-L etc. */
  struct predicate *eval_tree;

  if (argv[0])
    set_program_name (argv[0]);
  else
    set_program_name ("find");

  state.exit_status = 0;

  if (fd_leak_check_is_enabled ())
    {
      remember_non_cloexec_fds ();
    }

  record_initial_cwd ();

  state.already_issued_stat_error_msg = false;
  state.shared_files = sharefile_init ("w");
  if (NULL == state.shared_files)
    {
      error (EXIT_FAILURE, errno,
	     _("Failed to initialize shared-file hash table"));
    }

  /* Set the option defaults before we do the locale
   * initialisation as check_nofollow () needs to be executed in the
   * POSIX locale.
   */
  set_option_defaults (&options);

#ifdef HAVE_SETLOCALE
  setlocale (LC_ALL, "");
#endif
  bindtextdomain (PACKAGE, LOCALEDIR);
  textdomain (PACKAGE);
  if (atexit (close_stdin))
    {
      error (EXIT_FAILURE, errno, _("The atexit library function failed"));
    }

  /* Check for -P, -H or -L options. */
  end_of_leading_options = process_leading_options (argc, argv);

  if (options.debug_options & DebugStat)
    options.xstat = debug_stat;

#ifdef DEBUG
  fprintf (stderr, "cur_day_start = %s", ctime (&options.cur_day_start));
#endif /* DEBUG */

  /* state.cwd_dir_fd has to be initialized before we call build_expression_tree ()
   * because command-line parsing may lead us to stat some files.
   */
  state.cwd_dir_fd = AT_FDCWD;

  /* We are now processing the part of the "find" command line
   * after the -H/-L options (if any).
   */
  eval_tree = build_expression_tree (argc, argv, end_of_leading_options);


  /* safely_chdir () needs to check that it has ended up in the right place.
   * To avoid bailing out when something gets automounted, it checks if
   * the target directory appears to have had a directory mounted on it as
   * we chdir ()ed.  The problem with this is that in order to notice that
   * a file system was mounted, we would need to lstat () all the mount points.
   * That strategy loses if our machine is a client of a dead NFS server.
   *
   * Hence if safely_chdir () and wd_sanity_check () can manage without needing
   * to know the mounted device list, we do that.
   */
  if (!options.open_nofollow_available)
    {
#ifdef STAT_MOUNTPOINTS
      init_mounted_dev_list (0);
#endif
    }


  set_stat_placeholders (&starting_stat_buf);
  if ((*options.xstat) (".", &starting_stat_buf) != 0)
    error (EXIT_FAILURE, errno, _("cannot stat current directory"));

  /* If no paths are given, default to ".".  */
  for (i = end_of_leading_options; i < argc && !looks_like_expression (argv[i], true); i++)
    {
      process_top_path (argv[i], 0, starting_stat_buf.st_ino);
    }

  /* If there were no path arguments, default to ".". */
  if (i == end_of_leading_options)
    {
      /*
       * We use a temporary variable here because some actions modify
       * the path temporarily.  Hence if we use a string constant,
       * we get a coredump.  The best example of this is if we say
       * "find -printf %H" (note, not "find . -printf %H").
       */
      char defaultpath[2] = ".";
      process_top_path (defaultpath, 0, starting_stat_buf.st_ino);
    }

  /* If "-exec ... {} +" has been used, there may be some
   * partially-full command lines which have been built,
   * but which are not yet complete.   Execute those now.
   */
  show_success_rates (eval_tree);
  cleanup ();
  return state.exit_status;
}
Exemplo n.º 8
0
static void
process_dir (char *pathname, char *name, int pathlen, const struct stat *statp, char *parent)
{
  int subdirs_left;		/* Number of unexamined subdirs in PATHNAME. */
  bool subdirs_unreliable;	/* if true, cannot use dir link count as subdir limif (if false, it may STILL be unreliable) */
  struct stat stat_buf;
  size_t dircount = 0u;
  DIR *dirp;

  if (statp->st_nlink < 2)
    {
      subdirs_unreliable = true;
      subdirs_left = 0;
    }
  else
    {
      subdirs_unreliable = false; /* not necessarily right */
      subdirs_left = statp->st_nlink - 2; /* Account for name and ".". */
    }

  errno = 0;
  dirp = opendir_safer (name);

  if (dirp == NULL)
    {
      assert (errno != 0);
      error (0, errno, "%s", safely_quote_err_filename (0, pathname));
      state.exit_status = 1;
    }
  else
    {
      char *cur_path;		/* Full path of each file to process. */
      char *cur_name;		/* Base name of each file to process. */
      unsigned cur_path_size;	/* Bytes allocated for `cur_path'. */
      register unsigned file_len; /* Length of each path to process. */
      register unsigned pathname_len; /* PATHLEN plus trailing '/'. */
      bool did_stat = false;

      if (pathname[pathlen - 1] == '/')
	pathname_len = pathlen + 1; /* For '\0'; already have '/'. */
      else
	pathname_len = pathlen + 2; /* For '/' and '\0'. */
      cur_path_size = 0;
      cur_path = NULL;

      /* We're about to leave the directory.  If there are any
       * -execdir argument lists which have been built but have not
       * yet been processed, do them now because they must be done in
       * the same directory.
       */
      complete_pending_execdirs ();

      if (strcmp (name, "."))
	{
	  enum SafeChdirStatus status = safely_chdir (name, TraversingDown, &stat_buf, SymlinkHandleDefault, &did_stat);
	  switch (status)
	    {
	    case SafeChdirOK:
	      /* If there had been a change but wd_sanity_check()
	       * accepted it, we need to accept that on the
	       * way back up as well, so modify our record
	       * of what we think we should see later.
	       * If there was no change, the assignments are a no-op.
	       *
	       * However, before performing the assignment, we need to
	       * check that we have the stat information.   If O_NOFOLLOW
	       * is available, safely_chdir() will not have needed to use
	       * stat(), and so stat_buf will just contain random data.
	       */
	      if (!did_stat)
		{
		  /* If there is a link we need to follow it.  Hence
		   * the direct call to stat() not through (options.xstat)
		   */
		  set_stat_placeholders (&stat_buf);
		  if (0 != stat (".", &stat_buf))
		    break;	/* skip the assignment. */
		}
	      dir_ids[dir_curr].dev = stat_buf.st_dev;
	      dir_ids[dir_curr].ino = stat_buf.st_ino;

	      break;

	    case SafeChdirFailWouldBeUnableToReturn:
	      error (0, errno, ".");
	      state.exit_status = 1;
	      break;

	    case SafeChdirFailNonexistent:
	    case SafeChdirFailDestUnreadable:
	    case SafeChdirFailStat:
	    case SafeChdirFailNotDir:
	    case SafeChdirFailChdirFailed:
	      error (0, errno, "%s",
		     safely_quote_err_filename (0, pathname));
	      state.exit_status = 1;
	      return;

	    case SafeChdirFailSymlink:
	      error (0, 0,
		     _("warning: not following the symbolic link %s"),
		     safely_quote_err_filename (0, pathname));
	      state.exit_status = 1;
	      return;
	    }
	}

      while (1)
	{
	  const char *namep;
	  mode_t mode = 0;
	  const struct dirent *dp;

	  /* We reset errno here to distinguish between end-of-directory and an error */
	  errno = 0;
	  dp = readdir (dirp);
	  if (NULL == dp)
	    {
	      if (errno)
		{
		  /* an error occurred, but we are not yet at the end
		     of the directory stream. */
		  error (0, errno, "%s", safely_quote_err_filename (0, pathname));
		  continue;
		}
	      else
		{
		  break;	/* End of the directory stream. */
		}
	    }
	  else
	    {
	      namep = dp->d_name;
	      /* Skip "", ".", and "..".  "" is returned by at least one buggy
		 implementation: Solaris 2.4 readdir on NFS file systems.  */
	      if (!namep[0] ||
                  (namep[0] == '.' && (namep[1] == 0 ||
                                       (namep[1] == '.' && namep[2] == 0))))
		continue;
	    }

#if defined HAVE_STRUCT_DIRENT_D_TYPE
	  if (dp->d_type != DT_UNKNOWN)
	    mode = type_to_mode (dp->d_type);
#endif

	  /* Append this directory entry's name to the path being searched. */
	  file_len = pathname_len + strlen (namep);
	  if (file_len > cur_path_size)
	    {
	      while (file_len > cur_path_size)
		cur_path_size += 1024;
	      free (cur_path);
	      cur_path = xmalloc (cur_path_size);
	      strcpy (cur_path, pathname);
	      cur_path[pathname_len - 2] = '/';
	    }
	  cur_name = cur_path + pathname_len - 1;
	  strcpy (cur_name, namep);

	  state.curdepth++;
	  if (!options.no_leaf_check && !subdirs_unreliable)
	    {
	      if (mode && S_ISDIR(mode) && (subdirs_left == 0))
		{
		  /* This is a subdirectory, but the number of directories we
		   * have found now exceeds the number we would expect given
		   * the hard link count on the parent.   This is likely to be
		   * a bug in the file system driver (e.g. Linux's
		   * /proc file system) or may just be a fact that the OS
		   * doesn't really handle hard links with Unix semantics.
		   * In the latter case, -noleaf should be used routinely.
		   */
		  error (0, 0, _("WARNING: Hard link count is wrong for %s (saw only st_nlink=%" PRIuMAX  " but we already saw %" PRIuMAX " subdirectories): this may be a bug in your file system driver.  Automatically turning on find's -noleaf option.  Earlier results may have failed to include directories that should have been searched."),
			 safely_quote_err_filename(0, pathname),
			 (uintmax_t) statp->st_nlink,
			 (uintmax_t) dircount);
		  state.exit_status = 1; /* We know the result is wrong, now */
		  options.no_leaf_check = true;	/* Don't make same
						   mistake again */
		  subdirs_unreliable = 1;
		  subdirs_left = 1; /* band-aid for this iteration. */
		}

	      /* Normal case optimization.  On normal Unix
		 file systems, a directory that has no subdirectories
		 has two links: its name, and ".".  Any additional
		 links are to the ".." entries of its subdirectories.
		 Once we have processed as many subdirectories as
		 there are additional links, we know that the rest of
		 the entries are non-directories -- in other words,
		 leaf files. */
	      {
		int count;
		count = process_path (cur_path, cur_name,
				      subdirs_left == 0, pathname,
				      mode, D_INO(dp));
		subdirs_left -= count;
		dircount += count;
	      }
	    }
	  else
	    {
	      /* There might be weird (e.g., CD-ROM or MS-DOS) file systems
		 mounted, which don't have Unix-like directory link counts. */
	      process_path (cur_path, cur_name, false, pathname, mode,
			    D_INO(dp));
	    }

	  state.curdepth--;
	}


      /* We're about to leave the directory.  If there are any
       * -execdir argument lists which have been built but have not
       * yet been processed, do them now because they must be done in
       * the same directory.
       */
      complete_pending_execdirs ();

      if (strcmp (name, "."))
	{
	  enum SafeChdirStatus status;

	  /* We could go back and do the next command-line arg
	     instead, maybe using longjmp.  */
	  char const *dir;
	  bool deref = following_links () ? true : false;

	  if ( (state.curdepth>0) && !deref)
	    dir = "..";
	  else
	    {
	      chdir_back ();
	      dir = parent;
	    }

	  did_stat = false;
	  status = safely_chdir (dir, TraversingUp, &stat_buf, SymlinkHandleDefault, &did_stat);
	  switch (status)
	    {
	    case SafeChdirOK:
	      break;

	    case SafeChdirFailWouldBeUnableToReturn:
	      error (EXIT_FAILURE, errno, ".");
	      return;

	    case SafeChdirFailNonexistent:
	    case SafeChdirFailDestUnreadable:
	    case SafeChdirFailStat:
	    case SafeChdirFailSymlink:
	    case SafeChdirFailNotDir:
	    case SafeChdirFailChdirFailed:
	      error (EXIT_FAILURE, errno,
		     "%s", safely_quote_err_filename (0, pathname));
	      return;
	    }
	}

      free (cur_path);
      CLOSEDIR (dirp);
    }

  if (subdirs_unreliable)
    {
      /* Make sure we hasn't used the variable subdirs_left if we knew
       * we shouldn't do so.
       */
      assert (0 == subdirs_left || options.no_leaf_check);
    }
}