imagezip.c 75.9 KB
Newer Older
Leigh B. Stoller's avatar
Leigh B. Stoller committed
1
/*
2
 * Copyright (c) 2000-2015 University of Utah and the Flux Group.
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
 * 
 * {{{EMULAB-LICENSE
 * 
 * This file is part of the Emulab network testbed software.
 * 
 * This file is free software: you can redistribute it and/or modify it
 * under the terms of the GNU Affero General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or (at
 * your option) any later version.
 * 
 * This file is distributed in the hope that it will be useful, but WITHOUT
 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
 * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU Affero General Public
 * License for more details.
 * 
 * You should have received a copy of the GNU Affero General Public License
 * along with this file.  If not, see <http://www.gnu.org/licenses/>.
 * 
 * }}}
Leigh B. Stoller's avatar
Leigh B. Stoller committed
22
23
 */

24
25
/*
 * An image zipper.
Mike Hibler's avatar
all:    
Mike Hibler committed
26
27
 *
 * TODO:
28
29
30
 *	Multithread so that we can be reading ahead on the input device
 *	and overlapping IO with compression.  Maybe a third thread for
 *	doing output.
31
 */
32
33
34
#ifdef WITH_CRYPTO
#include <openssl/evp.h>
#include <openssl/sha.h>
35
#include <openssl/rsa.h>
36
#include <openssl/rand.h>
37
38
39
#ifdef SIGN_CHECKSUM
#include <openssl/err.h>
#endif
40
#endif
41
#include <ctype.h>
42
#include <err.h>
Mac Newbold's avatar
Mac Newbold committed
43
#include <fcntl.h>
44
#if !defined(__UCLIBC__)
45
#include <fstab.h>
46
#endif
47
48
#include <stdio.h>
#include <stdlib.h>
Mac Newbold's avatar
Mac Newbold committed
49
#include <unistd.h>
Mike Hibler's avatar
all:    
Mike Hibler committed
50
#include <string.h>
51
#include <assert.h>
Mike Hibler's avatar
all:    
Mike Hibler committed
52
#include <errno.h>
53
54
55
#include <sys/param.h>
#include <sys/time.h>
#include <sys/stat.h>
Mac Newbold's avatar
Mac Newbold committed
56
#include <zlib.h>
57

58
#include "imagehdr.h"
Mike Hibler's avatar
Mike Hibler committed
59
60
#include "sliceinfo.h"
#include "global.h"
61
#include "checksum.h"
62
63
64
65
66
67
68
69
70
71
72
73
74
75
#include "range.h"
#ifdef WITH_HASH
#include "hashmap/hashmap.h"
#endif

/*
 * Attempt to split chunks so that hash blocks don't span chunk boundaries.
 *
 * XXX nice thought, but it doesn't do a very good job (saves less than 50%)
 * of the crossings at the expense of wasting about 4% more space). Unless
 * we come up with a less hacky way to fill chunks (e.g. PD fitblk.c) where
 * we can avoid it entirely, don't even try.
 */
#undef WITH_HASH_CHUNKSPLIT
76

77
78
/* XXX this is a hack right now */
#define USE_HACKSORT 0
Mac Newbold's avatar
Mac Newbold committed
79

80
#define min(a,b) ((a) <= (b) ? (a) : (b))
81

82
char	*infilename;
83
int	infd, outfd, incanseek, outcanseek;
84
85
86
87
88
89
90
91
92
93
int	secsize	  = 512;	/* XXX bytes. */
int	debug	  = 0;
int	dots	  = 0;
int	info	  = 0;
int	version	  = 0;
int	slicemode = 0;
int	maxmode	  = 0;
int	slice	  = 0;
int	level	  = 4;
long	dev_bsize = 1;
94
uint32_t compat   = 0;
95
int	frangesize= 64;	/* 32k */
96
int	zerofrange= 0;
97
98
99
100
int	forcereads= 0;
int	badsectors= 0;
int	retrywrites= 1;
int	dorelocs  = 1;
101
int	forcerelocs = 0;
102
103
int	metaoptimize = 0;
int	filemode  = 0;
104
int	excludenonfs = 0;
105
int	do_encrypt = 0;
106
int	cipher = ENC_NONE;
107
int	do_checksum = 0;
108
int	csumalg = CSUM_NONE;
109
off_t	datawritten;
110
partmap_t ignore, forceraw;
Mike Hibler's avatar
Mike Hibler committed
111
static	int got_imageid;
112
static unsigned char imageid[UUID_LENGTH];
113

114
#ifdef WITH_HASH
115
char	*hashfile;
116
char	*newhashfile;
117
int	deltapct = -1;
118
119
120
#endif

#ifdef WITH_CRYPTO
121
#ifdef SIGN_CHECKSUM
122
RSA		*sig_key = NULL;	/* signing key */
123
124
static void output_public_key(char *, RSA *);
#endif
125
unsigned char	*enc_key = NULL;	/* encryption key */
126
static void output_encrypt_key(char *, unsigned char *, int);
127
#endif
128
static void output_uuid(char *, char *);
Mike Hibler's avatar
all:    
Mike Hibler committed
129

130
131
132
133
#define HDRUSED(reg, rel) \
    (sizeof(blockhdr_t) + \
    (reg) * sizeof(struct region) + (rel) * sizeof(struct blockreloc))

134
135
136
/*
 * We want to be able to compress slices by themselves, so we need
 * to know where the slice starts when reading the input file for
137
 * compression.
138
139
140
 *
 * These numbers are in sectors.
 */
141
142
unsigned long inputminsec	= 0;
unsigned long inputmaxsec	= 0;	/* 0 means the entire input image */
143

144
struct range	*ranges, *skips, *fixups;
145
int		numranges, numskips, numfixups;
146
147
148
149
150
151
152
153
154
155
struct blockreloc	*relocs;
int			numregions, numrelocs;

void	dumpskips(int verbose);
static void	sortrange(struct range **head, int domerge,
		  int (*rangecmp)(struct range *, struct range *));
int	mergeskips(int verbose);
int	mergeranges(struct range *head);
void	makeranges(void);
void	dumpranges(int verbose);
156
uint32_t sectinranges(struct range *range);
157
158
void	addvalid(uint32_t start, uint32_t size);
void	addreloc(off_t offset, off_t size, int reloctype);
159
void	removereloc(off_t offset, off_t size, int reloctype);
Mike Hibler's avatar
Mike Hibler committed
160
static int cmpfixups(struct range *r1, struct range *r2);
161
162

/* Forward decls */
Mike Hibler's avatar
Mike Hibler committed
163
164
int	read_image(int fd);
int	read_raw(int fd);
165
166
int	compress_image(void);
void	usage(void);
167

Mike Hibler's avatar
Mike Hibler committed
168
169
static SLICEMAP_PROCESS_PROTO(read_slice);

170
171
#define READ_RETRIES	1
#define WRITE_RETRIES	10
172
173
174
175

ssize_t
slowread(int fd, void *buf, size_t nbytes, off_t startoffset)
{
176
177
	int cc, i, count;

178
179
	assert(fd != infd || incanseek);

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
	fprintf(stderr, "read failed: will retry by sector %d more times\n",
		READ_RETRIES);

	count = 0;
	for (i = 0; i < READ_RETRIES; i++) {
		if (lseek(fd, startoffset, SEEK_SET) < 0) {
			perror("devread: seeking to set file ptr");
			exit(1);
		}
		while (nbytes > 0) {
			cc = read(fd, buf, nbytes);
			if (cc == 0) {
				nbytes = 0;
				continue;
			}
			if (cc > 0) {
				nbytes -= cc;
				buf     = (void *)((char *)buf + cc);
				count  += cc;
				continue;
			}

			nbytes += count;
			buf     = (void *)((char *)buf - count);
			count   = 0;
			break;
		}
		if (nbytes == 0)
			break;
	}
	return count;
211
}
212

Mike Hibler's avatar
Mike Hibler committed
213
214
215
216
217
/*
 * Assert the hell out of it...
 */
off_t
devlseek(int fd, off_t off, int whence)
218
{
219
	off_t noff;
220
221

	assert(fd != infd || incanseek);
222
223
224
225
226
227
	assert((off & (DEV_BSIZE-1)) == 0);
	noff = lseek(fd, off, whence);
	if (!filemode) {
		assert(noff == (off_t)-1 || (noff & (DEV_BSIZE-1)) == 0);
	}
	return noff;
228
229
}

230
231
232
233
234
235
/*
 * Wrap up read in a retry mechanism to persist in the face of IO errors,
 * even faking data if requested.
 */
ssize_t
devread(int fd, void *buf, size_t nbytes)
236
{
237
238
239
	int		cc, count;
	off_t		startoffset;
	size_t		onbytes;
240
241

#ifndef linux
242
	assert((nbytes & (DEV_BSIZE-1)) == 0);
243
#endif
244
	cc = read(fd, buf, nbytes);
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264

	/*
	 * If reading from a pipe, try to fill the input buffer
	 * even if it takes multiple reads. Forces deterministic
	 * behavior when compressing the same input stream.
	 */
	if (cc > 0 && cc != nbytes && !incanseek) {
		int ncc;

		count = nbytes - cc;
		while (count > 0) {
			ncc = read(fd, (char *)buf + cc, count);
			if (ncc <= 0)
				break;
			cc += ncc;
			count -= ncc;
		}
		return cc;
	}

265
266
267
	if (!forcereads || cc >= 0)
		return cc;

268
269
	assert(fd != infd || incanseek);

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
	/*
	 * Got an error reading the range.
	 * Retry one sector at a time, replacing any bad sectors with
	 * zeroed data.
	 */
	if ((startoffset = lseek(fd, (off_t) 0, SEEK_CUR)) < 0) {
		perror("devread: seeking to get input file ptr");
		exit(1);
	}
	assert((startoffset & (DEV_BSIZE-1)) == 0);

	onbytes = nbytes;
	while (nbytes > 0) {
		if (nbytes > DEV_BSIZE)
			count = DEV_BSIZE;
		else
			count = nbytes;
		cc = slowread(fd, buf, count, startoffset);
		if (cc != count) {
			fprintf(stderr, "devread: read failed on sector %u, "
				"returning zeros\n",
				bytestosec(startoffset));
			if (cc < 0)
				cc = 0;
			memset(buf, 0, count-cc);
			badsectors++;
			cc = count;
		}
		nbytes -= cc;
		buf = (void *)((char *)buf + cc);
		startoffset += cc;
	}
	return onbytes;
303
}
304

305
306
/*
 * Wrap up write in a retry mechanism to protect against transient NFS
307
 * errors causing a fatal error.
308
309
 */
ssize_t
310
devwrite(int fd, const void *buf, size_t nbytes)
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
	int		cc, i, count = 0;
	off_t		startoffset = 0;

	if (retrywrites && outcanseek &&
	    ((startoffset = lseek(fd, (off_t) 0, SEEK_CUR)) < 0)) {
		perror("devwrite: seeking to get output file ptr");
		exit(1);
	}

	for (i = 0; i < WRITE_RETRIES; i++) {
		while (nbytes) {
			cc = write(fd, buf, nbytes);

			if (cc > 0) {
				nbytes -= cc;
				buf     = (void *)((char *)buf + cc);
				count  += cc;
				continue;
			}

			if (!retrywrites)
				return cc;

			if (i == 0)
				perror("write error: will retry");

			sleep(1);
			nbytes += count;
			buf     = (void *)((char *)buf - count);
			count   = 0;
			goto again;
		}
		if (retrywrites && fsync(fd) < 0) {
			perror("fsync error: will retry");
			sleep(1);
			nbytes += count;
			buf     = (void *)((char *)buf - count);
			count   = 0;
			goto again;
		}
		datawritten += count;
		return count;
	again:
		if (lseek(fd, startoffset, SEEK_SET) < 0) {
			perror("devwrite: seeking to set file ptr");
			exit(1);
		}
	}
	perror("write error: busted for too long");
	fflush(stderr);
	exit(1);
363
364
}

365
366
367
static int
setpartition(partmap_t map, char *str)
{
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
	int dospart;
	char bsdpart;

	if (isdigit(str[1])) {
		bsdpart = str[2];
		str[2] = '\0';
	} else {
		bsdpart = str[1];
		str[1] = '\0';
	}
	dospart = atoi(str);
	if (dospart < 1 || dospart > MAXSLICES)
		return EINVAL;

	/* common case: apply to complete DOS partition */
	if (bsdpart == '\0') {
		map[dospart-1] = ~0;
		return 0;
	}

	if (bsdpart < 'a' || bsdpart > 'p')
		return EINVAL;

	map[dospart-1] |= (1 << (bsdpart - 'a'));
	return 0;
393
}
Mac Newbold's avatar
Mac Newbold committed
394
395

int
396
main(int argc, char *argv[])
Mac Newbold's avatar
Mac Newbold committed
397
{
398
399
400
401
402
403
404
	int	ch, rval;
	char	*outfilename = 0;
	int	rawmode	  = 0;
	int	slicetype = 0;
	struct timeval sstamp;
	extern char build_info[];

405
	memset(imageid, '\0', UUID_LENGTH);
406
407

	gettimeofday(&sstamp, 0);
408
	while ((ch = getopt(argc, argv, "vlbnNdihrs:c:z:ofI:13F:DR:S:XxH:U:P:Me:k:u:a:ZL")) != -1)
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
		switch(ch) {
		case 'v':
			version++;
			break;
		case 'i':
			info++;
			debug++;
			break;
		case 'D':
			retrywrites = 0;
			break;
		case 'd':
			debug++;
			break;
		case 'l':
Mike Hibler's avatar
Mike Hibler committed
424
			slicetype = IZTYPE_LINUX;
425
426
			break;
		case 'b':
Mike Hibler's avatar
Mike Hibler committed
427
			slicetype = IZTYPE_FBSDNOLABEL;
428
			break;
429
430
431
		case 'L':
			forcerelocs = 1;
			break;
432
433
434
435
		case 'N':
			dorelocs = 0;
			break;
		case 'n':
Mike Hibler's avatar
Mike Hibler committed
436
			slicetype = IZTYPE_NTFS;
437
438
439
440
441
442
443
444
445
			break;
		case 'o':
			dots++;
			break;
		case 'r':
			rawmode++;
			break;
		case 'S':
			slicetype = atoi(optarg);
Mike Hibler's avatar
Mike Hibler committed
446
447
448
			/* XXX special case for freebsd */
			if (slicetype == IZTYPE_386BSD)
				slicetype = IZTYPE_FBSDNOLABEL;
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
			break;
		case 's':
			slicemode = 1;
			slice = atoi(optarg);
			break;
		case 'z':
			level = atoi(optarg);
			if (level < 0 || level > 9)
				usage();
			break;
		case 'c':
			maxmode     = 1;
			inputmaxsec = atoi(optarg);
			break;
		case 'I':
			if (setpartition(ignore, optarg))
				usage();
			break;
		case 'R':
			if (setpartition(forceraw, optarg))
				usage();
			break;
		case '1':
472
473
474
475
			compat = COMPRESSED_V1;
			break;
		case '3':
			compat = COMPRESSED_V3;
476
477
478
479
480
481
			break;
		case 'F':
			frangesize = atoi(optarg);
			if (frangesize < 0)
				usage();
			break;
482
483
484
		case 'Z':
			zerofrange = 1;
			break;
485
486
487
		case 'x':
			excludenonfs++;
			break;
488
489
490
		case 'X':
			forcereads++;
			break;
491
#ifdef WITH_HASH
492
		case 'H':
493
			hashfile = optarg;
494
495
			break;
		case 'U':
496
			newhashfile = optarg;
497
498
499
500
501
502
			break;
		case 'P':
			deltapct = atoi(optarg);
			if (deltapct < 0)
				usage();
			break;
503
#else
504
505
506
507
		case 'H':
		case 'U':
		case 'P':
			fprintf(stderr, "'%c' option not supported\n", ch);
508
509
			usage();
			break;
510
#endif
511
512
513
514
515
516
517
518
519
		case 'M':
			metaoptimize++;
			break;
		case 'f':
			filemode++;
			rawmode++;
			break;
		case 'a':
#ifdef WITH_CRYPTO
520
			/* Authentication (checksum) hash algorithm */
521
522
523
524
525
			if (strcmp(optarg, "SHA1") == 0) {
				csumalg = CSUM_SHA1;
			}
			else {
				fprintf(stderr, "Only know \"SHA1\"\n");
526
527
				usage();
			}
528
			do_checksum++;
529
530
531
#ifdef SIGN_CHECKSUM
			csumalg |= CSUM_SIGNED;
#else
532
533
			fprintf(stderr, "WARNING: checksum is not signed\n");
#endif
534
#else
535
536
			fprintf(stderr, "'a' option not supported\n");
			usage();
537
#endif
538
539
540
			break;

		case 'e':
541
#ifdef WITH_CRYPTO
542
			/* Encryption cipher */
543
544
545
546
			if (strcmp(optarg, "bf_cbc") == 0) {
				cipher = ENC_BLOWFISH_CBC;
			}
			else {
547
548
549
550
551
				fprintf(stderr,
					"Only know \"bf_cbc\" (blowfish CBC)\n");
				usage();
			}
			do_encrypt++;
552
#else
553
554
			fprintf(stderr, "'e' option not supported\n");
			usage();
555
#endif
556
557
558
559
560
561
562
563
			break;
		case 'k':
#ifdef WITH_CRYPTO
			/* Encryption key file */
			enc_key = calloc(1, ENC_MAX_KEYLEN);
			if (enc_key == NULL ||
			    !encrypt_readkey(optarg, enc_key, ENC_MAX_KEYLEN))
				usage();
564
565
566
			/* XXX can you intuit the cipher from the key? */
			if (cipher == ENC_NONE)
				cipher = ENC_BLOWFISH_CBC;
567
568
569
570
571
572
573
#else
			fprintf(stderr, "'k' option not supported\n");
			usage();
#endif
			break;
		case 'u':
			/* UUID for image id. */
574
			if (!hexstr_to_mem(imageid, optarg, UUID_LENGTH))
575
				usage();
576
			got_imageid = 1;
577
578
579
580
581
582
583
584
585
586
587
588
			break;
		case 'h':
		case '?':
		default:
			usage();
		}
	argc -= optind;
	argv += optind;

	if (version || debug) {
		fprintf(stderr, "%s\n", build_info);
		if (version) {
Mike Hibler's avatar
Mike Hibler committed
589
590
591
592
593
594
595
596
597
598
599
600
			char *str;

			fprintf(stderr, "Supports:\n");
			fprintf(stderr, "  Partitioning: ");
#ifdef WITH_MBR
			fprintf(stderr, "MBR");
			str = ",";
#endif
#ifdef WITH_GPT
			fprintf(stderr, "%sGPT", str);
#endif
			fprintf(stderr, "\n  Partition types: ");
Mike Hibler's avatar
Mike Hibler committed
601
			printslicemap();
Mike Hibler's avatar
Mike Hibler committed
602
603
604
605
606
607
608
609
610
611
612
			fprintf(stderr, "\n  Features: ");
			fprintf(stderr, "image UUIDs");
#ifdef WITH_CRYPTO
#ifdef SIGN_CHECKSUM
			fprintf(stderr, ",signed");
#else
			fprintf(stderr, ",unsigned");
#endif
			fprintf(stderr, " SHA1 checksums");
			fprintf(stderr, ",blowfish encryption");
#endif
613
#ifdef WITH_HASH
Mike Hibler's avatar
Mike Hibler committed
614
			fprintf(stderr, ",delta image creation");
615
#endif
616
617
618
619
620
621
622
623
			fprintf(stderr, "\n");
			exit(0);
		}
	}

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

624
625
626
627
628
629
630
631
632
	if (compat &&
	    (
#ifdef WITH_CRYPTO
	     do_encrypt || do_checksum ||
#endif
	     got_imageid)) {
		fprintf(stderr, "Cannot use uuid/encrypt/checksum with -3\n");
		usage();
	}
633
634
635
636
637
638
639
640
641
642
643
644
645
646
	if (slicemode && (slice < 1 || slice > MAXSLICES)) {
		fprintf(stderr, "Slice must be a DOS partition (1-4) "
			"or extended DOS partition (5-%d)\n\n", MAXSLICES);
		usage();
	}
	if (maxmode && slicemode) {
		fprintf(stderr, "Count option (-c) cannot be used with "
			"the slice (-s) option\n\n");
		usage();
	}
	if (!info && argc != 2) {
		fprintf(stderr, "Must specify an output filename!\n\n");
		usage();
	}
647
	else
648
649
650
651
652
		outfilename = argv[1];

	if (!slicemode && !filemode && dorelocs)
		dorelocs = 0;

653
654
655
	if (forcerelocs && !dorelocs)
		dorelocs = 1;

656
	infilename = argv[0];
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
	if (strcmp(infilename, "-")) {
		if ((infd = open(infilename, O_RDONLY, 0)) < 0) {
			perror(infilename);
			exit(1);
		}
		incanseek = 1;
	}
	else {
		if (!rawmode) {
			fprintf(stderr,
				"Can only use stdin as input with -f or -r\n");
			usage();
		}
		infd = fileno(stdin);
		incanseek = 0;
		forcereads = 0;
673
	}
674

675
#ifdef WITH_CRYPTO
676
677
678
	/*
	 * Let's get random!
	 */
Mike Hibler's avatar
Mike Hibler committed
679
680
	if (!RAND_load_file("/dev/urandom", 1024))
		fprintf(stderr, "Error getting random seed\n");
681
682
683
684
685
686
687
688
689
690
691

#ifdef SIGN_CHECKSUM
	/*
	 * Generate a signing pubkey.
	 * The pubkey is written to <imagename>.skey.
	 */
	if (do_checksum) {
		sig_key = RSA_generate_key(CSUM_MAX_LEN*8, 17, NULL, NULL);
		if (!info)
			output_public_key(outfilename, sig_key);
	}
Mike Hibler's avatar
Mike Hibler committed
692
693
#endif

694
695
696
697
698
699
700
701
	/*
	 * Generate an encryption key if none was given on the command line.
	 * The key is written to <imagename>.ekey.
	 */
	if (do_encrypt && enc_key == NULL) {
		enc_key = calloc(1, ENC_MAX_KEYLEN);
		if (enc_key == NULL || !RAND_bytes(enc_key, ENC_MAX_KEYLEN)) {
			fprintf(stderr, "Unable to generate random key\n");
Mike Hibler's avatar
Mike Hibler committed
702
703
			exit(1);
		}
704
		if (!info)
705
706
			output_encrypt_key(outfilename, enc_key,
					   ENC_MAX_KEYLEN);
707
	}
708
709
#endif

710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
#ifdef WITH_V3COMPAT
	/*
	 * Deal with pre-crypto backward compatibility.
	 * We don't want to generate V4 images unless we really need it.
	 * Note that this means we do NOT generate a UUID if none was provided.
	 */
	if (!compat &&
#ifdef WITH_CRYPTO
	    !do_encrypt && !do_checksum &&
#endif
	    !got_imageid)
		compat = COMPRESSED_V3;
#endif

	/*
	 * Generate a random UUID if one was not provided and we are
	 * not operating in compatibility mode.
	 */
	if (!compat && !got_imageid) {
		int fd = open("/dev/urandom", O_RDONLY, 0);

		if (fd < 0 ||
		    read(fd, imageid, sizeof(imageid)) != sizeof(imageid)) {
			fprintf(stderr, "WARNING: no UUID generated\n");
734
			memset(imageid, '\0', UUID_LENGTH);
735
736
737
738
739
740
741
742
743
744
745
746
		} else {
			char uuidstr[UUID_LENGTH*2+1];

			mem_to_hexstr(uuidstr, imageid, UUID_LENGTH);
			output_uuid(outfilename, uuidstr);
			got_imageid = 1;
		}

		if (fd >= 0)
			close(fd);
	}

747
748
749
750
751
	/*
	 * Create the skip list by scanning the filesystems on
	 * the disk or indicated partition.
	 */
	if (slicetype != 0) {
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
		/*
		 * See if we can get a meaningful max size value via seek
		 */
		if (inputmaxsec == 0) {
			off_t maxoff = lseek(infd, (off_t)0, SEEK_END);
			if (maxoff != (off_t)-1 && maxoff != (off_t)0) {
				inputmaxsec = bytestosec(maxoff);
				assert(inputmaxsec == 0 ||
				       inputmaxsec > inputminsec);
				if (debug)
					fprintf(stderr, "Max sector: %lu\n",
						inputmaxsec);
			}
			(void) lseek(infd, (off_t)0, SEEK_SET);
		}

Mike Hibler's avatar
Mike Hibler committed
768
		rval = read_slice(-1, slicetype, 0, inputmaxsec,
769
770
771
772
				  infilename, infd);
		if (rval == -1)
			fprintf(stderr, ", cannot process\n");
	} else if (rawmode)
Mike Hibler's avatar
Mike Hibler committed
773
		rval = read_raw(infd);
774
	else
Mike Hibler's avatar
Mike Hibler committed
775
		rval = read_image(infd);
776
777
778
	if (rval) {
		fprintf(stderr, "* * * Aborting * * *\n");
		exit(1);
779
	}
780
781
782
783
784
785
786
787

	/*
	 * Create a valid range list from the skip list
	 */
	(void) mergeskips(debug > 1);
	if (debug)
		dumpskips(debug > 1);
	makeranges();
788
789
	if (debug)
		dumpranges(debug > 1);
790
791
792
793
794
795
796
797
798
799
800
801
802
803
	if (ranges == NULL) {
		/*
		 * No valid ranges, exit with an error.
		 *
		 * XXX it is debatable whether this is really an error.
		 * Perhaps we should just create a valid image file with
		 * one chunk containing no valid ranges, but it seems likely
		 * that they probably made a mistake and we should call it
		 * out immediately.
		 */
		fprintf(stderr,
			"*** No valid data on specified disk/partition!?\n");
		exit(1);
	}
804
805
	sortrange(&fixups, 0, cmpfixups);
	if (debug > 1)
806
		dumpfixups(debug > 2, 0);
807
	fflush(stderr);
808

809
#ifdef WITH_HASH
810
811
	/*
	 * If we are creating a "delta" image from a hash signature,
812
813
814
815
816
	 * (hashfile != NULL) we read in the signature info and reconcile
	 * that with the known allocated range that we have just computed.
	 * The result is a new list of ranges that are currently allocated
	 * and that have changed from the signature version.
	 *
817
	 * If we are creating a new signature file (newhashfile != NULL)
818
819
	 * then we also collect hashinfo along the way, writing out the
	 * newfile when done.
820
	 */
821
822
	if (hashfile || newhashfile) {
		struct range *nranges = NULL;
823
824
825
826
827
828

		/*
		 * next compare allocated 'ranges' and 'hinfo' to find out the
		 * changed blocks -- computing the hashes for some 'ranges'
		 * in the process
		 */
829
830
831
832
833
834
835
836
837
838
839
840
		if (hashmap_compute_delta(ranges, hashfile, infd, inputminsec,
					  newhashfile, &nranges)) {
			fprintf(stderr, "Errors while computing delta!\n");
			exit(1);
		}

		/*
		 * See if we should just create a full image.
		 */
		if (hashfile && deltapct >= 0) {
			uint32_t oldsect = sectinranges(ranges);
			uint32_t newsect = sectinranges(nranges);
841
842
843
844
845
846
847
848
849
			int dofull =
				(oldsect == 0 ||
				 ((double)newsect / oldsect) * 100 > deltapct);
			fprintf(stderr,
				"Full image size %u sect, "
				"delta image size %u sect\n"
				"Auto image selection creating %s image.\n",
				oldsect, newsect, dofull ? "full" : "delta");
			if (dofull) {
850
851
852
853
				freeranges(nranges);
				goto done;
			}
		}
854
855
856
857
858
859

		freeranges(ranges);
		ranges = nranges;
		if (debug) {
			fprintf(stderr, "\nAfter delta computation: ");
			dumpranges(debug > 1);
860
			dumpfixups(debug > 2, 0);
861
		}
862
863
	done:
		hashmap_dump_stats(slice);
864
	}
865
#endif
866

867
868
869
870
871
872
873
874
875
876
877
878
	/*
	 * Now we have all the allocated information, create the image
	 * (unless we just want an info report).
	 */
	if (!info) {
		if (strcmp(outfilename, "-")) {
			if ((outfd = open(outfilename, O_RDWR|O_CREAT|O_TRUNC,
					  0666)) < 0) {
				perror("opening output file");
				exit(1);
			}
			outcanseek = 1;
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
			/*
			 * XXX retrywrites is a hack to avoid transient NFS
			 * write errors: we use fsync after every write to
			 * check that it worked, re-trying if not. However,
			 * some devices (e.g., /dev/null) do not support
			 * fsync, so check for that here and disable the
			 * retry if necessary.
			 */
			if (retrywrites &&
			    fsync(outfd) < 0 && errno == EINVAL) {
				fprintf(stderr,
					"WARNING: '%s' does not support fsync,"
					" write errors may not be detected "
					"or corrected.\n", outfilename);
				retrywrites = 0;
			}
895
896
897
898
899
900
901
902
903
904
905
		}
		else {
			outfd = fileno(stdout);
			outcanseek = 0;
			retrywrites = 0;
		}
		compress_image();
		assert(fixups == NULL);

		if (outcanseek)
			close(outfd);
906
907
908
909
910
911

#ifdef WITH_HASH
		/*
		 * Write out new signature file.
		 */
		if (newhashfile &&
912
		    hashmap_write_hashfile(newhashfile, outfilename))
913
914
			fprintf(stderr, "Could not write new hashfile!\n");
#endif
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
	}
	close(infd);

	{
		struct timeval stamp;
		unsigned int ms;

		gettimeofday(&stamp, 0);
		if (stamp.tv_usec < sstamp.tv_usec) {
			stamp.tv_usec += 1000000;
			stamp.tv_sec--;
		}
		ms = (stamp.tv_sec - sstamp.tv_sec) * 1000 +
			(stamp.tv_usec - sstamp.tv_usec) / 1000;
		fprintf(stderr,
			"Finished in %u.%03u seconds\n",
			ms / 1000, ms % 1000);
	}
	fflush(stderr);
934
935

#ifdef WITH_CRYPTO
936
937
#ifdef SIGN_CHECKSUM
	if (do_checksum)
938
		RSA_free(sig_key);
939
#endif
940
941
#endif

942
	exit(0);
943
944
}

Mike Hibler's avatar
Mike Hibler committed
945
static int
Mike Hibler's avatar
Mike Hibler committed
946
read_slice(int snum, iz_type stype, iz_lba start, iz_size size,
947
	   char *sname, int sfd)
Mike Hibler's avatar
Mike Hibler committed
948
{
Mike Hibler's avatar
Mike Hibler committed
949
	struct sliceinfo *smap = getslicemap(stype);
950

951
952
	if (smap && smap->process)
		return (*smap->process)(snum, stype, start, size, sname, sfd);
953

954
955
956
	fprintf(stderr, "Slice %d is an unknown type %#x (%s)",
		snum+1, stype, smap ? smap->desc : "??");
	return -1;
Mike Hibler's avatar
Mike Hibler committed
957
958
}

959
/*
Mike Hibler's avatar
Mike Hibler committed
960
 * Parse the MBR/GPT and dispatch to the individual readers.
961
 */
Mike Hibler's avatar
Mike Hibler committed
962
963
int
read_image(int fd)
964
{
Mike Hibler's avatar
Mike Hibler committed
965
	int		i, rval = 0;
966
967
	struct iz_disk	disk;
	struct iz_slice	*parttab;
Mike Hibler's avatar
Mike Hibler committed
968
	int		gotbb = 0;
969
	char *		bbstr;
Mike Hibler's avatar
Mike Hibler committed
970
971
	iz_lba		dstart;
	iz_size		dsize;
972
	int		dowarn = debug ? 2 : 0;
973

Mike Hibler's avatar
Mike Hibler committed
974
#ifdef WITH_GPT
975
	if (!gotbb && parse_gpt(fd, &disk, dowarn) == 0) {
Mike Hibler's avatar
Mike Hibler committed
976
		gotbb = 1;
977
978
		bbstr = "GPT";
	}
Mike Hibler's avatar
Mike Hibler committed
979
980
#endif
#ifdef WITH_MBR
981
	if (!gotbb && parse_mbr(fd, &disk, dowarn) == 0) {
Mike Hibler's avatar
Mike Hibler committed
982
		gotbb = 2;
983
984
		bbstr = "MBR";
	}
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
	/*
	 * If we are forcing interpretation as a BSD slice, there should
	 * not be an MBR, however there is sometimes this thing called the
	 * "historical bogus partition table" (by the geom code) which
	 * is an MBR with P4 having 50000 sectors. We need to avoid this
	 * so we force looking for just a disklabel instead.
	 */
	if (gotbb == 2) {
		parttab = disk.slices;
		if (parttab[3].type == IZTYPE_386BSD &&
		    parttab[3].size == 50000 && parttab[3].offset == 0 &&
		    parttab[2].size == 0 && parttab[2].offset == 0 &&
		    parttab[1].size == 0 && parttab[1].offset == 0 &&
		    parttab[0].size == 0 && parttab[0].offset == 0) {
			fprintf(stderr,
				"WARNING: ignoring historical bogus MBR\n");
For faster browsing, not all history is shown. View entire blame