1N/A/* disk_io.c - implement abstract BIOS disk input and output */
1N/A/*
1N/A * GRUB -- GRand Unified Bootloader
1N/A * Copyright (C) 1999,2000,2001,2002,2003,2004 Free Software Foundation, Inc.
1N/A *
1N/A * This program is free software; you can redistribute it and/or modify
1N/A * it under the terms of the GNU General Public License as published by
1N/A * the Free Software Foundation; either version 2 of the License, or
1N/A * (at your option) any later version.
1N/A *
1N/A * This program is distributed in the hope that it will be useful,
1N/A * but WITHOUT ANY WARRANTY; without even the implied warranty of
1N/A * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
1N/A * GNU General Public License for more details.
1N/A *
1N/A * You should have received a copy of the GNU General Public License
1N/A * along with this program; if not, write to the Free Software
1N/A * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
1N/A */
1N/A
1N/A
1N/A#include <shared.h>
1N/A#include <filesys.h>
1N/A
1N/A#ifdef SUPPORT_NETBOOT
1N/A# include <grub.h>
1N/A#endif
1N/A
1N/A#ifdef GRUB_UTIL
1N/A# include <device.h>
1N/A#endif
1N/A
1N/A/* instrumentation variables */
1N/Avoid (*disk_read_hook) (unsigned int, int, int) = NULL;
1N/Avoid (*disk_read_func) (unsigned int, int, int) = NULL;
1N/A
1N/A#ifndef STAGE1_5
1N/Aint print_possibilities;
1N/A
1N/Astatic int do_completion;
1N/Astatic int unique;
1N/Astatic char *unique_string;
1N/A
1N/A#endif
1N/A
1N/Aint fsmax;
1N/Astruct fsys_entry fsys_table[NUM_FSYS + 1] =
1N/A{
1N/A /* TFTP should come first because others don't handle net device. */
1N/A# ifdef FSYS_TFTP
1N/A {"tftp", tftp_mount, tftp_read, tftp_dir, tftp_close, 0},
1N/A# endif
1N/A# ifdef FSYS_FAT
1N/A {"fat", fat_mount, fat_read, fat_dir, 0, 0},
1N/A# endif
1N/A# ifdef FSYS_EXT2FS
1N/A {"ext2fs", ext2fs_mount, ext2fs_read, ext2fs_dir, 0, 0},
1N/A# endif
1N/A# ifdef FSYS_MINIX
1N/A {"minix", minix_mount, minix_read, minix_dir, 0, 0},
1N/A# endif
1N/A# ifdef FSYS_REISERFS
1N/A {"reiserfs", reiserfs_mount, reiserfs_read, reiserfs_dir, 0, reiserfs_embed},
1N/A# endif
1N/A# ifdef FSYS_VSTAFS
1N/A {"vstafs", vstafs_mount, vstafs_read, vstafs_dir, 0, 0},
1N/A# endif
1N/A# ifdef FSYS_JFS
1N/A {"jfs", jfs_mount, jfs_read, jfs_dir, 0, jfs_embed},
1N/A# endif
1N/A# ifdef FSYS_XFS
1N/A {"xfs", xfs_mount, xfs_read, xfs_dir, 0, 0},
1N/A# endif
1N/A# ifdef FSYS_UFS
1N/A {"ufs", ufs_mount, ufs_read, ufs_dir, 0, ufs_embed},
1N/A# endif
1N/A# ifdef FSYS_UFS2
1N/A {"ufs2", ufs2_mount, ufs2_read, ufs2_dir, 0, ufs2_embed},
1N/A# endif
1N/A# ifdef FSYS_ZFS
1N/A {"zfs", zfs_mount, zfs_read, zfs_open, 0, zfs_embed},
1N/A# endif
1N/A# ifdef FSYS_ISO9660
1N/A {"iso9660", iso9660_mount, iso9660_read, iso9660_dir, 0, 0},
1N/A# endif
1N/A /* XX FFS should come last as it's superblock is commonly crossing tracks
1N/A on floppies from track 1 to 2, while others only use 1. */
1N/A# ifdef FSYS_FFS
1N/A {"ffs", ffs_mount, ffs_read, ffs_dir, 0, ffs_embed},
1N/A# endif
1N/A {0, 0, 0, 0, 0, 0}
1N/A};
1N/A
1N/A
1N/A/* These have the same format as "boot_drive" and "install_partition", but
1N/A are meant to be working values. */
1N/Aunsigned long current_drive = GRUB_INVALID_DRIVE;
1N/Aunsigned long current_partition;
1N/A
1N/A#ifndef STAGE1_5
1N/A/* The register ESI should contain the address of the partition to be
1N/A used for loading a chain-loader when chain-loading the loader. */
1N/Aunsigned long boot_part_addr = 0;
1N/A#endif
1N/A
1N/A/*
1N/A * Global variables describing details of the filesystem
1N/A */
1N/A
1N/A/* FIXME: BSD evil hack */
1N/A#include "freebsd.h"
1N/Aint bsd_evil_hack;
1N/A
1N/A/* filesystem type */
1N/Aint fsys_type = NUM_FSYS;
1N/A#ifndef NO_BLOCK_FILES
1N/Astatic int block_file = 0;
1N/A#endif /* NO_BLOCK_FILES */
1N/A
1N/A/* these are the translated numbers for the open partition */
1N/Aunsigned long part_start;
1N/Aunsigned long part_length;
1N/A
1N/Aint current_slice;
1N/A
1N/A/* ZFS root filesystem for booting */
1N/Achar current_rootpool[MAXNAMELEN];
1N/Achar current_bootfs[MAXNAMELEN];
1N/Auint64_t current_bootfs_obj;
1N/Achar current_bootpath[MAXPATHLEN];
1N/Achar current_devid[MAXPATHLEN];
1N/Aint is_zfs_mount;
1N/Aunsigned long best_drive;
1N/Aunsigned long best_part;
1N/Aint find_best_root;
1N/A
1N/A/* disk buffer parameters */
1N/Aint buf_drive = -1;
1N/Aunsigned int buf_track;
1N/Astruct geometry buf_geom;
1N/A
1N/A/* filesystem common variables */
1N/Aint filepos;
1N/Aint filemax;
1N/A
1N/Astatic inline unsigned long
1N/Agrub_log2 (unsigned long word)
1N/A{
1N/A asm volatile ("bsfl %1,%0"
1N/A : "=r" (word)
1N/A : "r" (word));
1N/A return word;
1N/A}
1N/A#define log2 grub_log2
1N/A
1N/Aint
1N/Arawread(int drive, unsigned int sector, int byte_offset, int byte_len,
1N/A char *buf)
1N/A{
1N/A int slen, sectors_per_vtrack;
1N/A int sector_size_bits = log2 (buf_geom.sector_size);
1N/A
1N/A if (byte_len <= 0)
1N/A return 1;
1N/A
1N/A while (byte_len > 0 && !errnum)
1N/A {
1N/A int soff, num_sect, size = byte_len;
1N/A unsigned int track;
1N/A char *bufaddr;
1N/A
1N/A /*
1N/A * Check track buffer. If it isn't valid or it is from the
1N/A * wrong disk, then reset the disk geometry.
1N/A */
1N/A if (buf_drive != drive)
1N/A {
1N/A if (get_diskinfo (drive, &buf_geom))
1N/A {
1N/A errnum = ERR_NO_DISK;
1N/A return 0;
1N/A }
1N/A buf_drive = drive;
1N/A buf_track = BUF_CACHE_INVALID;
1N/A sector_size_bits = log2 (buf_geom.sector_size);
1N/A }
1N/A
1N/A slen = ((byte_offset + byte_len + buf_geom.sector_size - 1)
1N/A >> sector_size_bits);
1N/A
1N/A /* Eliminate a buffer overflow. */
1N/A if ((buf_geom.sectors << sector_size_bits) > BUFFERLEN)
1N/A sectors_per_vtrack = (BUFFERLEN >> sector_size_bits);
1N/A else
1N/A sectors_per_vtrack = buf_geom.sectors;
1N/A
1N/A /* Get the first sector of track. */
1N/A soff = sector % sectors_per_vtrack;
1N/A track = sector - soff;
1N/A num_sect = sectors_per_vtrack - soff;
1N/A bufaddr = ((char *) BUFFERADDR
1N/A + (soff << sector_size_bits) + byte_offset);
1N/A
1N/A if (track != buf_track)
1N/A {
1N/A int bios_err, read_len = sectors_per_vtrack;
1N/A unsigned int read_start = track;
1N/A
1N/A /*
1N/A * If there's more than one read in this entire loop, then
1N/A * only make the earlier reads for the portion needed. This
1N/A * saves filling the buffer with data that won't be used!
1N/A */
1N/A if (slen > num_sect)
1N/A {
1N/A read_start = sector;
1N/A read_len = num_sect;
1N/A bufaddr = (char *) BUFFERADDR + byte_offset;
1N/A }
1N/A
1N/A bios_err = biosdisk (BIOSDISK_READ, drive, &buf_geom,
1N/A read_start, read_len, BUFFERSEG);
1N/A if (bios_err)
1N/A {
1N/A buf_track = BUF_CACHE_INVALID;
1N/A
1N/A if (bios_err == BIOSDISK_ERROR_GEOMETRY)
1N/A errnum = ERR_GEOM;
1N/A else
1N/A {
1N/A /*
1N/A * If there was an error, try to load only the
1N/A * required sector(s) rather than failing completely.
1N/A */
1N/A if (slen > num_sect
1N/A || biosdisk (BIOSDISK_READ, drive, &buf_geom,
1N/A sector, slen, BUFFERSEG))
1N/A errnum = ERR_READ;
1N/A
1N/A bufaddr = (char *) BUFFERADDR + byte_offset;
1N/A }
1N/A }
1N/A else
1N/A buf_track = track;
1N/A
1N/A if ((buf_track == 0 || sector == 0)
1N/A && (PC_SLICE_TYPE (BUFFERADDR, 0) == PC_SLICE_TYPE_EZD
1N/A || PC_SLICE_TYPE (BUFFERADDR, 1) == PC_SLICE_TYPE_EZD
1N/A || PC_SLICE_TYPE (BUFFERADDR, 2) == PC_SLICE_TYPE_EZD
1N/A || PC_SLICE_TYPE (BUFFERADDR, 3) == PC_SLICE_TYPE_EZD))
1N/A {
1N/A /* This is a EZD disk map sector 0 to sector 1 */
1N/A if (buf_track == 0 || slen >= 2)
1N/A {
1N/A /* We already read the sector 1, copy it to sector 0 */
1N/A memmove ((char *) BUFFERADDR,
1N/A (char *) BUFFERADDR + buf_geom.sector_size,
1N/A buf_geom.sector_size);
1N/A }
1N/A else
1N/A {
1N/A if (biosdisk (BIOSDISK_READ, drive, &buf_geom,
1N/A 1, 1, BUFFERSEG))
1N/A errnum = ERR_READ;
1N/A }
1N/A }
1N/A }
1N/A
1N/A if (size > ((num_sect << sector_size_bits) - byte_offset))
1N/A size = (num_sect << sector_size_bits) - byte_offset;
1N/A
1N/A /*
1N/A * Instrumentation to tell which sectors were read and used.
1N/A */
1N/A if (disk_read_func)
1N/A {
1N/A unsigned int sector_num = sector;
1N/A int length = buf_geom.sector_size - byte_offset;
1N/A if (length > size)
1N/A length = size;
1N/A (*disk_read_func) (sector_num++, byte_offset, length);
1N/A length = size - length;
1N/A if (length > 0)
1N/A {
1N/A while (length > buf_geom.sector_size)
1N/A {
1N/A (*disk_read_func) (sector_num++, 0, buf_geom.sector_size);
1N/A length -= buf_geom.sector_size;
1N/A }
1N/A (*disk_read_func) (sector_num, 0, length);
1N/A }
1N/A }
1N/A
1N/A grub_memmove (buf, bufaddr, size);
1N/A
1N/A buf += size;
1N/A byte_len -= size;
1N/A sector += num_sect;
1N/A byte_offset = 0;
1N/A }
1N/A
1N/A return (!errnum);
1N/A}
1N/A
1N/A
1N/Aint
1N/Adevread(unsigned int sector, int byte_offset, int byte_len, char *buf)
1N/A{
1N/A /*
1N/A * Check partition boundaries
1N/A */
1N/A if ((sector + ((byte_offset + byte_len - 1) >> SECTOR_BITS))
1N/A >= part_length)
1N/A {
1N/A errnum = ERR_OUTSIDE_PART;
1N/A return 0;
1N/A }
1N/A
1N/A /*
1N/A * Get the read to the beginning of a partition.
1N/A */
1N/A sector += byte_offset >> SECTOR_BITS;
1N/A byte_offset &= SECTOR_SIZE - 1;
1N/A
1N/A#if !defined(STAGE1_5)
1N/A if (disk_read_hook && debug)
1N/A printf ("<%u, %d, %d>", sector, byte_offset, byte_len);
1N/A#endif /* !STAGE1_5 */
1N/A
1N/A /*
1N/A * Call RAWREAD, which is very similar, but:
1N/A *
1N/A * -- It takes an extra parameter, the drive number.
1N/A * -- It requires that "sector" is relative to the beginning
1N/A * of the disk.
1N/A * -- It doesn't handle offsets of more than 511 bytes into the
1N/A * sector.
1N/A */
1N/A return rawread (current_drive, part_start + sector, byte_offset,
1N/A byte_len, buf);
1N/A}
1N/A
1N/A#ifndef STAGE1_5
1N/Aint
1N/Arawwrite(int drive, unsigned int sector, char *buf)
1N/A{
1N/A if (sector == 0)
1N/A {
1N/A if (biosdisk (BIOSDISK_READ, drive, &buf_geom, 0, 1, SCRATCHSEG))
1N/A {
1N/A errnum = ERR_WRITE;
1N/A return 0;
1N/A }
1N/A
1N/A if (PC_SLICE_TYPE (SCRATCHADDR, 0) == PC_SLICE_TYPE_EZD
1N/A || PC_SLICE_TYPE (SCRATCHADDR, 1) == PC_SLICE_TYPE_EZD
1N/A || PC_SLICE_TYPE (SCRATCHADDR, 2) == PC_SLICE_TYPE_EZD
1N/A || PC_SLICE_TYPE (SCRATCHADDR, 3) == PC_SLICE_TYPE_EZD)
1N/A sector = 1;
1N/A }
1N/A
1N/A memmove ((char *) SCRATCHADDR, buf, SECTOR_SIZE);
1N/A if (biosdisk (BIOSDISK_WRITE, drive, &buf_geom,
1N/A sector, 1, SCRATCHSEG))
1N/A {
1N/A errnum = ERR_WRITE;
1N/A return 0;
1N/A }
1N/A
1N/A if (sector - sector % buf_geom.sectors == buf_track)
1N/A /* Clear the cache. */
1N/A buf_track = BUF_CACHE_INVALID;
1N/A
1N/A return 1;
1N/A}
1N/A
1N/Aint
1N/Adevwrite(unsigned int sector, int sector_count, char *buf)
1N/A{
1N/A#if defined(GRUB_UTIL) && defined(__linux__)
1N/A if (current_partition != 0xFFFFFF
1N/A && is_disk_device (device_map, current_drive))
1N/A {
1N/A /* If the grub shell is running under Linux and the user wants to
1N/A embed a Stage 1.5 into a partition instead of a MBR, use system
1N/A calls directly instead of biosdisk, because of the bug in
1N/A Linux. *sigh* */
1N/A return write_to_partition (device_map, current_drive, current_partition,
1N/A sector, sector_count, buf);
1N/A }
1N/A else
1N/A#endif /* GRUB_UTIL && __linux__ */
1N/A {
1N/A int i;
1N/A
1N/A for (i = 0; i < sector_count; i++)
1N/A {
1N/A if (! rawwrite (current_drive, part_start + sector + i,
1N/A buf + (i << SECTOR_BITS)))
1N/A return 0;
1N/A
1N/A }
1N/A return 1;
1N/A }
1N/A}
1N/A
1N/Astatic int
1N/Asane_partition (void)
1N/A{
1N/A /* network drive */
1N/A if (current_drive == NETWORK_DRIVE)
1N/A return 1;
1N/A
1N/A if (!(current_partition & 0xFF000000uL)
1N/A && ((current_drive & 0xFFFFFF7F) < 8
1N/A || current_drive == cdrom_drive)
1N/A && (current_partition & 0xFF) == 0xFF
1N/A && ((current_partition & 0xFF00) == 0xFF00
1N/A || (current_partition & 0xFF00) < 0x1000)
1N/A && ((current_partition >> 16) == 0xFF
1N/A || (current_drive & 0x80)))
1N/A return 1;
1N/A
1N/A errnum = ERR_DEV_VALUES;
1N/A return 0;
1N/A}
1N/A#endif /* ! STAGE1_5 */
1N/A
1N/Astatic void
1N/Aattempt_mount (void)
1N/A{
1N/A#ifndef STAGE1_5
1N/A for (fsys_type = 0; fsys_type < NUM_FSYS; fsys_type++)
1N/A if ((fsys_table[fsys_type].mount_func) ())
1N/A break;
1N/A
1N/A if (fsys_type == NUM_FSYS && errnum == ERR_NONE)
1N/A errnum = ERR_FSYS_MOUNT;
1N/A#else
1N/A fsys_type = 0;
1N/A if ((*(fsys_table[fsys_type].mount_func)) () != 1)
1N/A {
1N/A fsys_type = NUM_FSYS;
1N/A errnum = ERR_FSYS_MOUNT;
1N/A }
1N/A#endif
1N/A}
1N/A
1N/A
1N/A#ifndef STAGE1_5
1N/A/* Turn on the active flag for the partition SAVED_PARTITION in the
1N/A drive SAVED_DRIVE. If an error occurs, return zero, otherwise return
1N/A non-zero. */
1N/Aint
1N/Amake_saved_active (void)
1N/A{
1N/A char mbr[512];
1N/A
1N/A if (saved_drive & 0x80)
1N/A {
1N/A /* Hard disk */
1N/A int part = saved_partition >> 16;
1N/A
1N/A /* If the partition is not a primary partition, the active flag is
1N/A meaningless. (XXX: Really?) */
1N/A if (part > 3)
1N/A {
1N/A errnum = ERR_DEV_VALUES;
1N/A return 0;
1N/A }
1N/A
1N/A /* Read the MBR in the scratch space. */
1N/A if (! rawread (saved_drive, 0, 0, SECTOR_SIZE, mbr))
1N/A return 0;
1N/A
1N/A /* If the partition is an extended partition, setting the active
1N/A flag violates the specification by IBM. */
1N/A if (IS_PC_SLICE_TYPE_EXTENDED (PC_SLICE_TYPE (mbr, part)))
1N/A {
1N/A errnum = ERR_DEV_VALUES;
1N/A return 0;
1N/A }
1N/A
1N/A /* Check if the active flag is disabled. */
1N/A if (PC_SLICE_FLAG (mbr, part) != PC_SLICE_FLAG_BOOTABLE)
1N/A {
1N/A int i;
1N/A
1N/A /* Clear all the active flags in this table. */
1N/A for (i = 0; i < 4; i++)
1N/A PC_SLICE_FLAG (mbr, i) = 0;
1N/A
1N/A /* Set the flag. */
1N/A PC_SLICE_FLAG (mbr, part) = PC_SLICE_FLAG_BOOTABLE;
1N/A
1N/A /* Write back the MBR. */
1N/A if (! rawwrite (saved_drive, 0, mbr))
1N/A return 0;
1N/A }
1N/A }
1N/A else
1N/A {
1N/A /* If the drive is not a hard disk drive, you shouldn't call this
1N/A function. (XXX: Should I just ignore this error?) */
1N/A errnum = ERR_DEV_VALUES;
1N/A return 0;
1N/A }
1N/A
1N/A return 1;
1N/A}
1N/A
1N/A/* Hide/Unhide CURRENT_PARTITION. */
1N/Aint
1N/Aset_partition_hidden_flag (int hidden)
1N/A{
1N/A unsigned long part = 0xFFFFFF;
1N/A unsigned long start, len, offset, ext_offset;
1N/A int entry, type;
1N/A char mbr[512];
1N/A
1N/A /* The drive must be a hard disk. */
1N/A if (! (current_drive & 0x80))
1N/A {
1N/A errnum = ERR_BAD_ARGUMENT;
1N/A return 1;
1N/A }
1N/A
1N/A /* The partition must be a PC slice. */
1N/A if ((current_partition >> 16) == 0xFF
1N/A || (current_partition & 0xFFFF) != 0xFFFF)
1N/A {
1N/A errnum = ERR_BAD_ARGUMENT;
1N/A return 1;
1N/A }
1N/A
1N/A /* Look for the partition. */
1N/A while (next_partition (current_drive, 0xFFFFFF, &part, &type,
1N/A &start, &len, &offset, &entry,
1N/A &ext_offset, mbr))
1N/A {
1N/A if (part == current_partition)
1N/A {
1N/A /* Found. */
1N/A if (hidden)
1N/A PC_SLICE_TYPE (mbr, entry) |= PC_SLICE_TYPE_HIDDEN_FLAG;
1N/A else
1N/A PC_SLICE_TYPE (mbr, entry) &= ~PC_SLICE_TYPE_HIDDEN_FLAG;
1N/A
1N/A /* Write back the MBR to the disk. */
1N/A buf_track = BUF_CACHE_INVALID;
1N/A if (! rawwrite (current_drive, offset, mbr))
1N/A return 1;
1N/A
1N/A /* Succeed. */
1N/A return 0;
1N/A }
1N/A }
1N/A
1N/A return 1;
1N/A}
1N/A
1N/A
1N/Astatic void
1N/Acheck_and_print_mount (void)
1N/A{
1N/A attempt_mount ();
1N/A if (errnum == ERR_FSYS_MOUNT)
1N/A errnum = ERR_NONE;
1N/A if (!errnum)
1N/A print_fsys_type ();
1N/A print_error ();
1N/A}
1N/A#endif /* STAGE1_5 */
1N/A
1N/A
1N/A/* Get the information on next partition on the drive DRIVE.
1N/A The caller must not modify the contents of the arguments when
1N/A iterating this function. The partition representation in GRUB will
1N/A be stored in *PARTITION. Likewise, the partition type in *TYPE, the
1N/A start sector in *START, the length in *LEN, the offset of the
1N/A partition table in *OFFSET, the entry number in the table in *ENTRY,
1N/A the offset of the extended partition in *EXT_OFFSET.
1N/A BUF is used to store a MBR, the boot sector of a partition, or
1N/A a BSD label sector, and it must be at least 512 bytes length.
1N/A When calling this function first, *PARTITION must be initialized to
1N/A 0xFFFFFF. The return value is zero if fails, otherwise non-zero. */
1N/Aint
1N/Anext_partition (unsigned long drive, unsigned long dest,
1N/A unsigned long *partition, int *type,
1N/A unsigned long *start, unsigned long *len,
1N/A unsigned long *offset, int *entry,
1N/A unsigned long *ext_offset, char *buf)
1N/A{
1N/A /* Forward declarations. */
1N/A auto int next_bsd_partition (void);
1N/A auto int next_solaris_partition(void);
1N/A auto int next_pc_slice (void);
1N/A
1N/A /* Get next BSD partition in current PC slice. */
1N/A int next_bsd_partition (void)
1N/A {
1N/A int i;
1N/A int bsd_part_no = (*partition & 0xFF00) >> 8;
1N/A
1N/A /* If this is the first time... */
1N/A if (bsd_part_no == 0xFF)
1N/A {
1N/A /* Check if the BSD label is within current PC slice. */
1N/A if (*len < BSD_LABEL_SECTOR + 1)
1N/A {
1N/A errnum = ERR_BAD_PART_TABLE;
1N/A return 0;
1N/A }
1N/A
1N/A /* Read the BSD label. */
1N/A if (! rawread (drive, *start + BSD_LABEL_SECTOR,
1N/A 0, SECTOR_SIZE, buf))
1N/A return 0;
1N/A
1N/A /* Check if it is valid. */
1N/A if (! BSD_LABEL_CHECK_MAG (buf))
1N/A {
1N/A errnum = ERR_BAD_PART_TABLE;
1N/A return 0;
1N/A }
1N/A
1N/A bsd_part_no = -1;
1N/A }
1N/A
1N/A /* Search next valid BSD partition. */
1N/A for (i = bsd_part_no + 1; i < BSD_LABEL_NPARTS (buf); i++)
1N/A {
1N/A if (BSD_PART_TYPE (buf, i))
1N/A {
1N/A /* Note that *TYPE and *PARTITION were set
1N/A for current PC slice. */
1N/A *type = (BSD_PART_TYPE (buf, i) << 8) | (*type & 0xFF);
1N/A *start = BSD_PART_START (buf, i);
1N/A *len = BSD_PART_LENGTH (buf, i);
1N/A *partition = (*partition & 0xFF00FF) | (i << 8);
1N/A
1N/A#ifndef STAGE1_5
1N/A /* XXX */
1N/A if ((drive & 0x80) && BSD_LABEL_DTYPE (buf) == DTYPE_SCSI)
1N/A bsd_evil_hack = 4;
1N/A#endif /* ! STAGE1_5 */
1N/A
1N/A return 1;
1N/A }
1N/A }
1N/A
1N/A errnum = ERR_NO_PART;
1N/A return 0;
1N/A }
1N/A
1N/A /* Get next Solaris partition in current PC slice. */
1N/A int next_solaris_partition (void)
1N/A {
1N/A static unsigned long pcs_start;
1N/A int i;
1N/A int sol_part_no = (*partition & 0xFF00) >> 8;
1N/A
1N/A /* If this is the first time... */
1N/A if (sol_part_no == 0xFF)
1N/A {
1N/A /* Check if the Solaris label is within current PC slice. */
1N/A if (*len < SOL_LABEL_LOC + 1)
1N/A {
1N/A errnum = ERR_BAD_PART_TABLE;
1N/A return 0;
1N/A }
1N/A
1N/A /* Read the Solaris label. */
1N/A if (! rawread (drive, *start + SOL_LABEL_LOC, 0, SECTOR_SIZE, buf))
1N/A return 0;
1N/A
1N/A /* Check if it is valid. */
1N/A if (! SOL_LABEL_CHECK_MAG (buf))
1N/A {
1N/A errnum = ERR_BAD_PART_TABLE;
1N/A return 0;
1N/A }
1N/A
1N/A sol_part_no = -1;
1N/A pcs_start = *start; /* save the start of pc slice */
1N/A }
1N/A
1N/A /* Search next valid Solaris partition. */
1N/A for (i = sol_part_no + 1; i < SOL_LABEL_NPARTS; i++)
1N/A {
1N/A if (SOL_PART_EXISTS (buf, i))
1N/A {
1N/A /* SOL_PART_START is relative to fdisk partition */
1N/A *start = SOL_PART_START (buf, i) + pcs_start;
1N/A *len = SOL_PART_LENGTH (buf, i);
1N/A *partition = (*partition & 0xFF00FF) | (i << 8);
1N/A
1N/A return 1;
1N/A }
1N/A }
1N/A
1N/A errnum = ERR_NO_PART;
1N/A return 0;
1N/A }
1N/A
1N/A /* Get next PC slice. Be careful of that this function may return
1N/A an empty PC slice (i.e. a partition whose type is zero) as well. */
1N/A int next_pc_slice (void)
1N/A {
1N/A int pc_slice_no = (*partition & 0xFF0000) >> 16;
1N/A
1N/A /* If this is the first time... */
1N/A if (pc_slice_no == 0xFF)
1N/A {
1N/A *offset = 0;
1N/A *ext_offset = 0;
1N/A *entry = -1;
1N/A pc_slice_no = -1;
1N/A }
1N/A
1N/A /* Read the MBR or the boot sector of the extended partition. */
1N/A if (! rawread (drive, *offset, 0, SECTOR_SIZE, buf))
1N/A return 0;
1N/A
1N/A /* Check if it is valid. */
1N/A if (! PC_MBR_CHECK_SIG (buf))
1N/A {
1N/A errnum = ERR_BAD_PART_TABLE;
1N/A return 0;
1N/A }
1N/A
1N/A /* Increase the entry number. */
1N/A (*entry)++;
1N/A
1N/A /* If this is out of current partition table... */
1N/A if (*entry == PC_SLICE_MAX)
1N/A {
1N/A int i;
1N/A
1N/A /* Search the first extended partition in current table. */
1N/A for (i = 0; i < PC_SLICE_MAX; i++)
1N/A {
1N/A if (IS_PC_SLICE_TYPE_EXTENDED (PC_SLICE_TYPE (buf, i)))
1N/A {
1N/A /* Found. Set the new offset and the entry number,
1N/A and restart this function. */
1N/A *offset = *ext_offset + PC_SLICE_START (buf, i);
1N/A if (! *ext_offset)
1N/A *ext_offset = *offset;
1N/A *entry = -1;
1N/A return next_pc_slice ();
1N/A }
1N/A }
1N/A
1N/A errnum = ERR_NO_PART;
1N/A return 0;
1N/A }
1N/A
1N/A *type = PC_SLICE_TYPE (buf, *entry);
1N/A *start = *offset + PC_SLICE_START (buf, *entry);
1N/A *len = PC_SLICE_LENGTH (buf, *entry);
1N/A
1N/A /* The calculation of a PC slice number is complicated, because of
1N/A the rather odd definition of extended partitions. Even worse,
1N/A there is no guarantee that this is consistent with every
1N/A operating systems. Uggh. */
1N/A if (pc_slice_no < PC_SLICE_MAX
1N/A || (! IS_PC_SLICE_TYPE_EXTENDED (*type)
1N/A && *type != PC_SLICE_TYPE_NONE))
1N/A pc_slice_no++;
1N/A
1N/A *partition = (pc_slice_no << 16) | 0xFFFF;
1N/A return 1;
1N/A }
1N/A
1N/A /* Start the body of this function. */
1N/A
1N/A#ifndef STAGE1_5
1N/A if (current_drive == NETWORK_DRIVE)
1N/A return 0;
1N/A#endif
1N/A
1N/A /* check for Solaris partition */
1N/A if (*partition != 0xFFFFFF && IS_PC_SLICE_TYPE_SOLARIS (*type & 0xff))
1N/A {
1N/A if (next_solaris_partition ())
1N/A return 1;
1N/A errnum = ERR_NONE;
1N/A }
1N/A
1N/A /* If previous partition is a BSD partition or a PC slice which
1N/A contains BSD partitions... */
1N/A if ((*partition != 0xFFFFFF && IS_PC_SLICE_TYPE_BSD (*type & 0xff))
1N/A || ! (drive & 0x80))
1N/A {
1N/A if (*type == PC_SLICE_TYPE_NONE)
1N/A *type = PC_SLICE_TYPE_FREEBSD;
1N/A
1N/A /* Get next BSD partition, if any. */
1N/A if (next_bsd_partition ())
1N/A return 1;
1N/A
1N/A /* If the destination partition is a BSD partition and current
1N/A BSD partition has any error, abort the operation. */
1N/A if ((dest & 0xFF00) != 0xFF00
1N/A && ((dest & 0xFF0000) == 0xFF0000
1N/A || (dest & 0xFF0000) == (*partition & 0xFF0000)))
1N/A return 0;
1N/A
1N/A /* Ignore the error. */
1N/A errnum = ERR_NONE;
1N/A }
1N/A
1N/A return next_pc_slice ();
1N/A}
1N/A
1N/A#ifndef STAGE1_5
1N/Astatic unsigned long cur_part_offset;
1N/Astatic unsigned long cur_part_addr;
1N/A#endif
1N/A
1N/A/* Open a partition. */
1N/Aint
1N/Areal_open_partition (int flags)
1N/A{
1N/A unsigned long dest_partition = current_partition;
1N/A unsigned long part_offset;
1N/A unsigned long ext_offset;
1N/A int entry;
1N/A char buf[SECTOR_SIZE];
1N/A int unix_part, pc_slice;
1N/A
1N/A /* For simplicity. */
1N/A auto int next (void);
1N/A int next (void)
1N/A {
1N/A int ret = next_partition (current_drive, dest_partition,
1N/A &current_partition, &current_slice,
1N/A &part_start, &part_length,
1N/A &part_offset, &entry, &ext_offset, buf);
1N/A unix_part = (current_partition >> 8) & 0xFF;
1N/A pc_slice = current_partition >> 16;
1N/A return ret;
1N/A }
1N/A
1N/A#ifndef STAGE1_5
1N/A /* network drive */
1N/A if (current_drive == NETWORK_DRIVE)
1N/A return 1;
1N/A
1N/A if (! sane_partition ())
1N/A return 0;
1N/A#endif
1N/A
1N/A bsd_evil_hack = 0;
1N/A current_slice = 0;
1N/A part_start = 0;
1N/A
1N/A /* Make sure that buf_geom is valid. */
1N/A if (buf_drive != current_drive)
1N/A {
1N/A if (get_diskinfo (current_drive, &buf_geom))
1N/A {
1N/A errnum = ERR_NO_DISK;
1N/A return 0;
1N/A }
1N/A buf_drive = current_drive;
1N/A buf_track = BUF_CACHE_INVALID;
1N/A }
1N/A part_length =
1N/A (buf_geom.total_sectors > MAXUINT) ? MAXUINT : buf_geom.total_sectors;
1N/A
1N/A /* If this is the whole disk, return here. */
1N/A if (! flags && current_partition == 0xFFFFFF)
1N/A return 1;
1N/A
1N/A if (flags)
1N/A dest_partition = 0xFFFFFF;
1N/A
1N/A /* Initialize CURRENT_PARTITION for next_partition. */
1N/A current_partition = 0xFFFFFF;
1N/A
1N/A while (next ())
1N/A {
1N/A#ifndef STAGE1_5
1N/A loop_start:
1N/A
1N/A cur_part_offset = part_offset;
1N/A cur_part_addr = BOOT_PART_TABLE + (entry << 4);
1N/A#endif /* ! STAGE1_5 */
1N/A
1N/A /* If this is a valid partition... */
1N/A if (current_slice)
1N/A {
1N/A#ifndef STAGE1_5
1N/A /* Display partition information. */
1N/A if (flags && ! IS_PC_SLICE_TYPE_EXTENDED (current_slice))
1N/A {
1N/A if (! do_completion)
1N/A {
1N/A if (current_drive & 0x80)
1N/A grub_printf (" Partition num: %d, ",
1N/A current_partition >> 16);
1N/A
1N/A if (! IS_PC_SLICE_TYPE_BSD (current_slice) &&
1N/A ! IS_PC_SLICE_TYPE_SOLARIS (current_slice))
1N/A check_and_print_mount ();
1N/A else
1N/A {
1N/A int got_part = 0;
1N/A int saved_slice = current_slice;
1N/A
1N/A while (next ())
1N/A {
1N/A if (unix_part == 0xFF)
1N/A break;
1N/A
1N/A if (! got_part)
1N/A {
1N/A grub_printf ("[BSD/SOLARIS sub-partitions immediately follow]\n");
1N/A got_part = 1;
1N/A }
1N/A
1N/A grub_printf (" BSD/SOLARIS Partition num: \'%c\', ",
1N/A unix_part + 'a');
1N/A check_and_print_mount ();
1N/A }
1N/A
1N/A if (! got_part)
1N/A grub_printf (" No BSD/SOLARIS sub-partition found, partition type 0x%x\n",
1N/A saved_slice);
1N/A
1N/A if (errnum)
1N/A {
1N/A errnum = ERR_NONE;
1N/A break;
1N/A }
1N/A
1N/A goto loop_start;
1N/A }
1N/A }
1N/A else
1N/A {
1N/A if (unix_part != 0xFF)
1N/A {
1N/A char str[16];
1N/A
1N/A if (! (current_drive & 0x80)
1N/A || (dest_partition >> 16) == pc_slice)
1N/A grub_sprintf (str, "%c)", unix_part + 'a');
1N/A else
1N/A grub_sprintf (str, "%d,%c)",
1N/A pc_slice, unix_part + 'a');
1N/A print_a_completion (str);
1N/A }
1N/A else if (! IS_PC_SLICE_TYPE_BSD (current_slice) &&
1N/A ! IS_PC_SLICE_TYPE_SOLARIS (current_slice))
1N/A {
1N/A char str[8];
1N/A
1N/A grub_sprintf (str, "%d)", pc_slice);
1N/A print_a_completion (str);
1N/A }
1N/A }
1N/A }
1N/A
1N/A errnum = ERR_NONE;
1N/A#endif /* ! STAGE1_5 */
1N/A
1N/A /* Check if this is the destination partition. */
1N/A if (! flags
1N/A && (dest_partition == current_partition
1N/A || ((dest_partition >> 16) == 0xFF
1N/A && ((dest_partition >> 8) & 0xFF) == unix_part)))
1N/A return 1;
1N/A }
1N/A }
1N/A
1N/A#ifndef STAGE1_5
1N/A if (flags)
1N/A {
1N/A if (! (current_drive & 0x80))
1N/A {
1N/A current_partition = 0xFFFFFF;
1N/A check_and_print_mount ();
1N/A }
1N/A
1N/A errnum = ERR_NONE;
1N/A return 1;
1N/A }
1N/A#endif /* ! STAGE1_5 */
1N/A
1N/A return 0;
1N/A}
1N/A
1N/A
1N/Aint
1N/Aopen_partition (void)
1N/A{
1N/A return real_open_partition (0);
1N/A}
1N/A
1N/A
1N/A#ifndef STAGE1_5
1N/A/* XX used for device completion in 'set_device' and 'print_completions' */
1N/Astatic int incomplete, disk_choice;
1N/Astatic enum
1N/A{
1N/A PART_UNSPECIFIED = 0,
1N/A PART_DISK,
1N/A PART_CHOSEN,
1N/A}
1N/Apart_choice;
1N/A#endif /* ! STAGE1_5 */
1N/A
1N/Achar *
1N/Aset_device (char *device)
1N/A{
1N/A#ifdef STAGE1_5
1N/A /* In Stage 1.5, the first 4 bytes of FILENAME has a device number. */
1N/A unsigned long dev = *((unsigned long *) device);
1N/A int drive = (dev >> 24) & 0xFF;
1N/A int partition = dev & 0xFFFFFF;
1N/A
1N/A /* If DRIVE is disabled, use SAVED_DRIVE instead. */
1N/A if (drive == GRUB_INVALID_DRIVE)
1N/A current_drive = saved_drive;
1N/A else
1N/A current_drive = drive;
1N/A
1N/A /* The `partition' part must always have a valid number. */
1N/A current_partition = partition;
1N/A
1N/A return device + sizeof (unsigned long);
1N/A
1N/A#else /* ! STAGE1_5 */
1N/A
1N/A int result = 0;
1N/A
1N/A incomplete = 0;
1N/A disk_choice = 1;
1N/A part_choice = PART_UNSPECIFIED;
1N/A current_drive = saved_drive;
1N/A current_partition = 0xFFFFFF;
1N/A
1N/A if (*device == '(' && !*(device + 1))
1N/A /* user has given '(' only, let disk_choice handle what disks we have */
1N/A return device + 1;
1N/A
1N/A if (*device == '(' && *(++device))
1N/A {
1N/A if (*device != ',' && *device != ')')
1N/A {
1N/A char ch = *device;
1N/A#ifdef SUPPORT_NETBOOT
1N/A if (*device == 'f' || *device == 'h'
1N/A || (*device == 'n' && network_ready)
1N/A || (*device == 'c' && cdrom_drive != GRUB_INVALID_DRIVE))
1N/A#else
1N/A if (*device == 'f' || *device == 'h'
1N/A || (*device == 'c' && cdrom_drive != GRUB_INVALID_DRIVE))
1N/A#endif /* SUPPORT_NETBOOT */
1N/A {
1N/A /* user has given '([fhn]', check for resp. add 'd' and
1N/A let disk_choice handle what disks we have */
1N/A if (!*(device + 1))
1N/A {
1N/A device++;
1N/A *device++ = 'd';
1N/A *device = '\0';
1N/A return device;
1N/A }
1N/A else if (*(device + 1) == 'd' && !*(device + 2))
1N/A return device + 2;
1N/A }
1N/A
1N/A if ((*device == 'f'
1N/A || *device == 'h'
1N/A#ifdef SUPPORT_NETBOOT
1N/A || (*device == 'n' && network_ready)
1N/A#endif
1N/A || (*device == 'c' && cdrom_drive != GRUB_INVALID_DRIVE))
1N/A && (device += 2, (*(device - 1) != 'd')))
1N/A errnum = ERR_NUMBER_PARSING;
1N/A
1N/A#ifdef SUPPORT_NETBOOT
1N/A if (ch == 'n' && network_ready)
1N/A current_drive = NETWORK_DRIVE;
1N/A else
1N/A#endif /* SUPPORT_NETBOOT */
1N/A {
1N/A if (ch == 'c' && cdrom_drive != GRUB_INVALID_DRIVE)
1N/A current_drive = cdrom_drive;
1N/A else
1N/A {
1N/A safe_parse_maxint (&device, (int *) &current_drive);
1N/A
1N/A disk_choice = 0;
1N/A if (ch == 'h')
1N/A current_drive += 0x80;
1N/A }
1N/A }
1N/A }
1N/A
1N/A if (errnum)
1N/A return 0;
1N/A
1N/A if (*device == ')')
1N/A {
1N/A part_choice = PART_CHOSEN;
1N/A result = 1;
1N/A }
1N/A else if (*device == ',')
1N/A {
1N/A /* Either an absolute PC, BSD, or Solaris partition. */
1N/A disk_choice = 0;
1N/A part_choice ++;
1N/A device++;
1N/A
1N/A if (*device >= '0' && *device <= '9')
1N/A {
1N/A part_choice ++;
1N/A current_partition = 0;
1N/A
1N/A if (!(current_drive & 0x80)
1N/A || !safe_parse_maxint (&device, (int *) &current_partition)
1N/A || current_partition > 254)
1N/A {
1N/A errnum = ERR_DEV_FORMAT;
1N/A return 0;
1N/A }
1N/A
1N/A current_partition = (current_partition << 16) + 0xFFFF;
1N/A
1N/A if (*device == ',')
1N/A device++;
1N/A
1N/A if (*device >= 'a' && *device <= 'p')
1N/A {
1N/A current_partition = (((*(device++) - 'a') << 8)
1N/A | (current_partition & 0xFF00FF));
1N/A }
1N/A }
1N/A else if (*device >= 'a' && *device <= 'p')
1N/A {
1N/A part_choice ++;
1N/A current_partition = ((*(device++) - 'a') << 8) | 0xFF00FF;
1N/A }
1N/A
1N/A if (*device == ')')
1N/A {
1N/A if (part_choice == PART_DISK)
1N/A {
1N/A current_partition = saved_partition;
1N/A part_choice ++;
1N/A }
1N/A
1N/A result = 1;
1N/A }
1N/A }
1N/A }
1N/A
1N/A if (! sane_partition ())
1N/A return 0;
1N/A
1N/A if (result)
1N/A return device + 1;
1N/A else
1N/A {
1N/A if (!*device)
1N/A incomplete = 1;
1N/A errnum = ERR_DEV_FORMAT;
1N/A }
1N/A
1N/A return 0;
1N/A
1N/A#endif /* ! STAGE1_5 */
1N/A}
1N/A
1N/A/*
1N/A * This performs a "mount" on the current device, both drive and partition
1N/A * number.
1N/A */
1N/A
1N/Aint
1N/Aopen_device (void)
1N/A{
1N/A if (open_partition ())
1N/A attempt_mount ();
1N/A
1N/A if (errnum != ERR_NONE)
1N/A return 0;
1N/A
1N/A return 1;
1N/A}
1N/A
1N/A
1N/A#ifndef STAGE1_5
1N/Aint
1N/Aset_bootdev (int hdbias)
1N/A{
1N/A int i, j;
1N/A
1N/A /* Copy the boot partition information to 0x7be-0x7fd for chain-loading. */
1N/A if ((saved_drive & 0x80) && cur_part_addr)
1N/A {
1N/A if (rawread (saved_drive, cur_part_offset,
1N/A 0, SECTOR_SIZE, (char *) SCRATCHADDR))
1N/A {
1N/A char *dst, *src;
1N/A
1N/A /* Need only the partition table.
1N/A XXX: We cannot use grub_memmove because BOOT_PART_TABLE
1N/A (0x07be) is less than 0x1000. */
1N/A dst = (char *) BOOT_PART_TABLE;
1N/A src = (char *) SCRATCHADDR + BOOTSEC_PART_OFFSET;
1N/A while (dst < (char *) BOOT_PART_TABLE + BOOTSEC_PART_LENGTH)
1N/A *dst++ = *src++;
1N/A
1N/A /* Set the active flag of the booted partition. */
1N/A for (i = 0; i < 4; i++)
1N/A PC_SLICE_FLAG (BOOT_PART_TABLE, i) = 0;
1N/A
1N/A *((unsigned char *) cur_part_addr) = PC_SLICE_FLAG_BOOTABLE;
1N/A boot_part_addr = cur_part_addr;
1N/A }
1N/A else
1N/A return 0;
1N/A }
1N/A
1N/A /*
1N/A * Set BSD boot device.
1N/A */
1N/A i = (saved_partition >> 16) + 2;
1N/A if (saved_partition == 0xFFFFFF)
1N/A i = 1;
1N/A else if ((saved_partition >> 16) == 0xFF)
1N/A i = 0;
1N/A
1N/A /* FIXME: extremely evil hack!!! */
1N/A j = 2;
1N/A if (saved_drive & 0x80)
1N/A j = bsd_evil_hack;
1N/A
1N/A return MAKEBOOTDEV (j, (i >> 4), (i & 0xF),
1N/A ((saved_drive - hdbias) & 0x7F),
1N/A ((saved_partition >> 8) & 0xFF));
1N/A}
1N/A#endif /* STAGE1_5 */
1N/A
1N/A
1N/Astatic char *
1N/Asetup_part (char *filename)
1N/A{
1N/A#ifdef STAGE1_5
1N/A
1N/A if (! (filename = set_device (filename)))
1N/A {
1N/A current_drive = GRUB_INVALID_DRIVE;
1N/A return 0;
1N/A }
1N/A
1N/A# ifndef NO_BLOCK_FILES
1N/A if (*filename != '/')
1N/A open_partition ();
1N/A else
1N/A# endif /* ! NO_BLOCK_FILES */
1N/A open_device ();
1N/A
1N/A#else /* ! STAGE1_5 */
1N/A
1N/A if (*filename == '(')
1N/A {
1N/A if ((filename = set_device (filename)) == 0)
1N/A {
1N/A current_drive = GRUB_INVALID_DRIVE;
1N/A return 0;
1N/A }
1N/A# ifndef NO_BLOCK_FILES
1N/A if (*filename != '/' && current_drive != NETWORK_DRIVE)
1N/A open_partition ();
1N/A else
1N/A# endif /* ! NO_BLOCK_FILES */
1N/A open_device ();
1N/A }
1N/A else if (saved_drive != current_drive
1N/A || saved_partition != current_partition
1N/A || (*filename == '/' && fsys_type == NUM_FSYS)
1N/A || buf_drive == -1)
1N/A {
1N/A current_drive = saved_drive;
1N/A current_partition = saved_partition;
1N/A /* allow for the error case of "no filesystem" after the partition
1N/A is found. This makes block files work fine on no filesystem */
1N/A# ifndef NO_BLOCK_FILES
1N/A if (*filename != '/' && current_drive != NETWORK_DRIVE)
1N/A open_partition ();
1N/A else
1N/A# endif /* ! NO_BLOCK_FILES */
1N/A open_device ();
1N/A }
1N/A
1N/A#endif /* ! STAGE1_5 */
1N/A
1N/A if (errnum && (*filename == '/' || errnum != ERR_FSYS_MOUNT))
1N/A return 0;
1N/A else
1N/A errnum = 0;
1N/A
1N/A#ifndef STAGE1_5
1N/A if (!sane_partition ())
1N/A return 0;
1N/A#endif
1N/A
1N/A return filename;
1N/A}
1N/A
1N/A
1N/A#ifndef STAGE1_5
1N/A/*
1N/A * This prints the filesystem type or gives relevant information.
1N/A */
1N/A
1N/Avoid
1N/Aprint_fsys_type (void)
1N/A{
1N/A if (! do_completion)
1N/A {
1N/A grub_printf (" Filesystem type ");
1N/A
1N/A if (fsys_type != NUM_FSYS)
1N/A grub_printf ("is %s, ", fsys_table[fsys_type].name);
1N/A else
1N/A grub_printf ("unknown, ");
1N/A
1N/A if (current_partition == 0xFFFFFF)
1N/A grub_printf ("using whole disk\n");
1N/A else
1N/A grub_printf ("partition type 0x%x\n", current_slice & 0xFF);
1N/A }
1N/A}
1N/A#endif /* STAGE1_5 */
1N/A
1N/A#ifndef STAGE1_5
1N/A/* If DO_COMPLETION is true, just print NAME. Otherwise save the unique
1N/A part into UNIQUE_STRING. */
1N/Avoid
1N/Aprint_a_completion (char *name)
1N/A{
1N/A /* If NAME is "." or "..", do not count it. */
1N/A if (grub_strcmp (name, ".") == 0 || grub_strcmp (name, "..") == 0)
1N/A return;
1N/A
1N/A if (do_completion)
1N/A {
1N/A char *buf = unique_string;
1N/A
1N/A if (! unique)
1N/A while ((*buf++ = *name++))
1N/A ;
1N/A else
1N/A {
1N/A while (*buf && (*buf == *name))
1N/A {
1N/A buf++;
1N/A name++;
1N/A }
1N/A /* mismatch, strip it. */
1N/A *buf = '\0';
1N/A }
1N/A }
1N/A else
1N/A grub_printf (" %s", name);
1N/A
1N/A unique++;
1N/A}
1N/A
1N/A/*
1N/A * This lists the possible completions of a device string, filename, or
1N/A * any sane combination of the two.
1N/A */
1N/A
1N/Aint
1N/Aprint_completions (int is_filename, int is_completion)
1N/A{
1N/A char *buf = (char *) COMPLETION_BUF;
1N/A char *ptr = buf;
1N/A
1N/A unique_string = (char *) UNIQUE_BUF;
1N/A *unique_string = 0;
1N/A unique = 0;
1N/A do_completion = is_completion;
1N/A
1N/A if (! is_filename)
1N/A {
1N/A /* Print the completions of builtin commands. */
1N/A struct builtin **builtin;
1N/A
1N/A if (! is_completion)
1N/A grub_printf (" Possible commands are:");
1N/A
1N/A for (builtin = builtin_table; (*builtin); builtin++)
1N/A {
1N/A /* If *BUILTIN cannot be run in the command-line, skip it. */
1N/A if (! ((*builtin)->flags & BUILTIN_CMDLINE))
1N/A continue;
1N/A
1N/A if (substring (buf, (*builtin)->name) <= 0)
1N/A print_a_completion ((*builtin)->name);
1N/A }
1N/A
1N/A if (is_completion && *unique_string)
1N/A {
1N/A if (unique == 1)
1N/A {
1N/A char *u = unique_string + grub_strlen (unique_string);
1N/A
1N/A *u++ = ' ';
1N/A *u = 0;
1N/A }
1N/A
1N/A grub_strcpy (buf, unique_string);
1N/A }
1N/A
1N/A if (! is_completion)
1N/A grub_putchar ('\n');
1N/A
1N/A print_error ();
1N/A do_completion = 0;
1N/A if (errnum)
1N/A return -1;
1N/A else
1N/A return unique - 1;
1N/A }
1N/A
1N/A if (*buf == '/' || (ptr = set_device (buf)) || incomplete)
1N/A {
1N/A errnum = 0;
1N/A
1N/A if (*buf == '(' && (incomplete || ! *ptr))
1N/A {
1N/A if (! part_choice)
1N/A {
1N/A /* disk completions */
1N/A int disk_no, i, j;
1N/A struct geometry geom;
1N/A
1N/A if (! is_completion)
1N/A grub_printf (" Possible disks are: ");
1N/A
1N/A if (!ptr
1N/A || *(ptr-1) != 'd'
1N/A#ifdef SUPPORT_NETBOOT
1N/A || *(ptr-2) != 'n'
1N/A#endif /* SUPPORT_NETBOOT */
1N/A || *(ptr-2) != 'c')
1N/A {
1N/A for (i = (ptr && (*(ptr-1) == 'd' && *(ptr-2) == 'h') ? 1:0);
1N/A i < (ptr && (*(ptr-1) == 'd' && *(ptr-2) == 'f') ? 1:2);
1N/A i++)
1N/A {
1N/A for (j = 0; j < 8; j++)
1N/A {
1N/A disk_no = (i * 0x80) + j;
1N/A if ((disk_choice || disk_no == current_drive)
1N/A && ! get_diskinfo (disk_no, &geom))
1N/A {
1N/A char dev_name[8];
1N/A
1N/A grub_sprintf (dev_name, "%cd%d", i ? 'h':'f', j);
1N/A print_a_completion (dev_name);
1N/A }
1N/A }
1N/A }
1N/A }
1N/A
1N/A if (cdrom_drive != GRUB_INVALID_DRIVE
1N/A && (disk_choice || cdrom_drive == current_drive)
1N/A && (!ptr
1N/A || *(ptr-1) == '('
1N/A || (*(ptr-1) == 'd' && *(ptr-2) == 'c')))
1N/A print_a_completion ("cd");
1N/A
1N/A# ifdef SUPPORT_NETBOOT
1N/A if (network_ready
1N/A && (disk_choice || NETWORK_DRIVE == current_drive)
1N/A && (!ptr
1N/A || *(ptr-1) == '('
1N/A || (*(ptr-1) == 'd' && *(ptr-2) == 'n')))
1N/A print_a_completion ("nd");
1N/A# endif /* SUPPORT_NETBOOT */
1N/A
1N/A if (is_completion && *unique_string)
1N/A {
1N/A ptr = buf;
1N/A while (*ptr != '(')
1N/A ptr--;
1N/A ptr++;
1N/A grub_strcpy (ptr, unique_string);
1N/A if (unique == 1)
1N/A {
1N/A ptr += grub_strlen (ptr);
1N/A if (*unique_string == 'h')
1N/A {
1N/A *ptr++ = ',';
1N/A *ptr = 0;
1N/A }
1N/A else
1N/A {
1N/A *ptr++ = ')';
1N/A *ptr = 0;
1N/A }
1N/A }
1N/A }
1N/A
1N/A if (! is_completion)
1N/A grub_putchar ('\n');
1N/A }
1N/A else
1N/A {
1N/A /* partition completions */
1N/A if (part_choice == PART_CHOSEN
1N/A && open_partition ()
1N/A && ! IS_PC_SLICE_TYPE_BSD (current_slice))
1N/A {
1N/A unique = 1;
1N/A ptr = buf + grub_strlen (buf);
1N/A if (*(ptr - 1) != ')')
1N/A {
1N/A *ptr++ = ')';
1N/A *ptr = 0;
1N/A }
1N/A }
1N/A else
1N/A {
1N/A if (! is_completion)
1N/A grub_printf (" Possible partitions are:\n");
1N/A real_open_partition (1);
1N/A
1N/A if (is_completion && *unique_string)
1N/A {
1N/A ptr = buf;
1N/A while (*ptr++ != ',')
1N/A ;
1N/A grub_strcpy (ptr, unique_string);
1N/A }
1N/A }
1N/A }
1N/A }
1N/A else if (ptr && *ptr == '/')
1N/A {
1N/A /* filename completions */
1N/A if (! is_completion)
1N/A grub_printf (" Possible files are:");
1N/A
1N/A dir (buf);
1N/A
1N/A if (is_completion && *unique_string)
1N/A {
1N/A ptr += grub_strlen (ptr);
1N/A while (*ptr != '/')
1N/A ptr--;
1N/A ptr++;
1N/A
1N/A grub_strcpy (ptr, unique_string);
1N/A
1N/A if (unique == 1)
1N/A {
1N/A ptr += grub_strlen (unique_string);
1N/A
1N/A /* Check if the file UNIQUE_STRING is a directory. */
1N/A *ptr = '/';
1N/A *(ptr + 1) = 0;
1N/A
1N/A dir (buf);
1N/A
1N/A /* Restore the original unique value. */
1N/A unique = 1;
1N/A
1N/A if (errnum)
1N/A {
1N/A /* Regular file */
1N/A errnum = 0;
1N/A *ptr = ' ';
1N/A *(ptr + 1) = 0;
1N/A }
1N/A }
1N/A }
1N/A
1N/A if (! is_completion)
1N/A grub_putchar ('\n');
1N/A }
1N/A else
1N/A errnum = ERR_BAD_FILENAME;
1N/A }
1N/A
1N/A print_error ();
1N/A do_completion = 0;
1N/A if (errnum)
1N/A return -1;
1N/A else
1N/A return unique - 1;
1N/A}
1N/A#endif /* STAGE1_5 */
1N/A
1N/A
1N/A/*
1N/A * This is the generic file open function.
1N/A */
1N/A
1N/Aint
1N/Agrub_open (char *filename)
1N/A{
1N/A#ifndef NO_DECOMPRESSION
1N/A compressed_file = 0;
1N/A#endif /* NO_DECOMPRESSION */
1N/A
1N/A /* if any "dir" function uses/sets filepos, it must
1N/A set it to zero before returning if opening a file! */
1N/A filepos = 0;
1N/A
1N/A if (!(filename = setup_part (filename)))
1N/A return 0;
1N/A
1N/A#ifndef NO_BLOCK_FILES
1N/A block_file = 0;
1N/A#endif /* NO_BLOCK_FILES */
1N/A
1N/A /* This accounts for partial filesystem implementations. */
1N/A fsmax = MAXINT;
1N/A
1N/A if (*filename != '/' && current_drive != NETWORK_DRIVE)
1N/A {
1N/A#ifndef NO_BLOCK_FILES
1N/A char *ptr = filename;
1N/A int tmp, list_addr = BLK_BLKLIST_START;
1N/A filemax = 0;
1N/A
1N/A while (list_addr < BLK_MAX_ADDR)
1N/A {
1N/A tmp = 0;
1N/A safe_parse_maxint (&ptr, &tmp);
1N/A errnum = 0;
1N/A
1N/A if (*ptr != '+')
1N/A {
1N/A if ((*ptr && *ptr != '/' && !isspace (*ptr))
1N/A || tmp == 0 || tmp > filemax)
1N/A errnum = ERR_BAD_FILENAME;
1N/A else
1N/A filemax = tmp;
1N/A
1N/A break;
1N/A }
1N/A
1N/A /* since we use the same filesystem buffer, mark it to
1N/A be remounted */
1N/A fsys_type = NUM_FSYS;
1N/A
1N/A BLK_BLKSTART (list_addr) = tmp;
1N/A ptr++;
1N/A
1N/A if (!safe_parse_maxint (&ptr, &tmp)
1N/A || tmp == 0
1N/A || (*ptr && *ptr != ',' && *ptr != '/' && !isspace (*ptr)))
1N/A {
1N/A errnum = ERR_BAD_FILENAME;
1N/A break;
1N/A }
1N/A
1N/A BLK_BLKLENGTH (list_addr) = tmp;
1N/A
1N/A filemax += (tmp * SECTOR_SIZE);
1N/A list_addr += BLK_BLKLIST_INC_VAL;
1N/A
1N/A if (*ptr != ',')
1N/A break;
1N/A
1N/A ptr++;
1N/A }
1N/A
1N/A if (list_addr < BLK_MAX_ADDR && ptr != filename && !errnum)
1N/A {
1N/A block_file = 1;
1N/A BLK_CUR_FILEPOS = 0;
1N/A BLK_CUR_BLKLIST = BLK_BLKLIST_START;
1N/A BLK_CUR_BLKNUM = 0;
1N/A
1N/A#ifndef NO_DECOMPRESSION
1N/A return gunzip_test_header ();
1N/A#else /* NO_DECOMPRESSION */
1N/A return 1;
1N/A#endif /* NO_DECOMPRESSION */
1N/A }
1N/A#else /* NO_BLOCK_FILES */
1N/A errnum = ERR_BAD_FILENAME;
1N/A#endif /* NO_BLOCK_FILES */
1N/A }
1N/A
1N/A if (!errnum && fsys_type == NUM_FSYS)
1N/A errnum = ERR_FSYS_MOUNT;
1N/A
1N/A# ifndef STAGE1_5
1N/A /* set "dir" function to open a file */
1N/A print_possibilities = 0;
1N/A# endif
1N/A
1N/A if (!errnum && (*(fsys_table[fsys_type].dir_func)) (filename))
1N/A {
1N/A#ifndef NO_DECOMPRESSION
1N/A return gunzip_test_header ();
1N/A#else /* NO_DECOMPRESSION */
1N/A return 1;
1N/A#endif /* NO_DECOMPRESSION */
1N/A }
1N/A
1N/A return 0;
1N/A}
1N/A
1N/A
1N/Aint
1N/Agrub_read (char *buf, int len)
1N/A{
1N/A /* Make sure "filepos" is a sane value */
1N/A if ((filepos < 0) || (filepos > filemax))
1N/A filepos = filemax;
1N/A
1N/A /* Make sure "len" is a sane value */
1N/A if ((len < 0) || (len > (filemax - filepos)))
1N/A len = filemax - filepos;
1N/A
1N/A /* if target file position is past the end of
1N/A the supported/configured filesize, then
1N/A there is an error */
1N/A if (filepos + len > fsmax)
1N/A {
1N/A errnum = ERR_FILELENGTH;
1N/A return 0;
1N/A }
1N/A
1N/A#ifndef NO_DECOMPRESSION
1N/A if (compressed_file)
1N/A return gunzip_read (buf, len);
1N/A#endif /* NO_DECOMPRESSION */
1N/A
1N/A#ifndef NO_BLOCK_FILES
1N/A if (block_file)
1N/A {
1N/A int size, off, ret = 0;
1N/A
1N/A while (len && !errnum)
1N/A {
1N/A /* we may need to look for the right block in the list(s) */
1N/A if (filepos < BLK_CUR_FILEPOS)
1N/A {
1N/A BLK_CUR_FILEPOS = 0;
1N/A BLK_CUR_BLKLIST = BLK_BLKLIST_START;
1N/A BLK_CUR_BLKNUM = 0;
1N/A }
1N/A
1N/A /* run BLK_CUR_FILEPOS up to filepos */
1N/A while (filepos > BLK_CUR_FILEPOS)
1N/A {
1N/A if ((filepos - (BLK_CUR_FILEPOS & ~(SECTOR_SIZE - 1)))
1N/A >= SECTOR_SIZE)
1N/A {
1N/A BLK_CUR_FILEPOS += SECTOR_SIZE;
1N/A BLK_CUR_BLKNUM++;
1N/A
1N/A if (BLK_CUR_BLKNUM >= BLK_BLKLENGTH (BLK_CUR_BLKLIST))
1N/A {
1N/A BLK_CUR_BLKLIST += BLK_BLKLIST_INC_VAL;
1N/A BLK_CUR_BLKNUM = 0;
1N/A }
1N/A }
1N/A else
1N/A BLK_CUR_FILEPOS = filepos;
1N/A }
1N/A
1N/A off = filepos & (SECTOR_SIZE - 1);
1N/A size = ((BLK_BLKLENGTH (BLK_CUR_BLKLIST) - BLK_CUR_BLKNUM)
1N/A * SECTOR_SIZE) - off;
1N/A if (size > len)
1N/A size = len;
1N/A
1N/A disk_read_func = disk_read_hook;
1N/A
1N/A /* read current block and put it in the right place in memory */
1N/A devread (BLK_BLKSTART (BLK_CUR_BLKLIST) + BLK_CUR_BLKNUM,
1N/A off, size, buf);
1N/A
1N/A disk_read_func = NULL;
1N/A
1N/A len -= size;
1N/A filepos += size;
1N/A ret += size;
1N/A buf += size;
1N/A }
1N/A
1N/A if (errnum)
1N/A ret = 0;
1N/A
1N/A return ret;
1N/A }
1N/A#endif /* NO_BLOCK_FILES */
1N/A
1N/A if (fsys_type == NUM_FSYS)
1N/A {
1N/A errnum = ERR_FSYS_MOUNT;
1N/A return 0;
1N/A }
1N/A
1N/A return (*(fsys_table[fsys_type].read_func)) (buf, len);
1N/A}
1N/A
1N/A#ifndef STAGE1_5
1N/A/* Reposition a file offset. */
1N/Aint
1N/Agrub_seek (int offset)
1N/A{
1N/A if (offset > filemax || offset < 0)
1N/A return -1;
1N/A
1N/A filepos = offset;
1N/A return offset;
1N/A}
1N/A
1N/Aint
1N/Adir (char *dirname)
1N/A{
1N/A#ifndef NO_DECOMPRESSION
1N/A compressed_file = 0;
1N/A#endif /* NO_DECOMPRESSION */
1N/A
1N/A if (!(dirname = setup_part (dirname)))
1N/A return 0;
1N/A
1N/A if (*dirname != '/')
1N/A errnum = ERR_BAD_FILENAME;
1N/A
1N/A if (fsys_type == NUM_FSYS)
1N/A errnum = ERR_FSYS_MOUNT;
1N/A
1N/A if (errnum)
1N/A return 0;
1N/A
1N/A /* set "dir" function to list completions */
1N/A print_possibilities = 1;
1N/A
1N/A return (*(fsys_table[fsys_type].dir_func)) (dirname);
1N/A}
1N/A#endif /* STAGE1_5 */
1N/A
1N/Avoid
1N/Agrub_close (void)
1N/A{
1N/A#ifndef NO_BLOCK_FILES
1N/A if (block_file)
1N/A return;
1N/A#endif /* NO_BLOCK_FILES */
1N/A
1N/A if (fsys_table[fsys_type].close_func != 0)
1N/A (*(fsys_table[fsys_type].close_func)) ();
1N/A}