From 7ab4e9c240649cd34e158ff100365c661e3ddeb9 Mon Sep 17 00:00:00 2001 From: okuji Date: Tue, 8 Aug 2000 16:41:56 +0000 Subject: [PATCH] add a workaround for the bug in Linux into the grub shell. --- ChangeLog | 33 ++++++ NEWS | 8 ++ docs/grub-install.8 | 2 +- docs/grub.8 | 2 +- docs/mbchk.1 | 2 +- lib/device.c | 83 ++++++++++++++ lib/device.h | 5 + stage2/builtins.c | 264 +++++++++++++++++++++++++++++++++++-------- util/grub-install.in | 2 +- 9 files changed, 350 insertions(+), 51 deletions(-) diff --git a/ChangeLog b/ChangeLog index 83ef2eebc..c28255349 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,36 @@ +2000-08-09 OKUJI Yoshinori + + * stage2/builtins.c [GRUB_UTIL]: Include stdio.h. + (embed_func) [GRUB_UTIL && __linux__]: When embedding a Stage + 1.5 into a partition, call write_to_partition instead of + biosdisk. + (install_func): Set DEST_PARTITION to the partition where Stage + 1 resides. + Set SRC_PART_START to the starting address of the partition + where Stage 2 resides. + (install_func) [GRUB_UTIL]: Set STAGE2_OS_FILE to the file name + of Stage 2 under an OS, if the new option "--stage2" is + specified. Otherwise, set it to null. + If STAGE2_OS_FILE is not null, modify the Stage 2 via the + filesystem serviced by the OS. + (install_func) [GRUB_UTIL && __linux__]: If STAGE2_OS_FILE is + null but the Stage2 resides in a partition, use + write_to_partition. + If DEST_PARTITION is not 0xFFFFFF, use write_to_partition, to + embed Stage 1. + (setup_func) [GRUB_UTIL]: If --stage2 is specified, set + STAGE2_ARG to the string pointing to the option. Otherwise, set + it to null. + (setup_func) [!GRUB_UTIL]: Set STAGE2_ARG to null. + (setup_func): If STAGE2_ARG is not null, add STAGE2_ARG and a + space character into CMD_ARG. + * lib/device.c (_LARGEFILE_SOURCE): Defined. + (_FILE_OFFSET_BITS): Likewise. + [__linux__] (write_to_partition): New function. + * lib/device.h [__linux__] (write_to_partition): Declared. + * util/grub-install.in: Specify the option "--stage2" for the + command "setup". + 2000-08-04 Jochen Hoenicke * stage2/fsys_fat.c (fat_superblock): clust_eof_marker added. diff --git a/NEWS b/NEWS index e6e3490a9..fc8c4c331 100644 --- a/NEWS +++ b/NEWS @@ -9,6 +9,14 @@ New in 0.5.96 - XXXX-XX-XX: * Now GRUB is compliant with the Linux/i386 boot protocol version 2.02. * The network support is updated to Etherboot-4.6.4. * Symlinks in ReiserFS are supported. +* Add a workaround into the grub shell, so that it works fine even under + Linux 2.4. +* Add a new option `--stage2' into the commands "install" and "setup", + to let the grub shell know what the file name of Stage 2 is under your + operating system. You must specify the option correctly, if you cannot + unmount the partition where GRUB images reside. We'd recommend _not_ + using those commands directly, but using the utility "grub-install" + instead, because this is safer. New in 0.5.95 - 2000-06-27: * NetBSD ELF kernel support is added. You have to specify the new option diff --git a/docs/grub-install.8 b/docs/grub-install.8 index d5163b755..f28a3f1dd 100644 --- a/docs/grub-install.8 +++ b/docs/grub-install.8 @@ -1,5 +1,5 @@ .\" DO NOT MODIFY THIS FILE! It was generated by help2man 1.020. -.TH GRUB-INSTALL "8" "July 2000" "grub-install (GNU GRUB 0.5.96)" FSF +.TH GRUB-INSTALL "8" "August 2000" "grub-install (GNU GRUB 0.5.96)" FSF .SH NAME grub-install \- install GRUB on your drive .SH SYNOPSIS diff --git a/docs/grub.8 b/docs/grub.8 index e95bc8213..af063a2f0 100644 --- a/docs/grub.8 +++ b/docs/grub.8 @@ -1,5 +1,5 @@ .\" DO NOT MODIFY THIS FILE! It was generated by help2man 1.020. -.TH GRUB "8" "July 2000" "GNU GRUB 0.5.96" FSF +.TH GRUB "8" "August 2000" "GNU GRUB 0.5.96" FSF .SH NAME GRUB \- the grub shell .SH SYNOPSIS diff --git a/docs/mbchk.1 b/docs/mbchk.1 index 8a72c28c1..146963032 100644 --- a/docs/mbchk.1 +++ b/docs/mbchk.1 @@ -1,5 +1,5 @@ .\" DO NOT MODIFY THIS FILE! It was generated by help2man 1.020. -.TH MBCHK "1" "July 2000" "mbchk (GNU GRUB 0.5.96)" FSF +.TH MBCHK "1" "August 2000" "mbchk (GNU GRUB 0.5.96)" FSF .SH NAME mbchk \- check the format of a Multiboot kernel .SH SYNOPSIS diff --git a/lib/device.c b/lib/device.c index 9114b0ccc..60c757dcf 100644 --- a/lib/device.c +++ b/lib/device.c @@ -18,6 +18,11 @@ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ +/* Try to use glibc's transparant LFS support. */ +#define _LARGEFILE_SOURCE 1 +/* lseek becomes synonymous with lseek64. */ +#define _FILE_OFFSET_BITS 64 + #include #include #include @@ -30,6 +35,11 @@ #include #ifdef __linux__ +# if !defined(__GLIBC__) || \ + ((__GLIBC__ < 2) || ((__GLIBC__ == 2) && (__GLIBC_MINOR__ < 1))) +/* Maybe libc doesn't have large file support. */ +# include /* _llseek */ +# endif /* (GLIBC < 2) || ((__GLIBC__ == 2) && (__GLIBC_MINOR < 1)) */ # include /* ioctl */ # include /* HDIO_GETGEO */ # include /* FLOPPY_MAJOR */ @@ -508,3 +518,76 @@ restore_device_map (char **map) free (map); } + +#ifdef __linux__ +/* Linux-only function, because Linux has a bug that the disk cache for + a whole disk is not consistent with the one for a partition of the + disk. */ +int +write_to_partition (char **map, int drive, int partition, + int sector, int size, const char *buf) +{ + char dev[64]; /* XXX */ + int fd; + + if ((partition & 0x00FF00) != 0x00FF00) + { + /* If the partition is a BSD partition, it is difficult to + obtain the representation in Linux. So don't support that. */ + errnum = ERR_DEV_VALUES; + return 1; + } + + assert (map[drive] != 0); + sprintf (dev, "%s%d", map[drive], ((partition >> 16) & 0xFF) + 1); + + /* Open the partition. */ + fd = open (dev, O_RDONLY); + if (fd < 0) + { + errnum = ERR_NO_PART; + return 0; + } + +#if defined(__linux__) && (!defined(__GLIBC__) || \ + ((__GLIBC__ < 2) || ((__GLIBC__ == 2) && (__GLIBC_MINOR__ < 1)))) + /* Maybe libc doesn't have large file support. */ + { + loff_t offset, result; + static int _llseek (uint filedes, ulong hi, ulong lo, + loff_t *res, uint wh); + _syscall5 (int, _llseek, uint, filedes, ulong, hi, ulong, lo, + loff_t *, res, uint, wh); + + offset = (loff_t) sector * (loff_t) SECTOR_SIZE; + if (_llseek (fd, offset >> 32, offset & 0xffffffff, &result, SEEK_SET)) + { + errnum = ERR_DEV_VALUES; + return 0; + } + } +#else + { + off_t offset = (off_t) sector * (off_t) SECTOR_SIZE; + + if (lseek (fd, offset, SEEK_SET) != offset) + { + errnum = ERR_DEV_VALUES; + return 0; + } + } +#endif + + if (write (fd, buf, size * SECTOR_SIZE) != (size * SECTOR_SIZE)) + { + close (fd); + errnum = ERR_WRITE; + return 0; + } + + sync (); /* Paranoia. */ + close (fd); + + return 1; +} +#endif /* __linux__ */ diff --git a/lib/device.h b/lib/device.h index 9eb419725..e616e4bdf 100644 --- a/lib/device.h +++ b/lib/device.h @@ -39,4 +39,9 @@ extern int init_device_map (char ***map, const char *map_file, int no_floppies); extern void restore_device_map (char **map); +#ifdef __linux__ +extern int write_to_partition (char **map, int drive, int partition, + int offset, int size, const char *buf); +#endif /* __linux__ */ + #endif /* DEVICE_MAP_HEADER */ diff --git a/stage2/builtins.c b/stage2/builtins.c index 0711f6bfb..4f76d604a 100644 --- a/stage2/builtins.c +++ b/stage2/builtins.c @@ -27,6 +27,7 @@ #endif #ifdef GRUB_UTIL +# include # include #else /* ! GRUB_UTIL */ # include @@ -851,7 +852,6 @@ embed_func (char *arg, int flags) char *stage1_5_buffer = (char *) RAW_ADDR (0x100000); int len, size; int sector; - int i; stage1_5 = arg; device = skip_to (0, stage1_5); @@ -929,20 +929,39 @@ embed_func (char *arg, int flags) /* Clear the cache. */ buf_track = -1; - + /* Now perform the embedding. */ - for (i = 0; i < size; i++) +#if defined(GRUB_UTIL) && defined(__linux__) + if (current_partition != 0xFFFFFF) { - grub_memmove ((char *) SCRATCHADDR, stage1_5_buffer + i * SECTOR_SIZE, - SECTOR_SIZE); - if (biosdisk (BIOSDISK_WRITE, current_drive, &buf_geom, - sector + i, 1, SCRATCHSEG)) + /* If the grub shell is running under Linux and the user wants to + embed a Stage 1.5 into a partition instead of a MBR, use system + calls directly instead of biosdisk, because of the bug in + Linux. *sigh* */ + + if (! write_to_partition (device_map, current_drive, current_partition, + sector - part_start, size, stage1_5_buffer)) + return 1; + } + else +#endif /* GRUB_UTIL && __linux__ */ + { + int i; + + for (i = 0; i < size; i++) { - errnum = ERR_WRITE; - return 1; + grub_memmove ((char *) SCRATCHADDR, + stage1_5_buffer + i * SECTOR_SIZE, + SECTOR_SIZE); + if (biosdisk (BIOSDISK_WRITE, current_drive, &buf_geom, + sector + i, 1, SCRATCHSEG)) + { + errnum = ERR_WRITE; + return 1; + } } } - + grub_printf (" %d sectors are embedded.\n", size); return 0; } @@ -1452,8 +1471,8 @@ install_func (char *arg, int flags) char *config_filename = stage2_second_buffer + SECTOR_SIZE; char *dummy = config_filename + SECTOR_SIZE; int new_drive = 0xFF; - int dest_drive, dest_sector; - int src_drive, src_partition; + int dest_drive, dest_partition, dest_sector; + int src_drive, src_partition, src_part_start; int i; struct geometry dest_geom, src_geom; int saved_sector; @@ -1471,6 +1490,12 @@ install_func (char *arg, int flags) /* Was the last sector full? */ int last_length = SECTOR_SIZE; +#ifdef GRUB_UTIL + /* If the Stage 2 is in a partition mounted by an OS, this will store + the filename under the OS. */ + char *stage2_os_file = 0; +#endif /* GRUB_UTIL */ + /* Save the first sector of Stage2 in STAGE2_SECT. */ static void disk_read_savesect_func (int sector, int offset, int length) { @@ -1522,10 +1547,23 @@ install_func (char *arg, int flags) } /* First, check the GNU-style long option. */ - if (grub_memcmp ("--force-lba", arg, 11) == 0) + while (1) { - is_force_lba = 1; - arg = skip_to (0, arg); + if (grub_memcmp ("--force-lba", arg, sizeof ("--force-lba") - 1) == 0) + { + is_force_lba = 1; + arg = skip_to (0, arg); + } +#ifdef GRUB_UTIL + else if (grub_memcmp ("--stage2=", arg, sizeof ("--stage2=") - 1) == 0) + { + stage2_os_file = arg + sizeof ("--stage2=") - 1; + arg = skip_to (0, arg); + nul_terminate (stage2_os_file); + } +#endif /* GRUB_UTIL */ + else + break; } stage1_file = arg; @@ -1568,6 +1606,7 @@ install_func (char *arg, int flags) /* Store the information for the destination device. */ dest_drive = current_drive; + dest_partition = current_partition; dest_geom = buf_geom; dest_sector = part_start; @@ -1612,6 +1651,7 @@ install_func (char *arg, int flags) src_drive = current_drive; src_partition = current_partition; + src_part_start = part_start; src_geom = buf_geom; if (! new_drive) @@ -1810,16 +1850,65 @@ install_func (char *arg, int flags) /* Copy the name. */ grub_strcpy (location, real_config_filename); - } /* Write it to the disk. */ buf_track = -1; - if (biosdisk (BIOSDISK_WRITE, current_drive, &buf_geom, - saved_sector, 1, SCRATCHSEG)) + +#ifdef GRUB_UTIL + /* In the grub shell, access the Stage 2 via the OS filesystem + service, if possible. */ + if (stage2_os_file) { - errnum = ERR_WRITE; - goto fail; + FILE *fp; + + fp = fopen (stage2_os_file, "r"); + if (! fp) + { + errnum = ERR_FILE_NOT_FOUND; + goto fail; + } + + if (fseek (fp, SECTOR_SIZE, SEEK_SET) != 0) + { + fclose (fp); + errnum = ERR_BAD_VERSION; + goto fail; + } + + if (fwrite ((const void *) SCRATCHADDR, 1, SECTOR_SIZE, fp) + != SECTOR_SIZE) + { + fclose (fp); + errnum = ERR_WRITE; + goto fail; + } + + fclose (fp); + } + else +# ifdef __linux__ + /* Avoid the bug in Linux, which makes the disk cache for + the whole disk inconsistent with the one for the partition. */ + if (current_partition != 0xFFFFFF) + { + if (! write_to_partition (device_map, current_drive, + current_partition, + saved_sector - part_start, + 1, + (const char *) SCRATCHADDR)) + goto fail; + } + else +# endif /* __linux__ */ +#endif /* GRUB_UTIL */ + { + if (biosdisk (BIOSDISK_WRITE, current_drive, &buf_geom, + saved_sector, 1, SCRATCHSEG)) + { + errnum = ERR_WRITE; + goto fail; + } } } } @@ -1827,31 +1916,90 @@ install_func (char *arg, int flags) /* Clear the cache. */ buf_track = -1; - /* Write the modified first sector of Stage2 to the disk. */ - grub_memmove ((char *) SCRATCHADDR, stage2_first_buffer, SECTOR_SIZE); - if (biosdisk (BIOSDISK_WRITE, src_drive, &src_geom, - stage2_first_sector, 1, SCRATCHSEG)) + /* Write the modified sectors of Stage2 to the disk. */ +#ifdef GRUB_UTIL + if (! is_stage1_5 && stage2_os_file) { - errnum = ERR_WRITE; - goto fail; + FILE *fp; + + fp = fopen (stage2_os_file, "r"); + if (! fp) + { + errnum = ERR_FILE_NOT_FOUND; + goto fail; + } + + if (fwrite (stage2_first_buffer, 1, SECTOR_SIZE, fp) != SECTOR_SIZE) + { + fclose (fp); + errnum = ERR_WRITE; + goto fail; + } + + if (fwrite (stage2_second_buffer, 1, SECTOR_SIZE, fp) != SECTOR_SIZE) + { + fclose (fp); + errnum = ERR_WRITE; + goto fail; + } + + fclose (fp); } - - /* Write the modified second sector of Stage2 to the disk. */ - grub_memmove ((char *) SCRATCHADDR, stage2_second_buffer, SECTOR_SIZE); - if (biosdisk (BIOSDISK_WRITE, src_drive, &src_geom, - stage2_second_sector, 1, SCRATCHSEG)) + else +# ifdef __linux__ + if (src_partition != 0xFFFFFF) + { + if (! write_to_partition (device_map, src_drive, src_partition, + stage2_first_sector - src_part_start, + 1, stage2_first_buffer)) + goto fail; + + if (! write_to_partition (device_map, src_drive, src_partition, + stage2_second_sector - src_part_start, + 1, stage2_second_buffer)) + goto fail; + } + else +# endif /* __linux__ */ +#endif /* GRUB_UTIL */ { - errnum = ERR_WRITE; - goto fail; + /* The first. */ + grub_memmove ((char *) SCRATCHADDR, stage2_first_buffer, SECTOR_SIZE); + if (biosdisk (BIOSDISK_WRITE, src_drive, &src_geom, + stage2_first_sector, 1, SCRATCHSEG)) + { + errnum = ERR_WRITE; + goto fail; + } + + /* The second. */ + grub_memmove ((char *) SCRATCHADDR, stage2_second_buffer, SECTOR_SIZE); + if (biosdisk (BIOSDISK_WRITE, src_drive, &src_geom, + stage2_second_sector, 1, SCRATCHSEG)) + { + errnum = ERR_WRITE; + goto fail; + } } /* Write the modified sector of Stage 1 to the disk. */ - grub_memmove ((char *) SCRATCHADDR, stage1_buffer, SECTOR_SIZE); - if (biosdisk (BIOSDISK_WRITE, dest_drive, &dest_geom, - dest_sector, 1, SCRATCHSEG)) +#if defined(GRUB_UTIL) && defined(__linux__) + if (dest_partition != 0xFFFFFF) { - errnum = ERR_WRITE; - goto fail; + if (! write_to_partition (device_map, dest_drive, dest_partition, + 0, 1, stage1_buffer)) + goto fail; + } + else +#endif /* GRUB_UTIL && __linux__ */ + { + grub_memmove ((char *) SCRATCHADDR, stage1_buffer, SECTOR_SIZE); + if (biosdisk (BIOSDISK_WRITE, dest_drive, &dest_geom, + dest_sector, 1, SCRATCHSEG)) + { + errnum = ERR_WRITE; + goto fail; + } } fail: @@ -1872,7 +2020,7 @@ static struct builtin builtin_install = "install", install_func, BUILTIN_CMDLINE, - "install [--force-lba] STAGE1 [d] DEVICE STAGE2 [ADDR] [p] [CONFIG_FILE] [REAL_CONFIG_FILE]", + "install [--stage2=STAGE2_FILE] [--force-lba] STAGE1 [d] DEVICE STAGE2 [ADDR] [p] [CONFIG_FILE] [REAL_CONFIG_FILE]", "Install STAGE1 on DEVICE, and install a blocklist for loading STAGE2" " as a Stage 2. If the option `d' is present, the Stage 1 will always" " look for the disk where STAGE2 was installed, rather than using" @@ -1885,7 +2033,8 @@ static struct builtin builtin_install = " 1.5 and REAL_CONFIG_FILE is present, then the Stage 2 CONFIG_FILE is" " patched with the configuration filename REAL_CONFIG_FILE." " If the option `--force-lba' is specified, disable some sanity checks" - " for LBA mode." + " for LBA mode. If the option `--stage2' is specified, rewrite the Stage" + " 2 via your OS's filesystem instead of the raw device." }; @@ -2729,6 +2878,7 @@ setup_func (char *arg, int flags) char device[16]; char *buffer = (char *) RAW_ADDR (0x100000); int is_force_lba = 0; + char *stage2_arg = 0; static void sprint_device (int drive, int partition) { @@ -2772,10 +2922,23 @@ setup_func (char *arg, int flags) tmp_partition = saved_partition; /* Check if the user specifies --force-lba. */ - if (grub_memcmp ("--force-lba", arg, 11) == 0) + while (1) { - is_force_lba = 1; - arg = skip_to (0, arg); + if (grub_memcmp ("--force-lba", arg, sizeof ("--force-lba") - 1) == 0) + { + is_force_lba = 1; + arg = skip_to (0, arg); + } +#ifdef GRUB_UTIL + else if (grub_memcmp ("--stage2=", arg, sizeof ("--stage2=") - 1) == 0) + { + stage2_arg = arg; + arg = skip_to (0, arg); + nul_terminate (stage2_arg); + } +#endif /* GRUB_UTIL */ + else + break; } install_ptr = arg; @@ -2889,8 +3052,10 @@ setup_func (char *arg, int flags) #ifdef NO_BUGGY_BIOS_IN_THE_WORLD /* I prefer this, but... */ - grub_sprintf (cmd_arg, "%s%s %s%s %s p %s", - is_force_lba? "--force-lba " : "", + grub_sprintf (cmd_arg, "%s%s%s%s %s%s %s p %s", + is_force_lba? "--force-lba" : "", + stage2_arg? stage2_arg : "", + stage2_arg? " " : "", stage1, (installed_drive != image_drive) ? "d " : "", device, @@ -2901,8 +3066,10 @@ setup_func (char *arg, int flags) may not expect that your BIOS will pass a booting drive to stage1 correctly. Thus, always specify the option `d', whether INSTALLED_DRIVE is identical with IMAGE_DRIVE or not. *sigh* */ - grub_sprintf (cmd_arg, "%s%s d %s %s p %s", + grub_sprintf (cmd_arg, "%s%s%s%s d %s %s p %s", is_force_lba? "--force-lba " : "", + stage2_arg? stage2_arg : "", + stage2_arg? " " : "", stage1, device, stage2, @@ -2932,7 +3099,7 @@ static struct builtin builtin_setup = "setup", setup_func, BUILTIN_CMDLINE, - "setup [--force-lba] INSTALL_DEVICE [IMAGE_DEVICE]", + "setup [--stage2=STAGE2_FILE] [--force-lba] INSTALL_DEVICE [IMAGE_DEVICE]", "Set up the installation of GRUB automatically. This command uses" " the more flexible command \"install\" in the backend and installs" " GRUB into the device INSTALL_DEVICE. If IMAGE_DEVICE is specified," @@ -2940,6 +3107,9 @@ static struct builtin builtin_setup = " use the current \"root device\", which can be set by the command" " \"root\". If you know that your BIOS should support LBA but GRUB" " doesn't work in LBA mode, specify the option `--force-lba'." + " If you install GRUB under the grub shell and you cannot unmount the" + " partition where GRUB images reside, specify the option `--stage2'" + " to tell GRUB the file name under your OS." }; diff --git a/util/grub-install.in b/util/grub-install.in index ef2cae2d1..55951aacd 100644 --- a/util/grub-install.in +++ b/util/grub-install.in @@ -262,7 +262,7 @@ test -x /bin/tempfile && log_file=`tempfile --prefix=grub` # Now perform the installation. $grub_shell --batch --device-map=$device_map <$log_file root $root_drive -setup $force_lba $install_drive +setup $force_lba --stage2=$grub_dir/stage2 $install_drive quit EOF