aboutsummaryrefslogblamecommitdiffstats
path: root/deadshred.c
blob: 24d02fa3dbde0cf75e4f941ed3d119ee4eb5f1b2 (plain) (tree)
1
2
3
4
5
6
7
8
                                                         
                   

                          
 
                                                                                                       

 





                                     
 
                                          
 
                            


                                 

                                                      


                                             

                                                                            
                             

 
           
                                        



                                     


                          

                          


                          
                                                
                                   
 

                                                                                  




                                                                            
                                                                  
 
                                        
                                                                          
                                                                                   





                                                                   



                                                               

                                
                                




                                                     

                                                     
                             




                                                        

                                                                    

                                       

                                 
                                                               


                               



                  






                                              
             
                           
 
                                                                        

                          







                                                                             
 
                       











                                                               
                                   
                                  

                                                                                                          
                                              
                                      

                                                   
                                                               
                                                                 

                                                                                   
                                                       
                         

























                                                                                                              
                 
                                                

                                                              
                              
                                                                                                    

                                      

                                      

                             
                      
                    



           
                                                                     
 
                     
                  
                                      
                                
                           
                               
                                
 

                                                                                    

                              
                                                         


                                                  

                                                 
                               
                 
 
                                                     
                                                              



                                                         
                                                                     



                                                            
                                                                                          





                                                                                                      
                                   

                                                                                                                              



                                                                

                                                                             
                                                              
                                         




                                                                          

















                                                                                                         
                 
                                                   


                                                                         
                                                            





                                                  
                                                          
                     
                                                            
                            
                                             
                                            
                                                      
                                                  
                         
                                         
                                                                                                        
                                                                                                                  
                                       
                                                         

                                         
                                                      


                                        
                                                          

                                         
                                                

                                 
                                                   


                                          
                                                                                                                             
                                       
                                                         

                                         
                                                      


                                        
                                                          

                                         
                                                
                 
                                            
                               
                                               
                                   
                                
                                                     

                                              
         

                              
                                          


 


                            
                                                     
                    
                                      



                                               

                  

                             
                                





                                                 





                                                 

                             
                                



                                                 
                      


                                         



                        
                                 

                        

                                                                     
 
                                               
                   
                                                              
 
                                                  
 






                                                      
 
                       


                                                                                    

                       





                                                                                                           
                              


                                                                                        

         
                                                                 




                                                        

                                                                  
 


                                                   












                                                                                   

                                 


                           





                                                              

                                         
 



                                                                                    


                                                

                                               
                  





                                                                              
                 


                                                                                               
                              

                                      

         
    
                  

                                         
                         
 
                                              

                                          
 
                                             
                                                      
                                 
 












                                                                     
         

                 
/* See LICENSE file for copyright and license details. */
#include "common.h"
#include <libsimple-arg.h>


USAGE("[-b blocksize] [-o offset] [-l length | -e postend] [-rY] device [map-file] [< random-source]");


struct auxthread_input {
	struct status initial_status;
	int map_fd;
	const char *map_fname;
};


static struct status status = STATUS_INIT;

static off_t total_size = 0;
static char total_size_1000[256];
static char total_size_1024[256];

static clockid_t clck = CLOCK_MONOTONIC_COARSE;
static const char *clkcstr = "CLOCK_MONOTONIC_COARSE";
static struct timespec max_success = {-1, 0};
static struct timespec start_time;

static const struct timespec status_print_interval = {0, MILLISECONDS(500)};
static const struct timespec poll_timeout          = {1, 0};
static int auxthread_pipe[2];


static void
print_status(int done, struct status *s)
{
	static char buf1[2048] = {0};
	static char buf2[2048] = {0};
	static int bufi = 0;

	char subbuf1[256];
	char subbuf2[256];
	char subbuf3[256];
	char subbuf4[256];
	char subbuf5[256];
	char subbuf6[256];
	char subbuf7[256];
	struct timespec since_success = {-1, 0};
	struct timespec time_spent;

	if (s->last_success.tv_sec >= 0) {
		libsimple_difftimespec(&since_success, &s->now, &s->last_success);
		if (libsimple_cmptimespec(&since_success, &max_success) > 0)
			max_success = since_success;
	}

	/* TODO deal with machine and process suspension */
	libsimple_difftimespec(&time_spent, &s->now, &start_time);

	sprintf(bufi == 0 ? buf1 : buf2,
	        "%ji bytes (%s, %s, %.2lf %%) of %s (%s) shredded\033[K\n"
	        "failed writes: %ju; bad sections: %ju (%ji bytes, %s, %s)\033[K\n"
	        "time spent shredding: %s; performance: %s\033[K\n"
	        "time since last successful write: %s\033[K\n"
	        "maximum time until a successful write: %s\033[K\n"
	        "pass: %ju; pass direction: %s\033[K\n"
	        "%s",
	        /* line 1 { */
	        (intmax_t)s->shredded,
	        humansize1000(s->shredded, subbuf1),
	        humansize1024(s->shredded, subbuf2),
	        100 * (double)s->shredded / (double)total_size,
	        total_size_1000,
	        total_size_1024,
	        /* } line 2 { */
	        s->bad_writes,
	        s->bad_sections,
	        (intmax_t)s->bad_bytes,
	        humansize1000(s->bad_bytes, subbuf3),
	        humansize1024(s->bad_bytes, subbuf4),
	        /* } line 3 { */
	        durationstr(&time_spent, subbuf5, 1),
	        wravg_get(s),
	        /* } line 4 { */
	        durationstr(&since_success, subbuf6, 2),
	        /* } line 5 { */
	        durationstr(&max_success, subbuf7, 2),
	        /* } line 6 { */
	        s->pass_nr,
	        s->direction == FORWARDS ? "forwards" : "backwards",
	        /* } */
	        done ? "" : "\033[6A");

	if (strcmp(buf1, buf2)) {
		fprintf(stderr, "%s", bufi == 0 ? buf1 : buf2);
		fflush(stderr);
	}

	bufi ^= 1;
}


static void
preprint(void)
{
	fprintf(stderr, "\n\n\n\n\n\n\033[K");
}


static void *
auxthread_loop(void *input)
{
	struct pollfd pfd = {.fd = auxthread_pipe[0], .events = POLLIN};
	ssize_t r;
	int terminate = 0;
	int map_fd = ((struct auxthread_input *)input)->map_fd;
	const char *map_fname = ((struct auxthread_input *)input)->map_fname;
	struct status s = ((struct auxthread_input *)input)->initial_status;
	size_t offset, spans_transfer_size;

	s.spans = NULL;
	s.spans_size = 0;
	s.span_off = 0;

	unblock_sigs();

	do {
		switch (ppoll(&pfd, 1U, &poll_timeout, NULL)) {
		case -1:
			if (errno != EINTR)
				eprintf("ppoll:");
			if (exiting)
				break;
			continue;
		case 0:
			break;
		default:
			offset = 0;
		read_status_again:
			r = read(auxthread_pipe[0], &((char *)&s)[offset], STATUS_TRANSFER_SIZE - offset);
			if (!r) {
				terminate = 1;
				break;
			} else if (r < 0) {
				if (errno == EINTR)
					goto read_status_again;
				eprintf("read <internal pipe>:");
			} else if (r != (ssize_t)(STATUS_TRANSFER_SIZE - offset)) {
				offset += (size_t)r;
				goto read_status_again;
			}

			if (!s.nspans)
				goto have_time;
			if (s.spans_size != s.nspans) {
				s.spans_size = s.nspans;
				s.spans = ereallocarray(s.spans, s.spans_size, sizeof(*s.spans));
			}

			offset = 0;
			spans_transfer_size = s.nspans * sizeof(*s.spans);
		read_spans_again:
			r = read(auxthread_pipe[0], &((char *)s.spans)[offset], spans_transfer_size - offset);
			if (!r) {
				terminate = 1;
				break;
			} else if (r < 0) {
				if (errno == EINTR)
					goto read_spans_again;
				eprintf("read <internal pipe>:");
			} else if (r != (ssize_t)(spans_transfer_size - offset)) {
				offset += (size_t)r;
				goto read_spans_again;
			}

			update_map(map_fd, &s, map_fname);
			goto have_time;
		}
		if (clock_gettime(clck, &s.now))
			eprintf("clock_gettime %s:", clkcstr);
	have_time:
		if (exiting) {
			fprintf(stderr, "\033[K\nTermination initiated by user...\033[K\n\033[K\n");
			terminate = 1;
		}
		print_status(0, &s);
		wravg_update_time(&s);
	} while (!terminate);

	free(s.spans);
	return NULL;
}


static void
shredspan(int fd, struct span *span, const char *fname, int have_map)
{
	off_t off, n;
	ssize_t r;
	struct timespec when = {0, 0};
	int bad = span->bad > 0;
	int first_fail = 1;
	int was_successful = 0;
	const char *random_data;

	off = (status.direction == FORWARDS ? span->start : span->end);
	while (status.direction == FORWARDS ? off < span->end : off > span->start) {
		if (exiting) {
		userexit:
			if (status.direction == FORWARDS)
				span->start = off;
			else
				span->end = off;
			close(auxthread_pipe[1]);
			auxthread_pipe[1] = -1;
			return;
		}

		if (clock_gettime(clck, &status.now))
			eprintf("clock_gettime %s:", clkcstr);
		if (was_successful) {
			was_successful = 0;
			status.last_success = status.now;
		}
		if (libsimple_cmptimespec(&status.now, &when) >= 0) {
			size_t saved_nspans = status.nspans;
			size_t spans_transfer_size;
			size_t spans_offset = 0;
			size_t status_off = 0;
			libsimple_sumtimespec(&when, &status.now, &status_print_interval);
			if (have_map && 0) /* TODO periodically 1 instead 0 */
				status.nspans -= status.span_off;
			else
				status.nspans = 0;
			spans_transfer_size = status.nspans * sizeof(*status.spans);
			spans_transfer_size += spans_offset = status.span_off * sizeof(*status.spans);
		write_status_again:
			r = write(auxthread_pipe[1], &((const char *)&status)[status_off], STATUS_TRANSFER_SIZE - status_off);
			if (r != (ssize_t)(STATUS_TRANSFER_SIZE - status_off)) {
				if (r >= 0) {
					status_off += (size_t)r;
					goto write_status_again;
				} else if (r == EINTR) {
					if (exiting) {
						status.nspans = saved_nspans;
						goto userexit;
					}
					goto write_status_again;
				} else {
					eprintf("write <internal pipe>:");
				}
			}
			status.nspans = saved_nspans;
			if (spans_offset < spans_transfer_size) {
			write_spans_again:
				r = write(auxthread_pipe[1], &((const char *)status.spans)[spans_offset],
				          spans_transfer_size - spans_offset);
				if (r != (ssize_t)(spans_transfer_size - spans_offset)) {
					if (r >= 0) {
						spans_offset += (size_t)r;
						goto write_spans_again;
					} else if (r == EINTR) {
						if (exiting)
							goto userexit;
						goto write_spans_again;
					} else {
						eprintf("write <internal pipe>:");
					}
				}
			}
		}
		if (status.direction == FORWARDS) {
			n = MIN((off_t)span->blocksize, span->end - off);
		} else {
			n = off--;
			off -= off % (off_t)span->blocksize;
			if (off < span->start)
				off = span->start;
			n -= off;
			if (!n)
				break;
		}
		random_data = get_random(span->blocksize);
	pwrite_again:
		r = pwrite(fd, random_data, (size_t)n, off);
		if (r < 0) {
			if (errno == EINTR) {
				if (exiting)
					goto userexit;
				goto pwrite_again;
			}
			if (errno != EIO)
				eprintf("pwrite %s <buffer> %zu %ji:", fname, (size_t)n, (intmax_t)off);
			add_span(&status, off, n, span->blocksize == 1U ? 1U : span->blocksize >> 1, !first_fail);
			first_fail = 0;
			if (status.direction == FORWARDS)
				off += n;
			if (!span->bad)
				status.bad_bytes += n;
			if (bad)
				bad = 0;
			else
				status.bad_sections += 1U;
			when.tv_sec = 0;
			when.tv_nsec = 0;
			status.bad_writes += 1U;
			continue;
		}
		if (status.direction == FORWARDS) {
			off += (off_t)r;
		} else if ((off_t)r < n) {
			n -= (off_t)r;
			add_span(&status, off + (off_t)r, n, span->blocksize == 1U ? 1U : span->blocksize >> 1, !first_fail);
			first_fail = 0;
			if (status.direction == FORWARDS)
				off += n;
			if (!span->bad)
				status.bad_bytes += n;
			if (bad)
				bad = 0;
			else
				status.bad_sections += 1U;
			when.tv_sec = 0;
			when.tv_nsec = 0;
			status.bad_writes += 1U;
		}
		status.shredded += (off_t)r;
		used_random(r);
		wravg_update_write(r, &status);
		was_successful = 1;
		if (span->bad) {
			status.bad_bytes -= (off_t)r;
			span->bad -= (off_t)r;
		}
	}

	if (bad && !span->bad)
		status.bad_sections -= 1U;
}


int
main(int argc, char *argv[])
{
	off_t off = -1, len = -1, end = -1, blk = -1;
	size_t i, j;
	int fd, reverse_direction = 0;
	int ask_for_confirmation = 1;
	int map_fd = -1;
	pthread_t auxthread;
	struct auxthread_input auxthread_input;

	ARGBEGIN {
	case 'b':
		if (blk >= 0)
			usage();
		blk = unhumansize(ARG(), FLAG());
		break;
	case 'e':
		if (len >= 0 || end >= 0)
			usage();
		end = unhumansize(ARG(), FLAG());
		break;
	case 'l':
		if (len >= 0 || end >= 0)
			usage();
		len = unhumansize(ARG(), FLAG());
		break;
	case 'o':
		if (off >= 0)
			usage();
		off = unhumansize(ARG(), FLAG());
		break;
	case 'r':
		reverse_direction = 1;
		break;
	case 'Y':
		ask_for_confirmation = 0;
		break;
	default:
		usage();
	} ARGEND;

	if (argc < 1 || argc > 2)
		usage();

	if (ask_for_confirmation && !confirm(argv[0], off, len, end))
		return 1;

	fd = open(argv[0], O_WRONLY | O_DSYNC);
	if (fd < 0)
		eprintf("open %s O_WRONLY|O_DSYNC:", argv[0]);

	init_random(STDIN_FILENO, "<stdin>", blk);

	status.spans = emalloc(sizeof(*status.spans));
	status.spans[0].start = 0;
	status.spans[0].end = filesize(fd, argv[0]);
	status.spans[0].bad = 0;
	status.spans[0].blocksize = max_blksize();
	status.nspans = 1U;
	status.spans_size = 1U;

	if (off >= 0) {
		if (off > status.spans[0].end)
			eprintf("value of -o option is beyond the end of the file");
		status.spans[0].start = off;
	}
	if (len >= 0) {
		if (len > OFF_MAX - status.spans[0].start)
			eprintf("the sum of the values of -o and -l option is too large");
		end = status.spans[0].start + len;
		if (end > status.spans[0].end)
			eprintf("the sum of the values of -o and -l option is beyond the end of the file");
		status.spans[0].end = end;
	} else if (end >= 0) {
		if (end > status.spans[0].end)
			eprintf("the value of -e option is beyond the end of the file");
		status.spans[0].end = end;
	}

	total_size = status.spans[0].end - status.spans[0].start;
	if (!total_size) {
		weprintf("attempting to shred 0 bytes");
		close(fd);
		return 0;
	}
	while ((off_t)status.spans[0].blocksize >> 1 > total_size)
		status.spans[0].blocksize >>= 1;

	humansize1000(total_size, total_size_1000);
	humansize1024(total_size, total_size_1024);

	if (reverse_direction)
		status.direction ^= 1;

	if (argv[1]) {
		map_fd = open(argv[1], O_RDWR | O_CREAT, 0666);
		if (map_fd < 0)
			eprintf("open %s O_RDWR|O_CREAT 0666:", argv[1]);
		if (lseek(map_fd, 0, SEEK_SET))
			eprintf("lseek %s 0 SEEK_SET:", argv[1]);
		status.shredded = load_map(fd, &status, status.direction, argv[1]);
	}

	if (pipe(auxthread_pipe))
		eprintf("pipe:");

	setup_sighandler();
	block_sigs();

	if (clock_gettime(clck, &start_time)) {
		clck = CLOCK_MONOTONIC;
		clkcstr = "CLOCK_MONOTONIC";
		if (clock_gettime(clck, &start_time))
			eprintf("clock_gettime %s:", clkcstr);
	}
	wravg_init(&start_time, &status);
	status.last_success = start_time;

	auxthread_input.initial_status = status;
	auxthread_input.map_fd = map_fd;
	auxthread_input.map_fname = argv[1];
	errno = pthread_create(&auxthread, NULL, &auxthread_loop, &auxthread_input);
	if (errno)
		eprintf("pthread_create NULL:");

	libsimple_eprintf_preprint = &preprint;

	for (;;) {
		size_t old_nspans = status.nspans;
		for (i = 0; i < old_nspans; i++) {
			status.span_off = i;
			shredspan(fd, &status.spans[i], argv[0], map_fd >= 0);
			if (exiting)
				goto out;
		}
		for (i = 0, j = status.nspans, status.nspans -= old_nspans; i < status.nspans;)
			status.spans[i++] = status.spans[--j];
		if (!status.nspans)
			break;
		status.direction ^= 1;
		status.pass_nr += 1U;
	}

out:
	close(fd);
	if (auxthread_pipe[1] >= 0)
		close(auxthread_pipe[1]);
	destroy_random();

	errno = pthread_join(auxthread, NULL);
	if (errno)
		weprintf("pthread_join:");

	if (clock_gettime(clck, &status.now))
		eprintf("clock_gettime %s:", clkcstr);
	print_status(1, &status);

	if (status.nspans > status.span_off) {
		if (map_fd >= 0) {
			update_map(map_fd, &status, argv[0]);
			if (close(map_fd))
				eprintf("write %s:", argv[0]);
		} else {
			dump_map(STDOUT_FILENO, &status, "<stdout>");
			if (close(STDOUT_FILENO))
				eprintf("write <stdout>:");
		}
	} else if (argv[1]) {
		if (unlink(argv[1]))
			weprintf("unlink %s:", argv[1]);
	}
	return 0;
}