/* * 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) { int cc, i, count; off_t startoffset; #ifndef linux assert((nbytes & (DEV_BSIZE-1)) == 0); #endif if (!forcereads) return read(fd, buf, nbytes); if ((startoffset = lseek(fd, (off_t) 0, SEEK_CUR)) < 0) { perror("devread: seeking to get input file ptr"); exit(1); } count = 0; for (i = 0; i < IORETRIES; i++) { while (nbytes) { cc = read(fd, buf, nbytes); if (cc == 0) break; if (cc > 0) { nbytes -= cc; buf += cc; count += cc; continue; } if (i == 0) fprintf(stderr, "read failed: %s, " "will retry %d more times\n", strerror(errno), IORETRIES-1); nbytes += count; buf -= count; count = 0; goto again; } return count; again: if (lseek(fd, startoffset, SEEK_SET) < 0) { perror("devread: seeking to set file ptr"); exit(1); } } fprintf(stderr, "devread: read failed in sector range [%u-%u], " "returning zeros\n", bytestosec(startoffset), bytestosec(startoffset+nbytes)); memset(buf, 0, nbytes); return nbytes; }
static void reloc_bsdlabel(struct disklabel *label, int reloctype) { int i, npart; uint32_t slicesize; /* * This relocation only makes sense in slice mode, * i.e., we are installing a slice image into another slice. */ if (slice == 0) return; if (label->d_magic != DISKMAGIC || label->d_magic2 != DISKMAGIC) { fprintf(stderr, "No disklabel at relocation offset\n"); exit(1); } assert(outputmaxsize > 0); slicesize = bytestosec(outputmaxsize); /* * Fixup the partition table. */ npart = label->d_npartitions; for (i = 0; i < npart; i++) { uint32_t poffset, psize; if (label->d_partitions[i].p_size == 0) continue; /* * Don't mess with OpenBSD partitions 8-15 which map * extended DOS partitions. Also leave raw partition * alone as it maps the entire disk (not just slice) * and we don't know how big that is. */ if (reloctype == RELOC_OBSDDISKLABEL && (i == 2 || (i >= 8 && i < 16))) continue; /* * Perform the relocation, making offsets absolute */ label->d_partitions[i].p_offset += outputminsec; poffset = label->d_partitions[i].p_offset; psize = label->d_partitions[i].p_size; /* * Tweak sizes so BSD doesn't whine: * - truncate any partitions that exceed the slice size * - change RAW ('c') partition to match slice size */ if (poffset + psize > outputmaxsec) { fprintf(stderr, "WARNING: partition '%c' " "too large for slice, truncating\n", 'a' + i); label->d_partitions[i].p_size = outputmaxsec - poffset; } else if (i == RAW_PART && psize != slicesize) { assert(label->d_partitions[i].p_offset == outputminsec); fprintf(stderr, "WARNING: raw partition '%c' " "too small for slice, growing\n", 'a' + i); label->d_partitions[i].p_size = slicesize; } } label->d_checksum = 0; label->d_checksum = dkcksum(label); }
/* * Decompress the chunk, calculating hashes */ static void hashchunk(int chunkno, char *chunkbufp, struct hashinfo **hinfop) { blockhdr_t *blockhdr; struct region *regp; z_stream z; int err, nreg; char hash[HASH_MAXSIZE]; unsigned char *(*hashfunc)(const unsigned char *, unsigned long, unsigned char *); readbuf_t *rbuf; #ifdef TIMEIT u_int64_t sstamp, estamp; #endif z.zalloc = Z_NULL; z.zfree = Z_NULL; z.opaque = Z_NULL; z.next_in = Z_NULL; z.avail_in = 0; z.next_out = Z_NULL; err = inflateInit(&z); CHECK_ERR(err, "inflateInit"); memset(hash, 0, sizeof hash); /* * Grab the header. It is uncompressed, and holds the real * image size and the magic number. Advance the pointer too. */ blockhdr = (blockhdr_t *)chunkbufp; chunkbufp += DEFAULTREGIONSIZE; nregions += blockhdr->regioncount; z.next_in = chunkbufp; z.avail_in = blockhdr->size; switch (blockhdr->magic) { case COMPRESSED_V1: regp = (struct region *)((struct blockhdr_V1 *)blockhdr + 1); break; case COMPRESSED_V2: regp = (struct region *)((struct blockhdr_V2 *)blockhdr + 1); break; default: fprintf(stderr, "Bad Magic Number!\n"); exit(1); } /* * Deterimine the hash function */ switch (hashtype) { case HASH_TYPE_MD5: default: hashfunc = MD5; break; case HASH_TYPE_SHA1: hashfunc = SHA1; break; } /* * Loop through all regions, decompressing and hashing data * in HASHBLK_SIZE or smaller blocks. */ rbuf = alloc_readbuf(0, bytestosec(HASHBLK_SIZE), 0); if (rbuf == NULL) { fprintf(stderr, "no memory\n"); exit(1); } for (nreg = 0; nreg < blockhdr->regioncount; nreg++) { uint32_t rstart, rsize, hsize; rstart = regp->start; rsize = regp->size; ndatabytes += sectobytes(rsize); while (rsize > 0) { if (rsize > bytestosec(HASHBLK_SIZE)) hsize = bytestosec(HASHBLK_SIZE); else hsize = rsize; z.next_out = rbuf->data; z.avail_out = sectobytes(hsize); #ifdef TIMEIT sstamp = rdtsc(); #endif err = inflate(&z, Z_SYNC_FLUSH); #ifdef TIMEIT estamp = rdtsc(); dcycles += (estamp - sstamp); #endif if (err != Z_OK && err != Z_STREAM_END) { fprintf(stderr, "inflate failed, err=%d\n", err); exit(1); } /* * Make sure we are still in synch */ if (z.avail_out != 0) { fprintf(stderr, "inflate failed to fill buf, %d left\n", z.avail_out); exit(1); } if (err == Z_STREAM_END && hsize != rsize) { fprintf(stderr, "inflate ran out of input, %d left\n", rsize - hsize); exit(1); } /* * Compute the hash */ (void)(*hashfunc)(rbuf->data, sectobytes(hsize), hash); addhash(hinfop, chunkno, rstart, hsize, hash); rstart += hsize; rsize -= hsize; } regp++; } free_readbuf(rbuf); if (z.avail_in != 0) { fprintf(stderr, "too much input for chunk, %d left\n", z.avail_in); exit(1); } }
void report_hash_stats(int pnum) { #ifdef HASHSTATS uint32_t b1, b2; double t; struct stat sb; fprintf(stderr,"\nHASH STATS:\n\n"); fprintf(stderr, "Signature file: %s ", hashfile); sb.st_mtime = 0; if (lstat(hashfile, &sb) >= 0 && S_ISLNK(sb.st_mode)) { char nbuf[128]; int i; i = readlink(hashfile, nbuf, sizeof(nbuf)); if (i > 0) { nbuf[i] = 0; fprintf(stderr, "-> %s ", nbuf); } stat(hashfile, &sb); } fprintf(stderr, "(%u)\n", (unsigned)sb.st_mtime); fprintf(stderr, "Partition: %d\n", pnum); fprintf(stderr, "Max hash block size: %u sectors\n\n", bytestosec(hashdatasize)); fprintf(stderr, "Hash incomplete ranges: %d\n", hash_free); t = time_curr_read.tv_sec + (double)time_curr_read.tv_usec / 1000000.0; fprintf(stderr, "Disk read time: %7.3f sec\n", t); t = time_hash.tv_sec + (double)time_hash.tv_usec / 1000000.0; fprintf(stderr, "Hash time: %7.3f sec\n", t); t = time_hash_and_cmp.tv_sec + (double)time_hash_and_cmp.tv_usec / 1000000.0; fprintf(stderr, "Read+hash time: %7.3f sec\n\n", t); b1 = hashstats.hash_compares; b2 = hashstats.hash_identical; fprintf(stderr, "Hash blocks compared: %10u\n", b1); fprintf(stderr, " Identical: %10u (%.1f%%)\n", b2, ((double)b2 / b1) * 100.0); b1 = hashstats.hash_scompares; b2 = hashstats.hash_sidentical; fprintf(stderr, "Total sectors compared: %10u\n", b1); fprintf(stderr, " Identical: %10u (%.1f%%)\n\n", b2, ((double)b2 / b1) * 100.0); b1 = hashstats.orig_allocated; fprintf(stderr, "Original sectors: %10u\n", b1); b1 = hashstats.cur_allocated; fprintf(stderr, "Current sectors: %10u\n", b1); b1 = hashstats.shared; fprintf(stderr, "Common sectors: %10u\n", b1); b1 = hashstats.orig_allocated; b2 = hashstats.orig_only + hashstats.gapsects; fprintf(stderr, "Deleted from original: %10u (%.1f%%)\n", b2, ((double)b2 / b1) * 100.0); b2 = hashstats.cur_only; fprintf(stderr, "Added to original: %10u (%.1f%%)\n", b2, ((double)b2 / b1) * 100.0); b2 = (hashstats.shared - hashstats.unchanged); fprintf(stderr, "Modified from original: %10u (%.1f%%)\n\n", b2, ((double)b2 / b1) * 100.0); fprintf(stderr, "Hash blocks covering free sectors: %u\n", hashstats.gaps); fprintf(stderr, " Total free sectors covered: %u\n", hashstats.gapsects); fprintf(stderr, " Hash blocks compared identical: %u\n", hashstats.unchangedgaps); fprintf(stderr, " Free sectors compared identical: %u\n", hashstats.gapunchanged); fprintf(stderr, " Allocated sectors assumed changed: %u\n", hashstats.nocompare); fprintf(stderr, " Assumed changed due to fixups: %u\n", hashstats.fixup); fprintf(stderr,"\nEND HASH STATS\n"); #endif }
/* * BSD partition table offsets are relative to the start of the raw disk. * Very convenient. */ static int read_bsdpartition(int infd, struct disklabel *dlabel, int part) { int i, cc, rval = 0; struct fs fs; union { struct cg cg; char pad[MAXBSIZE]; } cg; u_int32_t size, offset, fssect; int32_t sbfree; offset = dlabel->d_partitions[part].p_offset; size = dlabel->d_partitions[part].p_size; if (dlabel->d_partitions[part].p_fstype == FS_SWAP) { addskip(offset, size); return 0; } if (dlabel->d_partitions[part].p_fstype != FS_BSDFFS) { warnx("BSD Partition '%c': Not a BSD Filesystem", BSDPARTNAME(part)); return 1; } if (read_bsdsblock(infd, offset, part, &fs)) return 1; sbfree = (fs.fs_cstotal.cs_nbfree * fs.fs_frag) + fs.fs_cstotal.cs_nffree; if (debug) { fprintf(stderr, " bfree %9lld, bsize %9d, cgsize %9d\n", (long long)fs.fs_cstotal.cs_nbfree, fs.fs_bsize, fs.fs_cgsize); } assert(fs.fs_cgsize <= MAXBSIZE); assert((fs.fs_cgsize % secsize) == 0); /* * See if the filesystem is smaller than the containing partition. * If so, and we are skipping such space, inform the user. */ fssect = bytestosec(fs.fs_fsize * (off_t)fs.fs_size); if (excludenonfs && fssect < size) { warnx("BSD Partition '%c': filesystem smaller than partition, " "excluding [%u-%u]", BSDPARTNAME(part), offset+fssect, offset+size-1); addskip(offset + fssect, size - fssect); } freecount = 0; for (i = 0; i < fs.fs_ncg; i++) { unsigned long cgoff; cgoff = fsbtodb(&fs, cgtod(&fs, i)) + offset; if (devlseek(infd, sectobytes(cgoff), SEEK_SET) < 0) { warn("BSD Partition '%c': " "Could not seek to cg %d at %lld", BSDPARTNAME(part), i, (long long)sectobytes(cgoff)); return 1; } if ((cc = devread(infd, &cg, fs.fs_cgsize)) < 0) { warn("BSD Partition '%c': Could not read cg %d", BSDPARTNAME(part), i); return 1; } if (cc != fs.fs_cgsize) { warn("BSD Partition '%c': Truncated cg %d", BSDPARTNAME(part), i); return 1; } if (debug > 1) { fprintf(stderr, " CG%d\t offset %9ld, bfree %6d\n", i, cgoff, cg.cg.cg_cs.cs_nbfree); } rval = read_bsdcg(&fs, &cg.cg, i, offset); if (rval) return rval; } if (rval == 0 && freecount != sbfree) { warnx("BSD Partition '%c': " "computed free count (%d) != expected free count (%d)", BSDPARTNAME(part), freecount, sbfree); } return rval; }