Commit b8d79ac6 authored by Ryan Jackson's avatar Ryan Jackson

Merge imagezip ext4 code into extfs code

Merge the ext4 support for imagezip into the extfs code.  There's no
real reason to keep it separate, since ext4 is backward-compatible
with ext3 and ext2.

All of the macro definitions in extfs.c were changed from EXT2_* or
EXT3_* to EXT4_*.  The primary reason for this is that while ext4 is
backward compatible, the data structures have been extended and some
of the definitions needed to change to handle them.  Some things that
were constant before (block group descriptor sizes, for example) are
now dynamic and must be calculated from other fields in the
superblock.

Since imagezip and frisbee don't support 64-bit block numbers, ext4
filesystems that are larger than 2 TB (assuming 512-byte sectors) are
not supported.  These can be detected by examining the
feature_incompat field of the superblock.  Imagezip will fail if the
user tries to compress one of these filesystems.
parent 50d691eb
......@@ -135,13 +135,6 @@ SUBDIRS += extfs
ZIPLIBS += extfs/libextfs.a
endif
# EXT4
ifeq ($(WITH_EXT4FS),1)
CFLAGS += -DWITH_EXT4FS
SUBDIRS += ext4fs
ZIPLIBS += ext4fs/libext4fs.a
endif
# with NTFS
ifeq ($(WITH_NTFS),1)
ifndef HAVE_GCC3
......
......@@ -19,9 +19,8 @@ WITH_CRYPTO = 1
WITH_EXTFS = 1
WITH_FFS = 1
WITH_FAT = 1
WITH_NTFS = 1
WITH_NTFS = 0
WITH_HASH = 0
WITH_EXT4FS = 0
WITH_V3COMPAT = 1
......@@ -48,11 +47,6 @@ FSOBJS += extfs/extfs.o extfs/reloc_lilo.o
CFLAGS += -DWITH_EXTFS -Iextfs
endif
ifeq ($(WITH_EXT4FS),1)
FSOBJS += ext4fs/ext4fs.o ext4fs/reloc_lilo.o
CFLAGS += -DWITH_EXT4FS -Iext4fs
endif
ifeq ($(WITH_FFS),1)
FSOBJS += ffs/ffs.o
CFLAGS += -DWITH_FFS -Iffs
......
......@@ -17,7 +17,6 @@ WITH_EXTFS = 1
WITH_FFS = 1
WITH_FAT = 0
WITH_NTFS = 0 # needs work
WITH_EXT4FS = 0
.if defined(NOTHREADS)
PTHREADCFLAGS = -DNOTHREADS
......@@ -39,12 +38,6 @@ FSOBJS += extfs.o reloc_lilo.o
CFLAGS += -DWITH_EXTFS -Iextfs
.endif
.ifdef WITH_EXT4FS
FSDIRS += ext4fs
FSOBJS += ext4fs.o reloc_lilo.o
CFLAGS += -DWITH_EXT4FS -Iext4fs
.endif
.ifdef WITH_FFS
FSDIRS += ffs
FSOBJS += ffs.o
......
#
# EMULAB-COPYRIGHT
# Copyright (c) 2000-2004 University of Utah and the Flux Group.
# All rights reserved.
#
SRCDIR = @srcdir@
TESTBED_SRCDIR = @top_srcdir@
OBJDIR = ../../..
SUBDIR = os/imagezip/ext4fs
MAINDIR = $(SRCDIR)/..
include $(OBJDIR)/Makeconf
CFLAGS += $(SUBDIRCFLAGS) -I$(MAINDIR) -I$(SRCDIR)
all: libext4fs.a
include $(TESTBED_SRCDIR)/GNUmakerules
OBJS = ext4fs.o reloc_lilo.o
ext4fs.o: $(MAINDIR)/sliceinfo.h $(MAINDIR)/global.h
reloc_lilo.o: $(MAINDIR)/sliceinfo.h $(MAINDIR)/global.h $(MAINDIR)/imagehdr.h
libext4fs.a: $(OBJS)
$(AR) $(ARFLAGS) $@ $?
$(RANLIB) $@
install:
clean:
rm -f libext4fs.a $(OBJS)
This diff is collapsed.
/*
* EMULAB-COPYRIGHT
* Copyright (c) 2000-2004 University of Utah and the Flux Group.
* All rights reserved.
*/
#include <stdio.h>
#include <err.h>
#include <assert.h>
#include <sys/param.h>
#include <ext4_fs.h>
#include "sliceinfo.h"
#include "global.h"
static int read_ext4group(struct ext4_super_block *super,
struct ext4_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
* has the boot block in the first sector, part of the boot in the
* second sector, and then the superblock for the one big filesystem
* in the 3rd sector. Just start there and move on.
*
* Unlike BSD partitions, linux block offsets are from the start of the
* slice, so we have to add the starting sector to everything.
*/
int
read_ext4slice(int slice, int stype, u_int32_t start, u_int32_t size,
char *sname, int infd)
{
#define LINUX_SUPERBLOCK_OFFSET 1024
#define LINUX_SUPERBLOCK_SIZE 1024
#define LINUX_MAXGRPSPERBLK \
(EXT4_MAX_BLOCK_SIZE/sizeof(struct ext4_group_desc))
int cc, i, numgroups, rval = 0;
struct ext4_super_block fs;
struct ext4_group_desc groups[LINUX_MAXGRPSPERBLK];
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) & ~EXT4_MAX_BLOCK_SIZE) == 0);
if (debug)
fprintf(stderr, " P%d %s Linux Slice)\n",
dosslice, rval ? "(Bootable" : "(");
/*
* Skip ahead to the superblock.
*/
if (devlseek(infd, sectobytes(start) + LINUX_SUPERBLOCK_OFFSET,
SEEK_SET) < 0) {
warnx("Linux Slice %d: Could not seek to superblock",
dosslice);
return 1;
}
if ((cc = devread(infd, &fs, LINUX_SUPERBLOCK_SIZE)) < 0) {
warn("Linux Slice %d: Could not read superblock", dosslice);
return 1;
}
if (cc != LINUX_SUPERBLOCK_SIZE) {
warnx("Linux Slice %d: Truncated superblock", dosslice);
return 1;
}
if (fs.s_magic != EXT4_SUPER_MAGIC) {
warnx("Linux Slice %d: Bad magic number in superblock",
dosslice);
return (1);
}
if (EXT4_BLOCK_SIZE(&fs) < EXT4_MIN_BLOCK_SIZE ||
EXT4_BLOCK_SIZE(&fs) > EXT4_MAX_BLOCK_SIZE) {
warnx("Linux Slice %d: Block size not what I expect it to be: %d!",
dosslice, EXT4_BLOCK_SIZE(&fs));
return 1;
}
numgroups = ((fs.s_blocks_count - fs.s_first_data_block)
+ (fs.s_blocks_per_group - 1))
/ fs.s_blocks_per_group;
if (debug) {
fprintf(stderr, " %s\n",
(fs.s_feature_compat & 4) ? "EXT3" : "EXT2");
fprintf(stderr, " count %9u, size %9d, pergroup %9d\n",
fs.s_blocks_count, EXT4_BLOCK_SIZE(&fs),
fs.s_blocks_per_group);
fprintf(stderr, " bfree %9u, first %9u, groups %9d\n",
fs.s_free_blocks_count, fs.s_first_data_block,
numgroups);
}
/*
* Read each group descriptor. It says where the free block bitmap
* lives. The absolute block numbers a group descriptor refers to
* is determined by its index * s_blocks_per_group. Once we know where
* the bitmap lives, we can go out to the bitmap and see what blocks
* are free.
*
* Group descriptors are in the blocks right after the superblock.
*/
assert(LINUX_SUPERBLOCK_SIZE <= EXT4_BLOCK_SIZE(&fs));
assert(EXT4_DESC_PER_BLOCK(&fs) * sizeof(struct ext4_group_desc)
== EXT4_BLOCK_SIZE(&fs));
soff = sectobytes(start) +
(fs.s_first_data_block + 1) * EXT4_BLOCK_SIZE(&fs);
rval = 0;
for (i = 0; i < numgroups; i++) {
int gix;
/*
* Read the group descriptors in groups since they are
* smaller than a sector size, packed into EXT2_BLOCK_SIZE
* blocks right after the superblock.
*/
gix = (i % EXT4_DESC_PER_BLOCK(&fs));
if (gix == 0) {
if (devlseek(infd, soff, SEEK_SET) < 0) {
warnx("Linux Slice %d: "
"Could not seek to Group %d",
dosslice, i);
return 1;
}
if ((cc = devread(infd, groups, sizeof(groups))) < 0) {
warn("Linux Slice %d: "
"Could not read Group %d",
dosslice, i);
return 1;
}
if (cc != sizeof(groups)) {
warnx("Linux Slice %d: "
"Truncated Group %d", dosslice, i);
return 1;
}
soff += EXT4_BLOCK_SIZE(&fs);
}
if (debug) {
fprintf(stderr,
" Group:%-2d\tBitmap %9u, bfree %9d\n",
i, groups[gix].bg_block_bitmap,
groups[gix].bg_free_blocks_count);
}
if ((rval = read_ext4group(&fs, &groups[gix], i, start, infd)))
return rval;
}
return 0;
}
/*
* A group descriptor says where on the disk the block bitmap is. Its
* a 1bit per block map, where each bit is a FS block (instead of a
* fragment like in BSD). Since linux offsets are relative to the start
* of the slice, need to adjust the numbers using the slice offset.
*/
static int
read_ext4group(struct ext4_super_block *super,
struct ext4_group_desc *group,
int index,
u_int32_t sliceoffset /* Sector offset of slice */,
int infd)
{
char *p, bitmap[EXT4_MAX_BLOCK_SIZE];
int i, cc, max;
int count, j, freecount;
off_t offset;
unsigned long block;
block = super->s_first_data_block +
(index * super->s_blocks_per_group);
/*
* Sanity check the bitmap block numbers
*/
if (group->bg_block_bitmap < block ||
group->bg_block_bitmap >= block + super->s_blocks_per_group) {
warnx("Linux Group %d: "
"Group bitmap block (%d) out of range [%lu-%lu]",
index, group->bg_block_bitmap,
block, block + super->s_blocks_per_group - 1);
return 1;
}
offset = sectobytes(sliceoffset);
offset += (off_t)EXT4_BLOCK_SIZE(super) * group->bg_block_bitmap;
if (devlseek(infd, offset, SEEK_SET) < 0) {
warn("Linux Group %d: "
"Could not seek to Group bitmap block %d",
index, group->bg_block_bitmap);
return 1;
}
/*
* Sanity check this number since it the number of blocks in
* the group (bitmap size) is dependent on the block size.
*/
if (super->s_blocks_per_group > (EXT4_BLOCK_SIZE(super) * 8)) {
warnx("Linux Group %d: "
"Block count not what I expect it to be: %d!",
index, super->s_blocks_per_group);
return 1;
}
if ((cc = devread(infd, bitmap, EXT4_BLOCK_SIZE(super))) < 0) {
warn("Linux Group %d: "
"Could not read Group bitmap", index);
return 1;
}
if (cc != EXT4_BLOCK_SIZE(super)) {
warnx("Linux Group %d: Truncated Group bitmap", index);
return 1;
}
/*
* The final group may have fewer than s_blocks_per_group
*/
max = super->s_blocks_count - block;
if (max > super->s_blocks_per_group)
max = super->s_blocks_per_group;
else if (debug && max != super->s_blocks_per_group)
fprintf(stderr,
" Linux Group %d: only %d blocks\n", index, max);
p = bitmap;
freecount = 0;
/*
* XXX The bitmap is FS blocks.
* The bitmap is an "inuse" map, not a free map.
*/
#define LINUX_FSBTODB(count) \
((EXT4_BLOCK_SIZE(super) / secsize) * (count))
if (debug > 2)
fprintf(stderr, " ");
for (freecount = count = i = 0; i < max; i++)
if (!isset(p, i)) {
unsigned long dboff;
int dbcount;
j = i;
while ((i+1)<max && !isset(p, i+1))
i++;
/*
* The offset of this free range, relative
* to the start of the disk, is: the slice
* offset, plus the offset of the group itself,
* plus the index of the first free block in
* the current range, plus the offset of the
* first data block in the filesystem.
*/
dboff = sliceoffset +
LINUX_FSBTODB(index*super->s_blocks_per_group) +
LINUX_FSBTODB(j) +
LINUX_FSBTODB(super->s_first_data_block);
dbcount = LINUX_FSBTODB((i-j) + 1);
if (debug > 2) {
if (count)
fprintf(stderr, ",%s",
count % 4 ?
" " : "\n ");
fprintf(stderr, "%lu:%d %d:%d",
dboff, dbcount, j, i);
count++;
}
addskip(dboff, dbcount);
freecount += dbcount;
}
if (debug > 2)
fprintf(stderr, "\n");
if (freecount != LINUX_FSBTODB(group->bg_free_blocks_count)) {
warnx("Linux Group %d: "
"computed free count (%d) != expected free count (%d)",
index, freecount, group->bg_free_blocks_count);
}
return 0;
}
/*
* For a linux swap partition, all that matters is the first little
* bit of it. The rest of it does not need to be written to disk.
*/
int
read_linuxswap(int slice, int stype, u_int32_t start, u_int32_t size,
char *sname, int infd)
{
if (debug) {
fprintf(stderr,
" P%d (Linux Swap)\n", slice + 1 /* DOS Numbering */);
fprintf(stderr,
" start %12d, size %9d\n",
start, size);
}
start += bytestosec(0x8000);
size -= bytestosec(0x8000);
addskip(start, size);
return 0;
}
/*
* 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;
}
#ifndef _LINUX_TYPES_H
#define _LINUX_TYPES_H
#include <sys/types.h>
typedef u_int8_t __u8;
typedef u_int16_t __u16;
typedef u_int32_t __u32;
typedef u_int64_t __u64;
typedef int8_t __s8;
typedef int16_t __s16;
typedef int32_t __s32;
typedef int64_t __s64;
#endif /* _LINUX_TYPES_H */
/*
* EMULAB-COPYRIGHT
* Copyright (c) 2000-2004, 2009 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)(long)&((_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;
}
/*