#include <shared.h>
#include <filesys.h>
#include <gpt.h>
#ifdef SUPPORT_NETBOOT
# include <grub.h>
#endif
#ifdef GRUB_UTIL
# include <device.h>
#endif
void (*disk_read_hook) (unsigned long long, int, int) = NULL;
void (*disk_read_func) (unsigned long long, int, int) = NULL;
#ifndef STAGE1_5
int print_possibilities;
static int do_completion;
static int unique;
static char *unique_string;
#endif
int fsmax;
struct fsys_entry fsys_table[NUM_FSYS + 1] =
{
# ifdef FSYS_TFTP
{"tftp", tftp_mount, tftp_read, tftp_dir, tftp_close, 0},
# endif
# ifdef FSYS_FAT
{"fat", fat_mount, fat_read, fat_dir, 0, 0},
# endif
# ifdef FSYS_EXT2FS
{"ext2fs", ext2fs_mount, ext2fs_read, ext2fs_dir, 0, 0},
# endif
# ifdef FSYS_MINIX
{"minix", minix_mount, minix_read, minix_dir, 0, 0},
# endif
# ifdef FSYS_REISERFS
{"reiserfs", reiserfs_mount, reiserfs_read, reiserfs_dir, 0, reiserfs_embed},
# endif
# ifdef FSYS_VSTAFS
{"vstafs", vstafs_mount, vstafs_read, vstafs_dir, 0, 0},
# endif
# ifdef FSYS_JFS
{"jfs", jfs_mount, jfs_read, jfs_dir, 0, jfs_embed},
# endif
# ifdef FSYS_XFS
{"xfs", xfs_mount, xfs_read, xfs_dir, 0, 0},
# endif
# ifdef FSYS_UFS
{"ufs", ufs_mount, ufs_read, ufs_dir, 0, ufs_embed},
# endif
# ifdef FSYS_UFS2
{"ufs2", ufs2_mount, ufs2_read, ufs2_dir, 0, ufs2_embed},
# endif
# ifdef FSYS_ZFS
{"zfs", zfs_mount, zfs_read, zfs_open, 0, zfs_embed},
# endif
# ifdef FSYS_ISO9660
{"iso9660", iso9660_mount, iso9660_read, iso9660_dir, 0, 0},
# endif
# ifdef FSYS_FFS
{"ffs", ffs_mount, ffs_read, ffs_dir, 0, ffs_embed},
# endif
{0, 0, 0, 0, 0, 0}
};
unsigned long current_drive = GRUB_INVALID_DRIVE;
unsigned long current_partition;
#ifndef STAGE1_5
unsigned long boot_part_addr = 0;
#endif
#include "freebsd.h"
int bsd_evil_hack;
int fsys_type = NUM_FSYS;
#ifndef NO_BLOCK_FILES
static int block_file = 0;
#endif
unsigned long long part_start;
unsigned long long part_length;
int current_slice;
char current_rootpool[MAXNAMELEN];
char current_bootfs[MAXNAMELEN];
uint64_t current_bootfs_obj;
char current_bootpath[MAXPATHLEN];
char current_devid[MAXPATHLEN];
uint64_t current_bootguid;
uint64_t current_bootvdev;
int is_zfs_mount;
unsigned long best_drive;
unsigned long best_part;
int find_best_root;
int buf_drive = -1;
unsigned long long buf_track;
struct geometry buf_geom;
int filepos;
int filemax;
static inline unsigned long
grub_log2 (unsigned long word)
{
asm volatile ("bsfl %1,%0"
: "=r" (word)
: "r" (word));
return word;
}
#define log2 grub_log2
int
rawread(int drive, unsigned long long sector, int byte_offset, int byte_len,
char *buf)
{
int slen, sectors_per_vtrack;
int sector_size_bits = log2 (buf_geom.sector_size);
if (byte_len <= 0)
return 1;
while (byte_len > 0 && !errnum)
{
int soff, num_sect, size = byte_len;
unsigned long long track;
char *bufaddr;
if (buf_drive != drive)
{
if (get_diskinfo (drive, &buf_geom))
{
errnum = ERR_NO_DISK;
return 0;
}
buf_drive = drive;
buf_track = BUF_CACHE_INVALID;
sector_size_bits = log2 (buf_geom.sector_size);
}
slen = ((byte_offset + byte_len + buf_geom.sector_size - 1)
>> sector_size_bits);
if ((buf_geom.sectors << sector_size_bits) > BUFFERLEN)
sectors_per_vtrack = (BUFFERLEN >> sector_size_bits);
else
sectors_per_vtrack = buf_geom.sectors;
soff = sector % sectors_per_vtrack;
track = sector - soff;
num_sect = sectors_per_vtrack - soff;
bufaddr = ((char *) BUFFERADDR
+ (soff << sector_size_bits) + byte_offset);
if (track != buf_track)
{
int bios_err, read_len = sectors_per_vtrack;
unsigned long long read_start = track;
if (slen > num_sect)
{
read_start = sector;
read_len = num_sect;
bufaddr = (char *) BUFFERADDR + byte_offset;
}
bios_err = biosdisk (BIOSDISK_READ, drive, &buf_geom,
read_start, read_len, BUFFERSEG);
if (bios_err)
{
buf_track = BUF_CACHE_INVALID;
if (bios_err == BIOSDISK_ERROR_GEOMETRY)
errnum = ERR_GEOM;
else
{
if (slen > num_sect
|| biosdisk (BIOSDISK_READ, drive, &buf_geom,
sector, slen, BUFFERSEG))
errnum = ERR_READ;
bufaddr = (char *) BUFFERADDR + byte_offset;
}
}
else
buf_track = track;
if ((buf_track == 0 || sector == 0)
&& (PC_SLICE_TYPE (BUFFERADDR, 0) == PC_SLICE_TYPE_EZD
|| PC_SLICE_TYPE (BUFFERADDR, 1) == PC_SLICE_TYPE_EZD
|| PC_SLICE_TYPE (BUFFERADDR, 2) == PC_SLICE_TYPE_EZD
|| PC_SLICE_TYPE (BUFFERADDR, 3) == PC_SLICE_TYPE_EZD))
{
if (buf_track == 0 || slen >= 2)
{
memmove ((char *) BUFFERADDR,
(char *) BUFFERADDR + buf_geom.sector_size,
buf_geom.sector_size);
}
else
{
if (biosdisk (BIOSDISK_READ, drive, &buf_geom,
1, 1, BUFFERSEG))
errnum = ERR_READ;
}
}
}
if (size > ((num_sect << sector_size_bits) - byte_offset))
size = (num_sect << sector_size_bits) - byte_offset;
if (disk_read_func)
{
unsigned long long sector_num = sector;
int length = buf_geom.sector_size - byte_offset;
if (length > size)
length = size;
(*disk_read_func) (sector_num++, byte_offset, length);
length = size - length;
if (length > 0)
{
while (length > buf_geom.sector_size)
{
(*disk_read_func) (sector_num++, 0, buf_geom.sector_size);
length -= buf_geom.sector_size;
}
(*disk_read_func) (sector_num, 0, length);
}
}
grub_memmove (buf, bufaddr, size);
buf += size;
byte_len -= size;
sector += num_sect;
byte_offset = 0;
}
return (!errnum);
}
int
devread(unsigned long long sector, int byte_offset, int byte_len, char *buf)
{
if ((sector + ((byte_offset + byte_len - 1) >> SECTOR_BITS))
>= part_length)
{
errnum = ERR_OUTSIDE_PART;
return 0;
}
sector += byte_offset >> SECTOR_BITS;
byte_offset &= SECTOR_SIZE - 1;
#if !defined(STAGE1_5)
if (disk_read_hook && debug)
printf ("<%llu, %d, %d>", sector, byte_offset, byte_len);
#endif
return rawread (current_drive, part_start + sector, byte_offset,
byte_len, buf);
}
#ifndef STAGE1_5
int
rawwrite(int drive, unsigned long long sector, char *buf)
{
if (sector == 0)
{
if (biosdisk (BIOSDISK_READ, drive, &buf_geom, 0, 1, SCRATCHSEG))
{
errnum = ERR_WRITE;
return 0;
}
if (PC_SLICE_TYPE (SCRATCHADDR, 0) == PC_SLICE_TYPE_EZD
|| PC_SLICE_TYPE (SCRATCHADDR, 1) == PC_SLICE_TYPE_EZD
|| PC_SLICE_TYPE (SCRATCHADDR, 2) == PC_SLICE_TYPE_EZD
|| PC_SLICE_TYPE (SCRATCHADDR, 3) == PC_SLICE_TYPE_EZD)
sector = 1;
}
memmove ((char *) SCRATCHADDR, buf, SECTOR_SIZE);
if (biosdisk (BIOSDISK_WRITE, drive, &buf_geom,
sector, 1, SCRATCHSEG))
{
errnum = ERR_WRITE;
return 0;
}
if (sector - sector % buf_geom.sectors == buf_track)
buf_track = BUF_CACHE_INVALID;
return 1;
}
int
devwrite(unsigned long long sector, int sector_count, char *buf)
{
#if defined(GRUB_UTIL) && defined(__linux__)
if (current_partition != 0xFFFFFF
&& is_disk_device (device_map, current_drive))
{
return write_to_partition (device_map, current_drive, current_partition,
sector, sector_count, buf);
}
else
#endif
{
int i;
for (i = 0; i < sector_count; i++)
{
if (! rawwrite (current_drive, part_start + sector + i,
buf + (i << SECTOR_BITS)))
return 0;
}
return 1;
}
}
static int
sane_partition (void)
{
if (current_drive == NETWORK_DRIVE)
return 1;
if (!(current_partition & 0xFF000000uL)
&& ((current_drive & 0xFFFFFF7F) < 8
|| current_drive == cdrom_drive)
&& (current_partition & 0xFF) == 0xFF
&& ((current_partition & 0xFF00) == 0xFF00
|| (current_partition & 0xFF00) < 0x1000)
&& ((current_partition >> 16) == 0xFF
|| (current_drive & 0x80)))
return 1;
errnum = ERR_DEV_VALUES;
return 0;
}
#endif
static void
attempt_mount (void)
{
#ifndef STAGE1_5
for (fsys_type = 0; fsys_type < NUM_FSYS; fsys_type++)
if ((fsys_table[fsys_type].mount_func) ())
break;
if (fsys_type == NUM_FSYS && errnum == ERR_NONE)
errnum = ERR_FSYS_MOUNT;
#else
fsys_type = 0;
if ((*(fsys_table[fsys_type].mount_func)) () != 1)
{
fsys_type = NUM_FSYS;
errnum = ERR_FSYS_MOUNT;
}
#endif
}
#ifndef STAGE1_5
int
make_saved_active (void)
{
char mbr[512];
if (saved_drive & 0x80)
{
int part = saved_partition >> 16;
if (part > 3)
{
errnum = ERR_DEV_VALUES;
return 0;
}
if (! rawread (saved_drive, 0, 0, SECTOR_SIZE, mbr))
return 0;
if (IS_PC_SLICE_TYPE_EXTENDED (PC_SLICE_TYPE (mbr, part)))
{
errnum = ERR_DEV_VALUES;
return 0;
}
if (PC_SLICE_FLAG (mbr, part) != PC_SLICE_FLAG_BOOTABLE)
{
int i;
for (i = 0; i < 4; i++)
PC_SLICE_FLAG (mbr, i) = 0;
PC_SLICE_FLAG (mbr, part) = PC_SLICE_FLAG_BOOTABLE;
if (! rawwrite (saved_drive, 0, mbr))
return 0;
}
}
else
{
errnum = ERR_DEV_VALUES;
return 0;
}
return 1;
}
int
set_partition_hidden_flag (int hidden)
{
unsigned long part = 0xFFFFFF;
unsigned long long start, len, offset, ext_offset, gpt_offset;
int entry, type, gpt_count, gpt_size;
char mbr[512];
if (! (current_drive & 0x80))
{
errnum = ERR_BAD_ARGUMENT;
return 1;
}
if ((current_partition >> 16) == 0xFF
|| (current_partition & 0xFFFF) != 0xFFFF)
{
errnum = ERR_BAD_ARGUMENT;
return 1;
}
while (next_partition (current_drive, 0xFFFFFF, &part, &type,
&start, &len, &offset, &entry,
&ext_offset, &gpt_offset, &gpt_count, &gpt_size, mbr))
{
if (gpt_offset != 0)
{
errnum = ERR_BAD_ARGUMENT;
return 1;
}
if (part == current_partition)
{
if (hidden)
PC_SLICE_TYPE (mbr, entry) |= PC_SLICE_TYPE_HIDDEN_FLAG;
else
PC_SLICE_TYPE (mbr, entry) &= ~PC_SLICE_TYPE_HIDDEN_FLAG;
buf_track = BUF_CACHE_INVALID;
if (! rawwrite (current_drive, offset, mbr))
return 1;
return 0;
}
}
return 1;
}
static void
check_and_print_mount (void)
{
attempt_mount ();
if (errnum == ERR_FSYS_MOUNT)
errnum = ERR_NONE;
if (!errnum)
print_fsys_type ();
print_error ();
}
#endif
int
next_partition (unsigned long drive, unsigned long dest,
unsigned long *partition, int *type,
unsigned long long *start, unsigned long long *len,
unsigned long long *offset, int *entry,
unsigned long long *ext_offset,
unsigned long long *gpt_offset, int *gpt_count,
int *gpt_size, char *buf)
{
auto int next_bsd_partition (void);
auto int next_solaris_partition(void);
auto int next_pc_slice (void);
auto int next_gpt_slice(void);
int next_bsd_partition (void)
{
int i;
int bsd_part_no = (*partition & 0xFF00) >> 8;
if (bsd_part_no == 0xFF)
{
if (*len < BSD_LABEL_SECTOR + 1)
{
errnum = ERR_BAD_PART_TABLE;
return 0;
}
if (! rawread (drive, *start + BSD_LABEL_SECTOR,
0, SECTOR_SIZE, buf))
return 0;
if (! BSD_LABEL_CHECK_MAG (buf))
{
errnum = ERR_BAD_PART_TABLE;
return 0;
}
bsd_part_no = -1;
}
for (i = bsd_part_no + 1; i < BSD_LABEL_NPARTS (buf); i++)
{
if (BSD_PART_TYPE (buf, i))
{
*type = (BSD_PART_TYPE (buf, i) << 8) | (*type & 0xFF);
*start = BSD_PART_START (buf, i);
*len = BSD_PART_LENGTH (buf, i);
*partition = (*partition & 0xFF00FF) | (i << 8);
#ifndef STAGE1_5
if ((drive & 0x80) && BSD_LABEL_DTYPE (buf) == DTYPE_SCSI)
bsd_evil_hack = 4;
#endif
return 1;
}
}
errnum = ERR_NO_PART;
return 0;
}
int next_solaris_partition (void)
{
static unsigned long pcs_start;
int i;
int sol_part_no = (*partition & 0xFF00) >> 8;
if (sol_part_no == 0xFF)
{
if (*len < SOL_LABEL_LOC + 1)
{
errnum = ERR_BAD_PART_TABLE;
return 0;
}
if (! rawread (drive, *start + SOL_LABEL_LOC, 0, SECTOR_SIZE, buf))
return 0;
if (! SOL_LABEL_CHECK_MAG (buf))
{
errnum = ERR_BAD_PART_TABLE;
return 0;
}
sol_part_no = -1;
pcs_start = *start;
}
for (i = sol_part_no + 1; i < SOL_LABEL_NPARTS; i++)
{
if (SOL_PART_EXISTS (buf, i))
{
*start = SOL_PART_START (buf, i) + pcs_start;
*len = SOL_PART_LENGTH (buf, i);
*partition = (*partition & 0xFF00FF) | (i << 8);
return 1;
}
}
errnum = ERR_NO_PART;
return 0;
}
int next_pc_slice (void)
{
int pc_slice_no = (*partition & 0xFF0000) >> 16;
if (pc_slice_no == 0xFF)
{
*offset = 0;
*ext_offset = 0;
*entry = -1;
pc_slice_no = -1;
}
if (! rawread (drive, *offset, 0, SECTOR_SIZE, buf))
return 0;
if (! PC_MBR_CHECK_SIG (buf))
{
errnum = ERR_BAD_PART_TABLE;
return 0;
}
if (*entry == -1 && *offset == 0 && PC_SLICE_TYPE (buf, 0) == PC_SLICE_TYPE_GPT)
{
struct grub_gpt_header *hdr = (struct grub_gpt_header *) buf;
if (! rawread (drive, 1, 0, SECTOR_SIZE, buf))
return 0;
if (hdr->magic == GPT_HEADER_MAGIC && hdr->version == 0x10000)
{
*gpt_offset = hdr->partitions;
*gpt_count = hdr->maxpart;
*gpt_size = hdr->partentry_size;
return next_gpt_slice();
}
else
{
if (! rawread (drive, *offset, 0, SECTOR_SIZE, buf))
return 0;
}
}
*gpt_offset = 0;
(*entry)++;
if (*entry == PC_SLICE_MAX)
{
int i;
for (i = 0; i < PC_SLICE_MAX; i++)
{
if (IS_PC_SLICE_TYPE_EXTENDED (PC_SLICE_TYPE (buf, i)))
{
*offset = *ext_offset + PC_SLICE_START (buf, i);
if (! *ext_offset)
*ext_offset = *offset;
*entry = -1;
return next_pc_slice ();
}
}
errnum = ERR_NO_PART;
return 0;
}
*type = PC_SLICE_TYPE (buf, *entry);
*start = *offset + PC_SLICE_START (buf, *entry);
*len = PC_SLICE_LENGTH (buf, *entry);
if (pc_slice_no < PC_SLICE_MAX
|| (! IS_PC_SLICE_TYPE_EXTENDED (*type)
&& *type != PC_SLICE_TYPE_NONE))
pc_slice_no++;
*partition = (pc_slice_no << 16) | 0xFFFF;
return 1;
}
int next_gpt_slice (void)
{
struct grub_gpt_partentry *gptentry = (struct grub_gpt_partentry *) buf;
int pc_slice_no = (*partition & 0xFF0000) >> 16;
if (pc_slice_no == 0xFF)
{
pc_slice_no = -1;
*entry = -1;
}
do {
(*entry)++;
if (*entry >= *gpt_count)
{
errnum = ERR_NO_PART;
return 0;
}
if (! rawread (drive, (*gpt_offset) + GPT_ENTRY_SECTOR (*gpt_size, *entry), GPT_ENTRY_INDEX (*gpt_size, *entry), *gpt_size, buf))
return 0;
} while (! (gptentry->type1 && gptentry->type2));
pc_slice_no++;
*start = gptentry->start;
*len = gptentry->end - gptentry->start + 1;
*type = PC_SLICE_TYPE_EXT2FS;
*entry = pc_slice_no;
*partition = (*entry << 16) | 0xFFFF;
return 1;
}
#ifndef STAGE1_5
if (current_drive == NETWORK_DRIVE)
return 0;
#endif
if (*partition != 0xFFFFFF && IS_PC_SLICE_TYPE_SOLARIS (*type & 0xff))
{
if (next_solaris_partition ())
return 1;
errnum = ERR_NONE;
}
if (*partition != 0xFFFFFF && *gpt_offset != 0)
return next_gpt_slice ();
if ((*partition != 0xFFFFFF && IS_PC_SLICE_TYPE_BSD (*type & 0xff))
|| ! (drive & 0x80))
{
if (*type == PC_SLICE_TYPE_NONE)
*type = PC_SLICE_TYPE_FREEBSD;
if (next_bsd_partition ())
return 1;
if ((dest & 0xFF00) != 0xFF00
&& ((dest & 0xFF0000) == 0xFF0000
|| (dest & 0xFF0000) == (*partition & 0xFF0000)))
return 0;
errnum = ERR_NONE;
}
return next_pc_slice ();
}
#ifndef STAGE1_5
static unsigned long cur_part_offset;
static unsigned long cur_part_addr;
#endif
int
real_open_partition (int flags)
{
unsigned long dest_partition = current_partition;
unsigned long long part_offset;
unsigned long long ext_offset;
unsigned long long gpt_offset;
int gpt_count;
int gpt_size;
int entry;
char buf[SECTOR_SIZE];
int unix_part, pc_slice;
auto int next (void);
int next (void)
{
int ret = next_partition (current_drive, dest_partition,
¤t_partition, ¤t_slice,
&part_start, &part_length,
&part_offset, &entry, &ext_offset,
&gpt_offset, &gpt_count, &gpt_size, buf);
unix_part = (current_partition >> 8) & 0xFF;
pc_slice = current_partition >> 16;
return ret;
}
#ifndef STAGE1_5
if (current_drive == NETWORK_DRIVE)
return 1;
if (! sane_partition ())
return 0;
#endif
bsd_evil_hack = 0;
current_slice = 0;
part_start = 0;
if (buf_drive != current_drive)
{
if (get_diskinfo (current_drive, &buf_geom))
{
errnum = ERR_NO_DISK;
return 0;
}
buf_drive = current_drive;
buf_track = BUF_CACHE_INVALID;
}
part_length =
(buf_geom.total_sectors > MAXUINT) ? MAXUINT : buf_geom.total_sectors;
if (! flags && current_partition == 0xFFFFFF)
return 1;
if (flags)
dest_partition = 0xFFFFFF;
current_partition = 0xFFFFFF;
while (next ())
{
#ifndef STAGE1_5
loop_start:
cur_part_offset = part_offset;
cur_part_addr = BOOT_PART_TABLE + (entry << 4);
#endif
if (current_slice)
{
#ifndef STAGE1_5
if (flags && ! IS_PC_SLICE_TYPE_EXTENDED (current_slice))
{
if (! do_completion)
{
if (current_drive & 0x80)
grub_printf (" Partition num: %d, ",
current_partition >> 16);
if (! IS_PC_SLICE_TYPE_BSD (current_slice) &&
! IS_PC_SLICE_TYPE_SOLARIS (current_slice))
check_and_print_mount ();
else
{
int got_part = 0;
int saved_slice = current_slice;
while (next ())
{
if (unix_part == 0xFF)
break;
if (! got_part)
{
grub_printf ("[BSD/SOLARIS sub-partitions immediately follow]\n");
got_part = 1;
}
grub_printf (" BSD/SOLARIS Partition num: \'%c\', ",
unix_part + 'a');
check_and_print_mount ();
}
if (! got_part)
grub_printf (" No BSD/SOLARIS sub-partition found, partition type 0x%x\n",
saved_slice);
if (errnum)
{
errnum = ERR_NONE;
break;
}
goto loop_start;
}
}
else
{
if (unix_part != 0xFF)
{
char str[16];
if (! (current_drive & 0x80)
|| (dest_partition >> 16) == pc_slice)
grub_sprintf (str, "%c)", unix_part + 'a');
else
grub_sprintf (str, "%d,%c)",
pc_slice, unix_part + 'a');
print_a_completion (str);
}
else if (! IS_PC_SLICE_TYPE_BSD (current_slice) &&
! IS_PC_SLICE_TYPE_SOLARIS (current_slice))
{
char str[8];
grub_sprintf (str, "%d)", pc_slice);
print_a_completion (str);
}
}
}
errnum = ERR_NONE;
#endif
if (! flags
&& (dest_partition == current_partition
|| ((dest_partition >> 16) == 0xFF
&& ((dest_partition >> 8) & 0xFF) == unix_part)))
return 1;
}
}
#ifndef STAGE1_5
if (flags)
{
if (! (current_drive & 0x80))
{
current_partition = 0xFFFFFF;
check_and_print_mount ();
}
errnum = ERR_NONE;
return 1;
}
#endif
return 0;
}
int
open_partition (void)
{
return real_open_partition (0);
}
#ifndef STAGE1_5
static int incomplete, disk_choice;
static enum
{
PART_UNSPECIFIED = 0,
PART_DISK,
PART_CHOSEN,
}
part_choice;
#endif
char *
set_device (char *device)
{
#ifdef STAGE1_5
unsigned long dev = *((unsigned long *) device);
int drive = (dev >> 24) & 0xFF;
int partition = dev & 0xFFFFFF;
if (drive == GRUB_INVALID_DRIVE)
current_drive = saved_drive;
else
current_drive = drive;
current_partition = partition;
return device + sizeof (unsigned long);
#else
int result = 0;
incomplete = 0;
disk_choice = 1;
part_choice = PART_UNSPECIFIED;
current_drive = saved_drive;
current_partition = 0xFFFFFF;
if (*device == '(' && !*(device + 1))
return device + 1;
if (*device == '(' && *(++device))
{
if (*device != ',' && *device != ')')
{
char ch = *device;
#ifdef SUPPORT_NETBOOT
if (*device == 'f' || *device == 'h'
|| (*device == 'n' && network_ready)
|| (*device == 'c' && cdrom_drive != GRUB_INVALID_DRIVE))
#else
if (*device == 'f' || *device == 'h'
|| (*device == 'c' && cdrom_drive != GRUB_INVALID_DRIVE))
#endif
{
if (!*(device + 1))
{
device++;
*device++ = 'd';
*device = '\0';
return device;
}
else if (*(device + 1) == 'd' && !*(device + 2))
return device + 2;
}
if ((*device == 'f'
|| *device == 'h'
#ifdef SUPPORT_NETBOOT
|| (*device == 'n' && network_ready)
#endif
|| (*device == 'c' && cdrom_drive != GRUB_INVALID_DRIVE))
&& (device += 2, (*(device - 1) != 'd')))
errnum = ERR_NUMBER_PARSING;
#ifdef SUPPORT_NETBOOT
if (ch == 'n' && network_ready)
current_drive = NETWORK_DRIVE;
else
#endif
{
if (ch == 'c' && cdrom_drive != GRUB_INVALID_DRIVE)
current_drive = cdrom_drive;
else
{
safe_parse_maxint (&device, (int *) ¤t_drive);
disk_choice = 0;
if (ch == 'h')
current_drive += 0x80;
}
}
}
if (errnum)
return 0;
if (*device == ')')
{
part_choice = PART_CHOSEN;
result = 1;
}
else if (*device == ',')
{
disk_choice = 0;
part_choice ++;
device++;
if (*device >= '0' && *device <= '9')
{
part_choice ++;
current_partition = 0;
if (!(current_drive & 0x80)
|| !safe_parse_maxint (&device, (int *) ¤t_partition)
|| current_partition > 254)
{
errnum = ERR_DEV_FORMAT;
return 0;
}
current_partition = (current_partition << 16) + 0xFFFF;
if (*device == ',')
device++;
if (*device >= 'a' && *device <= 'p')
{
current_partition = (((*(device++) - 'a') << 8)
| (current_partition & 0xFF00FF));
}
}
else if (*device >= 'a' && *device <= 'p')
{
part_choice ++;
current_partition = ((*(device++) - 'a') << 8) | 0xFF00FF;
}
if (*device == ')')
{
if (part_choice == PART_DISK)
{
current_partition = saved_partition;
part_choice ++;
}
result = 1;
}
}
}
if (! sane_partition ())
return 0;
if (result)
return device + 1;
else
{
if (!*device)
incomplete = 1;
errnum = ERR_DEV_FORMAT;
}
return 0;
#endif
}
int
open_device (void)
{
if (open_partition ())
attempt_mount ();
if (errnum != ERR_NONE)
return 0;
return 1;
}
#ifndef STAGE1_5
int
set_bootdev (int hdbias)
{
int i, j;
if ((saved_drive & 0x80) && cur_part_addr)
{
if (rawread (saved_drive, cur_part_offset,
0, SECTOR_SIZE, (char *) SCRATCHADDR))
{
char *dst, *src;
dst = (char *) BOOT_PART_TABLE;
src = (char *) SCRATCHADDR + BOOTSEC_PART_OFFSET;
while (dst < (char *) BOOT_PART_TABLE + BOOTSEC_PART_LENGTH)
*dst++ = *src++;
for (i = 0; i < 4; i++)
PC_SLICE_FLAG (BOOT_PART_TABLE, i) = 0;
*((unsigned char *) cur_part_addr) = PC_SLICE_FLAG_BOOTABLE;
boot_part_addr = cur_part_addr;
}
else
return 0;
}
i = (saved_partition >> 16) + 2;
if (saved_partition == 0xFFFFFF)
i = 1;
else if ((saved_partition >> 16) == 0xFF)
i = 0;
j = 2;
if (saved_drive & 0x80)
j = bsd_evil_hack;
return MAKEBOOTDEV (j, (i >> 4), (i & 0xF),
((saved_drive - hdbias) & 0x7F),
((saved_partition >> 8) & 0xFF));
}
#endif
static char *
setup_part (char *filename)
{
#ifdef STAGE1_5
if (! (filename = set_device (filename)))
{
current_drive = GRUB_INVALID_DRIVE;
return 0;
}
# ifndef NO_BLOCK_FILES
if (*filename != '/')
open_partition ();
else
# endif
open_device ();
#else
if (*filename == '(')
{
if ((filename = set_device (filename)) == 0)
{
current_drive = GRUB_INVALID_DRIVE;
return 0;
}
# ifndef NO_BLOCK_FILES
if (*filename != '/' && current_drive != NETWORK_DRIVE)
open_partition ();
else
# endif
open_device ();
}
else if (saved_drive != current_drive
|| saved_partition != current_partition
|| (*filename == '/' && fsys_type == NUM_FSYS)
|| buf_drive == -1)
{
current_drive = saved_drive;
current_partition = saved_partition;
# ifndef NO_BLOCK_FILES
if (*filename != '/' && current_drive != NETWORK_DRIVE)
open_partition ();
else
# endif
open_device ();
}
#endif
if (errnum && (*filename == '/' || errnum != ERR_FSYS_MOUNT))
return 0;
else
errnum = 0;
#ifndef STAGE1_5
if (!sane_partition ())
return 0;
#endif
return filename;
}
#ifndef STAGE1_5
void
print_fsys_type (void)
{
if (! do_completion)
{
grub_printf (" Filesystem type ");
if (fsys_type != NUM_FSYS)
grub_printf ("is %s, ", fsys_table[fsys_type].name);
else
grub_printf ("unknown, ");
if (current_partition == 0xFFFFFF)
grub_printf ("using whole disk\n");
else
grub_printf ("partition type 0x%x\n", current_slice & 0xFF);
}
}
#endif
#ifndef STAGE1_5
void
print_a_completion (char *name)
{
if (grub_strcmp (name, ".") == 0 || grub_strcmp (name, "..") == 0)
return;
if (do_completion)
{
char *buf = unique_string;
if (! unique)
while ((*buf++ = *name++))
;
else
{
while (*buf && (*buf == *name))
{
buf++;
name++;
}
*buf = '\0';
}
}
else
grub_printf (" %s", name);
unique++;
}
int
print_completions (int is_filename, int is_completion)
{
char *buf = (char *) COMPLETION_BUF;
char *ptr = buf;
unique_string = (char *) UNIQUE_BUF;
*unique_string = 0;
unique = 0;
do_completion = is_completion;
if (! is_filename)
{
struct builtin **builtin;
if (! is_completion)
grub_printf (" Possible commands are:");
for (builtin = builtin_table; (*builtin); builtin++)
{
if (! ((*builtin)->flags & BUILTIN_CMDLINE))
continue;
if (substring (buf, (*builtin)->name) <= 0)
print_a_completion ((*builtin)->name);
}
if (is_completion && *unique_string)
{
if (unique == 1)
{
char *u = unique_string + grub_strlen (unique_string);
*u++ = ' ';
*u = 0;
}
grub_strcpy (buf, unique_string);
}
if (! is_completion)
grub_putchar ('\n');
print_error ();
do_completion = 0;
if (errnum)
return -1;
else
return unique - 1;
}
if (*buf == '/' || (ptr = set_device (buf)) || incomplete)
{
errnum = 0;
if (*buf == '(' && (incomplete || ! *ptr))
{
if (! part_choice)
{
int disk_no, i, j;
struct geometry geom;
if (! is_completion)
grub_printf (" Possible disks are: ");
if (!ptr
|| *(ptr-1) != 'd'
#ifdef SUPPORT_NETBOOT
|| *(ptr-2) != 'n'
#endif
|| *(ptr-2) != 'c')
{
for (i = (ptr && (*(ptr-1) == 'd' && *(ptr-2) == 'h') ? 1:0);
i < (ptr && (*(ptr-1) == 'd' && *(ptr-2) == 'f') ? 1:2);
i++)
{
for (j = 0; j < 8; j++)
{
disk_no = (i * 0x80) + j;
if ((disk_choice || disk_no == current_drive)
&& ! get_diskinfo (disk_no, &geom))
{
char dev_name[8];
grub_sprintf (dev_name, "%cd%d", i ? 'h':'f', j);
print_a_completion (dev_name);
}
}
}
}
if (cdrom_drive != GRUB_INVALID_DRIVE
&& (disk_choice || cdrom_drive == current_drive)
&& (!ptr
|| *(ptr-1) == '('
|| (*(ptr-1) == 'd' && *(ptr-2) == 'c')))
print_a_completion ("cd");
# ifdef SUPPORT_NETBOOT
if (network_ready
&& (disk_choice || NETWORK_DRIVE == current_drive)
&& (!ptr
|| *(ptr-1) == '('
|| (*(ptr-1) == 'd' && *(ptr-2) == 'n')))
print_a_completion ("nd");
# endif
if (is_completion && *unique_string)
{
ptr = buf;
while (*ptr != '(')
ptr--;
ptr++;
grub_strcpy (ptr, unique_string);
if (unique == 1)
{
ptr += grub_strlen (ptr);
if (*unique_string == 'h')
{
*ptr++ = ',';
*ptr = 0;
}
else
{
*ptr++ = ')';
*ptr = 0;
}
}
}
if (! is_completion)
grub_putchar ('\n');
}
else
{
if (part_choice == PART_CHOSEN
&& open_partition ()
&& ! IS_PC_SLICE_TYPE_BSD (current_slice))
{
unique = 1;
ptr = buf + grub_strlen (buf);
if (*(ptr - 1) != ')')
{
*ptr++ = ')';
*ptr = 0;
}
}
else
{
if (! is_completion)
grub_printf (" Possible partitions are:\n");
real_open_partition (1);
if (is_completion && *unique_string)
{
ptr = buf;
while (*ptr++ != ',')
;
grub_strcpy (ptr, unique_string);
}
}
}
}
else if (ptr && *ptr == '/')
{
if (! is_completion)
grub_printf (" Possible files are:");
dir (buf);
if (is_completion && *unique_string)
{
ptr += grub_strlen (ptr);
while (*ptr != '/')
ptr--;
ptr++;
grub_strcpy (ptr, unique_string);
if (unique == 1)
{
ptr += grub_strlen (unique_string);
*ptr = '/';
*(ptr + 1) = 0;
dir (buf);
unique = 1;
if (errnum)
{
errnum = 0;
*ptr = ' ';
*(ptr + 1) = 0;
}
}
}
if (! is_completion)
grub_putchar ('\n');
}
else
errnum = ERR_BAD_FILENAME;
}
print_error ();
do_completion = 0;
if (errnum)
return -1;
else
return unique - 1;
}
#endif
int
grub_open (char *filename)
{
#ifndef NO_DECOMPRESSION
compressed_file = 0;
#endif
filepos = 0;
if (!(filename = setup_part (filename)))
return 0;
#ifndef NO_BLOCK_FILES
block_file = 0;
#endif
fsmax = MAXINT;
if (*filename != '/' && current_drive != NETWORK_DRIVE)
{
#ifndef NO_BLOCK_FILES
char *ptr = filename;
int tmp, list_addr = BLK_BLKLIST_START;
filemax = 0;
while (list_addr < BLK_MAX_ADDR)
{
tmp = 0;
safe_parse_maxint (&ptr, &tmp);
errnum = 0;
if (*ptr != '+')
{
if ((*ptr && *ptr != '/' && !isspace (*ptr))
|| tmp == 0 || tmp > filemax)
errnum = ERR_BAD_FILENAME;
else
filemax = tmp;
break;
}
fsys_type = NUM_FSYS;
BLK_BLKSTART (list_addr) = tmp;
ptr++;
if (!safe_parse_maxint (&ptr, &tmp)
|| tmp == 0
|| (*ptr && *ptr != ',' && *ptr != '/' && !isspace (*ptr)))
{
errnum = ERR_BAD_FILENAME;
break;
}
BLK_BLKLENGTH (list_addr) = tmp;
filemax += (tmp * SECTOR_SIZE);
list_addr += BLK_BLKLIST_INC_VAL;
if (*ptr != ',')
break;
ptr++;
}
if (list_addr < BLK_MAX_ADDR && ptr != filename && !errnum)
{
block_file = 1;
BLK_CUR_FILEPOS = 0;
BLK_CUR_BLKLIST = BLK_BLKLIST_START;
BLK_CUR_BLKNUM = 0;
#ifndef NO_DECOMPRESSION
return gunzip_test_header ();
#else
return 1;
#endif
}
#else
errnum = ERR_BAD_FILENAME;
#endif
}
if (!errnum && fsys_type == NUM_FSYS)
errnum = ERR_FSYS_MOUNT;
# ifndef STAGE1_5
print_possibilities = 0;
# endif
if (!errnum && (*(fsys_table[fsys_type].dir_func)) (filename))
{
#ifndef NO_DECOMPRESSION
return gunzip_test_header ();
#else
return 1;
#endif
}
return 0;
}
int
grub_read (char *buf, int len)
{
if ((filepos < 0) || (filepos > filemax))
filepos = filemax;
if ((len < 0) || (len > (filemax - filepos)))
len = filemax - filepos;
if (filepos + len > fsmax)
{
errnum = ERR_FILELENGTH;
return 0;
}
#ifndef NO_DECOMPRESSION
if (compressed_file)
return gunzip_read (buf, len);
#endif
#ifndef NO_BLOCK_FILES
if (block_file)
{
int size, off, ret = 0;
while (len && !errnum)
{
if (filepos < BLK_CUR_FILEPOS)
{
BLK_CUR_FILEPOS = 0;
BLK_CUR_BLKLIST = BLK_BLKLIST_START;
BLK_CUR_BLKNUM = 0;
}
while (filepos > BLK_CUR_FILEPOS)
{
if ((filepos - (BLK_CUR_FILEPOS & ~(SECTOR_SIZE - 1)))
>= SECTOR_SIZE)
{
BLK_CUR_FILEPOS += SECTOR_SIZE;
BLK_CUR_BLKNUM++;
if (BLK_CUR_BLKNUM >= BLK_BLKLENGTH (BLK_CUR_BLKLIST))
{
BLK_CUR_BLKLIST += BLK_BLKLIST_INC_VAL;
BLK_CUR_BLKNUM = 0;
}
}
else
BLK_CUR_FILEPOS = filepos;
}
off = filepos & (SECTOR_SIZE - 1);
size = ((BLK_BLKLENGTH (BLK_CUR_BLKLIST) - BLK_CUR_BLKNUM)
* SECTOR_SIZE) - off;
if (size > len)
size = len;
disk_read_func = disk_read_hook;
devread (BLK_BLKSTART (BLK_CUR_BLKLIST) + BLK_CUR_BLKNUM,
off, size, buf);
disk_read_func = NULL;
len -= size;
filepos += size;
ret += size;
buf += size;
}
if (errnum)
ret = 0;
return ret;
}
#endif
if (fsys_type == NUM_FSYS)
{
errnum = ERR_FSYS_MOUNT;
return 0;
}
return (*(fsys_table[fsys_type].read_func)) (buf, len);
}
#ifndef STAGE1_5
int
grub_seek (int offset)
{
if (offset > filemax || offset < 0)
return -1;
filepos = offset;
return offset;
}
int
dir (char *dirname)
{
#ifndef NO_DECOMPRESSION
compressed_file = 0;
#endif
if (!(dirname = setup_part (dirname)))
return 0;
if (*dirname != '/')
errnum = ERR_BAD_FILENAME;
if (fsys_type == NUM_FSYS)
errnum = ERR_FSYS_MOUNT;
if (errnum)
return 0;
print_possibilities = 1;
return (*(fsys_table[fsys_type].dir_func)) (dirname);
}
#endif
void
grub_close (void)
{
#ifndef NO_BLOCK_FILES
if (block_file)
return;
#endif
if (fsys_table[fsys_type].close_func != 0)
(*(fsys_table[fsys_type].close_func)) ();
}