Index: trunk/src/sh_suidchk.c
===================================================================
--- trunk/src/sh_suidchk.c	(revision 119)
+++ trunk/src/sh_suidchk.c	(revision 131)
@@ -71,4 +71,5 @@
 
 #include "samhain.h"
+#include "sh_pthread.h"
 #include "sh_utils.h"
 #include "sh_error.h"
@@ -842,4 +843,6 @@
   char            fileHash[2*(KEY_LEN + 1)];
 
+  struct sh_dirent * dirlist = NULL;
+  struct sh_dirent * dirlist_orig = NULL;
 
   SL_ENTER(_("sh_suidchk_check_internal"));
@@ -870,4 +873,6 @@
   /* Loop over directory entries
    */
+  SH_MUTEX_LOCK(readdir_lock);
+
   do {
 
@@ -886,236 +891,255 @@
 	continue;
 
-      tmpcat = SH_ALLOC(PATH_MAX);
-      (void) sl_strlcpy(tmpcat, iname, PATH_MAX);
-
-      if ((sl_strlen(tmpcat) != sl_strlen(iname)) || (tmpcat[0] == '\0'))
-	{
-	  sl_status = SL_ETRUNC;
-	}
-      else
-	{
-	  if (tmpcat[1] != '\0') 
-	    sl_status = sl_strlcat(tmpcat, "/",                 PATH_MAX);
-	}
-
-      if (! SL_ISERROR(sl_status))
-	sl_status = sl_strlcat(tmpcat, thisEntry->d_name,   PATH_MAX);
-
-      if (SL_ISERROR(sl_status))
-	{
-	  tmp = sh_util_safe_name(tmpcat);
-	  sh_error_handle ((-1), FIL__, __LINE__, (int) sl_status, 
-			   MSG_E_SUBGPATH,
-			   _("path too long"),
-			   _("sh_suidchk_check_internal"), tmp );
-	  SH_FREE(tmp);
-	  continue;
-	}
-
-      ++FileLimNum;
-      ++FileLimTotal;
-
-      /* Rate limit (Fps == Files per second)
-       */
-      if ((ShSuidchkFps > 0 && FileLimNum > ShSuidchkFps && FileLimTotal > 0)&&
-	  (ShSuidchkYield == S_FALSE))
-	{
-	  FileLimNum  = 0;
-	  FileLimNow  = time(NULL);
- 
-	  if ( (FileLimNow  - FileLimStart) > 0 && 
-	       FileLimTotal/(FileLimNow  - FileLimStart) > ShSuidchkFps )
-	    (void) retry_msleep((int)((FileLimTotal/(FileLimNow-FileLimStart))/
-		   ShSuidchkFps) , 0);
-	}
+      dirlist = addto_sh_dirlist (thisEntry, dirlist);
+    }
+
+  } while (thisEntry != NULL);
+
+  SH_MUTEX_UNLOCK(readdir_lock);
+
+  closedir(thisDir);
+
+  dirlist_orig = dirlist;
+
+  do {
+
+    /* If the directory is empty, dirlist = NULL
+     */
+    if (!dirlist)
+      break;
+
+    tmpcat = SH_ALLOC(PATH_MAX);
+    (void) sl_strlcpy(tmpcat, iname, PATH_MAX);
+    
+    if ((sl_strlen(tmpcat) != sl_strlen(iname)) || (tmpcat[0] == '\0'))
+      {
+	sl_status = SL_ETRUNC;
+      }
+    else
+      {
+	if (tmpcat[1] != '\0') 
+	  sl_status = sl_strlcat(tmpcat, "/",                 PATH_MAX);
+      }
+
+    if (! SL_ISERROR(sl_status))
+      sl_status = sl_strlcat(tmpcat, dirlist->sh_d_name,   PATH_MAX);
+
+    if (SL_ISERROR(sl_status))
+      {
+	tmp = sh_util_safe_name(tmpcat);
+	sh_error_handle ((-1), FIL__, __LINE__, (int) sl_status, 
+			 MSG_E_SUBGPATH,
+			 _("path too long"),
+			 _("sh_suidchk_check_internal"), tmp );
+	SH_FREE(tmp);
+	dirlist = dirlist->next;
+	continue;
+      }
+
+    ++FileLimNum;
+    ++FileLimTotal;
+
+    /* Rate limit (Fps == Files per second)
+     */
+    if ((ShSuidchkFps > 0 && FileLimNum > ShSuidchkFps && FileLimTotal > 0)&&
+	(ShSuidchkYield == S_FALSE))
+      {
+	FileLimNum  = 0;
+	FileLimNow  = time(NULL);
+	
+	if ( (FileLimNow  - FileLimStart) > 0 && 
+	     FileLimTotal/(FileLimNow  - FileLimStart) > ShSuidchkFps )
+	  (void) retry_msleep((int)((FileLimTotal/(FileLimNow-FileLimStart))/
+				    ShSuidchkFps) , 0);
+      }
 	      
-      status = (int) retry_lstat(FIL__, __LINE__, tmpcat, &buf);
-
-      if (status != 0)
-	{
-	  status = errno;
-	  tmp = sh_util_safe_name(tmpcat);
-	  sh_error_handle (SH_ERR_ERR, FIL__, __LINE__, status, MSG_ERR_LSTAT,
-			   sh_error_message(status),
-			   tmpcat );
-	  SH_FREE(tmp);
-	}
-      else
-	{
-	  if (/*@-usedef@*/S_ISDIR(buf.st_mode)/*@+usedef@*/ &&
-	      (ShSuidchkExclude == NULL || 
-	       0 != strcmp(tmpcat, ShSuidchkExclude)))
-	    {
-	      /* fs is a STATIC string or NULL
-	       */
-	      fs = filesystem_type (tmpcat, tmpcat, &buf);
-	      if (fs != NULL 
+    status = (int) retry_lstat(FIL__, __LINE__, tmpcat, &buf);
+
+    if (status != 0)
+      {
+	status = errno;
+	tmp = sh_util_safe_name(tmpcat);
+	sh_error_handle (SH_ERR_ERR, FIL__, __LINE__, status, MSG_ERR_LSTAT,
+			 sh_error_message(status),
+			 tmpcat );
+	SH_FREE(tmp);
+      }
+    else
+      {
+	if (/*@-usedef@*/S_ISDIR(buf.st_mode)/*@+usedef@*/ &&
+	    (ShSuidchkExclude == NULL || 
+	     0 != strcmp(tmpcat, ShSuidchkExclude)))
+	  {
+	    /* fs is a STATIC string or NULL
+	     */
+	    fs = filesystem_type (tmpcat, tmpcat, &buf);
+	    if (fs != NULL 
 #ifndef SH_SUIDTESTDIR
-		  && 
-		  0 != strncmp (_("afs"),     fs, 3) && 
-		  0 != strncmp (_("devfs"),   fs, 5) &&
-		  0 != strncmp (_("iso9660"), fs, 7) &&
-		  0 != strncmp (_("lustre"),  fs, 6) &&
-		  0 != strncmp (_("mmfs"),    fs, 4) && 
-		  0 != strncmp (_("msdos"),   fs, 5) &&
-		  0 != strncmp (_("nfs"),     fs, 3) &&
-		  0 != strncmp (_("proc"),    fs, 4) &&
-		  0 != strncmp (_("vfat"),    fs, 4)
+		&& 
+		0 != strncmp (_("afs"),     fs, 3) && 
+		0 != strncmp (_("devfs"),   fs, 5) &&
+		0 != strncmp (_("iso9660"), fs, 7) &&
+		0 != strncmp (_("lustre"),  fs, 6) &&
+		0 != strncmp (_("mmfs"),    fs, 4) && 
+		0 != strncmp (_("msdos"),   fs, 5) &&
+		0 != strncmp (_("nfs"),     fs, 3) &&
+		0 != strncmp (_("proc"),    fs, 4) &&
+		0 != strncmp (_("vfat"),    fs, 4)
 #endif 
+		)
+	      {
+		if ((ShSuidchkNosuid == S_TRUE) || 
+		    (0 != strncmp (_("nosuid"),  fs, 6)))
+		  /* fprintf(stderr, "%s: %s\n", fs, tmpcat); */
+		  (void) sh_suidchk_check_internal(tmpcat);
+	      }
+	  }
+	else if (S_ISREG(buf.st_mode) &&
+		 (0 !=(S_ISUID & buf.st_mode) ||
+#if defined(HOST_IS_LINUX)
+		  (0 !=(S_ISGID & buf.st_mode) && 
+		   0 !=(S_IXGRP & buf.st_mode)) 
+#else  
+		  0 !=(S_ISGID & buf.st_mode)
+#endif
 		  )
-		{
-		  if ((ShSuidchkNosuid == S_TRUE) || 
-		      (0 != strncmp (_("nosuid"),  fs, 6)))
-		    /* fprintf(stderr, "%s: %s\n", fs, tmpcat); */
-		    (void) sh_suidchk_check_internal(tmpcat);
-		}
-	    }
-	  else if (S_ISREG(buf.st_mode) &&
-		   (0 !=(S_ISUID & buf.st_mode) ||
-#if defined(HOST_IS_LINUX)
-		    (0 !=(S_ISGID & buf.st_mode) && 
-		     0 !=(S_IXGRP & buf.st_mode)) 
-#else  
-		    0 !=(S_ISGID & buf.st_mode)
-#endif
-		    )
-		   )
-	    {
-	      
-	      (void) sl_strlcpy (theFile.fullpath, tmpcat, PATH_MAX);
-	      theFile.check_mask  = sh_files_maskof(SH_LEVEL_READONLY);
-	      CLEAR_SH_FFLAG_REPORTED(theFile.file_reported);
-	      theFile.attr_string = NULL;
-
-	      status = sh_unix_getinfo (ShDFLevel[SH_ERR_T_RO], 
-					thisEntry->d_name,
-					&theFile, fileHash, 0);
-
-	      tmp = sh_util_safe_name(tmpcat);
-
-	      if (status != 0)
-		{
-		  sh_error_handle (ShSuidchkSeverity, FIL__, __LINE__, 
-				   0, MSG_E_SUBGPATH,
-				   _("Could not check suid/sgid file"),
-				   _("sh_suidchk_check_internal"),
-				   tmp);
-		}
-	      else
-		{
-
-		  if ( sh.flag.update   == S_TRUE && 
-		      (sh.flag.checkSum == SH_CHECK_INIT  || 
-		       sh.flag.checkSum == SH_CHECK_CHECK))
-		    {
-		      /* Updating database. Report new files that
-		       * are not in database already. Then compare
-		       * to database and report changes.
-		       */
-		      if (-1 == sh_hash_have_it (tmpcat))
-			{
-			  sh_error_handle ((-1), FIL__, __LINE__, 
-					   0, MSG_SUID_FOUND, tmp );
-			}
-		      else
-			{
-			  sh_error_handle (SH_ERR_ALL, FIL__, __LINE__, 
-					   0, MSG_SUID_FOUND, tmp );
-			}
-
-		      if (0 == sh_hash_compdata (SH_LEVEL_READONLY, 
+		 )
+	  {
+	    
+	    (void) sl_strlcpy (theFile.fullpath, tmpcat, PATH_MAX);
+	    theFile.check_mask  = sh_files_maskof(SH_LEVEL_READONLY);
+	    CLEAR_SH_FFLAG_REPORTED(theFile.file_reported);
+	    theFile.attr_string = NULL;
+	    
+	    status = sh_unix_getinfo (ShDFLevel[SH_ERR_T_RO], 
+				      thisEntry->d_name,
+				      &theFile, fileHash, 0);
+	    
+	    tmp = sh_util_safe_name(tmpcat);
+	    
+	    if (status != 0)
+	      {
+		sh_error_handle (ShSuidchkSeverity, FIL__, __LINE__, 
+				 0, MSG_E_SUBGPATH,
+				 _("Could not check suid/sgid file"),
+				 _("sh_suidchk_check_internal"),
+				 tmp);
+	      }
+	    else
+	      {
+		
+		if ( sh.flag.update   == S_TRUE && 
+		     (sh.flag.checkSum == SH_CHECK_INIT  || 
+		      sh.flag.checkSum == SH_CHECK_CHECK))
+		  {
+		    /* Updating database. Report new files that
+		     * are not in database already. Then compare
+		     * to database and report changes.
+		     */
+		    if (-1 == sh_hash_have_it (tmpcat))
+		      {
+			sh_error_handle ((-1), FIL__, __LINE__, 
+					 0, MSG_SUID_FOUND, tmp );
+		      }
+		    else
+		      {
+			sh_error_handle (SH_ERR_ALL, FIL__, __LINE__, 
+					 0, MSG_SUID_FOUND, tmp );
+		      }
+		    
+		    if (0 == sh_hash_compdata (SH_LEVEL_READONLY, 
+					       &theFile, fileHash,
+					       _("[SuidCheck]"), 
+					       ShSuidchkSeverity))
+		      {
+			sh_hash_pushdata_memory (&theFile, fileHash);
+		      }
+		    
+		    sh_hash_addflag(tmpcat, SH_FFLAG_SUIDCHK);
+		    
+		  }
+		
+		else if (sh.flag.checkSum == SH_CHECK_INIT  && 
+			 sh.flag.update == S_FALSE )
+		  {
+		    /* Running init. Report on files detected.
+		     */
+		    sh_hash_pushdata (&theFile, fileHash);
+		    sh_error_handle ((-1), FIL__, __LINE__, 
+				     0, MSG_SUID_FOUND, tmp );
+		  }
+		
+		else if (sh.flag.checkSum == SH_CHECK_CHECK )
+		  {
+		    /* Running file check. Report on new files
+		     * detected, and quarantine them.
+		     */
+		    sh_error_handle (SH_ERR_ALL, FIL__, __LINE__, 
+				     0, MSG_SUID_FOUND, tmp );
+		    
+		    fflags = sh_hash_getflags(tmpcat);
+		    
+		    if ( (-1 == fflags) || (!SH_FFLAG_SUIDCHK_SET(fflags)))
+		      {
+			if (-1 == fflags)
+			  report_file(tmpcat, &theFile, timestrc, timestra, timestrm);
+			
+			/* Quarantine file according to configured method
+			 */
+			if (ShSuidchkQEnable == S_TRUE)
+			  {
+			    switch (ShSuidchkQMethod)
+			      {
+			      case SH_Q_DELETE:
+				sh_q_delete(theFile.fullpath);
+				break;
+			      case SH_Q_CHANGEPERM:
+				sh_q_changeperm(theFile.fullpath);
+				break;
+			      case SH_Q_MOVE:
+				sh_q_move(theFile.fullpath, &theFile, timestrc, timestra, timestrm);
+				break;
+			      default:
+				sh_error_handle (ShSuidchkSeverity, FIL__,
+						 __LINE__, 0, MSG_SUID_QREPORT,
+						 _("Bad quarantine method"), tmp);
+				break;
+			      }
+			  }
+			else
+			  {
+			    /* 1.8.1 push file to in-memory database
+			     */
+			    (void) sh_hash_compdata (SH_LEVEL_READONLY,
+						     &theFile, fileHash,
+						     _("[SuidCheck]"),
+						     ShSuidchkSeverity);
+			    
+			    sh_hash_addflag(tmpcat, SH_FFLAG_SUIDCHK);
+			    
+			  }
+		      }
+		    else
+		      {
+			/* File exists. Check for modifications.
+			 */
+			(void) sh_hash_compdata (SH_LEVEL_READONLY, 
 						 &theFile, fileHash,
-						 _("[SuidCheck]"), 
-						 ShSuidchkSeverity))
-			{
-			  sh_hash_pushdata_memory (&theFile, fileHash);
-			}
-		      
-		      sh_hash_addflag(tmpcat, SH_FFLAG_SUIDCHK);
-		      
-		    }
-
-		  else if (sh.flag.checkSum == SH_CHECK_INIT  && 
-			   sh.flag.update == S_FALSE )
-		    {
-		      /* Running init. Report on files detected.
-		       */
-		      sh_hash_pushdata (&theFile, fileHash);
-		      sh_error_handle ((-1), FIL__, __LINE__, 
-				       0, MSG_SUID_FOUND, tmp );
-		    }
-
-		  else if (sh.flag.checkSum == SH_CHECK_CHECK )
-		    {
-		      /* Running file check. Report on new files
-		       * detected, and quarantine them.
-		       */
-		      sh_error_handle (SH_ERR_ALL, FIL__, __LINE__, 
-				       0, MSG_SUID_FOUND, tmp );
-
-		      fflags = sh_hash_getflags(tmpcat);
-
-		      if ( (-1 == fflags) || (!SH_FFLAG_SUIDCHK_SET(fflags)))
-			{
-			  if (-1 == fflags)
-			    report_file(tmpcat, &theFile, timestrc, timestra, timestrm);
-
-			  /* Quarantine file according to configured method
-                          */
-			  if (ShSuidchkQEnable == S_TRUE)
-			    {
-			      switch (ShSuidchkQMethod)
-				{
-				  case SH_Q_DELETE:
-				    sh_q_delete(theFile.fullpath);
-				    break;
-				  case SH_Q_CHANGEPERM:
-				    sh_q_changeperm(theFile.fullpath);
-				    break;
-				  case SH_Q_MOVE:
-				    sh_q_move(theFile.fullpath, &theFile, timestrc, timestra, timestrm);
-				    break;
-				  default:
-				    sh_error_handle (ShSuidchkSeverity, FIL__,
-						     __LINE__, 0, MSG_SUID_QREPORT,
-						     _("Bad quarantine method"), tmp);
-				    break;
-				}
-			    }
-			  else
-			    {
-			      /* 1.8.1 push file to in-memory database
-			       */
-			      (void) sh_hash_compdata (SH_LEVEL_READONLY,
-						       &theFile, fileHash,
-						       _("[SuidCheck]"),
-						       ShSuidchkSeverity);
-
-			      sh_hash_addflag(tmpcat, SH_FFLAG_SUIDCHK);
-
-			    }
-			}
-		      else
-			{
-			  /* File exists. Check for modifications.
-			   */
-			  (void) sh_hash_compdata (SH_LEVEL_READONLY, 
-						   &theFile, fileHash,
-						   _("[SuidCheck]"),
-						   ShSuidchkSeverity);
-			  
-			  sh_hash_addflag(tmpcat, SH_FFLAG_SUIDCHK);
-
-			}
-		    }
-		}
-	      SH_FREE(tmp);
-	      if (theFile.attr_string)
-		SH_FREE(theFile.attr_string);
-	    }
-	}
-      SH_FREE(tmpcat);
-    }
-
+						 _("[SuidCheck]"),
+						 ShSuidchkSeverity);
+			
+			sh_hash_addflag(tmpcat, SH_FFLAG_SUIDCHK);
+			
+		      }
+		  }
+	      }
+	    SH_FREE(tmp);
+	    if (theFile.attr_string)
+	      SH_FREE(theFile.attr_string);
+	  }
+      }
+    SH_FREE(tmpcat);
+
+  
 #ifdef HAVE_SCHED_YIELD
     if (ShSuidchkYield == S_TRUE)
@@ -1125,13 +1149,17 @@
 	    status = errno;
 	    sh_error_handle ((-1), FIL__, __LINE__, status, MSG_E_SUBGEN,
-		             _("Failed to release time slice"),
+			     _("Failed to release time slice"),
 			     _("sh_suidchk_check_internal") );
 	  }
       }
 #endif
-
-  }  while (thisEntry != NULL);
-
-  (void) closedir (thisDir);
+  
+    dirlist = dirlist->next;
+
+  }  while (dirlist != NULL);
+
+
+  kill_sh_dirlist (dirlist_orig);
+
   SL_RETURN( (0), _("sh_suidchk_check_internal"));
 }
