Index: trunk/docs/Changelog
===================================================================
--- trunk/docs/Changelog	(revision 457)
+++ trunk/docs/Changelog	(revision 458)
@@ -12,4 +12,6 @@
 	* Fixed incorrect logic in setting the ALLIGNORE flag (more specific
 	  directory / file directives were ignored)
+	* Fix for tickets #358 (repetitive lstat warning about deleted 
+	  directory) and #359 (reporting of deleted/added top level directory)
 
 3.1.1 (01-0-2014):
Index: trunk/include/samhain.h
===================================================================
--- trunk/include/samhain.h	(revision 457)
+++ trunk/include/samhain.h	(revision 458)
@@ -188,4 +188,5 @@
 #define SH_FFLAG_REPORTED  (1<<3)
 #define SH_FFLAG_SUIDCHK   (1<<4)
+#define SH_FFLAG_ENOENT    (1<<5)
 
 #define SH_FFLAG_ALLIGNORE_SET(a)   (((a) & SH_FFLAG_ALLIGNORE) != 0)
@@ -208,4 +209,8 @@
 #define SET_SH_FFLAG_SUIDCHK(a)     ((a) |= SH_FFLAG_SUIDCHK)
 #define CLEAR_SH_FFLAG_SUIDCHK(a)   ((a) &= ~SH_FFLAG_SUIDCHK)
+
+#define SH_FFLAG_ENOENT_SET(a)      (((a) & SH_FFLAG_ENOENT) != 0)
+#define SET_SH_FFLAG_ENOENT(a)      ((a) |= SH_FFLAG_ENOENT)
+#define CLEAR_SH_FFLAG_ENOENT(a)    ((a) &= ~SH_FFLAG_ENOENT)
 
 /* Flags for inotify
Index: trunk/include/sh_hash.h
===================================================================
--- trunk/include/sh_hash.h	(revision 457)
+++ trunk/include/sh_hash.h	(revision 458)
@@ -111,5 +111,9 @@
 /* Set a file flag in in-memory database
  */
-void sh_hash_addflag  (char * filename, int flag);
+void sh_hash_set_flag  (char * filename, int flag);
+
+/* Unset a file flag in in-memory database
+ */
+void sh_hash_clear_flag (char * filename, int flag_to_set);
 
 /* Compare a file with its status in the database.
Index: trunk/include/zAVLTree.h
===================================================================
--- trunk/include/zAVLTree.h	(revision 457)
+++ trunk/include/zAVLTree.h	(revision 458)
@@ -78,4 +78,5 @@
 extern int zAVL_string_set (zAVLTree ** tree, const char * key); 
 extern void zAVL_string_reset (zAVLTree * tree);
+extern void zAVL_string_del (zAVLTree * tree, const char * key);
 
 #endif
Index: trunk/src/sh_files.c
===================================================================
--- trunk/src/sh_files.c	(revision 457)
+++ trunk/src/sh_files.c	(revision 458)
@@ -336,4 +336,130 @@
 }
 
+static int handle_filecheck_ret(dirstack_t * ptr, char * tmp_in, int status)
+{
+  int fcount = 0;
+  char * tmp;
+
+  if (!tmp_in)
+    tmp = sh_util_safe_name (ptr->name);
+  else
+    tmp = tmp_in;
+
+  if (status == SH_FILE_UNKNOWN && (!SH_FFLAG_REPORTED_SET(ptr->is_reported)))
+    {
+      TPT(( 0, FIL__, __LINE__, _("msg=<file: %s> status=<%d>\n"), 
+	    tmp, status));
+      
+      if ( sh.flag.checkSum == SH_CHECK_INIT  || 
+	   sh_hash_have_it (ptr->name) >= 0)
+	{
+	  if (S_FALSE == sh_ignore_chk_del(ptr->name))
+	    {
+	      if (0 != hashreport_missing(ptr->name, 
+					  (ptr->class == SH_LEVEL_ALLIGNORE) ? 
+					  ShDFLevel[ptr->class] : 
+					  ShDFLevel[SH_ERR_T_FILE])) {
+		if (tmp == NULL) 
+		  tmp = sh_util_safe_name (ptr->name);
+		sh_error_handle ((ptr->class == SH_LEVEL_ALLIGNORE) ? 
+				 ShDFLevel[ptr->class] : 
+				 ShDFLevel[SH_ERR_T_FILE],
+				 FIL__, __LINE__, 0, MSG_FI_MISS,
+				 tmp);
+		++sh.statistics.files_report;
+	      }
+	    }
+	}
+      else /* not there at init, and still missing */
+	{
+	  if (tmp == NULL) 
+	    tmp = sh_util_safe_name (ptr->name);
+	  sh_error_handle (SH_ERR_NOTICE,
+			   FIL__, __LINE__, 0,
+			   MSG_FI_FAIL,
+			   tmp);
+	}
+#ifndef REPLACE_OLD
+      /* this will tell that we have seen the file, and thus prevent
+       * deletion from the database, resulting in an incomplete
+       * message when the file reappears
+       */
+      if (sh.flag.checkSum != SH_CHECK_INIT) 
+	sh_hash_set_visited_true(ptr->name);
+#else
+      if (sh.flag.checkSum != SH_CHECK_INIT) 
+	sh_hash_set_missing(ptr->name);
+#endif
+      if (sh.flag.reportonce == S_TRUE)
+	SET_SH_FFLAG_REPORTED(ptr->is_reported);
+    }
+  else 
+    {
+      /* exists (status >= 0), but was missing (reported == TRUE)
+       */
+      if (status != SH_FILE_UNKNOWN && SH_FFLAG_REPORTED_SET(ptr->is_reported))
+	{
+	  CLEAR_SH_FFLAG_REPORTED(ptr->is_reported);
+	  sh_hash_clear_flag(ptr->name, SH_FFLAG_ENOENT);
+	}
+      
+      /* Catchall
+       */
+      else if (status == SH_FILE_UNKNOWN)
+	{
+	  /* Thu Mar  7 15:09:40 CET 2002 Make sure missing file
+	   * is reported if ptr->reported == S_TRUE because the
+	   * file has been added.
+	   */
+	  if (sh_hash_have_it (ptr->name) >= 0 && 
+	      !SH_FFLAG_REPORTED_SET(ptr->is_reported))
+	    {
+	      if (S_FALSE == sh_ignore_chk_del(ptr->name))
+		{
+		  if (0 != hashreport_missing(ptr->name, 
+					      (ptr->class == SH_LEVEL_ALLIGNORE) ? 
+					      ShDFLevel[ptr->class] : 
+					      ShDFLevel[SH_ERR_T_FILE])) {
+		    if (tmp == NULL) 
+		      tmp = sh_util_safe_name (ptr->name);
+		    sh_error_handle ((ptr->class == SH_LEVEL_ALLIGNORE)? 
+				     ShDFLevel[ptr->class] : 
+				     ShDFLevel[SH_ERR_T_FILE],
+				     FIL__, __LINE__, 0, MSG_FI_MISS,
+				     tmp);
+		    ++sh.statistics.files_report;
+		  }
+		}
+#ifndef REPLACE_OLD
+	      if (sh.flag.checkSum != SH_CHECK_INIT) 
+		sh_hash_set_visited_true(ptr->name);
+#else
+	      /* delete from database
+	       */
+	      if (sh.flag.checkSum != SH_CHECK_INIT) 
+		sh_hash_set_missing(ptr->name);
+#endif
+	    }
+	  else
+	    {
+	      if (tmp == NULL) 
+		tmp = sh_util_safe_name (ptr->name);
+	      sh_error_handle (SH_ERR_INFO, FIL__, __LINE__, 0,
+			       MSG_FI_FAIL,
+			       tmp);
+	      if (sh.flag.checkSum != SH_CHECK_INIT)
+		sh_hash_set_visited_true(ptr->name);
+	    }
+	}
+      
+      ++fcount;
+    }
+  if (!tmp_in)
+    SH_FREE(tmp);
+
+  return fcount;
+}
+
+
 unsigned long sh_files_chk ()
 {
@@ -396,111 +522,5 @@
 		tmp, status, ptr->is_reported));
 
-	  if (status == SH_FILE_UNKNOWN && (!SH_FFLAG_REPORTED_SET(ptr->is_reported)))
-	    {
-	      TPT(( 0, FIL__, __LINE__, _("msg=<file: %s> status=<%d>\n"), 
-		    tmp, status));
-
-	      if ( sh.flag.checkSum == SH_CHECK_INIT  || 
-		  sh_hash_have_it (ptr->name) >= 0)
-		{
-		  if (S_FALSE == sh_ignore_chk_del(ptr->name))
-		    {
-		      if (0 != hashreport_missing(ptr->name, 
-						  (ptr->class == SH_LEVEL_ALLIGNORE) ? 
-						  ShDFLevel[ptr->class] : 
-						  ShDFLevel[SH_ERR_T_FILE])) {
-			if (tmp == NULL) 
-			  tmp = sh_util_safe_name (ptr->name);
-			sh_error_handle ((ptr->class == SH_LEVEL_ALLIGNORE) ? 
-					 ShDFLevel[ptr->class] : 
-					 ShDFLevel[SH_ERR_T_FILE],
-					 FIL__, __LINE__, 0, MSG_FI_MISS,
-					 tmp);
-			++sh.statistics.files_report;
-		      }
-		    }
-		}
-	      else /* not there at init, and still missing */
-		{
-		  if (tmp == NULL) 
-		    tmp = sh_util_safe_name (ptr->name);
-		  sh_error_handle (SH_ERR_NOTICE,
-				   FIL__, __LINE__, 0,
-				   MSG_FI_FAIL,
-				   tmp);
-		}
-#ifndef REPLACE_OLD
-	      /* this will tell that we have seen the file, and thus prevent
-	       * deletion from the database, resulting in an incomplete
-	       * message when the file reappears
-	       */
-	      if (sh.flag.checkSum != SH_CHECK_INIT) 
-		sh_hash_set_visited_true(ptr->name);
-#else
-	      if (sh.flag.checkSum != SH_CHECK_INIT) 
-		sh_hash_set_missing(ptr->name);
-#endif
-	      if (sh.flag.reportonce == S_TRUE)
-		SET_SH_FFLAG_REPORTED(ptr->is_reported);
-	    }
-	  else 
-	    {
-	      /* exists (status >= 0), but was missing (reported == TRUE)
-	       */
-	      if (status != SH_FILE_UNKNOWN && SH_FFLAG_REPORTED_SET(ptr->is_reported))
-		{
-		  CLEAR_SH_FFLAG_REPORTED(ptr->is_reported);
-		}
-
-	      /* Catchall
-	       */
-	      else if (status == SH_FILE_UNKNOWN)
-		{
-		  /* Thu Mar  7 15:09:40 CET 2002 Make sure missing file
-		   * is reported if ptr->reported == S_TRUE because the
-		   * file has been added.
-		   */
-		  if (sh_hash_have_it (ptr->name) >= 0 && 
-		      !SH_FFLAG_REPORTED_SET(ptr->is_reported))
-		    {
-		      if (S_FALSE == sh_ignore_chk_del(ptr->name))
-			{
-			  if (0 != hashreport_missing(ptr->name, 
-						      (ptr->class == SH_LEVEL_ALLIGNORE) ? 
-						      ShDFLevel[ptr->class] : 
-						      ShDFLevel[SH_ERR_T_FILE])) {
-			    if (tmp == NULL) 
-			      tmp = sh_util_safe_name (ptr->name);
-			    sh_error_handle ((ptr->class == SH_LEVEL_ALLIGNORE)? 
-					     ShDFLevel[ptr->class] : 
-					     ShDFLevel[SH_ERR_T_FILE],
-					     FIL__, __LINE__, 0, MSG_FI_MISS,
-					     tmp);
-			    ++sh.statistics.files_report;
-			  }
-			}
-#ifndef REPLACE_OLD
-		      if (sh.flag.checkSum != SH_CHECK_INIT) 
-			sh_hash_set_visited_true(ptr->name);
-#else
-		      /* delete from database
-		       */
-		      if (sh.flag.checkSum != SH_CHECK_INIT) 
-			sh_hash_set_missing(ptr->name);
-#endif
-		    }
-		  else
-		    {
-		      if (tmp == NULL) 
-			tmp = sh_util_safe_name (ptr->name);
-		      sh_error_handle (SH_ERR_INFO, FIL__, __LINE__, 0,
-				       MSG_FI_FAIL,
-				       tmp);
-		      if (sh.flag.checkSum != SH_CHECK_INIT)
-			sh_hash_set_visited_true(ptr->name);
-		    }
-		}
-	      ++fcount;
-	    }
+	  fcount += handle_filecheck_ret(ptr, tmp, status);
 	  
 	  if (tmp != NULL)
@@ -1475,4 +1495,6 @@
   dirstack_t * dst_ptr;
   int          status;
+  int          tmp_reported;
+  volatile int          filetype = SH_FILE_UNKNOWN;
   volatile unsigned long dcount = 0;
   char       * tmp;
@@ -1508,7 +1530,11 @@
 		{
 		  BREAKEXIT(sh_files_filecheck);
-		  sh_files_filecheck (dst_ptr->class, dst_ptr->check_mask, 
-				      ptr->name,  
-				      NULL,  &status, 0);
+		  tmp_reported = dst_ptr->is_reported;
+		  filetype = sh_files_filecheck (dst_ptr->class, dst_ptr->check_mask, 
+						 ptr->name,  
+						 NULL,  &tmp_reported, 0);
+		  dst_ptr->is_reported = tmp_reported;
+		  (void) handle_filecheck_ret(dst_ptr, NULL, filetype);
+
 		  dst_ptr->checked = S_TRUE;
 		  status           = S_TRUE;
@@ -1522,6 +1548,11 @@
 
 	  if (status == S_FALSE)
-	    sh_files_filecheck (ptr->class,  ptr->check_mask, 
-				ptr->name,  NULL,  &status, 0);
+	    {
+	      tmp_reported = ptr->is_reported;
+	      filetype = sh_files_filecheck (ptr->class,  ptr->check_mask, 
+					     ptr->name,  NULL,  &tmp_reported, 0);
+	      ptr->is_reported = tmp_reported;
+	      (void) handle_filecheck_ret(ptr, NULL, filetype);
+	    }
 
 	  BREAKEXIT(sh_files_checkdir);
@@ -1559,4 +1590,5 @@
 		{
 		  CLEAR_SH_FFLAG_REPORTED(ptr->is_reported);
+		  sh_hash_clear_flag(ptr->name, SH_FFLAG_ENOENT);
 #if 0
 		  /* obsoleted (really?) by the mandatory sh_files_filecheck()
@@ -2661,5 +2693,4 @@
     } 
 
-
   /* stat the file and determine checksum (if a regular file)
    */
Index: trunk/src/sh_hash.c
===================================================================
--- trunk/src/sh_hash.c	(revision 457)
+++ trunk/src/sh_hash.c	(revision 458)
@@ -477,4 +477,8 @@
   SH_MUTEX_UNLOCK(mutex_hash);
 
+  /* remove here to avoid second message from hash_unvisited */
+  if (retval == 0)
+    sh_hash_remove (fullpath);
+
   return retval;
 }
@@ -537,14 +541,5 @@
       SL_RET0(_("hash_unvisited"));
     }
-#if 0
-  if (!strcmp(p->fullpath, "/var/lib/synaptic"))
-    {
-      fprintf(stderr, "FIXME: Check for missing files %s\n", p->fullpath);
-      if (SH_FFLAG_VISITED_SET(p->fflags)) fprintf(stderr, "FIXME: visited\n");
-      if (SH_FFLAG_CHECKED_SET(p->fflags)) fprintf(stderr, "FIXME: checked\n");
-      if (SH_FFLAG_REPORTED_SET(p->fflags)) fprintf(stderr, "FIXME: reported\n");
-      if (SH_FFLAG_ALLIGNORE_SET(p->fflags)) fprintf(stderr, "FIXME: allignore\n");
-    }
-#endif
+
   /* visited   flag not set: not seen; 
    * checked   flag     set: not seen (i.e. missing), and already checked 
@@ -560,8 +555,5 @@
     {
       i = retry_lstat(FIL__, __LINE__, p->fullpath, &buf);
-#if 0
-      if (!strcmp(p->fullpath, "/var/lib/synaptic"))
-	fprintf(stderr, "FIXME: Check for missing files %s (%d)\n", p->fullpath, i);
-#endif
+
      /* if file does not exist
        */
@@ -653,4 +645,5 @@
   CLEAR_SH_FFLAG_VISITED(p->fflags);
   CLEAR_SH_FFLAG_CHECKED(p->fflags);
+  SET_SH_FFLAG_ENOENT(p->fflags);
 
   SL_RET0(_("hash_unvisited"));
@@ -2202,5 +2195,5 @@
 /* needs lock to be threadsafe
  */
-void sh_hash_addflag (char * filename, int flag_to_set)
+void sh_hash_set_flag (char * filename, int flag_to_set)
 {
   sh_file_t * p;
@@ -2218,4 +2211,24 @@
   return;
 }
+
+/* needs lock to be threadsafe
+ */
+void sh_hash_clear_flag (char * filename, int flag_to_clear)
+{
+  sh_file_t * p;
+
+  if (IsInit != 1) 
+    sh_hash_init();
+
+  SH_MUTEX_LOCK(mutex_hash);
+  p = sh_hash_have_it_int (filename);
+  if (p)
+    {
+      p->fflags &= ~flag_to_clear;
+    }
+  SH_MUTEX_UNLOCK(mutex_hash);
+  return;
+}
+
 
 /*****************************************************************
Index: trunk/src/sh_suidchk.c
===================================================================
--- trunk/src/sh_suidchk.c	(revision 457)
+++ trunk/src/sh_suidchk.c	(revision 458)
@@ -1294,5 +1294,5 @@
 		      }
 		    
-		    sh_hash_addflag(tmpcat, SH_FFLAG_SUIDCHK); /* no call to sh_error_handle */
+		    sh_hash_set_flag(tmpcat, SH_FFLAG_SUIDCHK); /* no call to sh_error_handle */
 		    
 		  }
@@ -1367,5 +1367,5 @@
 			    SH_MUTEX_UNLOCK(mutex_thread_nolog);
 			    
-			    sh_hash_addflag(tmpcat, SH_FFLAG_SUIDCHK); /* no call to sh_error_handle */
+			    sh_hash_set_flag(tmpcat, SH_FFLAG_SUIDCHK); /* no call to sh_error_handle */
 			    
 			  }
@@ -1381,5 +1381,5 @@
 						 ShSuidchkSeverity);
 			SH_MUTEX_UNLOCK(mutex_thread_nolog);	
-			sh_hash_addflag(tmpcat, SH_FFLAG_SUIDCHK); /* no call to sh_error_handle */
+			sh_hash_set_flag(tmpcat, SH_FFLAG_SUIDCHK); /* no call to sh_error_handle */
 			
 		      }
Index: trunk/src/sh_unix.c
===================================================================
--- trunk/src/sh_unix.c	(revision 457)
+++ trunk/src/sh_unix.c	(revision 458)
@@ -3930,16 +3930,21 @@
 	{
 	  if (S_FALSE == sh_ignore_chk_del(theFile->fullpath)) {
-	    char errbuf[SH_ERRBUF_SIZE];
-	    uid_t euid;
-	    (void) sl_get_euid(&euid);
-	    tmp2 = sh_util_safe_name (theFile->fullpath);
-	    SH_MUTEX_LOCK(mutex_thread_nolog);
-	    sh_error_handle (level, FIL__, __LINE__, stat_return, MSG_FI_STAT,
-			     _("lstat"),
-			     sh_error_message (stat_errno, errbuf, sizeof(errbuf)),
-			     (long) euid,
-			     tmp2);
-	    SH_MUTEX_UNLOCK(mutex_thread_nolog);
-	    SH_FREE(tmp2);
+	    int flags = sh_hash_getflags (theFile->fullpath);
+
+	    if ((flags >= 0) && (flags & SH_FFLAG_ENOENT) == 0) {
+	      char errbuf[SH_ERRBUF_SIZE];
+	      uid_t euid;
+	      (void) sl_get_euid(&euid);
+	      tmp2 = sh_util_safe_name (theFile->fullpath);
+	      SH_MUTEX_LOCK(mutex_thread_nolog);
+	      sh_error_handle (level, FIL__, __LINE__, stat_return, MSG_FI_STAT,
+			       _("lstat"),
+			       sh_error_message (stat_errno, errbuf, sizeof(errbuf)),
+			       (long) euid,
+			       tmp2);
+	      SH_MUTEX_UNLOCK(mutex_thread_nolog);
+	      SH_FREE(tmp2);
+	      sh_hash_set_flag (theFile->fullpath, SH_FFLAG_ENOENT);
+	    }
 	  }
 	}
Index: trunk/src/zAVLTree.c
===================================================================
--- trunk/src/zAVLTree.c	(revision 457)
+++ trunk/src/zAVLTree.c	(revision 458)
@@ -80,4 +80,20 @@
   return NULL;
 }
+void zAVL_string_del (zAVLTree * tree, const char * key)
+{
+  /* zAVLSearch() checks for NULL tree 
+   */
+  if (key)
+    {
+      char * item = ((char *) zAVLSearch (tree, key));
+      if (item)
+	{
+	  zAVLDelete(tree, key);
+	  zfree_string(item);
+	}
+    }
+  return;
+}
+
 
 
