Commit 27d76461 authored by Mike Hibler's avatar Mike Hibler

Random imagezip hacking:

1. Make imagezip (and imageunzip) understand LILO bootblocks.
   Imagezip will create relocation information for all the absolute
   blocks that LILO embeds in the partition.  Imageunzip will correctly
   relocate those locations when laying down the new image.  Thus
   Linux slice images can be layed down anywhere on the disk (as can
   BSD images).

2. Because #1 is incompatible with the old imageunzip, I bumped the
   imagezip format version number (to 3) on images that contain relocations
   (i.e., slice images).  Make sure you have a new imageunzip to unzip
   new slice images.  Whole disk images are still created as version 2
   since they contain no relocs and thus are no different than before.

3. Added -N option to imagezip to create slice images without any
   relocations.  [ We could create our Emulab images this way, since we
   don't have any mechanism for, or reason to, let the user choose which
   partition to load an image in.  Hence our slice images always wind up
   at the same offset on the disk and relocations are unnecessary. ]
   Note that there is no significant expense in either time or space for
   relocations, this option was more of a debugging thing.
parent 45a42844
......@@ -31,7 +31,7 @@ UNZIPCFLAGS = $(CFLAGS) -Wall
UNZIPLIBS = $(LIBS)
ifdef WITH_EXTFS
FSOBJS += extfs/extfs.o
FSOBJS += extfs/extfs.o extfs/reloc_lilo.o
CFLAGS += -DWITH_EXTFS -Iextfs
endif
......
......@@ -34,7 +34,7 @@ UNZIPLIBS = $(LIBS) $(PTHREADLIBS)
.ifdef WITH_EXTFS
FSDIRS += extfs
FSOBJS += extfs.o
FSOBJS += extfs.o reloc_lilo.o
CFLAGS += -DWITH_EXTFS -Iextfs
.endif
......
#
# EMULAB-COPYRIGHT
# Copyright (c) 2000-2003 University of Utah and the Flux Group.
# Copyright (c) 2000-2004 University of Utah and the Flux Group.
# All rights reserved.
#
......@@ -18,9 +18,10 @@ all: libextfs.a
include $(TESTBED_SRCDIR)/GNUmakerules
OBJS = extfs.o
OBJS = extfs.o reloc_lilo.o
extfs.o: $(MAINDIR)/sliceinfo.h $(MAINDIR)/global.h
reloc_lilo.o: $(MAINDIR)/sliceinfo.h $(MAINDIR)/global.h $(MAINDIR)/imagehdr.h
libextfs.a: $(OBJS)
$(AR) $(ARFLAGS) $@ $?
......
/*
* EMULAB-COPYRIGHT
* Copyright (c) 2000-2003 University of Utah and the Flux Group.
* Copyright (c) 2000-2004 University of Utah and the Flux Group.
* All rights reserved.
*/
......@@ -17,6 +17,10 @@ static int read_linuxgroup(struct ext2_super_block *super,
struct ext2_group_desc *group, int index,
u_int32_t sliceoffset, int infd);
extern int fixup_lilo(int slice, int stype, u_int32_t start, u_int32_t size,
char *sname, int infd, int *found);
/*
* Operate on a linux slice. I actually don't have a clue what a linux
* slice looks like. I just know that in our images, the linux slice
......@@ -42,11 +46,20 @@ read_linuxslice(int slice, int stype, u_int32_t start, u_int32_t size,
int dosslice = slice + 1; /* DOS Numbering */
off_t soff;
/*
* Check for a LILO boot block and create relocations as necessary
* (if the partition starts at 0, the values are already relative)
*/
if (dorelocs && start > 0 &&
fixup_lilo(slice, stype, start, size, sname, infd, &rval) != 0)
return 1;
assert((sizeof(fs) & ~LINUX_SUPERBLOCK_SIZE) == 0);
assert((sizeof(groups) & ~EXT2_MAX_BLOCK_SIZE) == 0);
if (debug)
fprintf(stderr, " P%d (Linux Slice)\n", dosslice);
fprintf(stderr, " P%d %s Linux Slice)\n",
dosslice, rval ? "(Bootable" : "(");
/*
* Skip ahead to the superblock.
......@@ -106,6 +119,7 @@ read_linuxslice(int slice, int stype, u_int32_t start, u_int32_t size,
== EXT2_BLOCK_SIZE(&fs));
soff = sectobytes(start) +
(fs.s_first_data_block + 1) * EXT2_BLOCK_SIZE(&fs);
rval = 0;
for (i = 0; i < numgroups; i++) {
int gix;
......
/*
* EMULAB-COPYRIGHT
* Copyright (c) 2000-2004 University of Utah and the Flux Group.
* All rights reserved.
*/
/*
* LILO-based constants.
*/
#define MAX_BOOT2_SECT 10
#define MAX_IMAGE_DESC 19
#define MAX_MAP_SECT 101
/*
* Essential lilo data structs
*/
/*
* Sector addresses.
* These are the absolute values that must be relocated when moving
* a bootable partition. Is there an LBA form?
*/
typedef struct sectaddr {
u_int8_t sector;
u_int8_t track;
u_int8_t device; /* + flags, see below */
u_int8_t head;
u_int8_t nsect;
} sectaddr_t;
#define SADDR_S_SIZE 5
/* flags encoded in device */
#define HARD_DISK 0x80 /* not a floppy */
#define LINEAR_ADDR 0x40 /* mark linear address */
#define LBA32_ADDR 0x20 /* mark lba 32-bit address */
#define LBA32_NOCOUNT 0x10 /* mark address with count absent */
#define DEVFLAGS 0xF0
struct bb1 {
u_int8_t jumpinst[6];
char sig[4]; /* LILO */
u_int16_t stage; /* 1 -- stage1 loader */
u_int16_t version;
u_int16_t timeout;
u_int16_t delay;
u_int8_t port, portparams;
u_int32_t timestamp;
sectaddr_t idesc[2]; /* image descriptors */
sectaddr_t cmdline; /* command line (max 1 sector?) */
u_int8_t prompt;
u_int16_t msglen;
sectaddr_t msg; /* "initial greeting message" */
sectaddr_t keytab; /* "keyboard translation table" */
sectaddr_t boot2[MAX_BOOT2_SECT+1]; /* 2nd stage boot sectors */
};
#define BB1_S_SIZE 108
struct bb2 {
u_int8_t jumpinst[6];
char sig[4]; /* LILO */
u_int16_t stage; /* 2 -- stage2 loader */
u_int16_t version;
};
struct bb10 {
u_int8_t jumpinst[6];
char sig[4]; /* LILO */
u_int16_t stage; /* 0x10 -- chain loader */
u_int16_t version;
u_int16_t offset;
u_int8_t drive, head;
u_int16_t drivemap;
u_int8_t parttab[16*4];
};
union bblock {
struct bb1 bb1;
struct bb2 bb2;
struct bb10 bb10;
char data[1*512];
};
#define BBLOCK_S_SIZE 512
/*
* Image descriptors
*/
struct image {
char name[16];
char passwd[16];
u_int16_t rdsize[2];
sectaddr_t initrd;
sectaddr_t start;
u_int16_t spage, flags, vgamode;
};
#define IMAGE_S_SIZE 52
struct idtab {
u_int16_t sum;
struct image images[MAX_IMAGE_DESC];
};
#define IDTAB_S_SIZE 990
union idescriptors {
struct idtab idtab;
char data[2*512];
};
#define IDESC_S_SIZE 1024
/*
* Map sectors
*/
union mapsect {
sectaddr_t addr[MAX_MAP_SECT+1];
char data[512];
};
#define MSECT_S_SIZE 512
static inline u_int32_t
getsector(sectaddr_t *sect)
{
int flags = (sect->device & DEVFLAGS) & ~HARD_DISK;
u_int32_t sector = 0;
if (sect->device == 0 && sect->nsect == 0 &&
sect->head == 0 && sect->track == 0 && sect->sector == 0)
return 0;
/* XXX */
if (flags == 0) {
fprintf(stderr, "LILO parse: no can do CHS addresses!\n");
return 0;
}
if (flags & LINEAR_ADDR) {
sector |= sect->head << 16;
sector |= sect->track << 8;
sector |= sect->sector;
} else {
if (flags & LBA32_NOCOUNT)
sector |= sect->nsect << 24;
sector |= sect->head << 16;
sector |= sect->track << 8;
sector |= sect->sector;
}
return sector;
}
static inline void
putsector(sectaddr_t *sect, u_int32_t sector, int device, int nsect)
{
int flags = (device & DEVFLAGS) & ~HARD_DISK;
sect->device = device;
sect->nsect = nsect;
if (flags & LINEAR_ADDR) {
sect->head = (sector >> 16) & 0xFF;
sect->track = (sector >> 8) & 0xFF;
sect->sector = sector & 0xFF;
} else {
if (flags & LBA32_NOCOUNT)
sect->nsect = (sector >> 24) & 0xFF;
sect->head = (sector >> 16) & 0xFF;
sect->track = (sector >> 8) & 0xFF;
sect->sector = sector & 0xFF;
}
}
#define LILO_CKSUM 0xabcd
static inline uint16_t
lilocksum(union idescriptors *idp, uint16_t sum)
{
uint16_t *up = (uint16_t *)idp;
uint16_t *ep = (uint16_t *)(idp + 1);
while (up < ep)
sum ^= *up++;
return sum;
}
/*
* EMULAB-COPYRIGHT
* Copyright (c) 2000-2004 University of Utah and the Flux Group.
* All rights reserved.
*/
#include <stdio.h>
#include <stdlib.h>
#include <err.h>
#include <assert.h>
#include <string.h>
#include <sys/types.h>
#include "sliceinfo.h"
#include "global.h"
#include "imagehdr.h"
#include "lilo.h"
/*
* Grok enough LILO to be able to ferret out and create relocs for
* absolute block numbers. In a LILO bootblock and map file.
*
* This has only been tested with LILO 21.4-4 created boot blocks
* and will only work with linear or LBA32 addresses.
*/
static u_int32_t partoff, partsize;
static int readatsector(int fd, u_int32_t sect, void *buf, int size);
static void fixup_sector(u_int32_t poff, sectaddr_t *sect);
static void fixup_map(int fd, u_int32_t startsect);
#define FOFFSET(_b, _s, _f) \
((u_int32_t)&((_s *)0)->_f + (u_int32_t)_b)
int
fixup_lilo(int slice, int stype, u_int32_t start, u_int32_t size,
char *sname, int infd, int *found)
{
union bblock bblock;
union idescriptors dtab;
struct bb1 *bb;
struct image *ip;
u_int32_t s0, s1, s2, s4, poff;
int cc, i;
/*
* Check for compiler alignment errors
* LILO has some funky-sized structures (sectaddrs)
* that make me nervous...
*/
assert(sizeof(sectaddr_t) == SADDR_S_SIZE);
assert(sizeof(struct bb1) == BB1_S_SIZE);
assert(sizeof(union bblock) == BBLOCK_S_SIZE);
assert(sizeof(struct image) == IMAGE_S_SIZE);
assert(sizeof(struct idtab) == IDTAB_S_SIZE);
assert(sizeof(union idescriptors) == IDESC_S_SIZE);
assert(sizeof(union mapsect) == MSECT_S_SIZE);
if (devlseek(infd, sectobytes(start), SEEK_SET) < 0) {
warnx("Linux Slice %d: Could not seek to bootblock",
slice+1);
return 1;
}
if ((cc = devread(infd, &bblock, sizeof(bblock))) < 0) {
warn("Linux Slice %d: Could not read bootblock", slice+1);
return 1;
}
if (cc != sizeof(bblock)) {
warnx("Linux Slice %d: Truncated bootblock", slice+1);
return 1;
}
bb = &bblock.bb1;
if (strncmp(bb->sig, "LILO", 4) != 0) {
*found = 0;
return 0;
}
*found = 1;
/*
* Only ever tested with stage 1 boot loader
*/
if (bb->stage != 1) {
warnx("Linux Slice %d: no LILO relocs generated: "
"stage=%d, can only handle stage 1",
slice+1, bb->stage);
return 0;
}
/*
* According to docs, the following sections are all part of the
* map file, in contiguous order:
* default command line (1 sector)
* image descriptors (2 sectors)
* "zero" sector (1 sector)
* keyboard map (1 sector)
*/
s0 = getsector(&bb->cmdline);
s1 = getsector(&bb->idesc[0]);
s2 = getsector(&bb->idesc[1]);
s4 = getsector(&bb->keytab);
if (s1 != s0+1 || s2 != s0+2 || s4 != s0+4) {
warnx("Linux Slice %d: no LILO relocs generated: "
"map sectors out of order",
slice+1);
return 0;
}
partoff = start;
partsize = size;
/*
* Read the image descriptor table and checksum
*/
if (readatsector(infd, s1, &dtab, sizeof(dtab)) != 0)
return 1;
if (lilocksum(&dtab, LILO_CKSUM) != 0) {
warnx("Linux Slice %d: no LILO relocs generated: "
"bad checksum in descriptor table",
slice+1);
return 0;
}
/*
* Fixup bootblock sector addresses
*/
poff = 0;
fixup_sector(FOFFSET(poff, struct bb1, idesc[0]), &bb->idesc[0]);
fixup_sector(FOFFSET(poff, struct bb1, idesc[1]), &bb->idesc[1]);
fixup_sector(FOFFSET(poff, struct bb1, cmdline), &bb->cmdline);
fixup_sector(FOFFSET(poff, struct bb1, keytab), &bb->keytab);
if (bb->msglen > 0)
fixup_sector(FOFFSET(poff, struct bb1, msg), &bb->msg);
for (i = 0; i <= MAX_BOOT2_SECT; i++)
fixup_sector(FOFFSET(poff, struct bb1, boot2[i]),
&bb->boot2[i]);
/*
* Fixup the descriptor table
*/
poff = FOFFSET(sectobytes(s1-partoff), union idescriptors, idtab);
ip = dtab.idtab.images;
for (i = 0; i < MAX_IMAGE_DESC; i++) {
if (*ip->name == '\0')
break;
if (debug > 1)
fprintf(stderr, " LILO parse: found image %s\n",
ip->name);
if (*(u_int32_t *)ip->rdsize != 0) {
s0 = getsector(&ip->initrd);
fixup_sector(FOFFSET(poff, struct idtab,
images[i].initrd), &ip->initrd);
fixup_map(infd, s0);
}
s0 = getsector(&ip->start);
fixup_sector(FOFFSET(poff, struct idtab, images[i].start),
&ip->start);
fixup_map(infd, s0);
ip++;
}
/*
* Ensure that the checksum is recomputed
* XXX we schedule it after the last entry in the table to make
* sure it triggers after all the sectaddr relocs.
*/
poff += sizeof(struct idtab);
addfixup(sectobytes(partoff)+poff, sectobytes(partoff),
(off_t)2, &dtab.idtab.images[MAX_IMAGE_DESC],
RELOC_LILOCKSUM);
return 0;
}
static int
readatsector(int fd, u_int32_t sect, void *buf, int size)
{
assert(sect >= partoff);
assert(sect+bytestosec(size) <= partoff+partsize);
if (devlseek(fd, sectobytes(sect), SEEK_SET) < 0) {
perror("LILO parse: sector seek");
return 1;
}
if (devread(fd, buf, size) != size) {
perror("LILO parse: sector read");
return 1;
}
return 0;
}
/*
* Create a fixup entry for a LILO sector address
* poff is the offset of the address field from the beginning of the partition
*/
static void
fixup_sector(u_int32_t poff, sectaddr_t *sect)
{
u_int32_t sector;
sectaddr_t nsect;
off_t boff;
sector = getsector(sect);
if (sector == 0)
return;
assert(sector >= partoff);
assert(sector < partoff + partsize);
if (debug > 1)
fprintf(stderr, " LILO parse: "
"fixup sectaddr at poff %d: %d -> %d\n",
poff, sector, sector-partoff);
putsector(&nsect, sector-partoff, sect->device, sect->nsect);
boff = sectobytes(partoff);
addfixup(boff+poff, boff, (off_t)sizeof(nsect), &nsect,
RELOC_LILOSADDR);
}
static void
fixup_map(int fd, u_int32_t startsect)
{
union mapsect mapsect;
u_int32_t addr = 0, poff;
int i, mapsectno = 0;
off_t boff;
boff = sectobytes(partoff);
while (startsect != 0) {
if (readatsector(fd, startsect, &mapsect, sizeof(mapsect)) != 0)
exit(1);
poff = sectobytes(startsect - partoff);
for (i = 0; i <= MAX_MAP_SECT; i++) {
addr = getsector(&mapsect.addr[i]);
if (addr == 0)
break;
if (debug > 2)
fprintf(stderr, " LILO parse: "
"fixup map sector %d/%d: %d -> %d\n",
mapsectno, i, addr, addr-partoff);
putsector(&mapsect.addr[i], addr-partoff,
mapsect.addr[i].device, mapsect.addr[i].nsect);
}
if (debug > 1)
fprintf(stderr, " LILO parse: "
"fixup map sector at poff %d, %d sectaddrs\n",
poff, i);
/*
* Either we broke out because we hit the end (addr == 0)
* or we have finished the sector. If the latter, we use
* the final sectaddr to locate the next sector of the map.
* In either case we create a fixup for the sector we just
* completed.
*/
addfixup(boff+poff, boff, (off_t)sizeof(mapsect),
&mapsect, RELOC_LILOMAPSECT);
startsect = addr;
mapsectno++;
}
}
/*
* EMULAB-COPYRIGHT
* Copyright (c) 2000-2003 University of Utah and the Flux Group.
* Copyright (c) 2000-2004 University of Utah and the Flux Group.
* All rights reserved.
*/
......@@ -146,7 +146,7 @@ read_bsdslice(int slice, int bsdtype, u_int32_t start, u_int32_t size,
* Record a fixup for the partition table, adjusting the
* partition offsets to make them slice relative.
*/
if (slicemode &&
if (dorelocs &&
start != 0 && dlabel.label.d_partitions[0].p_offset == start) {
for (i = 0; i < npart; i++) {
if (dlabel.label.d_partitions[i].p_size == 0)
......@@ -166,7 +166,9 @@ read_bsdslice(int slice, int bsdtype, u_int32_t start, u_int32_t size,
}
dlabel.label.d_checksum = 0;
dlabel.label.d_checksum = dkcksum(&dlabel.label);
addfixup(sectobytes(start+LABELSECTOR), sectobytes(start),
addfixup(sectobytes(start+LABELSECTOR),
sectobytes(start),
(off_t)sizeof(dlabel.label), &dlabel,
bsdtype == DOSPTYP_OPENBSD ?
RELOC_OBSDDISKLABEL : RELOC_FBSDDISKLABEL);
......
/*
* EMULAB-COPYRIGHT
* Copyright (c) 2000-2003 University of Utah and the Flux Group.
* Copyright (c) 2000-2004 University of Utah and the Flux Group.
* All rights reserved.
*/
......@@ -10,6 +10,7 @@
extern int debug;
extern int secsize;
extern int slicemode;
extern int dorelocs;
extern char *slicename(int slice, u_int32_t offset, u_int32_t size, int type);
extern off_t devlseek(int fd, off_t off, int whence);
......
/*
* EMULAB-COPYRIGHT
* Copyright (c) 2000-2003 University of Utah and the Flux Group.
* Copyright (c) 2000-2004 University of Utah and the Flux Group.
* All rights reserved.
*/
......@@ -28,6 +28,8 @@ static int infd = -1;
static unsigned long long wasted;
static uint32_t sectinuse;
static uint32_t sectfree;
static uint32_t relocs;
static unsigned long long relocbytes;
static void usage(void);
static void dumpfile(char *name, int fd);
......@@ -242,8 +244,12 @@ dumpfile(char *name, int fd)
if (detail > 0)
printf("\n");
printf(" %qu bytes of overhead/wasted space (%5.2f%% of image file)\n",
wasted, (double)wasted / filesize * 100);
if (relocs)
printf(" %d relocations covering %qu bytes\n",
relocs, relocbytes);
printf(" %qu bytes of compressed data\n",
cbytes);
printf(" %5.2fx compression of allocated data (%qu bytes)\n",
......@@ -289,6 +295,7 @@ dumpchunk(char *name, char *buf, int chunkno, int checkindex)
reg = (struct region *)((struct blockhdr_V1 *)hdr + 1);
break;
case COMPRESSED_V2:
case COMPRESSED_V3:
reg = (struct region *)((struct blockhdr_V2 *)hdr + 1);
break;
default:
......@@ -421,6 +428,7 @@ dumpchunk(char *name, char *buf, int chunkno, int checkindex)
reg->start, reg->start + reg->size - 1);
break;
case COMPRESSED_V2:
case COMPRESSED_V3:
if (i == 0 && hdr->firstsect < reg->start)
printf("F: [%08x-%08x]\n",
hdr->firstsect, reg->start-1);
......@@ -446,21 +454,43 @@ dumpchunk(char *name, char *buf, int chunkno, int checkindex)
return 0;
for (i = 0; i < hdr->reloccount; i++) {
struct blockreloc *reloc = (struct blockreloc *)reg;
uint32_t offset = SECTOBYTES(reloc->sector) + reloc->sectoff;
struct blockreloc *reloc = &((struct blockreloc *)reg)[i];
relocs++;
relocbytes += reloc->size;
if (reloc->sector < hdr->firstsect ||
reloc->sector >= hdr->lastsect)
printf(" WARNING: "
"Reloc %d at %u not in chunk [%u-%u]\n", i,
reloc->sector, hdr->firstsect, hdr->lastsect-1);
if (detail > 1)
printf(" Reloc %d: %s [%u-%u] (sector %d)\n", i,
reloc->type == RELOC_FBSDDISKLABEL ?
"FBSDDISKLABEL" :