source: trunk/src/sh_files.c@ 21

Last change on this file since 21 was 20, checked in by rainer, 19 years ago

Enable command-line parsing for prelude, and make prelude regression test safer.

File size: 51.1 KB
Line 
1/* SAMHAIN file system integrity testing */
2/* Copyright (C) 1999 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 <stdio.h>
23#include <stdlib.h>
24#include <string.h>
25#include <limits.h>
26
27#include <errno.h>
28
29/* Must be before <utime.h> on FreeBSD
30 */
31#include <sys/types.h>
32#include <unistd.h>
33
34#include <utime.h>
35
36
37#ifdef HAVE_DIRENT_H
38#include <dirent.h>
39#define NAMLEN(dirent) sl_strlen((dirent)->d_name)
40#else
41#define dirent direct
42#define NAMLEN(dirent) (dirent)->d_namlen
43#ifdef HAVE_SYS_NDIR_H
44#include <sys/ndir.h>
45#endif
46#ifdef HAVE_SYS_DIR_H
47#include <sys/dir.h>
48#endif
49#ifdef HAVE_NDIR_H
50#include <ndir.h>
51#endif
52#endif
53
54#ifdef HAVE_GLOB_H
55#include <glob.h>
56#endif
57
58#include "samhain.h"
59
60#if (defined (SH_WITH_CLIENT) || defined (SH_STANDALONE))
61
62#include "sh_error.h"
63#include "sh_utils.h"
64#include "sh_unix.h"
65#include "sh_files.h"
66#include "sh_tiger.h"
67#include "sh_hash.h"
68#include "sh_ignore.h"
69#include "zAVLTree.h"
70
71#undef FIL__
72#define FIL__ _("sh_files.c")
73
74extern int safe_logger (int signal, int method, pid_t thepid);
75
76extern int flag_err_debug;
77extern int flag_err_info;
78
79int sh_files_reportonce(char * c)
80{
81 int i;
82 SL_ENTER(_("sh_files_reportonce"));
83 i = sh_util_flagval(c, &(sh.flag.reportonce));
84
85 SL_RETURN(i, _("sh_files_reportonce"));
86}
87
88int sh_files_fulldetail(char * c)
89{
90 int i;
91 SL_ENTER(_("sh_files_fulldetail"));
92 i = sh_util_flagval(c, &(sh.flag.fulldetail));
93
94 SL_RETURN((i), _("sh_files_fulldetail"));
95}
96
97
98typedef struct dir_struct {
99 long NumRegular;
100 long NumDirs;
101 long NumSymlinks;
102 long NumFifos;
103 long NumSockets;
104 long NumCDev;
105 long NumBDev;
106 long NumAll;
107 long TotalBytes;
108 char DirPath[PATH_MAX];
109} dir_type;
110
111typedef struct dirstack_entry {
112 char * name;
113 int class;
114 unsigned long check_mask;
115 int rdepth;
116 short checked;
117 short childs_checked;
118 short reported;
119 /* struct dirstack_entry * next; */
120} dirstack_t;
121
122
123/* the destructor
124 */
125void free_dirstack (void * inptr)
126{
127 dirstack_t * here;
128
129 SL_ENTER(_("free_dirstack"));
130 if (inptr == NULL)
131 SL_RET0(_("free_dirstack"));
132 else
133 here = (dirstack_t *) inptr;
134
135 if (here->name != NULL)
136 SH_FREE(here->name);
137 SH_FREE(here);
138 SL_RET0(_("free_dirstack"));
139}
140
141/* Function to return the key for indexing
142 * the argument
143 */
144zAVLKey zdirstack_key (void const * arg)
145{
146 const dirstack_t * sa = (const dirstack_t *) arg;
147 return (zAVLKey) sa->name;
148}
149
150
151static zAVLTree * zdirListOne = NULL;
152static zAVLTree * zdirListTwo = NULL;
153static zAVLTree * zfileList = NULL;
154
155
156static int sh_files_fullpath (char * testdir, char * d_name,
157 char * statpath);
158static int sh_files_pushdir (int class, char * str_s);
159static int sh_files_pushfile (int class, char * str_s);
160static int sh_files_checkdir (int class, int rdepth, char * dirName,
161 char * relativeName);
162static ShFileType sh_files_filecheck (int class, char * dirName,
163 char * fileName, int * reported,
164 int rsrcflag);
165
166static long MaxRecursionLevel = 0;
167
168/* set default recursion level
169 */
170int sh_files_setrecursion (const char * flag_s)
171{
172 long flag = 0;
173 static int reject = 0;
174
175 SL_ENTER( _("sh_files_setrecursion"));
176
177 if (reject == 1)
178 SL_RETURN((-1), _("sh_files_setrecursion"));
179
180 if (sh.flag.opts == 1)
181 reject = 1;
182
183 if (flag_s != NULL)
184 flag = (int)(atof(flag_s));
185
186 if (flag >= 0 && flag <= 99)
187 MaxRecursionLevel = flag;
188 else
189 SL_RETURN((-1), _("sh_files_setrecursion"));
190
191 SL_RETURN((0), _("sh_files_setrecursion"));
192}
193
194
195unsigned long sh_files_chk ()
196{
197 zAVLCursor cursor;
198 ShFileType status;
199 unsigned long fcount = 0;
200
201 char * tmp = NULL;
202
203 dirstack_t * ptr;
204 char * base;
205 char * file;
206
207 SL_ENTER(_("sh_files_chk"));
208
209 for (ptr = (dirstack_t *) zAVLFirst(&cursor, zfileList); ptr;
210 ptr = (dirstack_t *) zAVLNext(&cursor))
211 {
212
213 if (sig_urgent > 0) {
214 SL_RETURN(fcount, _("sh_files_chk"));
215 }
216
217 if (ptr->checked == S_FALSE)
218 {
219 base = sh_util_basename (ptr->name);
220 file = sh_util_filename (ptr->name);
221#if defined(WITH_TPT)
222 tmp = sh_util_safe_name (ptr->name);
223#endif
224
225
226 if (flag_err_info == SL_TRUE)
227 {
228#if !defined(WITH_TPT)
229 tmp = sh_util_safe_name (ptr->name);
230#endif
231 sh_error_handle ((-1), FIL__, __LINE__, 0, MSG_FI_CHK, tmp);
232 }
233
234 BREAKEXIT(sh_files_filecheck);
235 status = sh_files_filecheck (ptr->class, base, file,
236 (int *) &(ptr->reported), 0);
237
238 TPT(( 0, FIL__, __LINE__,
239 _("msg=<filecheck complete: %s> status=<%d> reported=<%d>\n"),
240 tmp, status, ptr->reported));
241
242 if (status == SH_FILE_UNKNOWN && ptr->reported == S_FALSE)
243 {
244 TPT(( 0, FIL__, __LINE__, _("msg=<file: %s> status=<%d>\n"),
245 tmp, status));
246
247 if ( sh.flag.checkSum == SH_CHECK_INIT ||
248 sh_hash_have_it (ptr->name) >= 0)
249 {
250 if (S_FALSE == sh_ignore_chk_del(ptr->name))
251 {
252 if (0 != hashreport_missing(ptr->name,
253 (ptr->class == SH_LEVEL_ALLIGNORE) ?
254 ShDFLevel[ptr->class] :
255 ShDFLevel[SH_ERR_T_FILE])) {
256 if (tmp == NULL)
257 tmp = sh_util_safe_name (ptr->name);
258 sh_error_handle ((ptr->class == SH_LEVEL_ALLIGNORE) ?
259 ShDFLevel[ptr->class] :
260 ShDFLevel[SH_ERR_T_FILE],
261 FIL__, __LINE__, 0, MSG_FI_MISS,
262 tmp);
263 }
264 }
265 }
266 else /* not there at init, and still missing */
267 {
268 if (tmp == NULL)
269 tmp = sh_util_safe_name (ptr->name);
270 sh_error_handle (SH_ERR_NOTICE,
271 FIL__, __LINE__, 0,
272 MSG_FI_FAIL,
273 tmp);
274 }
275#ifndef REPLACE_OLD
276 /* this will tell that we have seen the file, and thus prevent
277 * deletion from the database, resulting in an incomplete
278 * message when the file reappears
279 */
280 if (sh.flag.checkSum != SH_CHECK_INIT)
281 sh_hash_set_visited_true(ptr->name);
282#else
283 if (sh.flag.checkSum != SH_CHECK_INIT)
284 sh_hash_set_missing(ptr->name);
285#endif
286 if (sh.flag.reportonce == S_TRUE)
287 ptr->reported = S_TRUE;
288 }
289 else
290 {
291 /* exists (status >= 0), but was missing (reported == TRUE)
292 */
293 if (status != SH_FILE_UNKNOWN && ptr->reported == S_TRUE)
294 {
295 ptr->reported = S_FALSE;
296 }
297 /* Catchall
298 */
299 else if (status == SH_FILE_UNKNOWN)
300 {
301 /* Thu Mar 7 15:09:40 CET 2002 Make sure missing file
302 * is reported if ptr->reported == S_TRUE because the
303 * file has been added.
304 */
305 if (sh_hash_have_it (ptr->name) >= 0)
306 {
307 if (S_FALSE == sh_ignore_chk_del(ptr->name))
308 {
309 if (0 != hashreport_missing(ptr->name,
310 (ptr->class == SH_LEVEL_ALLIGNORE) ?
311 ShDFLevel[ptr->class] :
312 ShDFLevel[SH_ERR_T_FILE])) {
313 if (tmp == NULL)
314 tmp = sh_util_safe_name (ptr->name);
315 sh_error_handle ((ptr->class == SH_LEVEL_ALLIGNORE)?
316 ShDFLevel[ptr->class] :
317 ShDFLevel[SH_ERR_T_FILE],
318 FIL__, __LINE__, 0, MSG_FI_MISS,
319 tmp);
320 }
321 }
322#ifndef REPLACE_OLD
323 if (sh.flag.checkSum != SH_CHECK_INIT)
324 sh_hash_set_visited_true(ptr->name);
325#else
326 /* delete from database
327 */
328 if (sh.flag.checkSum != SH_CHECK_INIT)
329 sh_hash_set_missing(ptr->name);
330#endif
331 }
332 else
333 {
334 if (tmp == NULL)
335 tmp = sh_util_safe_name (ptr->name);
336 sh_error_handle (SH_ERR_INFO, FIL__, __LINE__, 0,
337 MSG_FI_FAIL,
338 tmp);
339 if (sh.flag.checkSum != SH_CHECK_INIT)
340 sh_hash_set_visited_true(ptr->name);
341 }
342 }
343 ++fcount;
344 }
345
346 if (tmp != NULL)
347 {
348 SH_FREE(tmp);
349 tmp = NULL;
350 }
351 SH_FREE(file);
352 SH_FREE(base);
353
354 ptr->checked = S_TRUE;
355 }
356 }
357
358 SL_RETURN(fcount, _("sh_files_chk"));
359}
360
361int sh_files_delfilestack ()
362{
363 SL_ENTER(_("sh_files_delfilestack"));
364
365 zAVLFreeTree (zfileList, free_dirstack);
366 zfileList = NULL;
367
368 SL_RETURN(0, _("sh_files_delfilestack"));
369}
370
371int sh_files_setrec_int (zAVLTree * tree)
372{
373 dirstack_t * ptr;
374 zAVLCursor avlcursor;
375
376 SL_ENTER(_("sh_files_setrec"));
377 if (tree != NULL) {
378 for (ptr = (dirstack_t *) zAVLFirst(&avlcursor, tree); ptr;
379 ptr = (dirstack_t *) zAVLNext(&avlcursor))
380 {
381 if (ptr->rdepth < (-1) || ptr->rdepth > 99)
382 {
383 ptr->rdepth = MaxRecursionLevel;
384 }
385 if (ptr->rdepth == (-1) && sh.flag.checkSum != SH_CHECK_INIT)
386 hash_remove_tree (ptr->name);
387 }
388 }
389 SL_RETURN(0, _("sh_files_setrec"));
390}
391
392int sh_files_setrec ()
393{
394 sh_files_setrec_int(zdirListOne);
395 return sh_files_setrec_int(zdirListTwo);
396}
397
398zAVLTree * sh_files_deldirstack_int (zAVLTree * ptr)
399{
400 SL_ENTER(_("sh_files_deldirstack"));
401
402 zAVLFreeTree (ptr, free_dirstack);
403
404 SL_RETURN(NULL, _("sh_files_deldirstack"));
405}
406
407int sh_files_deldirstack ()
408{
409 zdirListOne = sh_files_deldirstack_int(zdirListOne);
410 zdirListTwo = sh_files_deldirstack_int(zdirListTwo);
411 return 0;
412}
413
414void sh_files_reset()
415{
416 dirstack_t * ptr;
417 zAVLCursor avlcursor;
418
419 SL_ENTER(_("sh_files_reset"));
420
421 for (ptr = (dirstack_t *) zAVLFirst(&avlcursor, zfileList); ptr;
422 ptr = (dirstack_t *) zAVLNext(&avlcursor))
423 ptr->checked = 0;
424
425 SL_RET0(_("sh_files_reset"));
426}
427
428void sh_dirs_reset()
429{
430 dirstack_t * ptr;
431 zAVLCursor avlcursor1;
432 zAVLCursor avlcursor2;
433
434 SL_ENTER(_("sh_dirs_reset"));
435
436 for (ptr = (dirstack_t *) zAVLFirst(&avlcursor1, zdirListOne); ptr;
437 ptr = (dirstack_t *) zAVLNext(&avlcursor1))
438 ptr->checked = 0;
439
440 for (ptr = (dirstack_t *) zAVLFirst(&avlcursor2, zdirListTwo); ptr;
441 ptr = (dirstack_t *) zAVLNext(&avlcursor2))
442 ptr->checked = 0;
443
444 SL_RET0(_("sh_dirs_reset"));
445}
446
447
448int sh_files_pushfile_prelink (char * str_s)
449{
450 return (sh_files_pushfile (SH_LEVEL_PRELINK, str_s));
451}
452
453int sh_files_pushfile_user0 (char * str_s)
454{
455 return (sh_files_pushfile (SH_LEVEL_USER0, str_s));
456}
457
458
459int sh_files_pushfile_user1 (char * str_s)
460{
461 return (sh_files_pushfile (SH_LEVEL_USER1, str_s));
462}
463
464
465int sh_files_pushfile_ro (char * str_s)
466{
467 return (sh_files_pushfile (SH_LEVEL_READONLY, str_s));
468}
469
470int sh_files_pushfile_attr (char * str_s)
471{
472 return (sh_files_pushfile (SH_LEVEL_ATTRIBUTES, str_s));
473}
474
475int sh_files_pushfile_log (char * str_s)
476{
477 return (sh_files_pushfile (SH_LEVEL_LOGFILES, str_s));
478}
479
480int sh_files_pushfile_glog (char * str_s)
481{
482 return (sh_files_pushfile (SH_LEVEL_LOGGROW, str_s));
483}
484
485int sh_files_pushfile_noig (char * str_s)
486{
487 return (sh_files_pushfile (SH_LEVEL_NOIGNORE, str_s));
488}
489
490int sh_files_pushfile_allig (char * str_s)
491{
492 return (sh_files_pushfile (SH_LEVEL_ALLIGNORE, str_s));
493}
494
495
496static void sh_files_set_mask (unsigned long * mask,
497 unsigned long val, int act)
498{
499 SL_ENTER(_("sh_files_set_mask"));
500
501 if (act == 0)
502 (*mask) = val;
503 else if (act > 0)
504 (*mask) |= val;
505 else
506 (*mask) &= ~val;
507
508 SL_RET0(_("sh_files_set_mask"));
509}
510
511/* set mask(class)
512 */
513static int sh_files_parse_mask (unsigned long * mask, char * str)
514{
515 int l, i = 0, act = 0, k = 0;
516 char myword[64];
517
518 SL_ENTER(_("sh_files_parse_mask"));
519
520 if (str == NULL)
521 {
522 SL_RETURN ( (-1), _("sh_files_parse_mask"));
523 }
524 else
525 l = sl_strlen(str);
526
527 while (i < l) {
528 if (str[i] == '\0')
529 break;
530 if (str[i] == ' ' || str[i] == '\t' || str[i] == ',')
531 {
532 ++i;
533 continue;
534 }
535
536 if (str[i] == '+')
537 {
538 act = +1; ++i;
539 continue;
540 }
541 else if (str[i] == '-')
542 {
543 act = -1; ++i;
544 continue;
545 }
546 else /* a word */
547 {
548 k = 0;
549 while (k < 63 && str[i] != ' ' && str[i] != '\t' && str[i] != ','
550 && str[i] != '+' && str[i] != '-' && str[i] != '\0') {
551 myword[k] = str[i];
552 ++i; ++k;
553 }
554 myword[k] = '\0';
555
556/* checksum */
557 if (0 == strncmp(myword, _("CHK"), 3))
558 sh_files_set_mask (mask, MODI_CHK, act);
559/* link */
560 if (0 == strncmp(myword, _("LNK"), 3))
561 sh_files_set_mask (mask, MODI_LNK, act);
562/* inode */
563 if (0 == strncmp(myword, _("RDEV"), 3))
564 sh_files_set_mask (mask, MODI_RDEV, act);
565/* inode */
566 if (0 == strncmp(myword, _("INO"), 3))
567 sh_files_set_mask (mask, MODI_INO, act);
568/* user */
569 if (0 == strncmp(myword, _("USR"), 3))
570 sh_files_set_mask (mask, MODI_USR, act);
571/* group */
572 if (0 == strncmp(myword, _("GRP"), 3))
573 sh_files_set_mask (mask, MODI_GRP, act);
574/* mtime */
575 if (0 == strncmp(myword, _("MTM"), 3))
576 sh_files_set_mask (mask, MODI_MTM, act);
577/* ctime */
578 if (0 == strncmp(myword, _("CTM"), 3))
579 sh_files_set_mask (mask, MODI_CTM, act);
580/* atime */
581 if (0 == strncmp(myword, _("ATM"), 3))
582 sh_files_set_mask (mask, MODI_ATM, act);
583/* size */
584 if (0 == strncmp(myword, _("SIZ"), 3))
585 sh_files_set_mask (mask, MODI_SIZ, act);
586/* file mode */
587 if (0 == strncmp(myword, _("MOD"), 3))
588 sh_files_set_mask (mask, MODI_MOD, act);
589/* hardlinks */
590 if (0 == strncmp(myword, _("HLN"), 3))
591 sh_files_set_mask (mask, MODI_HLN, act);
592/* size may grow */
593 if (0 == strncmp(myword, _("GROW"), 3))
594 sh_files_set_mask (mask, MODI_SGROW, act);
595/* use prelink */
596 if (0 == strncmp(myword, _("PRE"), 3))
597 sh_files_set_mask (mask, MODI_PREL, act);
598
599 }
600 }
601 SL_RETURN ( (0), _("sh_files_parse_mask"));
602}
603
604int sh_files_redef_prelink(char * str)
605{
606 return (sh_files_parse_mask(&mask_PRELINK, str));
607}
608int sh_files_redef_user0(char * str)
609{
610 return (sh_files_parse_mask(&mask_USER0, str));
611}
612int sh_files_redef_user1(char * str)
613{
614 return (sh_files_parse_mask(&mask_USER1, str));
615}
616int sh_files_redef_readonly(char * str)
617{
618 return (sh_files_parse_mask(&mask_READONLY, str));
619}
620int sh_files_redef_loggrow(char * str)
621{
622 return (sh_files_parse_mask(&mask_LOGGROW, str));
623}
624int sh_files_redef_logfiles(char * str)
625{
626 return (sh_files_parse_mask(&mask_LOGFILES, str));
627}
628int sh_files_redef_attributes(char * str)
629{
630 return (sh_files_parse_mask(&mask_ATTRIBUTES, str));
631}
632int sh_files_redef_noignore(char * str)
633{
634 return (sh_files_parse_mask(&mask_NOIGNORE, str));
635}
636int sh_files_redef_allignore(char * str)
637{
638 return (sh_files_parse_mask(&mask_ALLIGNORE, str));
639}
640
641unsigned long sh_files_maskof (int class)
642{
643 switch (class)
644 {
645 case SH_LEVEL_READONLY:
646 return (unsigned long) mask_READONLY;
647 case SH_LEVEL_ATTRIBUTES:
648 return (unsigned long) mask_ATTRIBUTES;
649 case SH_LEVEL_LOGFILES:
650 return (unsigned long) mask_LOGFILES;
651 case SH_LEVEL_LOGGROW:
652 return (unsigned long) mask_LOGGROW;
653 case SH_LEVEL_ALLIGNORE:
654 return (unsigned long) mask_ALLIGNORE;
655 case SH_LEVEL_NOIGNORE:
656 return (unsigned long) mask_NOIGNORE;
657 case SH_LEVEL_USER0:
658 return (unsigned long) mask_USER0;
659 case SH_LEVEL_USER1:
660 return (unsigned long) mask_USER1;
661 case SH_LEVEL_PRELINK:
662 return (unsigned long) mask_PRELINK;
663 default:
664 return (unsigned long) 0;
665 }
666}
667
668#ifdef HAVE_GLOB_H
669int sh_files_has_metachar (const char * str)
670{
671 SL_ENTER(_("sh_files_has_metachar"));
672 if (NULL != strchr(str, '*'))
673 SL_RETURN(1, _("sh_files_has_metachar"));
674 else if (NULL != strchr(str, '?'))
675 SL_RETURN(1, _("sh_files_has_metachar"));
676 else if (NULL != (strchr(str, '[')))
677 SL_RETURN(1, _("sh_files_has_metachar"));
678 else
679 SL_RETURN(0, _("sh_files_has_metachar"));
680}
681
682
683int sh_files_globerr (const char * epath, int errnum)
684{
685 char * p;
686
687 SL_ENTER(_("sh_files_globerr"));
688
689 p = sh_util_safe_name (epath);
690 sh_error_handle (SH_ERR_ERR, FIL__, __LINE__, errnum, MSG_FI_GLOB,
691 sh_error_message (errnum), p);
692 SH_FREE(p);
693
694 SL_RETURN(0, _("sh_files_globerr"));
695}
696
697/* #ifdef HAVE_GLOB_H
698 */
699#endif
700
701int sh_files_push_file_int (int class, const char * str_s, int len)
702{
703 dirstack_t * new_item_ptr;
704 char * fileName;
705 int ret;
706
707 SL_ENTER(_("sh_files_push_file_int"));
708
709 fileName = SH_ALLOC(len+1);
710 sl_strlcpy(fileName, str_s, len+1);
711
712 new_item_ptr = (dirstack_t *) SH_ALLOC (sizeof(dirstack_t));
713
714 new_item_ptr->name = fileName;
715 new_item_ptr->class = class;
716 new_item_ptr->check_mask = sh_files_maskof(class);
717 new_item_ptr->rdepth = 0;
718 new_item_ptr->checked = S_FALSE;
719 new_item_ptr->reported = S_FALSE;
720 new_item_ptr->childs_checked = S_FALSE;
721
722 if (zfileList == NULL)
723 {
724 zfileList = zAVLAllocTree (zdirstack_key);
725 if (zfileList == NULL)
726 {
727 (void) safe_logger (0, 0, getpid());
728 aud__exit(FIL__, __LINE__, EXIT_FAILURE);
729 }
730 }
731
732 ret = zAVLInsert (zfileList, new_item_ptr);
733
734 if (-1 == ret)
735 {
736 (void) safe_logger (0, 0, getpid());
737 aud__exit(FIL__, __LINE__, EXIT_FAILURE);
738 }
739 if (3 == ret)
740 sh_error_handle ((-1), FIL__, __LINE__, 0, MSG_FI_DOUBLE,
741 fileName);
742
743 SL_RETURN(0, _("sh_files_push_file_int"));
744}
745
746
747static int sh_files_pushfile (int class, char * str_s)
748{
749 char * tmp;
750 int len;
751#ifdef HAVE_GLOB_H
752 glob_t pglob;
753 int globstatus = -1;
754 unsigned int gloop;
755#endif
756
757 static int reject = 0;
758
759 SL_ENTER(_("sh_files_pushfile"));
760
761 if (reject == 1)
762 SL_RETURN((-1),_("sh_files_pushfile"));
763
764 /* if we push a filename from the command line, make sure it
765 * is the only one -- and will stay the only one
766 */
767 if (sh.flag.opts == 1)
768 {
769 sh_files_delfilestack ();
770 sh_files_deldirstack ();
771 reject = 1;
772 }
773
774 if (str_s == NULL)
775 SL_RETURN((-1),_("sh_files_pushfile"));
776
777 len = sl_strlen(str_s);
778
779 if (len >= PATH_MAX)
780 {
781 /* Name too long
782 */
783 tmp = sh_util_safe_name (str_s);
784 sh_error_handle ((-1), FIL__, __LINE__, 0, MSG_FI_2LONG,
785 tmp);
786 SH_FREE(tmp);
787 SL_RETURN((-1),_("sh_files_pushfile"));
788 }
789 else if (len < 1)
790 {
791 /* Should not happen (str_s == NULL caught further above)
792 */
793 SL_RETURN((-1),_("sh_files_pushfile"));
794 }
795 else if (str_s[0] != '/')
796 {
797 /* Not an absolute path
798 */
799 tmp = sh_util_safe_name (str_s);
800 sh_error_handle ((-1), FIL__, __LINE__, 0, MSG_FI_NOPATH,
801 tmp);
802 SH_FREE(tmp);
803 SL_RETURN((-1),_("sh_files_pushfile"));
804 }
805 else
806 {
807 /* remove a terminating '/', take care of the
808 * special case of the root directory.
809 */
810 if (str_s[len-1] == '/' && len > 1)
811 {
812 str_s[len-1] = '\0';
813 --len;
814 }
815
816 }
817
818#ifdef HAVE_GLOB_H
819 if (0 == sh_files_has_metachar(str_s))
820 {
821 sh_files_push_file_int (class, str_s, len);
822 }
823 else
824 {
825 pglob.gl_offs = 0;
826 globstatus = glob (str_s, 0, sh_files_globerr, &pglob);
827
828 if (globstatus == 0 && pglob.gl_pathc > 0)
829 {
830 for (gloop = 0; gloop < (unsigned int) pglob.gl_pathc; ++gloop)
831 sh_files_push_file_int (class, pglob.gl_pathv[gloop],
832 sl_strlen(pglob.gl_pathv[gloop]));
833 }
834 else
835 {
836 tmp = sh_util_safe_name (str_s);
837
838 if (pglob.gl_pathc == 0
839#ifdef GLOB_NOMATCH
840 || globstatus == GLOB_NOMATCH
841#endif
842 )
843 sh_error_handle (SH_ERR_ERR, FIL__, __LINE__,
844 globstatus, MSG_FI_GLOB,
845 _("No matches found"), tmp);
846#ifdef GLOB_NOSPACE
847 else if (globstatus == GLOB_NOSPACE)
848 sh_error_handle (SH_ERR_ERR, FIL__, __LINE__,
849 globstatus, MSG_FI_GLOB,
850 _("Out of memory"), tmp);
851#endif
852#ifdef GLOB_ABORTED
853 else if (globstatus == GLOB_ABORTED)
854 sh_error_handle (SH_ERR_ERR, FIL__, __LINE__,
855 globstatus, MSG_FI_GLOB,
856 _("Read error"), tmp);
857#endif
858 else
859 sh_error_handle (SH_ERR_ERR, FIL__, __LINE__,
860 globstatus, MSG_FI_GLOB,
861 _("Unknown error"), tmp);
862
863 SH_FREE(tmp);
864
865 }
866
867 globfree(&pglob);
868 }
869
870#else
871 sh_files_push_file_int (class, str_s, len);
872#endif
873
874 SL_RETURN((0),_("sh_files_pushfile"));
875}
876
877
878/* ------ directories ----- */
879
880int sh_files_is_allignore_int (char * str, zAVLTree * tree)
881{
882 dirstack_t * ptr;
883
884 SL_ENTER(_("sh_files_is_allignore"));
885
886 if (tree)
887 {
888 ptr = zAVLSearch(tree, str);
889 if (ptr)
890 {
891 if (ptr->class == SH_LEVEL_ALLIGNORE)
892 SL_RETURN( 1, _("sh_files_is_allignore"));
893 else
894 SL_RETURN( 0, _("sh_files_is_allignore"));
895 }
896 }
897 SL_RETURN( 0, _("sh_files_is_allignore"));
898}
899
900int sh_files_is_allignore (char * str)
901{
902 if (1 == sh_files_is_allignore_int(str, zdirListOne))
903 return 1;
904 if (NULL == zdirListTwo)
905 return 0;
906 return sh_files_is_allignore_int(str, zdirListTwo);
907}
908
909unsigned long sh_dirs_chk (int which)
910{
911 zAVLTree * tree;
912 zAVLCursor cursor;
913 dirstack_t * ptr;
914 dirstack_t * dst_ptr;
915 int status;
916 unsigned long dcount = 0;
917 char * tmp;
918
919 SL_ENTER(_("sh_dirs_chk"));
920
921 if (which == 1)
922 tree = zdirListOne;
923 else
924 tree = zdirListTwo;
925
926 for (ptr = (dirstack_t *) zAVLFirst(&cursor, tree); ptr;
927 ptr = (dirstack_t *) zAVLNext(&cursor))
928 {
929 if (sig_urgent > 0) {
930 SL_RETURN(dcount, _("sh_dirs_chk"));
931 }
932
933 if (ptr->checked == S_FALSE)
934 {
935 /* 28 Aug 2001 check the top level directory
936 */
937 status = S_FALSE;
938 dst_ptr = zAVLSearch(zfileList, ptr->name);
939 if (dst_ptr)
940 {
941 if (dst_ptr->checked == S_FALSE)
942 {
943 BREAKEXIT(sh_files_filecheck);
944 sh_files_filecheck (dst_ptr->class, ptr->name,
945 NULL, &status, 0);
946 dst_ptr->checked = S_TRUE;
947 status = S_TRUE;
948 }
949 else
950 {
951 status = S_TRUE;
952 }
953 }
954
955 if (status == S_FALSE)
956 sh_files_filecheck (ptr->class, ptr->name, NULL, &status, 0);
957
958 BREAKEXIT(sh_files_checkdir);
959 status = sh_files_checkdir (ptr->class, ptr->rdepth, ptr->name,
960 ptr->name);
961
962 if (status < 0 && ptr->reported == S_FALSE)
963 {
964 /* directory is missing
965 */
966 if (S_FALSE == sh_ignore_chk_del(ptr->name))
967 {
968 if (0 != hashreport_missing(ptr->name,
969 (ptr->class == SH_LEVEL_ALLIGNORE) ?
970 ShDFLevel[ptr->class] :
971 ShDFLevel[SH_ERR_T_DIR])) {
972 tmp = sh_util_safe_name (ptr->name);
973 sh_error_handle ((ptr->class == SH_LEVEL_ALLIGNORE) ?
974 ShDFLevel[ptr->class] :
975 ShDFLevel[SH_ERR_T_DIR], FIL__, __LINE__,
976 0, MSG_FI_MISS, tmp);
977 SH_FREE(tmp);
978 }
979 }
980 if (sh.flag.reportonce == S_TRUE)
981 ptr->reported = S_TRUE;
982 }
983 else
984 {
985 /* exists (status >= 0), but was missing (reported == TRUE)
986 */
987 if (status >= 0 && ptr->reported == S_TRUE)
988 {
989 ptr->reported = S_FALSE;
990#if 0
991 /* obsoleted (really?) by the mandatory sh_files_filecheck()
992 * above, which will catch missing directories anyway
993 */
994 tmp = sh_util_safe_name (ptr->name);
995 sh_error_handle ((ptr->class == SH_LEVEL_ALLIGNORE) ?
996 ShDFLevel[ptr->class] :
997 ShDFLevel[SH_ERR_T_DIR],
998 FIL__, __LINE__, 0, MSG_FI_ADD,
999 tmp);
1000 SH_FREE(tmp);
1001#endif
1002 }
1003 else if (status == SH_FILE_UNKNOWN)
1004 {
1005 /* catchall
1006 */
1007 tmp = sh_util_safe_name (ptr->name);
1008 sh_error_handle (SH_ERR_INFO, FIL__, __LINE__, 0,
1009 MSG_FI_FAIL,
1010 tmp);
1011 SH_FREE(tmp);
1012 if (sh.flag.checkSum != SH_CHECK_INIT)
1013 sh_hash_set_visited_true(ptr->name);
1014 }
1015
1016 ++dcount;
1017 }
1018 ptr->checked = S_TRUE;
1019 ptr->childs_checked = S_TRUE;
1020 }
1021
1022 if (sig_urgent > 0) {
1023 SL_RETURN(dcount, _("sh_dirs_chk"));
1024 }
1025
1026 }
1027 SL_RETURN(dcount, _("sh_dirs_chk"));
1028}
1029
1030int sh_files_pushdir_prelink (char * str_s)
1031{
1032 return (sh_files_pushdir (SH_LEVEL_PRELINK, str_s));
1033}
1034
1035int sh_files_pushdir_user0 (char * str_s)
1036{
1037 return (sh_files_pushdir (SH_LEVEL_USER0, str_s));
1038}
1039
1040int sh_files_pushdir_user1 (char * str_s)
1041{
1042 return (sh_files_pushdir (SH_LEVEL_USER1, str_s));
1043}
1044
1045int sh_files_pushdir_attr (char * str_s)
1046{
1047 return (sh_files_pushdir (SH_LEVEL_ATTRIBUTES, str_s));
1048}
1049
1050int sh_files_pushdir_ro (char * str_s)
1051{
1052 return (sh_files_pushdir (SH_LEVEL_READONLY, str_s));
1053}
1054
1055int sh_files_pushdir_log (char * str_s)
1056{
1057 return (sh_files_pushdir (SH_LEVEL_LOGFILES, str_s));
1058}
1059
1060int sh_files_pushdir_glog (char * str_s)
1061{
1062 return (sh_files_pushdir (SH_LEVEL_LOGGROW, str_s));
1063}
1064
1065int sh_files_pushdir_noig (char * str_s)
1066{
1067 return (sh_files_pushdir (SH_LEVEL_NOIGNORE, str_s));
1068}
1069
1070int sh_files_pushdir_allig (char * str_s)
1071{
1072 return (sh_files_pushdir (SH_LEVEL_ALLIGNORE, str_s));
1073}
1074
1075static int which_dirList = 1;
1076
1077int set_dirList (int which)
1078{
1079 if (which == 2)
1080 which_dirList = 2;
1081 else
1082 which_dirList = 1;
1083 return 0;
1084}
1085
1086int sh_files_push_dir_int (int class, char * tail, int len, int rdepth)
1087{
1088 zAVLTree * tree;
1089 dirstack_t * new_item_ptr;
1090 char * dirName;
1091 int ret;
1092
1093 SL_ENTER(_("sh_files_push_dir_int"));
1094
1095 dirName = SH_ALLOC(len+1);
1096 sl_strlcpy(dirName, tail, len+1);
1097
1098 new_item_ptr = (dirstack_t * ) SH_ALLOC (sizeof(dirstack_t));
1099
1100 new_item_ptr->name = dirName;
1101 new_item_ptr->class = class;
1102 new_item_ptr->check_mask = sh_files_maskof(class);
1103 new_item_ptr->rdepth = rdepth;
1104 new_item_ptr->checked = S_FALSE;
1105 new_item_ptr->reported = S_FALSE;
1106 new_item_ptr->childs_checked = S_FALSE;
1107
1108 if (which_dirList == 1)
1109 {
1110 tree = zdirListOne;
1111 }
1112 else
1113 {
1114 tree = zdirListTwo;
1115 }
1116
1117 if (tree == NULL)
1118 {
1119 tree = zAVLAllocTree (zdirstack_key);
1120 if (tree == NULL)
1121 {
1122 (void) safe_logger (0, 0, getpid());
1123 aud__exit(FIL__, __LINE__, EXIT_FAILURE);
1124 }
1125 if (which_dirList == 1)
1126 zdirListOne = tree;
1127 else
1128 zdirListTwo = tree;
1129 }
1130
1131 ret = zAVLInsert (tree, new_item_ptr);
1132
1133 if (-1 == ret)
1134 {
1135 (void) safe_logger (0, 0, getpid());
1136 aud__exit(FIL__, __LINE__, EXIT_FAILURE);
1137 }
1138 if (3 == ret)
1139 sh_error_handle ((-1), FIL__, __LINE__, 0, MSG_FI_DOUBLE,
1140 dirName);
1141
1142 SL_RETURN(0, _("sh_files_push_dir_int"));
1143}
1144
1145static int sh_files_pushdir (int class, char * str_s)
1146{
1147 char * tmp;
1148 int len;
1149 int rdepth = 0;
1150 char * tail = NULL;
1151
1152#ifdef HAVE_GLOB_H
1153 glob_t pglob;
1154 int globstatus = -1;
1155 unsigned int gloop;
1156#endif
1157
1158 SL_ENTER(_("sh_files_pushdir"));
1159
1160 if (sh.flag.opts == 1) {
1161 sh_files_delfilestack ();
1162 sh_files_deldirstack ();
1163 }
1164
1165 if (str_s == NULL)
1166 SL_RETURN((-1), _("sh_files_pushdir"));
1167
1168
1169 if (str_s[0] != '/')
1170 {
1171 rdepth = strtol(str_s, &tail, 10);
1172 if (tail == str_s)
1173 SL_RETURN((-1), _("sh_files_pushdir"));
1174 }
1175 else
1176 tail = str_s;
1177
1178
1179 if (rdepth < (-1) || tail == str_s || rdepth > 99)
1180 rdepth = (-2);
1181
1182 len = sl_strlen(tail);
1183
1184 if (len >= PATH_MAX)
1185 {
1186 tmp = sh_util_safe_name (tail);
1187 sh_error_handle ((-1), FIL__, __LINE__, 0, MSG_FI_2LONG,
1188 tmp);
1189 SH_FREE(tmp);
1190 SL_RETURN((-1), _("sh_files_pushdir"));
1191 }
1192 else if (len < 1)
1193 {
1194 SL_RETURN((-1), _("sh_files_pushdir"));
1195 }
1196 else if (tail[0] != '/')
1197 {
1198 tmp = sh_util_safe_name (tail);
1199 sh_error_handle ((-1), FIL__, __LINE__, 0, MSG_FI_NOPATH,
1200 tmp);
1201 SH_FREE(tmp);
1202 SL_RETURN((-1), _("sh_files_pushdir"));
1203 }
1204 else
1205 {
1206
1207 if (tail[len-1] == '/' && len > 1)
1208 {
1209 tail[len-1] = '\0';
1210 --len;
1211 }
1212
1213 }
1214
1215#ifdef HAVE_GLOB_H
1216 if (0 == sh_files_has_metachar(tail))
1217 {
1218 sh_files_push_dir_int (class, tail, len, rdepth);
1219 }
1220 else
1221 {
1222 pglob.gl_offs = 0;
1223 globstatus = glob (tail, 0, sh_files_globerr, &pglob);
1224
1225 if (globstatus == 0 && pglob.gl_pathc > 0)
1226 {
1227 for (gloop = 0; gloop < (unsigned int) pglob.gl_pathc; ++gloop)
1228 sh_files_push_dir_int (class,
1229 pglob.gl_pathv[gloop],
1230 sl_strlen(pglob.gl_pathv[gloop]),
1231 rdepth);
1232 }
1233 else
1234 {
1235 tmp = sh_util_safe_name (tail);
1236
1237 if (pglob.gl_pathc == 0
1238#ifdef GLOB_NOMATCH
1239 || globstatus == GLOB_NOMATCH
1240#endif
1241 )
1242 sh_error_handle (SH_ERR_ERR, FIL__, __LINE__,
1243 globstatus, MSG_FI_GLOB,
1244 _("No matches found"), tmp);
1245#ifdef GLOB_NOSPACE
1246 else if (globstatus == GLOB_NOSPACE)
1247 sh_error_handle (SH_ERR_ERR, FIL__, __LINE__,
1248 globstatus, MSG_FI_GLOB,
1249 _("Out of memory"), tmp);
1250#endif
1251#ifdef GLOB_ABORTED
1252 else if (globstatus == GLOB_ABORTED)
1253 sh_error_handle (SH_ERR_ERR, FIL__, __LINE__,
1254 globstatus, MSG_FI_GLOB,
1255 _("Read error"), tmp);
1256#endif
1257 else
1258 sh_error_handle (SH_ERR_ERR, FIL__, __LINE__,
1259 globstatus, MSG_FI_GLOB,
1260 _("Unknown error"), tmp);
1261 SH_FREE(tmp);
1262 }
1263
1264 globfree(&pglob);
1265 }
1266#else
1267 sh_files_push_dir_int (class, tail, len, rdepth);
1268#endif
1269
1270 SL_RETURN((0), _("sh_files_pushdir"));
1271}
1272
1273struct sh_dirent {
1274 /* char sh_d_name[NAME_MAX + 2]; */
1275 char * sh_d_name;
1276 struct sh_dirent * next;
1277};
1278
1279static void kill_sh_dirlist (struct sh_dirent * dirlist)
1280{
1281 struct sh_dirent * this;
1282
1283 while (dirlist)
1284 {
1285 this = dirlist->next;
1286 SH_FREE(dirlist->sh_d_name);
1287 SH_FREE(dirlist);
1288 dirlist = this;
1289 }
1290 return;
1291}
1292
1293/* -- add an entry to a directory listing
1294 */
1295static struct sh_dirent * addto_sh_dirlist (struct dirent * thisEntry,
1296 struct sh_dirent * dirlist)
1297{
1298 struct sh_dirent * this;
1299 int i;
1300
1301 if (thisEntry == NULL)
1302 return dirlist;
1303
1304 i = sl_strlen(thisEntry->d_name);
1305 if (i == 0)
1306 return dirlist;
1307 ++i;
1308
1309 this = SH_ALLOC(sizeof(struct sh_dirent));
1310 if (!this)
1311 return dirlist;
1312
1313 this->sh_d_name = SH_ALLOC(i);
1314 sl_strlcpy(this->sh_d_name, thisEntry->d_name, i);
1315
1316 this->next = dirlist;
1317 return this;
1318}
1319
1320static int sh_check_hardlinks = S_TRUE;
1321
1322/* Simply sets our boolean as to whether this check is active
1323 */
1324int sh_files_check_hardlinks (char * opt)
1325{
1326 int i;
1327 SL_ENTER(_("sh_files_check_hardlinks"));
1328 i = sh_util_flagval(opt, &sh_check_hardlinks);
1329 SL_RETURN(i, _("sh_files_check_hardlinks"));
1330}
1331
1332struct sh_hle_struct {
1333 long offset;
1334 char * path;
1335 struct sh_hle_struct * next;
1336};
1337
1338static struct sh_hle_struct * sh_hl_exc = NULL;
1339
1340int sh_files_hle_reg (char * str)
1341{
1342 long offset;
1343 size_t len;
1344 char * path;
1345
1346 struct sh_hle_struct * tmp = sh_hl_exc;
1347
1348 SL_ENTER(_("sh_files_hle_reg"));
1349
1350 /* Free the linked list if called with NULL argument
1351 */
1352 if (str == NULL)
1353 {
1354 while (tmp)
1355 {
1356 sh_hl_exc = tmp->next;
1357 SH_FREE(tmp->path);
1358 SH_FREE(tmp);
1359 tmp = sh_hl_exc;
1360 }
1361 sh_hl_exc = NULL;
1362 SL_RETURN(0, _("sh_files_hle_reg"));
1363 }
1364
1365 /* We expect 'offset:/path'
1366 */
1367 offset = strtol(str, &path, 0);
1368 if ((path == NULL) || (*path == '\0') || (*path != ':') || (path[1] != '/'))
1369 {
1370 SL_RETURN(-1, _("sh_files_hle_reg"));
1371 }
1372 ++path;
1373 len = 1 + sl_strlen(path);
1374
1375 tmp = SH_ALLOC(sizeof(struct sh_hle_struct));
1376 tmp->path = SH_ALLOC(len);
1377 sl_strlcpy (tmp->path, path, len);
1378 tmp->offset = offset;
1379 tmp->next = sh_hl_exc;
1380 sh_hl_exc = tmp;
1381
1382 SL_RETURN(0, _("sh_files_hle_reg"));
1383}
1384
1385#if !defined(HOST_IS_DARWIN)
1386static int sh_files_hle_test (int offset, char * path)
1387{
1388 struct sh_hle_struct * tmp = sh_hl_exc;
1389
1390 SL_ENTER(_("sh_files_hle_reg"));
1391
1392 while(tmp)
1393 {
1394 if ((offset == tmp->offset) && (0 == strcmp(path, tmp->path)))
1395 {
1396 SL_RETURN(0, _("sh_files_hle_test"));
1397 }
1398 tmp = tmp->next;
1399 }
1400 SL_RETURN(-1, _("sh_files_hle_test"));
1401}
1402#endif
1403
1404/* -- check a single directory and its content
1405 */
1406static int sh_files_checkdir (int iclass, int idepth, char * iname,
1407 char * relativeName)
1408{
1409 struct sh_dirent * dirlist = NULL;
1410 struct sh_dirent * dirlist_orig = NULL;
1411
1412 DIR * thisDir = NULL;
1413 struct dirent * thisEntry;
1414 int status;
1415 int dummy = S_FALSE;
1416 dir_type theDir;
1417 ShFileType checkit;
1418
1419
1420 file_type theFile;
1421 char * tmpname;
1422 char * tmpcat;
1423
1424 int rdepth = 0;
1425 int class = 0;
1426 int rdepth_next;
1427 int class_next;
1428 int file_class_next;
1429
1430 int checked_flag = S_FALSE;
1431 int cchecked_flag = S_FALSE;
1432
1433 dirstack_t * dst_ptr;
1434 dirstack_t * tmp_ptr;
1435
1436 int hardlink_num = 0;
1437
1438
1439 SL_ENTER(_("sh_files_checkdir"));
1440
1441 if (sig_urgent > 0) {
1442 SL_RETURN((0), _("sh_files_checkdir"));
1443 }
1444
1445 if (iname == NULL || idepth < (-1))
1446 SL_RETURN((-1), _("sh_files_checkdir"));
1447
1448 if (idepth < 0)
1449 {
1450 /* hash_remove_tree (iname); */
1451 SL_RETURN((0), _("sh_files_checkdir"));
1452 }
1453
1454 rdepth = idepth;
1455 class = iclass;
1456
1457 tmpname = sh_util_safe_name (iname);
1458
1459 /* ---- check for obscure name ----
1460 */
1461 if (iclass != SH_LEVEL_ALLIGNORE)
1462 {
1463 sh_util_obscurename (ShDFLevel[SH_ERR_T_NAME], iname, S_TRUE);
1464 }
1465
1466 if (flag_err_info == SL_TRUE)
1467 {
1468 sh_error_handle ((-1), FIL__, __LINE__, 0, MSG_FI_CHK, tmpname);
1469 }
1470
1471 /* ---- check input ----
1472 */
1473 if ( sl_strlen(iname) >= PATH_MAX)
1474 {
1475 sh_error_handle (ShDFLevel[SH_ERR_T_DIR], FIL__, __LINE__, 0,
1476 MSG_FI_2LONG,
1477 tmpname);
1478 SH_FREE(tmpname);
1479 SL_RETURN((-1), _("sh_files_checkdir"));
1480 }
1481
1482 /* ---- check for absolute path ---- */
1483 if ( iname[0] != '/')
1484 {
1485 sh_error_handle (ShDFLevel[SH_ERR_T_DIR], FIL__, __LINE__, 0,
1486 MSG_FI_NOPATH,
1487 tmpname);
1488 SH_FREE(tmpname);
1489 SL_RETURN((-1), _("sh_files_checkdir"));
1490 }
1491
1492
1493 /* ---- stat the directory ----
1494 */
1495 sl_strlcpy (theFile.fullpath, iname, PATH_MAX);
1496
1497 (void) relativeName;
1498 status = sh_unix_getinfo (ShDFLevel[SH_ERR_T_DIR],
1499 iname,
1500 &theFile, NULL, iclass);
1501
1502 if ((sig_termfast == 1) || (sig_terminate == 1))
1503 {
1504 SL_RETURN((0), _("sh_files_checkdir"));
1505 }
1506
1507 if (status == -1)
1508 {
1509 SH_FREE(tmpname);
1510 SL_RETURN((-1), _("sh_files_checkdir"));
1511 }
1512
1513 if (theFile.c_mode[0] != 'd')
1514 {
1515 sh_error_handle (ShDFLevel[SH_ERR_T_DIR], FIL__, __LINE__, 0,
1516 MSG_FI_NODIR,
1517 tmpname);
1518 SH_FREE(tmpname);
1519 SL_RETURN((-1), _("sh_files_checkdir"));
1520 }
1521
1522 hardlink_num = theFile.hardlinks;
1523
1524
1525 /* ---- open directory for reading ----
1526 *
1527 * opendir() will fail with ENOTDIR if the path has been changed
1528 * to a non-directory in between lstat() and opendir().
1529 */
1530 thisDir = opendir (iname);
1531
1532 if (thisDir == NULL)
1533 {
1534 status = errno;
1535 sh_error_handle (ShDFLevel[SH_ERR_T_DIR], FIL__, __LINE__, 0,
1536 MSG_E_OPENDIR,
1537 sh_error_message (status), tmpname);
1538 SH_FREE(tmpname);
1539
1540 SL_RETURN((-1), _("sh_files_checkdir"));
1541 }
1542
1543 theDir.NumRegular = 0;
1544 theDir.NumDirs = 0;
1545 theDir.NumSymlinks = 0;
1546 theDir.NumFifos = 0;
1547 theDir.NumSockets = 0;
1548 theDir.NumCDev = 0;
1549 theDir.NumBDev = 0;
1550 theDir.NumAll = 0;
1551 theDir.TotalBytes = 0;
1552 sl_strlcpy (theDir.DirPath, iname, PATH_MAX);
1553
1554
1555 /* ---- read ----
1556 */
1557 do {
1558 thisEntry = readdir (thisDir);
1559 if (thisEntry != NULL)
1560 {
1561 ++theDir.NumAll;
1562 if (sl_strcmp (thisEntry->d_name, ".") == 0)
1563 {
1564 ++theDir.NumDirs;
1565 continue;
1566 }
1567 if (sl_strcmp (thisEntry->d_name, "..") == 0)
1568 {
1569 ++theDir.NumDirs;
1570 continue;
1571 }
1572 dirlist = addto_sh_dirlist (thisEntry, dirlist);
1573 }
1574 } while (thisEntry != NULL);
1575
1576 closedir (thisDir);
1577
1578 ++sh.statistics.dirs_checked;
1579
1580 dirlist_orig = dirlist;
1581
1582 do {
1583
1584 /* If the directory is empty, dirlist = NULL
1585 */
1586 if (!dirlist)
1587 break;
1588
1589 if (sig_termfast == 1)
1590 {
1591 SL_RETURN((0), _("sh_files_checkdir"));
1592 }
1593
1594 BREAKEXIT(sh_derr);
1595 if (0 == (rand() % 5))
1596 (void) sh_derr();
1597
1598 /* ---- Check the file. ----
1599 */
1600 tmpcat = SH_ALLOC(PATH_MAX);
1601 sl_strlcpy(tmpcat, iname, PATH_MAX);
1602 if (sl_strlen(tmpcat) > 1 || tmpcat[0] != '/')
1603 sl_strlcat(tmpcat, "/", PATH_MAX);
1604 sl_strlcat(tmpcat, dirlist->sh_d_name, PATH_MAX);
1605
1606 rdepth_next = rdepth - 1;
1607 class_next = class;
1608 file_class_next = class;
1609 checked_flag = -1;
1610 cchecked_flag = -1;
1611
1612 /* Wed Aug 24 2005 compare against dirListOne, dirListTwo
1613 * this fixes the problem that the directory special file
1614 * is checked with the policy of the parent directory
1615 */
1616 dst_ptr = (dirstack_t *) zAVLSearch(zdirListOne, tmpcat);
1617
1618 if (dst_ptr)
1619 {
1620 /* Tue Aug 6 22:13:27 CEST 2002 introduce file_class_next
1621 * this fixes the problem that a policy for the directory
1622 * inode erroneously becomes a policy for the directory itself.
1623 */
1624 file_class_next = dst_ptr->class;
1625 checked_flag = dst_ptr->checked;
1626 cchecked_flag = dst_ptr->childs_checked;
1627 }
1628
1629 if (checked_flag == -1)
1630 {
1631 dst_ptr = (dirstack_t *) zAVLSearch(zdirListTwo, tmpcat);
1632
1633 if (dst_ptr)
1634 {
1635 /* Tue Aug 6 22:13:27 CEST 2002 introduce file_class_next
1636 * this fixes the problem that a policy for the directory
1637 * inode erroneously becomes a policy for the directory itself.
1638 */
1639 file_class_next = dst_ptr->class;
1640 checked_flag = dst_ptr->checked;
1641 cchecked_flag = dst_ptr->childs_checked;
1642 }
1643 }
1644
1645 dst_ptr = (dirstack_t *) zAVLSearch(zfileList, tmpcat);
1646
1647 if (dst_ptr)
1648 {
1649 /* Tue Aug 6 22:13:27 CEST 2002 introduce file_class_next
1650 * this fixes the problem that a policy for the directory
1651 * inode erroneously becomes a policy for the directory itself.
1652 */
1653 file_class_next = dst_ptr->class;
1654 checked_flag = dst_ptr->checked;
1655 /* not set, hence always FALSE */
1656 /* cchecked_flag = dst_ptr->childs_checked; */
1657 }
1658
1659 /* ---- Has been checked already. ----
1660 */
1661 if (checked_flag == S_TRUE && cchecked_flag == S_TRUE)
1662 {
1663 /* Mar 11 2004 get ftype for complete directory count
1664 */
1665 checkit = sh_unix_get_ftype(tmpcat);
1666 if (checkit == SH_FILE_DIRECTORY)
1667 {
1668 ++theDir.NumDirs;
1669 }
1670 SH_FREE(tmpcat);
1671 dirlist = dirlist->next;
1672 continue;
1673 }
1674
1675 /* --- May be true, false, or not found. ---
1676 */
1677 if (checked_flag == S_TRUE)
1678 {
1679 /* -- need only the file type --
1680 */
1681 checkit = sh_unix_get_ftype(tmpcat);
1682 }
1683 else
1684 {
1685 /* -- need to check the file itself --
1686 */
1687 if (dst_ptr && sh.flag.reportonce == S_TRUE)
1688 dummy = dst_ptr->reported;
1689
1690 checkit = sh_files_filecheck (file_class_next,
1691 iname,
1692 dirlist->sh_d_name,
1693 &dummy, 0);
1694
1695 if (dst_ptr && checked_flag == S_FALSE)
1696 dst_ptr->checked = S_TRUE;
1697 /* Thu Mar 7 15:09:40 CET 2002 Propagate the 'reported' flag
1698 */
1699 if (dst_ptr && sh.flag.reportonce == S_TRUE)
1700 dst_ptr->reported = dummy;
1701 }
1702
1703 if (checkit == SH_FILE_REGULAR)
1704 ++theDir.NumRegular;
1705
1706 else if (checkit == SH_FILE_DIRECTORY)
1707 {
1708 ++theDir.NumDirs;
1709 if (rdepth_next >= 0 && cchecked_flag != S_TRUE)
1710 {
1711 rdepth_next = rdepth - 1;
1712
1713 /* check whether the new directory is in the
1714 * list with a recursion depth already defined
1715 */
1716 checked_flag = -1;
1717 cchecked_flag = -1;
1718
1719 tmp_ptr = (dirstack_t *) zAVLSearch(zdirListOne, tmpcat);
1720
1721 if (tmp_ptr)
1722 {
1723 TPT((0, FIL__, __LINE__,
1724 _("msg=<%s -> recursion depth %d\n>"),
1725 tmp_ptr->name, tmp_ptr->rdepth));
1726 rdepth_next = tmp_ptr->rdepth;
1727 class_next = tmp_ptr->class;
1728 /* 28. Aug 2001 reversed
1729 */
1730 cchecked_flag = tmp_ptr->childs_checked;
1731 checked_flag = tmp_ptr->checked;
1732 }
1733
1734 if (checked_flag == -1)
1735 {
1736 tmp_ptr = (dirstack_t *) zAVLSearch(zdirListTwo, tmpcat);
1737
1738 if (tmp_ptr)
1739 {
1740 TPT((0, FIL__, __LINE__,
1741 _("msg=<%s -> recursion depth %d\n>"),
1742 tmp_ptr->name, tmp_ptr->rdepth));
1743 rdepth_next = tmp_ptr->rdepth;
1744 class_next = tmp_ptr->class;
1745 /* 28. Aug 2001 reversed
1746 */
1747 cchecked_flag = tmp_ptr->childs_checked;
1748 checked_flag = tmp_ptr->checked;
1749 }
1750 }
1751
1752 if (cchecked_flag == S_FALSE)
1753 {
1754 sh_files_checkdir (class_next, rdepth_next, tmpcat,
1755 dirlist->sh_d_name);
1756 tmp_ptr->childs_checked = S_TRUE;
1757 /*
1758 * 04. Feb 2006 avoid double checking
1759 */
1760 tmp_ptr->checked = S_TRUE;
1761 }
1762 else if (checked_flag == -1)
1763 sh_files_checkdir (class_next, rdepth_next, tmpcat,
1764 dirlist->sh_d_name);
1765
1766 }
1767 }
1768
1769 else if (checkit == SH_FILE_SYMLINK) ++theDir.NumSymlinks;
1770 else if (checkit == SH_FILE_FIFO) ++theDir.NumFifos;
1771 else if (checkit == SH_FILE_SOCKET) ++theDir.NumSockets;
1772 else if (checkit == SH_FILE_CDEV) ++theDir.NumCDev;
1773 else if (checkit == SH_FILE_BDEV) ++theDir.NumBDev;
1774
1775 SH_FREE(tmpcat);
1776
1777 if ((sig_termfast == 1) || (sig_terminate == 1))
1778 {
1779 SL_RETURN((0), _("sh_files_checkdir"));
1780 }
1781
1782 dirlist = dirlist->next;
1783
1784 if (dst_ptr)
1785 dst_ptr->childs_checked = S_TRUE;
1786
1787 } while (dirlist != NULL);
1788
1789 if (flag_err_info == SL_TRUE)
1790 {
1791 sh_error_handle ((-1), FIL__, __LINE__, 0, MSG_FI_DSUM,
1792 theDir.NumDirs,
1793 theDir.NumRegular,
1794 theDir.NumSymlinks,
1795 theDir.NumFifos,
1796 theDir.NumSockets,
1797 theDir.NumCDev,
1798 theDir.NumBDev);
1799 }
1800
1801 kill_sh_dirlist (dirlist_orig);
1802
1803#if !defined(HOST_IS_DARWIN)
1804 /*
1805 * Hardlink check; not done on MacOS X because of resource forks
1806 */
1807 if ((sh_check_hardlinks == S_TRUE) && (hardlink_num != theDir.NumDirs))
1808 {
1809 if (0 != sh_files_hle_test(hardlink_num-theDir.NumDirs, iname))
1810 {
1811 tmpcat = SH_ALLOC(strlen(tmpname) + 256);
1812 sl_snprintf(tmpcat, strlen(tmpname) + 256,
1813 _("%s: subdirectory count (%d) != hardlinks (%d)"),
1814 tmpname, theDir.NumDirs, hardlink_num);
1815 sh_error_handle (ShDFLevel[SH_ERR_T_DIR], FIL__, __LINE__, 0,
1816 MSG_E_SUBGEN, tmpcat, _("sh_files_checkdir"));
1817 SH_FREE(tmpcat);
1818 }
1819 }
1820#endif
1821
1822 SH_FREE(tmpname);
1823
1824 SL_RETURN((0), _("sh_files_checkdir"));
1825}
1826
1827int get_the_fd (SL_TICKET ticket);
1828
1829
1830static ShFileType sh_files_filecheck (int class, char * dirName,
1831 char * fileName,
1832 int * reported,
1833 int rsrcflag)
1834{
1835 /* 28 Aug 2001 allow NULL fileName
1836 */
1837 char fullpath[PATH_MAX];
1838 char fileHash[2*(KEY_LEN + 1)];
1839 int status;
1840 file_type theFile;
1841 char * tmpdir;
1842 char * tmpname;
1843 struct utimbuf utime_buf;
1844
1845 SL_ENTER(_("sh_files_filecheck"));
1846
1847 BREAKEXIT(sh_derr);
1848 if (0 == (rand() % 2))
1849 (void) sh_derr();
1850
1851 /* fileName may be NULL if this is a directory
1852 */
1853 if (dirName == NULL /* || fileName == NULL */)
1854 {
1855 sh_error_handle ((-1), FIL__, __LINE__, 0, MSG_FI_NULL);
1856 SL_RETURN(SH_FILE_UNKNOWN, _("sh_files_filecheck"));
1857 }
1858
1859 if ((fileName != NULL) && (class != SH_LEVEL_ALLIGNORE) &&
1860 (0 != sh_util_obscurename (ShDFLevel[SH_ERR_T_NAME],
1861 fileName, S_FALSE)))
1862 {
1863 if ((dirName != NULL) && (dirName[0] == '/') && (dirName[1] == '\0'))
1864 {
1865 tmpname = sh_util_safe_name (fileName);
1866 sh_error_handle (ShDFLevel[SH_ERR_T_NAME], FIL__, __LINE__, 0,
1867 MSG_FI_OBSC2,
1868 "", tmpname);
1869 SH_FREE(tmpname);
1870 }
1871 else
1872 {
1873 tmpdir = sh_util_safe_name (dirName);
1874 tmpname = sh_util_safe_name (fileName);
1875 sh_error_handle (ShDFLevel[SH_ERR_T_NAME], FIL__, __LINE__, 0,
1876 MSG_FI_OBSC2,
1877 tmpdir, tmpname);
1878 SH_FREE(tmpname);
1879 SH_FREE(tmpdir);
1880 }
1881 }
1882
1883 /* sh_files_fullpath accepts NULL fileName
1884 */
1885 if (0 != sh_files_fullpath (dirName, fileName, fullpath))
1886 {
1887 tmpdir = sh_util_safe_name (dirName);
1888 tmpname = sh_util_safe_name (fileName);
1889 sh_error_handle (ShDFLevel[SH_ERR_T_FILE], FIL__, __LINE__, 0,
1890 MSG_FI_2LONG2,
1891 tmpdir, tmpname);
1892 SH_FREE(tmpname);
1893 SH_FREE(tmpdir);
1894 SL_RETURN(SH_FILE_UNKNOWN, _("sh_files_filecheck"));
1895 }
1896
1897
1898 /* stat the file and determine checksum (if a regular file)
1899 */
1900 sl_strlcpy (theFile.fullpath, fullpath, PATH_MAX);
1901 theFile.check_mask = sh_files_maskof(class);
1902 theFile.reported = (*reported);
1903
1904 TPT(( 0, FIL__, __LINE__, _("msg=<checking file: %s>\n"), fullpath));
1905
1906 status = sh_unix_getinfo ( (class == SH_LEVEL_ALLIGNORE) ?
1907 ShDFLevel[class] : ShDFLevel[SH_ERR_T_FILE],
1908 fileName,
1909 &theFile, fileHash, class);
1910
1911 if (status != 0)
1912 {
1913 TPT(( 0, FIL__, __LINE__, _("msg=<file: %s> status=<%d>\n"),
1914 fullpath, status));
1915 if (class == SH_LEVEL_ALLIGNORE && sh.flag.checkSum != SH_CHECK_INIT)
1916 sh_hash_set_visited_true (fullpath);
1917 SL_RETURN(SH_FILE_UNKNOWN, _("sh_files_filecheck"));
1918 }
1919
1920 if (sig_termfast == 1) {
1921 goto ret_point;
1922 }
1923
1924 /* report
1925 */
1926 if ((flag_err_debug == SL_TRUE) && (theFile.c_mode[0] == '-'))
1927 {
1928 tmpname = sh_util_safe_name (fullpath); /* fixed in 1.5.4 */
1929 sh_error_handle ((-1), FIL__, __LINE__, 0, MSG_FI_CSUM,
1930 fileHash, tmpname);
1931 SH_FREE(tmpname);
1932 }
1933 ++sh.statistics.files_checked;
1934
1935 if ( sh.flag.checkSum == SH_CHECK_INIT && sh.flag.update == S_FALSE )
1936 {
1937 sh_hash_pushdata (&theFile, fileHash);
1938 }
1939 else if (sh.flag.checkSum == SH_CHECK_INIT && sh.flag.update == S_TRUE )
1940 {
1941 if (0 == sh_hash_compdata (class, &theFile, fileHash, NULL, -1))
1942 {
1943 sh_hash_pushdata (&theFile, fileHash);
1944 }
1945 }
1946 else if (sh.flag.checkSum == SH_CHECK_CHECK
1947 /* && theFile.c_mode[0] == '-' */
1948 /* && class != SH_LEVEL_ALLIGNORE */
1949 )
1950 {
1951 sh_hash_compdata (class, &theFile, fileHash, NULL, -1);
1952 }
1953
1954 (*reported) = theFile.reported;
1955
1956 /* reset the access time
1957 */
1958 if (class == SH_LEVEL_NOIGNORE && (theFile.check_mask & MODI_ATM) != 0)
1959 {
1960 utime_buf.actime = (time_t) theFile.atime;
1961 utime_buf.modtime = (time_t) theFile.mtime;
1962#if !defined(O_NOATIME)
1963 retry_aud_utime (FIL__, __LINE__, fullpath, &utime_buf);
1964#endif
1965 }
1966
1967#ifdef HOST_IS_DARWIN
1968 /*
1969 * Check for resource fork
1970 */
1971 if ( (theFile.c_mode[0] != 'd') && (rsrcflag == 0) )
1972 {
1973 int dummy;
1974 static int rsrc_init = 0;
1975 static char rsrc[17];
1976 char testpath[PATH_MAX];
1977
1978 if (rsrc_init == 0) {
1979 sl_strlcpy(rsrc, _("..namedfork/rsrc"), 17);
1980 rsrc_init = 1;
1981 }
1982 sl_strlcpy (testpath, fullpath, PATH_MAX);
1983 sl_strlcat (testpath, "/", PATH_MAX);
1984 sl_strlcat (testpath, rsrc, PATH_MAX);
1985
1986 if (sl_strlen(testpath) == (17 + sl_strlen(fullpath)))
1987 {
1988 if (0 == sh_unix_file_stat (testpath))
1989 {
1990 sh_files_filecheck (class, fullpath, rsrc, &dummy, 1);
1991 }
1992 }
1993 }
1994#else
1995 (void) rsrcflag; /* avoid compiler warning */
1996#endif
1997
1998 ret_point:
1999
2000 switch (theFile.c_mode[0])
2001 {
2002 case '-': SL_RETURN(SH_FILE_REGULAR, _("sh_files_filecheck"));
2003 case 'l': SL_RETURN(SH_FILE_SYMLINK, _("sh_files_filecheck"));
2004 case 'd': SL_RETURN(SH_FILE_DIRECTORY, _("sh_files_filecheck"));
2005 case 'c': SL_RETURN(SH_FILE_CDEV, _("sh_files_filecheck"));
2006 case 'b': SL_RETURN(SH_FILE_BDEV, _("sh_files_filecheck"));
2007 case '|': SL_RETURN(SH_FILE_FIFO, _("sh_files_filecheck"));
2008 case 's': SL_RETURN(SH_FILE_SOCKET, _("sh_files_filecheck"));
2009 default: SL_RETURN(SH_FILE_UNKNOWN, _("sh_files_filecheck"));
2010 }
2011
2012 /* notreached */
2013}
2014
2015/* concatenate statpath = testdir"/"d_name
2016 */
2017static int sh_files_fullpath (char * testdir, char * d_name, char * statpath)
2018{
2019 int llen = 0;
2020
2021 SL_ENTER(_("sh_files_fullpath"));
2022
2023 if (testdir != NULL)
2024 {
2025 if ( (llen = sl_strlen(testdir)) > (PATH_MAX-2) )
2026 SL_RETURN((-1),_("sh_files_fullpath"));
2027 sl_strlcpy(statpath, testdir, PATH_MAX - 1);
2028 }
2029 if (d_name != NULL)
2030 {
2031 if (llen > 1 || statpath[0] != '/')
2032 sl_strlcat(statpath, "/", PATH_MAX);
2033 if ((sl_strlen(d_name) + sl_strlen(statpath)) >= PATH_MAX)
2034 SL_RETURN((-1),_("sh_files_fullpath"));
2035 sl_strlcat(statpath, d_name, PATH_MAX);
2036 }
2037 if (statpath == NULL)
2038 SL_RETURN((-1),_("sh_files_fullpath"));
2039 SL_RETURN((0),_("sh_files_fullpath"));
2040}
2041
2042
2043/* -----------------------------------
2044 *
2045 * The following two routines serve to
2046 * verify that the user has selected
2047 * a proper setup for file policies.
2048 *
2049 * -----------------------------------
2050 */
2051static int check_file(char * name)
2052{
2053 dirstack_t * pfilL;
2054 zAVLCursor cursor;
2055
2056 SL_ENTER(_("check_file"));
2057
2058 if (SH_FILE_DIRECTORY == sh_unix_get_ftype(name))
2059 SL_RETURN(0, _("check_file"));
2060
2061 for (pfilL = (dirstack_t *) zAVLFirst (&cursor, zfileList); pfilL;
2062 pfilL = (dirstack_t *) zAVLNext (&cursor))
2063 {
2064 if (0 == strcmp(name, pfilL->name) &&
2065 (pfilL->check_mask & MODI_ATM) == 0 &&
2066 (pfilL->check_mask & MODI_CTM) == 0 &&
2067 (pfilL->check_mask & MODI_MTM) == 0)
2068 SL_RETURN(0, _("check_file"));
2069 }
2070 SL_RETURN((-1), _("check_file"));
2071}
2072
2073int sh_files_test_setup_int (zAVLTree * tree)
2074{
2075 int dlen, flen;
2076 zAVLCursor cursor1;
2077 zAVLCursor cursor2;
2078
2079 dirstack_t * pdirL;
2080 dirstack_t * pfilL;
2081
2082 SL_ENTER(_("sh_files_test_setup"));
2083
2084 for (pdirL = (dirstack_t *) zAVLFirst (&cursor1, tree); pdirL;
2085 pdirL = (dirstack_t *) zAVLNext (&cursor1))
2086 {
2087 dlen = strlen(pdirL->name);
2088
2089 for (pfilL = (dirstack_t *) zAVLFirst (&cursor2, zfileList); pfilL;
2090 pfilL = (dirstack_t *) zAVLNext (&cursor2))
2091 {
2092 flen = strlen(pfilL->name);
2093
2094 /* check whether file is in tree of dir
2095 */
2096 if ((pfilL->class == SH_LEVEL_READONLY) ||
2097 (pfilL->class == SH_LEVEL_NOIGNORE))
2098 {
2099 ; /* do nothing */
2100 }
2101 else
2102 {
2103 if ((flen > (dlen+1)) &&
2104 (pfilL->name[dlen] == '/') &&
2105 (NULL == strchr(&(pfilL->name[dlen+1]), '/')) && /*30-5-01*/
2106 (0 == strncmp(pfilL->name, pdirL->name, dlen)))
2107 {
2108 if ((pdirL->check_mask & MODI_ATM) != 0 ||
2109 (pdirL->check_mask & MODI_MTM) != 0 ||
2110 (pdirL->check_mask & MODI_CTM) != 0)
2111 {
2112 if (check_file (pdirL->name) != 0)
2113 sh_error_handle ((-1), FIL__, __LINE__, 0, MSG_FI_COLL,
2114 pdirL->name, pfilL->name);
2115 }
2116 }
2117 }
2118 }
2119 }
2120
2121 SL_RETURN((0), _("sh_files_test_setup"));
2122}
2123
2124int sh_files_test_double (zAVLTree * firstList, zAVLTree * secondList)
2125{
2126 int count;
2127 int retval = 0;
2128
2129 zAVLCursor cursor;
2130
2131 dirstack_t * first;
2132
2133 for (first = (dirstack_t *) zAVLFirst (&cursor, firstList); first;
2134 first = (dirstack_t *) zAVLNext (&cursor))
2135 {
2136
2137 if (NULL != zAVLSearch(secondList, first->name))
2138 {
2139 ++count;
2140 sh_error_handle ((-1), FIL__, __LINE__, 0, MSG_FI_DOUBLE,
2141 first->name);
2142 retval = 1;
2143 }
2144 }
2145 return retval;
2146}
2147
2148extern void aud_exit (char * file, int line, int fd);
2149
2150int sh_files_test_setup ()
2151{
2152 int retval = 0;
2153
2154 /* Test for modifications allowed in ReadOnly directory
2155 */
2156 sh_files_test_setup_int (zdirListOne);
2157 sh_files_test_setup_int (zdirListTwo);
2158
2159 /* Test for files/dirz defined twice
2160 */
2161 retval = sh_files_test_double (zdirListOne, zdirListTwo);
2162 if (retval != 0)
2163 aud_exit(FIL__, __LINE__, EXIT_FAILURE);
2164
2165 retval = sh_files_test_double (zdirListTwo, zdirListOne);
2166 if (retval != 0)
2167 aud_exit(FIL__, __LINE__, EXIT_FAILURE);
2168
2169
2170 /*
2171 retval = sh_files_test_double (zfileList, NULL);
2172 if (retval != 0)
2173 aud_exit(FIL__, __LINE__, EXIT_FAILURE);
2174 */
2175 return 0;
2176}
2177
2178#endif
Note: See TracBrowser for help on using the repository browser.