1 /*
2 * Copyright (C) 2011 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 "util.h"
21 #include "elem.h"
22 #include "state.h"
23 #include "parity.h"
24 #include "handle.h"
25 #include "io.h"
26 #include "raid/raid.h"
27
28 /****************************************************************************/
29 /* dry */
30
dry_data_reader(struct snapraid_worker * worker,struct snapraid_task * task)31 static void dry_data_reader(struct snapraid_worker* worker, struct snapraid_task* task)
32 {
33 struct snapraid_io* io = worker->io;
34 struct snapraid_state* state = io->state;
35 struct snapraid_handle* handle = worker->handle;
36 struct snapraid_disk* disk = handle->disk;
37 block_off_t blockcur = task->position;
38 unsigned char* buffer = task->buffer;
39 int ret;
40 char esc_buffer[ESC_MAX];
41
42 /* if the disk position is not used */
43 if (!disk) {
44 /* use an empty block */
45 memset(buffer, 0, state->block_size);
46 task->state = TASK_STATE_DONE;
47 return;
48 }
49
50 /* get the block */
51 task->block = fs_par2block_find(disk, blockcur);
52
53 /* if the block is not used */
54 if (!block_has_file(task->block)) {
55 /* use an empty block */
56 memset(buffer, 0, state->block_size);
57 task->state = TASK_STATE_DONE;
58 return;
59 }
60
61 /* get the file of this block */
62 task->file = fs_par2file_get(disk, blockcur, &task->file_pos);
63
64 /* if the file is different than the current one, close it */
65 if (handle->file != 0 && handle->file != task->file) {
66 /* keep a pointer at the file we are going to close for error reporting */
67 struct snapraid_file* report = handle->file;
68 ret = handle_close(handle);
69 if (ret == -1) {
70 /* LCOV_EXCL_START */
71 /* This one is really an unexpected error, because we are only reading */
72 /* and closing a descriptor should never fail */
73 if (errno == EIO) {
74 log_tag("error:%u:%s:%s: Close EIO error. %s\n", blockcur, disk->name, esc_tag(report->sub, esc_buffer), strerror(errno));
75 log_fatal("DANGER! Unexpected input/output close error in a data disk, it isn't possible to dry.\n");
76 log_fatal("Ensure that disk '%s' is sane and that file '%s' can be accessed.\n", disk->dir, handle->path);
77 log_fatal("Stopping at block %u\n", blockcur);
78 task->state = TASK_STATE_IOERROR;
79 return;
80 }
81
82 log_tag("error:%u:%s:%s: Close error. %s\n", blockcur, disk->name, esc_tag(report->sub, esc_buffer), strerror(errno));
83 log_fatal("WARNING! Unexpected close error in a data disk, it isn't possible to dry.\n");
84 log_fatal("Ensure that file '%s' can be accessed.\n", handle->path);
85 log_fatal("Stopping at block %u\n", blockcur);
86 task->state = TASK_STATE_ERROR;
87 return;
88 /* LCOV_EXCL_STOP */
89 }
90 }
91
92 ret = handle_open(handle, task->file, state->file_mode, log_error, 0);
93 if (ret == -1) {
94 if (errno == EIO) {
95 /* LCOV_EXCL_START */
96 log_tag("error:%u:%s:%s: Open EIO error. %s\n", blockcur, disk->name, esc_tag(task->file->sub, esc_buffer), strerror(errno));
97 log_fatal("DANGER! Unexpected input/output open error in a data disk, it isn't possible to dry.\n");
98 log_fatal("Ensure that disk '%s' is sane and that file '%s' can be accessed.\n", disk->dir, handle->path);
99 log_fatal("Stopping at block %u\n", blockcur);
100 task->state = TASK_STATE_IOERROR;
101 return;
102 /* LCOV_EXCL_STOP */
103 }
104
105 log_tag("error:%u:%s:%s: Open error. %s\n", blockcur, disk->name, esc_tag(task->file->sub, esc_buffer), strerror(errno));
106 task->state = TASK_STATE_ERROR_CONTINUE;
107 return;
108 }
109
110 task->read_size = handle_read(handle, task->file_pos, buffer, state->block_size, log_error, 0);
111 if (task->read_size == -1) {
112 if (errno == EIO) {
113 log_tag("error:%u:%s:%s: Read EIO error at position %u. %s\n", blockcur, disk->name, esc_tag(task->file->sub, esc_buffer), task->file_pos, strerror(errno));
114 log_error("Input/Output error in file '%s' at position '%u'\n", handle->path, task->file_pos);
115 task->state = TASK_STATE_IOERROR_CONTINUE;
116 return;
117 }
118
119 log_tag("error:%u:%s:%s: Read error at position %u. %s\n", blockcur, disk->name, esc_tag(task->file->sub, esc_buffer), task->file_pos, strerror(errno));
120 task->state = TASK_STATE_ERROR_CONTINUE;
121 return;
122 }
123
124 /* store the path of the opened file */
125 pathcpy(task->path, sizeof(task->path), handle->path);
126
127 task->state = TASK_STATE_DONE;
128 }
129
dry_parity_reader(struct snapraid_worker * worker,struct snapraid_task * task)130 static void dry_parity_reader(struct snapraid_worker* worker, struct snapraid_task* task)
131 {
132 struct snapraid_io* io = worker->io;
133 struct snapraid_state* state = io->state;
134 struct snapraid_parity_handle* parity_handle = worker->parity_handle;
135 unsigned level = parity_handle->level;
136 block_off_t blockcur = task->position;
137 unsigned char* buffer = task->buffer;
138 int ret;
139
140 /* read the parity */
141 ret = parity_read(parity_handle, blockcur, buffer, state->block_size, log_error);
142 if (ret == -1) {
143 if (errno == EIO) {
144 log_tag("parity_error:%u:%s: Read EIO error. %s\n", blockcur, lev_config_name(level), strerror(errno));
145 log_error("Input/Output error in parity '%s' at position '%u'\n", lev_config_name(level), blockcur);
146 task->state = TASK_STATE_IOERROR_CONTINUE;
147 return;
148 }
149
150 log_tag("parity_error:%u:%s: Read error. %s\n", blockcur, lev_config_name(level), strerror(errno));
151 task->state = TASK_STATE_ERROR_CONTINUE;
152 return;
153 }
154
155 task->state = TASK_STATE_DONE;
156 }
157
state_dry_process(struct snapraid_state * state,struct snapraid_parity_handle * parity_handle,block_off_t blockstart,block_off_t blockmax)158 static int state_dry_process(struct snapraid_state* state, struct snapraid_parity_handle* parity_handle, block_off_t blockstart, block_off_t blockmax)
159 {
160 struct snapraid_io io;
161 struct snapraid_handle* handle;
162 unsigned diskmax;
163 block_off_t blockcur;
164 unsigned j;
165 unsigned buffermax;
166 int ret;
167 data_off_t countsize;
168 block_off_t countpos;
169 block_off_t countmax;
170 unsigned error;
171 unsigned io_error;
172 unsigned l;
173 unsigned* waiting_map;
174 unsigned waiting_mac;
175 char esc_buffer[ESC_MAX];
176
177 handle = handle_mapping(state, &diskmax);
178
179 /* we need 1 * data + 2 * parity */
180 buffermax = diskmax + 2 * state->level;
181
182 /* initialize the io threads */
183 io_init(&io, state, state->opt.io_cache, buffermax, dry_data_reader, handle, diskmax, dry_parity_reader, 0, parity_handle, state->level);
184
185 /* possibly waiting disks */
186 waiting_mac = diskmax > RAID_PARITY_MAX ? diskmax : RAID_PARITY_MAX;
187 waiting_map = malloc_nofail(waiting_mac * sizeof(unsigned));
188
189 error = 0;
190 io_error = 0;
191
192 /* drop until now */
193 state_usage_waste(state);
194
195 countmax = blockmax - blockstart;
196 countsize = 0;
197 countpos = 0;
198
199 /* start all the worker threads */
200 io_start(&io, blockstart, blockmax, 0);
201
202 state_progress_begin(state, blockstart, blockmax, countmax);
203 while (1) {
204 void** buffer;
205
206 /* go to the next block */
207 blockcur = io_read_next(&io, &buffer);
208 if (blockcur >= blockmax)
209 break;
210
211 /* until now is scheduling */
212 state_usage_sched(state);
213
214 /* for each disk, process the block */
215 for (j = 0; j < diskmax; ++j) {
216 struct snapraid_task* task;
217 int read_size;
218 struct snapraid_block* block;
219 struct snapraid_disk* disk;
220 unsigned diskcur;
221
222 /* until now is misc */
223 state_usage_misc(state);
224
225 /* get the next task */
226 task = io_data_read(&io, &diskcur, waiting_map, &waiting_mac);
227
228 /* until now is disk */
229 state_usage_disk(state, handle, waiting_map, waiting_mac);
230
231 /* get the task results */
232 disk = task->disk;
233 block = task->block;
234 read_size = task->read_size;
235
236 /* if the disk position is not used */
237 if (!disk)
238 continue;
239
240 state_usage_file(state, disk, task->file);
241
242 /* if the block is not used */
243 if (!block_has_file(block))
244 continue;
245
246 /* handle error conditions */
247 if (task->state == TASK_STATE_IOERROR) {
248 /* LCOV_EXCL_START */
249 ++io_error;
250 goto bail;
251 /* LCOV_EXCL_STOP */
252 }
253 if (task->state == TASK_STATE_ERROR) {
254 /* LCOV_EXCL_START */
255 ++error;
256 goto bail;
257 /* LCOV_EXCL_STOP */
258 }
259 if (task->state == TASK_STATE_ERROR_CONTINUE) {
260 ++error;
261 continue;
262 }
263 if (task->state == TASK_STATE_IOERROR_CONTINUE) {
264 ++io_error;
265 if (io_error >= state->opt.io_error_limit) {
266 /* LCOV_EXCL_START */
267 log_fatal("DANGER! Too many input/output read error in a data disk, it isn't possible to scrub.\n");
268 log_fatal("Ensure that disk '%s' is sane and that file '%s' can be accessed.\n", disk->dir, task->path);
269 log_fatal("Stopping at block %u\n", blockcur);
270 goto bail;
271 /* LCOV_EXCL_STOP */
272 }
273
274 /* otherwise continue */
275 continue;
276 }
277 if (task->state != TASK_STATE_DONE) {
278 /* LCOV_EXCL_START */
279 log_fatal("Internal inconsistency in task state\n");
280 os_abort();
281 /* LCOV_EXCL_STOP */
282 }
283
284 countsize += read_size;
285 }
286
287 /* until now is misc */
288 state_usage_misc(state);
289
290 /* read the parity */
291 for (l = 0; l < state->level; ++l) {
292 struct snapraid_task* task;
293 unsigned levcur;
294
295 task = io_parity_read(&io, &levcur, waiting_map, &waiting_mac);
296
297 /* until now is parity */
298 state_usage_parity(state, waiting_map, waiting_mac);
299
300 /* handle error conditions */
301 if (task->state == TASK_STATE_IOERROR) {
302 /* LCOV_EXCL_START */
303 ++io_error;
304 goto bail;
305 /* LCOV_EXCL_STOP */
306 }
307 if (task->state == TASK_STATE_ERROR) {
308 /* LCOV_EXCL_START */
309 ++error;
310 goto bail;
311 /* LCOV_EXCL_STOP */
312 }
313 if (task->state == TASK_STATE_ERROR_CONTINUE) {
314 ++error;
315 continue;
316 }
317 if (task->state == TASK_STATE_IOERROR_CONTINUE) {
318 ++io_error;
319 if (io_error >= state->opt.io_error_limit) {
320 /* LCOV_EXCL_START */
321 log_fatal("DANGER! Too many input/output read error in the %s disk, it isn't possible to scrub.\n", lev_name(levcur));
322 log_fatal("Ensure that disk '%s' is sane and can be read.\n", lev_config_name(levcur));
323 log_fatal("Stopping at block %u\n", blockcur);
324 goto bail;
325 /* LCOV_EXCL_STOP */
326 }
327 continue;
328 }
329 if (task->state != TASK_STATE_DONE) {
330 /* LCOV_EXCL_START */
331 log_fatal("Internal inconsistency in task state\n");
332 os_abort();
333 /* LCOV_EXCL_STOP */
334 }
335 }
336
337 /* count the number of processed block */
338 ++countpos;
339
340 /* progress */
341 if (state_progress(state, &io, blockcur, countpos, countmax, countsize)) {
342 /* LCOV_EXCL_START */
343 break;
344 /* LCOV_EXCL_STOP */
345 }
346 }
347
348 state_progress_end(state, countpos, countmax, countsize);
349
350 state_usage_print(state);
351
352 bail:
353 /* stop all the worker threads */
354 io_stop(&io);
355
356 for (j = 0; j < diskmax; ++j) {
357 struct snapraid_file* file = handle[j].file;
358 struct snapraid_disk* disk = handle[j].disk;
359 ret = handle_close(&handle[j]);
360 if (ret == -1) {
361 /* LCOV_EXCL_START */
362 log_tag("error:%u:%s:%s: Close error. %s\n", blockmax, disk->name, esc_tag(file->sub, esc_buffer), strerror(errno));
363 log_fatal("DANGER! Unexpected close error in a data disk.\n");
364 ++error;
365 /* continue, as we are already exiting */
366 /* LCOV_EXCL_STOP */
367 }
368 }
369
370 if (error || io_error) {
371 msg_status("\n");
372 msg_status("%8u file errors\n", error);
373 msg_status("%8u io errors\n", io_error);
374 } else {
375 msg_status("Everything OK\n");
376 }
377
378 if (error)
379 log_fatal("DANGER! Unexpected errors!\n");
380 if (io_error)
381 log_fatal("DANGER! Unexpected input/output errors!\n");
382
383 free(handle);
384 free(waiting_map);
385 io_done(&io);
386
387 if (error + io_error != 0)
388 return -1;
389 return 0;
390 }
391
state_dry(struct snapraid_state * state,block_off_t blockstart,block_off_t blockcount)392 void state_dry(struct snapraid_state* state, block_off_t blockstart, block_off_t blockcount)
393 {
394 block_off_t blockmax;
395 int ret;
396 struct snapraid_parity_handle parity_handle[LEV_MAX];
397 unsigned error;
398 unsigned l;
399
400 msg_progress("Drying...\n");
401
402 blockmax = parity_allocated_size(state);
403
404 if (blockstart > blockmax) {
405 /* LCOV_EXCL_START */
406 log_fatal("Error in the specified starting block %u. It's bigger than the parity size %u.\n", blockstart, blockmax);
407 exit(EXIT_FAILURE);
408 /* LCOV_EXCL_STOP */
409 }
410
411 /* adjust the number of block to process */
412 if (blockcount != 0 && blockstart + blockcount < blockmax) {
413 blockmax = blockstart + blockcount;
414 }
415
416 /* open the file for reading */
417 /* it may fail if the file doesn't exist, in this case we continue to dry the files */
418 for (l = 0; l < state->level; ++l) {
419 ret = parity_open(&parity_handle[l], &state->parity[l], l, state->file_mode, state->block_size, state->opt.parity_limit_size);
420 if (ret == -1) {
421 /* LCOV_EXCL_START */
422 log_fatal("WARNING! Without an accessible %s file, it isn't possible to dry.\n", lev_name(l));
423 exit(EXIT_FAILURE);
424 /* LCOV_EXCL_STOP */
425 }
426 }
427
428 error = 0;
429
430 /* skip degenerated cases of empty parity, or skipping all */
431 if (blockstart < blockmax) {
432 ret = state_dry_process(state, parity_handle, blockstart, blockmax);
433 if (ret == -1) {
434 /* LCOV_EXCL_START */
435 ++error;
436 /* continue, as we are already exiting */
437 /* LCOV_EXCL_STOP */
438 }
439 }
440
441 /* try to close only if opened */
442 for (l = 0; l < state->level; ++l) {
443 ret = parity_close(&parity_handle[l]);
444 if (ret == -1) {
445 /* LCOV_EXCL_START */
446 ++error;
447 /* continue, as we are already exiting */
448 /* LCOV_EXCL_STOP */
449 }
450 }
451
452 /* abort if required */
453 if (error != 0) {
454 /* LCOV_EXCL_START */
455 exit(EXIT_FAILURE);
456 /* LCOV_EXCL_STOP */
457 }
458 }
459
460