Line data Source code
1 : // SPDX-License-Identifier: GPL-3.0-or-later
2 : // Copyright (C) 2013 Andrea Mazzoleni
3 :
4 : #include "portable.h"
5 :
6 : #include "support.h"
7 : #include "import.h"
8 :
9 : /****************************************************************************/
10 : /* import */
11 :
12 : /**
13 : * Compare the hash of two import blocks.
14 : */
15 5460 : int import_block_hash_compare(const void* void_arg, const void* void_data)
16 : {
17 5460 : const unsigned char* arg = void_arg;
18 5460 : const struct snapraid_import_block* block = void_data;
19 :
20 5460 : return memcmp(arg, block->hash, BLOCK_HASH_SIZE);
21 : }
22 :
23 0 : int import_block_prevhash_compare(const void* void_arg, const void* void_data)
24 : {
25 0 : const unsigned char* arg = void_arg;
26 0 : const struct snapraid_import_block* block = void_data;
27 :
28 0 : return memcmp(arg, block->prevhash, BLOCK_HASH_SIZE);
29 : }
30 :
31 : /**
32 : * Compute the hash of the hash for an import block.
33 : * We just use the first 32 bit of the hash itself.
34 : */
35 700555 : static inline tommy_uint32_t import_block_hash(const unsigned char* hash)
36 : {
37 : /* the hash data is not aligned, and we cannot access it with a direct cast */
38 700555 : return hash[0] | ((uint32_t)hash[1] << 8) | ((uint32_t)hash[2] << 16) | ((uint32_t)hash[3] << 24);
39 : }
40 :
41 1771 : static void import_file(struct snapraid_state* state, const char* path, uint64_t size)
42 : {
43 : struct snapraid_import_file* file;
44 : block_off_t i;
45 : data_off_t offset;
46 : void* buffer;
47 : int ret;
48 : int f;
49 : int flags;
50 1771 : unsigned block_size = state->block_size;
51 : struct advise_struct advise;
52 :
53 1771 : file = malloc_nofail(sizeof(struct snapraid_import_file));
54 1771 : file->path = strdup_nofail(path);
55 1771 : file->size = size;
56 1771 : file->blockmax = (size + block_size - 1) / block_size;
57 1771 : file->blockimp = malloc_nofail(file->blockmax * sizeof(struct snapraid_import_block));
58 1771 : file->is_runtime = 1;
59 :
60 1771 : buffer = malloc_nofail(block_size);
61 :
62 1771 : advise_init(&advise, state->file_mode);
63 :
64 : /* open for read */
65 1771 : flags = O_RDONLY | O_BINARY | advise_flags(&advise);
66 1771 : f = open(path, flags);
67 1771 : if (f == -1) {
68 : /* LCOV_EXCL_START */
69 : log_fatal(errno, "Error opening file '%s'. %s.\n", path, strerror(errno));
70 : exit(EXIT_FAILURE);
71 : /* LCOV_EXCL_STOP */
72 : }
73 :
74 1771 : ret = advise_open(&advise, f);
75 1771 : if (ret != 0) {
76 : /* LCOV_EXCL_START */
77 : log_fatal(errno, "Error advising file '%s'. %s.\n", path, strerror(errno));
78 : exit(EXIT_FAILURE);
79 : /* LCOV_EXCL_STOP */
80 : }
81 :
82 1771 : offset = 0;
83 6304 : for (i = 0; i < file->blockmax; ++i) {
84 4533 : struct snapraid_import_block* block = &file->blockimp[i];
85 4533 : unsigned read_size = block_size;
86 4533 : if (read_size > size)
87 1767 : read_size = size;
88 :
89 4533 : ret = read(f, buffer, read_size);
90 4533 : if (ret < 0 || (unsigned)ret != read_size) {
91 : /* LCOV_EXCL_START */
92 : log_fatal(errno, "Error reading file '%s'. %s.\n", path, strerror(errno));
93 : exit(EXIT_FAILURE);
94 : /* LCOV_EXCL_STOP */
95 : }
96 :
97 4533 : block->file = file;
98 4533 : block->offset = offset;
99 4533 : block->size = read_size;
100 :
101 4533 : memhash(state->hash, state->hashseed, block->hash, buffer, read_size);
102 4533 : tommy_hashdyn_insert(&state->importset, &block->nodeset, block, import_block_hash(block->hash));
103 :
104 : /* if we are in a rehash state */
105 4533 : if (state->prevhash != HASH_UNDEFINED) {
106 : /* compute also the previous hash */
107 0 : memhash(state->prevhash, state->prevhashseed, block->prevhash, buffer, read_size);
108 0 : tommy_hashdyn_insert(&state->previmportset, &block->prevnodeset, block, import_block_hash(block->prevhash));
109 : }
110 :
111 4533 : offset += read_size;
112 4533 : size -= read_size;
113 : }
114 :
115 1771 : ret = close(f);
116 1771 : if (ret != 0) {
117 : /* LCOV_EXCL_START */
118 : log_fatal(errno, "Error closing file '%s'. %s.\n", path, strerror(errno));
119 : exit(EXIT_FAILURE);
120 : /* LCOV_EXCL_STOP */
121 : }
122 :
123 1771 : tommy_list_insert_tail(&state->importlist, &file->nodelist, file);
124 :
125 1771 : free(buffer);
126 1771 : }
127 :
128 1629 : static void import_dealloc(struct snapraid_state* state, const char* dir, struct snapraid_dealloc* dealloc)
129 : {
130 : char path[PATH_MAX];
131 : struct snapraid_import_file* file;
132 : block_off_t i;
133 : data_off_t offset;
134 : data_off_t size;
135 1629 : unsigned block_size = state->block_size;
136 :
137 1629 : pathcpy(path, sizeof(path), dir);
138 1629 : pathcat(path, sizeof(path), dealloc->sub);
139 1629 : size = dealloc->size;
140 :
141 1629 : file = malloc_nofail(sizeof(struct snapraid_import_file));
142 1629 : file->path = strdup_nofail(path);
143 1629 : file->size = size;
144 1629 : file->blockmax = (size + block_size - 1) / block_size;
145 1629 : file->blockimp = malloc_nofail(file->blockmax * sizeof(struct snapraid_import_block));
146 1629 : file->is_runtime = 0;
147 :
148 1629 : offset = 0;
149 3257 : for (i = 0; i < file->blockmax; ++i) {
150 1628 : struct snapraid_import_block* block = &file->blockimp[i];
151 1628 : unsigned read_size = block_size;
152 1628 : if (read_size > size)
153 1628 : read_size = size;
154 :
155 1628 : block->file = file;
156 1628 : block->offset = offset;
157 1628 : block->size = read_size;
158 :
159 1628 : memcpy(block->hash, dealloc->blockhash + i * BLOCK_HASH_SIZE, BLOCK_HASH_SIZE);
160 1628 : hash_invalid_set(block->prevhash);
161 :
162 : /* do not insert invalid hashes */
163 1628 : if (!hash_is_invalid(block->hash))
164 1628 : tommy_hashdyn_insert(&state->importset, &block->nodeset, block, import_block_hash(block->hash));
165 : /* do not insert prevhash */
166 :
167 1628 : offset += read_size;
168 1628 : size -= read_size;
169 : }
170 :
171 1629 : tommy_list_insert_tail(&state->importlist, &file->nodelist, file);
172 1629 : }
173 :
174 3400 : void import_file_free(struct snapraid_import_file* file)
175 : {
176 3400 : free(file->path);
177 3400 : free(file->blockimp);
178 3400 : free(file);
179 3400 : }
180 :
181 694394 : int state_import_fetch(struct snapraid_state* state, int rehash, struct snapraid_block* missing_block, unsigned char* buffer)
182 : {
183 : struct snapraid_import_block* block;
184 : int ret;
185 : int f;
186 694394 : const unsigned char* hash = missing_block->hash;
187 694394 : unsigned block_size = state->block_size;
188 : unsigned read_size;
189 : unsigned char buffer_hash[HASH_MAX];
190 : const char* path;
191 :
192 694394 : if (rehash) {
193 0 : block = tommy_hashdyn_search(&state->previmportset, import_block_prevhash_compare, hash, import_block_hash(hash));
194 : } else {
195 694394 : block = tommy_hashdyn_search(&state->importset, import_block_hash_compare, hash, import_block_hash(hash));
196 : }
197 694394 : if (!block)
198 688934 : return -1;
199 :
200 5460 : path = block->file->path;
201 5460 : read_size = block->size;
202 :
203 5460 : f = open(path, O_RDONLY | O_BINARY);
204 5460 : if (f == -1) {
205 : /* if no file, just skip it */
206 0 : if (errno == ENOENT && !block->file->is_runtime) {
207 0 : log_error(EUSER, "WARNING! Unexpected missing deallocated file '%s'.\n", path);
208 0 : return -1;
209 : }
210 :
211 : /* LCOV_EXCL_START */
212 : if (errno == ENOENT) {
213 : log_fatal(errno, "DANGER! file '%s' disappeared.\n", path);
214 : log_fatal(errno, "If you moved it, please rerun the same command.\n");
215 : } else {
216 : log_fatal(errno, "Error opening file '%s'. %s.\n", path, strerror(errno));
217 : }
218 : exit(EXIT_FAILURE);
219 : /* LCOV_EXCL_STOP */
220 : }
221 :
222 5460 : ret = pread(f, buffer, read_size, block->offset);
223 5460 : if (ret < 0 || (unsigned)ret != read_size) {
224 : /* LCOV_EXCL_START */
225 : log_fatal(errno, "Error reading file '%s'. %s.\n", path, strerror(errno));
226 : exit(EXIT_FAILURE);
227 : /* LCOV_EXCL_STOP */
228 : }
229 :
230 5460 : ret = close(f);
231 5460 : if (ret != 0) {
232 : /* LCOV_EXCL_START */
233 : log_fatal(errno, "Error closing file '%s'. %s.\n", path, strerror(errno));
234 : exit(EXIT_FAILURE);
235 : /* LCOV_EXCL_STOP */
236 : }
237 :
238 5460 : if (read_size != block_size) {
239 : /* fill the remaining with 0 */
240 2694 : memset(buffer + read_size, 0, block_size - read_size);
241 : }
242 :
243 : /* recheck the hash */
244 5460 : if (rehash)
245 0 : memhash(state->prevhash, state->prevhashseed, buffer_hash, buffer, read_size);
246 : else
247 5460 : memhash(state->hash, state->hashseed, buffer_hash, buffer, read_size);
248 :
249 5460 : if (memcmp(buffer_hash, hash, BLOCK_HASH_SIZE) != 0) {
250 : /* if hash mismatch, skip it */
251 0 : if (!block->file->is_runtime) {
252 0 : log_error(EUSER, "WARNING! Unexpected hash mismatch from deallocated file '%s'.\n", path);
253 0 : return -1;
254 : }
255 :
256 : /* LCOV_EXCL_START */
257 : log_fatal(EUSER, "Mismatch in data reading file '%s'.\n", path);
258 : log_fatal(EUSER, "Please don't change imported files while running.\n");
259 : exit(EXIT_FAILURE);
260 : /* LCOV_EXCL_STOP */
261 : }
262 :
263 5460 : return 0;
264 : }
265 :
266 1 : static void import_dir(struct snapraid_state* state, const char* dir)
267 : {
268 : DIR* d;
269 :
270 1 : d = opendir(dir);
271 1 : if (!d) {
272 : /* LCOV_EXCL_START */
273 : log_fatal(errno, "Error opening directory '%s'. %s.\n", dir, strerror(errno));
274 : exit(EXIT_FAILURE);
275 : /* LCOV_EXCL_STOP */
276 : }
277 :
278 1834 : while (1) {
279 : char path_next[PATH_MAX];
280 : struct stat st;
281 : const char* name;
282 : struct dirent* dd;
283 :
284 : /* clear errno to detect erroneous conditions */
285 1835 : errno = 0;
286 1835 : dd = readdir(d);
287 1835 : if (dd == 0 && errno != 0) {
288 : /* LCOV_EXCL_START */
289 : log_fatal(errno, "Error reading directory '%s'. %s.\n", dir, strerror(errno));
290 : exit(EXIT_FAILURE);
291 : /* LCOV_EXCL_STOP */
292 : }
293 1835 : if (dd == 0) {
294 1 : break; /* finished */
295 : }
296 :
297 : /* skip "." and ".." files */
298 1834 : name = dd->d_name;
299 1834 : if (name[0] == '.' && (name[1] == 0 || (name[1] == '.' && name[2] == 0)))
300 2 : continue;
301 :
302 1832 : pathprint(path_next, sizeof(path_next), "%s%s", dir, name);
303 :
304 : #if HAVE_STRUCT_DIRENT_D_STAT
305 : /* convert dirent to lstat result */
306 : dirent_lstat(dd, &st);
307 :
308 : /*
309 : * If the st_mode field is missing, takes care to fill it using normal lstat()
310 : * at now this can happen only in Windows (with HAVE_STRUCT_DIRENT_D_STAT defined),
311 : * because we use a directory reading method that doesn't read info about ReparsePoint.
312 : * Note that here we cannot call here lstat_sync(), because we don't know what kind
313 : * of file is it, and lstat_sync() doesn't always work
314 : */
315 : if (st.st_mode == 0) {
316 : if (lstat(path_next, &st) != 0) {
317 : /* LCOV_EXCL_START */
318 : log_fatal(errno, "Error in stat file/directory '%s'. %s.\n", path_next, strerror(errno));
319 : exit(EXIT_FAILURE);
320 : /* LCOV_EXCL_STOP */
321 : }
322 : }
323 : #else
324 : /* get lstat info about the file */
325 1832 : if (lstat(path_next, &st) != 0) {
326 : /* LCOV_EXCL_START */
327 : log_fatal(errno, "Error in stat file/directory '%s'. %s.\n", path_next, strerror(errno));
328 : exit(EXIT_FAILURE);
329 : /* LCOV_EXCL_STOP */
330 : }
331 : #endif
332 :
333 1832 : if (S_ISREG(st.st_mode)) {
334 1771 : import_file(state, path_next, st.st_size);
335 61 : } else if (S_ISDIR(st.st_mode)) {
336 0 : pathslash(path_next, sizeof(path_next));
337 0 : import_dir(state, path_next);
338 : }
339 : }
340 :
341 1 : if (closedir(d) != 0) {
342 : /* LCOV_EXCL_START */
343 : log_fatal(errno, "Error closing directory '%s'. %s.\n", dir, strerror(errno));
344 : exit(EXIT_FAILURE);
345 : /* LCOV_EXCL_STOP */
346 : }
347 1 : }
348 :
349 1 : void state_import(struct snapraid_state* state, const char* dir)
350 : {
351 : char path[PATH_MAX];
352 :
353 1 : msg_progress("Importing...\n");
354 :
355 : /* if the hash is not full */
356 1 : if (BLOCK_HASH_SIZE != HASH_MAX) {
357 : /* LCOV_EXCL_START */
358 : log_fatal(EUSER, "You cannot import files when using a reduced hash.\n");
359 : exit(EXIT_FAILURE);
360 : /* LCOV_EXCL_STOP */
361 : }
362 :
363 : /* add the final slash */
364 1 : pathimport(path, sizeof(path), dir);
365 1 : pathslash(path, sizeof(path));
366 :
367 1 : import_dir(state, path);
368 1 : }
369 :
370 2 : void state_dealloc(struct snapraid_state* state, const char* dir, tommy_list* dealloclist)
371 : {
372 : /* snapshot should be enabled */
373 2 : if (!state->snapshot)
374 0 : return;
375 :
376 : /* the hash must be full */
377 2 : if (BLOCK_HASH_SIZE != HASH_MAX)
378 0 : return;
379 :
380 1631 : for (tommy_node* i = tommy_list_head(dealloclist); i != 0; i = i->next) {
381 1629 : struct snapraid_dealloc* dealloc = i->data;
382 :
383 1629 : import_dealloc(state, dir, dealloc);
384 : }
385 : }
386 :
|