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