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