Example #1
0
DIR *
opendir_safer (char const *name)
{
  DIR *dp = opendir (name);

  if (dp)
    {
      int fd = dirfd (dp);

      if (0 <= fd && fd <= STDERR_FILENO)
        {
          /* If fdopendir is native (as on Linux), then it is safe to
             assume dirfd(fdopendir(n))==n.  If we are using the
             gnulib module fdopendir, then this guarantee is not met,
             but fdopendir recursively calls opendir_safer up to 3
             times to at least get a safe fd.  If fdopendir is not
             present but dirfd is accurate (as on cygwin 1.5.x), then
             we recurse up to 3 times ourselves.  Finally, if dirfd
             always fails (as on mingw), then we are already safe.  */
          DIR *newdp;
          int e;
#if HAVE_FDOPENDIR || GNULIB_FDOPENDIR
          int f = dup_safer (fd);
          if (f < 0)
            {
              e = errno;
              newdp = NULL;
            }
          else
            {
              newdp = fdopendir (f);
              e = errno;
              if (! newdp)
                close (f);
            }
#else /* !FDOPENDIR */
          newdp = opendir_safer (name);
          e = errno;
#endif
          closedir (dp);
          errno = e;
          dp = newdp;
        }
    }

  return dp;
}
Example #2
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);
    }
}