1 /*
2 integrit - file integrity verification system
3 Copyright (C) 2006 Ed L. Cashin
4 
5 This program is free software; you can redistribute it and/or
6 modify it under the terms of the GNU General Public License
7 as published by the Free Software Foundation; either version 2
8 of the License, or (at your option) any later version.
9 
10 This program is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13 GNU General Public License for more details.
14 
15 You should have received a copy of the GNU General Public License
16 along with this program; if not, write to the Free Software
17 Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
18 
19 */
20 /*
21  * Looks like struct dirent's on my Linux box always have DT_UNKNOWN
22  * as their d_type member:
23  *
24  * if (result)
25  *    printf("type: %d %s DT_UNKNOWN\t",
26  *           result->d_type, (result->d_type == DT_UNKNOWN) ? "==" : "!=");
27  *
28  * ... as the docs say they might.
29  */
30 
31 /* This file tree walker silently ignores files that vanish between the
32  * time the readdir is done and the time the lstat is done.
33  */
34 #include	<config.h>
35 #include	<stdio.h>
36 #include	<stdlib.h>
37 #include	<unistd.h>
38 #include	<string.h>
39 #include	<sys/stat.h>
40 #include	<sys/types.h>
41 #include	<dirent.h>
42 #include	<errno.h>
43 #include	"xstrdup.h"
44 #include	"xstradd.h"
45 #include	"elcwft.h"
46 #include	"elcwft_p.h"
47 #include	"elcerror.h"
48 #include	"elcerror_p.h"
49 #ifdef		ELC_FIND_LEAKS
50 #include	"leakfind.h"
51 #endif
52 
53 typedef struct wftnode {		/* walk file tree node */
54   DIR			*dir;
55   struct wftnode	*next;
56 } wftnode;
57 
is_directory(const char * path,struct stat * sb)58 inline static int is_directory(const char *path, struct stat *sb)
59 {
60     if (lstat(path, sb) == -1)
61       return -1;
62     return S_ISDIR(sb->st_mode);
63 }
64 
endoflist(wftnode * head)65 inline static wftnode *endoflist(wftnode *head)
66 {
67     wftnode	*np;
68 
69     for (np = head; np->next; np = np->next)
70       if (np->next && (! np->next->dir)) {
71 	/* clean up used node */
72 	free(np->next);
73 	np->next	 = NULL;
74 	break;
75       }
76     return np;
77 }
78 
backupdir(char * dirname)79 inline static int backupdir(char *dirname)
80 {
81     char	*lastslash	 = strrchr(dirname, '/');
82     if (! lastslash) {
83       errno	 = EINVAL;
84       return -1;
85     }
86     *lastslash	 = '\0';
87 
88     return 0;
89 }
90 
91 /* the function below is tied to the internal workings of
92  * walk_file_tree, and it helps make the big loop in walk_file_tree
93  * more readable/managable. */
finishdir(wftnode * np,wftnode * head,int * done,char * curr_dirname)94 inline static int finishdir(wftnode *np, wftnode *head,
95 			    int *done, char *curr_dirname)
96 {
97     if (np == head) {
98       *done	 = 1;
99       closedir(np->dir);
100       return 0;
101     }
102 
103     closedir(np->dir);
104     np->dir	 = NULL; /* signal to endoflist that we're done here */
105     if (backupdir(curr_dirname) == -1)
106       return -1;
107 
108     return 0;
109 }
110 
111 /* the old parameters were:
112  * const char *rootname, wft_cb_t callback, void *cbdata */
walk_file_tree(wft_context_t * ctx)113 int walk_file_tree(wft_context_t *ctx)
114 {
115     DIR			*root	 = opendir(ctx->rootname);
116     wftnode		dirs;
117     wftnode		*np;
118     char		*curr_dirname	 = xstrdup(ctx->rootname);
119     char		*path		 = NULL;
120     struct dirent	*result;
121     struct stat		statbuf;
122     int			ret, cb_ret;
123     int			done	 = 0;
124     int			err	 = 0;
125 
126     dirs.dir	 = root;
127     dirs.next	 = NULL;
128 
129     if (! root)
130       return WFT_ERROR;
131 
132     while (! done) {
133       /* find the deepest directory and maintain list */
134       np	 = endoflist(&dirs);
135 
136       /* while we don't want to go deeper into file tree */
137       while (! np->next) {
138 	result	 = readdir(np->dir);
139 	if (!result) {
140 	  /* no more entries in this dir */
141 	  if (finishdir(np, &dirs, &done, curr_dirname) == -1)
142 	    return WFT_ERROR;
143 	  break;
144 	}
145 	if (!strcmp(result->d_name, ".") || !strcmp(result->d_name, "..")) {
146 	  /* skip the current and parent dir */
147 	  continue;
148 	}
149 
150 	if (path)
151 	  free(path);
152 	path	= xstradd(curr_dirname, "/", result->d_name, NULL);
153 	if ((ret = is_directory(path, &statbuf)) == -1) {
154 	  if (errno == ENOENT)	/* no such file or directory */
155 	    continue;		/* silently skip the vanished file */
156 	  else
157 	    return WFT_ERROR;
158 	}
159 	cb_ret	= ctx->callback(path, &statbuf, ctx->cb_data);
160 	switch (cb_ret) {
161 	case WFT_ERROR:
162 	  if (ctx->options & WFT_STOP_ON_ERR)
163 	    return WFT_ERROR;
164           err = 1;
165 
166 	case WFT_PRUNE:
167 	  continue;
168 	}
169 
170 	if (ret == 1) { /* directory */
171 	  if (! (np->next = malloc(sizeof(* np->next))) )
172 	    return WFT_ERROR;
173 	  if (! (np->next->dir = opendir(path)) ) {
174 	    warn(__FUNCTION__, "Warning: cannot open directory (%s): %s",
175 		 path, strerror(errno));
176 	    if (ctx->options & WFT_STOP_ON_ERR)
177 	      return WFT_ERROR;
178 	    err = 1;
179 	    continue;
180 	  }
181 	  free(curr_dirname);
182 	  curr_dirname		 = xstrdup(path);
183 	  np->next->next	 = NULL;
184 	}
185       } /* end iterating through a given dir */
186     } /* end while not done */
187 
188     if (path)
189       free(path);
190     free(curr_dirname);
191     return (err? WFT_SOME_ERR: WFT_PROCEED);
192 }
193 
194