From 6e7a315eb67cb6c113cf37e1d66c4f11a51a2b3e Mon Sep 17 00:00:00 2001 From: Daniel Baumann Date: Sun, 7 Apr 2024 18:29:51 +0200 Subject: Adding upstream version 2.06. Signed-off-by: Daniel Baumann --- grub-core/osdep/linux/hostdisk.c | 465 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 465 insertions(+) create mode 100644 grub-core/osdep/linux/hostdisk.c (limited to 'grub-core/osdep/linux/hostdisk.c') diff --git a/grub-core/osdep/linux/hostdisk.c b/grub-core/osdep/linux/hostdisk.c new file mode 100644 index 0000000..da62f92 --- /dev/null +++ b/grub-core/osdep/linux/hostdisk.c @@ -0,0 +1,465 @@ +/* hostdisk.c - emulate biosdisk */ +/* + * GRUB -- GRand Unified Bootloader + * Copyright (C) 1999,2000,2001,2002,2003,2004,2006,2007,2008,2009,2010,2011,2012,2013 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 3 of the License, or + * (at your option) any later version. + * + * GRUB 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, see . + */ + +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +# include /* ioctl */ +# include +# ifndef BLKFLSBUF +# define BLKFLSBUF _IO (0x12,97) /* flush buffer cache */ +# endif /* ! BLKFLSBUF */ +# include /* 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 BLKGETSIZE64 +# define BLKGETSIZE64 _IOR(0x12,114,size_t) /* return device size */ +# endif /* ! BLKGETSIZE64 */ + + +grub_int64_t +grub_util_get_fd_size_os (grub_util_fd_t fd, const char *name, unsigned *log_secsize) +{ + unsigned long long nr; + unsigned sector_size, log_sector_size; + + if (ioctl (fd, BLKGETSIZE64, &nr)) + return -1; + + if (ioctl (fd, BLKSSZGET, §or_size)) + return -1; + + if (sector_size & (sector_size - 1) || !sector_size) + return -1; + for (log_sector_size = 0; + (1 << log_sector_size) < sector_size; + log_sector_size++); + + if (log_secsize) + *log_secsize = log_sector_size; + + if (nr & ((1 << log_sector_size) - 1)) + grub_util_error ("%s", _("unaligned device size")); + + return nr; +} + +static char * +sysfs_partition_path (const char *dev, const char *entry) +{ + const char *argv[7]; + int fd; + pid_t pid; + FILE *udevadm; + char *buf = NULL; + size_t len = 0; + char *path = NULL; + + argv[0] = "udevadm"; + argv[1] = "info"; + argv[2] = "--query"; + argv[3] = "path"; + argv[4] = "--name"; + argv[5] = dev; + argv[6] = NULL; + + pid = grub_util_exec_pipe (argv, &fd); + + if (!pid) + return NULL; + + /* Parent. Read udevadm's output. */ + udevadm = fdopen (fd, "r"); + if (!udevadm) + { + grub_util_warn (_("Unable to open stream from %s: %s"), + "udevadm", strerror (errno)); + close (fd); + goto out; + } + + if (getline (&buf, &len, udevadm) > 0) + { + char *newline; + + newline = strchr (buf, '\n'); + if (newline) + *newline = '\0'; + path = xasprintf ("/sys%s/%s", buf, entry); + } + +out: + if (udevadm) + fclose (udevadm); + waitpid (pid, NULL, 0); + free (buf); + + return path; +} + +static int +sysfs_partition_start (const char *dev, grub_disk_addr_t *start) +{ + char *path; + FILE *fp; + unsigned long long val; + int ret = 0; + + path = sysfs_partition_path (dev, "start"); + if (!path) + return 0; + + fp = grub_util_fopen (path, "r"); + if (!fp) + goto out; + + if (fscanf (fp, "%llu", &val) == 1) + { + *start = (grub_disk_addr_t) val; + ret = 1; + } + +out: + free (path); + if (fp) + fclose (fp); + + return ret; +} + +grub_disk_addr_t +grub_util_find_partition_start_os (const char *dev) +{ + grub_disk_addr_t start = 0; + grub_util_fd_t fd; + struct hd_geometry hdg; + + if (sysfs_partition_start (dev, &start)) + return start; + + fd = open (dev, O_RDONLY); + if (fd == -1) + { + grub_error (GRUB_ERR_BAD_DEVICE, N_("cannot open `%s': %s"), + dev, strerror (errno)); + return 0; + } + + if (ioctl (fd, HDIO_GETGEO, &hdg)) + { + grub_error (GRUB_ERR_BAD_DEVICE, + "cannot get disk geometry of `%s'", dev); + close (fd); + return 0; + } + + close (fd); + + return hdg.start; +} + +/* Cache of partition start sectors for each disk. */ +struct linux_partition_cache +{ + struct linux_partition_cache *next; + struct linux_partition_cache **prev; + char *dev; + unsigned long start; + int partno; +}; + +struct linux_partition_cache *linux_partition_cache_list; + +/* Check if we have devfs support. */ +static int +have_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; +} + +#pragma GCC diagnostic ignored "-Wformat-nonliteral" + +static int +grub_hostdisk_linux_find_partition (char *dev, grub_disk_addr_t sector) +{ + size_t len = strlen (dev); + const char *format; + char *p; + int i; + char real_dev[PATH_MAX]; + struct linux_partition_cache *cache; + int missing = 0; + + strcpy(real_dev, dev); + + if (have_devfs () && strcmp (real_dev + len - 5, "/disc") == 0) + { + p = real_dev + len - 4; + format = "part%d"; + } + else if (strncmp (real_dev, "/dev/disk/by-id/", + sizeof ("/dev/disk/by-id/") - 1) == 0) + { + p = real_dev + len; + format = "-part%d"; + } + else if (real_dev[len - 1] >= '0' && real_dev[len - 1] <= '9') + { + p = real_dev + len; + format = "p%d"; + } + else + { + p = real_dev + len; + format = "%d"; + } + + for (cache = linux_partition_cache_list; cache; cache = cache->next) + { + if (strcmp (cache->dev, dev) == 0 && cache->start == sector) + { + sprintf (p, format, cache->partno); + strcpy (dev, real_dev); + return 1; + } + } + + for (i = 1; i < 10000; i++) + { + grub_util_fd_t fd; + grub_disk_addr_t start; + struct stat st; + + sprintf (p, format, i); + + fd = open (real_dev, O_RDONLY); + if (fd == -1) + { + if (missing++ < 10) + continue; + else + return 0; + } + missing = 0; + + if (fstat (fd, &st) < 0 + || !grub_util_device_is_mapped_stat (&st) + || !grub_util_get_dm_node_linear_info (st.st_rdev, 0, 0, &start)) + start = grub_util_find_partition_start_os (real_dev); + /* We don't care about errors here. */ + grub_errno = GRUB_ERR_NONE; + + close (fd); + + if (start == sector) + { + struct linux_partition_cache *new_cache_item; + + new_cache_item = xmalloc (sizeof *new_cache_item); + new_cache_item->dev = xstrdup (dev); + new_cache_item->start = start; + new_cache_item->partno = i; + grub_list_push (GRUB_AS_LIST_P (&linux_partition_cache_list), + GRUB_AS_LIST (new_cache_item)); + + strcpy (dev, real_dev); + return 1; + } + } + + return 0; +} + +#pragma GCC diagnostic error "-Wformat-nonliteral" + +void +grub_hostdisk_flush_initial_buffer (const char *os_dev) +{ + grub_util_fd_t fd; + struct stat st; + + fd = open (os_dev, O_RDONLY); + if (fd >= 0 && fstat (fd, &st) >= 0 && S_ISBLK (st.st_mode)) + ioctl (fd, BLKFLSBUF, 0); + if (fd >= 0) + close (fd); +} + +int +grub_util_fd_open_device (const grub_disk_t disk, grub_disk_addr_t sector, int flags, + grub_disk_addr_t *max) +{ + grub_util_fd_t fd; + struct grub_util_hostdisk_data *data = disk->data; + + *max = ~0ULL; + +#ifdef O_LARGEFILE + flags |= O_LARGEFILE; +#endif +#ifdef O_SYNC + flags |= O_SYNC; +#endif +#ifdef O_FSYNC + flags |= O_FSYNC; +#endif +#ifdef O_BINARY + flags |= O_BINARY; +#endif +#ifdef O_CLOEXEC + flags |= O_CLOEXEC; +#endif + + /* 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]; + grub_disk_addr_t part_start = 0; + + part_start = grub_partition_get_start (disk->partition) + >> (disk->log_sector_size - GRUB_DISK_SECTOR_BITS); + + strncpy (dev, grub_util_biosdisk_get_osdev (disk), sizeof (dev) - 1); + dev[sizeof(dev) - 1] = '\0'; + if (disk->partition + && strncmp (dev, "/dev/", 5) == 0) + { + if (sector >= part_start) + is_partition = grub_hostdisk_linux_find_partition (dev, part_start); + else + *max = part_start - sector; + } + + reopen: + + if (data->dev && strcmp (data->dev, dev) == 0 && + data->access_mode == (flags & O_ACCMODE)) + { + grub_dprintf ("hostdisk", "reusing open device `%s'\n", dev); + fd = data->fd; + } + else + { + free (data->dev); + data->dev = 0; + if (data->fd != -1) + { + if (data->access_mode == O_RDWR || data->access_mode == O_WRONLY) + { + fsync (data->fd); + if (data->is_disk) + ioctl (data->fd, BLKFLSBUF, 0); + } + + close (data->fd); + data->fd = -1; + } + + /* Open the partition. */ + grub_dprintf ("hostdisk", "opening the device `%s' in open_device()\n", dev); + fd = open (dev, flags); + if (fd < 0) + { + grub_error (GRUB_ERR_BAD_DEVICE, N_("cannot open `%s': %s"), + dev, strerror (errno)); + return -1; + } + + data->dev = xstrdup (dev); + data->access_mode = (flags & O_ACCMODE); + data->fd = fd; + + if (data->is_disk) + ioctl (data->fd, BLKFLSBUF, 0); + } + + if (is_partition) + { + *max = grub_util_get_fd_size (fd, dev, 0); + *max >>= disk->log_sector_size; + if (sector - part_start >= *max) + { + *max = disk->partition->len - (sector - part_start); + if (*max == 0) + *max = ~0ULL; + is_partition = 0; + strncpy (dev, grub_util_biosdisk_get_osdev (disk), sizeof (dev) - 1); + dev[sizeof(dev) - 1] = '\0'; + goto reopen; + } + sector -= part_start; + *max -= sector; + } + } + + if (grub_util_fd_seek (fd, sector << disk->log_sector_size)) + { + close (fd); + grub_error (GRUB_ERR_BAD_DEVICE, N_("cannot seek `%s': %s"), + grub_util_biosdisk_get_osdev (disk), strerror (errno)); + return -1; + } + + return fd; +} -- cgit v1.2.3