Line data Source code
1 : /*
2 : * Copyright (C) 2013 Andrea Mazzoleni
3 : *
4 : * This program is free software: you can redistribute it and/or modify
5 : * it under the terms of the GNU General Public License as published by
6 : * the Free Software Foundation, either version 3 of the License, or
7 : * (at your option) any later version.
8 : *
9 : * This program is distributed in the hope that it will be useful,
10 : * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 : * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 : * GNU General Public License for more details.
13 : *
14 : * You should have received a copy of the GNU General Public License
15 : * along with this program. If not, see <http://www.gnu.org/licenses/>.
16 : */
17 :
18 : #include "portable.h"
19 :
20 : #include "support.h"
21 : #include "import.h"
22 :
23 : /****************************************************************************/
24 : /* import */
25 :
26 : /**
27 : * Compare the hash of two import blocks.
28 : */
29 4472 : int import_block_hash_compare(const void* void_arg, const void* void_data)
30 : {
31 4472 : const unsigned char* arg = void_arg;
32 4472 : const struct snapraid_import_block* block = void_data;
33 :
34 4472 : return memcmp(arg, block->hash, BLOCK_HASH_SIZE);
35 : }
36 :
37 0 : int import_block_prevhash_compare(const void* void_arg, const void* void_data)
38 : {
39 0 : const unsigned char* arg = void_arg;
40 0 : const struct snapraid_import_block* block = void_data;
41 :
42 0 : return memcmp(arg, block->prevhash, BLOCK_HASH_SIZE);
43 : }
44 :
45 : /**
46 : * Compute the hash of the hash for an import block.
47 : * We just use the first 32 bit of the hash itself.
48 : */
49 657477 : static inline tommy_uint32_t import_block_hash(const unsigned char* hash)
50 : {
51 : /* the hash data is not aligned, and we cannot access it with a direct cast */
52 657477 : return hash[0] | ((uint32_t)hash[1] << 8) | ((uint32_t)hash[2] << 16) | ((uint32_t)hash[3] << 24);
53 : }
54 :
55 1750 : static void import_file(struct snapraid_state* state, const char* path, uint64_t size)
56 : {
57 : struct snapraid_import_file* file;
58 : block_off_t i;
59 : data_off_t offset;
60 : void* buffer;
61 : int ret;
62 : int f;
63 : int flags;
64 1750 : unsigned block_size = state->block_size;
65 : struct advise_struct advise;
66 :
67 1750 : file = malloc_nofail(sizeof(struct snapraid_import_file));
68 1750 : file->path = strdup_nofail(path);
69 1750 : file->size = size;
70 1750 : file->blockmax = (size + block_size - 1) / block_size;
71 1750 : file->blockimp = malloc_nofail(file->blockmax * sizeof(struct snapraid_import_block));
72 :
73 1750 : buffer = malloc_nofail(block_size);
74 :
75 1750 : advise_init(&advise, state->file_mode);
76 :
77 : /* open for read */
78 1750 : flags = O_RDONLY | O_BINARY | advise_flags(&advise);
79 1750 : f = open(path, flags);
80 1750 : if (f == -1) {
81 : /* LCOV_EXCL_START */
82 : log_fatal("Error opening file '%s'. %s.\n", path, strerror(errno));
83 : exit(EXIT_FAILURE);
84 : /* LCOV_EXCL_STOP */
85 : }
86 :
87 1750 : ret = advise_open(&advise, f);
88 1750 : if (ret != 0) {
89 : /* LCOV_EXCL_START */
90 : log_fatal("Error advising file '%s'. %s.\n", path, strerror(errno));
91 : exit(EXIT_FAILURE);
92 : /* LCOV_EXCL_STOP */
93 : }
94 :
95 1750 : offset = 0;
96 6222 : for (i = 0; i < file->blockmax; ++i) {
97 4472 : struct snapraid_import_block* block = &file->blockimp[i];
98 4472 : unsigned read_size = block_size;
99 4472 : if (read_size > size)
100 1745 : read_size = size;
101 :
102 4472 : ret = read(f, buffer, read_size);
103 4472 : if (ret < 0 || (unsigned)ret != read_size) {
104 : /* LCOV_EXCL_START */
105 : log_fatal("Error reading file '%s'. %s.\n", path, strerror(errno));
106 : exit(EXIT_FAILURE);
107 : /* LCOV_EXCL_STOP */
108 : }
109 :
110 4472 : block->file = file;
111 4472 : block->offset = offset;
112 4472 : block->size = read_size;
113 :
114 4472 : memhash(state->hash, state->hashseed, block->hash, buffer, read_size);
115 4472 : tommy_hashdyn_insert(&state->importset, &block->nodeset, block, import_block_hash(block->hash));
116 :
117 : /* if we are in a rehash state */
118 4472 : if (state->prevhash != HASH_UNDEFINED) {
119 : /* compute also the previous hash */
120 0 : memhash(state->prevhash, state->prevhashseed, block->prevhash, buffer, read_size);
121 0 : tommy_hashdyn_insert(&state->previmportset, &block->prevnodeset, block, import_block_hash(block->prevhash));
122 : }
123 :
124 4472 : offset += read_size;
125 4472 : size -= read_size;
126 : }
127 :
128 1750 : ret = close(f);
129 1750 : if (ret != 0) {
130 : /* LCOV_EXCL_START */
131 : log_fatal("Error closing file '%s'. %s.\n", path, strerror(errno));
132 : exit(EXIT_FAILURE);
133 : /* LCOV_EXCL_STOP */
134 : }
135 :
136 1750 : tommy_list_insert_tail(&state->importlist, &file->nodelist, file);
137 :
138 1750 : free(buffer);
139 1750 : }
140 :
141 1750 : void import_file_free(struct snapraid_import_file* file)
142 : {
143 1750 : free(file->path);
144 1750 : free(file->blockimp);
145 1750 : free(file);
146 1750 : }
147 :
148 653005 : int state_import_fetch(struct snapraid_state* state, int rehash, struct snapraid_block* missing_block, unsigned char* buffer)
149 : {
150 : struct snapraid_import_block* block;
151 : int ret;
152 : int f;
153 653005 : const unsigned char* hash = missing_block->hash;
154 653005 : unsigned block_size = state->block_size;
155 : unsigned read_size;
156 : unsigned char buffer_hash[HASH_MAX];
157 : const char* path;
158 :
159 653005 : if (rehash) {
160 0 : block = tommy_hashdyn_search(&state->previmportset, import_block_prevhash_compare, hash, import_block_hash(hash));
161 : } else {
162 653005 : block = tommy_hashdyn_search(&state->importset, import_block_hash_compare, hash, import_block_hash(hash));
163 : }
164 653005 : if (!block)
165 648533 : return -1;
166 :
167 4472 : path = block->file->path;
168 4472 : read_size = block->size;
169 :
170 4472 : f = open(path, O_RDONLY | O_BINARY);
171 4472 : if (f == -1) {
172 : /* LCOV_EXCL_START */
173 : if (errno == ENOENT) {
174 : log_fatal("DANGER! file '%s' disappeared.\n", path);
175 : log_fatal("If you moved it, please rerun the same command.\n");
176 : } else {
177 : log_fatal("Error opening file '%s'. %s.\n", path, strerror(errno));
178 : }
179 : exit(EXIT_FAILURE);
180 : /* LCOV_EXCL_STOP */
181 : }
182 :
183 4472 : ret = pread(f, buffer, read_size, block->offset);
184 4472 : if (ret < 0 || (unsigned)ret != read_size) {
185 : /* LCOV_EXCL_START */
186 : log_fatal("Error reading file '%s'. %s.\n", path, strerror(errno));
187 : exit(EXIT_FAILURE);
188 : /* LCOV_EXCL_STOP */
189 : }
190 :
191 4472 : ret = close(f);
192 4472 : if (ret != 0) {
193 : /* LCOV_EXCL_START */
194 : log_fatal("Error closing file '%s'. %s.\n", path, strerror(errno));
195 : exit(EXIT_FAILURE);
196 : /* LCOV_EXCL_STOP */
197 : }
198 :
199 4472 : if (read_size != block_size) {
200 : /* fill the remaining with 0 */
201 1745 : memset(buffer + read_size, 0, block_size - read_size);
202 : }
203 :
204 : /* recheck the hash */
205 4472 : if (rehash)
206 0 : memhash(state->prevhash, state->prevhashseed, buffer_hash, buffer, read_size);
207 : else
208 4472 : memhash(state->hash, state->hashseed, buffer_hash, buffer, read_size);
209 :
210 4472 : if (memcmp(buffer_hash, hash, BLOCK_HASH_SIZE) != 0) {
211 : /* LCOV_EXCL_START */
212 : log_fatal("Error in data reading file '%s'.\n", path);
213 : log_fatal("Please don't change imported files while running.\n");
214 : exit(EXIT_FAILURE);
215 : /* LCOV_EXCL_STOP */
216 : }
217 :
218 4472 : return 0;
219 : }
220 :
221 1 : static void import_dir(struct snapraid_state* state, const char* dir)
222 : {
223 : DIR* d;
224 :
225 1 : d = opendir(dir);
226 1 : if (!d) {
227 : /* LCOV_EXCL_START */
228 : log_fatal("Error opening directory '%s'. %s.\n", dir, strerror(errno));
229 : exit(EXIT_FAILURE);
230 : /* LCOV_EXCL_STOP */
231 : }
232 :
233 : while (1) {
234 : char path_next[PATH_MAX];
235 : struct stat st;
236 : const char* name;
237 : struct dirent* dd;
238 :
239 : /* clear errno to detect erroneous conditions */
240 1812 : errno = 0;
241 1812 : dd = readdir(d);
242 1812 : if (dd == 0 && errno != 0) {
243 : /* LCOV_EXCL_START */
244 : log_fatal("Error reading directory '%s'. %s.\n", dir, strerror(errno));
245 : exit(EXIT_FAILURE);
246 : /* LCOV_EXCL_STOP */
247 : }
248 1812 : if (dd == 0) {
249 1 : break; /* finished */
250 : }
251 :
252 : /* skip "." and ".." files */
253 1811 : name = dd->d_name;
254 1811 : if (name[0] == '.' && (name[1] == 0 || (name[1] == '.' && name[2] == 0)))
255 2 : continue;
256 :
257 1809 : pathprint(path_next, sizeof(path_next), "%s%s", dir, name);
258 :
259 : #if HAVE_STRUCT_DIRENT_D_STAT
260 : /* convert dirent to lstat result */
261 : dirent_lstat(dd, &st);
262 :
263 : /* if the st_mode field is missing, takes care to fill it using normal lstat() */
264 : /* at now this can happen only in Windows (with HAVE_STRUCT_DIRENT_D_STAT defined), */
265 : /* because we use a directory reading method that doesn't read info about ReparsePoint. */
266 : /* Note that here we cannot call here lstat_sync(), because we don't know what kind */
267 : /* of file is it, and lstat_sync() doesn't always work */
268 : if (st.st_mode == 0) {
269 : if (lstat(path_next, &st) != 0) {
270 : /* LCOV_EXCL_START */
271 : log_fatal("Error in stat file/directory '%s'. %s.\n", path_next, strerror(errno));
272 : exit(EXIT_FAILURE);
273 : /* LCOV_EXCL_STOP */
274 : }
275 : }
276 : #else
277 : /* get lstat info about the file */
278 1809 : if (lstat(path_next, &st) != 0) {
279 : /* LCOV_EXCL_START */
280 : log_fatal("Error in stat file/directory '%s'. %s.\n", path_next, strerror(errno));
281 : exit(EXIT_FAILURE);
282 : /* LCOV_EXCL_STOP */
283 : }
284 : #endif
285 :
286 1809 : if (S_ISREG(st.st_mode)) {
287 1750 : import_file(state, path_next, st.st_size);
288 59 : } else if (S_ISDIR(st.st_mode)) {
289 0 : pathslash(path_next, sizeof(path_next));
290 0 : import_dir(state, path_next);
291 : }
292 1811 : }
293 :
294 1 : if (closedir(d) != 0) {
295 : /* LCOV_EXCL_START */
296 : log_fatal("Error closing directory '%s'. %s.\n", dir, strerror(errno));
297 : exit(EXIT_FAILURE);
298 : /* LCOV_EXCL_STOP */
299 : }
300 1 : }
301 :
302 1 : void state_import(struct snapraid_state* state, const char* dir)
303 : {
304 : char path[PATH_MAX];
305 :
306 1 : msg_progress("Importing...\n");
307 :
308 : /* if the hash is not full */
309 1 : if (BLOCK_HASH_SIZE != HASH_MAX) {
310 : /* LCOV_EXCL_START */
311 : log_fatal("You cannot import files when using a reduced hash.\n");
312 : exit(EXIT_FAILURE);
313 : /* LCOV_EXCL_STOP */
314 : }
315 :
316 : /* add the final slash */
317 1 : pathimport(path, sizeof(path), dir);
318 1 : pathslash(path, sizeof(path));
319 :
320 1 : import_dir(state, path);
321 1 : }
322 :
|