Line data Source code
1 : // SPDX-License-Identifier: GPL-3.0-or-later
2 : // Copyright (C) 2014 Andrea Mazzoleni
3 :
4 : #include "portable.h"
5 :
6 : #include "support.h"
7 : #include "search.h"
8 :
9 : /****************************************************************************/
10 : /* search */
11 :
12 1913697 : static void search_file(struct snapraid_state* state, const char* path, data_off_t size, int64_t mtime_sec, int mtime_nsec)
13 : {
14 : struct snapraid_search_file* file;
15 : tommy_uint32_t file_hash;
16 :
17 1913697 : file = malloc_nofail(sizeof(struct snapraid_search_file));
18 1913697 : file->path = strdup_nofail(path);
19 1913697 : file->size = size;
20 1913697 : file->mtime_sec = mtime_sec;
21 1913697 : file->mtime_nsec = mtime_nsec;
22 :
23 1913697 : file_hash = file_stamp_hash(file->size, file->mtime_sec, file->mtime_nsec);
24 :
25 1913697 : tommy_hashdyn_insert(&state->searchset, &file->node, file, file_hash);
26 1913697 : }
27 :
28 1913697 : void search_file_free(struct snapraid_search_file* file)
29 : {
30 1913697 : free(file->path);
31 1913697 : free(file);
32 1913697 : }
33 :
34 : struct search_file_compare_arg {
35 : const struct snapraid_state* state;
36 : const struct snapraid_block* block;
37 : const struct snapraid_file* file;
38 : unsigned char* buffer;
39 : data_off_t offset;
40 : unsigned read_size;
41 : int prevhash;
42 : };
43 :
44 19776 : int search_file_compare(const void* void_arg, const void* void_data)
45 : {
46 19776 : const struct search_file_compare_arg* arg = void_arg;
47 19776 : const struct snapraid_search_file* file = void_data;
48 19776 : const struct snapraid_state* state = arg->state;
49 : unsigned char buffer_hash[HASH_MAX];
50 19776 : const char* path = file->path;
51 : int f;
52 : ssize_t ret;
53 :
54 : /* compare file info */
55 19776 : if (arg->file->size != file->size)
56 0 : return -1;
57 :
58 19776 : if (arg->file->mtime_sec != file->mtime_sec)
59 0 : return -1;
60 :
61 19776 : if (arg->file->mtime_nsec != file->mtime_nsec)
62 0 : return -1;
63 :
64 : /* read the block and compare the hash */
65 19776 : f = open(path, O_RDONLY | O_BINARY);
66 19776 : if (f == -1) {
67 : /* LCOV_EXCL_START */
68 : if (errno == ENOENT) {
69 : log_fatal(EUSER, "DANGER! file '%s' disappeared.\n", path);
70 : log_fatal(EUSER, "If you moved it, please rerun the same command.\n");
71 : } else {
72 : log_fatal(errno, "Error opening file '%s'. %s.\n", path, strerror(errno));
73 : }
74 : exit(EXIT_FAILURE);
75 : /* LCOV_EXCL_STOP */
76 : }
77 :
78 19776 : ret = pread(f, arg->buffer, arg->read_size, arg->offset);
79 19776 : if (ret < 0 || (unsigned)ret != arg->read_size) {
80 : /* LCOV_EXCL_START */
81 : log_fatal(errno, "Error reading file '%s'. %s.\n", path, strerror(errno));
82 : exit(EXIT_FAILURE);
83 : /* LCOV_EXCL_STOP */
84 : }
85 :
86 19776 : ret = close(f);
87 19776 : if (ret != 0) {
88 : /* LCOV_EXCL_START */
89 : log_fatal(errno, "Error closing file '%s'. %s.\n", path, strerror(errno));
90 : exit(EXIT_FAILURE);
91 : /* LCOV_EXCL_STOP */
92 : }
93 :
94 : /* compute the hash */
95 19776 : if (arg->prevhash)
96 0 : memhash(state->prevhash, state->prevhashseed, buffer_hash, arg->buffer, arg->read_size);
97 : else
98 19776 : memhash(state->hash, state->hashseed, buffer_hash, arg->buffer, arg->read_size);
99 :
100 : /* check if the hash is matching */
101 19776 : if (memcmp(buffer_hash, arg->block->hash, BLOCK_HASH_SIZE) != 0)
102 2098 : return -1;
103 :
104 17678 : if (arg->read_size != state->block_size) {
105 : /* fill the remaining with 0 */
106 7004 : memset(arg->buffer + arg->read_size, 0, state->block_size - arg->read_size);
107 : }
108 :
109 17678 : return 0;
110 : }
111 :
112 679808 : int state_search_fetch(struct snapraid_state* state, int prevhash, struct snapraid_file* missing_file, block_off_t missing_file_pos, struct snapraid_block* missing_block, unsigned char* buffer)
113 : {
114 : struct snapraid_search_file* file;
115 : tommy_uint32_t file_hash;
116 : struct search_file_compare_arg arg;
117 :
118 679808 : arg.state = state;
119 679808 : arg.block = missing_block;
120 679808 : arg.file = missing_file;
121 679808 : arg.buffer = buffer;
122 679808 : arg.offset = state->block_size * (data_off_t)missing_file_pos;
123 679808 : arg.read_size = file_block_size(missing_file, missing_file_pos, state->block_size);
124 679808 : arg.prevhash = prevhash;
125 :
126 679808 : file_hash = file_stamp_hash(arg.file->size, arg.file->mtime_sec, arg.file->mtime_nsec);
127 :
128 : /* search in the hashtable, and also check if the data matches the hash */
129 679808 : file = tommy_hashdyn_search(&state->searchset, search_file_compare, &arg, file_hash);
130 679808 : if (!file)
131 662130 : return -1;
132 :
133 : /* if found, buffer is already set with data */
134 17678 : return 0;
135 : }
136 :
137 2619 : static void search_dir(struct snapraid_state* state, struct snapraid_disk* disk, const char* dir, const char* sub)
138 : {
139 : DIR* d;
140 :
141 2619 : d = opendir(dir);
142 2619 : if (!d) {
143 : /* LCOV_EXCL_START */
144 : log_fatal(errno, "Error opening directory '%s'. %s.\n", dir, strerror(errno));
145 : exit(EXIT_FAILURE);
146 : /* LCOV_EXCL_STOP */
147 : }
148 :
149 2109424 : while (1) {
150 : char path_next[PATH_MAX];
151 : char sub_next[PATH_MAX];
152 : char out[PATH_MAX];
153 2112043 : struct snapraid_filter* reason = 0;
154 : struct stat st;
155 : const char* name;
156 : struct dirent* dd;
157 :
158 : /* clear errno to detect erroneous conditions */
159 2112043 : errno = 0;
160 2112043 : dd = readdir(d);
161 2112043 : if (dd == 0 && errno != 0) {
162 : /* LCOV_EXCL_START */
163 : log_fatal(errno, "Error reading directory '%s'. %s.\n", dir, strerror(errno));
164 : exit(EXIT_FAILURE);
165 : /* LCOV_EXCL_STOP */
166 : }
167 2112043 : if (dd == 0) {
168 2619 : break; /* finished */
169 : }
170 :
171 : /* skip "." and ".." files */
172 2109424 : name = dd->d_name;
173 2109424 : if (name[0] == '.' && (name[1] == 0 || (name[1] == '.' && name[2] == 0)))
174 5238 : continue;
175 :
176 2104186 : pathprint(path_next, sizeof(path_next), "%s%s", dir, name);
177 2104186 : pathprint(sub_next, sizeof(sub_next), "%s%s", sub, name);
178 :
179 : /* exclude hidden files even before calling lstat() */
180 2104186 : if (disk != 0 && filter_hidden(state->filter_hidden, dd) != 0) {
181 0 : msg_verbose("Excluding hidden '%s'\n", path_next);
182 0 : continue;
183 : }
184 :
185 : /* exclude content files even before calling lstat() */
186 2104186 : if (disk != 0 && filter_content(&state->contentlist, path_next) != 0) {
187 0 : msg_verbose("Excluding content '%s'\n", path_next);
188 0 : continue;
189 : }
190 :
191 : #if HAVE_STRUCT_DIRENT_D_STAT
192 : /* convert dirent to lstat result */
193 : dirent_lstat(dd, &st);
194 :
195 : /*
196 : * If the st_mode field is missing, takes care to fill it using normal lstat()
197 : * at now this can happen only in Windows (with HAVE_STRUCT_DIRENT_D_STAT defined),
198 : * because we use a directory reading method that doesn't read info about ReparsePoint.
199 : * Note that here we cannot call here lstat_sync(), because we don't know what kind
200 : * of file is it, and lstat_sync() doesn't always work
201 : */
202 : if (st.st_mode == 0) {
203 : if (lstat(path_next, &st) != 0) {
204 : /* LCOV_EXCL_START */
205 : log_fatal(errno, "Error in stat file/directory '%s'. %s.\n", path_next, strerror(errno));
206 : exit(EXIT_FAILURE);
207 : /* LCOV_EXCL_STOP */
208 : }
209 : }
210 : #else
211 : /* get lstat info about the file */
212 2104186 : if (lstat(path_next, &st) != 0) {
213 : /* LCOV_EXCL_START */
214 : log_fatal(errno, "Error in stat file/directory '%s'. %s.\n", path_next, strerror(errno));
215 : exit(EXIT_FAILURE);
216 : /* LCOV_EXCL_STOP */
217 : }
218 : #endif
219 :
220 2104186 : if (S_ISREG(st.st_mode)) {
221 2037786 : if (disk == 0 || filter_path(&state->filterlist, &reason, disk->name, sub_next) == 0) {
222 1913697 : search_file(state, path_next, st.st_size, st.st_mtime, STAT_NSEC(&st));
223 : } else {
224 124089 : msg_verbose("Excluding link '%s' for rule '%s'\n", path_next, filter_type(reason, out, sizeof(out)));
225 : }
226 66400 : } else if (S_ISDIR(st.st_mode)) {
227 1914 : if (disk == 0 || filter_subdir(&state->filterlist, &reason, disk->name, sub_next) == 0) {
228 1884 : pathslash(path_next, sizeof(path_next));
229 1884 : pathslash(sub_next, sizeof(sub_next));
230 1884 : search_dir(state, disk, path_next, sub_next);
231 : } else {
232 30 : msg_verbose("Excluding directory '%s' for rule '%s'\n", path_next, filter_type(reason, out, sizeof(out)));
233 : }
234 : }
235 : }
236 :
237 2619 : if (closedir(d) != 0) {
238 : /* LCOV_EXCL_START */
239 : log_fatal(errno, "Error closing directory '%s'. %s.\n", dir, strerror(errno));
240 : exit(EXIT_FAILURE);
241 : /* LCOV_EXCL_STOP */
242 : }
243 2619 : }
244 :
245 1 : void state_search(struct snapraid_state* state, const char* dir)
246 : {
247 : char path[PATH_MAX];
248 :
249 1 : msg_progress("Importing...\n");
250 :
251 : /* add the final slash */
252 1 : pathimport(path, sizeof(path), dir);
253 1 : pathslash(path, sizeof(path));
254 :
255 1 : search_dir(state, 0, path, "");
256 1 : }
257 :
258 128 : void state_search_array(struct snapraid_state* state)
259 : {
260 : tommy_node* i;
261 :
262 : /* import from all the disks */
263 863 : for (i = state->disklist; i != 0; i = i->next) {
264 735 : struct snapraid_disk* disk = i->data;
265 :
266 : /* skip data disks that are not accessible */
267 735 : if (disk->skip_access)
268 1 : continue;
269 :
270 734 : msg_progress("Searching disk %s...\n", disk->name);
271 :
272 734 : search_dir(state, disk, disk->dir, "");
273 : }
274 128 : }
275 :
|