Re: EMC Celerra: opendir bug? [Re: in ls.c, should check errno after readdir

Jim Meyering <[email protected]>
Newsgroups gmane.comp.gnu.fileutils.bugs
Message-ID <[email protected]>
Ok.  The Solaris5.8 man page for readdir suggests the same thing,
so I've changed remove.c and ls.c to check for readdir failures:

	* src/remove.c (remove_cwd_entries): Use closedir (not CLOSEDIR)
	when ignoring any return value.

	* src/remove.c (remove_cwd_entries): Detect and diagnose readdir
	failures.  On some systems (at least EMC Celerra and Solaris5.8),
	this appears to be necessary.
	(is_empty_dir): Likewise.  Also, always close directory handle.
	* src/ls.c (print_dir): Likewise.
	(print_dir): Rename local variable: reading -> dirp.
	Reported by Mike Coleman.

There remain three files in lib/ that need similar changes.

Thanks again for the report and follow-up.

Index: ls.c
===================================================================
RCS file: /fetish/cu/src/ls.c,v
retrieving revision 1.304
retrieving revision 1.306
diff -u -p -u -r1.304 -r1.306
--- ls.c	8 Jul 2002 09:12:48 -0000	1.304
+++ ls.c	29 Aug 2002 10:16:00 -0000	1.306
@@ -2030,14 +2030,14 @@ queue_directory (const char *name, const
 static void
 print_dir (const char *name, const char *realname)
 {
-  register DIR *reading;
+  register DIR *dirp;
   register struct dirent *next;
   register uintmax_t total_blocks = 0;
   static int first = 1;
 
   errno = 0;
-  reading = opendir (name);
-  if (!reading)
+  dirp = opendir (name);
+  if (!dirp)
     {
       error (0, errno, "%s", quotearg_colon (name));
       exit_status = 1;
@@ -2047,7 +2047,7 @@ print_dir (const char *name, const char 
   if (LOOP_DETECT)
     {
       struct stat dir_stat;
-      int fd = dirfd (reading);
+      int fd = dirfd (dirp);
 
       /* If dirfd failed, endure the overhead of using stat.  */
       if ((0 <= fd
@@ -2077,23 +2077,43 @@ print_dir (const char *name, const char 
 
   clear_files ();
 
-  while ((next = readdir (reading)) != NULL)
-    if (file_interesting (next))
-      {
-	enum filetype type = unknown;
+  while (1)
+    {
+      /* Set errno to zero so we can distinguish between a readdir failure
+	 and when readdir simply finds that there are no more entries.  */
+      errno = 0;
+      if ((next = readdir (dirp)) == NULL)
+	{
+	  if (errno)
+	    {
+	      /* Save/restore errno across closedir call.  */
+	      int e = errno;
+	      closedir (dirp);
+	      errno = e;
+
+	      /* Arrange to give a diagnostic after exiting this loop.  */
+	      dirp = NULL;
+	    }
+	  break;
+	}
+
+      if (file_interesting (next))
+	{
+	  enum filetype type = unknown;
 
 #if HAVE_STRUCT_DIRENT_D_TYPE
-	if (next->d_type == DT_DIR || next->d_type == DT_CHR
-	    || next->d_type == DT_BLK || next->d_type == DT_SOCK
-	    || next->d_type == DT_FIFO)
-	  type = next->d_type;
+	  if (next->d_type == DT_DIR || next->d_type == DT_CHR
+	      || next->d_type == DT_BLK || next->d_type == DT_SOCK
+	      || next->d_type == DT_FIFO)
+	    type = next->d_type;
 #endif
-	total_blocks += gobble_file (next->d_name, type, 0, name);
-      }
+	  total_blocks += gobble_file (next->d_name, type, 0, name);
+	}
+    }
 
-  if (CLOSEDIR (reading))
+  if (dirp == NULL || CLOSEDIR (dirp))
     {
-      error (0, errno, "%s", quotearg_colon (name));
+      error (0, errno, _("reading directory %s"), quotearg_colon (name));
       exit_status = 1;
       /* Don't return; print whatever we got. */
     }

Index: remove.c
===================================================================
RCS file: /fetish/cu/src/remove.c,v
retrieving revision 1.59
retrieving revision 1.63
diff -u -p -u -r1.59 -r1.63
--- remove.c	22 Jul 2002 06:59:26 -0000	1.59
+++ remove.c	29 Aug 2002 10:42:12 -0000	1.63
@@ -522,19 +522,30 @@ is_empty_dir (char const *dir)
 {
   DIR *dirp = opendir (dir);
   if (dirp == NULL)
-    return false;
+    {
+      closedir (dirp);
+      return false;
+    }
 
   while (1)
     {
-      struct dirent *dp = readdir (dirp);
+      struct dirent *dp;
       const char *f;
 
+      errno = 0;
+      dp = readdir (dirp);
       if (dp == NULL)
-	return true;
+	{
+	  closedir (dirp);
+	  return errno == 0 ? true : false;
+	}
 
       f = dp->d_name;
       if ( ! DOT_OR_DOTDOT (f))
-	return false;
+	{
+	  closedir (dirp);
+	  return false;
+	}
     }
 }
 
@@ -810,12 +821,27 @@ remove_cwd_entries (char **subdir, struc
 
   while (1)
     {
-      struct dirent *dp = readdir (dirp);
+      struct dirent *dp;
       enum RM_status tmp_status;
       const char *f;
 
-      if (dp == NULL)
-	break;
+      /* Set errno to zero so we can distinguish between a readdir failure
+	 and when readdir simply finds that there are no more entries.  */
+      errno = 0;
+      if ((dp = readdir (dirp)) == NULL)
+	{
+	  if (errno)
+	    {
+	      /* Save/restore errno across closedir call.  */
+	      int e = errno;
+	      closedir (dirp);
+	      errno = e;
+
+	      /* Arrange to give a diagnostic after exiting this loop.  */
+	      dirp = NULL;
+	    }
+	  break;
+	}
 
       f = dp->d_name;
       if (DOT_OR_DOTDOT (f))
@@ -871,7 +897,13 @@ remove_cwd_entries (char **subdir, struc
 	break;
     }
 
-  closedir (dirp);
+  if (dirp == NULL || CLOSEDIR (dirp) != 0)
+    {
+      /* Note that this diagnostic serves for both readdir
+	 and closedir failures.  */
+      error (0, errno, _("reading directory %s"), quote (full_filename (".")));
+      status = RM_ERROR;
+    }
 
   return status;
 }
lmpx.com only provides a reader for public news (NNTP) servers. It is not affiliated with the servers or forums shown here and is not responsible for the content of articles, which is written by their respective authors.