1 /* SAMHAIN file system integrity testing                                   */
2 /* Copyright (C) 1999, 2000, 2001, 2002 Rainer Wichmann                    */
3 /*                                                                         */
4 /*  This program is free software; you can redistribute it                 */
5 /*  and/or modify                                                          */
6 /*  it under the terms of the GNU General Public License as                */
7 /*  published by                                                           */
8 /*  the Free Software Foundation; either version 2 of the License, or      */
9 /*  (at your option) any later version.                                    */
10 /*                                                                         */
11 /*  This program is distributed in the hope that it will be useful,        */
12 /*  but WITHOUT ANY WARRANTY; without even the implied warranty of         */
13 /*  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the          */
14 /*  GNU General Public License for more details.                           */
15 /*                                                                         */
16 /*  You should have received a copy of the GNU General Public License      */
17 /*  along with this program; if not, write to the Free Software            */
18 /*  Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.              */
19 
20 #include "config_xor.h"
21 
22 #include <stdlib.h>
23 #include <string.h>
24 #include <stdio.h>
25 #include <sys/types.h>
26 #ifdef HAVE_SYS_SYSMACROS_H
27 #include <sys/sysmacros.h>
28 #endif
29 #include <sys/stat.h>
30 #include <unistd.h>
31 #include <ctype.h>
32 
33 #ifdef MAJOR_IN_MKDEV
34 #include <sys/mkdev.h>
35 #else
36 #ifdef MAJOR_IN_SYSMACROS
37 #include <sys/sysmacros.h>
38 #endif
39 #endif
40 
41 #ifdef HAVE_MEMORY_H
42 #include <memory.h>
43 #endif
44 
45 
46 #if defined(SH_WITH_CLIENT) || defined(SH_STANDALONE)
47 
48 #include "samhain.h"
49 #include "sh_utils.h"
50 #include "sh_unix.h"
51 #include "sh_dbIO_int.h"
52 #include "sh_dbIO.h"
53 #include "sh_hash.h"
54 #include "sh_error.h"
55 #include "sh_tiger.h"
56 #include "sh_sig.h"
57 #include "sh_unix.h"
58 #include "sh_files.h"
59 #include "sh_ignore.h"
60 #include "sh_pthread.h"
61 
62 #if defined(SH_WITH_CLIENT)
63 #include "sh_xfer.h"
64 #endif
65 
66 
67 #define SH_KEY_NULL _("000000000000000000000000000000000000000000000000")
68 
69 
70 #undef  FIL__
71 #define FIL__  _("sh_hash.c")
72 
73 SH_MUTEX_INIT(mutex_hash,PTHREAD_MUTEX_INITIALIZER);
74 
75 static char * all_items (file_type * theFile, char * fileHash, int is_new);
76 
77 static const char  *policy[] = {
78   N_("[]"),
79   N_("[ReadOnly]"),
80   N_("[LogFiles]"),
81   N_("[GrowingLogs]"),
82   N_("[IgnoreNone]"),
83   N_("[IgnoreAll]"),
84   N_("[Attributes]"),
85   N_("[User0]"),
86   N_("[User1]"),
87   N_("[User2]"),
88   N_("[User3]"),
89   N_("[User4]"),
90   N_("[Prelink]"),
91   NULL
92 };
93 
94 static int report_checkflags = S_FALSE;
set_report_checkflags(const char * c)95 int set_report_checkflags(const char * c)
96 {
97   return sh_util_flagval(c, &report_checkflags);
98 }
get_report_checkflags()99 int get_report_checkflags()
100 {
101   return report_checkflags;
102 }
103 
104 
105 
sh_hash_getpolicy(int class)106 const char * sh_hash_getpolicy(int class)
107 {
108   if (class > 0 && class < SH_ERR_T_DIR)
109     return _(policy[class]);
110   return _("[indef]");
111 }
112 
113 /**********************************
114  *
115  * hash table functions
116  *
117  **********************************
118  */
119 
120 #include "sh_hash.h"
121 
122 
123 /**************************************************************
124  *
125  * create a file_type from a sh_file_t
126  *
127  **************************************************************/
sh_hash_create_ft(const sh_file_t * p,char * fileHash)128 file_type * sh_hash_create_ft (const sh_file_t * p, char * fileHash)
129 {
130   file_type * theFile;
131 
132   SL_ENTER(_("sh_hash_create_ft"));
133 
134   theFile = SH_ALLOC(sizeof(file_type));
135 
136   sl_strlcpy(theFile->c_mode, p->theFile.c_mode, 11);
137   theFile->mode  =  p->theFile.mode;
138 #if defined(__linux__) || defined(HAVE_STAT_FLAGS)
139   sl_strlcpy(theFile->c_attributes, p->theFile.c_attributes, ATTRBUF_SIZE);
140   theFile->attributes =  p->theFile.attributes;
141 #endif
142 
143   sl_strlcpy(theFile->fullpath, p->fullpath, PATH_MAX);
144   if (p->linkpath != NULL /* && theFile->c_mode[0] == 'l' */)
145     {
146       theFile->link_path = sh_util_strdup(p->linkpath);
147     }
148   else
149     {
150       theFile->link_path = NULL;
151     }
152   sl_strlcpy(fileHash, p->theFile.checksum, KEY_LEN+1);
153 
154   theFile->mtime =  p->theFile.mtime;
155   theFile->ctime =  p->theFile.ctime;
156   theFile->atime =  p->theFile.atime;
157 
158   theFile->size  =  p->theFile.size;
159 
160   sl_strlcpy(theFile->c_group, p->theFile.c_group, GROUP_MAX+2);
161   theFile->group =  p->theFile.group;
162   sl_strlcpy(theFile->c_owner, p->theFile.c_owner, USER_MAX+2);
163   theFile->owner =  p->theFile.owner;
164 
165   theFile->ino   =  p->theFile.ino;
166   theFile->rdev  =  p->theFile.rdev;
167   theFile->dev   =  p->theFile.dev;
168   theFile->hardlinks = p->theFile.hardlinks;
169   theFile->check_flags = p->theFile.checkflags;
170 
171   if (p->attr_string)
172     theFile->attr_string = sh_util_strdup(p->attr_string);
173   else
174     theFile->attr_string = NULL;
175 
176   SL_RETURN((theFile), _("sh_hash_create_ft"));
177 }
178 
179 struct two_sh_file_t {
180   sh_file_t * prev;
181   sh_file_t * this;
182 };
183 
184 static sh_file_t * hashsearch (const char * s);
185 static int hashsearch_prev (const char * s, struct two_sh_file_t * a, int * index);
186 
187 
188 /**************************************************************
189  *
190  * >>>> The internal database <<<
191  *
192  **************************************************************/
193 
194 static sh_file_t * tab[TABSIZE];
195 
get_default_data_table()196 sh_file_t ** get_default_data_table()
197 {
198   return tab;
199 }
200 
201 /**************************************************************
202  *
203  * compute hash function
204  *
205  **************************************************************/
206 
hashfunc(const char * s)207 static int hashfunc(const char *s)
208 {
209   unsigned int n = 0;
210 
211   for ( ; *s; s++)
212     n = 31 * n + *s;
213 
214   return n & (TABSIZE - 1); /* % TABSIZE */;
215 }
216 
217 
hashreport_missing(char * fullpath,int level)218 int hashreport_missing( char *fullpath, int level)
219 {
220   sh_file_t * p;
221   char * tmp;
222   char   fileHash[KEY_LEN + 1];
223   file_type * theFile;
224   char * str;
225   char hashbuf[KEYBUF_SIZE];
226   volatile int  retval;
227 
228   /* --------  find the entry for the file ----------------       */
229 
230   SH_MUTEX_LOCK(mutex_hash);
231 
232   retval = 0;
233 
234   if (sl_strlen(fullpath) <= MAX_PATH_STORE)
235     p = hashsearch(fullpath);
236   else
237     p = hashsearch( sh_tiger_hash(fullpath,
238 				  TIGER_DATA,
239 				  sl_strlen(fullpath),
240 				  hashbuf, sizeof(hashbuf))
241 		    );
242   if (p == NULL)
243     {
244       retval = -1;
245       goto unlock_and_return;
246     }
247 
248   theFile = sh_hash_create_ft (p, fileHash);
249   str = all_items(theFile, fileHash, 0);
250   tmp = sh_util_safe_name(fullpath);
251 
252   SH_MUTEX_LOCK(mutex_thread_nolog);
253   if (!sh_global_check_silent)
254     sh_error_handle (level, FIL__, __LINE__, 0,
255 		     MSG_FI_MISS2, tmp, str);
256   SH_MUTEX_UNLOCK(mutex_thread_nolog);
257   ++sh.statistics.files_report;
258 
259   SH_FREE(tmp);
260   SH_FREE(str);
261   if (theFile->attr_string) SH_FREE(theFile->attr_string);
262   if (theFile->link_path)   SH_FREE(theFile->link_path);
263   SH_FREE(theFile);
264 
265  unlock_and_return:
266   ; /* 'label at end of compound statement */
267   SH_MUTEX_UNLOCK(mutex_hash);
268 
269   /* remove here to avoid second message from hash_unvisited */
270   if (retval == 0)
271     sh_hash_remove (fullpath);
272 
273   return retval;
274 }
275 
276 
277 /**************************************************************
278  *
279  * search for files not visited, and check whether they exist
280  *
281  **************************************************************/
delete_db_entry(sh_file_t * p)282 static sh_file_t * delete_db_entry(sh_file_t *p)
283 {
284   if (p->fullpath)
285     {
286       SH_FREE(p->fullpath);
287       p->fullpath = NULL;
288     }
289   if (p->linkpath)
290     {
291       SH_FREE(p->linkpath);
292       p->linkpath = NULL;
293     }
294   if (p->attr_string)
295     {
296       SH_FREE(p->attr_string);
297       p->attr_string = NULL;
298     }
299   SH_FREE(p);
300   return NULL;
301 }
302 
hash_unvisited(int j,sh_file_t * prev,sh_file_t * p,ShErrLevel level)303 static void hash_unvisited (int j,
304 			    sh_file_t *prev, sh_file_t *p, ShErrLevel level)
305 {
306   struct stat buf;
307   int i;
308   char * tmp;
309   char * ptr;
310   char   fileHash[KEY_LEN + 1];
311   file_type * theFile;
312   char * str;
313 
314   SL_ENTER(_("hash_unvisited"));
315 
316   if (p->next != NULL)
317     hash_unvisited (j, p, p->next, level);
318 
319   if (p->fullpath == NULL)
320     {
321       SL_RET0(_("hash_unvisited"));
322     }
323 
324   /* Not a fully qualified path, i.e. some info stored by some module
325    */
326   if (p->fullpath[0] != '/')
327     {
328       SL_RET0(_("hash_unvisited"));
329     }
330 
331   /* visited   flag not set: not seen;
332    * checked   flag     set: not seen (i.e. missing), and already checked
333    * reported  flag not set: not reported yet
334    * allignore flag not set: not under IgnoreAll
335    *
336    * Files/directories under IgnoreAll are noticed as missing already
337    * during the file check.
338    */
339   if (((!SH_FFLAG_VISITED_SET(p->fflags)) || SH_FFLAG_CHECKED_SET(p->fflags))
340       && (!SH_FFLAG_REPORTED_SET(p->fflags))
341       /* && (!SH_FFLAG_ALLIGNORE_SET(p->fflags)) */)
342     {
343       i = retry_lstat(FIL__, __LINE__, p->fullpath, &buf);
344 
345      /* if file does not exist
346        */
347       if (0 != i)
348 	{
349 	  ptr = sh_util_dirname (p->fullpath);
350 	  if (ptr)
351 	    {
352 	      /* If any of the parent directories is under IgnoreAll
353 	       */
354 	      if ((0 != sh_files_is_allignore(ptr)) || SH_FFLAG_ALLIGNORE_SET(p->fflags))
355 		level = ShDFLevel[SH_LEVEL_ALLIGNORE];
356 	      SH_FREE(ptr);
357 	    }
358 
359 	  /* Only report if !SH_FFLAG_CHECKED_SET
360 	   */
361 	  if (!SH_FFLAG_CHECKED_SET(p->fflags))
362 	    {
363 	      if (S_FALSE == sh_ignore_chk_del(p->fullpath))
364 		{
365 		  tmp = sh_util_safe_name(p->fullpath);
366 
367 		  theFile = sh_hash_create_ft (p, fileHash);
368 		  str = all_items(theFile, fileHash, 0);
369 		  if (!sh_global_check_silent)
370 		    sh_error_handle (level, FIL__, __LINE__, 0,
371 				     MSG_FI_MISS2, tmp, str);
372 		  ++sh.statistics.files_report;
373 		  SH_FREE(str);
374 		  if (theFile->attr_string) SH_FREE(theFile->attr_string);
375 		  if (theFile->link_path)   SH_FREE(theFile->link_path);
376 		  SH_FREE(theFile);
377 
378 		  SH_FREE(tmp);
379 		}
380 	    }
381 
382 	  /* We rewrite the db on update, thus we need to keep this
383 	   * if the user does not want to purge it from the db.
384 	   */
385 
386 	  if ((sh.flag.reportonce == S_TRUE && sh.flag.update == S_FALSE) ||
387 	      (S_TRUE == sh.flag.update && S_TRUE == sh_util_ask_update(p->fullpath)))
388 	    {
389 	      /* Remove the old entry
390 	       */
391 	      if (prev == p)
392 		tab[j] = p->next;
393 	      else
394 		prev->next = p->next;
395 
396 	      delete_db_entry(p);
397 
398 	      SL_RET0(_("hash_unvisited"));
399 	    }
400 	}
401     }
402 
403   else if (SH_FFLAG_VISITED_SET(p->fflags) && SH_FFLAG_REPORTED_SET(p->fflags)
404 	   && (!SH_FFLAG_ALLIGNORE_SET(p->fflags)))
405     {
406       if (S_FALSE == sh_ignore_chk_new(p->fullpath))
407 	{
408 	  tmp = sh_util_safe_name(p->fullpath);
409 
410 	  theFile = sh_hash_create_ft (p, fileHash);
411 	  str = all_items(theFile, fileHash, 0);
412 	  if (!sh_global_check_silent)
413 	    sh_error_handle (level, FIL__, __LINE__, 0,
414 			     MSG_FI_MISS2, tmp, str);
415 	  ++sh.statistics.files_report;
416 	  SH_FREE(str);
417 	  if (theFile->attr_string)
418 	    SH_FREE(theFile->attr_string);
419 	  SH_FREE(theFile);
420 
421 	  SH_FREE(tmp);
422 	}
423 
424       CLEAR_SH_FFLAG_REPORTED(p->fflags);
425     }
426 
427   if (sh.flag.reportonce == S_FALSE)
428     CLEAR_SH_FFLAG_REPORTED(p->fflags);
429 
430   CLEAR_SH_FFLAG_VISITED(p->fflags);
431   CLEAR_SH_FFLAG_CHECKED(p->fflags);
432   SET_SH_FFLAG_ENOENT(p->fflags);
433 
434   SL_RET0(_("hash_unvisited"));
435 }
436 
437 
438 
439 /*********************************************************************
440  *
441  * Search for files in the database that have been deleted from disk.
442  *
443  *********************************************************************/
sh_hash_unvisited(ShErrLevel level)444 void sh_hash_unvisited (ShErrLevel level)
445 {
446   int i;
447 
448   SL_ENTER(_("sh_hash_unvisited"));
449 
450   SH_MUTEX_LOCK(mutex_hash);
451   for (i = 0; i < TABSIZE; ++i)
452     {
453       if (tab[i] != NULL)
454 	hash_unvisited (i, tab[i], tab[i], level);
455     }
456   SH_MUTEX_UNLOCK(mutex_hash);
457 
458   SL_RET0(_("hash_unvisited"));
459 }
460 
461 /*********************************************************************
462  *
463  * Remove a single file from the database.
464  *
465  *********************************************************************/
sh_hash_remove_unconditional(const char * path)466 void sh_hash_remove_unconditional (const char * path)
467 {
468   struct two_sh_file_t entries;
469   int index;
470 
471   SL_ENTER(_("sh_hash_remove_unconditional"));
472 
473   SH_MUTEX_LOCK(mutex_hash);
474   if (0 == hashsearch_prev (path, &entries, &index))
475     {
476       sh_file_t * p = entries.this;
477 
478       /* Remove the old entry
479        */
480       if (entries.prev == p)
481 	tab[index] = p->next;
482       else
483 	entries.prev->next = p->next;
484 
485       delete_db_entry(p);
486     }
487   SH_MUTEX_UNLOCK(mutex_hash);
488 
489   SL_RET0(_("sh_hash_remove_unconditional"));
490 }
491 
sh_hash_remove(const char * path)492 void sh_hash_remove (const char * path)
493 {
494   SL_ENTER(_("sh_hash_remove"));
495 
496   if ((sh.flag.reportonce == S_TRUE && sh.flag.update == S_FALSE) ||
497       (S_TRUE == sh.flag.update && S_TRUE == sh_util_ask_update(path)))
498     {
499       sh_hash_remove_unconditional (path);
500     }
501   SL_RET0(_("sh_hash_remove"));
502 }
503 
504 
505 /*********************************************************************
506  *
507  * Search for unvisited entries in the database, custom error handler.
508  *
509  *********************************************************************/
sh_hash_unvisited_custom(char prefix,void (* handler)(const char * key))510 void sh_hash_unvisited_custom (char prefix, void(*handler)(const char * key))
511 {
512   int i;
513   sh_file_t *p    = NULL;
514   sh_file_t *prev = NULL;
515   sh_file_t *next = NULL;
516 
517   SL_ENTER(_("sh_hash_unvisited_custom"));
518 
519   SH_MUTEX_LOCK(mutex_hash);
520   for (i = 0; i < TABSIZE; ++i)
521     {
522       if (tab[i] != NULL)
523 	{
524 	  p = tab[i]; prev = p;
525 
526 	  do
527 	    {
528 	      next = p->next;
529 
530 	      if (p->fullpath &&
531 		  prefix == p->fullpath[0])
532 		{
533 		  if ((!SH_FFLAG_VISITED_SET(p->fflags))
534 		      && (!SH_FFLAG_REPORTED_SET(p->fflags)))
535 		    {
536 		      handler(p->fullpath);
537 
538 		      if (!SH_FFLAG_CHECKED_SET(p->fflags))
539 			{
540 			  /* delete */
541 			  if (tab[i] == p)
542 			    {
543 			      tab[i] = p->next;
544 			      prev   = tab[i];
545 			      next   = prev;
546 			    }
547 			  else
548 			    {
549 			      prev->next = p->next;
550 			      next       = prev->next;
551 			    }
552 
553 			  p = delete_db_entry(p);
554 			}
555 		    }
556 		  if (p)
557 		    {
558 		      CLEAR_SH_FFLAG_VISITED(p->fflags);
559 		      CLEAR_SH_FFLAG_CHECKED(p->fflags);
560 		    }
561 		}
562 	      if (p)
563 		prev = p;
564 	      p    = next;
565 	    }
566 	  while (p);
567 	}
568     }
569   SH_MUTEX_UNLOCK(mutex_hash);
570 
571   SL_RET0(_("hash_unvisited_custom"));
572 }
573 
574 
575 /**********************************************************************
576  *
577  * delete hash array
578  *
579  **********************************************************************/
hash_kill(sh_file_t * p)580 static void hash_kill (sh_file_t *p)
581 {
582   SL_ENTER(_("hash_kill"));
583 
584   if (p == NULL)
585     SL_RET0(_("hash_kill"));
586 
587   if (p->next != NULL)
588     hash_kill (p->next);
589 
590   if (p->fullpath)
591     {
592       SH_FREE(p->fullpath);
593       p->fullpath = NULL;
594     }
595   if (p->linkpath)
596     {
597       SH_FREE(p->linkpath);
598       p->linkpath = NULL;
599     }
600   if (p->attr_string)
601     {
602       SH_FREE(p->attr_string);
603       p->attr_string = NULL;
604     }
605   SH_FREE(p);
606   p = NULL;
607   SL_RET0(_("hash_kill"));
608 }
609 
610 
611 /***********************************************************************
612  *
613  * get info out of hash array
614  *
615  ***********************************************************************/
hashsearch(const char * s)616 static sh_file_t * hashsearch (const char * s)
617 {
618   sh_file_t * p;
619 
620   SL_ENTER(_("hashsearch"));
621 
622   if (s)
623     {
624       for (p = tab[hashfunc(s)]; p; p = p->next)
625 	if ((p->fullpath != NULL) && (0 == strcmp(s, p->fullpath)))
626 	  SL_RETURN( p, _("hashsearch"));
627     }
628   SL_RETURN( NULL, _("hashsearch"));
629 }
630 
hashsearch_prev(const char * s,struct two_sh_file_t * a,int * index)631 static int hashsearch_prev (const char * s, struct two_sh_file_t * a, int * index)
632 {
633   sh_file_t * this;
634   sh_file_t * prev = NULL;
635 
636   SL_ENTER(_("hashsearch_prev"));
637 
638   if (s)
639     {
640       *index = hashfunc(s);
641       this   = tab[*index];
642       prev   = this;
643 
644       if (this)
645 	{
646 	  do {
647 	    if ((this->fullpath != NULL) && (0 == strcmp(s, this->fullpath)))
648 	      {
649 		a->prev = prev;
650 		a->this = this;
651 		SL_RETURN( 0, _("hashsearch_prev"));
652 	      }
653 	    prev = this;
654 	    this = this->next;
655 	  } while(this);
656 	}
657     }
658   SL_RETURN( -1, _("hashsearch"));
659 }
660 
661 
662 /***********************************************************************
663  *
664  * insert into hash array
665  *
666  ***********************************************************************/
hashinsert(sh_file_t * mtab[TABSIZE],sh_file_t * s)667 void hashinsert (sh_file_t * mtab[TABSIZE], sh_file_t * s)
668 {
669   sh_file_t * p;
670   sh_file_t * q;
671   int key;
672 
673   SL_ENTER(_("hashinsert"));
674 
675   key = hashfunc(s->fullpath);
676 
677   if (mtab[key] == NULL)
678     {
679       mtab[key] = s;
680       mtab[key]->next = NULL;
681       SL_RET0(_("hashinsert"));
682     }
683   else
684     {
685       p = mtab[key];
686       while (1)
687 	{
688 	  if (p && p->fullpath && 0 == strcmp(s->fullpath, p->fullpath))
689 	    {
690 	      q = p->next;
691 	      SH_FREE(p->fullpath);
692 	      if(p->linkpath)    SH_FREE(p->linkpath);
693 	      if(p->attr_string) SH_FREE(p->attr_string);
694 	      memcpy(p, s, sizeof(sh_file_t));
695 	      p->next = q;
696 	      SH_FREE(s); s = NULL;
697 	      SL_RET0(_("hashinsert"));
698 	    }
699 	  else if (p && p->next == NULL)
700 	    {
701 	      p->next = s;
702 	      p->next->next = NULL;
703 	      SL_RET0(_("hashinsert"));
704 	    }
705 	  if (p)
706 	    p = p->next;
707 	  else /* cannot really happen, but llvm/clang does not know */
708 	    break;
709 	}
710     }
711   /* notreached */
712 }
713 
714 
715 
716 /******************************************************************
717  *
718  * ------- Check functions -------
719  *
720  ******************************************************************/
721 
722 static int IsInit = 0;
723 
sh_hash_set_initialized()724 void sh_hash_set_initialized()
725 {
726   IsInit = 1;
727   return;
728 }
729 
sh_hash_get_initialized()730 int sh_hash_get_initialized()
731 {
732   return IsInit;
733 }
734 
735 
736 /******************************************************************
737  *
738  * Initialize
739  *
740  ******************************************************************/
sh_hash_init()741 void sh_hash_init ()
742 {
743   volatile int  retval  = 0;
744   volatile int  exitval = EXIT_SUCCESS;
745 
746   SL_ENTER(_("sh_hash_init"));
747 
748   if ( sh.flag.checkSum == SH_CHECK_INIT )
749     {
750       dlog(1, FIL__, __LINE__,
751 	   _("Attempt to load the baseline database during initialisation. This is an internal error, please report it to the developer.\n"));
752       SH_ABORT;
753       aud_exit (FIL__, __LINE__, EXIT_FAILURE);
754     }
755 
756   SH_MUTEX_LOCK(mutex_hash);
757 
758   if (IsInit == 1)
759     {
760       goto unlock_and_return;
761     }
762 
763   /* Initialization completed.
764    */
765   retval = sh_dbIO_load_db(tab);
766 
767   if (0 == retval)
768     IsInit = 1;
769   else
770     exitval = EXIT_FAILURE;
771 
772  unlock_and_return:
773   ; /* 'label at end of compound statement */
774   SH_MUTEX_UNLOCK(mutex_hash);
775   if (retval == 0)
776     {
777       SL_RET0(_("sh_hash_init"));
778     }
779   sh_error_handle ((-1), FIL__, __LINE__, 0, MSG_EXIT_ABORT1, sh.prg_name);
780   aud_exit (FIL__, __LINE__, exitval);
781 }
782 
sh_hash_init_and_checksum()783 void sh_hash_init_and_checksum()
784 {
785   TPT((0, FIL__, __LINE__, _("msg=<Get checksum of the database.>\n")))
786   if (sh.flag.checkSum == SH_CHECK_CHECK)
787     {
788       if (0 != sl_strcmp(file_path('D', 'R'), _("REQ_FROM_SERVER")))
789 	{
790 	  char hashbuf[KEYBUF_SIZE];
791 	  (void) sl_strlcpy(sh.data.hash,
792 			    sh_tiger_hash (file_path('D', 'R'),
793 					   TIGER_FILE, TIGER_NOLIM,
794 					   hashbuf, sizeof(hashbuf)),
795 			    KEY_LEN+1);
796 	}
797 
798       /* this eventually fetches the file from server to get checksum
799        */
800       sh_hash_init ();
801     }
802   return;
803 }
804 
805 /*****************************************************************
806  *
807  * delete hash array
808  *
809  *****************************************************************/
sh_hash_hashdelete()810 void sh_hash_hashdelete ()
811 {
812   int i;
813 
814   SL_ENTER(_("sh_hash_hashdelete"));
815 
816   /* need deadlock detection here if called from exit handler
817    */
818   SH_MUTEX_TRYLOCK(mutex_hash);
819 
820   if (IsInit == 0)
821     goto unlock_and_exit;
822 
823   for (i = 0; i < TABSIZE; ++i)
824     if (tab[i] != NULL)
825       {
826 	hash_kill (tab[i]);
827 	tab[i] = NULL;
828       }
829   IsInit = 0;
830 
831  unlock_and_exit:
832   ; /* 'label at end of compound statement */
833   SH_MUTEX_TRYLOCK_UNLOCK(mutex_hash);
834 
835   SL_RET0(_("sh_hash_hashdelete"));
836 }
837 
838 static int sh_loosedircheck = S_FALSE;
839 
sh_hash_loosedircheck(const char * str)840 int sh_hash_loosedircheck(const char * str)
841 {
842   return sh_util_flagval(str, &sh_loosedircheck);
843 }
844 
845 
846 
847 
848 /*********************************************************************
849  *
850  * Check whether a file is present in the database.
851  *
852  *********************************************************************/
sh_hash_have_it_int(const char * newname)853 static sh_file_t *  sh_hash_have_it_int (const char * newname)
854 {
855   sh_file_t * p;
856   char hashbuf[KEYBUF_SIZE];
857 
858   SL_ENTER(_("sh_hash_have_it_int"));
859 
860   if (newname == NULL)
861     SL_RETURN( (NULL), _("sh_hash_have_it_int"));
862 
863   if (sl_strlen(newname) <= MAX_PATH_STORE)
864     p = hashsearch(newname);
865   else
866     p = hashsearch ( sh_tiger_hash(newname, TIGER_DATA, sl_strlen(newname),
867 				   hashbuf, sizeof(hashbuf)) );
868   if (p == NULL)
869      SL_RETURN( (NULL), _("sh_hash_have_it_int"));
870 
871   SL_RETURN( (p), _("sh_hash_have_it_int"));
872 }
873 
sh_hash_have_it(const char * newname)874 int sh_hash_have_it (const char * newname)
875 {
876   sh_file_t * p;
877   int retval;
878 
879   if (IsInit != 1)
880     sh_hash_init();
881 
882   SH_MUTEX_LOCK(mutex_hash);
883 
884   retval = 0;
885 
886   p = sh_hash_have_it_int (newname);
887 
888   if (!p)
889     retval = (-1);
890   else if ((!SH_FFLAG_ALLIGNORE_SET(p->fflags)) &&
891 	   (p->modi_mask & MODI_CHK) != 0 &&
892 	   (p->modi_mask & MODI_MOD) != 0)
893     retval = 1;
894   SH_MUTEX_UNLOCK(mutex_hash);
895 
896   return retval;
897 }
898 
sh_hash_get_it(const char * newname,file_type * tmpFile,char * fileHash)899 int sh_hash_get_it (const char * newname, file_type * tmpFile, char * fileHash)
900 {
901   sh_file_t * p;
902   int retval;
903 
904   if (IsInit != 1)
905     sh_hash_init();
906 
907   tmpFile->link_path   = NULL;
908   tmpFile->attr_string = NULL;
909 
910   SH_MUTEX_LOCK(mutex_hash);
911 
912   retval = (-1);
913 
914   p = sh_hash_have_it_int (newname);
915   if (p)
916     {
917       sl_strlcpy(tmpFile->fullpath,  p->fullpath, PATH_MAX);
918       if (p->linkpath)
919 	tmpFile->link_path = sh_util_strdup (p->linkpath);
920       tmpFile->size  = p->theFile.size;
921       tmpFile->mtime = p->theFile.mtime;
922       tmpFile->ctime = p->theFile.ctime;
923       tmpFile->atime = p->theFile.atime;
924 
925       if (NULL != fileHash)
926 	sl_strlcpy(fileHash, p->theFile.checksum, KEY_LEN+1);
927 
928       tmpFile->attr_string = NULL;
929       retval = 0;
930     }
931   SH_MUTEX_UNLOCK(mutex_hash);
932 
933   return retval;
934 }
935 
sh_hash_getflags(char * filename)936 int sh_hash_getflags (char * filename)
937 {
938   sh_file_t * p;
939   int retval = 0;
940 
941   if ( sh.flag.checkSum != SH_CHECK_INIT )
942     {
943       if (IsInit != 1)
944 	sh_hash_init();
945 
946       SH_MUTEX_LOCK(mutex_hash);
947       p = sh_hash_have_it_int (filename);
948       if (p)
949 	retval = p->fflags;
950       else
951 	retval = -1;
952       SH_MUTEX_UNLOCK(mutex_hash);
953     }
954   return retval;
955 }
956 
sh_hash_setflags(char * filename,int flags)957 int sh_hash_setflags (char * filename, int flags)
958 {
959   sh_file_t * p;
960   int retval = 0;
961 
962   if ( sh.flag.checkSum != SH_CHECK_INIT )
963     {
964       if (IsInit != 1)
965 	sh_hash_init();
966 
967       SH_MUTEX_LOCK(mutex_hash);
968       p = sh_hash_have_it_int (filename);
969       if (p)
970 	{
971 	  p->fflags = flags;
972 	  retval = 0;
973 	}
974       else
975 	retval = -1;
976       SH_MUTEX_UNLOCK(mutex_hash);
977     }
978   return retval;
979 }
980 
981 /* needs lock to be threadsafe
982  */
sh_hash_set_flag(char * filename,int flag_to_set)983 void sh_hash_set_flag (char * filename, int flag_to_set)
984 {
985   sh_file_t * p;
986 
987   if ( sh.flag.checkSum != SH_CHECK_INIT )
988     {
989       if (IsInit != 1)
990 	sh_hash_init();
991 
992       SH_MUTEX_LOCK(mutex_hash);
993       p = sh_hash_have_it_int (filename);
994       if (p)
995 	{
996 	  p->fflags |= flag_to_set;
997 	}
998       SH_MUTEX_UNLOCK(mutex_hash);
999     }
1000   return;
1001 }
1002 
1003 /* needs lock to be threadsafe
1004  */
sh_hash_clear_flag(char * filename,int flag_to_clear)1005 void sh_hash_clear_flag (char * filename, int flag_to_clear)
1006 {
1007   sh_file_t * p;
1008 
1009   if ( sh.flag.checkSum != SH_CHECK_INIT )
1010     {
1011       if (IsInit != 1)
1012 	sh_hash_init();
1013 
1014       SH_MUTEX_LOCK(mutex_hash);
1015       p = sh_hash_have_it_int (filename);
1016       if (p)
1017 	{
1018 	  p->fflags &= ~flag_to_clear;
1019 	}
1020       SH_MUTEX_UNLOCK(mutex_hash);
1021     }
1022   return;
1023 }
1024 
1025 
1026 /*****************************************************************
1027  *
1028  * Set a file's status to 'visited'. This is required for
1029  * files that should be ignored, and may be present in the
1030  * database, but not on disk.
1031  *
1032  *****************************************************************/
sh_hash_set_visited_int(char * newname,int flag)1033 static int sh_hash_set_visited_int (char * newname, int flag)
1034 {
1035   sh_file_t * p;
1036   char hashbuf[KEYBUF_SIZE];
1037   int  retval;
1038 
1039   SL_ENTER(_("sh_hash_set_visited_int"));
1040 
1041   if (newname == NULL)
1042     SL_RETURN((-1), _("sh_hash_set_visited_int"));
1043 
1044   if (IsInit != 1)
1045     sh_hash_init();
1046 
1047   SH_MUTEX_LOCK(mutex_hash);
1048 
1049   if (sl_strlen(newname) <= MAX_PATH_STORE)
1050     p = hashsearch(newname);
1051   else
1052     p = hashsearch (sh_tiger_hash(newname, TIGER_DATA, sl_strlen(newname),
1053 				  hashbuf, sizeof(hashbuf)));
1054 
1055   if (p)
1056     {
1057       if (flag == SH_FFLAG_CHECKED)
1058 	{
1059 	  CLEAR_SH_FFLAG_REPORTED(p->fflags);
1060 	  CLEAR_SH_FFLAG_VISITED(p->fflags);
1061 	  SET_SH_FFLAG_CHECKED(p->fflags);
1062 	}
1063       else
1064 	{
1065 	  SET_SH_FFLAG_VISITED(p->fflags);
1066 	  CLEAR_SH_FFLAG_CHECKED(p->fflags);
1067 	  if (flag == SH_FFLAG_REPORTED)
1068 	    SET_SH_FFLAG_REPORTED(p->fflags);
1069 	  else
1070 	    CLEAR_SH_FFLAG_REPORTED(p->fflags);
1071 	}
1072       retval = 0;
1073     }
1074   else
1075     retval = -1;
1076 
1077   SH_MUTEX_UNLOCK(mutex_hash);
1078   SL_RETURN((retval), _("sh_hash_set_visited_int"));
1079 }
1080 
1081 
1082 /* cause the record to be deleted without a 'missing' message
1083  */
sh_hash_set_missing(char * newname)1084 int sh_hash_set_missing (char * newname)
1085 {
1086   int i;
1087   SL_ENTER(_("sh_hash_set_missing"));
1088 
1089   i = sh_hash_set_visited_int(newname, SH_FFLAG_CHECKED);
1090 
1091   if (sh.flag.checkSum != SH_CHECK_INIT) {
1092     sh_hash_remove(newname);
1093   }
1094 
1095   SL_RETURN(i, _("sh_hash_set_missing"));
1096 }
1097 
1098 /* mark the file as visited and reported
1099  */
sh_hash_set_visited(char * newname)1100 int sh_hash_set_visited (char * newname)
1101 {
1102   int i;
1103   SL_ENTER(_("sh_hash_set_visited"));
1104   i = sh_hash_set_visited_int(newname, SH_FFLAG_REPORTED);
1105   SL_RETURN(i, _("sh_hash_set_visited"));
1106 }
1107 
1108 /* mark the file as visited and NOT reported
1109  * used to avoid deletion of file from internal database
1110  */
sh_hash_set_visited_true(char * newname)1111 int sh_hash_set_visited_true (char * newname)
1112 {
1113   int i;
1114   SL_ENTER(_("sh_hash_set_visited_true"));
1115   i = sh_hash_set_visited_int(newname, 0);
1116   SL_RETURN(i, _("sh_hash_set_visited_true"));
1117 }
1118 
1119 
1120 /******************************************************************
1121  *
1122  * Data entry for arbitrary data into database
1123  *
1124  ******************************************************************/
1125 
sh_hash_push2db(const char * key,struct store2db * save)1126 void sh_hash_push2db (const char * key, struct store2db * save)
1127 {
1128   int         i = 0;
1129   char      * p;
1130   char        i2h[2];
1131   file_type * tmpFile = SH_ALLOC(sizeof(file_type));
1132 
1133   int size            = save->size;
1134   unsigned char * str = save->str;
1135 
1136 
1137   tmpFile->attr_string = NULL;
1138   tmpFile->link_path   = NULL;
1139 
1140   sl_strlcpy(tmpFile->fullpath, key, PATH_MAX);
1141   tmpFile->size  = save->val0;
1142   tmpFile->mtime = save->val1;
1143   tmpFile->ctime = save->val2;
1144   tmpFile->atime = save->val3;
1145 
1146   tmpFile->mode  = 0;
1147   tmpFile->owner = 0;
1148   tmpFile->group = 0;
1149   sl_strlcpy(tmpFile->c_owner, _("root"), 5);
1150   sl_strlcpy(tmpFile->c_group, _("root"), 5);
1151 
1152   tmpFile->check_flags = 0;
1153 
1154   if ((str != NULL) && (size < (PATH_MAX/2)-1))
1155     {
1156       tmpFile->c_mode[0] = 'l';
1157       tmpFile->c_mode[1] = 'r'; tmpFile->c_mode[2]  = 'w';
1158       tmpFile->c_mode[3] = 'x'; tmpFile->c_mode[4]  = 'r';
1159       tmpFile->c_mode[5] = 'w'; tmpFile->c_mode[6]  = 'x';
1160       tmpFile->c_mode[7] = 'r'; tmpFile->c_mode[8]  = 'w';
1161       tmpFile->c_mode[9] = 'x'; tmpFile->c_mode[10] = '\0';
1162       tmpFile->link_path = SH_ALLOC((size * 2) + 2);
1163       for (i = 0; i < size; ++i)
1164 	{
1165 	  p = sh_util_charhex (str[i],i2h);
1166 	  tmpFile->link_path[2*i]   = p[0];
1167 	  tmpFile->link_path[2*i+1] = p[1];
1168 	  tmpFile->link_path[2*i+2] = '\0';
1169 	}
1170     }
1171   else
1172     {
1173       for (i = 0; i < 10; ++i)
1174 	tmpFile->c_mode[i] = '-';
1175       tmpFile->c_mode[10] = '\0';
1176       tmpFile->link_path = sh_util_strdup("-");
1177     }
1178 
1179   if (sh.flag.checkSum == SH_CHECK_INIT)
1180     sh_dbIO_data_write (tmpFile,
1181 			(save->checksum[0] == '\0') ? SH_KEY_NULL : save->checksum);
1182   else
1183     sh_hash_pushdata_memory (tmpFile,
1184 			     (save->checksum[0] == '\0') ? SH_KEY_NULL : save->checksum);
1185 
1186   if (tmpFile->link_path) SH_FREE(tmpFile->link_path);
1187   SH_FREE(tmpFile);
1188   return;
1189 }
1190 
1191 extern int sh_util_hextobinary (char * binary, char * hex, int bytes);
1192 
sh_hash_db2pop(const char * key,struct store2db * save)1193 char * sh_hash_db2pop (const char * key, struct store2db * save)
1194 {
1195   size_t      len;
1196   char      * p;
1197   int         i;
1198   char      * retval = NULL;
1199   char        fileHash[KEY_LEN+1];
1200   file_type * tmpFile = SH_ALLOC(sizeof(file_type));
1201 
1202   save->size = 0;
1203 
1204   if (0 == sh_hash_get_it (key, tmpFile, fileHash))
1205     {
1206       save->val0 = tmpFile->size;
1207       save->val1 = tmpFile->mtime;
1208       save->val2 = tmpFile->ctime;
1209       save->val3 = tmpFile->atime;
1210 
1211       sl_strlcpy(save->checksum, fileHash, KEY_LEN+1);
1212 
1213       if (tmpFile->link_path && tmpFile->link_path[0] != '-')
1214 	{
1215 	  len = strlen(tmpFile->link_path);
1216 
1217 	  p = SH_ALLOC((len/2)+1);
1218 	  i = sh_util_hextobinary (p, tmpFile->link_path, len);
1219 
1220 	  if (i == 0)
1221 	    {
1222 	      save->size = (len/2);
1223 	      p[save->size] = '\0';
1224 	      retval = p;
1225 	    }
1226 	  else
1227 	    {
1228 	      SH_FREE(p);
1229 	      save->size = 0;
1230 	    }
1231 	}
1232       else
1233 	{
1234 	  save->size = 0;
1235 	}
1236     }
1237   else
1238     {
1239       save->size = -1;
1240       save->val0 = 0;
1241       save->val1 = 0;
1242       save->val2 = 0;
1243       save->val3 = 0;
1244     }
1245   if (tmpFile->link_path) SH_FREE(tmpFile->link_path);
1246   SH_FREE(tmpFile);
1247   return retval;
1248 }
1249 
1250 
1251 
1252 
1253 /******************************************************************
1254  *
1255  * Data entry in hash table
1256  *
1257  ******************************************************************/
sh_hash_push_int(file_type * buf,char * fileHash)1258 sh_file_t * sh_hash_push_int (file_type * buf, char * fileHash)
1259 {
1260   sh_file_t    * fp = NULL;
1261   sh_filestore_t p;
1262 
1263   size_t len;
1264   char * fullpath;
1265   char * linkpath;
1266   char * attr_string = NULL;
1267   char hashbuf[KEYBUF_SIZE];
1268 
1269   SL_ENTER(_("sh_hash_push_int"));
1270 
1271   if (!buf)
1272     SL_RETURN(NULL, _("sh_hash_push_int"));
1273 
1274   fp = SH_ALLOC(sizeof(sh_file_t));
1275 
1276   p.mark = REC_MAGIC;
1277   if (buf->attr_string)
1278     p.mark |= REC_FLAGS_ATTR;
1279   sl_strlcpy(p.c_mode,   buf->c_mode,   11);
1280   sl_strlcpy(p.c_group,  buf->c_group,  GROUP_MAX+1);
1281   sl_strlcpy(p.c_owner,  buf->c_owner,  USER_MAX+1);
1282   sl_strlcpy(p.checksum, fileHash,      KEY_LEN+1);
1283 #if defined(__linux__) || defined(HAVE_STAT_FLAGS)
1284   sl_strlcpy(p.c_attributes, buf->c_attributes, 13);
1285 #endif
1286 
1287 #if defined(__linux__) || defined(HAVE_STAT_FLAGS)
1288   p.attributes  = (UINT32) buf->attributes;
1289 #endif
1290   p.linkmode    = (UINT32) buf->linkmode;
1291   p.hardlinks   = (UINT32) buf->hardlinks;
1292   p.dev   = (UINT64) buf->dev;
1293   p.rdev  = (UINT64) buf->rdev;
1294   p.mode  = (UINT32) buf->mode;
1295   p.ino   = (UINT32) buf->ino;
1296   p.size  = (UINT64) buf->size;
1297   p.mtime = (UINT64) buf->mtime;
1298   p.atime = (UINT64) buf->atime;
1299   p.ctime = (UINT64) buf->ctime;
1300   p.owner = (UINT32) buf->owner;
1301   p.group = (UINT32) buf->group;
1302 
1303   p.checkflags = (UINT32) buf->check_flags;
1304 
1305   memcpy( &(*fp).theFile, &p, sizeof(sh_filestore_t) );
1306   fp->fflags    = 0;  /* init fflags */
1307   fp->modi_mask = 0L;
1308 
1309   if (buf->attr_string)
1310     attr_string = sh_util_strdup(buf->attr_string);
1311   fp->attr_string = attr_string;
1312 
1313   len = sl_strlen(buf->fullpath);
1314   if (len <= MAX_PATH_STORE)
1315     {
1316       fullpath = SH_ALLOC(len+1);
1317       sl_strlcpy(fullpath, buf->fullpath, len+1);
1318     }
1319   else
1320     {
1321       fullpath = SH_ALLOC(KEY_LEN + 1);
1322       sl_strlcpy(fullpath,
1323 		 sh_tiger_hash (buf->fullpath, TIGER_DATA, len,
1324 				hashbuf, sizeof(hashbuf)),
1325 		 KEY_LEN+1);
1326     }
1327   fp->fullpath  = fullpath;
1328 
1329   if (buf->link_path)
1330     {
1331       len = sl_strlen(buf->link_path);
1332       if (len <= MAX_PATH_STORE)
1333 	{
1334 	  linkpath = SH_ALLOC(len+1);
1335 	  sl_strlcpy(linkpath, buf->link_path, len+1);
1336 	}
1337       else
1338 	{
1339 	  linkpath = SH_ALLOC(KEY_LEN + 1);
1340 	  sl_strlcpy(linkpath,
1341 		     sh_tiger_hash (buf->link_path, TIGER_DATA, len,
1342 				    hashbuf, sizeof(hashbuf)),
1343 		     KEY_LEN+1);
1344 	}
1345       fp->linkpath  = linkpath;
1346     }
1347   else
1348     fp->linkpath  = NULL;
1349 
1350   SL_RETURN( fp, _("sh_hash_push_int"));
1351 }
1352 
1353 #ifdef HAVE_INTTYPES_H
1354 #include <inttypes.h>
1355 #else
1356 #ifdef HAVE_STDINT_H
1357 #include <stdint.h>
1358 #endif
1359 #endif
1360 
1361 #ifndef PRIu64
1362 #ifdef  HAVE_LONG_32
1363 #define PRIu64 "llu"
1364 #else
1365 #define PRIu64 "lu"
1366 #endif
1367 #endif
1368 
sh_hash_size_format()1369 char * sh_hash_size_format()
1370 {
1371   static char form_rval[81];
1372 
1373   SL_ENTER(_("sh_hash_size_format"));
1374 
1375 
1376 #ifdef SH_USE_XML
1377   sl_snprintf(form_rval, 80, _("%s%s%s%s%s"),
1378 	      _("size_old=\"%"), PRIu64, _("\" size_new=\"%"), PRIu64, "\" ");
1379 #else
1380   sl_snprintf(form_rval, 80, _("%s%s%s%s%s"),
1381 	      _("size_old=<%"), PRIu64, _(">, size_new=<%"), PRIu64, ">, ");
1382 #endif
1383 
1384   SL_RETURN( form_rval, _("sh_hash_size_format"));
1385 }
1386 
1387 
1388 #ifdef SH_USE_XML
all_items(file_type * theFile,char * fileHash,int is_new)1389 static char * all_items (file_type * theFile, char * fileHash, int is_new)
1390 {
1391   char timstr1c[32];
1392   char timstr1a[32];
1393   char timstr1m[32];
1394 
1395   char * tmp_lnk;
1396   char * format;
1397 
1398   char * tmp = SH_ALLOC(SH_MSG_BUF);
1399   char * msg = SH_ALLOC(SH_MSG_BUF);
1400 
1401   tmp[0] = '\0';
1402   msg[0] = '\0';
1403 
1404   if (report_checkflags != S_FALSE)
1405     {
1406       if (is_new)
1407 	format = _("checkflags_new=\"0%lo\" ");
1408       else
1409 	format = _("checkflags_old=\"0%lo\" ");
1410       sl_snprintf(tmp, SH_MSG_BUF, format,
1411 		  (unsigned long) theFile->check_flags);
1412       sl_strlcat(msg, tmp, SH_MSG_BUF);
1413     }
1414 
1415 #if defined(__linux__) || defined(HAVE_STAT_FLAGS)
1416   if (is_new)
1417     format = _("mode_new=\"%s\" attr_new=\"%s\" imode_new=\"%ld\" iattr_new=\"%ld\" ");
1418   else
1419     format = _("mode_old=\"%s\" attr_old=\"%s\" imode_old=\"%ld\" iattr_old=\"%ld\" ");
1420   sl_snprintf(tmp, SH_MSG_BUF, format,
1421 	      theFile->c_mode,
1422 	      theFile->c_attributes,
1423 	      (long) theFile->mode,
1424 	      (long) theFile->attributes
1425 	      );
1426 #else
1427   if (is_new)
1428     format = _("mode_new=\"%s\" imode_new=\"%ld\" ");
1429   else
1430     format = _("mode_old=\"%s\" imode_old=\"%ld\" ");
1431 
1432   sl_snprintf(tmp, SH_MSG_BUF, format,
1433 	      theFile->c_mode,
1434 	      (long) theFile->mode
1435 	      );
1436 #endif
1437   sl_strlcat(msg, tmp, SH_MSG_BUF);
1438 
1439   if (is_new)
1440     format = _("hardlinks_new=\"%lu\" ");
1441   else
1442     format = _("hardlinks_old=\"%lu\" ");
1443   sl_snprintf(tmp, SH_MSG_BUF, format,
1444 	      (unsigned long) theFile->hardlinks);
1445   sl_strlcat(msg, tmp, SH_MSG_BUF);
1446 
1447 
1448   if (is_new)
1449     format = _("idevice_new=\"%lu\" ");
1450   else
1451     format = _("idevice_old=\"%lu\" ");
1452   sl_snprintf(tmp, SH_MSG_BUF, format, (unsigned long) theFile->rdev);
1453   sl_strlcat(msg, tmp, SH_MSG_BUF);
1454 
1455 
1456   if (is_new)
1457     format = _("inode_new=\"%lu\" ");
1458   else
1459     format = _("inode_old=\"%lu\" ");
1460   sl_snprintf(tmp, SH_MSG_BUF, format, (unsigned long) theFile->ino);
1461   sl_strlcat(msg, tmp, SH_MSG_BUF);
1462 
1463   /*
1464    * also report device for prelude
1465    */
1466 #if defined(HAVE_LIBPRELUDE)
1467   if (is_new)
1468     format = _("dev_new=\"%lu,%lu\" ");
1469   else
1470     format = _("dev_old=\"%lu,%lu\" ");
1471   sl_snprintf(tmp, SH_MSG_BUF, format,
1472 	      (unsigned long) major(theFile->dev),
1473 	      (unsigned long) minor(theFile->dev));
1474   sl_strlcat(msg, tmp, SH_MSG_BUF);
1475 #endif
1476 
1477 
1478   if (is_new)
1479     format = _("owner_new=\"%s\" iowner_new=\"%ld\" ");
1480   else
1481     format = _("owner_old=\"%s\" iowner_old=\"%ld\" ");
1482   sl_snprintf(tmp, SH_MSG_BUF, format,
1483 	      theFile->c_owner, (long) theFile->owner);
1484   sl_strlcat(msg, tmp, SH_MSG_BUF);
1485 
1486 
1487   if (is_new)
1488     format = _("group_new=\"%s\" igroup_new=\"%ld\" ");
1489   else
1490     format = _("group_old=\"%s\" igroup_old=\"%ld\" ");
1491   sl_snprintf(tmp, SH_MSG_BUF, format,
1492 	      theFile->c_group, (long) theFile->group);
1493   sl_strlcat(msg, tmp, SH_MSG_BUF);
1494 
1495 
1496   if (is_new)
1497     sl_snprintf(tmp, SH_MSG_BUF, sh_hash_size_format(),
1498 		(UINT64) 0, (UINT64) theFile->size);
1499   else
1500     sl_snprintf(tmp, SH_MSG_BUF, sh_hash_size_format(),
1501 		(UINT64) theFile->size, (UINT64) 0);
1502   sl_strlcat(msg, tmp, SH_MSG_BUF);
1503 
1504 
1505   (void) sh_unix_gmttime (theFile->ctime, timstr1c,  sizeof(timstr1c));
1506   if (is_new)
1507     sl_snprintf(tmp, SH_MSG_BUF, _("ctime_new=\"%s\" "), timstr1c);
1508   else
1509     sl_snprintf(tmp, SH_MSG_BUF, _("ctime_old=\"%s\" "), timstr1c);
1510   sl_strlcat(msg, tmp, SH_MSG_BUF);
1511 
1512   (void) sh_unix_gmttime (theFile->atime, timstr1a,  sizeof(timstr1a));
1513   if (is_new)
1514     sl_snprintf(tmp, SH_MSG_BUF, _("atime_new=\"%s\" "), timstr1a);
1515   else
1516     sl_snprintf(tmp, SH_MSG_BUF, _("atime_old=\"%s\" "), timstr1a);
1517   sl_strlcat(msg, tmp, SH_MSG_BUF);
1518 
1519   (void) sh_unix_gmttime (theFile->mtime, timstr1m,  sizeof(timstr1m));
1520   if (is_new)
1521     sl_snprintf(tmp, SH_MSG_BUF, _("mtime_new=\"%s\" "), timstr1m);
1522   else
1523     sl_snprintf(tmp, SH_MSG_BUF, _("mtime_old=\"%s\" "), timstr1m);
1524   sl_strlcat(msg, tmp, SH_MSG_BUF);
1525 
1526   if (is_new)
1527     sl_snprintf(tmp, SH_MSG_BUF, _("chksum_new=\"%s\" "), fileHash);
1528   else
1529     sl_snprintf(tmp, SH_MSG_BUF, _("chksum_old=\"%s\" "), fileHash);
1530   sl_strlcat(msg, tmp, SH_MSG_BUF);
1531 
1532   if (theFile->c_mode[0] == 'l' ||
1533       (theFile->link_path != NULL && theFile->link_path[0] != '-'))
1534     {
1535       tmp_lnk     = sh_util_safe_name(theFile->link_path);
1536       if (tmp_lnk)
1537 	{
1538 	  if (is_new)
1539 	    sl_snprintf(tmp, SH_MSG_BUF, _("link_new=\"%s\" "), tmp_lnk);
1540 	  else
1541 	    sl_snprintf(tmp, SH_MSG_BUF, _("link_old=\"%s\" "), tmp_lnk);
1542 	  SH_FREE(tmp_lnk);
1543 	  sl_strlcat(msg, tmp, SH_MSG_BUF);
1544 	}
1545     }
1546 
1547   if (theFile->attr_string)
1548     {
1549       tmp_lnk     = sh_util_safe_name(theFile->attr_string);
1550       if (tmp_lnk)
1551 	{
1552 	  if (is_new)
1553 	    sl_snprintf(tmp, SH_MSG_BUF, _("acl_new=\"%s\" "), tmp_lnk);
1554 	  else
1555 	    sl_snprintf(tmp, SH_MSG_BUF, _("acl_old=\"%s\" "), tmp_lnk);
1556 	  SH_FREE(tmp_lnk);
1557 	  sl_strlcat(msg, tmp, SH_MSG_BUF);
1558 	}
1559     }
1560 
1561 
1562   SH_FREE(tmp);
1563   return (msg);
1564 }
1565 #else
all_items(file_type * theFile,char * fileHash,int is_new)1566 static char * all_items (file_type * theFile, char * fileHash, int is_new)
1567 {
1568   char timstr1c[32];
1569   char timstr1a[32];
1570   char timstr1m[32];
1571 
1572   char * tmp_lnk;
1573   char * format;
1574 
1575   char * tmp = SH_ALLOC(SH_MSG_BUF);
1576   char * msg = SH_ALLOC(SH_MSG_BUF);
1577 
1578   tmp[0] = '\0';
1579   msg[0] = '\0';
1580 
1581   if (report_checkflags == S_TRUE)
1582     {
1583       if (is_new)
1584 	format = _("checkflags_new=<0%lo> ");
1585       else
1586 	format = _("checkflags_old=<0%lo> ");
1587       sl_snprintf(tmp, SH_MSG_BUF, format,
1588 		  (unsigned long) theFile->check_flags);
1589       sl_strlcat(msg, tmp, SH_MSG_BUF);
1590     }
1591 
1592 
1593 #if defined(__linux__) || defined(HAVE_STAT_FLAGS)
1594   if (is_new)
1595     format = _("mode_new=<%s>, attr_new=<%s>, imode_new=<%ld>, iattr_new=<%ld>, ");
1596   else
1597     format = _("mode_old=<%s>, attr_old=<%s>, imode_old=<%ld>, iattr_old=<%ld>, ");
1598   sl_snprintf(tmp, SH_MSG_BUF, format,
1599 	      theFile->c_mode,
1600 	      theFile->c_attributes,
1601 	      (long) theFile->mode,
1602 	      (long) theFile->attributes
1603 	      );
1604 #else
1605   if (is_new)
1606     format = _("mode_new=<%s>, imode_new=<%ld>, ");
1607   else
1608     format = _("mode_old=<%s>, imode_old=<%ld>, ");
1609 
1610   sl_snprintf(tmp, SH_MSG_BUF, format,
1611 	      theFile->c_mode,
1612 	      (long) theFile->mode
1613 	      );
1614 #endif
1615   sl_strlcat(msg, tmp, SH_MSG_BUF);
1616 
1617   if (is_new)
1618     format = _("hardlinks_new=<%lu>, ");
1619   else
1620     format = _("hardlinks_old=<%lu>, ");
1621   sl_snprintf(tmp, SH_MSG_BUF, format,
1622 	      (unsigned long) theFile->hardlinks);
1623   sl_strlcat(msg, tmp, SH_MSG_BUF);
1624 
1625 
1626   if (is_new)
1627     format = _("idevice_new=<%lu>, ");
1628   else
1629     format = _("idevice_old=<%lu>, ");
1630   sl_snprintf(tmp, SH_MSG_BUF, format, (unsigned long) theFile->rdev);
1631   sl_strlcat(msg, tmp, SH_MSG_BUF);
1632 
1633 
1634   if (is_new)
1635     format = _("inode_new=<%lu>, ");
1636   else
1637     format = _("inode_old=<%lu>, ");
1638   sl_snprintf(tmp, SH_MSG_BUF, format, (unsigned long) theFile->ino);
1639   sl_strlcat(msg, tmp, SH_MSG_BUF);
1640 
1641 
1642   /*
1643    * also report device for prelude
1644    */
1645 #if defined(HAVE_LIBPRELUDE)
1646   if (is_new)
1647     format = _("dev_new=<%lu,%lu>, ");
1648   else
1649     format = _("dev_old=<%lu,%lu>, ");
1650   sl_snprintf(tmp, SH_MSG_BUF, format,
1651 	      (unsigned long) major(theFile->dev),
1652 	      (unsigned long) minor(theFile->dev));
1653   sl_strlcat(msg, tmp, SH_MSG_BUF);
1654 #endif
1655 
1656   if (is_new)
1657     format = _("owner_new=<%s>, iowner_new=<%ld>, ");
1658   else
1659     format = _("owner_old=<%s>, iowner_old=<%ld>, ");
1660   sl_snprintf(tmp, SH_MSG_BUF, format,
1661 	      theFile->c_owner, (long) theFile->owner);
1662   sl_strlcat(msg, tmp, SH_MSG_BUF);
1663 
1664 
1665   if (is_new)
1666     format = _("group_new=<%s>, igroup_new=<%ld>, ");
1667   else
1668     format = _("group_old=<%s>, igroup_old=<%ld>, ");
1669   sl_snprintf(tmp, SH_MSG_BUF, format,
1670 	      theFile->c_group, (long) theFile->group);
1671   sl_strlcat(msg, tmp, SH_MSG_BUF);
1672 
1673 
1674   if (is_new)
1675     sl_snprintf(tmp, SH_MSG_BUF, sh_hash_size_format(),
1676 		(UINT64) 0, (UINT64) theFile->size);
1677   else
1678     sl_snprintf(tmp, SH_MSG_BUF, sh_hash_size_format(),
1679 		(UINT64) theFile->size, (UINT64) 0);
1680   sl_strlcat(msg, tmp, SH_MSG_BUF);
1681 
1682 
1683   (void) sh_unix_gmttime (theFile->ctime, timstr1c,  sizeof(timstr1c));
1684   if (is_new)
1685     sl_snprintf(tmp, SH_MSG_BUF, _("ctime_new=<%s>, "), timstr1c);
1686   else
1687     sl_snprintf(tmp, SH_MSG_BUF, _("ctime_old=<%s>, "), timstr1c);
1688   sl_strlcat(msg, tmp, SH_MSG_BUF);
1689 
1690   (void) sh_unix_gmttime (theFile->atime, timstr1a,  sizeof(timstr1a));
1691   if (is_new)
1692     sl_snprintf(tmp, SH_MSG_BUF, _("atime_new=<%s>, "), timstr1a);
1693   else
1694     sl_snprintf(tmp, SH_MSG_BUF, _("atime_old=<%s>, "), timstr1a);
1695   sl_strlcat(msg, tmp, SH_MSG_BUF);
1696 
1697   (void) sh_unix_gmttime (theFile->mtime, timstr1m,  sizeof(timstr1m));
1698   if (is_new)
1699     sl_snprintf(tmp, SH_MSG_BUF, _("mtime_new=<%s>, "), timstr1m);
1700   else
1701     sl_snprintf(tmp, SH_MSG_BUF, _("mtime_old=<%s>, "), timstr1m);
1702   sl_strlcat(msg, tmp, SH_MSG_BUF);
1703 
1704   if (is_new)
1705     sl_snprintf(tmp, SH_MSG_BUF, _("chksum_new=<%s>"), fileHash);
1706   else
1707     sl_snprintf(tmp, SH_MSG_BUF, _("chksum_old=<%s>"), fileHash);
1708   sl_strlcat(msg, tmp, SH_MSG_BUF);
1709 
1710   if (theFile->c_mode[0] == 'l' ||
1711       (theFile->link_path != NULL && theFile->link_path[0] != '-'))
1712     {
1713       tmp_lnk     = sh_util_safe_name(theFile->link_path);
1714       if (tmp_lnk)
1715 	{
1716 	  if (is_new)
1717 	    sl_snprintf(tmp, SH_MSG_BUF, _(", link_new=<%s> "), tmp_lnk);
1718 	  else
1719 	    sl_snprintf(tmp, SH_MSG_BUF, _(", link_old=<%s> "), tmp_lnk);
1720 	  SH_FREE(tmp_lnk);
1721 	  sl_strlcat(msg, tmp, SH_MSG_BUF);
1722 	}
1723     }
1724 
1725   if (theFile->attr_string)
1726     {
1727       tmp_lnk     = sh_util_safe_name(theFile->attr_string);
1728       if (tmp_lnk)
1729 	{
1730 	  if (is_new)
1731 	    sl_snprintf(tmp, SH_MSG_BUF, _(", acl_new=<%s> "), tmp_lnk);
1732 	  else
1733 	    sl_snprintf(tmp, SH_MSG_BUF, _(", acl_old=<%s> "), tmp_lnk);
1734 	  SH_FREE(tmp_lnk);
1735 	  sl_strlcat(msg, tmp, SH_MSG_BUF);
1736 	}
1737     }
1738 
1739   SH_FREE(tmp);
1740   return (msg);
1741 }
1742 #endif
1743 
sh_hash_pushdata_memory(file_type * theFile,char * fileHash)1744 void sh_hash_pushdata_memory (file_type * theFile, char * fileHash)
1745 {
1746   sh_file_t * p;
1747 
1748   SL_ENTER(_("sh_hash_pushdata_memory"));
1749 
1750   p = sh_hash_push_int(theFile, fileHash);
1751   if (p)
1752     {
1753       SH_MUTEX_LOCK(mutex_hash);
1754       hashinsert (tab, p);
1755       p->modi_mask = theFile->check_flags;
1756       SH_MUTEX_UNLOCK(mutex_hash);
1757     }
1758 
1759   SL_RET0(_("sh_hash_pushdata_memory"));
1760 }
1761 
sh_hash_is_null_file(file_type * theFile)1762 int sh_hash_is_null_file(file_type * theFile)
1763 {
1764   if (theFile->hardlinks == SH_DEADFILE && theFile->mode  == 0 &&
1765       theFile->ino == 0                 && theFile->ctime == 0)
1766     {
1767       return S_TRUE;
1768     }
1769   return S_FALSE;
1770 }
1771 
sh_hash_is_null_record(sh_filestore_t * theFile)1772 int sh_hash_is_null_record(sh_filestore_t * theFile)
1773 {
1774   if (theFile->hardlinks == SH_DEADFILE && theFile->mode  == 0 &&
1775       theFile->ino == 0                 && theFile->ctime == 0)
1776     {
1777       return S_TRUE;
1778     }
1779   return S_FALSE;
1780 }
1781 
sh_hash_insert_null(char * str)1782 void sh_hash_insert_null(char * str)
1783 {
1784   file_type theFile = { 0, 0, {'\0'}, 0, 0, 0, 0, 0,
1785 #if defined(__linux__) || defined(HAVE_STAT_FLAGS)
1786 			0, {'\0'},
1787 #endif
1788 			{'\0'}, 0, {'\0'}, 0, {'\0'},
1789 			0, 0, 0, 0, 0, 0, 0, NULL,  0, {'\0'}, 0, NULL
1790   }; /* clang compiler bails out on standard conforming init with just {0} */
1791   char      fileHash[KEY_LEN+1];
1792   char      hashbuf[KEYBUF_SIZE];
1793 
1794   sl_strlcpy(fileHash, SH_KEY_NULL, sizeof(fileHash));
1795   theFile.hardlinks = SH_DEADFILE;
1796 
1797   if (sl_strlen(str) < PATH_MAX)
1798     sl_strlcpy(theFile.fullpath, str, PATH_MAX);
1799   else
1800      sl_strlcpy(theFile.fullpath,
1801 		sh_tiger_hash(str, TIGER_DATA, sl_strlen(str),
1802 			      hashbuf, sizeof(hashbuf)),
1803 		PATH_MAX);
1804 
1805   sh_hash_pushdata_memory(&theFile, fileHash);
1806   return;
1807 }
1808 
handle_notfound(int log_severity,int class,file_type * theFile,char * fileHash)1809 static int handle_notfound(int  log_severity, int class,
1810 			   file_type * theFile, char * fileHash)
1811 {
1812   sh_file_t * p;
1813   int         retval = 0;
1814 
1815   if (!theFile)
1816     return retval;
1817 
1818   if (S_FALSE == sh_ignore_chk_new(theFile->fullpath))
1819     {
1820       char * tmp = sh_util_safe_name(theFile->fullpath);
1821       char * str;
1822 
1823       sh_files_fixup_mask(class, &(theFile->check_flags));
1824       str = all_items (theFile, fileHash, 1);
1825 
1826       if (!sh_global_check_silent)
1827 	sh_error_handle (log_severity, FIL__, __LINE__, 0,
1828 			 MSG_FI_ADD2,
1829 			 tmp, str);
1830       ++sh.statistics.files_report;
1831       SH_FREE(str);
1832       SH_FREE(tmp);
1833     }
1834 
1835   if (sh.flag.reportonce == S_TRUE)
1836     SET_SH_FFLAG_REPORTED(theFile->file_reported);
1837 
1838   if (sh.flag.reportonce == S_TRUE && sh.flag.update == S_FALSE)
1839     {
1840       p = sh_hash_push_int(theFile, fileHash);
1841       if (p)
1842 	{
1843 	  hashinsert (tab, p);
1844 	  p->modi_mask = theFile->check_flags;
1845 	  p->theFile.checkflags = p->modi_mask;
1846 	}
1847     }
1848 
1849   else if (S_TRUE == sh.flag.update)
1850     {
1851       if (S_TRUE == sh_util_ask_update (theFile->fullpath))
1852 	{
1853 	  p = sh_hash_push_int(theFile, fileHash);
1854 	  if (p)
1855 	    {
1856 	      hashinsert (tab, p);
1857 	      p->modi_mask = theFile->check_flags;
1858 	      p->theFile.checkflags = p->modi_mask;
1859 	    }
1860 	}
1861       else
1862 	retval = 1;
1863     }
1864   return retval;
1865 }
1866 
1867 /*****************************************************************
1868  *
1869  * Compare a file with the database status.
1870  *
1871  *****************************************************************/
sh_hash_compdata(int class,file_type * theFile,char * fileHash,char * policy_override,int severity_override)1872 int sh_hash_compdata (int class, file_type * theFile, char * fileHash,
1873 		      char * policy_override, int severity_override)
1874 {
1875   char * msg;
1876   sh_file_t * p;
1877   char * tmp;
1878   char * tmp_path;
1879   char * tmp_lnk;
1880   char * tmp_lnk_old;
1881 
1882   char timstr1c[32];
1883   char timstr2c[32];
1884   char timstr1a[32];
1885   char timstr2a[32];
1886   char timstr1m[32];
1887   char timstr2m[32];
1888   char linkHash[KEY_LEN+1];
1889   char * linkComp;
1890   int  maxcomp;
1891   volatile int  checksum_flag = 0;
1892 
1893   char change_code[16];
1894   int  i;
1895 
1896   unsigned long modi_mask;
1897 
1898   char log_policy[32];
1899   volatile int  log_severity;
1900   char hashbuf[KEYBUF_SIZE];
1901   struct {
1902     unsigned long oldflags;
1903     unsigned long newflags;
1904   } cf_report;
1905 
1906   int  retval;
1907 
1908   SL_ENTER(_("sh_hash_compdata"));
1909 
1910   if (!theFile)
1911     SL_RETURN(0, _("sh_hash_compdata"));
1912 
1913  if (IsInit != 1) sh_hash_init();
1914 
1915   if (severity_override < 0)
1916     log_severity = ShDFLevel[class];
1917   else
1918     log_severity = severity_override;
1919 
1920   if (policy_override != NULL)
1921     sl_strlcpy (log_policy, policy_override, 32);
1922 
1923   /* --------  find the entry for the file ----------------       */
1924 
1925   SH_MUTEX_LOCK(mutex_hash);
1926 
1927   modi_mask = 0;
1928   retval    = 0;
1929 
1930   if (sl_strlen(theFile->fullpath) <= MAX_PATH_STORE)
1931     p = hashsearch(theFile->fullpath);
1932   else
1933     p = hashsearch( sh_tiger_hash(theFile->fullpath,
1934 				  TIGER_DATA,
1935 				  sl_strlen(theFile->fullpath),
1936 				  hashbuf, sizeof(hashbuf))
1937 		    );
1938 
1939 
1940   /* --------- Not found in database. ------------
1941    */
1942 
1943   if (p == NULL)
1944     {
1945       retval = handle_notfound(log_severity, class, theFile, fileHash);
1946       goto unlock_and_return;
1947     }
1948 
1949   /* ---------  Skip if we don't want to report changes. ------------
1950    */
1951 
1952   if (S_TRUE == sh_ignore_chk_mod(theFile->fullpath))
1953     {
1954       MODI_SET(theFile->check_flags, MODI_NOCHECK);
1955       p->modi_mask = theFile->check_flags;
1956       p->theFile.checkflags = p->modi_mask;
1957       goto unlock_and_return;
1958     }
1959 
1960   cf_report.oldflags = p->theFile.checkflags;
1961   cf_report.newflags = theFile->check_flags;
1962 
1963   p->modi_mask = theFile->check_flags;
1964   p->theFile.checkflags = p->modi_mask;
1965 
1966   /* initialize change_code */
1967   for (i = 0; i < 15; ++i)
1968     change_code[i] = '-';
1969   change_code[15] = '\0';
1970 
1971   TPT ((0, FIL__, __LINE__, _("file=<%s>, cs_old=<%s>, cs_new=<%s>\n"),
1972 	theFile->fullpath, fileHash, p->theFile.checksum));
1973 
1974   if ( (fileHash != NULL) &&
1975        (strncmp (fileHash, p->theFile.checksum, KEY_LEN) != 0) &&
1976        (theFile->check_flags & MODI_CHK) != 0)
1977     {
1978       checksum_flag = 1;
1979 
1980       if ((theFile->check_flags & MODI_SGROW) == 0)
1981 	{
1982 	  modi_mask |= MODI_CHK;
1983 	  change_code[0] = 'C';
1984 	  TPT ((0, FIL__, __LINE__, _("mod=<checksum>")));
1985 	}
1986       else
1987 	{
1988 	  if (0 != strncmp (&fileHash[KEY_LEN + 1], p->theFile.checksum, KEY_LEN))
1989 	    {
1990 	      if (S_FALSE == sh_check_rotated_log (theFile->fullpath, (UINT64) p->theFile.size,
1991 						   (UINT64) p->theFile.ino, p->theFile.checksum,
1992 						   p->theFile.checkflags))
1993 		{
1994 		  modi_mask |= MODI_CHK;
1995 		  change_code[0] = 'C';
1996 		  TPT ((0, FIL__, __LINE__, _("mod=<checksum>")));
1997 		}
1998 	      else
1999 		{
2000 		  /* logfile has been rotated */
2001 		  p->theFile.size  = theFile->size;
2002 		  p->theFile.ino   = theFile->ino;
2003 		  sl_strlcpy(p->theFile.checksum, fileHash, KEY_LEN+1);
2004 		}
2005 	    }
2006 	  else
2007 	    {
2008 	      p->theFile.size  = theFile->size;
2009 	      sl_strlcpy(p->theFile.checksum, fileHash, KEY_LEN+1);
2010 	    }
2011 	}
2012     }
2013 
2014   if (p->theFile.c_mode[0] == 'l')
2015     {
2016       if (!(theFile->link_path) &&
2017 	  (theFile->check_flags & MODI_LNK) != 0)
2018 	{
2019 	  linkComp = NULL;
2020 	  modi_mask |= MODI_LNK;
2021 	  change_code[1] = 'L';
2022 	  TPT ((0, FIL__, __LINE__, _("mod=<link>")));
2023 	}
2024       else
2025 	{
2026 	  if (sl_strlen(theFile->link_path) >= MAX_PATH_STORE)
2027 	    {
2028 	      sl_strlcpy(linkHash,
2029 			 sh_tiger_hash(theFile->link_path,
2030 				       TIGER_DATA,
2031 				       sl_strlen(theFile->link_path),
2032 				       hashbuf, sizeof(hashbuf)),
2033 			 MAX_PATH_STORE+1);
2034 	      linkComp = linkHash;
2035 	      maxcomp  = KEY_LEN;
2036 	    }
2037 	  else
2038 	    {
2039 	      linkComp = theFile->link_path;
2040 	      maxcomp  = MAX_PATH_STORE;
2041 	    }
2042 
2043 	  if ( sl_strncmp (linkComp, p->linkpath, maxcomp) != 0 &&
2044 	       (theFile->check_flags & MODI_LNK) != 0)
2045 	    {
2046 	      modi_mask |= MODI_LNK;
2047 	      change_code[1] = 'L';
2048 	      TPT ((0, FIL__, __LINE__, _("mod=<link>")));
2049 	    }
2050 	}
2051     }
2052 
2053   if (p->theFile.c_mode[0] == 'c' || p->theFile.c_mode[0] == 'b')
2054     {
2055       if ( ( major(theFile->rdev) != major((dev_t)p->theFile.rdev) ||
2056 	     minor(theFile->rdev) != minor((dev_t)p->theFile.rdev) ) &&
2057 	   (theFile->check_flags & MODI_RDEV) != 0)
2058 	{
2059 	  modi_mask |= MODI_RDEV;
2060 	  change_code[2] = 'D';
2061 	  TPT ((0, FIL__, __LINE__, _("mod=<rdev>")));
2062 	}
2063     }
2064 
2065   /* cast to UINT32 in case ino_t is not 32bit
2066    */
2067   if ( (UINT32) theFile->ino != (UINT32) p->theFile.ino  &&
2068        (theFile->check_flags & MODI_INO) != 0)
2069     {
2070       if ((theFile->check_flags & MODI_SGROW) == 0)
2071 	{
2072 	  modi_mask |= MODI_INO;
2073 	  change_code[3] = 'I';
2074 	  TPT ((0, FIL__, __LINE__, _("mod=<inode>")));
2075 	}
2076       else
2077 	{
2078 	  /* growing log, checksum ok but inode changed
2079 	   */
2080 	  if (checksum_flag == 0)
2081 	    {
2082 	      if (S_FALSE == sh_check_rotated_log (theFile->fullpath, (UINT64) p->theFile.size,
2083 						   (UINT64) p->theFile.ino, p->theFile.checksum,
2084 						   p->theFile.checkflags))
2085 		{
2086 		  modi_mask |= MODI_INO;
2087 		  change_code[3] = 'I';
2088 		  TPT ((0, FIL__, __LINE__, _("mod=<inode>")));
2089 		}
2090 	      else
2091 		{
2092 		  /* logfile has been rotated */
2093 		  p->theFile.size  = theFile->size;
2094 		  p->theFile.ino   = theFile->ino;
2095 		  sl_strlcpy(p->theFile.checksum, fileHash, KEY_LEN+1);
2096 		}
2097 	    }
2098 	  else
2099 	    {
2100 	      modi_mask |= MODI_INO;
2101 	      change_code[3] = 'I';
2102 	      TPT ((0, FIL__, __LINE__, _("mod=<inode>")));
2103 	    }
2104 	}
2105     }
2106 
2107   if ( theFile->hardlinks != (nlink_t) p->theFile.hardlinks &&
2108        (theFile->check_flags & MODI_HLN) != 0)
2109     {
2110       modi_mask |= MODI_HLN;
2111       change_code[4] = 'H';
2112       TPT ((0, FIL__, __LINE__, _("mod=<hardlink>")));
2113     }
2114 
2115 
2116   if ( (  (theFile->mode != p->theFile.mode)
2117 #if defined(USE_ACL) || defined(USE_XATTR)
2118 	  || ( (sh_unix_check_selinux|sh_unix_check_acl) &&
2119 	       (
2120 		(theFile->attr_string == NULL && p->attr_string != NULL) ||
2121 		(theFile->attr_string != NULL && p->attr_string == NULL) ||
2122 		(theFile->attr_string != NULL && 0 != strcmp(theFile->attr_string, p->attr_string))
2123 		)
2124 	       )
2125 #endif
2126 #if defined(__linux__) || defined(HAVE_STAT_FLAGS)
2127           || (theFile->attributes != p->theFile.attributes)
2128 #endif
2129 	  )
2130        && (theFile->check_flags & MODI_MOD) != 0)
2131     {
2132       modi_mask |= MODI_MOD;
2133       change_code[5] = 'M';
2134       TPT ((0, FIL__, __LINE__, _("mod=<mode>")));
2135       /*
2136        * report link path if switch link/no link
2137        */
2138       if ((theFile->check_flags & MODI_LNK) != 0 &&
2139 	  (theFile->c_mode[0] != p->theFile.c_mode[0]) &&
2140 	  (theFile->c_mode[0] == 'l' || p->theFile.c_mode[0] == 'l'))
2141 	{
2142 	  modi_mask |= MODI_LNK;
2143 	  change_code[1] = 'L';
2144 	  TPT ((0, FIL__, __LINE__, _("mod=<link>")));
2145 	}
2146     }
2147 
2148   if ( theFile->owner != (uid_t) p->theFile.owner &&
2149        (theFile->check_flags & MODI_USR) != 0)
2150     {
2151       modi_mask |= MODI_USR;
2152       change_code[6] = 'U';
2153       TPT ((0, FIL__, __LINE__, _("mod=<user>")));
2154     }
2155 
2156   if ( theFile->group != (gid_t) p->theFile.group &&
2157        (theFile->check_flags & MODI_GRP) != 0)
2158     {
2159       modi_mask |= MODI_GRP;
2160       change_code[7] = 'G';
2161       TPT ((0, FIL__, __LINE__, _("mod=<group>")));
2162     }
2163 
2164 
2165   if ( theFile->mtime != (time_t) p->theFile.mtime &&
2166        (theFile->check_flags & MODI_MTM) != 0)
2167     {
2168       modi_mask |= MODI_MTM;
2169       change_code[8] = 'T';
2170       TPT ((0, FIL__, __LINE__, _("mod=<mtime>")));
2171     }
2172 
2173   if ( (theFile->check_flags & MODI_ATM) != 0 &&
2174        theFile->atime != (time_t) p->theFile.atime)
2175     {
2176       modi_mask |= MODI_ATM;
2177       change_code[8] = 'T';
2178       TPT ((0, FIL__, __LINE__, _("mod=<atime>")));
2179     }
2180 
2181 
2182   /* Resetting the access time will set a new ctime. Thus, either we ignore
2183    * the access time or the ctime for NOIGNORE
2184    */
2185   if ( theFile->ctime != (time_t) p->theFile.ctime &&
2186        (theFile->check_flags & MODI_CTM) != 0)
2187     {
2188       modi_mask |= MODI_CTM;
2189       change_code[8] = 'T';
2190       TPT ((0, FIL__, __LINE__, _("mod=<ctime>")));
2191     }
2192 
2193   if ( theFile->size != (off_t) p->theFile.size &&
2194        (theFile->check_flags & MODI_SIZ) != 0)
2195     {
2196       if ((theFile->check_flags & MODI_SGROW) == 0 ||
2197 	  theFile->size < (off_t) p->theFile.size)
2198 	{
2199 	  modi_mask |= MODI_SIZ;
2200 	  change_code[9] = 'S';
2201 	  TPT ((0, FIL__, __LINE__, _("mod=<size>")));
2202 	}
2203     }
2204   change_code[10] = '\0';
2205 
2206   /* --- Directories special case ---
2207    */
2208   if (p->theFile.c_mode[0] == 'd'                               &&
2209       0 == (modi_mask & ~(MODI_SIZ|MODI_ATM|MODI_CTM|MODI_MTM)) &&
2210       sh_loosedircheck == S_TRUE)
2211     {
2212       modi_mask = 0;
2213     }
2214 
2215   /* --- Report full details. ---
2216    */
2217   if (modi_mask != 0 && sh.flag.fulldetail == S_TRUE)
2218     {
2219       if ((theFile->check_flags & MODI_ATM) == 0)
2220 	modi_mask = MASK_READONLY_;
2221       else
2222 	modi_mask = MASK_NOIGNORE_;
2223     }
2224 
2225   /* --- Report on modified files. ---
2226    */
2227   if (modi_mask != 0 && (!SH_FFLAG_REPORTED_SET(p->fflags)))
2228     {
2229       tmp = SH_ALLOC(SH_MSG_BUF);
2230       msg = SH_ALLOC(SH_MSG_BUF);
2231       msg[0] = '\0';
2232 
2233       sh_files_fixup_mask(class, &(cf_report.newflags));
2234 
2235       if ( (report_checkflags != S_FALSE) && (cf_report.oldflags != cf_report.newflags))
2236 	{
2237 	  sl_snprintf(tmp, SH_MSG_BUF,
2238 #ifdef SH_USE_XML
2239 		      _("checkflags_old=\"0%lo\" checkflags_new=\"0%lo\" "),
2240 #else
2241 		      _("checkflags_old=<0%lo>, checkflags_new=<0%lo>, "),
2242 #endif
2243 		      cf_report.oldflags,  cf_report.newflags);
2244 	  sl_strlcat(msg, tmp, SH_MSG_BUF);
2245 	}
2246 
2247       if (   ((modi_mask & MODI_MOD) != 0)
2248 #if defined(HAVE_LIBPRELUDE)
2249 	     || ((modi_mask & MODI_USR) != 0)
2250 	     || ((modi_mask & MODI_GRP) != 0)
2251 #endif
2252 	     )
2253 	{
2254 #if defined(__linux__) || defined(HAVE_STAT_FLAGS)
2255 	  sl_snprintf(tmp, SH_MSG_BUF,
2256 #ifdef SH_USE_XML
2257 		      _("mode_old=\"%s\" mode_new=\"%s\" attr_old=\"%s\" attr_new=\"%s\" imode_old=\"%ld\" imode_new=\"%ld\" iattr_old=\"%ld\" iattr_new=\"%ld\" "),
2258 #else
2259 		      _("mode_old=<%s>, mode_new=<%s>, attr_old=<%s>, attr_new=<%s>, "),
2260 #endif
2261 		      p->theFile.c_mode, theFile->c_mode,
2262 		      p->theFile.c_attributes, theFile->c_attributes
2263 #ifdef SH_USE_XML
2264 		      , (long) p->theFile.mode, (long) theFile->mode,
2265 		      (long) p->theFile.attributes,
2266 		      (long) theFile->attributes
2267 #endif
2268 		      );
2269 #else
2270 #ifdef SH_USE_XML
2271 	  sl_snprintf(tmp, SH_MSG_BUF,
2272 		      _("mode_old=\"%s\" mode_new=\"%s\" imode_old=\"%ld\" imode_new=\"%ld\" "),
2273 		      p->theFile.c_mode, theFile->c_mode,
2274 		      (long) p->theFile.mode, (long) theFile->mode);
2275 #else
2276 	  sl_snprintf(tmp, SH_MSG_BUF, _("mode_old=<%s>, mode_new=<%s>, "),
2277 		      p->theFile.c_mode, theFile->c_mode);
2278 #endif
2279 #endif
2280 	  sl_strlcat(msg, tmp, SH_MSG_BUF);
2281 
2282 #if defined(USE_ACL) || defined(USE_XATTR)
2283 	  if (theFile->attr_string != NULL || p->attr_string != NULL)
2284 	    {
2285 	      sl_snprintf(tmp, SH_MSG_BUF,
2286 #ifdef SH_USE_XML
2287 			  _("acl_old=\"%s\" acl_new=\"%s\" "),
2288 #else
2289 			  _("acl_old=<%s>, acl_new=<%s>, "),
2290 #endif
2291 			  (p->attr_string)       ? p->attr_string       : _("none"),
2292 			  (theFile->attr_string) ? theFile->attr_string : _("none"));
2293 
2294 	      sl_strlcat(msg, tmp, SH_MSG_BUF);
2295 	    }
2296 #endif
2297 
2298 	  if ((modi_mask & MODI_MOD) != 0)
2299 	    {
2300 	      /*
2301 	       * We postpone update if sh.flag.update == S_TRUE because
2302 	       * in interactive mode the user may not accept the change.
2303 	       */
2304 	      if (sh.flag.reportonce == S_TRUE && sh.flag.update == S_FALSE)
2305 		{
2306 		  sl_strlcpy(p->theFile.c_mode, theFile->c_mode, 11);
2307 		  p->theFile.mode = theFile->mode;
2308 #if defined(__linux__) || defined(HAVE_STAT_FLAGS)
2309 		  sl_strlcpy(p->theFile.c_attributes,theFile->c_attributes,16);
2310 		  p->theFile.attributes = theFile->attributes;
2311 #endif
2312 #if defined(USE_ACL) || defined(USE_XATTR)
2313 		  if      (p->attr_string == NULL && theFile->attr_string != NULL)
2314 		    { p->attr_string = sh_util_strdup (theFile->attr_string); }
2315 		  else if (p->attr_string != NULL && theFile->attr_string == NULL)
2316 		    { SH_FREE(p->attr_string); p->attr_string = NULL; }
2317 		  else if (theFile->attr_string != NULL && p->attr_string != NULL)
2318 		    {
2319 		      if (0 != strcmp(theFile->attr_string, p->attr_string))
2320 			{
2321 			  SH_FREE(p->attr_string);
2322 			  p->attr_string = sh_util_strdup (theFile->attr_string);
2323 			}
2324 		    }
2325 #endif
2326 		}
2327 	    }
2328 
2329 	}
2330 
2331       if ((modi_mask & MODI_HLN) != 0)
2332 	{
2333 	  sl_snprintf(tmp, SH_MSG_BUF,
2334 #ifdef SH_USE_XML
2335 		      _("hardlinks_old=\"%lu\" hardlinks_new=\"%lu\" "),
2336 #else
2337 		      _("hardlinks_old=<%lu>, hardlinks_new=<%lu>, "),
2338 #endif
2339 		      (unsigned long) p->theFile.hardlinks,
2340 		      (unsigned long) theFile->hardlinks);
2341 	  sl_strlcat(msg, tmp, SH_MSG_BUF);
2342 
2343 	  if (sh.flag.reportonce == S_TRUE && sh.flag.update == S_FALSE)
2344 	    p->theFile.hardlinks = theFile->hardlinks;
2345 	}
2346 
2347       if ((modi_mask & MODI_RDEV) != 0)
2348 	{
2349 	  sl_snprintf(tmp, SH_MSG_BUF,
2350 #ifdef SH_USE_XML
2351 		      _("device_old=\"%lu,%lu\" device_new=\"%lu,%lu\" idevice_old=\"%lu\" idevice_new=\"%lu\" "),
2352 #else
2353 		      _("device_old=<%lu,%lu>, device_new=<%lu,%lu>, "),
2354 #endif
2355 		      (unsigned long) major(p->theFile.rdev),
2356 		      (unsigned long) minor(p->theFile.rdev),
2357 		      (unsigned long) major(theFile->rdev),
2358 		      (unsigned long) minor(theFile->rdev)
2359 #ifdef SH_USE_XML
2360 		      , (unsigned long) p->theFile.rdev,
2361 		      (unsigned long) theFile->rdev
2362 #endif
2363 		      );
2364 	  sl_strlcat(msg, tmp, SH_MSG_BUF);
2365 
2366 	  if (sh.flag.reportonce == S_TRUE && sh.flag.update == S_FALSE)
2367 	    p->theFile.rdev = theFile->rdev;
2368 	}
2369 
2370       if ((modi_mask & MODI_INO) != 0)
2371 	{
2372 	  sl_snprintf(tmp, SH_MSG_BUF,
2373 #ifdef SH_USE_XML
2374 		      _("inode_old=\"%lu\" inode_new=\"%lu\" "),
2375 #else
2376 		      _("inode_old=<%lu>, inode_new=<%lu>, "),
2377 #endif
2378 		      (unsigned long) p->theFile.ino,
2379 		      (unsigned long) theFile->ino);
2380 	  sl_strlcat(msg, tmp, SH_MSG_BUF);
2381 
2382 	  if (sh.flag.reportonce == S_TRUE && sh.flag.update == S_FALSE)
2383 	    {
2384 	      p->theFile.ino = theFile->ino;
2385 	      p->theFile.dev = theFile->dev;
2386 	    }
2387 	}
2388 
2389 
2390       /*
2391        * also report device for prelude
2392        */
2393 #if defined(HAVE_LIBPRELUDE)
2394       if ((modi_mask & MODI_INO) != 0)
2395 	{
2396 	  sl_snprintf(tmp, SH_MSG_BUF,
2397 #ifdef SH_USE_XML
2398 		      _("dev_old=\"%lu,%lu\" dev_new=\"%lu,%lu\" "),
2399 #else
2400 		      _("dev_old=<%lu,%lu>, dev_new=<%lu,%lu>, "),
2401 #endif
2402 		      (unsigned long) major(p->theFile.dev),
2403 		      (unsigned long) minor(p->theFile.dev),
2404 		      (unsigned long) major(theFile->dev),
2405 		      (unsigned long) minor(theFile->dev)
2406 		      );
2407 	  sl_strlcat(msg, tmp, SH_MSG_BUF);
2408 
2409 	  if (sh.flag.reportonce == S_TRUE && sh.flag.update == S_FALSE)
2410 	    p->theFile.dev = theFile->dev;
2411 	}
2412 #endif
2413 
2414       if (   ((modi_mask & MODI_USR) != 0)
2415 #if defined(HAVE_LIBPRELUDE)
2416 	  || ((modi_mask & MODI_MOD) != 0)
2417 #endif
2418 	  )
2419 	{
2420 #ifdef SH_USE_XML
2421 	  sl_snprintf(tmp, SH_MSG_BUF,
2422 		      _("owner_old=\"%s\" owner_new=\"%s\" iowner_old=\"%ld\" iowner_new=\"%ld\" "),
2423 #else
2424 	  sl_snprintf(tmp, SH_MSG_BUF,
2425 		      _("owner_old=<%s>, owner_new=<%s>, iowner_old=<%ld>, iowner_new=<%ld>, "),
2426 #endif
2427 		      p->theFile.c_owner, theFile->c_owner,
2428 		      (long) p->theFile.owner, (long) theFile->owner
2429 		      );
2430 	  sl_strlcat(msg, tmp, SH_MSG_BUF);
2431 
2432 	  if ((modi_mask & MODI_USR) != 0) {
2433 	    if (sh.flag.reportonce == S_TRUE && sh.flag.update == S_FALSE)
2434 	      {
2435 		sl_strlcpy(p->theFile.c_owner, theFile->c_owner, USER_MAX+2);
2436 		p->theFile.owner = theFile->owner;
2437 	      }
2438 	  }
2439 	}
2440 
2441       if (   ((modi_mask & MODI_GRP) != 0)
2442 #if defined(HAVE_LIBPRELUDE)
2443 	  || ((modi_mask & MODI_MOD) != 0)
2444 #endif
2445 	  )
2446 	{
2447 #ifdef SH_USE_XML
2448 	  sl_snprintf(tmp, SH_MSG_BUF,
2449 		      _("group_old=\"%s\" group_new=\"%s\" igroup_old=\"%ld\" igroup_new=\"%ld\" "),
2450 		      p->theFile.c_group, theFile->c_group,
2451 		      (long) p->theFile.group, (long) theFile->group);
2452 #else
2453 	  sl_snprintf(tmp, SH_MSG_BUF,
2454 		      _("group_old=<%s>, group_new=<%s>, igroup_old=<%ld>, igroup_new=<%ld>, "),
2455 		      p->theFile.c_group, theFile->c_group,
2456 		      (long) p->theFile.group, (long) theFile->group);
2457 #endif
2458 
2459 	  sl_strlcat(msg, tmp, SH_MSG_BUF);
2460 
2461           if ((modi_mask & MODI_GRP) != 0) {
2462 	    if (sh.flag.reportonce == S_TRUE && sh.flag.update == S_FALSE)
2463 	      {
2464 		sl_strlcpy(p->theFile.c_group, theFile->c_group, GROUP_MAX+2);
2465 		p->theFile.group = theFile->group;
2466 	      }
2467 	  }
2468 	}
2469 
2470       if ((modi_mask & MODI_SIZ) != 0)
2471 	{
2472 	  sl_snprintf(tmp, SH_MSG_BUF, sh_hash_size_format(),
2473 		      (UINT64) p->theFile.size,
2474 		      (UINT64) theFile->size);
2475 	  sl_strlcat(msg, tmp, SH_MSG_BUF);
2476 
2477 	  if (sh.flag.reportonce == S_TRUE && sh.flag.update == S_FALSE)
2478 	    p->theFile.size = theFile->size;
2479 	}
2480 
2481       if ((modi_mask & MODI_CTM) != 0)
2482 	{
2483 	  (void) sh_unix_gmttime (p->theFile.ctime, timstr1c, sizeof(timstr1c));
2484 	  (void) sh_unix_gmttime (theFile->ctime,   timstr2c, sizeof(timstr2c));
2485 #ifdef SH_USE_XML
2486 	  sl_snprintf(tmp, SH_MSG_BUF, _("ctime_old=\"%s\" ctime_new=\"%s\" "),
2487 		      timstr1c, timstr2c);
2488 #else
2489 	  sl_snprintf(tmp, SH_MSG_BUF, _("ctime_old=<%s>, ctime_new=<%s>, "),
2490 		      timstr1c, timstr2c);
2491 #endif
2492 	  sl_strlcat(msg, tmp, SH_MSG_BUF);
2493 
2494 	  if (sh.flag.reportonce == S_TRUE && sh.flag.update == S_FALSE)
2495 	    p->theFile.ctime = theFile->ctime;
2496 	}
2497 
2498       if ((modi_mask & MODI_ATM) != 0)
2499 	{
2500 	  (void) sh_unix_gmttime (p->theFile.atime, timstr1a, sizeof(timstr1a));
2501 	  (void) sh_unix_gmttime (theFile->atime,   timstr2a, sizeof(timstr2a));
2502 #ifdef SH_USE_XML
2503 	  sl_snprintf(tmp, SH_MSG_BUF, _("atime_old=\"%s\" atime_new=\"%s\" "),
2504 		      timstr1a, timstr2a);
2505 #else
2506 	  sl_snprintf(tmp, SH_MSG_BUF, _("atime_old=<%s>, atime_new=<%s>, "),
2507 		      timstr1a, timstr2a);
2508 #endif
2509 	  sl_strlcat(msg, tmp, SH_MSG_BUF);
2510 
2511 	  if (sh.flag.reportonce == S_TRUE && sh.flag.update == S_FALSE)
2512 	    p->theFile.atime = theFile->atime;
2513 	}
2514 
2515       if ((modi_mask & MODI_MTM) != 0)
2516 	{
2517 	  (void) sh_unix_gmttime (p->theFile.mtime, timstr1m, sizeof(timstr1m));
2518 	  (void) sh_unix_gmttime (theFile->mtime,   timstr2m, sizeof(timstr2m));
2519 #ifdef SH_USE_XML
2520 	  sl_snprintf(tmp, SH_MSG_BUF, _("mtime_old=\"%s\" mtime_new=\"%s\" "),
2521 		      timstr1m, timstr2m);
2522 #else
2523 	  sl_snprintf(tmp, SH_MSG_BUF, _("mtime_old=<%s>, mtime_new=<%s>, "),
2524 		      timstr1m, timstr2m);
2525 #endif
2526 	  sl_strlcat(msg, tmp, SH_MSG_BUF);
2527 
2528 	  if (sh.flag.reportonce == S_TRUE && sh.flag.update == S_FALSE)
2529 	    p->theFile.mtime = theFile->mtime;
2530 	}
2531 
2532 
2533       if ((modi_mask & MODI_CHK) != 0)
2534 	{
2535 	  sl_snprintf(tmp, SH_MSG_BUF,
2536 #ifdef SH_USE_XML
2537 		      _("chksum_old=\"%s\" chksum_new=\"%s\" "),
2538 #else
2539 		      _("chksum_old=<%s>, chksum_new=<%s>, "),
2540 #endif
2541 		      p->theFile.checksum, fileHash);
2542 	  sl_strlcat(msg, tmp, SH_MSG_BUF);
2543 
2544 	  if (sh.flag.reportonce == S_TRUE && sh.flag.update == S_FALSE)
2545 	    {
2546 	      sl_strlcpy(p->theFile.checksum, fileHash, KEY_LEN+1);
2547 	      if ((theFile->check_flags & MODI_SGROW) != 0)
2548 		p->theFile.size  = theFile->size;
2549 	    }
2550 
2551 
2552 	  if (theFile->c_mode[0] != 'l' && theFile->link_path &&
2553 	      strlen(theFile->link_path) > 2)
2554 	    modi_mask |= MODI_LNK;
2555 	}
2556 
2557 
2558       if ((modi_mask & MODI_LNK) != 0 /* && theFile->c_mode[0] == 'l' */)
2559 	{
2560 	  if (theFile->link_path)
2561 	    tmp_lnk     = sh_util_safe_name(theFile->link_path);
2562 	  else
2563 	    tmp_lnk     = sh_util_strdup("-");
2564 	  if (p->linkpath)
2565 	    tmp_lnk_old = sh_util_safe_name(p->linkpath);
2566 	  else
2567 	    tmp_lnk_old = sh_util_strdup("-");
2568 #ifdef SH_USE_XML
2569 	  sl_snprintf(tmp, SH_MSG_BUF, _("link_old=\"%s\" link_new=\"%s\" "),
2570 		      tmp_lnk_old, tmp_lnk);
2571 #else
2572 	  sl_snprintf(tmp, SH_MSG_BUF, _("link_old=<%s>, link_new=<%s>, "),
2573 		      tmp_lnk_old, tmp_lnk);
2574 #endif
2575 	  SH_FREE(tmp_lnk);
2576 	  SH_FREE(tmp_lnk_old);
2577 	  sl_strlcat(msg, tmp, SH_MSG_BUF);
2578 
2579 	  if (sh.flag.reportonce == S_TRUE && sh.flag.update == S_FALSE)
2580 	    {
2581 	      if (p->linkpath != NULL)
2582 		SH_FREE(p->linkpath);
2583 	      if (!(theFile->link_path))
2584 		p->linkpath = sh_util_strdup("-");
2585 	      else
2586 		p->linkpath = sh_util_strdup(theFile->link_path);
2587 	    }
2588 	}
2589 
2590       if (MODI_AUDIT_ENABLED(theFile->check_flags))
2591 	{
2592 	  char result[256];
2593 
2594 	  sh_error_handle (SH_ERR_INFO, FIL__, __LINE__,
2595 			   0, MSG_E_SUBGPATH,
2596 			   _("Fetching audit record"),
2597 			   _("sh_hash"),  theFile->fullpath );
2598 
2599 	  if (NULL != sh_audit_fetch (theFile->fullpath, theFile->mtime, theFile->ctime, theFile->atime,
2600 				      result, sizeof(result)))
2601 	    {
2602 #ifdef SH_USE_XML
2603 	      sl_strlcat(msg, _("obj=\""), SH_MSG_BUF);
2604 #else
2605 	      sl_strlcat(msg, _("obj=<"), SH_MSG_BUF);
2606 #endif
2607 
2608 	      sl_strlcat(msg, result, SH_MSG_BUF);
2609 
2610 #ifdef SH_USE_XML
2611 	      sl_strlcat(msg, _("\" "), SH_MSG_BUF);
2612 #else
2613 	      sl_strlcat(msg, _(">"), SH_MSG_BUF);
2614 #endif
2615 	    }
2616 	}
2617 
2618       /****************************************************
2619        *
2620        * REPORT on file change
2621        *
2622        ****************************************************/
2623       tmp_path = sh_util_safe_name(theFile->fullpath);
2624       if (!sh_global_check_silent)
2625 	sh_error_handle(log_severity, FIL__, __LINE__,
2626 			(long) modi_mask, MSG_FI_CHAN,
2627 			(policy_override == NULL) ? _(policy[class]):log_policy,
2628 			change_code, tmp_path, msg);
2629       ++sh.statistics.files_report;
2630 
2631       SH_FREE(tmp_path);
2632       SH_FREE(tmp);
2633       SH_FREE(msg);
2634 
2635       if (S_TRUE  == sh.flag.update)
2636 	{
2637 	  if (S_FALSE == sh_util_ask_update(theFile->fullpath))
2638 	    {
2639 	      /* user does not want to update, thus we replace
2640 	       * with data from the baseline database
2641 	       */
2642 	      sl_strlcpy(theFile->c_mode, p->theFile.c_mode, 11);
2643 	      theFile->mode  =  p->theFile.mode;
2644 #if defined(__linux__) || defined(HAVE_STAT_FLAGS)
2645 	      sl_strlcpy(theFile->c_attributes, p->theFile.c_attributes, 16);
2646 	      theFile->attributes =  p->theFile.attributes;
2647 #endif
2648 #if defined(USE_ACL) || defined(USE_XATTR)
2649 	      if      (theFile->attr_string == NULL && p->attr_string != NULL)
2650 		{ theFile->attr_string = sh_util_strdup (p->attr_string); }
2651 	      else if (theFile->attr_string != NULL && p->attr_string == NULL)
2652 		{ SH_FREE(theFile->attr_string); theFile->attr_string = NULL; }
2653 	      else if (theFile->attr_string != NULL && p->attr_string != NULL)
2654 		{
2655 		  if (0 != strcmp(theFile->attr_string, p->attr_string))
2656 		    {
2657 		      SH_FREE(theFile->attr_string);
2658 		      theFile->attr_string = sh_util_strdup (p->attr_string);
2659 		    }
2660 		}
2661 #endif
2662 
2663 	      if (theFile->c_mode[0] == 'l') /* c_mode is already copied */
2664 		{
2665 		  if (theFile->link_path)
2666 		    SH_FREE(theFile->link_path);
2667 		  if (p->linkpath)
2668 		    theFile->link_path = sh_util_strdup(p->linkpath);
2669 		  else
2670 		    theFile->link_path = sh_util_strdup("-");
2671 		}
2672 	      else
2673 		{
2674 		  if (theFile->link_path)
2675 		    SH_FREE(theFile->link_path);
2676 		  if (p->linkpath)
2677 		    theFile->link_path = sh_util_strdup(p->linkpath);
2678 		  else
2679 		    theFile->link_path = NULL;
2680 		}
2681 
2682 	      sl_strlcpy(fileHash, p->theFile.checksum, KEY_LEN+1);
2683 
2684 	      theFile->mtime =  p->theFile.mtime;
2685 	      theFile->ctime =  p->theFile.ctime;
2686 	      theFile->atime =  p->theFile.atime;
2687 
2688 	      theFile->size  =  p->theFile.size;
2689 
2690 	      sl_strlcpy(theFile->c_group, p->theFile.c_group, GROUP_MAX+2);
2691 	      theFile->group =  p->theFile.group;
2692 	      sl_strlcpy(theFile->c_owner, p->theFile.c_owner, USER_MAX+2);
2693 	      theFile->owner =  p->theFile.owner;
2694 
2695 	      theFile->ino   =  p->theFile.ino;
2696 	      theFile->rdev  =  p->theFile.rdev;
2697 	      theFile->dev   =  p->theFile.dev;
2698 	      theFile->hardlinks = p->theFile.hardlinks;
2699 
2700 	      SET_SH_FFLAG_VISITED(p->fflags);
2701 	      CLEAR_SH_FFLAG_CHECKED(p->fflags);
2702 	      retval = 1;
2703 	      goto unlock_and_return;
2704 	    }
2705 	  else /* if (sh.flag.reportonce == S_TRUE) */
2706 	    {
2707 	      /* we replace the data in the in-memory copy of the
2708 	       * baseline database, because otherwise we would get
2709 	       * another warning if the suidcheck runs
2710 	       */
2711 	      sl_strlcpy(p->theFile.c_mode, theFile->c_mode, 11);
2712 	      p->theFile.mode  =  theFile->mode;
2713 #if defined(__linux__) || defined(HAVE_STAT_FLAGS)
2714 	      sl_strlcpy(p->theFile.c_attributes, theFile->c_attributes, 16);
2715 	      p->theFile.attributes = theFile->attributes;
2716 #endif
2717 #if defined(USE_ACL) || defined(USE_XATTR)
2718 	      if      (p->attr_string == NULL && theFile->attr_string != NULL)
2719 		{ p->attr_string = sh_util_strdup (theFile->attr_string); }
2720 	      else if (p->attr_string != NULL && theFile->attr_string == NULL)
2721 		{ SH_FREE(p->attr_string); p->attr_string = NULL; }
2722 	      else if (theFile->attr_string != NULL && p->attr_string != NULL)
2723 		{
2724 		  if (0 != strcmp(theFile->attr_string, p->attr_string))
2725 		    {
2726 		      SH_FREE(p->attr_string);
2727 		      p->attr_string = sh_util_strdup (theFile->attr_string);
2728 		    }
2729 		}
2730 #endif
2731 
2732 	      if (theFile->c_mode[0] == 'l' || theFile->link_path)
2733 		{
2734                   if (p->linkpath != NULL)
2735 		    SH_FREE(p->linkpath);
2736 		  p->linkpath = sh_util_strdup(theFile->link_path);
2737 		}
2738 	      else
2739 		{
2740 	          if (p->linkpath != NULL)
2741 		    SH_FREE(p->linkpath);
2742 		  p->linkpath = sh_util_strdup("-");
2743 		}
2744 
2745 	      sl_strlcpy(p->theFile.checksum, fileHash, KEY_LEN+1);
2746 
2747 	      p->theFile.mtime = theFile->mtime;
2748 	      p->theFile.ctime = theFile->ctime;
2749 	      p->theFile.atime = theFile->atime;
2750 
2751 	      p->theFile.size  = theFile->size;
2752 
2753 	      sl_strlcpy(p->theFile.c_group, theFile->c_group, GROUP_MAX+2);
2754 	      p->theFile.group =  theFile->group;
2755 	      sl_strlcpy(p->theFile.c_owner, theFile->c_owner, USER_MAX+2);
2756 	      p->theFile.owner =  theFile->owner;
2757 
2758 	      p->theFile.ino  = theFile->ino;
2759 	      p->theFile.rdev = theFile->rdev;
2760 	      p->theFile.dev  = theFile->dev;
2761 	      p->theFile.hardlinks = theFile->hardlinks;
2762 	    }
2763 	}
2764     }
2765 
2766   SET_SH_FFLAG_VISITED(p->fflags);
2767   CLEAR_SH_FFLAG_CHECKED(p->fflags);
2768 
2769  unlock_and_return:
2770   ; /* 'label at end of compound statement */
2771   SH_MUTEX_UNLOCK(mutex_hash);
2772   SL_RETURN(retval, _("sh_hash_compdata"));
2773 }
2774 
hash_full_tree()2775 int hash_full_tree ()
2776 {
2777   sh_file_t * p;
2778   int         i;
2779 
2780   SL_ENTER(_("hash_full_tree"));
2781 
2782   if (IsInit != 1)
2783     SL_RETURN(0, _("hash_full_tree"));
2784 
2785   SH_MUTEX_LOCK_UNSAFE(mutex_hash);
2786   for (i = 0; i < TABSIZE; ++i)
2787     {
2788       for (p = tab[i]; p; p = p->next)
2789 	CLEAR_SH_FFLAG_ALLIGNORE(p->fflags);
2790     }
2791   SH_MUTEX_UNLOCK_UNSAFE(mutex_hash);
2792   SL_RETURN (0, _("hash_full_tree"));
2793 }
2794 
2795 #if !defined(SH_CUTEST)
2796 static
2797 #endif
hash_remove_tree_test(char * s,char * fullpath,size_t len_s)2798 int hash_remove_tree_test(char * s, char * fullpath, size_t len_s)
2799 {
2800   size_t       len_p;
2801   char      *  test;
2802 
2803   len_p = strlen(fullpath);
2804 
2805   if (len_p >= len_s)
2806     {
2807       if (0 == strncmp(s, fullpath, len_s))
2808 	{
2809 	  if (len_p > len_s)
2810 	    {
2811 	      /* continue if not inside directory;
2812 	       * len_s > 1 because everything is inside '/'
2813 	       */
2814 	      if ((len_s > 1) && (fullpath[len_s] != '/'))
2815 		return S_FALSE;
2816 
2817 	      test = sh_files_find_mostspecific_dir(fullpath);
2818 
2819 	      if (test && 0 != strcmp(test, s)) {
2820 		/* There is a more specific directory, continue */
2821 		return S_FALSE;
2822 	      }
2823 
2824 	      if (NULL == sh_files_findfile(fullpath)) {
2825 		/* SET_SH_FFLAG_ALLIGNORE(p->fflags); */
2826 		return S_TRUE;
2827 	      }
2828 	    }
2829 	  else /* len_p == len */
2830 	    {
2831 	      /* it is 's' itself, mark and continue
2832 	       * unless there is a policy for the inode itself
2833 	       */
2834 	      if (NULL == sh_files_findfile(fullpath)) {
2835 		/* SET_SH_FFLAG_ALLIGNORE(p->fflags); */
2836 		return S_TRUE;
2837 	      }
2838 	      else {
2839 		return S_FALSE;
2840 	      }
2841 	    }
2842 
2843 	} /* if path is in tree */
2844     } /* if path is possibly in tree */
2845   return S_FALSE;
2846 }
2847 
2848 
hash_remove_tree(char * s)2849 int hash_remove_tree (char * s)
2850 {
2851   sh_file_t *  p;
2852   size_t       len_s;
2853   unsigned int i;
2854 
2855   SL_ENTER(_("hash_remove_tree"));
2856 
2857   if (!s || *s == '\0')
2858     SL_RETURN ((-1), _("hash_remove_tree"));
2859 
2860   len_s = sl_strlen(s);
2861 
2862   if (IsInit != 1)
2863     sh_hash_init();
2864 
2865   SH_MUTEX_LOCK_UNSAFE(mutex_hash);
2866   for (i = 0; i < TABSIZE; ++i)
2867     {
2868       for (p = tab[i]; p; p = p->next)
2869 	{
2870 	  if (p->fullpath)
2871 	    {
2872 	      /* if (0 == strncmp(s, p->fullpath, len_s)) *//* old */
2873 	      if (S_TRUE == hash_remove_tree_test(s, p->fullpath, len_s)) {
2874 		SET_SH_FFLAG_ALLIGNORE(p->fflags);
2875 		MODI_SET(p->theFile.checkflags, MODI_ALLIGNORE);
2876 	      }
2877 	    } /* if path is not null */
2878 
2879 	}
2880     }
2881   SH_MUTEX_UNLOCK_UNSAFE(mutex_hash);
2882   SL_RETURN ((0), _("hash_remove_tree"));
2883 }
2884 
2885 #if TIME_WITH_SYS_TIME
2886 #include <sys/time.h>
2887 #include <time.h>
2888 #else
2889 #if HAVE_SYS_TIME_H
2890 #include <sys/time.h>
2891 #else
2892 #include <time.h>
2893 #endif
2894 #endif
2895 
2896 static int ListFullDetail    = S_FALSE;
2897 static int ListWithDelimiter = S_FALSE;
2898 static char * ListFile       = NULL;
2899 
set_list_file(const char * c)2900 int set_list_file (const char * c)
2901 {
2902   ListFile = sh_util_strdup(c);
2903   return 0;
2904 }
get_list_file()2905 char * get_list_file()
2906 {
2907   return ListFile;
2908 }
2909 
set_full_detail(const char * c)2910 int set_full_detail (const char * c)
2911 {
2912   (void) c;
2913   ListFullDetail = S_TRUE;
2914   return 0;
2915 }
2916 
set_list_delimited(const char * c)2917 int set_list_delimited (const char * c)
2918 {
2919   (void) c;
2920   ListFullDetail = S_TRUE;
2921   ListWithDelimiter = S_TRUE;
2922   return 0;
2923 }
2924 
2925 /* Always quote the string, except if it is empty. Quote quotes by
2926  * doubling them.
2927  */
csv_escape(const char * str)2928 char * csv_escape(const char * str)
2929 {
2930   const  char * p = str;
2931   const  char * q;
2932 
2933   size_t size       = 0;
2934   size_t flag_quote = 0;
2935 
2936   char * new;
2937   char * pnew;
2938 
2939   if (p)
2940     {
2941 
2942       while (*p)
2943 	{
2944 	  if (*p == '"')
2945 	    ++flag_quote;
2946 
2947 	  ++size; ++p;
2948 	}
2949 
2950       if (sl_ok_adds(size, flag_quote))
2951 	size += flag_quote;      /* double each quote */
2952       else
2953 	return NULL;
2954 
2955       if (sl_ok_adds(size, 3))
2956 	size += 3; /* two quotes and terminating null */
2957       else
2958 	return NULL;
2959 
2960       new = SH_ALLOC(size);
2961 
2962       if (flag_quote != 0)
2963 	{
2964 	  new[0] = '"';
2965 	  pnew = &new[1];
2966 	  q    = str;
2967 	  while (*q)
2968 	    {
2969 	      *pnew = *q;
2970 	      if (*pnew == '"')
2971 		{
2972 		  ++pnew; *pnew = '"';
2973 		}
2974 	      ++pnew; ++q;
2975 	    }
2976 	  *pnew = '"'; ++pnew;
2977 	  *pnew = '\0';
2978 	}
2979       else
2980 	{
2981 	  if (size > 3)
2982 	    {
2983 	      new[0] = '"';
2984 	      sl_strlcpy (&new[1], str, size-1);
2985 	      new[size-2] = '"';
2986 	      new[size-1] = '\0';
2987 	    }
2988 	  else
2989 	    {
2990 	      new[0] = '\0';
2991 	    }
2992 	}
2993 
2994       return new;
2995     }
2996   return NULL;
2997 }
2998 
isHexKey(char * s)2999 int isHexKey(char * s)
3000 {
3001   int i;
3002 
3003   for (i = 0; i < KEY_LEN; ++i)
3004     {
3005       if (*s)
3006 	{
3007 	  if ((*s >= '0' && *s <= '9') ||
3008 	      (*s >= 'A' && *s <= 'F') ||
3009 	      (*s >= 'a' && *s <= 'f'))
3010 	    {
3011 	      ++s;
3012 	      continue;
3013 	    }
3014 	}
3015       return S_FALSE;
3016     }
3017   return S_TRUE;
3018 }
3019 
3020 #include "sh_checksum.h"
3021 
KEYBUFtolower(char * s,char * result)3022 static char * KEYBUFtolower (char * s, char * result)
3023 {
3024   char * r = result;
3025   if (s)
3026     {
3027       for (; *s; ++s)
3028 	{
3029 	  *r = tolower((unsigned char) *s); ++r;
3030 	}
3031       *r = '\0';
3032     }
3033   return result;
3034 }
3035 
sh_hash_list_db_entry_full_detail(sh_file_t * p)3036 void sh_hash_list_db_entry_full_detail (sh_file_t * p)
3037 {
3038   char * tmp;
3039   char * esc;
3040   char   str[81];
3041   char   hexdigest[SHA256_DIGEST_STRING_LENGTH];
3042   char   keybuffer[KEYBUF_SIZE];
3043 
3044   if (ListWithDelimiter == S_TRUE)
3045     {
3046       printf(_("%7ld, %7ld, %10s, %5d, %12s, %5d, %3d, %-8s, %5d, %-8s, %5d, "),
3047 	     (unsigned long) p->theFile.ino, (unsigned long) p->theFile.dev,
3048 	     p->theFile.c_mode, (int) p->theFile.mode,
3049 	     p->theFile.c_attributes, (int) p->theFile.attributes,
3050 	     (int) p->theFile.hardlinks,
3051 	     p->theFile.c_owner, (int) p->theFile.owner,
3052 	     p->theFile.c_group, (int) p->theFile.group);
3053     }
3054   else
3055     {
3056       printf(_("%7ld %7ld %10s %5d %12s %5d %3d %-8s %5d %-8s %5d "),
3057 	     (unsigned long) p->theFile.ino, (unsigned long) p->theFile.dev,
3058 	     p->theFile.c_mode, (int) p->theFile.mode,
3059 	     p->theFile.c_attributes, (int) p->theFile.attributes,
3060 	     (int) p->theFile.hardlinks,
3061 	     p->theFile.c_owner, (int) p->theFile.owner,
3062 	     p->theFile.c_group, (int) p->theFile.group);
3063     }
3064 
3065   if ('c' == p->theFile.c_mode[0] || 'b' == p->theFile.c_mode[0])
3066     sl_snprintf(str, sizeof(str), "%"PRIu64, p->theFile.rdev);
3067   else
3068     sl_snprintf(str, sizeof(str), "%"PRIu64, p->theFile.size);
3069 
3070   printf( _(" %8s"), str);
3071   if (ListWithDelimiter == S_TRUE)
3072     putchar(',');
3073 
3074   printf( _(" %s"), sh_unix_gmttime (p->theFile.ctime, str, sizeof(str)));
3075   if (ListWithDelimiter == S_TRUE)
3076     putchar(',');
3077   printf( _(" %s"), sh_unix_gmttime (p->theFile.mtime, str, sizeof(str)));
3078   if (ListWithDelimiter == S_TRUE)
3079     putchar(',');
3080   printf( _(" %s"), sh_unix_gmttime (p->theFile.atime, str, sizeof(str)));
3081   if (ListWithDelimiter == S_TRUE)
3082     putchar(',');
3083 
3084   if (isHexKey(p->theFile.checksum))
3085     printf( _(" %s"), KEYBUFtolower(p->theFile.checksum, keybuffer));
3086   else
3087     printf( _(" %s"), SHA256_Base2Hex(p->theFile.checksum, hexdigest));
3088   if (ListWithDelimiter == S_TRUE)
3089     putchar(',');
3090 
3091   tmp = sh_util_safe_name(p->fullpath);
3092   if (ListWithDelimiter != S_TRUE)
3093     {
3094       printf( _(" %s"), tmp);
3095     }
3096   else
3097     {
3098       esc = csv_escape(tmp);
3099       printf( _(" %s,"), (esc != NULL) ? esc : _("(null)"));
3100       if (esc)
3101 	SH_FREE(esc);
3102     }
3103   SH_FREE(tmp);
3104 
3105   if ('l' == p->theFile.c_mode[0])
3106     {
3107       tmp = sh_util_safe_name(p->linkpath);
3108       if (ListWithDelimiter != S_TRUE)
3109 	{
3110 	  printf(_(" -> %s"), tmp);
3111 	}
3112       else
3113 	{
3114 	  esc = csv_escape(tmp);
3115 	  printf( _(" %s,"), (esc != NULL) ? esc : _("(null)"));
3116 	  if (esc)
3117 	    SH_FREE(esc);
3118 	}
3119       SH_FREE(tmp);
3120     }
3121 
3122   if (p->attr_string)
3123     {
3124       tmp = sh_util_safe_name(p->attr_string);
3125       if (ListWithDelimiter != S_TRUE)
3126 	{
3127 	  printf(_(" %s"), tmp);
3128 	}
3129       else
3130 	{
3131 	  esc = csv_escape(tmp);
3132 	  printf( _(" %s"), (esc != NULL) ? esc : _("(null)"));
3133 	  if (esc)
3134 	    SH_FREE(esc);
3135 	}
3136       SH_FREE(tmp);
3137     }
3138   else
3139     {
3140       if (ListWithDelimiter == S_TRUE)
3141 	printf("%s",_(" no_attr"));
3142     }
3143   putchar('\n');
3144 
3145   return;
3146 }
3147 
sh_hash_list_db_entry(sh_file_t * p)3148 void sh_hash_list_db_entry (sh_file_t * p)
3149 {
3150   char nowtime[128];
3151   char thetime[128];
3152   char * tmp;
3153   time_t now  = time(NULL);
3154   time_t then = (time_t) p->theFile.mtime;
3155   struct tm   * time_ptr;
3156 
3157 #if defined(HAVE_PTHREAD) && defined (_POSIX_THREAD_SAFE_FUNCTIONS) && defined(HAVE_GMTIME_R)
3158   struct tm     time_tm;
3159 #endif
3160 
3161   if (ListFullDetail != S_FALSE)
3162     {
3163       sh_hash_list_db_entry_full_detail (p);
3164       return;
3165     }
3166 
3167 #if defined(HAVE_PTHREAD) && defined (_POSIX_THREAD_SAFE_FUNCTIONS) && defined(HAVE_GMTIME_R)
3168   time_ptr = gmtime_r(&then, &time_tm);
3169   if (!time_ptr)
3170     return;
3171   strftime(thetime, 127, _("%b %d  %Y"), time_ptr);
3172   time_ptr = gmtime_r(&now,  &time_tm);
3173   if (!time_ptr)
3174     return;
3175   strftime(nowtime, 127, _("%b %d  %Y"), time_ptr);
3176   if (0 == strncmp(&nowtime[7], &thetime[7], 4))
3177     {
3178       time_ptr = gmtime_r(&then, &time_tm);
3179       if (!time_ptr)
3180 	return;
3181       strftime(thetime, 127, _("%b %d %H:%M"), time_ptr);
3182     }
3183 #else
3184   time_ptr = gmtime(&then);
3185   if (!time_ptr)
3186     return;
3187   strftime(thetime, 127, _("%b %d  %Y"), time_ptr);
3188   time_ptr = gmtime(&now);
3189   if (!time_ptr)
3190     return;
3191   strftime(nowtime, 127, _("%b %d  %Y"), time_ptr);
3192   if (0 == strncmp(&nowtime[7], &thetime[7], 4))
3193     {
3194       time_ptr = gmtime(&then);
3195       if (!time_ptr)
3196 	return;
3197       strftime(thetime, 127, _("%b %d %H:%M"), time_ptr);
3198     }
3199 #endif
3200 
3201   tmp = sh_util_safe_name(p->fullpath);
3202   if ('c' == p->theFile.c_mode[0] || 'b' == p->theFile.c_mode[0])
3203     printf(_("%10s %3d %-8s %-8s %3d,%4d %s %s"),
3204 	   p->theFile.c_mode, (int) p->theFile.hardlinks,
3205 	   p->theFile.c_owner, p->theFile.c_group,
3206 	   (int) major((dev_t)p->theFile.rdev),
3207 	   (int) minor((dev_t)p->theFile.rdev),
3208 	   thetime,
3209 	   tmp);
3210   else
3211     printf(_("%10s %3d %-8s %-8s %8ld %s %s"),
3212 	   p->theFile.c_mode, (int) p->theFile.hardlinks,
3213 	   p->theFile.c_owner, p->theFile.c_group, (long) p->theFile.size,
3214 	   thetime,
3215 	   tmp);
3216   SH_FREE(tmp);
3217 
3218   if ('l' == p->theFile.c_mode[0])
3219     {
3220       tmp = sh_util_safe_name(p->linkpath);
3221       printf(_(" -> %s\n"), tmp);
3222       SH_FREE(tmp);
3223     }
3224   else
3225     printf("\n");
3226 
3227   return;
3228 }
3229 
3230 #ifdef HAVE_LIBZ
3231 #include <zlib.h>
3232 #endif
3233 
sh_hash_printcontent(char * linkpath)3234 int sh_hash_printcontent(char * linkpath)
3235 {
3236 #ifdef HAVE_LIBZ
3237   unsigned char * decoded;
3238   unsigned char * decompressed = NULL;
3239   size_t dlen;
3240   unsigned long clen;
3241   unsigned long clen_o;
3242   int    res;
3243 
3244   if (linkpath && *linkpath != '-')
3245     {
3246       dlen = sh_util_base64_dec_alloc (&decoded,
3247 				       (unsigned char *)linkpath,
3248 				       strlen(linkpath));
3249 
3250       clen = dlen * 2 + 1;
3251 
3252       do {
3253 	if (decompressed)
3254 	  SH_FREE(decompressed);
3255 	clen += dlen; clen_o = clen;
3256 	decompressed = SH_ALLOC(clen);
3257 	res = uncompress(decompressed, &clen, decoded, dlen);
3258 	if (res == Z_MEM_ERROR)
3259 	  { fprintf(stderr, "%s",_("Error: Not enough memory\n")); return -1; }
3260 	if (res == Z_DATA_ERROR)
3261 	  { fprintf(stderr, "%s",_("Error: Data corrupt or incomplete\n")); return -1; }
3262       } while (res == Z_BUF_ERROR || clen == clen_o);
3263 
3264       decompressed[clen] = '\0';
3265       fputs( (char*) decompressed, stdout);
3266       SH_FREE(decompressed);
3267       return 0;
3268     }
3269 #else
3270   (void) linkpath;
3271 #endif
3272   fprintf(stderr, "%s",_("Error: No data available\n"));
3273   return -1;
3274 }
3275 
3276 /* if defined(SH_WITH_CLIENT) || defined(SH_STANDALONE) */
3277 #endif
3278