📄 biosdisk.c
字号:
/* biosdisk.c - emulate biosdisk *//* * GRUB -- GRand Unified Bootloader * Copyright (C) 1999,2000,2001,2002,2003,2004 Free Software Foundation, Inc. * * GRUB is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with GRUB; if not, write to the Free Software * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */#include <grub/machine/biosdisk.h>#include <grub/disk.h>#include <grub/partition.h>#include <grub/pc_partition.h>#include <grub/types.h>#include <grub/err.h>#include <grub/util/misc.h>#include <stdio.h>#include <stdlib.h>#include <string.h>#include <ctype.h>#include <assert.h>#include <unistd.h>#include <sys/types.h>#include <sys/stat.h>#include <fcntl.h>#include <errno.h>#include <limits.h>#ifdef __linux__# include <sys/ioctl.h> /* ioctl */# if !defined(__GLIBC__) || \ ((__GLIBC__ < 2) || ((__GLIBC__ == 2) && (__GLIBC_MINOR__ < 1)))/* Maybe libc doesn't have large file support. */# include <linux/unistd.h> /* _llseek */# endif /* (GLIBC < 2) || ((__GLIBC__ == 2) && (__GLIBC_MINOR < 1)) */# ifndef BLKFLSBUF# define BLKFLSBUF _IO (0x12,97) /* flush buffer cache */# endif /* ! BLKFLSBUF */# include <sys/ioctl.h> /* ioctl */# ifndef HDIO_GETGEO# define HDIO_GETGEO 0x0301 /* get device geometry *//* If HDIO_GETGEO is not defined, it is unlikely that hd_geometry is defined. */struct hd_geometry{ unsigned char heads; unsigned char sectors; unsigned short cylinders; unsigned long start;};# endif /* ! HDIO_GETGEO */# ifndef BLKGETSIZE# define BLKGETSIZE _IO(0x12,96) /* return device size */# endif /* ! BLKGETSIZE */# ifndef MAJOR# ifndef MINORBITS# define MINORBITS 8# endif /* ! MINORBITS */# define MAJOR(dev) ((unsigned) ((dev) >> MINORBITS))# endif /* ! MAJOR */# ifndef FLOPPY_MAJOR# define FLOPPY_MAJOR 2# endif /* ! FLOPPY_MAJOR */# ifndef LOOP_MAJOR# define LOOP_MAJOR 7# endif /* ! LOOP_MAJOR */#endif /* __linux__ */static char *map[256];#ifdef __linux__/* Check if we have devfs support. */static inthave_devfs (void){ static int dev_devfsd_exists = -1; if (dev_devfsd_exists < 0) { struct stat st; dev_devfsd_exists = stat ("/dev/.devfsd", &st) == 0; } return dev_devfsd_exists;}#endif /* __linux__ */static intget_drive (const char *name){ unsigned long drive; char *p; if ((name[0] != 'f' && name[0] != 'h') || name[1] != 'd') goto fail; drive = strtoul (name + 2, &p, 10); if (p == name + 2) goto fail; if (name[0] == 'h') drive += 0x80; if (drive > sizeof (map) / sizeof (map[0])) goto fail; return (int) drive; fail: grub_error (GRUB_ERR_UNKNOWN_DEVICE, "not a biosdisk"); return -1;}static intcall_hook (int (*hook) (const char *name), int drive){ char name[10]; sprintf (name, (drive & 0x80) ? "hd%d" : "fd%d", drive & (~0x80)); return hook (name);}static intgrub_util_biosdisk_iterate (int (*hook) (const char *name)){ unsigned i; for (i = 0; i < sizeof (map) / sizeof (map[0]); i++) if (map[i] && call_hook (hook, i)) return 1; return 0;}static grub_err_tgrub_util_biosdisk_open (const char *name, grub_disk_t disk){ int drive; struct stat st; drive = get_drive (name); if (drive < 0) return grub_errno; if (! map[drive]) return grub_error (GRUB_ERR_BAD_DEVICE, "no mapping exists for `%s'", name); disk->has_partitions = (drive & 0x80); disk->id = drive; /* Get the size. */#ifdef __linux__ { unsigned long nr; int fd; fd = open (map[drive], O_RDONLY); if (! fd) return grub_error (GRUB_ERR_BAD_DEVICE, "cannot open `%s'", map[drive]); if (fstat (fd, &st) < 0 || ! S_ISBLK (st.st_mode)) { close (fd); goto fail; } if (ioctl (fd, BLKGETSIZE, &nr)) { close (fd); goto fail; } close (fd); disk->total_sectors = nr; grub_util_info ("the size of %s is %lu", name, disk->total_sectors); return GRUB_ERR_NONE; } fail: /* In GNU/Hurd, stat() will return the right size. */#elif !defined (__GNU__)# warning "No special routine to get the size of a block device is implemented for your OS. This is not possibly fatal."#endif if (stat (map[drive], &st) < 0) return grub_error (GRUB_ERR_BAD_DEVICE, "cannot stat `%s'", map[drive]); disk->total_sectors = st.st_size >> GRUB_DISK_SECTOR_BITS; grub_util_info ("the size of %s is %lu", name, disk->total_sectors); return GRUB_ERR_NONE;}#ifdef __linux__static intlinux_find_partition (char *dev, unsigned long sector){ size_t len = strlen (dev); const char *format; char *p; int i; char *real_dev; real_dev = xstrdup (dev); if (have_devfs () && strcmp (real_dev + len - 5, "/disc") == 0) { p = real_dev + len - 4; format = "part%d"; } else if ((strncmp (real_dev + 5, "hd", 2) == 0 || strncmp (real_dev + 5, "sd", 2) == 0) && real_dev[7] >= 'a' && real_dev[7] <= 'z') { p = real_dev + 8; format = "%d"; } else if (strncmp (real_dev + 5, "rd/c", 4) == 0) { p = strchr (real_dev + 9, 'd'); if (! p) return 0; p++; while (*p && isdigit (*p)) p++; format = "p%d"; } else { free (real_dev); return 0; } for (i = 1; i < 10000; i++) { int fd; struct hd_geometry hdg; sprintf (p, format, i); fd = open (real_dev, O_RDONLY); if (! fd) { free (real_dev); return 0; } if (ioctl (fd, HDIO_GETGEO, &hdg)) { close (fd); free (real_dev); return 0; } close (fd); if (hdg.start == sector) { strcpy (dev, real_dev); free (real_dev); return 1; } } free (real_dev); return 0;}#endif /* __linux__ */static intopen_device (const grub_disk_t disk, unsigned long sector, int flags){ int fd;#ifdef O_LARGEFILE flags |= O_LARGEFILE;#endif#ifdef O_SYNC flags |= O_SYNC;#endif#ifdef O_FSYNC flags |= O_FSYNC;#endif #ifdef __linux__ /* 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 is_partition = 0; char dev[PATH_MAX]; strcpy (dev, map[disk->id]); if (disk->partition && strncmp (map[disk->id], "/dev/", 5) == 0) is_partition = linux_find_partition (dev, disk->partition->start); /* Open the partition. */ grub_util_info ("opening the device `%s'", dev); fd = open (dev, flags); if (fd < 0) { grub_error (GRUB_ERR_BAD_DEVICE, "cannot open `%s'", dev); return -1; } /* Make the buffer cache consistent with the physical disk. */ ioctl (fd, BLKFLSBUF, 0); if (is_partition) sector -= disk->partition->start; }#else /* ! __linux__ */ fd = open (map[disk->id], flags); if (fd < 0) { grub_error (GRUB_ERR_BAD_DEVICE, "cannot open `%s'", map[disk->id]); return -1; }#endif /* ! __linux__ */#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 << GRUB_DISK_SECTOR_BITS; if (_llseek (fd, offset >> 32, offset & 0xffffffff, &result, SEEK_SET)) { grub_error (GRUB_ERR_BAD_DEVICE, "cannot seek `%s'", map[disk->id]); close (fd); return -1; } }#else { off_t offset = (off_t) sector << GRUB_DISK_SECTOR_BITS; if (lseek (fd, offset, SEEK_SET) != offset) { grub_error (GRUB_ERR_BAD_DEVICE, "cannot seek `%s'", map[disk->id]); close (fd); return -1; } }#endif return fd;} /* Read LEN bytes from FD in BUF. Return less than or equal to zero if an error occurs, otherwise return LEN. */static ssize_tnread (int fd, char *buf, size_t len){ ssize_t size = len; while (len) { ssize_t ret = read (fd, buf, len); if (ret <= 0) { if (errno == EINTR) continue; else return ret; } len -= ret; buf += ret; } return size;}/* Write LEN bytes from BUF to FD. Return less than or equal to zero if an error occurs, otherwise return LEN. */static ssize_tnwrite (int fd, const char *buf, size_t len){ ssize_t size = len; while (len) { ssize_t ret = write (fd, buf, len); if (ret <= 0) { if (errno == EINTR) continue; else return ret; } len -= ret; buf += ret; } return size;}static grub_err_tgrub_util_biosdisk_read (grub_disk_t disk, unsigned long sector, unsigned long size, char *buf){ int fd; fd = open_device (disk, sector, O_RDONLY); if (fd < 0) return grub_errno;
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -