File: floppybackup.c

package info (click to toggle)
floppybackup 1.3-3.1
  • links: PTS
  • area: main
  • in suites: etch, etch-m68k
  • size: 104 kB
  • ctags: 67
  • sloc: ansic: 629; makefile: 46; sh: 15
file content (572 lines) | stat: -rw-r--r-- 15,465 bytes parent folder | download | duplicates (3)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
/* This program is under the GNU copyright
 * Author Damiano Bolla (Italy)
 * 
 * Modified by K.Veijalainen (veijalai@cc.lut.fi)
 */

#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <sys/ioctl.h>
#include <string.h>
#include <time.h>

#include <linux/fd.h>

#include "defines.h"

#define MINIMUM_FEASIBLE_DISKSIZE 360*1024

struct floppy_struct floppy_parameters;/* Current floppy parameters */
int floppysize;								/* Size of current floppy in bytes. */
struct archive_header arch;				/* This holds archive info  */
char *output_device=NULL;					/* Name of the floppy device to use    */
unsigned char *archive_name=NULL;		/* Name of the archive. */
int outf;										/* The fd pointing to output device */
int verifyf;									/* The fd pointing to verify data  */
char *buff;										/* The I/O buffer          */
int bytes_read;										/* Number of bytes read.*/
int disknum;									/* The current disk number       */
int written;									/* Number of bytes written to current disk */
int totalread;									/* Total number of bytes read from stdin */
int totalwritten;								/* Total number of bytes writte to floppies. */
/* On/off flags */
int verify=1;									/* Verify mode (N/A) */
int skip_diskwrite=0;						/* If an error occurs writing... */
int quiet=0;									/* Quiet mode */
/* Time variables */
time_t dtime,btime,user_time=0;
/* Temp variables. */
int count,wcount;
int fixed_size=0;								/* Fixed size. Addmuzh! */
int errors=0;							/* Errors total */



/* -------------------------------------------------------------- 
 * This function will behave in a consistemt way if used over a file 
 * or a pipe. Thanks to Linux for pointing out the reasons of the
 * pipe behaviour.
 */
int Rread(int chan, char *buff, int size)
{
	int err=0;
	int requested;
	requested = size;
	/**/	
	while (size > 0) {
		err = read(chan, buff, size);
		if (err > 0) {
			buff += err;
			size -= err;
		} else
		  break;
	}
	if (requested - size > 0)
	  return (requested - size);
	else
	  return (err);
}



void print_readwrite()
{
	/* Print info for user. Makes detecting hangups and stuff easy :-) */
	if(!quiet){
		if(!skip_diskwrite)
		  fprintf(stderr, "\rWritten: %d",written);
		else
		  fprintf(stderr, "\rStored : %d",written);
		/* Append speed if it can be done*/
		if(time(NULL)>dtime)
		  fprintf(stderr," (%d bytes/sec) ",written/(int)(time(NULL)-dtime));
		fflush(stderr);
	}
}

void closedevice()
{
	
	if(close(outf)<0){
		perror("floppybackup: Couldn't close device.");
		exit(1);
	}
}


/* This func asks for next disk,
 * asks the floppy device driver about drive parameters,
 * calcs size of the floppy in question,
 * updates floppysize-variable and seeks just enough to leave
 * space for archive_header.*/
void open_disk(unsigned char *current_archive_name,int minimum_size)
{
	struct archive_header newarch;
	char dummy[10];		  /* For the ENTER        */
	dtime=time(NULL);
	while(1){
		fprintf(stderr, "\nInsert a floppy in device %s and press ENTER\n",output_device);
		read(2, dummy, 2);
		/**/
		if((outf = open(output_device, O_RDWR)) >0 ){
			if(!(floppysize=fixed_size)){
				if(ioctl(outf,FDGETPRM,&floppy_parameters)<0){
					perror("floppybackup: FDGETPRM ioctl failed.");
					closedevice();
					continue;
				}
				floppysize=(floppy_parameters.size)*512;
				if(!quiet)
				  fprintf(stderr,
							 "%s: %d tracks, %d sectors, %d heads.\n",
							 output_device,
							 floppy_parameters.track,
							 floppy_parameters.sect,
							 floppy_parameters.head);
			}
			/* If the disk is too small, forget it.*/
			if(floppysize<minimum_size){
				fprintf(stderr,
						  "This disk is too small (%d bytes requested).\n",
						  minimum_size);
				closedevice();
				continue;
			}
		}
		else{
			if(!quiet)
			  perror("floppybackup: Error opening device (no disk inserted?).");
			continue;
		}
		/* Read header.*/
 		if(read(outf,&newarch,sizeof(struct archive_header))<0){
			perror("floppybackup: Error reading archive header.");
			closedevice();
			continue;
		}
		/* Accept disk if it is NOT a backup disk.*/
		if(strcmp(newarch.magicbytes,MAGICBYTES))
		  break;
		else{
			/* It is of a backup set.*/
#if 0
			if(!quiet)
			  fprintf(stderr,"This disk belongs to another archive. Overwrite?");
			/* Ask it */
#endif
			if(!strcmp(newarch.name,current_archive_name)){
				/* If you fumble with ENTER, no data you just wrote to disk will
				 * be overwritten.*/
				if(!quiet){
					fprintf(stderr,
							  "This disk belongs to archive we are creating.\n");
					closedevice();
					continue;
				}
			}
			else
			  /* Accept disk  - it IS a backup disk, but of different set.*/
			  break;
		}
	}
	/* At this point the disk is OK.*/
#if 0
	/* FIX. Write a archive header with fd_length==floppysize here,
	 * so that IF and when some moron removes the disk too early,
	 * all data is not lost. Unnecessary? */
#endif
	/* Add time spent waiting for disk to used_time */
	user_time+=(time(NULL)-dtime);
	/* Get current time so we can calc write speed to this disk.*/
	dtime=time(NULL);
	/**/
	skip_diskwrite=0;
}


void open_verifyfile()
{
	/* Open temp file for writing stuff that is written
	 * to disk (for verifying later).*/
	if(verify){
	    char * tempfname = strdup("/tmp/flbkXXXXXX");
	    /* CPhipps 2000/02/09 - use mkstemp(3) to get a temporary file, that
	     *  way it is opened safely, with a good random filename. */
	    if(!(verifyf=mkstemp(tempfname))){
	      
			perror("floppybackup: Could not open verify data file.");
			exit(1);
		}
		else{
			/* Use unix unlink(2) semantics to our advantage, we can unlink=20
			 * the file now, and our file stays around until we close it */
			unlink(tempfname); free(tempfname);
			if(lseek(verifyf,sizeof(struct archive_header),SEEK_SET)<0){
				perror("floppybackup: Could not seek verify file.");
				close(verifyf);				/* add check */
				exit(1);
			}
		}
	}
}

/*
 * Asks for new disk and then copies verifyfile to it.
 * Done in case of verify error.
 */
int make_new_disk(int minimum_size)
{
	int c,r,sync_counter;
	int copysize=minimum_size;
	unsigned char buf2[BUFFSIZE];
	if(verify){
		open_disk(arch.name,minimum_size);
		/* Copy verify data to disk.*/
		/* add checks */
		lseek(outf,0,SEEK_SET);       /* Beginning of disk */
		lseek(verifyf,0,SEEK_SET);    /* Beginning of verify file  */
		r=BUFFSIZE;                      /* Bytes to read at a time  */
		sync_counter=0;/* Just to make the output look better */
		for(c=0;c<copysize;c+=BUFFSIZE){
			/* Check number of bytes to read */
			if((c+r)>copysize)
			  r=copysize-c;
			/* Copy. */
			read(verifyf,buf2,r);/* add check */
			if(write(outf,buf2,r)<0){
				/* Something went very wrong - again.
				 * Exit with error value.*/
				fprintf(stderr,
						  "\nfloppybackup: make_new_disk() write() failed!\n"
						  "Either you have 2 crappy disks, or your drive\n"
						  "truly sucks, or you removed disk from drive\n"
						  "too soon.\n\n");
				close(outf);
				return -1;
			}
			/**/
			fprintf(stderr,"Copying... (%d%%)\r",(c*100)/copysize);
			/* Make output look somewhat better instead of immediate
			 *  * 0%->99% leap.*/
			if(!((++sync_counter)%20))
			  fsync(outf);
		}
		return 0;
	}
	else{
		fprintf(stderr,"floppybackup: make_new_disk() called while not in verify mode!\n");
		return -1;
	}
}


void close_disk()
{
	int
	  c,r,bytes_on_disk=atoi(arch.fd_length),
	verify_another,copysize,retries=0;
	unsigned char buf1[BUFFSIZE],buf2[BUFFSIZE];
	/**/
	sprintf(arch.fd_number,"%d",disknum);
	sprintf(arch.fd_total,"-2");			/* "Check disk 1" */
	sprintf(arch.fd_total_length,"-1");	/* Not defined */
	sprintf(arch.creation_time,"ddmmyyyyhhmm");/* Not defined */

	/**/
	copysize=bytes_on_disk+sizeof(struct archive_header);

	/* If writing to disk was stopped,
	 * outf already is closed.
	 * Ask new disk.*/
	if(skip_diskwrite)
	  while(make_new_disk(copysize));

	/* Otherwise write header to disk.
	 * Note! Even if new disk was made above,
	 * it does not yet contain header.*/
	
	if(!quiet)
	  fprintf(stderr,"\nClosing disk and writing header...");
	lseek(outf,0,SEEK_SET);					/* add check */
	if(write(outf,&arch,sizeof(struct archive_header))<0){
		perror("floppybackup: Error writing header! The disk more than probably is unusable.");
		/* add recovery */
	}
	/* Flush buffers to disk.*/
	fsync(outf);								/* add check */
	if(!quiet)
	  fprintf(stderr,"ok. Header size: %d bytes.\n",sizeof(struct archive_header));   /* FIX */
	
	/* This is done even if writing to disk was stopped. */
	if(verify){
		/* First write the header.*/
		lseek(verifyf,0,SEEK_SET);			/* add check */
		if(write(verifyf,&arch,sizeof(struct archive_header))<0){
			perror("floppybackup: Error writing header to verify data file.");
			/* add recovery */
		}
	}
	
	/* Now, if verify mode is on, lseek to beginning
	 * of disk and file, read in both some bytes at a time, and
	 * compare contents. If there are any errors, the disk is
	 * most probably screwed, in which case another disk is asked,
	 * and tempfile is written to it. Then the new disk and contents
	 * of temp file are likewise compared.*/
	if(verify){
		verify_another=1;
		while(verify_another){
			verify_another=0;
			/* If rest of current disk was skipped,
			 * outf if closed 
			 * but close the device.*/
			fsync(outf);						/* add checks to these 4 lines! */
			ioctl(outf,FDFLUSH,NULL);		/* Make floppy driver forget buffers */
			lseek(outf,0,SEEK_SET);			/* Beginning of disk */
			lseek(verifyf,0,SEEK_SET);		/* Beginning of verify file  */
			r=BUFFSIZE;								/* Bytes to read at a time  */
			for(c=0;c<bytes_on_disk;c+=BUFFSIZE){
				/* Check number of bytes to read */
				if((c+r)>bytes_on_disk)
				  r=bytes_on_disk-c;
				/* Read. */
				read(verifyf,buf2,r);		/* add check */
				read(outf,buf1,r);			/* add check */
				/* Check.*/
				if(memcmp(buf1,buf2,r)){
					fprintf(stderr,
							  "\nVERIFY ERROR BETWEEN BYTES %d-%d !\n"
							  "Probably bad media or crappy floppy drive.\n"
							  "The wise thing to do is to throw the offending\n"
							  "disk away. You will now be requested to insert\n"
							  "another disk. This disk must be able to hold\n"
							  "%d bytes of data.\n",
							  c,c+r-1,copysize);
					close(outf);				/* add check  */
					++retries;
					/**/
					while(make_new_disk(copysize));
					/**/
					verify_another=1;
					break;
				}
				/* No error verifying current block */
				fprintf(stderr,"Verifying... (%d%%)\r",(c*100)/bytes_on_disk);
			}
		}
		close(verifyf);						/* add check */
		fprintf(stderr,"Verifying pass succesful");
		if(retries>0)
		  fprintf(stderr," after %d retries.\n",retries);
		else
		  fprintf(stderr,".\n");
	}
	/* Close device */
	closedevice();
	++disknum;
}




int main(int argc, char *argv[])
{
	int l,c;
	
	for(l=1;l<argc;l++){
		if(argv[l][0]=='-'){
			for(c=1;c<strlen(argv[l]);c++){
				switch(argv[l][c]){
				 case 'q':
					quiet=1;
					break;
				 case 'n':
					verify=0;
					break;
				}
			}
		}
		else{
			if(!output_device){
				output_device=strdup(argv[l]);
				continue;
			}
			/* Create archive name using*/
			if(!archive_name){
				archive_name=strdup(argv[l]);
				continue;
			}
		}
	}
		
	/* Fallback..*/
	if(!output_device){
		output_device=DEFAULT_DEVICE;
		if(!quiet)
		  fprintf(stderr,
					 "No device given. Using "DEFAULT_DEVICE"\n");
	}
	/* Fallback.. add checks */
	if(!archive_name){
		unsigned char buffer[1024];
		FILE *fp;
		/* CPhipps - use popen to read from date(1) rather then temp file.
		 *  Even better would be to call the time functions directly. */
		if((fp = popen("date +%X-%x","r")) != NULL) {
			fgets(buffer,1024,fp);
			fclose(fp);
			/* FIXME: should be doing error checking here */
			archive_name=strdup(buffer);
			if(!quiet)
				fprintf(stderr,

							 "No archive name given. Using %s\n",
							 archive_name);
		}
	}
	
	/* This is the I/O buffer.....               */
	buff = (char *) malloc(BUFFSIZE*2);	/* ????????????2*  */
	if (buff == NULL) {
		fprintf(stderr, "Sorry can't allocate buffer\n");
		exit(2);
	}
	
	/* These are constant */
	strcpy(arch.magicbytes,MAGICBYTES);
	strcpy(arch.version,VERSION);
	strncpy(arch.name, archive_name, 128);
	
	/**/
	bytes_read = 0;
	disknum = 0;				  /* The first disk is 0,1,2,3,4......   */
	written=0;
	totalread=0;
	totalwritten=0;
	btime=time(NULL);
	
	/* Print beginning info */
	if(!quiet)
	  fprintf(stderr,
				 "Starting floppybackup %s.\n"
				 "Archive name: %s\n",
				 VERSION,arch.name);
	
	/* The initial disk.*/
	open_disk(arch.name,MINIMUM_FEASIBLE_DISKSIZE);
	if(verify)
	  open_verifyfile();
	
	/* While there is stuff to read, read BUFFSIZE bytes and dump them to disk immediately.*/
	while ((count = Rread(0, buff, BUFFSIZE)) == BUFFSIZE) {
		
		bytes_read += count;
		totalread+=count;
		/* Not correct??? below 2*/
		written+=count;
		totalwritten+=count;
		
		/* Write data to disk.*/
		if(!skip_diskwrite){
			if(write(outf,buff,count)<0){
				fprintf(stderr,"Write error - disk removed?\n");
				close(outf);
				if(verify){
					fprintf(stderr,"The rest of this disk will be written to tempfile only.\n");
					skip_diskwrite=1;
				}
				else{
					fprintf(stderr,"Use verify mode to survive errors like these.\n");
					exit(4);
				}
			}
		}
		/*Write data to tempfile.*/
		if(verify){
			if(write(verifyf,buff,count)<0){
				fprintf(stderr,"Error writing to tempfile!\n");
				exit(4);
			}
		}

		print_readwrite();
		
		/* Write header to disk and ask for next disk if current disk is full.*/
		if (bytes_read >= (floppysize-BUFFSIZE)) {
			/* Fill archive_header with correct values */
			sprintf(arch.fd_length,"%d",bytes_read);
			arch.fd_more=MORE;
			/* Write headers and close disk.*/
			close_disk();
			/**/
			bytes_read = written= 0;		  /* Reset the length */
			/* Wait for next floppy to be inserted */
			open_disk(arch.name,MINIMUM_FEASIBLE_DISKSIZE);
			if(verify)
			  open_verifyfile();
		}
	}
	
	/* we reached the end of stdin........          */
	if (count > 0) {
		totalread += count;
		bytes_read+=count;
		if(!skip_diskwrite){
			wcount=write(outf,buff,count);
			if(wcount>0){
				written+=wcount;
				totalwritten+=wcount;
			}
			else{
				perror("floppybackup: wcount");
				close(outf);
				exit(2);
			}
		}
		if(verify)
		  write(verifyf,buff,count);
	} else if (count < 0){
		/* We don't have data.... BUT did we had an error ???    */
		perror("floppybackup: Rread");
		exit(1);
	}
	print_readwrite();
	/* Now write the header at the beginning of disk.*/
	/* Fill archive_header with correct values */
	arch.fd_more=NOMORE;
	sprintf(arch.fd_number,"%d",disknum);
	sprintf(arch.fd_length,"%d",bytes_read);
	close_disk();
	
	/* Info */
	if(!quiet)
	  fprintf(stderr,
				 "\rSummary: read %d bytes, wrote %d bytes in %d seconds",
				 totalread,totalwritten,(int)(time(NULL)-btime));
	if((dtime=(time(NULL)-btime))>0){
		if(!quiet)
		  fprintf(stderr,
					 " (%d bytes/second).\n"
					 "%d seconds of the time (%d%%) was spent waiting for a new disk.\n",
					 totalwritten/(int)(dtime),
					 (int)(user_time),((int)(user_time)*100)/(int)(dtime));
	}
	else if(!quiet)
	  fprintf(stderr,".\n");
#if 0
	if(!quiet){
		if(!errors)
		  fprintf(stderr,"No errors encountered.\n");
		else
		  fprintf(stderr,
					 "At least %d errors were encountered, and (hopefully) solved.\n",
					 errors);
	}
#endif
	sync();
	exit(0);
}