diff -burN grub-0.94/ChangeLog grub-0.94-lvm/ChangeLog --- grub-0.94/ChangeLog 2004-01-25 09:41:08.000000000 -0600 +++ grub-0.94-lvm/ChangeLog 2004-02-21 20:24:01.841074104 -0600 @@ -538,6 +538,23 @@ * stage2/stage2.c (run_menu): Reverse if (!) to if () for uniformitty. +2002-08-09 Jörg Walter + + * stage2/linuxlvm.c: New file, implements full Linux-LVM + support + * stage2/builtins.c: Added support for LVM disk syntax + * stage2/disk_io.c: Added glue code for Linux-LVM + * lib/device.c: Added LVM support to write_to_partition + * stage2/char_io.c: Added grub_strncpy and grub_strncmp, + made functions needed by LVM available in Stage 1.5 + * stage2/shared.h: Added LVM prototypes/defines, added + two prototyes for char_io.c + * stage2/Makefile.am: Added linuxlvm.c, disable Linux-LVM + for ffs_stage1.5 (to keep size low for bootloader area) + * configure.in: Added options to disable LVM in Stage 1.5 + or completely; fixed alignment compiler options + * docs/grub.texi: Added explanation of LVM syntax + 2002-07-12 Yoshinori K. Okuji * stage2/boot.c (load_image): Rewrite the Linux booting support diff -burN grub-0.94/config.h.in grub-0.94-lvm/config.h.in --- grub-0.94/config.h.in 2003-10-19 12:27:16.000000000 -0500 +++ grub-0.94-lvm/config.h.in 2004-02-21 20:24:01.842073952 -0600 @@ -47,6 +47,9 @@ /* Define to 1 if you have the header file. */ #undef HAVE_CURSES_H +/* Define to 1 if you have the header file. */ +#undef HAVE_DLFCN_H + /* Define to 1 if you have the header file. */ #undef HAVE_INTTYPES_H @@ -83,6 +86,12 @@ /* Define to 1 if you have the header file. */ #undef HAVE_UNISTD_H +/* Define if LVM support should be omitted completely */ +#undef NO_LVM + +/* Define if LVM support should be omitted in Stage 1.5 */ +#undef NO_LVM_IN_STAGE1_5 + /* Name of package */ #undef PACKAGE diff -burN grub-0.94/configure.ac grub-0.94-lvm/configure.ac --- grub-0.94/configure.ac 2004-02-21 18:29:57.244610928 -0600 +++ grub-0.94-lvm/configure.ac 2004-02-21 20:24:01.843073800 -0600 @@ -205,6 +205,20 @@ # Check for user options. +AC_ARG_ENABLE(lvm15, + [ --disable-lvm15 disable Linux-LVM support in Stage 1.5], + if test "$enable_lvm15" = "no"; then + AC_DEFINE(NO_LVM_IN_STAGE1_5,1,[define if LVM support should be omitted in Stage 1.5]) + fi +) + +AC_ARG_ENABLE(lvm, + [ --disable-lvm disable Linux-LVM support completely], + if test "$enable_lvm" = "no"; then + AC_DEFINE(NO_LVM,1,[define if LVM support should be omitted completely]) + fi +) + # filesystems support. AC_ARG_ENABLE(ext2fs, [ --disable-ext2fs disable ext2fs support in Stage 2]) diff -burN grub-0.94/docs/grub.texi grub-0.94-lvm/docs/grub.texi --- grub-0.94/docs/grub.texi 2004-01-25 09:39:14.000000000 -0600 +++ grub-0.94-lvm/docs/grub.texi 2004-02-21 20:24:01.890066656 -0600 @@ -416,6 +416,14 @@ GRUB searches for the first @sc{pc} slice which has a BSD @samp{a} partition. +@example + +@end example + +This means the Linux-LVM logical volume named @samp{boot} on the volume +group named @samp{system}. Number, size and structure of +VGs and LVs is not important. + Of course, to actually access the disks or partitions with GRUB, you need to use the device specification in a command, like @samp{root (fd0)} or @samp{unhide (hd0,2)}. To help you find out which number @@ -1617,6 +1625,20 @@ also available. Before using the network drive, you must initialize the network. @xref{Network}, for more information. +To access Linux-LVM logical volumes, use a syntax like this: + +@example + +@end example + +This means the Linux-LVM logical volume named @samp{boot} on the volume +group named @samp{system}. GRUB scans all available harddisks for volume +groups when you first use the LVM syntax. Number, size and structure of +VGs and LVs is not important. In practice, you might get out-of-memory +errors if you have more than 256 GB of allocated logical volumes +(assuming the default PE size of 4MB). In that case, all LVs beyond this +limit are not accessible. Currently, a limit of 15 VGs exists, but this +can be raised considerably via compile-time defines. @node File name syntax @section How to specify files diff -burN grub-0.94/lib/device.c grub-0.94-lvm/lib/device.c --- grub-0.94/lib/device.c 2004-01-17 09:57:57.000000000 -0600 +++ grub-0.94-lvm/lib/device.c 2004-02-21 20:24:01.890066656 -0600 @@ -710,6 +710,15 @@ char dev[PATH_MAX]; /* XXX */ int fd; +#ifndef NO_LVM + if (drive >= LVM_MIN_DRIVE && drive <= LVM_MAX_DRIVE) + { + grub_sprintf(dev, "/dev/%s/%s", lvm_vg_name(drive-LVM_MIN_DRIVE), + lvm_lv_name(drive-LVM_MIN_DRIVE, partition)); + } + else +#endif + { if ((partition & 0x00FF00) != 0x00FF00) { /* If the partition is a BSD partition, it is difficult to @@ -727,6 +736,7 @@ strcpy (dev + strlen(dev) - 5, "/part"); } sprintf (dev + strlen(dev), "%d", ((partition >> 16) & 0xFF) + 1); + } /* Open the partition. */ fd = open (dev, O_RDWR); diff -burN grub-0.94/stage2/builtins.c grub-0.94-lvm/stage2/builtins.c --- grub-0.94/stage2/builtins.c 2004-02-21 18:29:57.477575512 -0600 +++ grub-0.94-lvm/stage2/builtins.c 2004-02-21 20:24:01.937059512 -0600 @@ -1329,6 +1329,43 @@ errnum = ERR_NONE; } +#ifndef NO_LVM + /* LVM logical volumes. */ + for (drive = LVM_MIN_DRIVE; drive <= LVM_MAX_DRIVE; drive++) + { + unsigned long part = 0xFFFFFF; + unsigned long start, len, offset, ext_offset; + int type, entry; + char buf[SECTOR_SIZE]; + + current_drive = drive; + while (next_partition (drive, 0xFFFFFF, &part, &type, + &start, &len, &offset, &entry, + &ext_offset, buf)) + { + current_partition = part; + if (open_device ()) + { + saved_drive = current_drive; + saved_partition = current_partition; + if (grub_open (filename)) + { + grub_close (); + grub_printf (" <%s:%s>\n", lvm_vg_name(drive-LVM_MIN_DRIVE), lvm_lv_name(drive-LVM_MIN_DRIVE, part)); + got_file = 1; + } + } + + /* We want to ignore any error here. */ + errnum = ERR_NONE; + } + + /* next_partition always sets ERRNUM in the last call, so clear + it. */ + errnum = ERR_NONE; + } +#endif + /* Hard disks. */ for (drive = 0x80; drive < 0x88; drive++) { @@ -3182,6 +3219,13 @@ /* Network drive. */ grub_printf (" (nd):"); } +#ifndef NO_LVM + else if (saved_drive >= LVM_MIN_DRIVE && saved_drive <= LVM_MAX_DRIVE) + { + grub_printf (" <%s:%s>:", lvm_vg_name(saved_drive-LVM_MIN_DRIVE), + lvm_lv_name(saved_drive-LVM_MIN_DRIVE, saved_partition)); + } +#endif else if (saved_drive & 0x80) { /* Hard disk drive. */ @@ -3828,6 +3872,15 @@ /* Construct a device name in DEVICE. */ void sprint_device (int drive, int partition) { + +#ifndef NO_LVM + if (drive >= LVM_MIN_DRIVE && drive <= LVM_MAX_DRIVE) + { + grub_sprintf (device, "<%s:%s>", lvm_vg_name(drive-LVM_MIN_DRIVE), + lvm_lv_name(drive-LVM_MIN_DRIVE, partition)); + return; + } +#endif grub_sprintf (device, "(%cd%d", (drive & 0x80) ? 'h' : 'f', drive & ~0x80); diff -burN grub-0.94/stage2/char_io.c grub-0.94-lvm/stage2/char_io.c --- grub-0.94/stage2/char_io.c 2004-02-21 18:29:57.489573688 -0600 +++ grub-0.94-lvm/stage2/char_io.c 2004-02-21 20:24:01.938059360 -0600 @@ -991,11 +991,16 @@ a static library supporting minimal standard C functions and link each image with the library. Complicated things should be left to computer, definitely. -okuji */ -#if !defined(STAGE1_5) || defined(FSYS_VSTAFS) +#if !defined(NO_LVM) || !defined(STAGE1_5) || defined(FSYS_VSTAFS) int grub_strcmp (const char *s1, const char *s2) { - while (*s1 || *s2) + return grub_strncmp(s1,s2,1<<30); +} +int +grub_strncmp (const char *s1, const char *s2, int n) +{ + while ((*s1 || *s2) && n > 0) { if (*s1 < *s2) return -1; @@ -1003,6 +1008,7 @@ return 1; s1 ++; s2 ++; + n--; } return 0; @@ -1183,6 +1189,8 @@ return 0; } +#endif /* ! STAGE1_5 */ + int grub_strlen (const char *str) { @@ -1193,7 +1201,6 @@ return len; } -#endif /* ! STAGE1_5 */ int memcheck (int addr, int len) @@ -1295,7 +1302,16 @@ return errnum ? NULL : start; } -#ifndef STAGE1_5 +#if !defined(STAGE1_5) || !defined(NO_LVM) +char * +grub_strncpy (char *dest, const char *src, int maxlen) +{ + int len = 0; + while (src[len] && len < maxlen) len++; + grub_memmove (dest, src, len); + return dest; +} + char * grub_strcpy (char *dest, const char *src) { diff -burN grub-0.94/stage2/disk_io.c grub-0.94-lvm/stage2/disk_io.c --- grub-0.94/stage2/disk_io.c 2003-10-19 10:58:03.000000000 -0500 +++ grub-0.94-lvm/stage2/disk_io.c 2004-02-21 20:24:01.939059208 -0600 @@ -38,9 +38,9 @@ #ifndef STAGE1_5 int print_possibilities; -static int do_completion; -static int unique; -static char *unique_string; +int do_completion; +int unique; +char *unique_string; #endif @@ -129,6 +129,11 @@ if (byte_len <= 0) return 1; +#ifndef NO_LVM + if (drive >= LVM_MIN_DRIVE && drive <= LVM_MAX_DRIVE) + return lvm_rawread(drive-LVM_MIN_DRIVE, sector, byte_offset, byte_len, buf); +#endif + while (byte_len > 0 && !errnum) { int soff, num_sect, bufaddr, track, size = byte_len; @@ -304,6 +309,13 @@ int rawwrite (int drive, int sector, char *buf) { +#ifndef NO_LVM + if (drive >= LVM_MIN_DRIVE && drive <= LVM_MAX_DRIVE) { + errnum = ERR_WRITE; + return 1; + } +#endif + if (sector == 0) { if (biosdisk (BIOSDISK_READ, drive, &buf_geom, 0, 1, SCRATCHSEG)) @@ -366,6 +378,11 @@ static int sane_partition (void) { +#ifndef NO_LVM + if (current_drive >= LVM_MIN_DRIVE && current_drive <= LVM_MAX_DRIVE) + return 1; +#endif + /* network drive */ if (current_drive == NETWORK_DRIVE) return 1; @@ -682,6 +699,11 @@ /* Start the body of this function. */ +#ifndef NO_LVM + if (drive >= LVM_MIN_DRIVE && drive <= LVM_MAX_DRIVE) + return lvm_next_partition(drive-LVM_MIN_DRIVE,dest,partition,type,start,len,offset,entry,ext_offset,buf); +#endif + #ifndef STAGE1_5 if (current_drive == NETWORK_DRIVE) return 0; @@ -742,6 +764,15 @@ return ret; } +#ifndef NO_LVM + if (current_drive >= LVM_MIN_DRIVE && current_drive <= LVM_MAX_DRIVE) { + int rc = 1; + current_partition = 0xffffff; + while (current_partition != dest_partition && rc == 1) rc = next(); + return rc; + } +#endif + #ifndef STAGE1_5 /* network drive */ if (current_drive == NETWORK_DRIVE) @@ -899,8 +930,8 @@ #ifndef STAGE1_5 /* XX used for device completion in 'set_device' and 'print_completions' */ -static int incomplete, disk_choice; -static enum +int incomplete, disk_choice; +enum { PART_UNSPECIFIED = 0, PART_DISK, @@ -939,6 +970,10 @@ current_drive = saved_drive; current_partition = 0xFFFFFF; +#ifndef NO_LVM + if (*device == '<') return lvm_set_device(device+1); +#endif + if (*device == '(' && !*(device + 1)) /* user has given '(' only, let disk_choice handle what disks we have */ return device + 1; @@ -1090,6 +1125,11 @@ { int i, j; +#ifndef NO_LVM + if (saved_drive >= LVM_MIN_DRIVE && saved_drive <= LVM_MAX_DRIVE) + return 0; +#endif + /* Copy the boot partition information to 0x7be-0x7fd for chain-loading. */ if ((saved_drive & 0x80) && cur_part_addr) { @@ -1158,7 +1198,7 @@ #else /* ! STAGE1_5 */ - if (*filename == '(') + if (*filename == '(' || *filename == '<') { if ((filename = set_device (filename)) == 0) { @@ -1222,6 +1262,11 @@ else printf ("unknown, "); +#ifndef NO_LVM + if (current_drive >= LVM_MIN_DRIVE && current_drive <= LVM_MAX_DRIVE) + printf("on LVM logical volume\n"); + else +#endif if (current_partition == 0xFFFFFF) printf ("using whole disk\n"); else @@ -1326,6 +1371,11 @@ { errnum = 0; +#ifndef NO_LVM + if (*buf == '<' && (incomplete || ! *ptr)) + return lvm_print_completions(buf, ptr, is_completion); +#endif + if (*buf == '(' && (incomplete || ! *ptr)) { if (! part_choice) diff -burN grub-0.94/stage2/linuxlvm.c grub-0.94-lvm/stage2/linuxlvm.c --- grub-0.94/stage2/linuxlvm.c 1969-12-31 18:00:00.000000000 -0600 +++ grub-0.94-lvm/stage2/linuxlvm.c 2004-02-21 20:24:18.469546192 -0600 @@ -0,0 +1,654 @@ +/* + * GRUB -- GRand Unified Bootloader + * Copyright (C) 2002 Jörg Walter + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +/* this file written by Jörg Walter except where + stated otherwise + */ +/* TODO: + - handle endianness issues - x86 only (no problem at the moment) + - use more memory efficient mapping algorithm +*/ + +#include +#ifndef NO_LVM +#include +#define PART_CHOSEN 2 + +/* from lvm.h and liblvm.h */ + +#define LVM_STRUCT_VERSION 1 + +#ifndef uint8_t +# define uint8_t unsigned char +#endif +#ifndef uint16_t +# define uint16_t unsigned short int +#endif +#ifndef uint32_t +# define uint32_t unsigned int +#endif +#ifndef uint64_t +# define uint64_t unsigned long long int +#endif + +#define NAME_LEN 128 +#define UUID_LEN 32 + +typedef struct { + uint16_t lv_num; + uint16_t le_num; +} pe_disk_t; + +typedef struct { + uint32_t base; + uint32_t size; +} lvm_disk_data_t; + +typedef struct pv_disk_v2 { + uint8_t id[2]; + uint16_t version; + lvm_disk_data_t pv_on_disk; + lvm_disk_data_t vg_on_disk; + lvm_disk_data_t pv_uuidlist_on_disk; + lvm_disk_data_t lv_on_disk; + lvm_disk_data_t pe_on_disk; + uint8_t pv_uuid[NAME_LEN]; + uint8_t vg_name[NAME_LEN]; + uint8_t system_id[NAME_LEN]; + uint32_t pv_major; + uint32_t pv_number; + uint32_t pv_status; + uint32_t pv_allocatable; + uint32_t pv_size; + uint32_t lv_cur; + uint32_t pe_size; + uint32_t pe_total; + uint32_t pe_allocated; + uint32_t pe_start; + +} pv_disk_t; + +typedef struct lv_disk_v3 { + uint8_t lv_name[NAME_LEN]; + uint8_t vg_name[NAME_LEN]; + uint32_t lv_access; + uint32_t lv_status; + uint32_t lv_open; + uint32_t lv_dev; + uint32_t lv_number; + uint32_t lv_mirror_copies; + uint32_t lv_recovery; + uint32_t lv_schedule; + uint32_t lv_size; + uint32_t lv_snapshot_minor; + uint16_t lv_chunk_size; + uint16_t dummy; + uint32_t lv_allocated_le; + uint32_t lv_stripes; + uint32_t lv_stripesize; + uint32_t lv_badblock; + uint32_t lv_allocation; + uint32_t lv_io_timeout; + uint32_t lv_read_ahead; +} lv_disk_t; + +typedef struct vg_disk_v2 { + uint8_t vg_uuid[UUID_LEN]; + uint8_t vg_name_dummy[NAME_LEN-UUID_LEN]; + uint32_t vg_number; + uint32_t vg_access; + uint32_t vg_status; + uint32_t lv_max; + uint32_t lv_cur; + uint32_t lv_open; + uint32_t pv_max; + uint32_t pv_cur; + uint32_t pv_act; + uint32_t dummy; + uint32_t vgda; + uint32_t pe_size; + uint32_t pe_total; + uint32_t pe_allocated; + uint32_t pvg_total; +} vg_disk_t; + + +#define VG_ACTIVE 0x01 +#define VG_EXPORTED 0x02 +#define VG_EXTENDABLE 0x04 + +#define VG_READ 0x01 +#define VG_WRITE 0x02 +#define VG_CLUSTERED 0x04 +#define VG_SHARED 0x08 + +#define LV_ACTIVE 0x01 +#define LV_SPINDOWN 0x02 + +#define LV_READ 0x01 +#define LV_WRITE 0x02 +#define LV_SNAPSHOT 0x04 +#define LV_SNAPSHOT_ORG 0x08 + +#define LV_BADBLOCK_ON 0x01 + +#define LV_STRICT 0x01 +#define LV_CONTIGUOUS 0x02 + +#define PV_ACTIVE 0x01 +#define PV_ALLOCATABLE 0x02 + + +#define EXPORTED "PV_EXP" +#define LVM_ID "HM" + +#if 0 +/* use this when adding support for big endian machines */ +#if __BYTE_ORDER == __BIG_ENDIAN +#define LVM_TO_CORE16(x) ( \ + ((uint16_t)((((uint16_t)(x) & 0x00FFU) << 8) | \ + (((uint16_t)(x) & 0xFF00U) >> 8)))) +#define LVM_TO_CORE32(x) ( \ + ((uint32_t)((((uint32_t)(x) & 0x000000FFU) << 24) | \ + (((uint32_t)(x) & 0x0000FF00U) << 8) | \ + (((uint32_t)(x) & 0x00FF0000U) >> 8) | \ + (((uint32_t)(x) & 0xFF000000U) >> 24)))) +#elif __BYTE_ORDER == __LITTLE_ENDIAN +#define LVM_TO_CORE16(x) x +#define LVM_TO_CORE32(x) x +#else +#error "__BYTE_ORDER must be defined as __LITTLE_ENDIAN or __BIG_ENDIAN" +#endif +#endif + +/* end of external headers */ + +#define MAX_VGS (LVM_MAX_DRIVE-LVM_MIN_DRIVE+1) + +static unsigned long linalloc_topaddr; +static unsigned long linalloc_botaddr; + +/* from gunzip.c */ +static void *linalloc(unsigned long size) +{ + static int warning = 0; + + if (linalloc_topaddr-size < linalloc_botaddr) { + if (!warning) grub_printf("\nOut of memory in LVM - some LVs and/or VGs will be inaccessible\n"); + warning = 1; + return 0; + } + linalloc_topaddr -= size; + linalloc_topaddr &= ~3; + return (void *)linalloc_topaddr; +} + +static void +reset_linalloc (void) +{ + linalloc_topaddr = RAW_ADDR ((mbi.mem_upper << 10) + 0x0c0000); + linalloc_botaddr = RAW_ADDR ((mbi.mem_upper << 10)); +} + +typedef struct { + unsigned long drive; + unsigned long sector; +} grub_le_map_t; + +typedef struct { + char name[NAME_LEN+1]; + int num; + int le_count; + grub_le_map_t *le_map; +} grub_lv_t; + +typedef struct { + char name[NAME_LEN+1]; + char uuid[UUID_LEN]; + int lv_count; + int le_size; + int le_count; + grub_lv_t *lv; +} grub_vg_t; + +static grub_vg_t vg[MAX_VGS]; + +static void lvm_init() +{ + vg_disk_t disk_vg; + pv_disk_t pv; + lv_disk_t lv; + pe_disk_t pe; + int drive = 0x80; + int vgnum; + static int initialized = 0; + + if (initialized) return; + initialized = 1; + + reset_linalloc(); + grub_memset(&vg,0,sizeof(grub_vg_t)*MAX_VGS); + + for (drive = 0x80; drive < 0x88; drive++) { + unsigned long partition = -1, start, len, offset, ext_offset; + int type, entry, rc, lvnum, pos, i; + int last_lv = -1; + char buf[512], *ptr, *optr; + + while (next_partition(drive, 0, &partition, &type, &start, &len, &offset, &entry, &ext_offset, buf)) { + rc = rawread(drive, start, 0, sizeof(pv_disk_t), (void *)&pv); + if (!rc || grub_strncmp(pv.id,LVM_ID,sizeof(LVM_ID)-1) || !(pv.version == 2 || pv.version == LVM_STRUCT_VERSION)) + continue; + + if (pv.version == 1) pv.pe_start = (pv.pe_on_disk.base + pv.pe_on_disk.size) / SECTOR_SIZE; + + rc = rawread(drive, start+(pv.vg_on_disk.base/SECTOR_SIZE), pv.vg_on_disk.base%SECTOR_SIZE, sizeof(vg_disk_t), (void *)&disk_vg); + if (!rc) continue; + + for (vgnum = 0; vgnum < MAX_VGS && vg[vgnum].name[0]; vgnum++) { + if (!grub_strncmp(disk_vg.vg_uuid,vg[vgnum].uuid,UUID_LEN)) break; + } + if (vgnum >= MAX_VGS) continue; + + if (!vg[vgnum].name[0]) { + grub_strncpy(vg[vgnum].name, pv.vg_name, NAME_LEN); + grub_strncpy(vg[vgnum].uuid, disk_vg.vg_uuid, UUID_LEN); + vg[vgnum].lv_count = disk_vg.lv_cur; + vg[vgnum].le_size = pv.pe_size; + vg[vgnum].lv = linalloc(sizeof(grub_lv_t)*disk_vg.lv_cur); + if (vg[vgnum].lv == 0) { + vg[vgnum].name[0] = 0; + continue; + } + vg[vgnum].le_count = 0; + + pos = pv.lv_on_disk.base; + for (lvnum = 0, i = 0; lvnum < disk_vg.lv_cur && i < disk_vg.lv_max; i++, pos += sizeof(lv_disk_t)) { + rc = rawread(drive, start+(pos/SECTOR_SIZE), pos%SECTOR_SIZE, sizeof(lv_disk_t), (void *)&lv); + if (!rc || lv.lv_name[0] == 0) continue; + vg[vgnum].lv[lvnum].num = lv.lv_number; + vg[vgnum].lv[lvnum].le_count = lv.lv_allocated_le; + vg[vgnum].le_count += lv.lv_allocated_le; + + optr = ptr = lv.lv_name; + optr--; + while (*ptr && ptr < (char *)lv.vg_name) { + if (*ptr == '/') optr = ptr; + ptr++; + } + grub_strncpy(vg[vgnum].lv[lvnum].name, optr+1, NAME_LEN); + vg[vgnum].lv[lvnum].name[NAME_LEN] = 0; + vg[vgnum].lv[lvnum].le_map = linalloc(lv.lv_allocated_le*sizeof(grub_le_map_t)); + if (vg[vgnum].lv[lvnum].le_map == 0) { + vg[vgnum].lv_count = lvnum; + break; + } + grub_memset(vg[vgnum].lv[lvnum].le_map, 0, lv.lv_allocated_le*sizeof(grub_le_map_t)); + lvnum++; + } + } + + if (vg[vgnum].lv_count > 0) { + pos = pv.pe_on_disk.base; + last_lv = -1; + for (i = 0; i < pv.pe_total; i++, pos += sizeof(pe_disk_t)) { + rc = rawread(drive, start+(pos/SECTOR_SIZE), pos%SECTOR_SIZE, sizeof(pe_disk_t), (void *)&pe); + + if (pe.lv_num > 0) { + if (last_lv < 0 || vg[vgnum].lv[last_lv].num != pe.lv_num-1) { + for (last_lv = vg[vgnum].lv_count-1; last_lv > 0; last_lv--) { + if (vg[vgnum].lv[last_lv].num == pe.lv_num-1) break; + } + if (vg[vgnum].lv[last_lv].num != pe.lv_num-1) continue; + } + if (pe.le_num >= vg[vgnum].lv[last_lv].le_count) continue; + vg[vgnum].lv[last_lv].le_map[pe.le_num].drive = drive; + vg[vgnum].lv[last_lv].le_map[pe.le_num].sector = i * vg[vgnum].le_size + pv.pe_start + start; + } + } + } + } + } +} + + +const char *lvm_vg_name(int vgnum) +{ + return vg[vgnum].name; +} + +const char *lvm_lv_name(int vgnum, int lvnum) +{ + return vg[vgnum].lv[lvnum].name; +} + +int lvm_rawread(int vgnum, int sector, int byte_offset, int byte_len, char *buf) +{ + int start_le, le_offset, cont_len; + int lvnum, virtual_le, rc; + int size = byte_len; + int le_bytes = vg[vgnum].le_size*SECTOR_SIZE; + + + lvm_init(); + if (vgnum > MAX_VGS || !vg[vgnum].name[0]) { + errnum = ERR_NO_DISK; + return 0; + } + + start_le = sector/vg[vgnum].le_size; + virtual_le = 0; + for (lvnum = 0; lvnum < vg[vgnum].lv_count; lvnum++) { + if (virtual_le+vg[vgnum].lv[lvnum].le_count > start_le) break; + virtual_le += vg[vgnum].lv[lvnum].le_count; + } + if (lvnum == vg[vgnum].lv_count) { + errnum = ERR_READ; + return 0; + } + + start_le -= virtual_le; + le_offset = sector%vg[vgnum].le_size; + cont_len = le_bytes-le_offset*SECTOR_SIZE-byte_offset; + if (cont_len > byte_len) cont_len = byte_len; + rc = rawread(vg[vgnum].lv[lvnum].le_map[start_le].drive, vg[vgnum].lv[lvnum].le_map[start_le].sector+le_offset, byte_offset, cont_len, buf); + if (!rc) return rc; + buf += cont_len; + byte_len -= cont_len; + start_le++; + + while (byte_len > le_bytes) { + rc = rawread(vg[vgnum].lv[lvnum].le_map[start_le].drive, vg[vgnum].lv[lvnum].le_map[start_le].sector, 0, le_bytes, buf); + if (!rc) return rc; + buf += le_bytes; + byte_len -= le_bytes; + start_le++; + } + + if (byte_len > 0) { + rc = rawread(vg[vgnum].lv[lvnum].le_map[start_le].drive, vg[vgnum].lv[lvnum].le_map[start_le].sector, 0, byte_len, buf); + if (!rc) return rc; + } + + if (disk_read_func) { + int sector_num = sector; + int length = SECTOR_SIZE - byte_offset; + if (length > size) length = size; + (*disk_read_func)(sector_num++, byte_offset, length); + length = size - length; + while (length > SECTOR_SIZE) { + (*disk_read_func)(sector_num++, 0, SECTOR_SIZE); + length -= SECTOR_SIZE; + } + if (length > 0) (*disk_read_func)(sector_num, 0, length); + } + + return 1; +} + +int lvm_next_partition (unsigned long vgnum, unsigned long dest, unsigned long *partition, int *type, + unsigned long *start, unsigned long *len, unsigned long *offset, int *entry, unsigned long *ext_offset, char *buf) +{ + lvm_init(); + if (vgnum > MAX_VGS || !vg[vgnum].name[0]) { + errnum = ERR_NO_DISK; + return 0; + } + errnum = ERR_NONE; + + if (*partition == 0xffffff) { + if (vg[vgnum].lv_count <= 0) return 0; + *partition = 0; + *type = PC_SLICE_TYPE_EXT2FS; + *start = 0; + *len = vg[vgnum].lv[0].le_count*vg[vgnum].le_size; + *offset = 0; + *entry = 0; + *buf = 0; + + return 1; + } + + *start += vg[vgnum].lv[*partition].le_count*vg[vgnum].le_size; + (*partition)++; + if (vg[vgnum].lv_count <= *partition) return 0; + *len = vg[vgnum].lv[*partition].le_count*vg[vgnum].le_size; + *type = PC_SLICE_TYPE_EXT2FS; + + return 1; +} + +#ifndef STAGE1_5 + +static int colon = -1, colon_vgnum; +extern int incomplete, part_choice, unique; +extern char *unique_string; + +char *lvm_set_device(char *device) +{ + int lvnum = -1; + int vgnum = -1, first_choice = -1; + int choices = 0, colon_pos; + int vg_len; + + lvm_init(); + colon = -1; + errnum = 0; + + colon_pos = grub_strstr(device,":")-device; + vg_len = colon_pos; + if (vg_len < 0) vg_len = grub_strlen(device); + + for (vgnum = 0; vgnum < MAX_VGS && vg[vgnum].name[0]; vgnum++) { + if (!grub_strncmp(device, vg[vgnum].name, vg_len)) { + if (!choices || vg[vgnum].name[vg_len] == 0) first_choice = vgnum;+ choices++; + } + } + + if (choices == 0) { + errnum = ERR_DEV_FORMAT; + return 0; + } else if (colon_pos == grub_strlen(vg[first_choice].name)) { + vgnum = first_choice; + device += vg_len; + if (*device == 0) device[1] = 0; + *device = ':'; + device++; + } else if (choices == 1) { + if (colon_pos < 0) { + grub_strcpy(device,vg[first_choice].name); + device += grub_strlen(device); + incomplete = 1; + return device; + } else { + errnum = ERR_DEV_FORMAT; + return 0; + } + } else { + if (colon_pos < 0) { + int common = choices; + + while (1) { + choices = 0; + vg_len++; + device[vg_len] = vg[first_choice].name[vg_len]; + + for (vgnum = 0; vgnum < MAX_VGS && vg[vgnum].name[0]; vgnum++) { + if (!grub_strncmp(device, vg[vgnum].name, vg_len)) { + if (!choices || vg[vgnum].name[vg_len] == 0) first_choice = vgnum; + choices++; + } + } + if (choices != common) break; + } + device += vg_len; + *device = 0; + incomplete = 1; + return device; + + } else { + errnum = ERR_DEV_FORMAT; + return 0; + } + } + + colon = vg_len; + colon_vgnum = vgnum; + + colon_pos = grub_strstr(device,">")-device; + vg_len = colon_pos; + if (vg_len < 0) vg_len = grub_strlen(device); + + choices = 0; + for (lvnum = 0; lvnum < vg[vgnum].lv_count; lvnum++) { + if (!grub_strncmp(device, vg[vgnum].lv[lvnum].name, vg_len)) { + if (!choices || vg[vgnum].lv[lvnum].name[vg_len] == 0) first_choice = lvnum; + choices++; + } + } + + if (choices == 0) { + errnum = ERR_DEV_FORMAT; + return 0; + } else if (colon_pos == grub_strlen(vg[vgnum].lv[first_choice].name)) { + lvnum = first_choice; + device += vg_len; + if (*device == 0) device[1] = 0; + *device = '>'; + device++; + } else if (choices == 1) { + if (colon_pos < 0) { + grub_strcpy(device,vg[vgnum].lv[first_choice].name); + device += grub_strlen(device); + *device = 0; + return device; + } else { + errnum = ERR_DEV_FORMAT; + return 0; + } + } else { + if (colon_pos < 0) { + int common = choices; + + while (1) { + choices = 0; + vg_len++; + device[vg_len] = vg[vgnum].lv[first_choice].name[vg_len]; + + for (lvnum = 0; lvnum < vg[vgnum].lv_count; lvnum++) { + if (!grub_strncmp(device, vg[vgnum].lv[lvnum].name, vg_len)) { + if (!choices || vg[vgnum].lv[lvnum].name[vg_len] == 0) first_choice = lvnum; + choices++; + } + } + if (choices != common) break; + } + device += vg_len-1; + *device = 0; + incomplete = 1; + return device; + + } else { + errnum = ERR_DEV_FORMAT; + return 0; + } + } + + colon += vg_len+1; + + current_drive = vgnum+LVM_MIN_DRIVE; + current_partition = lvnum; + part_choice = PART_CHOSEN; + return device; + +} + +extern int do_completion; + +int lvm_print_completions(char *buf, char *ptr, int is_completion) +{ + int vgnum, lvnum, vg_len; + lvm_init(); + + if (colon < 0) { + if (!is_completion) + grub_printf(" Possible volume groups are:\n"); + + vg_len = ptr-buf-1; + for (vgnum = 0; vgnum < MAX_VGS && vg[vgnum].name[0]; vgnum++) { + if (!grub_strncmp(buf+1, vg[vgnum].name, vg_len)) { + print_a_completion(vg[vgnum].name); + } + } + + if (is_completion && *unique_string) { + ptr = buf; + while (*ptr++ != '<') ; + grub_strcpy (ptr, unique_string); + if (unique == 1) { + ptr += grub_strlen (ptr); + *ptr++ = ':'; + *ptr = 0; + } + } + + } else if (buf[colon+1] == ':') { + if (!is_completion) + grub_printf(" Possible logical volumes are:\n"); + + vg_len = ptr-buf-colon-2; + vgnum = colon_vgnum; + for (lvnum = 0; lvnum < vg[vgnum].lv_count; lvnum++) { + if (!grub_strncmp(buf+colon+2, vg[vgnum].lv[lvnum].name, vg_len)) { + print_a_completion(vg[vgnum].lv[lvnum].name); + } + } + + if (is_completion && *unique_string) { + ptr = buf; + while (*ptr++ != ':') ; + grub_strcpy (ptr, unique_string); + if (unique == 1) { + ptr += grub_strlen (ptr); + *ptr++ = '>'; + *ptr = 0; + } + } + + } else if (buf[colon+1] == '>') { + unique = 1; + + } else { + errnum = ERR_BAD_ARGUMENT; + + } + + if (!is_completion) grub_putchar('\n'); + + print_error(); + colon = -1; + do_completion = 0; + + if (errnum) return -1; + return unique - 1; +} + +#endif +#endif + diff -burN grub-0.94/stage2/Makefile.am grub-0.94-lvm/stage2/Makefile.am --- grub-0.94/stage2/Makefile.am 2004-02-21 18:29:57.493573080 -0600 +++ grub-0.94-lvm/stage2/Makefile.am 2004-02-21 20:24:01.986052064 -0600 @@ -18,7 +18,7 @@ libgrub_a_SOURCES = boot.c builtins.c char_io.c cmdline.c common.c \ disk_io.c fsys_ext2fs.c fsys_fat.c fsys_ffs.c fsys_jfs.c \ fsys_minix.c fsys_reiserfs.c fsys_vstafs.c fsys_xfs.c gunzip.c \ - md5.c serial.c stage2.c terminfo.c tparm.c graphics.c + md5.c linuxlvm.c serial.c stage2.c terminfo.c tparm.c graphics.c libgrub_a_CFLAGS = $(GRUB_CFLAGS) -I$(top_srcdir)/lib \ -DGRUB_UTIL=1 -DFSYS_EXT2FS=1 -DFSYS_FAT=1 -DFSYS_FFS=1 \ -DFSYS_JFS=1 -DFSYS_MINIX=1 -DFSYS_REISERFS=1 -DFSYS_VSTAFS=1 \ @@ -92,7 +92,7 @@ cmdline.c common.c console.c disk_io.c fsys_ext2fs.c \ fsys_fat.c fsys_ffs.c fsys_jfs.c fsys_minix.c fsys_reiserfs.c \ fsys_vstafs.c fsys_xfs.c gunzip.c hercules.c md5.c serial.c \ - smp-imps.c stage2.c terminfo.c tparm.c graphics.c + smp-imps.c stage2.c linuxlvm.c terminfo.c tparm.c graphics.c pre_stage2_exec_CFLAGS = $(STAGE2_COMPILE) $(FSYS_CFLAGS) pre_stage2_exec_CCASFLAGS = $(STAGE2_COMPILE) $(FSYS_CFLAGS) pre_stage2_exec_LDFLAGS = $(PRE_STAGE2_LINK) @@ -130,7 +130,7 @@ # For e2fs_stage1_5 target. e2fs_stage1_5_exec_SOURCES = start.S asm.S common.c char_io.c disk_io.c \ - stage1_5.c fsys_ext2fs.c bios.c + stage1_5.c fsys_ext2fs.c bios.c linuxlvm.c e2fs_stage1_5_exec_CFLAGS = $(STAGE1_5_COMPILE) -DFSYS_EXT2FS=1 \ -DNO_BLOCK_FILES=1 e2fs_stage1_5_exec_CCASFLAGS = $(STAGE1_5_COMPILE) -DFSYS_EXT2FS=1 \ @@ -139,7 +139,7 @@ # For fat_stage1_5 target. fat_stage1_5_exec_SOURCES = start.S asm.S common.c char_io.c disk_io.c \ - stage1_5.c fsys_fat.c bios.c + stage1_5.c fsys_fat.c bios.c linuxlvm.c fat_stage1_5_exec_CFLAGS = $(STAGE1_5_COMPILE) -DFSYS_FAT=1 \ -DNO_BLOCK_FILES=1 fat_stage1_5_exec_CCASFLAGS = $(STAGE1_5_COMPILE) -DFSYS_FAT=1 \ @@ -148,16 +148,16 @@ # For ffs_stage1_5 target. ffs_stage1_5_exec_SOURCES = start.S asm.S common.c char_io.c disk_io.c \ - stage1_5.c fsys_ffs.c bios.c + stage1_5.c fsys_ffs.c bios.c linuxlvm.c ffs_stage1_5_exec_CFLAGS = $(STAGE1_5_COMPILE) -DFSYS_FFS=1 \ - -DNO_BLOCK_FILES=1 + -DNO_BLOCK_FILES=1 -DNO_LVM_IN_STAGE1_5=1 ffs_stage1_5_exec_CCASFLAGS = $(STAGE1_5_COMPILE) -DFSYS_FFS=1 \ -DNO_BLOCK_FILES=1 ffs_stage1_5_exec_LDFLAGS = $(STAGE1_5_LINK) # For minix_stage1_5 target. minix_stage1_5_exec_SOURCES = start.S asm.S common.c char_io.c disk_io.c \ - stage1_5.c fsys_minix.c bios.c + stage1_5.c fsys_minix.c bios.c linuxlvm.c minix_stage1_5_exec_CFLAGS = $(STAGE1_5_COMPILE) -DFSYS_MINIX=1 \ -DNO_BLOCK_FILES=1 minix_stage1_5_exec_CCASFLAGS = $(STAGE1_5_COMPILE) -DFSYS_MINIX=1 \ @@ -166,7 +166,7 @@ # For reiserfs_stage1_5 target. reiserfs_stage1_5_exec_SOURCES = start.S asm.S common.c char_io.c \ - disk_io.c stage1_5.c fsys_reiserfs.c bios.c + disk_io.c stage1_5.c fsys_reiserfs.c bios.c linuxlvm.c reiserfs_stage1_5_exec_CFLAGS = $(STAGE1_5_COMPILE) -DFSYS_REISERFS=1 \ -DNO_BLOCK_FILES=1 reiserfs_stage1_5_exec_CCASFLAGS = $(STAGE1_5_COMPILE) -DFSYS_REISERFS=1 \ @@ -175,7 +175,7 @@ # For vstafs_stage1_5 target. vstafs_stage1_5_exec_SOURCES = start.S asm.S common.c char_io.c \ - disk_io.c stage1_5.c fsys_vstafs.c bios.c + disk_io.c stage1_5.c fsys_vstafs.c bios.c linuxlvm.c vstafs_stage1_5_exec_CFLAGS = $(STAGE1_5_COMPILE) -DFSYS_VSTAFS=1 \ -DNO_BLOCK_FILES=1 vstafs_stage1_5_exec_CCASFLAGS = $(STAGE1_5_COMPILE) -DFSYS_VSTAFS=1 \ @@ -184,7 +184,7 @@ # For jfs_stage1_5 target. jfs_stage1_5_exec_SOURCES = start.S asm.S common.c char_io.c \ - disk_io.c stage1_5.c fsys_jfs.c bios.c + disk_io.c stage1_5.c fsys_jfs.c bios.c linuxlvm.c jfs_stage1_5_exec_CFLAGS = $(STAGE1_5_COMPILE) -DFSYS_JFS=1 \ -DNO_BLOCK_FILES=1 jfs_stage1_5_exec_CCASFLAGS = $(STAGE1_5_COMPILE) -DFSYS_JFS=1 \ @@ -193,7 +193,7 @@ # For xfs_stage1_5 target. xfs_stage1_5_exec_SOURCES = start.S asm.S common.c char_io.c \ - disk_io.c stage1_5.c fsys_xfs.c bios.c + disk_io.c stage1_5.c fsys_xfs.c bios.c linuxlvm.c xfs_stage1_5_exec_CFLAGS = $(STAGE1_5_COMPILE) -DFSYS_XFS=1 \ -DNO_BLOCK_FILES=1 xfs_stage1_5_exec_CCASFLAGS = $(STAGE1_5_COMPILE) -DFSYS_XFS=1 \ diff -burN grub-0.94/stage2/shared.h grub-0.94-lvm/stage2/shared.h --- grub-0.94/stage2/shared.h 2004-02-21 18:29:57.495572776 -0600 +++ grub-0.94-lvm/stage2/shared.h 2004-02-21 20:24:01.987051912 -0600 @@ -794,6 +794,22 @@ int sector, int nsec, int segment); void stop_floppy (void); +#if defined(STAGE1_5) && defined(NO_LVM_IN_STAGE1_5) && !defined(NO_LVM) +#define NO_LVM +#endif + +#ifndef NO_LVM +#define LVM_MIN_DRIVE 0x21 +#define LVM_MAX_DRIVE 0x2f +int lvm_rawread(int vgnum, int sector, int byte_offset, int byte_len, char *buf); +int lvm_next_partition (unsigned long vgnum, unsigned long dest, unsigned long *partition, int *type, + unsigned long *start, unsigned long *len, unsigned long *offset, int *entry, unsigned long *ext_offset, char *buf); +char *lvm_set_device(char *device); +int lvm_print_completions(char *buf, char *ptr, int is_completion); +const char *lvm_vg_name(int vgnum); +const char *lvm_lv_name(int vgnum, int lvnum); +#endif + /* Command-line interface functions. */ #ifndef STAGE1_5 @@ -854,7 +870,7 @@ int grub_sprintf (char *buffer, const char *format, ...); int grub_tolower (int c); int grub_isspace (int c); -int grub_strncat (char *s1, const char *s2, int n); +int grub_strcat (char *s1, const char *s2); void grub_memcpy(void *dest, const void *src, int len); void *grub_memmove (void *to, const void *from, int len); void *grub_memset (void *start, int c, int len); @@ -862,8 +878,10 @@ char *grub_strstr (const char *s1, const char *s2); int grub_memcmp (const char *s1, const char *s2, int n); int grub_strcmp (const char *s1, const char *s2); +int grub_strncmp (const char *s1, const char *s2, int len); int grub_strlen (const char *str); char *grub_strcpy (char *dest, const char *src); +char *grub_strncpy (char *dest, const char *src, int len); #ifndef GRUB_UTIL typedef unsigned long grub_jmp_buf[6];