diff --git a/ChangeLog b/ChangeLog index 9e5d2a356..156d45768 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,9 @@ +2013-09-22 Vladimir Serbinenko + + * grub-core/kern/emu/hostdisk.c: Disentagle into a series of OS-specific + files rather than one file with loads of #if's. + * util/getroot.c: Likewise. + 2013-09-22 Vladimir Serbinenko * grub-core/lib/posix_wrap/sys/types.h: Use stddef on *BSD. diff --git a/Makefile.util.def b/Makefile.util.def index 40781235c..38ce370bf 100644 --- a/Makefile.util.def +++ b/Makefile.util.def @@ -10,8 +10,12 @@ library = { common = grub-core/kern/device.c; common = grub-core/kern/disk.c; common = util/getroot.c; + common = util/getroot_os.c; + common = util/getroot_devmapper.c; common = util/raid.c; common = grub-core/kern/emu/hostdisk.c; + common = grub-core/kern/emu/hostdisk_devmapper.c; + common = grub-core/kern/emu/hostdisk_os.c; common = grub-core/kern/emu/misc.c; common = grub-core/kern/emu/mm.c; common = grub-core/kern/env.c; diff --git a/conf/Makefile.extra-dist b/conf/Makefile.extra-dist index 4b67d4dad..b666468e3 100644 --- a/conf/Makefile.extra-dist +++ b/conf/Makefile.extra-dist @@ -61,3 +61,22 @@ EXTRA_DIST += m4/threadlib.m4 EXTRA_DIST += m4/uintmax_t.m4 EXTRA_DIST += m4/visibility.m4 EXTRA_DIST += m4/math_h.m4 + +EXTRA_DIST += grub-core/kern/emu/hostdisk_apple.c +EXTRA_DIST += grub-core/kern/emu/hostdisk_basic.c +EXTRA_DIST += grub-core/kern/emu/hostdisk_bsd.c +EXTRA_DIST += grub-core/kern/emu/hostdisk_cygwin.c +EXTRA_DIST += grub-core/kern/emu/hostdisk_freebsd.c +EXTRA_DIST += grub-core/kern/emu/hostdisk_hurd.c +EXTRA_DIST += grub-core/kern/emu/hostdisk_linux.c +EXTRA_DIST += grub-core/kern/emu/hostdisk_mingw.c +EXTRA_DIST += grub-core/kern/emu/hostdisk_sun.c + +EXTRA_DIST += util/getroot_apple.c +EXTRA_DIST += util/getroot_basic.c +EXTRA_DIST += util/getroot_bsd.c +EXTRA_DIST += util/getroot_cygwin.c +EXTRA_DIST += util/getroot_freebsd.c +EXTRA_DIST += util/getroot_hurd.c +EXTRA_DIST += util/getroot_linux.c +EXTRA_DIST += util/getroot_sun.c diff --git a/grub-core/Makefile.core.def b/grub-core/Makefile.core.def index bf0a6cd26..a4e6862bb 100644 --- a/grub-core/Makefile.core.def +++ b/grub-core/Makefile.core.def @@ -241,6 +241,8 @@ kernel = { emu = kern/emu/error.c; emu = kern/emu/cache_s.S; emu = kern/emu/hostdisk.c; + emu = kern/emu/hostdisk_devmapper.c; + emu = kern/emu/hostdisk_os.c; emu = kern/emu/hostfs.c; emu = kern/emu/main.c; emu = kern/emu/argp_common.c; diff --git a/grub-core/kern/emu/hostdisk.c b/grub-core/kern/emu/hostdisk.c index b90a26d48..f1f757055 100644 --- a/grub-core/kern/emu/hostdisk.c +++ b/grub-core/kern/emu/hostdisk.c @@ -43,12 +43,6 @@ #include #include -#ifdef __MINGW32__ -#include -#include -#include "dirname.h" -#endif - #ifdef __linux__ # include /* ioctl */ # include @@ -61,73 +55,12 @@ # 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 */ #endif /* __linux__ */ -#ifdef __CYGWIN__ -# include -# include /* BLKGETSIZE64 */ -# include /* HDIO_GETGEO */ -#endif - #if defined(__FreeBSD__) || defined(__FreeBSD_kernel__) -# include /* DIOCGMEDIASIZE */ -# include # include -# include -#include #endif -#if defined (__sun__) -# include -#endif - -#if defined(__APPLE__) -# include -#endif - -#ifdef HAVE_DEVICE_MAPPER -# include -#endif - -#if defined(__NetBSD__) || defined(__OpenBSD__) -# define HAVE_DIOCGDINFO -# include -# include /* struct disklabel */ -# include /* struct dkwedge_info */ -#else /* !defined(__NetBSD__) && !defined(__FreeBSD__) && !defined(__FreeBSD_kernel__) */ -# undef HAVE_DIOCGDINFO -#endif /* defined(__NetBSD__) || defined(__FreeBSD__) || defined(__FreeBSD_kernel__) */ - -#if defined(__NetBSD__) || defined(__OpenBSD__) -# ifdef HAVE_GETRAWPARTITION -# include /* getrawpartition */ -# endif /* HAVE_GETRAWPARTITION */ -# if defined(__NetBSD__) -# include -# endif -# if defined(__OpenBSD__) -# include -# endif -# ifndef RAW_FLOPPY_MAJOR -# define RAW_FLOPPY_MAJOR 9 -# endif /* ! RAW_FLOPPY_MAJOR */ -#endif /* defined(__NetBSD__) */ - static struct { char *drive; @@ -144,49 +77,6 @@ struct grub_util_biosdisk_data int device_map; }; -#ifdef __linux__ -/* 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; -} -#endif /* __linux__ */ - -#if defined(__NetBSD__) -/* Adjust device driver parameters. This function should be called just - after successfully opening the device. For now, it simply prevents the - floppy driver from retrying operations on failure, as otherwise the - driver takes a while to abort when there is no floppy in the drive. */ -static void -configure_device_driver (int fd) -{ - struct stat st; - - if (fstat (fd, &st) < 0 || ! S_ISCHR (st.st_mode)) - return; - if (major(st.st_rdev) == RAW_FLOPPY_MAJOR) - { - int floppy_opts; - - if (ioctl (fd, FDIOCGETOPTS, &floppy_opts) == -1) - return; - floppy_opts |= FDOPT_NORETRY; - if (ioctl (fd, FDIOCSETOPTS, &floppy_opts) == -1) - return; - } -} -#endif /* defined(__NetBSD__) */ - static int unescape_cmp (const char *a, const char *b_escaped) { @@ -251,171 +141,41 @@ grub_util_biosdisk_iterate (grub_disk_dev_iterate_hook_t hook, void *hook_data, return 0; } - #ifdef __MINGW32__ grub_uint64_t -grub_util_get_fd_size (int fd __attribute__ ((unused)), const char *name_in, +grub_util_get_fd_size (int fd, const char *name, unsigned *log_secsize) { - HANDLE hd; - grub_int64_t size = -1LL; - int log_sector_size = 9; - char *name = xstrdup (name_in); - - if (log_secsize) - *log_secsize = log_sector_size; - - strip_trailing_slashes(name); - hd = CreateFile (name, GENERIC_READ, FILE_SHARE_READ | FILE_SHARE_WRITE, - 0, OPEN_EXISTING, 0, 0); - - if (hd == INVALID_HANDLE_VALUE) - { - free (name); - return size; - } - - if (((name[0] == '/') || (name[0] == '\\')) && - ((name[1] == '/') || (name[1] == '\\')) && - (name[2] == '.') && - ((name[3] == '/') || (name[3] == '\\')) && - (! strncasecmp (name + 4, "PHYSICALDRIVE", 13))) - { - DWORD nr; - DISK_GEOMETRY g; - - if (! DeviceIoControl (hd, IOCTL_DISK_GET_DRIVE_GEOMETRY, - 0, 0, &g, sizeof (g), &nr, 0)) - goto fail; - - size = g.Cylinders.QuadPart; - size *= g.TracksPerCylinder * g.SectorsPerTrack * g.BytesPerSector; - - for (log_sector_size = 0; - (1 << log_sector_size) < g.BytesPerSector; - log_sector_size++); - } - else - { - ULARGE_INTEGER s; - - s.LowPart = GetFileSize (hd, &s.HighPart); - size = s.QuadPart; - } - -fail: - - if (log_secsize) - *log_secsize = log_sector_size; - - free (name); - - CloseHandle (hd); - - return size; + return grub_util_get_fd_size_os (fd, name, log_secsize); } -#endif +#else -#if !defined(__MINGW32__) grub_uint64_t grub_util_get_fd_size (int fd, const char *name, unsigned *log_secsize) { -#if !defined (__GNU__) -# if defined(__NetBSD__) || defined(__OpenBSD__) - struct disklabel label; -# elif defined (__sun__) - struct dk_minfo minfo; -# else - unsigned long long nr; -# endif -#endif - unsigned sector_size, log_sector_size; struct stat st; + grub_int64_t ret = -1; if (fstat (fd, &st) < 0) /* TRANSLATORS: "stat" comes from the name of POSIX function. */ grub_util_error (_("cannot stat `%s': %s"), name, strerror (errno)); - -#if defined(__linux__) || defined(__CYGWIN__) || defined(__FreeBSD__) || \ - defined(__FreeBSD_kernel__) || defined(__APPLE__) || defined(__NetBSD__) \ - || defined (__sun__) || defined(__OpenBSD__) - -# if defined(__FreeBSD__) || defined(__FreeBSD_kernel__) || defined(__APPLE__) || defined(__NetBSD__) || defined (__sun__) || defined(__OpenBSD__) - if (! S_ISCHR (st.st_mode)) -# else - if (! S_ISBLK (st.st_mode)) -# endif - goto fail; - -# if defined(__FreeBSD__) || defined(__FreeBSD_kernel__) - if (ioctl (fd, DIOCGMEDIASIZE, &nr)) -# elif defined(__APPLE__) - if (ioctl (fd, DKIOCGETBLOCKCOUNT, &nr)) -# elif defined(__NetBSD__) || defined(__OpenBSD__) -# if defined(__NetBSD__) - configure_device_driver (fd); -# endif - if (ioctl (fd, DIOCGDINFO, &label) == -1) -# elif defined (__sun__) - if (!ioctl (fd, DKIOCGMEDIAINFO, &minfo)) -# else - if (ioctl (fd, BLKGETSIZE64, &nr)) -# endif - goto fail; - -# if defined(__FreeBSD__) || defined(__FreeBSD_kernel__) - if (ioctl (fd, DIOCGSECTORSIZE, §or_size)) - goto fail; -# elif defined(__APPLE__) - if (ioctl (fd, DKIOCGETBLOCKSIZE, §or_size)) - goto fail; -# elif defined(__sun__) - sector_size = minfo.dki_lbsize; -# elif defined(__NetBSD__) || defined(__OpenBSD__) - sector_size = label.d_secsize; -# else - if (ioctl (fd, BLKSSZGET, §or_size)) - goto fail; -# endif - if (sector_size & (sector_size - 1) || !sector_size) - goto fail; - for (log_sector_size = 0; - (1 << log_sector_size) < sector_size; - log_sector_size++); - - if (log_secsize) - *log_secsize = log_sector_size; - -# if defined (__APPLE__) - return nr << log_sector_size; -# elif defined(__NetBSD__) || defined(__OpenBSD__) - return (grub_uint64_t) label.d_secperunit << log_sector_size; -# elif defined (__sun__) - return minfo.dki_capacity << log_sector_size; -# else - if (nr & ((1 << log_sector_size) - 1)) - grub_util_error ("%s", _("unaligned device size")); - - return nr; -# endif - - 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." +#if GRUB_DISK_DEVS_ARE_CHAR + if (S_ISCHR (st.st_mode)) +#else + if (S_ISBLK (st.st_mode)) #endif - - sector_size = 512; - log_sector_size = 9; + ret = grub_util_get_fd_size_os (fd, name, log_secsize); + if (ret != -1LL) + return ret; if (log_secsize) - *log_secsize = 9; + *log_secsize = 9; return st.st_size; } + #endif static grub_err_t @@ -457,7 +217,7 @@ grub_util_biosdisk_open (const char *name, grub_disk_t disk) #if !defined(__MINGW32__) -# if defined(__FreeBSD__) || defined(__FreeBSD_kernel__) || defined(__APPLE__) || defined(__NetBSD__) +# if GRUB_DISK_DEVS_ARE_CHAR if (fstat (fd, &st) < 0 || ! S_ISCHR (st.st_mode)) # else if (fstat (fd, &st) < 0 || ! S_ISBLK (st.st_mode)) @@ -474,412 +234,6 @@ grub_util_biosdisk_open (const char *name, grub_disk_t disk) } } -#ifdef HAVE_DEVICE_MAPPER -static void device_mapper_null_log (int level __attribute__ ((unused)), - const char *file __attribute__ ((unused)), - int line __attribute__ ((unused)), - int dm_errno __attribute__ ((unused)), - const char *f __attribute__ ((unused)), - ...) -{ -} - -int -grub_device_mapper_supported (void) -{ - static int supported = -1; - - if (supported == -1) - { - struct dm_task *dmt; - - /* Suppress annoying log messages. */ - dm_log_with_errno_init (&device_mapper_null_log); - - dmt = dm_task_create (DM_DEVICE_VERSION); - supported = (dmt != NULL); - if (dmt) - dm_task_destroy (dmt); - - /* Restore the original logger. */ - dm_log_with_errno_init (NULL); - } - - return supported; -} -#endif /* HAVE_DEVICE_MAPPER */ - -int -grub_util_device_is_mapped (const char *dev) -{ -#ifdef HAVE_DEVICE_MAPPER - struct stat st; - - if (!grub_device_mapper_supported ()) - return 0; - - if (stat (dev, &st) < 0) - return 0; - - return dm_is_dm_major (major (st.st_rdev)); -#else - return 0; -#endif /* HAVE_DEVICE_MAPPER */ -} - -#ifdef HAVE_DEVICE_MAPPER -int -grub_util_get_dm_node_linear_info (const char *dev, - int *maj, int *min, - grub_disk_addr_t *st) -{ - struct dm_task *dmt; - void *next = NULL; - uint64_t length, start; - char *target, *params; - char *ptr; - int major = 0, minor = 0; - int first = 1; - grub_disk_addr_t partstart = 0; - const char *node_uuid; - - while (1) - { - dmt = dm_task_create(DM_DEVICE_TABLE); - if (!dmt) - break; - - if (! (first ? dm_task_set_name (dmt, dev) - : dm_task_set_major_minor (dmt, major, minor, 0))) - { - dm_task_destroy (dmt); - break; - } - dm_task_no_open_count(dmt); - if (!dm_task_run(dmt)) - { - dm_task_destroy (dmt); - break; - } - node_uuid = dm_task_get_uuid (dmt); - if (node_uuid && (strncmp (node_uuid, "LVM-", 4) == 0 - || strncmp (node_uuid, "mpath-", 6) == 0 - || strncmp (node_uuid, "DMRAID-", 7) == 0)) - { - dm_task_destroy (dmt); - break; - } - - next = dm_get_next_target(dmt, next, &start, &length, - &target, ¶ms); - if (grub_strcmp (target, "linear") != 0) - { - dm_task_destroy (dmt); - break; - } - major = grub_strtoul (params, &ptr, 10); - if (grub_errno) - { - dm_task_destroy (dmt); - grub_errno = GRUB_ERR_NONE; - return 0; - } - if (*ptr != ':') - { - dm_task_destroy (dmt); - return 0; - } - ptr++; - minor = grub_strtoul (ptr, &ptr, 10); - if (grub_errno) - { - grub_errno = GRUB_ERR_NONE; - dm_task_destroy (dmt); - return 0; - } - - if (*ptr != ' ') - { - dm_task_destroy (dmt); - return 0; - } - ptr++; - partstart += grub_strtoull (ptr, &ptr, 10); - if (grub_errno) - { - grub_errno = GRUB_ERR_NONE; - dm_task_destroy (dmt); - return 0; - } - - dm_task_destroy (dmt); - first = 0; - if (!dm_is_dm_major (major)) - break; - } - if (first) - return 0; - if (maj) - *maj = major; - if (min) - *min = minor; - if (st) - *st = partstart; - return 1; -} -#endif - -#if defined (__FreeBSD__) || defined(__FreeBSD_kernel__) - -/* FIXME: geom actually gives us the whole container hierarchy. - It can be used more efficiently than this. */ -void -grub_util_follow_gpart_up (const char *name, grub_disk_addr_t *off_out, char **name_out) -{ - struct gmesh mesh; - struct gclass *class; - int error; - struct ggeom *geom; - - grub_util_info ("following geom '%s'", name); - - error = geom_gettree (&mesh); - if (error != 0) - /* TRANSLATORS: geom is the name of (k)FreeBSD device framework. - Usually left untranslated. - */ - grub_util_error ("%s", _("couldn't open geom")); - - LIST_FOREACH (class, &mesh.lg_class, lg_class) - if (strcasecmp (class->lg_name, "part") == 0) - break; - if (!class) - /* TRANSLATORS: geom is the name of (k)FreeBSD device framework. - Usually left untranslated. "part" is the identifier of one of its - classes. */ - grub_util_error ("%s", _("couldn't find geom `part' class")); - - LIST_FOREACH (geom, &class->lg_geom, lg_geom) - { - struct gprovider *provider; - LIST_FOREACH (provider, &geom->lg_provider, lg_provider) - if (strcmp (provider->lg_name, name) == 0) - { - char *name_tmp = xstrdup (geom->lg_name); - grub_disk_addr_t off = 0; - struct gconfig *config; - grub_util_info ("geom '%s' has parent '%s'", name, geom->lg_name); - - grub_util_follow_gpart_up (name_tmp, &off, name_out); - free (name_tmp); - LIST_FOREACH (config, &provider->lg_config, lg_config) - if (strcasecmp (config->lg_name, "start") == 0) - off += strtoull (config->lg_val, 0, 10); - if (off_out) - *off_out = off; - return; - } - } - grub_util_info ("geom '%s' has no parent", name); - if (name_out) - *name_out = xstrdup (name); - if (off_out) - *off_out = 0; -} - -grub_disk_addr_t -grub_hostdisk_find_partition_start (const char *dev) -{ - grub_disk_addr_t out; - if (strncmp (dev, "/dev/", sizeof ("/dev/") - 1) != 0) - return 0; - grub_util_follow_gpart_up (dev + sizeof ("/dev/") - 1, &out, NULL); - - return out; -} - -#elif defined(__linux__) || defined(__CYGWIN__) || defined(HAVE_DIOCGDINFO) || defined (__sun__) -grub_disk_addr_t -grub_hostdisk_find_partition_start (const char *dev) -{ - int fd; -#ifdef __sun__ - struct extpart_info pinfo; -# elif !defined(HAVE_DIOCGDINFO) - struct hd_geometry hdg; -# else /* defined(HAVE_DIOCGDINFO) */ -# if defined(__NetBSD__) - struct dkwedge_info dkw; -# endif /* defined(__NetBSD__) */ - struct disklabel label; - int p_index; -# endif /* !defined(HAVE_DIOCGDINFO) */ - -# ifdef HAVE_DEVICE_MAPPER - grub_disk_addr_t partition_start; - if (grub_util_device_is_mapped (dev) - && grub_util_get_dm_node_linear_info (dev, 0, 0, &partition_start)) - return partition_start; -# endif /* HAVE_DEVICE_MAPPER */ - - 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 defined(__sun__) - if (ioctl (fd, DKIOCEXTPARTINFO, &pinfo)) -# elif !defined(HAVE_DIOCGDINFO) - if (ioctl (fd, HDIO_GETGEO, &hdg)) -# else /* defined(HAVE_DIOCGDINFO) */ -# if defined(__NetBSD__) - configure_device_driver (fd); - /* First handle the case of disk wedges. */ - if (ioctl (fd, DIOCGWEDGEINFO, &dkw) == 0) - { - close (fd); - return (grub_disk_addr_t) dkw.dkw_offset; - } -# endif /* defined(__NetBSD__) */ - if (ioctl (fd, DIOCGDINFO, &label) == -1) -# endif /* !defined(HAVE_DIOCGDINFO) */ - { - grub_error (GRUB_ERR_BAD_DEVICE, -# if !defined(HAVE_DIOCGDINFO) - "cannot get disk geometry of `%s'", dev); -# else /* defined(HAVE_DIOCGDINFO) */ - "cannot get disk label of `%s'", dev); -# endif /* !defined(HAVE_DIOCGDINFO) */ - close (fd); - return 0; - } - - close (fd); - -#ifdef __sun__ - return pinfo.p_start; -# elif !defined(HAVE_DIOCGDINFO) - return hdg.start; -# else /* defined(HAVE_DIOCGDINFO) */ - if (dev[0]) - p_index = dev[strlen(dev) - 1] - 'a'; - else - p_index = -1; - - if (p_index >= label.d_npartitions || p_index < 0) - { - grub_error (GRUB_ERR_BAD_DEVICE, - "no disk label entry for `%s'", dev); - return 0; - } - return (grub_disk_addr_t) label.d_partitions[p_index].p_offset; -# endif /* !defined(HAVE_DIOCGDINFO) */ -} -#endif /* __linux__ || __CYGWIN__ || HAVE_DIOCGDINFO */ - -#ifdef __linux__ -/* 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; - -static int -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++) - { - int fd; - grub_disk_addr_t start; - - sprintf (p, format, i); - - fd = open (real_dev, O_RDONLY); - if (fd == -1) - { - if (missing++ < 10) - continue; - else - return 0; - } - missing = 0; - close (fd); - - start = grub_hostdisk_find_partition_start (real_dev); - /* We don't care about errors here. */ - grub_errno = GRUB_ERR_NONE; - - 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; -} -#endif /* __linux__ */ - #if defined(__linux__) && (!defined(__GLIBC__) || \ ((__GLIBC__ < 2) || ((__GLIBC__ == 2) && (__GLIBC_MINOR__ < 1)))) /* Maybe libc doesn't have large file support. */ @@ -911,21 +265,6 @@ grub_util_fd_seek (int fd, const char *name, grub_uint64_t off) } #endif -static void -flush_initial_buffer (const char *os_dev __attribute__ ((unused))) -{ -#ifdef __linux__ - int 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); -#endif -} - const char * grub_hostdisk_os_dev_to_grub_drive (const char *os_disk, int add) { @@ -961,7 +300,7 @@ grub_hostdisk_os_dev_to_grub_drive (const char *os_disk, int add) strcpy (map[i].drive + sizeof ("hostdisk/") - 1, os_disk); map[i].device_map = 0; - flush_initial_buffer (os_disk); + grub_hostdisk_flush_initial_buffer (os_disk); return map[i].drive; } @@ -1003,7 +342,7 @@ open_device (const grub_disk_t disk, grub_disk_addr_t sector, int flags, && strncmp (map[disk->id].device, "/dev/", 5) == 0) { if (sector >= part_start) - is_partition = linux_find_partition (dev, part_start); + is_partition = grub_hostdisk_linux_find_partition (dev, part_start); else *max = part_start - sector; } @@ -1025,10 +364,8 @@ open_device (const grub_disk_t disk, grub_disk_addr_t sector, int flags, if (data->access_mode == O_RDWR || data->access_mode == O_WRONLY) { fsync (data->fd); -#ifdef __linux__ if (data->is_disk) ioctl (data->fd, BLKFLSBUF, 0); -#endif } close (data->fd); @@ -1049,10 +386,8 @@ open_device (const grub_disk_t disk, grub_disk_addr_t sector, int flags, data->access_mode = (flags & O_ACCMODE); data->fd = fd; -#ifdef __linux__ if (data->is_disk) ioctl (data->fd, BLKFLSBUF, 0); -#endif } if (is_partition) @@ -1106,10 +441,6 @@ open_device (const grub_disk_t disk, grub_disk_addr_t sector, int flags, if (data->access_mode == O_RDWR || data->access_mode == O_WRONLY) { fsync (data->fd); -#ifdef __linux__ - if (data->is_disk) - ioctl (data->fd, BLKFLSBUF, 0); -#endif } close (data->fd); data->fd = -1; @@ -1147,9 +478,7 @@ open_device (const grub_disk_t disk, grub_disk_addr_t sector, int flags, } #endif /* ! __linux__ */ -#if defined(__NetBSD__) - configure_device_driver (fd); -#endif /* defined(__NetBSD__) */ + grub_hostdisk_configure_device_driver (fd); if (grub_util_fd_seek (fd, map[disk->id].device, sector << disk->log_sector_size)) @@ -1485,7 +814,7 @@ read_device_map (const char *dev_map) grub_util_info ("adding `%s' -> `%s' from device.map", map[drive].drive, map[drive].device); - flush_initial_buffer (map[drive].device); + grub_hostdisk_flush_initial_buffer (map[drive].device); } fclose (fp); diff --git a/grub-core/kern/emu/hostdisk_apple.c b/grub-core/kern/emu/hostdisk_apple.c new file mode 100644 index 000000000..8ba785e2d --- /dev/null +++ b/grub-core/kern/emu/hostdisk_apple.c @@ -0,0 +1,74 @@ +/* + * 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 + +grub_uint64_t +grub_util_get_fd_size (int fd, const char *name, unsigned *log_secsize) +{ + unsigned long long nr; + unsigned sector_size, log_sector_size; + + if (ioctl (fd, DKIOCGETBLOCKCOUNT, &nr)) + return -1; + + if (ioctl (fd, DKIOCGETBLOCKSIZE, §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; + + return nr << log_sector_size; +} + +void +grub_hostdisk_configure_device_driver (int fd __attribute__ ((unused))) +{ +} diff --git a/grub-core/kern/emu/hostdisk_basic.c b/grub-core/kern/emu/hostdisk_basic.c new file mode 100644 index 000000000..a9342d017 --- /dev/null +++ b/grub-core/kern/emu/hostdisk_basic.c @@ -0,0 +1,63 @@ +/* + * 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 + +grub_int64_t +grub_util_get_fd_size_os (int fd __attribute__ ((unused)), + const char *name __attribute__ ((unused)), + unsigned *log_secsize __attribute__ ((unused))) +{ +# warning "No special routine to get the size of a block device is implemented for your OS. This is not possibly fatal." + + return -1; +} + +void +grub_hostdisk_configure_device_driver (int fd __attribute__ ((unused))) +{ +} + +void +grub_hostdisk_flush_initial_buffer (const char *os_dev __attribute__ ((unused))) +{ +} diff --git a/grub-core/kern/emu/hostdisk_bsd.c b/grub-core/kern/emu/hostdisk_bsd.c new file mode 100644 index 000000000..b986f0321 --- /dev/null +++ b/grub-core/kern/emu/hostdisk_bsd.c @@ -0,0 +1,115 @@ +/* + * 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 /* struct disklabel */ +# include /* struct dkwedge_info */ +# ifdef HAVE_GETRAWPARTITION +# include /* getrawpartition */ +# endif /* HAVE_GETRAWPARTITION */ +# if defined(__NetBSD__) +# include +# endif +# if defined(__OpenBSD__) +# include +# endif + +#if defined(__NetBSD__) +/* Adjust device driver parameters. This function should be called just + after successfully opening the device. For now, it simply prevents the + floppy driver from retrying operations on failure, as otherwise the + driver takes a while to abort when there is no floppy in the drive. */ +void +grub_hostdisk_configure_device_driver (int fd) +{ + struct stat st; + + if (fstat (fd, &st) < 0 || ! S_ISCHR (st.st_mode)) + return; + if (major(st.st_rdev) == RAW_FLOPPY_MAJOR) + { + int floppy_opts; + + if (ioctl (fd, FDIOCGETOPTS, &floppy_opts) == -1) + return; + floppy_opts |= FDOPT_NORETRY; + if (ioctl (fd, FDIOCSETOPTS, &floppy_opts) == -1) + return; + } +} +#else +void +grub_hostdisk_configure_device_driver (int fd __attribute__ ((unused))) +{ +} +#endif + +grub_int64_t +grub_util_get_fd_size_os (int fd, const char *name, unsigned *log_secsize) +{ + struct disklabel label; + unsigned sector_size, log_sector_size; + + grub_hostdisk_configure_device_driver (fd); + + if (ioctl (fd, DIOCGDINFO, &label) == -1) + return -1; + + sector_size = label.d_secsize; + 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; + + return (grub_uint64_t) label.d_secperunit << log_sector_size; +} + +void +grub_hostdisk_flush_initial_buffer (const char *os_dev __attribute__ ((unused))) +{ +} diff --git a/grub-core/kern/emu/hostdisk_cygwin.c b/grub-core/kern/emu/hostdisk_cygwin.c new file mode 100644 index 000000000..c1e4bd56a --- /dev/null +++ b/grub-core/kern/emu/hostdisk_cygwin.c @@ -0,0 +1,84 @@ +/* + * 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 /* BLKGETSIZE64 */ +# include /* HDIO_GETGEO */ + +grub_int64_t +grub_util_get_fd_size_os (int 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; +} + +void +grub_hostdisk_flush_initial_buffer (const char *os_dev __attribute__ ((unused))) +{ +} + +void +grub_hostdisk_configure_device_driver (int fd __attribute__ ((unused))) +{ +} diff --git a/grub-core/kern/emu/hostdisk_devmapper.c b/grub-core/kern/emu/hostdisk_devmapper.c new file mode 100644 index 000000000..347813ed6 --- /dev/null +++ b/grub-core/kern/emu/hostdisk_devmapper.c @@ -0,0 +1,194 @@ +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + + +#ifdef HAVE_DEVICE_MAPPER +# include + +static void device_mapper_null_log (int level __attribute__ ((unused)), + const char *file __attribute__ ((unused)), + int line __attribute__ ((unused)), + int dm_errno __attribute__ ((unused)), + const char *f __attribute__ ((unused)), + ...) +{ +} + +int +grub_device_mapper_supported (void) +{ + static int supported = -1; + + if (supported == -1) + { + struct dm_task *dmt; + + /* Suppress annoying log messages. */ + dm_log_with_errno_init (&device_mapper_null_log); + + dmt = dm_task_create (DM_DEVICE_VERSION); + supported = (dmt != NULL); + if (dmt) + dm_task_destroy (dmt); + + /* Restore the original logger. */ + dm_log_with_errno_init (NULL); + } + + return supported; +} + +int +grub_util_device_is_mapped (const char *dev) +{ + struct stat st; + + if (!grub_device_mapper_supported ()) + return 0; + + if (stat (dev, &st) < 0) + return 0; + + return dm_is_dm_major (major (st.st_rdev)); +} + +int +grub_util_get_dm_node_linear_info (const char *dev, + int *maj, int *min, + grub_disk_addr_t *st) +{ + struct dm_task *dmt; + void *next = NULL; + uint64_t length, start; + char *target, *params; + char *ptr; + int major = 0, minor = 0; + int first = 1; + grub_disk_addr_t partstart = 0; + const char *node_uuid; + + while (1) + { + dmt = dm_task_create(DM_DEVICE_TABLE); + if (!dmt) + break; + + if (! (first ? dm_task_set_name (dmt, dev) + : dm_task_set_major_minor (dmt, major, minor, 0))) + { + dm_task_destroy (dmt); + break; + } + dm_task_no_open_count(dmt); + if (!dm_task_run(dmt)) + { + dm_task_destroy (dmt); + break; + } + node_uuid = dm_task_get_uuid (dmt); + if (node_uuid && (strncmp (node_uuid, "LVM-", 4) == 0 + || strncmp (node_uuid, "mpath-", 6) == 0 + || strncmp (node_uuid, "DMRAID-", 7) == 0)) + { + dm_task_destroy (dmt); + break; + } + + next = dm_get_next_target(dmt, next, &start, &length, + &target, ¶ms); + if (grub_strcmp (target, "linear") != 0) + { + dm_task_destroy (dmt); + break; + } + major = grub_strtoul (params, &ptr, 10); + if (grub_errno) + { + dm_task_destroy (dmt); + grub_errno = GRUB_ERR_NONE; + return 0; + } + if (*ptr != ':') + { + dm_task_destroy (dmt); + return 0; + } + ptr++; + minor = grub_strtoul (ptr, &ptr, 10); + if (grub_errno) + { + grub_errno = GRUB_ERR_NONE; + dm_task_destroy (dmt); + return 0; + } + + if (*ptr != ' ') + { + dm_task_destroy (dmt); + return 0; + } + ptr++; + partstart += grub_strtoull (ptr, &ptr, 10); + if (grub_errno) + { + grub_errno = GRUB_ERR_NONE; + dm_task_destroy (dmt); + return 0; + } + + dm_task_destroy (dmt); + first = 0; + if (!dm_is_dm_major (major)) + break; + } + if (first) + return 0; + if (maj) + *maj = major; + if (min) + *min = minor; + if (st) + *st = partstart; + return 1; +} +#else + +int +grub_util_device_is_mapped (const char *dev __attribute__ ((unused))) +{ + return 0; +} + +int +grub_util_get_dm_node_linear_info (const char *dev __attribute__ ((unused)), + int *maj __attribute__ ((unused)), + int *min __attribute__ ((unused)), + grub_disk_addr_t *st __attribute__ ((unused))) +{ + return 0; +} + +#endif diff --git a/grub-core/kern/emu/hostdisk_freebsd.c b/grub-core/kern/emu/hostdisk_freebsd.c new file mode 100644 index 000000000..72ff59137 --- /dev/null +++ b/grub-core/kern/emu/hostdisk_freebsd.c @@ -0,0 +1,85 @@ +/* + * 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 /* DIOCGMEDIASIZE */ +# include +# include +# include +# include + +void +grub_hostdisk_configure_device_driver (int fd __attribute__ ((unused))) +{ +} + +grub_int64_t +grub_util_get_fd_size_os (int fd, const char *name, unsigned *log_secsize) +{ + unsigned long long nr; + unsigned sector_size, log_sector_size; + + if (ioctl (fd, DIOCGMEDIASIZE, &nr)) + return -1; + + if (ioctl (fd, DIOCGSECTORSIZE, §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 & (sector_size - 1)) + grub_util_error ("%s", _("unaligned device size")); + + return nr; +} + +void +grub_hostdisk_flush_initial_buffer (const char *os_dev __attribute__ ((unused))) +{ +} diff --git a/grub-core/kern/emu/hostdisk_hurd.c b/grub-core/kern/emu/hostdisk_hurd.c new file mode 100644 index 000000000..7a364ed2f --- /dev/null +++ b/grub-core/kern/emu/hostdisk_hurd.c @@ -0,0 +1,151 @@ +/* + * 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 +#include + +void +grub_hostdisk_configure_device_driver (int fd __attribute__ ((unused))) +{ +} + +int +grub_util_hurd_get_disk_info (const char *dev, grub_uint32_t *secsize, grub_disk_addr_t *offset, + grub_disk_addr_t *size, char **parent) +{ + file_t file; + mach_port_t *ports; + int *ints; + loff_t *offsets; + char *data; + error_t err; + mach_msg_type_number_t num_ports = 0, num_ints = 0, num_offsets = 0, data_len = 0; + + file = file_name_lookup (dev, 0, 0); + if (file == MACH_PORT_NULL) + return 0; + + err = file_get_storage_info (file, + &ports, &num_ports, + &ints, &num_ints, + &offsets, &num_offsets, + &data, &data_len); + + if (num_ints < 1) + grub_util_error (_("Storage info for `%s' does not include type"), dev); + if (ints[0] != STORAGE_DEVICE) + grub_util_error (_("`%s' is not a local disk"), dev); + + if (num_offsets != 2) + grub_util_error (_("Storage info for `%s' does indicate neither plain partition nor plain disk"), dev); + if (parent) + { + *parent = NULL; + if (num_ints >= 5) + { + size_t len = ints[4]; + if (len > data_len) + len = data_len; + *parent = xmalloc (len+1); + memcpy (*parent, data, len); + (*parent)[len] = '\0'; + } + } + if (offset) + *offset = offsets[0]; + if (size) + *size = offsets[1]; + if (secsize) + *secsize = ints[2]; + if (ports && num_ports > 0) + { + mach_msg_type_number_t i; + for (i = 0; i < num_ports; i++) + { + mach_port_t port = ports[i]; + if (port != MACH_PORT_NULL) + mach_port_deallocate (mach_task_self(), port); + } + munmap ((caddr_t) ports, num_ports * sizeof (*ports)); + } + + if (ints && num_ints > 0) + munmap ((caddr_t) ints, num_ints * sizeof (*ints)); + if (offsets && num_offsets > 0) + munmap ((caddr_t) offsets, num_offsets * sizeof (*offsets)); + if (data && data_len > 0) + munmap (data, data_len); + mach_port_deallocate (mach_task_self (), file); + + return 1; +} + +grub_int64_t +grub_util_get_fd_size_os (int fd, const char *name, unsigned *log_secsize) +{ + grub_uint32_t sector_size; + grub_disk_addr_t size; + unsigned log_sector_size; + + if (!grub_util_hurd_get_disk_info (name, §or_size, NULL, &size, NULL)) + 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; + + return size << log_sector_size; +} + +void +grub_hostdisk_flush_initial_buffer (const char *os_dev __attribute__ ((unused))) +{ +} diff --git a/grub-core/kern/emu/hostdisk_linux.c b/grub-core/kern/emu/hostdisk_linux.c new file mode 100644 index 000000000..eef89ad42 --- /dev/null +++ b/grub-core/kern/emu/hostdisk_linux.c @@ -0,0 +1,255 @@ +/* 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 /* 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 (int 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; +} + +grub_disk_addr_t +grub_util_find_partition_start_os (const char *dev) +{ + int fd; + struct hd_geometry hdg; + + 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; +} + +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++) + { + int fd; + grub_disk_addr_t start; + + sprintf (p, format, i); + + fd = open (real_dev, O_RDONLY); + if (fd == -1) + { + if (missing++ < 10) + continue; + else + return 0; + } + missing = 0; + close (fd); + + if (!grub_util_device_is_mapped (real_dev) + || !grub_util_get_dm_node_linear_info (real_dev, 0, 0, &start)) + start = grub_util_find_partition_start_os (real_dev); + /* We don't care about errors here. */ + grub_errno = GRUB_ERR_NONE; + + 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; +} + +void +grub_hostdisk_flush_initial_buffer (const char *os_dev) +{ + int 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); +} + +void +grub_hostdisk_configure_device_driver (int fd __attribute__ ((unused))) +{ +} diff --git a/grub-core/kern/emu/hostdisk_mingw.c b/grub-core/kern/emu/hostdisk_mingw.c new file mode 100644 index 000000000..eceb5d408 --- /dev/null +++ b/grub-core/kern/emu/hostdisk_mingw.c @@ -0,0 +1,120 @@ +/* + * 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 "dirname.h" + +grub_int64_t +grub_util_get_fd_size_os (int fd __attribute__ ((unused)), + const char *name_in, + unsigned *log_secsize) +{ + HANDLE hd; + grub_int64_t size = -1LL; + int log_sector_size = 9; + char *name = xstrdup (name_in); + + if (log_secsize) + *log_secsize = log_sector_size; + + strip_trailing_slashes(name); + hd = CreateFile (name, GENERIC_READ, FILE_SHARE_READ | FILE_SHARE_WRITE, + 0, OPEN_EXISTING, 0, 0); + + if (hd == INVALID_HANDLE_VALUE) + { + free (name); + return size; + } + + if (((name[0] == '/') || (name[0] == '\\')) && + ((name[1] == '/') || (name[1] == '\\')) && + (name[2] == '.') && + ((name[3] == '/') || (name[3] == '\\')) && + (! strncasecmp (name + 4, "PHYSICALDRIVE", 13))) + { + DWORD nr; + DISK_GEOMETRY g; + + if (! DeviceIoControl (hd, IOCTL_DISK_GET_DRIVE_GEOMETRY, + 0, 0, &g, sizeof (g), &nr, 0)) + goto fail; + + size = g.Cylinders.QuadPart; + size *= g.TracksPerCylinder * g.SectorsPerTrack * g.BytesPerSector; + + for (log_sector_size = 0; + (1 << log_sector_size) < g.BytesPerSector; + log_sector_size++); + } + else + { + ULARGE_INTEGER s; + + s.LowPart = GetFileSize (hd, &s.HighPart); + size = s.QuadPart; + } + + fail: + + if (log_secsize) + *log_secsize = log_sector_size; + + free (name); + + CloseHandle (hd); + + return size; +} + +void +grub_hostdisk_configure_device_driver (int fd __attribute__ ((unused))) +{ +} + +void +grub_hostdisk_flush_initial_buffer (const char *os_dev __attribute__ ((unused))) +{ +} diff --git a/grub-core/kern/emu/hostdisk_os.c b/grub-core/kern/emu/hostdisk_os.c new file mode 100644 index 000000000..c21b76439 --- /dev/null +++ b/grub-core/kern/emu/hostdisk_os.c @@ -0,0 +1,20 @@ +#ifdef __linux__ +#include "hostdisk_linux.c" +#elif defined(__FreeBSD__) || defined(__FreeBSD_kernel__) +#include "hostdisk_freebsd.c" +#elif defined(__NetBSD__) || defined(__OpenBSD__) +#include "hostdisk_bsd.c" +#elif defined(__APPLE__) +#include "hostdisk_apple.c" +#elif defined(__sun__) +#include "hostdisk_sun.c" +#elif defined(__GNU__) +#include "hostdisk_hurd.c" +#elif defined(__CYGWIN__) +#include "hostdisk_cygwin.c" +#elif defined(__MINGW32__) +#include "hostdisk_mingw.c" +#else +# warning "No hostdisk OS-specific functions is available for your system. Device detection may not work properly." +#include "hostdisk_basic.c" +#endif diff --git a/grub-core/kern/emu/hostdisk_sun.c b/grub-core/kern/emu/hostdisk_sun.c new file mode 100644 index 000000000..0232fa253 --- /dev/null +++ b/grub-core/kern/emu/hostdisk_sun.c @@ -0,0 +1,78 @@ +/* + * 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 + +grub_int64_t +grub_util_get_fd_size_os (int fd, const char *name, unsigned *log_secsize) +{ + struct dk_minfo minfo; + unsigned sector_size, log_sector_size; + + if (!ioctl (fd, DKIOCGMEDIAINFO, &minfo)) + return -1; + + sector_size = minfo.dki_lbsize; + + 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; + + return minfo.dki_capacity << log_sector_size; +} + +void +grub_hostdisk_configure_device_driver (int fd __attribute__ ((unused))) +{ +} + +void +grub_hostdisk_flush_initial_buffer (const char *os_dev __attribute__ ((unused))) +{ +} diff --git a/include/grub/emu/getroot.h b/include/grub/emu/getroot.h index 83455ddda..e4b903fa6 100644 --- a/include/grub/emu/getroot.h +++ b/include/grub/emu/getroot.h @@ -19,6 +19,8 @@ #ifndef GRUB_UTIL_GETROOT_HEADER #define GRUB_UTIL_GETROOT_HEADER 1 +#include + #include enum grub_dev_abstraction_types { @@ -30,12 +32,13 @@ enum grub_dev_abstraction_types { }; char *grub_find_device (const char *dir, dev_t dev); +void grub_util_pull_device (const char *osname); char **grub_guess_root_devices (const char *dir); int grub_util_get_dev_abstraction (const char *os_dev); -char *grub_util_get_grub_dev (const char *os_dev); char *grub_make_system_path_relative_to_its_root (const char *path); const char *grub_util_check_block_device (const char *blk_dev); const char *grub_util_check_char_device (const char *blk_dev); +char *grub_util_get_grub_dev (const char *os_dev); #ifdef __linux__ char **grub_util_raid_getmembers (const char *name, int bootable); #endif @@ -44,4 +47,57 @@ void grub_util_follow_gpart_up (const char *name, grub_disk_addr_t *off_out, char **name_out); #endif +#include + +#ifdef __linux__ +char ** +grub_find_root_devices_from_mountinfo (const char *dir, char **relroot); +#endif +#if defined (__GNU__) +char * +grub_util_find_hurd_root_device (const char *path); +#endif + +/* Devmapper functions provided by getroot_devmapper.c. */ +void +grub_util_pull_devmapper (const char *os_dev); +int +grub_util_device_is_mapped_stat (struct stat *st); +void grub_util_devmapper_cleanup (void); +enum grub_dev_abstraction_types +grub_util_get_dm_abstraction (const char *os_dev); +char * +grub_util_get_vg_uuid (const char *os_dev); +char * +grub_util_devmapper_part_to_disk (struct stat *st, + int *is_part, const char *os_dev); +char * +grub_util_get_devmapper_grub_dev (const char *os_dev); + +/* Functions provided by getroot.c. */ +#if !defined (__MINGW32__) && !defined (__CYGWIN__) && !defined (__GNU__) +#include +pid_t +grub_util_exec_pipe (char **argv, int *fd); +#endif +char ** +grub_util_find_root_devices_from_poolname (char *poolname); + +grub_disk_addr_t +grub_util_find_partition_start (const char *dev); + +/* OS-specific functions provided by getroot_*.c. */ +enum grub_dev_abstraction_types +grub_util_get_dev_abstraction_os (const char *os_dev); +char * +grub_util_part_to_disk (const char *os_dev, struct stat *st, + int *is_part); +int +grub_util_pull_device_os (const char *osname, + enum grub_dev_abstraction_types ab); +char * +grub_util_get_grub_dev_os (const char *os_dev); +grub_disk_addr_t +grub_util_find_partition_start_os (const char *dev); + #endif /* ! GRUB_UTIL_GETROOT_HEADER */ diff --git a/include/grub/emu/hostdisk.h b/include/grub/emu/hostdisk.h index a4e7d1434..fd882dfad 100644 --- a/include/grub/emu/hostdisk.h +++ b/include/grub/emu/hostdisk.h @@ -33,7 +33,6 @@ int grub_util_biosdisk_is_floppy (grub_disk_t disk); const char * grub_util_biosdisk_get_compatibility_hint (grub_disk_t disk); grub_err_t grub_util_biosdisk_flush (struct grub_disk *disk); -void grub_util_pull_device (const char *osname); grub_err_t grub_util_fd_seek (int fd, const char *name, grub_uint64_t sector); ssize_t grub_util_fd_read (int fd, char *buf, size_t len); @@ -52,8 +51,6 @@ grub_util_ldm_embed (struct grub_disk *disk, unsigned int *nsectors, grub_embed_type_t embed_type, grub_disk_addr_t **sectors); #endif -grub_disk_addr_t -grub_hostdisk_find_partition_start (const char *dev); const char * grub_hostdisk_os_dev_to_grub_drive (const char *os_dev, int add); @@ -63,11 +60,45 @@ grub_util_get_fd_size (int fd, const char *name, unsigned *log_secsize); char * grub_util_get_os_disk (const char *os_dev); -#ifdef HAVE_DEVICE_MAPPER +#ifdef __linux__ +int +grub_hostdisk_linux_find_partition (char *dev, grub_disk_addr_t sector); +#endif + int grub_util_get_dm_node_linear_info (const char *dev, int *maj, int *min, grub_disk_addr_t *st); + + +/* Supplied by hostdisk_*.c. */ +grub_int64_t +grub_util_get_fd_size_os (int fd, const char *name, unsigned *log_secsize); +/* REturns partition offset in 512B blocks. */ +grub_disk_addr_t +grub_hostdisk_find_partition_start_os (const char *dev); +/* Adjust device driver parameters. This function should be called just + after successfully opening the device. For now, it simply prevents the + floppy driver from retrying operations on failure, as otherwise the + driver takes a while to abort when there is no floppy in the drive. + For now it's non-nop only on NetBSD. +*/ +void +grub_hostdisk_configure_device_driver (int fd); +void +grub_hostdisk_flush_initial_buffer (const char *os_dev); + +#if defined(__FreeBSD__) || defined(__FreeBSD_kernel__) || defined(__APPLE__) || defined(__NetBSD__) || defined (__sun__) || defined(__OpenBSD__) +#define GRUB_DISK_DEVS_ARE_CHAR 1 +#else +#define GRUB_DISK_DEVS_ARE_CHAR 0 +#endif + +#ifdef __GNU__ +int +grub_util_hurd_get_disk_info (const char *dev, grub_uint32_t *secsize, + grub_disk_addr_t *offset, + grub_disk_addr_t *size, char **parent); #endif #endif /* ! GRUB_BIOSDISK_MACHINE_UTIL_HEADER */ diff --git a/include/grub/util/lvm.h b/include/grub/util/lvm.h index 59944333f..f4577d56f 100644 --- a/include/grub/util/lvm.h +++ b/include/grub/util/lvm.h @@ -20,14 +20,10 @@ #ifndef GRUB_LVM_UTIL_HEADER #define GRUB_LVM_UTIL_HEADER 1 -#if defined(__linux__) || defined(__FreeBSD__) || defined(__FreeBSD_kernel__) - -#ifdef __linux__ -#define LVM_DEV_MAPPER_STRING "/dev/mapper/" -#else +#if defined(__FreeBSD__) || defined(__FreeBSD_kernel__) #define LVM_DEV_MAPPER_STRING "/dev/linux_lvm/" -#endif - +#else +#define LVM_DEV_MAPPER_STRING "/dev/mapper/" #endif #endif /* ! GRUB_RAID_UTIL_HEADER */ diff --git a/util/getroot.c b/util/getroot.c index 69e04dc90..eab6f24df 100644 --- a/util/getroot.c +++ b/util/getroot.c @@ -40,8 +40,18 @@ #include #include -#ifdef HAVE_DEVICE_MAPPER -# include +#ifdef __linux__ +#include /* ioctl */ +#include +#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 */ #endif #ifdef __GNU__ @@ -53,41 +63,17 @@ #include -#if defined(__FreeBSD__) || defined(__FreeBSD_kernel__) || defined(__OpenBSD__) -# include -# include -#endif - #if defined(HAVE_LIBZFS) && defined(HAVE_LIBNVPAIR) # include # include #endif -#ifdef __sun__ -# include -# include -#endif - #include #include #include #include #include -#ifdef __linux__ -# include /* ioctl */ -# include -# 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 */ -#endif - #ifdef __CYGWIN__ # include # include /* BLKGETSIZE64 */ @@ -99,40 +85,24 @@ #endif #if defined(__FreeBSD__) || defined(__FreeBSD_kernel__) -# include /* DIOCGMEDIASIZE */ -# include -# include -# include -#include # define MAJOR(dev) major(dev) # define FLOPPY_MAJOR 2 #endif -#if defined (__sun__) -# include -#endif - -#if defined(__APPLE__) -# include -# include -# include -# include -#endif - -#ifdef HAVE_DEVICE_MAPPER -# include +#if defined (__FreeBSD__) || defined (__FreeBSD_kernel__) +#include #endif #if defined(__NetBSD__) || defined(__OpenBSD__) -# define HAVE_DIOCGDINFO # include # include /* struct disklabel */ # include /* struct dkwedge_info */ -#else /* !defined(__NetBSD__) && !defined(__FreeBSD__) && !defined(__FreeBSD_kernel__) */ -# undef HAVE_DIOCGDINFO +#include +#include #endif /* defined(__NetBSD__) || defined(__FreeBSD__) || defined(__FreeBSD_kernel__) */ -#if defined(__NetBSD__) || defined(__OpenBSD__) +#if defined(__NetBSD__) || defined(__OpenBSD__) +# define MAJOR(dev) major(dev) # ifdef HAVE_GETRAWPARTITION # include /* getrawpartition */ # endif /* HAVE_GETRAWPARTITION */ @@ -147,39 +117,13 @@ # endif /* ! RAW_FLOPPY_MAJOR */ #endif /* defined(__NetBSD__) */ -#ifdef __linux__ -/* Defines taken from btrfs/ioctl.h. */ +#if !defined (__MINGW32__) && !defined (__CYGWIN__) -struct btrfs_ioctl_dev_info_args -{ - grub_uint64_t devid; - grub_uint8_t uuid[16]; - grub_uint64_t bytes_used; - grub_uint64_t total_bytes; - grub_uint64_t unused[379]; - grub_uint8_t path[1024]; -}; - -struct btrfs_ioctl_fs_info_args -{ - grub_uint64_t max_id; - grub_uint64_t num_devices; - grub_uint8_t fsid[16]; - grub_uint64_t reserved[124]; -}; - -#define BTRFS_IOC_DEV_INFO _IOWR(0x94, 30, \ - struct btrfs_ioctl_dev_info_args) -#define BTRFS_IOC_FS_INFO _IOR(0x94, 31, \ - struct btrfs_ioctl_fs_info_args) +static void +pull_lvm_by_command (const char *os_dev); #endif -#ifdef __linux__ -static int -grub_util_is_imsm (const char *os_dev); -#endif - -#if ! defined(__CYGWIN__) && !defined(__GNU__) +#if ! defined(__CYGWIN__) static void strip_extra_slashes (char *dir) @@ -222,13 +166,13 @@ xgetcwd (void) #endif -#if !defined (__MINGW32__) && !defined (__CYGWIN__) && !defined (__GNU__) +#if !defined (__MINGW32__) && !defined (__CYGWIN__) #include #include -static pid_t -exec_pipe (char **argv, int *fd) +pid_t +grub_util_exec_pipe (char **argv, int *fd) { int mdadm_pipe[2]; pid_t mdadm_pid; @@ -249,9 +193,7 @@ exec_pipe (char **argv, int *fd) /* Child. */ /* Close fd's. */ -#ifdef HAVE_DEVICE_MAPPER - dm_lib_release (); -#endif + grub_util_devmapper_cleanup (); grub_diskfilter_fini (); /* Ensure child is not localised. */ @@ -272,8 +214,11 @@ exec_pipe (char **argv, int *fd) } } -static char ** -find_root_devices_from_poolname (char *poolname) +#endif + +#if !defined (__CYGWIN__) && !defined(__MINGW32__) && !defined (__GNU__) +char ** +grub_util_find_root_devices_from_poolname (char *poolname) { char **devices = 0; size_t ndevices = 0; @@ -361,7 +306,7 @@ find_root_devices_from_poolname (char *poolname) argv[2] = (char *) poolname; argv[3] = NULL; - pid = exec_pipe (argv, &fd); + pid = grub_util_exec_pipe (argv, &fd); if (!pid) return NULL; @@ -451,258 +396,6 @@ find_root_devices_from_poolname (char *poolname) #endif -#ifdef __linux__ - -#define ESCAPED_PATH_MAX (4 * PATH_MAX) -struct mountinfo_entry -{ - int id; - int major, minor; - char enc_root[ESCAPED_PATH_MAX + 1], enc_path[ESCAPED_PATH_MAX + 1]; - char fstype[ESCAPED_PATH_MAX + 1], device[ESCAPED_PATH_MAX + 1]; -}; - -/* Statting something on a btrfs filesystem always returns a virtual device - major/minor pair rather than the real underlying device, because btrfs - can span multiple underlying devices (and even if it's currently only - using a single device it can be dynamically extended onto another). We - can't deal with the multiple-device case yet, but in the meantime, we can - at least cope with the single-device case by scanning - /proc/self/mountinfo. */ -static void -unescape (char *str) -{ - char *optr; - const char *iptr; - for (iptr = optr = str; *iptr; optr++) - { - if (iptr[0] == '\\' && iptr[1] >= '0' && iptr[1] < '8' - && iptr[2] >= '0' && iptr[2] < '8' - && iptr[3] >= '0' && iptr[3] < '8') - { - *optr = (((iptr[1] - '0') << 6) | ((iptr[2] - '0') << 3) - | (iptr[3] - '0')); - iptr += 4; - } - else - *optr = *iptr++; - } - *optr = 0; -} - -static char ** -grub_find_root_devices_from_btrfs (const char *dir) -{ - int fd; - struct btrfs_ioctl_fs_info_args fsi; - int i, j = 0; - char **ret; - - fd = open (dir, 0); - if (!fd) - return NULL; - - if (ioctl (fd, BTRFS_IOC_FS_INFO, &fsi) < 0) - { - close (fd); - return NULL; - } - - ret = xmalloc ((fsi.num_devices + 1) * sizeof (ret[0])); - - for (i = 1; i <= fsi.max_id && j < fsi.num_devices; i++) - { - struct btrfs_ioctl_dev_info_args devi; - memset (&devi, 0, sizeof (devi)); - devi.devid = i; - if (ioctl (fd, BTRFS_IOC_DEV_INFO, &devi) < 0) - { - close (fd); - free (ret); - return NULL; - } - ret[j++] = xstrdup ((char *) devi.path); - if (j >= fsi.num_devices) - break; - } - close (fd); - ret[j] = 0; - return ret; -} - -static char ** -grub_find_root_devices_from_mountinfo (const char *dir, char **relroot) -{ - FILE *fp; - char *buf = NULL; - size_t len = 0; - grub_size_t entry_len = 0, entry_max = 4; - struct mountinfo_entry *entries; - struct mountinfo_entry parent_entry = { 0, 0, 0, "", "", "", "" }; - int i; - - if (! *dir) - dir = "/"; - if (relroot) - *relroot = NULL; - - fp = fopen ("/proc/self/mountinfo", "r"); - if (! fp) - return NULL; /* fall through to other methods */ - - entries = xmalloc (entry_max * sizeof (*entries)); - - /* First, build a list of relevant visible mounts. */ - while (getline (&buf, &len, fp) > 0) - { - struct mountinfo_entry entry; - int count; - size_t enc_path_len; - const char *sep; - - if (sscanf (buf, "%d %d %u:%u %s %s%n", - &entry.id, &parent_entry.id, &entry.major, &entry.minor, - entry.enc_root, entry.enc_path, &count) < 6) - continue; - - unescape (entry.enc_root); - unescape (entry.enc_path); - - enc_path_len = strlen (entry.enc_path); - /* Check that enc_path is a prefix of dir. The prefix must either be - the entire string, or end with a slash, or be immediately followed - by a slash. */ - if (strncmp (dir, entry.enc_path, enc_path_len) != 0 || - (enc_path_len && dir[enc_path_len - 1] != '/' && - dir[enc_path_len] && dir[enc_path_len] != '/')) - continue; - - sep = strstr (buf + count, " - "); - if (!sep) - continue; - - sep += sizeof (" - ") - 1; - if (sscanf (sep, "%s %s", entry.fstype, entry.device) != 2) - continue; - - unescape (entry.device); - - /* Using the mount IDs, find out where this fits in the list of - visible mount entries we've seen so far. There are three - interesting cases. Firstly, it may be inserted at the end: this is - the usual case of /foo/bar being mounted after /foo. Secondly, it - may be inserted at the start: for example, this can happen for - filesystems that are mounted before / and later moved under it. - Thirdly, it may occlude part or all of the existing filesystem - tree, in which case the end of the list needs to be pruned and this - new entry will be inserted at the end. */ - if (entry_len >= entry_max) - { - entry_max <<= 1; - entries = xrealloc (entries, entry_max * sizeof (*entries)); - } - - if (!entry_len) - { - /* Initialise list. */ - entry_len = 2; - entries[0] = parent_entry; - entries[1] = entry; - } - else - { - for (i = entry_len - 1; i >= 0; i--) - { - if (entries[i].id == parent_entry.id) - { - /* Insert at end, pruning anything previously above this. */ - entry_len = i + 2; - entries[i + 1] = entry; - break; - } - else if (i == 0 && entries[i].id == entry.id) - { - /* Insert at start. */ - entry_len++; - memmove (entries + 1, entries, - (entry_len - 1) * sizeof (*entries)); - entries[0] = parent_entry; - entries[1] = entry; - break; - } - } - } - } - - /* Now scan visible mounts for the ones we're interested in. */ - for (i = entry_len - 1; i >= 0; i--) - { - char **ret = NULL; - if (!*entries[i].device) - continue; - - if (grub_strcmp (entries[i].fstype, "fuse.zfs") == 0 - || grub_strcmp (entries[i].fstype, "zfs") == 0) - { - char *slash; - slash = strchr (entries[i].device, '/'); - if (slash) - *slash = 0; - ret = find_root_devices_from_poolname (entries[i].device); - if (slash) - *slash = '/'; - if (relroot) - { - if (!slash) - *relroot = xasprintf ("/@%s", entries[i].enc_root); - else if (strchr (slash + 1, '@')) - *relroot = xasprintf ("/%s%s", slash + 1, entries[i].enc_root); - else - *relroot = xasprintf ("/%s@%s", slash + 1, entries[i].enc_root); - } - } - else if (grub_strcmp (entries[i].fstype, "btrfs") == 0) - { - ret = grub_find_root_devices_from_btrfs (dir); - if (relroot) - { - char *ptr; - *relroot = xmalloc (strlen (entries[i].enc_root) + - 2 + strlen (dir)); - ptr = grub_stpcpy (*relroot, entries[i].enc_root); - if (strlen (dir) > strlen (entries[i].enc_path)) - { - while (ptr > *relroot && *(ptr - 1) == '/') - ptr--; - if (dir[strlen (entries[i].enc_path)] != '/') - *ptr++ = '/'; - ptr = grub_stpcpy (ptr, dir + strlen (entries[i].enc_path)); - } - *ptr = 0; - } - } - if (!ret) - { - ret = xmalloc (2 * sizeof (ret[0])); - ret[0] = strdup (entries[i].device); - ret[1] = 0; - if (relroot) - *relroot = strdup (entries[i].enc_root); - } - free (buf); - free (entries); - fclose (fp); - return ret; - } - - free (buf); - free (entries); - fclose (fp); - return NULL; -} - -#endif /* __linux__ */ - #if !defined (__MINGW32__) && !defined (__CYGWIN__) && !defined (__GNU__) static char ** @@ -716,7 +409,7 @@ find_root_devices_from_libzfs (const char *dir) if (! poolname) return NULL; - devices = find_root_devices_from_poolname (poolname); + devices = grub_util_find_root_devices_from_poolname (poolname); free (poolname); if (poolfs) @@ -736,83 +429,6 @@ grub_find_device (const char *dir __attribute__ ((unused)), return 0; } -#elif defined (__GNU__) - -static char * -find_hurd_root_device (const char *path) -{ - file_t file; - error_t err; - char *argz = NULL, *name = NULL, *ret; - size_t argz_len = 0; - int i; - - file = file_name_lookup (path, 0, 0); - if (file == MACH_PORT_NULL) - /* TRANSLATORS: The first %s is the file being looked at, the second %s is - the error message. */ - grub_util_error (_("cannot open `%s': %s"), path, strerror (errno)); - - /* This returns catenated 0-terminated strings. */ - err = file_get_fs_options (file, &argz, &argz_len); - if (err) - /* TRANSLATORS: On GNU/Hurd, a "translator" is similar to a filesystem - mount, but handled by a userland daemon, whose invocation command line - is being fetched here. First %s is the file being looked at (for which - we are fetching the "translator" command line), second %s is the error - message. - */ - grub_util_error (_("cannot get translator command line " - "for path `%s': %s"), path, strerror(err)); - if (argz_len == 0) - grub_util_error (_("translator command line is empty for path `%s'"), path); - - /* Make sure the string is terminated. */ - argz[argz_len-1] = 0; - - /* Skip first word (translator path) and options. */ - for (i = strlen (argz) + 1; i < argz_len; i += strlen (argz + i) + 1) - { - if (argz[i] != '-') - { - /* Non-option. Only accept one, assumed to be the FS path. */ - /* XXX: this should be replaced by an RPC to the translator. */ - if (name) - /* TRANSLATORS: we expect to get something like - /hurd/foobar --option1 --option2=baz /dev/something - */ - grub_util_error (_("translator `%s' for path `%s' has several " - "non-option words, at least `%s' and `%s'"), - argz, path, name, argz + i); - name = argz + i; - } - } - - if (!name) - /* TRANSLATORS: we expect to get something like - /hurd/foobar --option1 --option2=baz /dev/something - */ - grub_util_error (_("translator `%s' for path `%s' is given only options, " - "cannot find device part"), argz, path); - - if (strncmp (name, "device:", sizeof ("device:") - 1) == 0) - { - char *dev_name = name + sizeof ("device:") - 1; - size_t size = sizeof ("/dev/") - 1 + strlen (dev_name) + 1; - char *next; - ret = malloc (size); - next = stpncpy (ret, "/dev/", size); - stpncpy (next, dev_name, size - (next - ret)); - } - else if (!strncmp (name, "file:", sizeof ("file:") - 1)) - ret = strdup (name + sizeof ("file:") - 1); - else - ret = strdup (name); - - munmap (argz, argz_len); - return ret; -} - #elif ! defined(__CYGWIN__) char * @@ -913,7 +529,7 @@ grub_find_device (const char *dir, dev_t dev) /* Found! */ char *res; char *cwd; -#if defined(__NetBSD__) +#if defined(__NetBSD__) || defined(__OpenBSD__) /* Convert this block device to its character (raw) device. */ const char *template = "%s/r%s"; #else @@ -1110,7 +726,7 @@ grub_guess_root_devices (const char *dir) #elif defined __GNU__ /* GNU/Hurd specific function. */ - os_dev[0] = find_hurd_root_device (dir); + os_dev[0] = grub_util_find_hurd_root_device (dir); #else @@ -1128,171 +744,67 @@ grub_guess_root_devices (const char *dir) return os_dev; } -#ifdef HAVE_DEVICE_MAPPER - -static int -grub_util_open_dm (const char *os_dev, struct dm_tree **tree, - struct dm_tree_node **node) +grub_disk_addr_t +grub_util_find_partition_start (const char *dev) { - uint32_t maj, min; - struct stat st; + grub_disk_addr_t partition_start; + if (grub_util_device_is_mapped (dev) + && grub_util_get_dm_node_linear_info (dev, 0, 0, &partition_start)) + return partition_start; - *node = NULL; - *tree = NULL; - - if (stat (os_dev, &st) < 0) - return 0; - - maj = major (st.st_rdev); - min = minor (st.st_rdev); - - if (!dm_is_dm_major (maj)) - return 0; - - *tree = dm_tree_create (); - if (! *tree) - { - grub_puts_ (N_("Failed to create `device-mapper' tree")); - grub_dprintf ("hostdisk", "dm_tree_create failed\n"); - return 0; - } - - if (! dm_tree_add_dev (*tree, maj, min)) - { - grub_dprintf ("hostdisk", "dm_tree_add_dev failed\n"); - dm_tree_free (*tree); - *tree = NULL; - return 0; - } - - *node = dm_tree_find_node (*tree, maj, min); - if (! *node) - { - grub_dprintf ("hostdisk", "dm_tree_find_node failed\n"); - dm_tree_free (*tree); - *tree = NULL; - return 0; - } - return 1; + return grub_util_find_partition_start_os (dev); } -#endif - -#ifdef HAVE_DEVICE_MAPPER -static char * -get_dm_uuid (const char *os_dev) +void +grub_util_pull_device (const char *os_dev) +{ + enum grub_dev_abstraction_types ab; + ab = grub_util_get_dev_abstraction (os_dev); + switch (ab) + { + case GRUB_DEV_ABSTRACTION_LVM: +#if !defined (__MINGW32__) && !defined (__CYGWIN__) + pull_lvm_by_command (os_dev); +#endif + /* Fallthrough in case that lvm-tools are unavailable. */ + case GRUB_DEV_ABSTRACTION_LUKS: + grub_util_pull_devmapper (os_dev); + return; + + default: + if (grub_util_pull_device_os (os_dev, ab)) + return; + case GRUB_DEV_ABSTRACTION_NONE: + free (grub_util_biosdisk_get_grub_dev (os_dev)); + return; + } +} + +char * +grub_util_get_grub_dev (const char *os_dev) { - struct dm_tree *tree; - struct dm_tree_node *node; - const char *node_uuid; char *ret; - if (!grub_util_open_dm (os_dev, &tree, &node)) - return NULL; + grub_util_pull_device (os_dev); - node_uuid = dm_tree_node_get_uuid (node); - if (! node_uuid) - { - grub_dprintf ("hostdisk", "%s has no DM uuid\n", os_dev); - dm_tree_free (tree); - return NULL; - } - - ret = grub_strdup (node_uuid); - - dm_tree_free (tree); - - return ret; -} -#endif - -#ifdef __linux__ - -static enum grub_dev_abstraction_types -grub_util_get_dm_abstraction (const char *os_dev) -{ -#ifdef HAVE_DEVICE_MAPPER - char *uuid; - - uuid = get_dm_uuid (os_dev); - - if (uuid == NULL) - return GRUB_DEV_ABSTRACTION_NONE; - - if (strncmp (uuid, "LVM-", 4) == 0) - { - grub_free (uuid); - return GRUB_DEV_ABSTRACTION_LVM; - } - if (strncmp (uuid, "CRYPT-LUKS1-", 4) == 0) - { - grub_free (uuid); - return GRUB_DEV_ABSTRACTION_LUKS; - } - - grub_free (uuid); - return GRUB_DEV_ABSTRACTION_NONE; -#else - if ((strncmp ("/dev/mapper/", os_dev, 12) != 0)) - return GRUB_DEV_ABSTRACTION_NONE; - return GRUB_DEV_ABSTRACTION_LVM; -#endif + ret = grub_util_get_devmapper_grub_dev (os_dev); + if (ret) + return ret; + ret = grub_util_get_grub_dev_os (os_dev); + if (ret) + return ret; + return grub_util_biosdisk_get_grub_dev (os_dev); } -#endif - -#if defined (__FreeBSD__) || defined(__FreeBSD_kernel__) -#include - -static const char * -grub_util_get_geom_abstraction (const char *dev) -{ - char *whole; - struct gmesh mesh; - struct gclass *class; - const char *name; - int err; - - if (strncmp (dev, "/dev/", sizeof ("/dev/") - 1) != 0) - return 0; - name = dev + sizeof ("/dev/") - 1; - grub_util_follow_gpart_up (name, NULL, &whole); - - grub_util_info ("following geom '%s'", name); - - err = geom_gettree (&mesh); - if (err != 0) - /* TRANSLATORS: geom is the name of (k)FreeBSD device framework. - Usually left untranslated. - */ - grub_util_error ("%s", _("couldn't open geom")); - - LIST_FOREACH (class, &mesh.lg_class, lg_class) - { - struct ggeom *geom; - LIST_FOREACH (geom, &class->lg_geom, lg_geom) - { - struct gprovider *provider; - LIST_FOREACH (provider, &geom->lg_provider, lg_provider) - if (strcmp (provider->lg_name, name) == 0) - return class->lg_name; - } - } - return NULL; -} -#endif int grub_util_get_dev_abstraction (const char *os_dev) { -#if defined(__linux__) || defined(__FreeBSD__) || defined(__FreeBSD_kernel__) + enum grub_dev_abstraction_types ret; + /* User explicitly claims that this drive is visible by BIOS. */ if (grub_util_biosdisk_is_present (os_dev)) return GRUB_DEV_ABSTRACTION_NONE; -#endif - -#ifdef __linux__ - enum grub_dev_abstraction_types ret; /* Check for LVM and LUKS. */ ret = grub_util_get_dm_abstraction (os_dev); @@ -1300,29 +812,10 @@ grub_util_get_dev_abstraction (const char *os_dev) if (ret != GRUB_DEV_ABSTRACTION_NONE) return ret; - /* Check for RAID. */ - if (!strncmp (os_dev, "/dev/md", 7) && ! grub_util_device_is_mapped (os_dev) - && !grub_util_is_imsm (os_dev)) - return GRUB_DEV_ABSTRACTION_RAID; -#endif - -#if defined (__FreeBSD__) || defined(__FreeBSD_kernel__) - const char *abstrac; - abstrac = grub_util_get_geom_abstraction (os_dev); - grub_util_info ("abstraction of %s is %s", os_dev, abstrac); - if (abstrac && grub_strcasecmp (abstrac, "eli") == 0) - return GRUB_DEV_ABSTRACTION_GELI; - - /* Check for LVM. */ - if (!strncmp (os_dev, LVM_DEV_MAPPER_STRING, sizeof(LVM_DEV_MAPPER_STRING)-1)) - return GRUB_DEV_ABSTRACTION_LVM; -#endif - - /* No abstraction found. */ - return GRUB_DEV_ABSTRACTION_NONE; + return grub_util_get_dev_abstraction_os (os_dev); } -#if !defined (__MINGW32__) && !defined (__CYGWIN__) && !defined (__GNU__) +#if !defined (__MINGW32__) && !defined (__CYGWIN__) static void pull_lvm_by_command (const char *os_dev) @@ -1339,28 +832,9 @@ pull_lvm_by_command (const char *os_dev) char *vgid = NULL; grub_size_t vgidlen = 0; -#ifdef HAVE_DEVICE_MAPPER - char *uuid; - - uuid = get_dm_uuid (os_dev); - if (uuid) - { - int dashes[] = { 0, 6, 10, 14, 18, 22, 26, 32}; - unsigned i; - vgid = xmalloc (grub_strlen (uuid)); - optr = vgid; - for (i = 0; i < ARRAY_SIZE (dashes) - 1; i++) - { - memcpy (optr, uuid + sizeof ("LVM-") - 1 + dashes[i], - dashes[i+1] - dashes[i]); - optr += dashes[i+1] - dashes[i]; - *optr++ = '-'; - } - optr--; - *optr = '\0'; - vgidlen = optr - vgid; - } -#endif + vgid = grub_util_get_vg_uuid (os_dev); + if (vgid) + vgidlen = grub_strlen (vgid); if (!vgid) { @@ -1400,7 +874,7 @@ pull_lvm_by_command (const char *os_dev) argv[6] = vgname; argv[7] = NULL; - pid = exec_pipe (argv, &fd); + pid = grub_util_exec_pipe (argv, &fd); free (vgname); if (!pid) @@ -1440,299 +914,6 @@ out: #endif -#ifdef __linux__ -static char * -get_mdadm_uuid (const char *os_dev) -{ - char *argv[5]; - int fd; - pid_t pid; - FILE *mdadm; - char *buf = NULL; - size_t len = 0; - char *name = NULL; - - /* execvp has inconvenient types, hence the casts. None of these - strings will actually be modified. */ - argv[0] = (char *) "mdadm"; - argv[1] = (char *) "--detail"; - argv[2] = (char *) "--export"; - argv[3] = (char *) os_dev; - argv[4] = NULL; - - pid = exec_pipe (argv, &fd); - - if (!pid) - return NULL; - - /* Parent. Read mdadm's output. */ - mdadm = fdopen (fd, "r"); - if (! mdadm) - { - grub_util_warn (_("Unable to open stream from %s: %s"), - "mdadm", strerror (errno)); - goto out; - } - - while (getline (&buf, &len, mdadm) > 0) - { - if (strncmp (buf, "MD_UUID=", sizeof ("MD_UUID=") - 1) == 0) - { - char *name_start, *ptri, *ptro; - - free (name); - name_start = buf + sizeof ("MD_UUID=") - 1; - ptro = name = xmalloc (strlen (name_start) + 1); - for (ptri = name_start; *ptri && *ptri != '\n' && *ptri != '\r'; - ptri++) - if ((*ptri >= '0' && *ptri <= '9') - || (*ptri >= 'a' && *ptri <= 'f') - || (*ptri >= 'A' && *ptri <= 'F')) - *ptro++ = *ptri; - *ptro = 0; - } - } - -out: - close (fd); - waitpid (pid, NULL, 0); - free (buf); - - return name; -} - -static int -grub_util_is_imsm (const char *os_dev) -{ - int retry; - int is_imsm = 0; - int container_seen = 0; - const char *dev = os_dev; - - do - { - char *argv[5]; - int fd; - pid_t pid; - FILE *mdadm; - char *buf = NULL; - size_t len = 0; - - retry = 0; /* We'll do one more pass if device is part of container */ - - /* execvp has inconvenient types, hence the casts. None of these - strings will actually be modified. */ - argv[0] = (char *) "mdadm"; - argv[1] = (char *) "--detail"; - argv[2] = (char *) "--export"; - argv[3] = (char *) dev; - argv[4] = NULL; - - pid = exec_pipe (argv, &fd); - - if (!pid) - { - if (dev != os_dev) - free ((void *) dev); - return 0; - } - - /* Parent. Read mdadm's output. */ - mdadm = fdopen (fd, "r"); - if (! mdadm) - { - grub_util_warn (_("Unable to open stream from %s: %s"), - "mdadm", strerror (errno)); - close (fd); - waitpid (pid, NULL, 0); - if (dev != os_dev) - free ((void *) dev); - return 0; - } - - while (getline (&buf, &len, mdadm) > 0) - { - if (strncmp (buf, "MD_CONTAINER=", sizeof ("MD_CONTAINER=") - 1) == 0 - && !container_seen) - { - char *newdev, *ptr; - newdev = xstrdup (buf + sizeof ("MD_CONTAINER=") - 1); - ptr = newdev + strlen (newdev) - 1; - for (; ptr >= newdev && (*ptr == '\n' || *ptr == '\r'); ptr--); - ptr[1] = 0; - grub_util_info ("Container of %s is %s", dev, newdev); - dev = newdev; - container_seen = retry = 1; - break; - } - if (strncmp (buf, "MD_METADATA=imsm", - sizeof ("MD_METADATA=imsm") - 1) == 0) - { - is_imsm = 1; - grub_util_info ("%s is imsm", dev); - break; - } - } - - free (buf); - close (fd); - waitpid (pid, NULL, 0); - } - while (retry); - - if (dev != os_dev) - free ((void *) dev); - return is_imsm; -} -#endif /* __linux__ */ - -void -grub_util_pull_device (const char *os_dev) -{ - int ab; - ab = grub_util_get_dev_abstraction (os_dev); - switch (ab) - { - case GRUB_DEV_ABSTRACTION_GELI: -#if defined (__FreeBSD__) || defined(__FreeBSD_kernel__) - { - char *whole; - struct gmesh mesh; - struct gclass *class; - const char *name; - int err; - char *lastsubdev = NULL; - - if (strncmp (os_dev, "/dev/", sizeof ("/dev/") - 1) != 0) - return; - name = os_dev + sizeof ("/dev/") - 1; - grub_util_follow_gpart_up (name, NULL, &whole); - - grub_util_info ("following geom '%s'", name); - - err = geom_gettree (&mesh); - if (err != 0) - /* TRANSLATORS: geom is the name of (k)FreeBSD device framework. - Usually left untranslated. - */ - grub_util_error ("%s", _("couldn't open geom")); - - LIST_FOREACH (class, &mesh.lg_class, lg_class) - { - struct ggeom *geom; - LIST_FOREACH (geom, &class->lg_geom, lg_geom) - { - struct gprovider *provider; - LIST_FOREACH (provider, &geom->lg_provider, lg_provider) - if (strcmp (provider->lg_name, name) == 0) - { - struct gconsumer *consumer; - char *fname; - - LIST_FOREACH (consumer, &geom->lg_consumer, lg_consumer) - break; - if (!consumer) - grub_util_error ("%s", - _("couldn't find geli consumer")); - fname = xasprintf ("/dev/%s", consumer->lg_provider->lg_name); - grub_util_info ("consumer %s", consumer->lg_provider->lg_name); - lastsubdev = consumer->lg_provider->lg_name; - grub_util_pull_device (fname); - free (fname); - } - } - } - if (ab == GRUB_DEV_ABSTRACTION_GELI && lastsubdev) - { - char *fname = xasprintf ("/dev/%s", lastsubdev); - char *grdev = grub_util_get_grub_dev (fname); - free (fname); - - if (grdev) - { - grub_err_t gr_err; - gr_err = grub_cryptodisk_cheat_mount (grdev, os_dev); - if (gr_err) - grub_util_error (_("can't mount encrypted volume `%s': %s"), - lastsubdev, grub_errmsg); - } - - grub_free (grdev); - } - } -#endif - break; - - case GRUB_DEV_ABSTRACTION_LVM: -#if !defined (__MINGW32__) && !defined (__CYGWIN__) && !defined (__GNU__) - pull_lvm_by_command (os_dev); -#endif - /* Fallthrough in case that lvm-tools are unavailable. */ - case GRUB_DEV_ABSTRACTION_LUKS: -#ifdef HAVE_DEVICE_MAPPER - { - struct dm_tree *tree; - struct dm_tree_node *node; - struct dm_tree_node *child; - void *handle = NULL; - char *lastsubdev = NULL; - - if (!grub_util_open_dm (os_dev, &tree, &node)) - return; - - while ((child = dm_tree_next_child (&handle, node, 0))) - { - const struct dm_info *dm = dm_tree_node_get_info (child); - char *subdev; - if (!dm) - continue; - subdev = grub_find_device ("/dev", makedev (dm->major, dm->minor)); - if (subdev) - { - lastsubdev = subdev; - grub_util_pull_device (subdev); - } - } - if (ab == GRUB_DEV_ABSTRACTION_LUKS && lastsubdev) - { - char *grdev = grub_util_get_grub_dev (lastsubdev); - dm_tree_free (tree); - if (grdev) - { - grub_err_t err; - err = grub_cryptodisk_cheat_mount (grdev, os_dev); - if (err) - grub_util_error (_("can't mount encrypted volume `%s': %s"), - lastsubdev, grub_errmsg); - } - grub_free (grdev); - } - else - dm_tree_free (tree); - } -#endif - return; - case GRUB_DEV_ABSTRACTION_RAID: -#ifdef __linux__ - { - char **devicelist = grub_util_raid_getmembers (os_dev, 0); - int i; - for (i = 0; devicelist[i];i++) - { - grub_util_pull_device (devicelist[i]); - free (devicelist[i]); - } - free (devicelist); - } -#endif - return; - - default: /* GRUB_DEV_ABSTRACTION_NONE */ - free (grub_util_biosdisk_get_grub_dev (os_dev)); - return; - } -} - int grub_util_biosdisk_is_floppy (grub_disk_t disk) { @@ -1785,476 +966,12 @@ convert_system_partition_to_system_disk (const char *os_dev, struct stat *st, { *is_part = 0; -#if defined(__linux__) - char *path = xmalloc (PATH_MAX); + if (grub_util_device_is_mapped_stat (st)) + return grub_util_devmapper_part_to_disk (st, is_part, os_dev); - if (! realpath (os_dev, path)) - return NULL; + *is_part = 0; - if (strncmp ("/dev/", path, 5) == 0) - { - char *p = path + 5; - - /* If this is an IDE disk. */ - if (strncmp ("ide/", p, 4) == 0) - { - p = strstr (p, "part"); - if (p) - { - *is_part = 1; - strcpy (p, "disc"); - } - - return path; - } - - /* If this is a SCSI disk. */ - if (strncmp ("scsi/", p, 5) == 0) - { - p = strstr (p, "part"); - if (p) - { - *is_part = 1; - strcpy (p, "disc"); - } - - return path; - } - - /* If this is a DAC960 disk. */ - if (strncmp ("rd/c", p, 4) == 0) - { - /* /dev/rd/c[0-9]+d[0-9]+(p[0-9]+)? */ - p = strchr (p, 'p'); - if (p) - { - *is_part = 1; - *p = '\0'; - } - - return path; - } - - /* If this is a Mylex AcceleRAID Array. */ - if (strncmp ("rs/c", p, 4) == 0) - { - /* /dev/rd/c[0-9]+d[0-9]+(p[0-9]+)? */ - p = strchr (p, 'p'); - if (p) - { - *is_part = 1; - *p = '\0'; - } - - return path; - } - /* If this is a CCISS disk. */ - if (strncmp ("cciss/c", p, sizeof ("cciss/c") - 1) == 0) - { - /* /dev/cciss/c[0-9]+d[0-9]+(p[0-9]+)? */ - p = strchr (p, 'p'); - if (p) - { - *is_part = 1; - *p = '\0'; - } - - return path; - } - - /* If this is an AOE disk. */ - if (strncmp ("etherd/e", p, sizeof ("etherd/e") - 1) == 0) - { - /* /dev/etherd/e[0-9]+\.[0-9]+(p[0-9]+)? */ - p = strchr (p, 'p'); - if (p) - { - *is_part = 1; - *p = '\0'; - } - - return path; - } - - /* If this is a Compaq Intelligent Drive Array. */ - if (strncmp ("ida/c", p, sizeof ("ida/c") - 1) == 0) - { - /* /dev/ida/c[0-9]+d[0-9]+(p[0-9]+)? */ - p = strchr (p, 'p'); - if (p) - { - *is_part = 1; - *p = '\0'; - } - - return path; - } - - /* If this is an I2O disk. */ - if (strncmp ("i2o/hd", p, sizeof ("i2o/hd") - 1) == 0) - { - /* /dev/i2o/hd[a-z]([0-9]+)? */ - if (p[sizeof ("i2o/hda") - 1]) - *is_part = 1; - p[sizeof ("i2o/hda") - 1] = '\0'; - return path; - } - - /* If this is a MultiMediaCard (MMC). */ - if (strncmp ("mmcblk", p, sizeof ("mmcblk") - 1) == 0) - { - /* /dev/mmcblk[0-9]+(p[0-9]+)? */ - p = strchr (p, 'p'); - if (p) - { - *is_part = 1; - *p = '\0'; - } - - return path; - } - - if (strncmp ("md", p, 2) == 0 - && p[2] >= '0' && p[2] <= '9') - { - char *ptr = p + 2; - while (*ptr >= '0' && *ptr <= '9') - ptr++; - if (*ptr) - *is_part = 1; - *ptr = 0; - return path; - } - - if (strncmp ("nbd", p, 3) == 0 - && p[3] >= '0' && p[3] <= '9') - { - char *ptr = p + 3; - while (*ptr >= '0' && *ptr <= '9') - ptr++; - if (*ptr) - *is_part = 1; - *ptr = 0; - return path; - } - - /* If this is an IDE, SCSI or Virtio disk. */ - if (strncmp ("vdisk", p, 5) == 0 - && p[5] >= 'a' && p[5] <= 'z') - { - /* /dev/vdisk[a-z][0-9]* */ - if (p[6]) - *is_part = 1; - p[6] = '\0'; - return path; - } - if ((strncmp ("hd", p, 2) == 0 - || strncmp ("vd", p, 2) == 0 - || strncmp ("sd", p, 2) == 0) - && p[2] >= 'a' && p[2] <= 'z') - { - char *pp = p + 2; - while (*pp >= 'a' && *pp <= 'z') - pp++; - if (*pp) - *is_part = 1; - /* /dev/[hsv]d[a-z]+[0-9]* */ - *pp = '\0'; - return path; - } - - /* If this is a Xen virtual block device. */ - if ((strncmp ("xvd", p, 3) == 0) && p[3] >= 'a' && p[3] <= 'z') - { - char *pp = p + 3; - while (*pp >= 'a' && *pp <= 'z') - pp++; - if (*pp) - *is_part = 1; - /* /dev/xvd[a-z]+[0-9]* */ - *pp = '\0'; - return path; - } - -#ifdef HAVE_DEVICE_MAPPER - if (dm_is_dm_major (major (st->st_rdev))) - { - struct dm_tree *tree; - uint32_t maj, min; - struct dm_tree_node *node = NULL, *child; - void *handle; - const char *node_uuid, *mapper_name = NULL, *child_uuid, *child_name; - - tree = dm_tree_create (); - if (! tree) - { - grub_util_info ("dm_tree_create failed"); - goto devmapper_out; - } - - maj = major (st->st_rdev); - min = minor (st->st_rdev); - if (! dm_tree_add_dev (tree, maj, min)) - { - grub_util_info ("dm_tree_add_dev failed"); - goto devmapper_out; - } - - node = dm_tree_find_node (tree, maj, min); - if (! node) - { - grub_util_info ("dm_tree_find_node failed"); - goto devmapper_out; - } - reiterate: - node_uuid = dm_tree_node_get_uuid (node); - if (! node_uuid) - { - grub_util_info ("%s has no DM uuid", path); - goto devmapper_out; - } - if (strncmp (node_uuid, "LVM-", 4) == 0) - { - grub_util_info ("%s is an LVM", path); - goto devmapper_out; - } - if (strncmp (node_uuid, "mpath-", 6) == 0) - { - /* Multipath partitions have partN-mpath-* UUIDs, and are - linear mappings so are handled by - grub_util_get_dm_node_linear_info. Multipath disks are not - linear mappings and must be handled specially. */ - grub_util_info ("%s is a multipath disk", path); - goto devmapper_out; - } - if (strncmp (node_uuid, "DMRAID-", 7) != 0) - { - int major, minor; - const char *node_name; - grub_util_info ("%s is not DM-RAID", path); - - if ((node_name = dm_tree_node_get_name (node)) - && grub_util_get_dm_node_linear_info (node_name, - &major, &minor, 0)) - { - *is_part = 1; - if (tree) - dm_tree_free (tree); - free (path); - char *ret = grub_find_device ("/dev", - (major << 8) | minor); - return ret; - } - - goto devmapper_out; - } - - handle = NULL; - /* Counter-intuitively, device-mapper refers to the disk-like - device containing a DM-RAID partition device as a "child" of - the partition device. */ - child = dm_tree_next_child (&handle, node, 0); - if (! child) - { - grub_util_info ("%s has no DM children", path); - goto devmapper_out; - } - child_uuid = dm_tree_node_get_uuid (child); - if (! child_uuid) - { - grub_util_info ("%s child has no DM uuid", path); - goto devmapper_out; - } - else if (strncmp (child_uuid, "DMRAID-", 7) != 0) - { - grub_util_info ("%s child is not DM-RAID", path); - goto devmapper_out; - } - child_name = dm_tree_node_get_name (child); - if (! child_name) - { - grub_util_info ("%s child has no DM name", path); - goto devmapper_out; - } - mapper_name = child_name; - *is_part = 1; - node = child; - goto reiterate; - -devmapper_out: - if (! mapper_name && node) - { - /* This is a DM-RAID disk, not a partition. */ - mapper_name = dm_tree_node_get_name (node); - if (! mapper_name) - grub_util_info ("%s has no DM name", path); - } - char *ret; - if (mapper_name) - ret = xasprintf ("/dev/mapper/%s", mapper_name); - else - ret = NULL; - - if (tree) - dm_tree_free (tree); - free (path); - return ret; - } -#endif /* HAVE_DEVICE_MAPPER */ - } - - return path; - -#elif defined(__GNU__) - char *path = xstrdup (os_dev); - if (strncmp ("/dev/sd", path, 7) == 0 || strncmp ("/dev/hd", path, 7) == 0) - { - char *p = strchr (path + 7, 's'); - if (p) - { - *is_part = 1; - *p = '\0'; - } - } - return path; - -#elif defined(__CYGWIN__) - char *path = xstrdup (os_dev); - if (strncmp ("/dev/sd", path, 7) == 0 && 'a' <= path[7] && path[7] <= 'z' - && path[8]) - { - *is_part = 1; - path[8] = 0; - } - return path; - -#elif defined(__FreeBSD__) || defined(__FreeBSD_kernel__) - char *out, *out2; - if (strncmp (os_dev, "/dev/", sizeof ("/dev/") - 1) != 0) - return xstrdup (os_dev); - grub_util_follow_gpart_up (os_dev + sizeof ("/dev/") - 1, NULL, &out); - - if (grub_strcmp (os_dev + sizeof ("/dev/") - 1, out) != 0) - *is_part = 1; - out2 = xasprintf ("/dev/%s", out); - free (out); - - return out2; -#elif defined(__APPLE__) - char *path = xstrdup (os_dev); - if (strncmp ("/dev/", path, 5) == 0) - { - char *p; - for (p = path + 5; *p; ++p) - if (grub_isdigit(*p)) - { - p = strpbrk (p, "sp"); - if (p) - { - *is_part = 1; - *p = '\0'; - } - break; - } - } - return path; - -#elif defined(__NetBSD__) || defined(__OpenBSD__) - int rawpart = -1; -# ifdef HAVE_GETRAWPARTITION - rawpart = getrawpartition(); -# endif /* HAVE_GETRAWPARTITION */ - if (rawpart < 0) - return xstrdup (os_dev); - -#if defined(__NetBSD__) - /* NetBSD disk wedges are of the form "/dev/rdk.*". */ - if (strncmp ("/dev/rdk", os_dev, sizeof("/dev/rdk") - 1) == 0) - { - struct dkwedge_info dkw; - int fd; - - fd = open (os_dev, O_RDONLY); - if (fd == -1) - { - grub_error (GRUB_ERR_BAD_DEVICE, - N_("cannot open `%s': %s"), os_dev, - strerror (errno)); - return xstrdup (os_dev); - } - /* We don't call configure_device_driver since this isn't a floppy device name. */ - if (ioctl (fd, DIOCGWEDGEINFO, &dkw) == -1) - { - grub_error (GRUB_ERR_BAD_DEVICE, - "cannot get disk wedge info of `%s'", os_dev); - close (fd); - return xstrdup (os_dev); - } - *is_part = (dkw.dkw_offset != 0); - close (fd); - return xasprintf ("/dev/r%s%c", dkw.dkw_parent, 'a' + rawpart); - } -#endif - - /* NetBSD (disk label) partitions are of the form "/dev/r[a-z]+[0-9][a-z]". */ - if (strncmp ("/dev/r", os_dev, sizeof("/dev/r") - 1) == 0 && - (os_dev[sizeof("/dev/r") - 1] >= 'a' && os_dev[sizeof("/dev/r") - 1] <= 'z') && - strncmp ("fd", os_dev + sizeof("/dev/r") - 1, sizeof("fd") - 1) != 0) /* not a floppy device name */ - { - char *path = xstrdup (os_dev); - char *p; - for (p = path + sizeof("/dev/r"); *p >= 'a' && *p <= 'z'; p++); - if (grub_isdigit(*p)) - { - p++; - if ((*p >= 'a' && *p <= 'z') && (*(p+1) == '\0')) - { - if (*p != 'a' + rawpart) - *is_part = 1; - /* path matches the required regular expression and - p points to its last character. */ - *p = 'a' + rawpart; - } - } - return path; - } - - return xstrdup (os_dev); - -#elif defined (__sun__) - char *colon = grub_strrchr (os_dev, ':'); - if (grub_memcmp (os_dev, "/devices", sizeof ("/devices") - 1) == 0 - && colon) - { - char *ret = xmalloc (colon - os_dev + sizeof (":q,raw")); - if (grub_strcmp (colon, ":q,raw") != 0) - *is_part = 1; - grub_memcpy (ret, os_dev, colon - os_dev); - grub_memcpy (ret + (colon - os_dev), ":q,raw", sizeof (":q,raw")); - return ret; - } - else - return xstrdup (os_dev); -#elif defined (__APPLE__) - char *ptr; - char *ret = xstrdup (os_dev); - int disk = grub_memcmp (ret, "/dev/disk", sizeof ("/dev/disk") - 1) == 0; - int rdisk = grub_memcmp (ret, "/dev/rdisk", sizeof ("/dev/rdisk") - 1) == 0; - if (!disk && !rdisk) - return ret; - ptr = ret + sizeof ("/dev/disk") + rdisk - 1; - while (*ptr >= '0' && *ptr <= '9') - ptr++; - if (*ptr) - { - *is_part = 1; - *ptr = 0; - } - return ret; -#else -# warning "The function `convert_system_partition_to_system_disk' might not work on your OS correctly." - return xstrdup (os_dev); -#endif + return grub_util_part_to_disk (os_dev, st, is_part); } static const char * @@ -2329,7 +1046,7 @@ grub_util_get_os_disk (const char *os_dev) return convert_system_partition_to_system_disk (os_dev, &st, &is_part); } -#if defined(__linux__) || defined(__CYGWIN__) || defined(HAVE_DIOCGDINFO) || defined(__FreeBSD__) || defined(__FreeBSD_kernel__) || defined (__sun__) +#if !defined(__APPLE__) /* Context for grub_util_biosdisk_get_grub_dev. */ struct grub_util_biosdisk_get_grub_dev_ctx { @@ -2390,14 +1107,36 @@ grub_util_biosdisk_get_grub_dev (const char *os_dev) } free (sys_disk); -#if defined(__FreeBSD__) || defined(__FreeBSD_kernel__) || defined(__APPLE__) || defined(__NetBSD__) || defined (__sun__) +#if GRUB_DISK_DEVS_ARE_CHAR if (! S_ISCHR (st.st_mode)) #else if (! S_ISBLK (st.st_mode)) #endif return make_device_name (drive, -1, -1); -#if defined(__linux__) || defined(__CYGWIN__) || defined(HAVE_DIOCGDINFO) || defined(__FreeBSD__) || defined(__FreeBSD_kernel__) || defined (__sun__) || defined(__OpenBSD__) +#if defined(__APPLE__) + /* Apple uses "/dev/r?disk[0-9]+(s[0-9]+)?". */ + { + const char *p; + int disk = (grub_memcmp (os_dev, "/dev/disk", sizeof ("/dev/disk") - 1) + == 0); + int rdisk = (grub_memcmp (os_dev, "/dev/rdisk", sizeof ("/dev/rdisk") - 1) + == 0); + + if (!disk && !rdisk) + return make_device_name (drive, -1, -1); + + p = os_dev + sizeof ("/dev/disk") + rdisk - 1; + while (*p >= '0' && *p <= '9') + p++; + if (*p != 's') + return make_device_name (drive, -1, -1); + p++; + + return make_device_name (drive, strtol (p, NULL, 10) - 1, -1); + } + +#else /* Linux counts partitions uniformly, whether a BSD partition or a DOS partition, so mapping them to GRUB devices is not trivial. @@ -2417,21 +1156,52 @@ grub_util_biosdisk_get_grub_dev (const char *os_dev) name = make_device_name (drive, -1, -1); -# if !defined(HAVE_DIOCGDINFO) && !defined(__sun__) +# ifdef FLOPPY_MAJOR if (MAJOR (st.st_rdev) == FLOPPY_MAJOR) return name; -# else /* defined(HAVE_DIOCGDINFO) */ +# else /* Since os_dev and convert_system_partition_to_system_disk (os_dev) are * different, we know that os_dev cannot be a floppy device. */ -# endif /* !defined(HAVE_DIOCGDINFO) */ +# endif - ctx.start = grub_hostdisk_find_partition_start (os_dev); + ctx.start = grub_util_find_partition_start (os_dev); if (grub_errno != GRUB_ERR_NONE) { free (name); return 0; } +#if defined(__GNU__) + /* Some versions of Hurd use badly glued Linux code to handle partitions + resulting in partitions being promoted to disks. */ + /* GNU uses "/dev/[hs]d[0-9]+(s[0-9]+[a-z]?)?". */ + if (ctx.start == (grub_disk_addr_t) -1) + { + char *p; + int dos_part = -1; + int bsd_part = -1; + + p = strrchr (os_dev + sizeof ("/dev/hd") - 1, 's'); + if (p) + { + long int n; + char *q; + + p++; + n = strtol (p, &q, 10); + if (p != q && n != GRUB_LONG_MIN && n != GRUB_LONG_MAX) + { + dos_part = (int) n - 1; + + if (*q >= 'a' && *q <= 'g') + bsd_part = *q - 'a'; + } + } + + return make_device_name (drive, dos_part, bsd_part); + } +#endif + grub_util_info ("%s starts from %" PRIuGRUB_UINT64_T, os_dev, ctx.start); if (ctx.start == 0 && !is_part) @@ -2495,57 +1265,6 @@ grub_util_biosdisk_get_grub_dev (const char *os_dev) return name; } -#elif defined(__GNU__) - /* GNU uses "/dev/[hs]d[0-9]+(s[0-9]+[a-z]?)?". */ - { - char *p; - int dos_part = -1; - int bsd_part = -1; - - p = strrchr (os_dev, 's'); - if (p) - { - long int n; - char *q; - - p++; - n = strtol (p, &q, 10); - if (p != q && n != GRUB_LONG_MIN && n != GRUB_LONG_MAX) - { - dos_part = (int) n - 1; - - if (*q >= 'a' && *q <= 'g') - bsd_part = *q - 'a'; - } - } - - return make_device_name (drive, dos_part, bsd_part); - } - -#elif defined(__APPLE__) - /* Apple uses "/dev/r?disk[0-9]+(s[0-9]+)?". */ - { - const char *p; - int disk = (grub_memcmp (os_dev, "/dev/disk", sizeof ("/dev/disk") - 1) - == 0); - int rdisk = (grub_memcmp (os_dev, "/dev/rdisk", sizeof ("/dev/rdisk") - 1) - == 0); - - if (!disk && !rdisk) - return make_device_name (drive, -1, -1); - - p = os_dev + sizeof ("/dev/disk") + rdisk - 1; - while (*p >= '0' && *p <= '9') - p++; - if (*p != 's') - return make_device_name (drive, -1, -1); - p++; - - return make_device_name (drive, strtol (p, NULL, 10) - 1, -1); - } -#else -# warning "The function `grub_util_biosdisk_get_grub_dev' might not work on your OS correctly." - return make_device_name (drive, -1, -1); #endif } @@ -2563,239 +1282,6 @@ grub_util_biosdisk_is_present (const char *os_dev) return ret; } -char * -grub_util_get_grub_dev (const char *os_dev) -{ - char *grub_dev = NULL; - - grub_util_pull_device (os_dev); - - switch (grub_util_get_dev_abstraction (os_dev)) - { -#ifdef HAVE_DEVICE_MAPPER - case GRUB_DEV_ABSTRACTION_LVM: - { - char *uuid, *optr; - unsigned i; - int dashes[] = { 0, 6, 10, 14, 18, 22, 26, 32, 38, 42, 46, 50, 54, 58}; - uuid = get_dm_uuid (os_dev); - if (!uuid) - break; - grub_dev = xmalloc (grub_strlen (uuid) + 40); - optr = grub_stpcpy (grub_dev, "lvmid/"); - for (i = 0; i < ARRAY_SIZE (dashes) - 1; i++) - { - memcpy (optr, uuid + sizeof ("LVM-") - 1 + dashes[i], - dashes[i+1] - dashes[i]); - optr += dashes[i+1] - dashes[i]; - *optr++ = '-'; - } - optr = stpcpy (optr, uuid + sizeof ("LVM-") - 1 + dashes[i]); - *optr = '\0'; - grub_dev[sizeof("lvmid/xxxxxx-xxxx-xxxx-xxxx-xxxx-xxxx-xxxxxx") - 1] - = '/'; - free (uuid); - } - break; -#elif defined(__linux__) || defined(__FreeBSD__) || defined(__FreeBSD_kernel__) - case GRUB_DEV_ABSTRACTION_LVM: - - { - unsigned short len; - grub_size_t offset = sizeof (LVM_DEV_MAPPER_STRING) - 1; - - len = strlen (os_dev) - offset + 1; - grub_dev = xmalloc (len + sizeof ("lvm/")); - - grub_memcpy (grub_dev, "lvm/", sizeof ("lvm/") - 1); - grub_memcpy (grub_dev + sizeof ("lvm/") - 1, os_dev + offset, len); - } - - break; -#endif - - case GRUB_DEV_ABSTRACTION_LUKS: -#ifdef HAVE_DEVICE_MAPPER - { - char *uuid, *dash; - uuid = get_dm_uuid (os_dev); - if (!uuid) - break; - dash = grub_strchr (uuid + sizeof ("CRYPT-LUKS1-") - 1, '-'); - if (dash) - *dash = 0; - grub_dev = grub_xasprintf ("cryptouuid/%s", - uuid + sizeof ("CRYPT-LUKS1-") - 1); - grub_free (uuid); - } -#endif - break; - -#if defined (__FreeBSD__) || defined(__FreeBSD_kernel__) - case GRUB_DEV_ABSTRACTION_GELI: - { - char *whole; - struct gmesh mesh; - struct gclass *class; - const char *name; - int err; - - if (strncmp (os_dev, "/dev/", sizeof ("/dev/") - 1) != 0) - return 0; - name = os_dev + sizeof ("/dev/") - 1; - grub_util_follow_gpart_up (name, NULL, &whole); - - grub_util_info ("following geom '%s'", name); - - err = geom_gettree (&mesh); - if (err != 0) - /* TRANSLATORS: geom is the name of (k)FreeBSD device framework. - Usually left untranslated. - */ - grub_util_error ("%s", _("couldn't open geom")); - - LIST_FOREACH (class, &mesh.lg_class, lg_class) - { - struct ggeom *geom; - LIST_FOREACH (geom, &class->lg_geom, lg_geom) - { - struct gprovider *provider; - LIST_FOREACH (provider, &geom->lg_provider, lg_provider) - if (strcmp (provider->lg_name, name) == 0) - { - struct gconsumer *consumer; - char *fname; - char *uuid; - - LIST_FOREACH (consumer, &geom->lg_consumer, lg_consumer) - break; - if (!consumer) - grub_util_error ("%s", - _("couldn't find geli consumer")); - fname = xasprintf ("/dev/%s", consumer->lg_provider->lg_name); - uuid = grub_util_get_geli_uuid (fname); - if (!uuid) - grub_util_error ("%s", - _("couldn't retrieve geli UUID")); - grub_dev = xasprintf ("cryptouuid/%s", uuid); - free (fname); - free (uuid); - } - } - } - } - break; -#endif - -#ifdef __linux__ - case GRUB_DEV_ABSTRACTION_RAID: - - if (os_dev[7] == '_' && os_dev[8] == 'd') - { - /* This a partitionable RAID device of the form /dev/md_dNNpMM. */ - - char *p, *q; - - p = strdup (os_dev + sizeof ("/dev/md_d") - 1); - - q = strchr (p, 'p'); - if (q) - *q = ','; - - grub_dev = xasprintf ("md%s", p); - free (p); - } - else if (os_dev[7] == '/' && os_dev[8] == 'd') - { - /* This a partitionable RAID device of the form /dev/md/dNNpMM. */ - - char *p, *q; - - p = strdup (os_dev + sizeof ("/dev/md/d") - 1); - - q = strchr (p, 'p'); - if (q) - *q = ','; - - grub_dev = xasprintf ("md%s", p); - free (p); - } - else if (os_dev[7] >= '0' && os_dev[7] <= '9') - { - char *p , *q; - - p = strdup (os_dev + sizeof ("/dev/md") - 1); - - q = strchr (p, 'p'); - if (q) - *q = ','; - - grub_dev = xasprintf ("md%s", p); - free (p); - } - else if (os_dev[7] == '/' && os_dev[8] >= '0' && os_dev[8] <= '9') - { - char *p , *q; - - p = strdup (os_dev + sizeof ("/dev/md/") - 1); - - q = strchr (p, 'p'); - if (q) - *q = ','; - - grub_dev = xasprintf ("md%s", p); - free (p); - } - else if (os_dev[7] == '/') - { - /* mdraid 1.x with a free name. */ - char *p , *q; - - p = strdup (os_dev + sizeof ("/dev/md/") - 1); - - q = strchr (p, 'p'); - if (q) - *q = ','; - - grub_dev = xasprintf ("md/%s", p); - free (p); - } - else - grub_util_error (_("unknown kind of RAID device `%s'"), os_dev); - - { - char *mdadm_name = get_mdadm_uuid (os_dev); - - if (mdadm_name) - { - const char *q; - - for (q = os_dev + strlen (os_dev) - 1; q >= os_dev - && grub_isdigit (*q); q--); - - if (q >= os_dev && *q == 'p') - { - free (grub_dev); - grub_dev = xasprintf ("mduuid/%s,%s", mdadm_name, q + 1); - goto done; - } - free (grub_dev); - grub_dev = xasprintf ("mduuid/%s", mdadm_name); - - done: - free (mdadm_name); - } - } - break; -#endif /* __linux__ */ - - default: /* GRUB_DEV_ABSTRACTION_NONE */ - grub_dev = grub_util_biosdisk_get_grub_dev (os_dev); - } - - return grub_dev; -} - const char * grub_util_check_block_device (const char *blk_dev) { diff --git a/util/getroot_apple.c b/util/getroot_apple.c new file mode 100644 index 000000000..9ca8afcef --- /dev/null +++ b/util/getroot_apple.c @@ -0,0 +1,102 @@ +/* + * GRUB -- GRand Unified Bootloader + * Copyright (C) 1999,2000,2001,2002,2003,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 +#ifdef HAVE_LIMITS_H +#include +#endif + +#include + +#include +#include +#include +#include +#include +#include +#include + +#include + +# include +# include +# include +# include + +char * +grub_util_part_to_disk (const char *os_dev, struct stat *st, + int *is_part) +{ + char *path = xstrdup (os_dev); + if (strncmp ("/dev/", path, 5) == 0) + { + char *p; + for (p = path + 5; *p; ++p) + if (grub_isdigit(*p)) + { + p = strpbrk (p, "sp"); + if (p) + { + *is_part = 1; + *p = '\0'; + } + break; + } + } + return path; +} + +enum grub_dev_abstraction_types +grub_util_get_dev_abstraction_os (const char *os_dev __attribute__((unused))) +{ + return GRUB_DEV_ABSTRACTION_NONE; +} + +int +grub_util_pull_device_os (const char *os_dev __attribute__ ((unused)), + enum grub_dev_abstraction_types ab __attribute__ ((unused))) +{ + return 0; +} + +char * +grub_util_get_grub_dev_os (const char *os_dev __attribute__ ((unused))) +{ + return NULL; +} + + +grub_disk_addr_t +grub_util_find_partition_start_os (const char *dev __attribute__ ((unused))) +{ + return 0; +} diff --git a/util/getroot_basic.c b/util/getroot_basic.c new file mode 100644 index 000000000..0242c9cc1 --- /dev/null +++ b/util/getroot_basic.c @@ -0,0 +1,82 @@ +/* + * GRUB -- GRand Unified Bootloader + * Copyright (C) 1999,2000,2001,2002,2003,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 +#ifdef HAVE_LIMITS_H +#include +#endif + +#include + +#include +#include +#include +#include +#include +#include +#include + + +char * +grub_util_part_to_disk (const char *os_dev, + struct stat *st __attribute__ ((unused)), + int *is_part) +{ + *is_part = 0; + return xstrdup (os_dev); +} + +enum grub_dev_abstraction_types +grub_util_get_dev_abstraction_os (const char *os_dev __attribute__((unused))) +{ + return GRUB_DEV_ABSTRACTION_NONE; +} + +int +grub_util_pull_device_os (const char *os_dev __attribute__ ((unused)), + enum grub_dev_abstraction_types ab __attribute__ ((unused))) +{ + return 0; +} + +char * +grub_util_get_grub_dev_os (const char *os_dev __attribute__ ((unused))) +{ + return NULL; +} + + +grub_disk_addr_t +grub_util_find_partition_start_os (const char *dev __attribute__ ((unused))) +{ + return 0; +} diff --git a/util/getroot_bsd.c b/util/getroot_bsd.c new file mode 100644 index 000000000..062bce294 --- /dev/null +++ b/util/getroot_bsd.c @@ -0,0 +1,199 @@ +/* + * GRUB -- GRand Unified Bootloader + * Copyright (C) 1999,2000,2001,2002,2003,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 +#ifdef HAVE_LIMITS_H +#include +#endif + +#include + +#include +#include +#include +#include +#include +#include +#include + +#include + +# include +# include /* struct disklabel */ +# include /* struct dkwedge_info */ +# ifdef HAVE_GETRAWPARTITION +# include /* getrawpartition */ +# endif /* HAVE_GETRAWPARTITION */ +#if defined(__NetBSD__) +# include +#endif +#if defined(__OpenBSD__) +# include +#endif + +char * +grub_util_part_to_disk (const char *os_dev, struct stat *st, + int *is_part) +{ + int rawpart = -1; +# ifdef HAVE_GETRAWPARTITION + rawpart = getrawpartition(); +# endif /* HAVE_GETRAWPARTITION */ + if (rawpart < 0) + return xstrdup (os_dev); + +#if defined(__NetBSD__) + /* NetBSD disk wedges are of the form "/dev/rdk.*". */ + if (strncmp ("/dev/rdk", os_dev, sizeof("/dev/rdk") - 1) == 0) + { + struct dkwedge_info dkw; + int fd; + + fd = open (os_dev, O_RDONLY); + if (fd == -1) + { + grub_error (GRUB_ERR_BAD_DEVICE, + N_("cannot open `%s': %s"), os_dev, + strerror (errno)); + return xstrdup (os_dev); + } + /* We don't call configure_device_driver since this isn't a floppy device name. */ + if (ioctl (fd, DIOCGWEDGEINFO, &dkw) == -1) + { + grub_error (GRUB_ERR_BAD_DEVICE, + "cannot get disk wedge info of `%s'", os_dev); + close (fd); + return xstrdup (os_dev); + } + *is_part = (dkw.dkw_offset != 0); + close (fd); + return xasprintf ("/dev/r%s%c", dkw.dkw_parent, 'a' + rawpart); + } +#endif + + /* NetBSD (disk label) partitions are of the form "/dev/r[a-z]+[0-9][a-z]". */ + if (strncmp ("/dev/r", os_dev, sizeof("/dev/r") - 1) == 0 && + (os_dev[sizeof("/dev/r") - 1] >= 'a' && os_dev[sizeof("/dev/r") - 1] <= 'z') && + strncmp ("fd", os_dev + sizeof("/dev/r") - 1, sizeof("fd") - 1) != 0) /* not a floppy device name */ + { + char *path = xstrdup (os_dev); + char *p; + for (p = path + sizeof("/dev/r"); *p >= 'a' && *p <= 'z'; p++); + if (grub_isdigit(*p)) + { + p++; + if ((*p >= 'a' && *p <= 'z') && (*(p+1) == '\0')) + { + if (*p != 'a' + rawpart) + *is_part = 1; + /* path matches the required regular expression and + p points to its last character. */ + *p = 'a' + rawpart; + } + } + return path; + } + + return xstrdup (os_dev); +} + +enum grub_dev_abstraction_types +grub_util_get_dev_abstraction_os (const char *os_dev __attribute__((unused))) +{ + return GRUB_DEV_ABSTRACTION_NONE; +} + +int +grub_util_pull_device_os (const char *os_dev __attribute__ ((unused)), + enum grub_dev_abstraction_types ab __attribute__ ((unused))) +{ + return 0; +} + +char * +grub_util_get_grub_dev_os (const char *os_dev __attribute__ ((unused))) +{ + return NULL; +} + + +grub_disk_addr_t +grub_util_find_partition_start_os (const char *dev) +{ + int fd; +# if defined(__NetBSD__) + struct dkwedge_info dkw; +# endif /* defined(__NetBSD__) */ + struct disklabel label; + int p_index; + + 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 defined(__NetBSD__) + configure_device_driver (fd); + /* First handle the case of disk wedges. */ + if (ioctl (fd, DIOCGWEDGEINFO, &dkw) == 0) + { + close (fd); + return (grub_disk_addr_t) dkw.dkw_offset; + } +# endif /* defined(__NetBSD__) */ + + if (ioctl (fd, DIOCGDINFO, &label) == -1) + { + grub_error (GRUB_ERR_BAD_DEVICE, + "cannot get disk label of `%s'", dev); + close (fd); + return 0; + } + + close (fd); + + if (dev[0]) + p_index = dev[strlen(dev) - 1] - 'a'; + else + p_index = -1; + + if (p_index >= label.d_npartitions || p_index < 0) + { + grub_error (GRUB_ERR_BAD_DEVICE, + "no disk label entry for `%s'", dev); + return 0; + } + return (grub_disk_addr_t) label.d_partitions[p_index].p_offset; +} diff --git a/util/getroot_cygwin.c b/util/getroot_cygwin.c new file mode 100644 index 000000000..6407d2c1e --- /dev/null +++ b/util/getroot_cygwin.c @@ -0,0 +1,115 @@ +/* + * GRUB -- GRand Unified Bootloader + * Copyright (C) 1999,2000,2001,2002,2003,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 +#ifdef HAVE_LIMITS_H +#include +#endif + +#include + +#include +#include +#include +#include +#include +#include +#include + +#include + +# include +# include /* BLKGETSIZE64 */ +# include /* HDIO_GETGEO */ +# include + + +char * +grub_util_part_to_disk (const char *os_dev, + struct stat *st __attribute__ ((unused)), + int *is_part) +{ + char *path = xstrdup (os_dev); + if (strncmp ("/dev/sd", path, 7) == 0 && 'a' <= path[7] && path[7] <= 'z' + && path[8]) + { + *is_part = 1; + path[8] = 0; + } + return path; +} + +enum grub_dev_abstraction_types +grub_util_get_dev_abstraction_os (const char *os_dev __attribute__((unused))) +{ + return GRUB_DEV_ABSTRACTION_NONE; +} + +int +grub_util_pull_device_os (const char *os_dev __attribute__ ((unused)), + enum grub_dev_abstraction_types ab __attribute__ ((unused))) +{ + return 0; +} + +char * +grub_util_get_grub_dev_os (const char *os_dev __attribute__ ((unused))) +{ + return NULL; +} + +grub_disk_addr_t +grub_util_find_partition_start_os (const char *dev) +{ + int fd; + struct hd_geometry hdg; + + 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; +} diff --git a/util/getroot_devmapper.c b/util/getroot_devmapper.c new file mode 100644 index 000000000..d919a40d3 --- /dev/null +++ b/util/getroot_devmapper.c @@ -0,0 +1,451 @@ +/* + * GRUB -- GRand Unified Bootloader + * Copyright (C) 1999,2000,2001,2002,2003,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 + +#ifdef HAVE_DEVICE_MAPPER + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#ifdef HAVE_LIMITS_H +#include +#endif + +#include + +#include +#include +#include +#include +#include +#include +#include + +static int +grub_util_open_dm (const char *os_dev, struct dm_tree **tree, + struct dm_tree_node **node) +{ + uint32_t maj, min; + struct stat st; + + *node = NULL; + *tree = NULL; + + if (stat (os_dev, &st) < 0) + return 0; + + maj = major (st.st_rdev); + min = minor (st.st_rdev); + + if (!dm_is_dm_major (maj)) + return 0; + + *tree = dm_tree_create (); + if (! *tree) + { + grub_puts_ (N_("Failed to create `device-mapper' tree")); + grub_dprintf ("hostdisk", "dm_tree_create failed\n"); + return 0; + } + + if (! dm_tree_add_dev (*tree, maj, min)) + { + grub_dprintf ("hostdisk", "dm_tree_add_dev failed\n"); + dm_tree_free (*tree); + *tree = NULL; + return 0; + } + + *node = dm_tree_find_node (*tree, maj, min); + if (! *node) + { + grub_dprintf ("hostdisk", "dm_tree_find_node failed\n"); + dm_tree_free (*tree); + *tree = NULL; + return 0; + } + return 1; +} + +static char * +get_dm_uuid (const char *os_dev) +{ + struct dm_tree *tree; + struct dm_tree_node *node; + const char *node_uuid; + char *ret; + + if (!grub_util_open_dm (os_dev, &tree, &node)) + return NULL; + + node_uuid = dm_tree_node_get_uuid (node); + if (! node_uuid) + { + grub_dprintf ("hostdisk", "%s has no DM uuid\n", os_dev); + dm_tree_free (tree); + return NULL; + } + + ret = grub_strdup (node_uuid); + + dm_tree_free (tree); + + return ret; +} + +enum grub_dev_abstraction_types +grub_util_get_dm_abstraction (const char *os_dev) +{ + char *uuid; + + uuid = get_dm_uuid (os_dev); + + if (uuid == NULL) + return GRUB_DEV_ABSTRACTION_NONE; + + if (strncmp (uuid, "LVM-", 4) == 0) + { + grub_free (uuid); + return GRUB_DEV_ABSTRACTION_LVM; + } + if (strncmp (uuid, "CRYPT-LUKS1-", 4) == 0) + { + grub_free (uuid); + return GRUB_DEV_ABSTRACTION_LUKS; + } + + grub_free (uuid); + return GRUB_DEV_ABSTRACTION_NONE; +} + +void +grub_util_pull_devmapper (const char *os_dev) +{ + struct dm_tree *tree; + struct dm_tree_node *node; + struct dm_tree_node *child; + void *handle = NULL; + char *lastsubdev = NULL; + char *uuid; + + uuid = get_dm_uuid (os_dev); + + if (!grub_util_open_dm (os_dev, &tree, &node)) + return; + + while ((child = dm_tree_next_child (&handle, node, 0))) + { + const struct dm_info *dm = dm_tree_node_get_info (child); + char *subdev; + if (!dm) + continue; + subdev = grub_find_device ("/dev", makedev (dm->major, dm->minor)); + if (subdev) + { + lastsubdev = subdev; + grub_util_pull_device (subdev); + } + } + if (uuid && strncmp (uuid, "CRYPT-LUKS1-", sizeof ("CRYPT-LUKS1-") - 1) == 0 + && lastsubdev) + { + char *grdev = grub_util_get_grub_dev (lastsubdev); + dm_tree_free (tree); + if (grdev) + { + grub_err_t err; + err = grub_cryptodisk_cheat_mount (grdev, os_dev); + if (err) + grub_util_error (_("can't mount encrypted volume `%s': %s"), + lastsubdev, grub_errmsg); + } + grub_free (grdev); + } + else + dm_tree_free (tree); +} + +char * +grub_util_devmapper_part_to_disk (struct stat *st, + int *is_part, const char *path) +{ + struct dm_tree *tree; + uint32_t maj, min; + struct dm_tree_node *node = NULL, *child; + void *handle; + const char *node_uuid, *mapper_name = NULL, *child_uuid, *child_name; + + tree = dm_tree_create (); + if (! tree) + { + grub_util_info ("dm_tree_create failed"); + goto devmapper_out; + } + + maj = major (st->st_rdev); + min = minor (st->st_rdev); + if (! dm_tree_add_dev (tree, maj, min)) + { + grub_util_info ("dm_tree_add_dev failed"); + goto devmapper_out; + } + + node = dm_tree_find_node (tree, maj, min); + if (! node) + { + grub_util_info ("dm_tree_find_node failed"); + goto devmapper_out; + } + reiterate: + node_uuid = dm_tree_node_get_uuid (node); + if (! node_uuid) + { + grub_util_info ("%s has no DM uuid", path); + goto devmapper_out; + } + if (strncmp (node_uuid, "LVM-", 4) == 0) + { + grub_util_info ("%s is an LVM", path); + goto devmapper_out; + } + if (strncmp (node_uuid, "mpath-", 6) == 0) + { + /* Multipath partitions have partN-mpath-* UUIDs, and are + linear mappings so are handled by + grub_util_get_dm_node_linear_info. Multipath disks are not + linear mappings and must be handled specially. */ + grub_util_info ("%s is a multipath disk", path); + goto devmapper_out; + } + if (strncmp (node_uuid, "DMRAID-", 7) != 0) + { + int major, minor; + const char *node_name; + grub_util_info ("%s is not DM-RAID", path); + + if ((node_name = dm_tree_node_get_name (node)) + && grub_util_get_dm_node_linear_info (node_name, + &major, &minor, 0)) + { + *is_part = 1; + if (tree) + dm_tree_free (tree); + char *ret = grub_find_device ("/dev", + (major << 8) | minor); + return ret; + } + + goto devmapper_out; + } + + handle = NULL; + /* Counter-intuitively, device-mapper refers to the disk-like + device containing a DM-RAID partition device as a "child" of + the partition device. */ + child = dm_tree_next_child (&handle, node, 0); + if (! child) + { + grub_util_info ("%s has no DM children", path); + goto devmapper_out; + } + child_uuid = dm_tree_node_get_uuid (child); + if (! child_uuid) + { + grub_util_info ("%s child has no DM uuid", path); + goto devmapper_out; + } + else if (strncmp (child_uuid, "DMRAID-", 7) != 0) + { + grub_util_info ("%s child is not DM-RAID", path); + goto devmapper_out; + } + child_name = dm_tree_node_get_name (child); + if (! child_name) + { + grub_util_info ("%s child has no DM name", path); + goto devmapper_out; + } + mapper_name = child_name; + *is_part = 1; + node = child; + goto reiterate; + + devmapper_out: + if (! mapper_name && node) + { + /* This is a DM-RAID disk, not a partition. */ + mapper_name = dm_tree_node_get_name (node); + if (! mapper_name) + grub_util_info ("%s has no DM name", path); + } + char *ret; + if (mapper_name) + ret = xasprintf ("/dev/mapper/%s", mapper_name); + else + ret = NULL; + + if (tree) + dm_tree_free (tree); + return ret; +} + +int +grub_util_device_is_mapped_stat (struct stat *st) +{ + if (!grub_device_mapper_supported ()) + return 0; + + return dm_is_dm_major (major (st->st_rdev)); +} + +char * +grub_util_get_devmapper_grub_dev (const char *os_dev) +{ + char *uuid, *optr; + char *grub_dev; + + uuid = get_dm_uuid (os_dev); + if (!uuid) + return NULL; + + if (strncmp (uuid, "LVM-", sizeof ("LVM-") - 1) == 0) + { + unsigned i; + int dashes[] = { 0, 6, 10, 14, 18, 22, 26, 32, 38, 42, 46, 50, 54, 58}; + grub_dev = xmalloc (grub_strlen (uuid) + 40); + optr = grub_stpcpy (grub_dev, "lvmid/"); + for (i = 0; i < ARRAY_SIZE (dashes) - 1; i++) + { + memcpy (optr, uuid + sizeof ("LVM-") - 1 + dashes[i], + dashes[i+1] - dashes[i]); + optr += dashes[i+1] - dashes[i]; + *optr++ = '-'; + } + optr = stpcpy (optr, uuid + sizeof ("LVM-") - 1 + dashes[i]); + *optr = '\0'; + grub_dev[sizeof("lvmid/xxxxxx-xxxx-xxxx-xxxx-xxxx-xxxx-xxxxxx") - 1] + = '/'; + free (uuid); + return grub_dev; + } + + if (strncmp (uuid, "CRYPT-LUKS1-", sizeof ("CRYPT-LUKS1-") - 1) == 0) + { + char *dash; + dash = grub_strchr (uuid + sizeof ("CRYPT-LUKS1-") - 1, '-'); + if (dash) + *dash = 0; + grub_dev = grub_xasprintf ("cryptouuid/%s", + uuid + sizeof ("CRYPT-LUKS1-") - 1); + grub_free (uuid); + return grub_dev; + } + return NULL; +} + +char * +grub_util_get_vg_uuid (const char *os_dev) +{ + char *uuid, *vgid; + int dashes[] = { 0, 6, 10, 14, 18, 22, 26, 32}; + unsigned i; + char *optr; + + uuid = get_dm_uuid (os_dev); + if (!uuid) + return NULL; + + vgid = xmalloc (grub_strlen (uuid)); + optr = vgid; + for (i = 0; i < ARRAY_SIZE (dashes) - 1; i++) + { + memcpy (optr, uuid + sizeof ("LVM-") - 1 + dashes[i], + dashes[i+1] - dashes[i]); + optr += dashes[i+1] - dashes[i]; + *optr++ = '-'; + } + optr--; + *optr = '\0'; + return vgid; +} + +void +grub_util_devmapper_cleanup (void) +{ + dm_lib_release (); +} + +#else +void +grub_util_pull_devmapper (const char *os_dev __attribute__ ((unused))) +{ + return; +} + +int +grub_util_device_is_mapped_stat (struct stat *st __attribute__ ((unused))) +{ + return 0; +} + +void +grub_util_devmapper_cleanup (void) +{ +} + +enum grub_dev_abstraction_types +grub_util_get_dm_abstraction (const char *os_dev __attribute__ ((unused))) +{ + return GRUB_DEV_ABSTRACTION_NONE; +} + +char * +grub_util_get_vg_uuid (const char *os_dev __attribute__ ((unused))) +{ + return NULL; +} + +char * +grub_util_devmapper_part_to_disk (struct stat *st __attribute__ ((unused)), + int *is_part __attribute__ ((unused)), + const char *os_dev __attribute__ ((unused))) +{ + return NULL; +} + +char * +grub_util_get_devmapper_grub_dev (const char *os_dev __attribute__ ((unused))) +{ + return NULL; +} + +#endif diff --git a/util/getroot_freebsd.c b/util/getroot_freebsd.c new file mode 100644 index 000000000..024743300 --- /dev/null +++ b/util/getroot_freebsd.c @@ -0,0 +1,355 @@ +/* + * GRUB -- GRand Unified Bootloader + * Copyright (C) 1999,2000,2001,2002,2003,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 +#ifdef HAVE_LIMITS_H +#include +#endif + +#include +# include +# include +# include /* DIOCGMEDIASIZE */ +# include +# include +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include + +static const char * +grub_util_get_geom_abstraction (const char *dev) +{ + char *whole; + struct gmesh mesh; + struct gclass *class; + const char *name; + int err; + + if (strncmp (dev, "/dev/", sizeof ("/dev/") - 1) != 0) + return 0; + name = dev + sizeof ("/dev/") - 1; + grub_util_follow_gpart_up (name, NULL, &whole); + + grub_util_info ("following geom '%s'", name); + + err = geom_gettree (&mesh); + if (err != 0) + /* TRANSLATORS: geom is the name of (k)FreeBSD device framework. + Usually left untranslated. + */ + grub_util_error ("%s", _("couldn't open geom")); + + LIST_FOREACH (class, &mesh.lg_class, lg_class) + { + struct ggeom *geom; + LIST_FOREACH (geom, &class->lg_geom, lg_geom) + { + struct gprovider *provider; + LIST_FOREACH (provider, &geom->lg_provider, lg_provider) + if (strcmp (provider->lg_name, name) == 0) + return class->lg_name; + } + } + return NULL; +} + +enum grub_dev_abstraction_types +grub_util_get_dev_abstraction_os (const char *os_dev) +{ + const char *abstrac; + abstrac = grub_util_get_geom_abstraction (os_dev); + grub_util_info ("abstraction of %s is %s", os_dev, abstrac); + if (abstrac && grub_strcasecmp (abstrac, "eli") == 0) + return GRUB_DEV_ABSTRACTION_GELI; + + /* Check for LVM. */ + if (!strncmp (os_dev, LVM_DEV_MAPPER_STRING, sizeof(LVM_DEV_MAPPER_STRING)-1)) + return GRUB_DEV_ABSTRACTION_LVM; + return GRUB_DEV_ABSTRACTION_NONE; +} + +char * +grub_util_part_to_disk (const char *os_dev, struct stat *st, + int *is_part) +{ + char *out, *out2; + if (strncmp (os_dev, "/dev/", sizeof ("/dev/") - 1) != 0) + return xstrdup (os_dev); + grub_util_follow_gpart_up (os_dev + sizeof ("/dev/") - 1, NULL, &out); + + if (grub_strcmp (os_dev + sizeof ("/dev/") - 1, out) != 0) + *is_part = 1; + out2 = xasprintf ("/dev/%s", out); + free (out); + + return out2; +} + +int +grub_util_pull_device_os (const char *os_dev, + enum grub_dev_abstraction_types ab) +{ + switch (ab) + { + case GRUB_DEV_ABSTRACTION_GELI: + { + char *whole; + struct gmesh mesh; + struct gclass *class; + const char *name; + int err; + char *lastsubdev = NULL; + + if (strncmp (os_dev, "/dev/", sizeof ("/dev/") - 1) != 0) + return 1; + name = os_dev + sizeof ("/dev/") - 1; + grub_util_follow_gpart_up (name, NULL, &whole); + + grub_util_info ("following geom '%s'", name); + + err = geom_gettree (&mesh); + if (err != 0) + /* TRANSLATORS: geom is the name of (k)FreeBSD device framework. + Usually left untranslated. + */ + grub_util_error ("%s", _("couldn't open geom")); + + LIST_FOREACH (class, &mesh.lg_class, lg_class) + { + struct ggeom *geom; + LIST_FOREACH (geom, &class->lg_geom, lg_geom) + { + struct gprovider *provider; + LIST_FOREACH (provider, &geom->lg_provider, lg_provider) + if (strcmp (provider->lg_name, name) == 0) + { + struct gconsumer *consumer; + char *fname; + + LIST_FOREACH (consumer, &geom->lg_consumer, lg_consumer) + break; + if (!consumer) + grub_util_error ("%s", + _("couldn't find geli consumer")); + fname = xasprintf ("/dev/%s", consumer->lg_provider->lg_name); + grub_util_info ("consumer %s", consumer->lg_provider->lg_name); + lastsubdev = consumer->lg_provider->lg_name; + grub_util_pull_device (fname); + free (fname); + } + } + } + if (ab == GRUB_DEV_ABSTRACTION_GELI && lastsubdev) + { + char *fname = xasprintf ("/dev/%s", lastsubdev); + char *grdev = grub_util_get_grub_dev (fname); + free (fname); + + if (grdev) + { + grub_err_t gr_err; + gr_err = grub_cryptodisk_cheat_mount (grdev, os_dev); + if (gr_err) + grub_util_error (_("can't mount encrypted volume `%s': %s"), + lastsubdev, grub_errmsg); + } + + grub_free (grdev); + } + } + return 1; + default: + return 0; + } +} + +char * +grub_util_get_grub_dev_os (const char *os_dev) +{ + char *grub_dev = NULL; + + switch (grub_util_get_dev_abstraction (os_dev)) + { + /* Fallback for non-devmapper build. In devmapper-builds LVM is handled + in rub_util_get_devmapper_grub_dev and this point isn't reached. + */ + case GRUB_DEV_ABSTRACTION_LVM: + { + unsigned short len; + grub_size_t offset = sizeof (LVM_DEV_MAPPER_STRING) - 1; + + len = strlen (os_dev) - offset + 1; + grub_dev = xmalloc (len + sizeof ("lvm/")); + + grub_memcpy (grub_dev, "lvm/", sizeof ("lvm/") - 1); + grub_memcpy (grub_dev + sizeof ("lvm/") - 1, os_dev + offset, len); + } + break; + + case GRUB_DEV_ABSTRACTION_GELI: + { + char *whole; + struct gmesh mesh; + struct gclass *class; + const char *name; + int err; + + if (strncmp (os_dev, "/dev/", sizeof ("/dev/") - 1) != 0) + return 0; + name = os_dev + sizeof ("/dev/") - 1; + grub_util_follow_gpart_up (name, NULL, &whole); + + grub_util_info ("following geom '%s'", name); + + err = geom_gettree (&mesh); + if (err != 0) + /* TRANSLATORS: geom is the name of (k)FreeBSD device framework. + Usually left untranslated. + */ + grub_util_error ("%s", _("couldn't open geom")); + + LIST_FOREACH (class, &mesh.lg_class, lg_class) + { + struct ggeom *geom; + LIST_FOREACH (geom, &class->lg_geom, lg_geom) + { + struct gprovider *provider; + LIST_FOREACH (provider, &geom->lg_provider, lg_provider) + if (strcmp (provider->lg_name, name) == 0) + { + struct gconsumer *consumer; + char *fname; + char *uuid; + + LIST_FOREACH (consumer, &geom->lg_consumer, lg_consumer) + break; + if (!consumer) + grub_util_error ("%s", + _("couldn't find geli consumer")); + fname = xasprintf ("/dev/%s", consumer->lg_provider->lg_name); + uuid = grub_util_get_geli_uuid (fname); + if (!uuid) + grub_util_error ("%s", + _("couldn't retrieve geli UUID")); + grub_dev = xasprintf ("cryptouuid/%s", uuid); + free (fname); + free (uuid); + } + } + } + } + break; + + default: + break; + } + + return grub_dev; +} + +/* FIXME: geom actually gives us the whole container hierarchy. + It can be used more efficiently than this. */ +void +grub_util_follow_gpart_up (const char *name, grub_disk_addr_t *off_out, char **name_out) +{ + struct gmesh mesh; + struct gclass *class; + int err; + struct ggeom *geom; + + grub_util_info ("following geom '%s'", name); + + err = geom_gettree (&mesh); + if (err != 0) + /* TRANSLATORS: geom is the name of (k)FreeBSD device framework. + Usually left untranslated. + */ + grub_util_error ("%s", _("couldn't open geom")); + + LIST_FOREACH (class, &mesh.lg_class, lg_class) + if (strcasecmp (class->lg_name, "part") == 0) + break; + if (!class) + /* TRANSLATORS: geom is the name of (k)FreeBSD device framework. + Usually left untranslated. "part" is the identifier of one of its + classes. */ + grub_util_error ("%s", _("couldn't find geom `part' class")); + + LIST_FOREACH (geom, &class->lg_geom, lg_geom) + { + struct gprovider *provider; + LIST_FOREACH (provider, &geom->lg_provider, lg_provider) + if (strcmp (provider->lg_name, name) == 0) + { + char *name_tmp = xstrdup (geom->lg_name); + grub_disk_addr_t off = 0; + struct gconfig *config; + grub_util_info ("geom '%s' has parent '%s'", name, geom->lg_name); + + grub_util_follow_gpart_up (name_tmp, &off, name_out); + free (name_tmp); + LIST_FOREACH (config, &provider->lg_config, lg_config) + if (strcasecmp (config->lg_name, "start") == 0) + off += strtoull (config->lg_val, 0, 10); + if (off_out) + *off_out = off; + return; + } + } + grub_util_info ("geom '%s' has no parent", name); + if (name_out) + *name_out = xstrdup (name); + if (off_out) + *off_out = 0; +} + +grub_disk_addr_t +grub_util_find_partition_start_os (const char *dev) +{ + grub_disk_addr_t out; + if (strncmp (dev, "/dev/", sizeof ("/dev/") - 1) != 0) + return 0; + grub_util_follow_gpart_up (dev + sizeof ("/dev/") - 1, &out, NULL); + + return out; +} diff --git a/util/getroot_hurd.c b/util/getroot_hurd.c new file mode 100644 index 000000000..f63f4de74 --- /dev/null +++ b/util/getroot_hurd.c @@ -0,0 +1,221 @@ +/* + * GRUB -- GRand Unified Bootloader + * Copyright (C) 1999,2000,2001,2002,2003,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 +#ifdef HAVE_LIMITS_H +#include +#endif + +#include + +#include +#include +#include +#include +#include +#include +#include + +#include + +#include +#include +#include +#include + + +char * +grub_util_find_hurd_root_device (const char *path) +{ + file_t file; + error_t err; + char *argz = NULL, *name = NULL, *ret; + size_t argz_len = 0; + int i; + + file = file_name_lookup (path, 0, 0); + if (file == MACH_PORT_NULL) + /* TRANSLATORS: The first %s is the file being looked at, the second %s is + the error message. */ + grub_util_error (_("cannot open `%s': %s"), path, strerror (errno)); + + /* This returns catenated 0-terminated strings. */ + err = file_get_fs_options (file, &argz, &argz_len); + if (err) + /* TRANSLATORS: On GNU/Hurd, a "translator" is similar to a filesystem + mount, but handled by a userland daemon, whose invocation command line + is being fetched here. First %s is the file being looked at (for which + we are fetching the "translator" command line), second %s is the error + message. + */ + grub_util_error (_("cannot get translator command line " + "for path `%s': %s"), path, strerror(err)); + if (argz_len == 0) + grub_util_error (_("translator command line is empty for path `%s'"), path); + + /* Make sure the string is terminated. */ + argz[argz_len-1] = 0; + + /* Skip first word (translator path) and options. */ + for (i = strlen (argz) + 1; i < argz_len; i += strlen (argz + i) + 1) + { + if (argz[i] != '-') + { + /* Non-option. Only accept one, assumed to be the FS path. */ + /* XXX: this should be replaced by an RPC to the translator. */ + if (name) + /* TRANSLATORS: we expect to get something like + /hurd/foobar --option1 --option2=baz /dev/something + */ + grub_util_error (_("translator `%s' for path `%s' has several " + "non-option words, at least `%s' and `%s'"), + argz, path, name, argz + i); + name = argz + i; + } + } + + if (!name) + /* TRANSLATORS: we expect to get something like + /hurd/foobar --option1 --option2=baz /dev/something + */ + grub_util_error (_("translator `%s' for path `%s' is given only options, " + "cannot find device part"), argz, path); + + if (strncmp (name, "device:", sizeof ("device:") - 1) == 0) + { + char *dev_name = name + sizeof ("device:") - 1; + size_t size = sizeof ("/dev/") - 1 + strlen (dev_name) + 1; + char *next; + ret = malloc (size); + next = stpncpy (ret, "/dev/", size); + stpncpy (next, dev_name, size - (next - ret)); + } + else if (!strncmp (name, "file:", sizeof ("file:") - 1)) + ret = strdup (name + sizeof ("file:") - 1); + else + ret = strdup (name); + + munmap (argz, argz_len); + return ret; +} + +static int +is_fulldisk (const char *child, const char *parent) +{ + if (strcmp (parent, child) == 0) + return 1; + if (strncmp (parent, "/dev/", sizeof ("/dev/") - 1) == 0 + && child[0] !=0 && strcmp (parent + sizeof ("/dev/") - 1, child) == 0) + return 1; + if (strncmp (child, "/dev/", sizeof ("/dev/") - 1) == 0 + && parent[0] != 0 && strcmp (child + sizeof ("/dev/") - 1, parent) == 0) + return 1; + return 0; +} + +char * +grub_util_part_to_disk (const char *os_dev, + struct stat *st __attribute__ ((unused)), + int *is_part) +{ + char *path; + grub_disk_addr_t offset; + char *p; + + if (!grub_util_hurd_get_disk_info (os_dev, NULL, &offset, NULL, &path)) + return xstrdup (os_dev); + + /* Some versions of Hurd use badly glued Linux code to handle partitions + resulting in partitions being promoted to disks. */ + if (path && !(offset == 0 && is_fulldisk (path, os_dev) + && (strncmp ("/dev/sd", os_dev, 7) == 0 + || strncmp ("/dev/hd", os_dev, 7) == 0))) + { + *is_part = !is_fulldisk (path, os_dev); + if (path[0] != '/') + { + char *n = xasprintf ("/dev/%s", path); + free (path); + path = n; + } + return path; + } + free (path); + + path = xstrdup (os_dev); + + p = strchr (path + 7, 's'); + if (p) + { + *is_part = 1; + *p = '\0'; + } + return path; +} + +enum grub_dev_abstraction_types +grub_util_get_dev_abstraction_os (const char *os_dev __attribute__((unused))) +{ + return GRUB_DEV_ABSTRACTION_NONE; +} + +int +grub_util_pull_device_os (const char *os_dev __attribute__ ((unused)), + enum grub_dev_abstraction_types ab __attribute__ ((unused))) +{ + return 0; +} + +char * +grub_util_get_grub_dev_os (const char *os_dev __attribute__ ((unused))) +{ + return NULL; +} + +grub_disk_addr_t +grub_util_find_partition_start_os (const char *dev) +{ + grub_uint32_t secsize; + grub_disk_addr_t offset; + char *path; + if (!grub_util_hurd_get_disk_info (dev, &secsize, &offset, NULL, &path)) + return 0; + if (path && !(offset == 0 && is_fulldisk (path, dev) + && (strncmp ("/dev/sd", dev, 7) == 0 + || strncmp ("/dev/hd", dev, 7) == 0))) + { + free (path); + return (secsize / 512) * offset; + } + free (path); + return -1; +} diff --git a/util/getroot_linux.c b/util/getroot_linux.c new file mode 100644 index 000000000..13b78e5a3 --- /dev/null +++ b/util/getroot_linux.c @@ -0,0 +1,845 @@ +/* + * GRUB -- GRand Unified Bootloader + * Copyright (C) 1999,2000,2001,2002,2003,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 +#ifdef HAVE_LIMITS_H +#include +#endif + +#include +#include /* ioctl */ +#include + +#include +#include +#include +#include +#include +#include +#include + +#include + + +/* Defines taken from btrfs/ioctl.h. */ + +struct btrfs_ioctl_dev_info_args +{ + grub_uint64_t devid; + grub_uint8_t uuid[16]; + grub_uint64_t bytes_used; + grub_uint64_t total_bytes; + grub_uint64_t unused[379]; + grub_uint8_t path[1024]; +}; + +struct btrfs_ioctl_fs_info_args +{ + grub_uint64_t max_id; + grub_uint64_t num_devices; + grub_uint8_t fsid[16]; + grub_uint64_t reserved[124]; +}; + +#define BTRFS_IOC_DEV_INFO _IOWR(0x94, 30, \ + struct btrfs_ioctl_dev_info_args) +#define BTRFS_IOC_FS_INFO _IOR(0x94, 31, \ + struct btrfs_ioctl_fs_info_args) + +static int +grub_util_is_imsm (const char *os_dev); + + +#define ESCAPED_PATH_MAX (4 * PATH_MAX) +struct mountinfo_entry +{ + int id; + int major, minor; + char enc_root[ESCAPED_PATH_MAX + 1], enc_path[ESCAPED_PATH_MAX + 1]; + char fstype[ESCAPED_PATH_MAX + 1], device[ESCAPED_PATH_MAX + 1]; +}; + +/* Statting something on a btrfs filesystem always returns a virtual device + major/minor pair rather than the real underlying device, because btrfs + can span multiple underlying devices (and even if it's currently only + using a single device it can be dynamically extended onto another). We + can't deal with the multiple-device case yet, but in the meantime, we can + at least cope with the single-device case by scanning + /proc/self/mountinfo. */ +static void +unescape (char *str) +{ + char *optr; + const char *iptr; + for (iptr = optr = str; *iptr; optr++) + { + if (iptr[0] == '\\' && iptr[1] >= '0' && iptr[1] < '8' + && iptr[2] >= '0' && iptr[2] < '8' + && iptr[3] >= '0' && iptr[3] < '8') + { + *optr = (((iptr[1] - '0') << 6) | ((iptr[2] - '0') << 3) + | (iptr[3] - '0')); + iptr += 4; + } + else + *optr = *iptr++; + } + *optr = 0; +} + +static char ** +grub_find_root_devices_from_btrfs (const char *dir) +{ + int fd; + struct btrfs_ioctl_fs_info_args fsi; + int i, j = 0; + char **ret; + + fd = open (dir, 0); + if (!fd) + return NULL; + + if (ioctl (fd, BTRFS_IOC_FS_INFO, &fsi) < 0) + { + close (fd); + return NULL; + } + + ret = xmalloc ((fsi.num_devices + 1) * sizeof (ret[0])); + + for (i = 1; i <= fsi.max_id && j < fsi.num_devices; i++) + { + struct btrfs_ioctl_dev_info_args devi; + memset (&devi, 0, sizeof (devi)); + devi.devid = i; + if (ioctl (fd, BTRFS_IOC_DEV_INFO, &devi) < 0) + { + close (fd); + free (ret); + return NULL; + } + ret[j++] = xstrdup ((char *) devi.path); + if (j >= fsi.num_devices) + break; + } + close (fd); + ret[j] = 0; + return ret; +} + +char ** +grub_find_root_devices_from_mountinfo (const char *dir, char **relroot) +{ + FILE *fp; + char *buf = NULL; + size_t len = 0; + grub_size_t entry_len = 0, entry_max = 4; + struct mountinfo_entry *entries; + struct mountinfo_entry parent_entry = { 0, 0, 0, "", "", "", "" }; + int i; + + if (! *dir) + dir = "/"; + if (relroot) + *relroot = NULL; + + fp = fopen ("/proc/self/mountinfo", "r"); + if (! fp) + return NULL; /* fall through to other methods */ + + entries = xmalloc (entry_max * sizeof (*entries)); + + /* First, build a list of relevant visible mounts. */ + while (getline (&buf, &len, fp) > 0) + { + struct mountinfo_entry entry; + int count; + size_t enc_path_len; + const char *sep; + + if (sscanf (buf, "%d %d %u:%u %s %s%n", + &entry.id, &parent_entry.id, &entry.major, &entry.minor, + entry.enc_root, entry.enc_path, &count) < 6) + continue; + + unescape (entry.enc_root); + unescape (entry.enc_path); + + enc_path_len = strlen (entry.enc_path); + /* Check that enc_path is a prefix of dir. The prefix must either be + the entire string, or end with a slash, or be immediately followed + by a slash. */ + if (strncmp (dir, entry.enc_path, enc_path_len) != 0 || + (enc_path_len && dir[enc_path_len - 1] != '/' && + dir[enc_path_len] && dir[enc_path_len] != '/')) + continue; + + sep = strstr (buf + count, " - "); + if (!sep) + continue; + + sep += sizeof (" - ") - 1; + if (sscanf (sep, "%s %s", entry.fstype, entry.device) != 2) + continue; + + unescape (entry.device); + + /* Using the mount IDs, find out where this fits in the list of + visible mount entries we've seen so far. There are three + interesting cases. Firstly, it may be inserted at the end: this is + the usual case of /foo/bar being mounted after /foo. Secondly, it + may be inserted at the start: for example, this can happen for + filesystems that are mounted before / and later moved under it. + Thirdly, it may occlude part or all of the existing filesystem + tree, in which case the end of the list needs to be pruned and this + new entry will be inserted at the end. */ + if (entry_len >= entry_max) + { + entry_max <<= 1; + entries = xrealloc (entries, entry_max * sizeof (*entries)); + } + + if (!entry_len) + { + /* Initialise list. */ + entry_len = 2; + entries[0] = parent_entry; + entries[1] = entry; + } + else + { + for (i = entry_len - 1; i >= 0; i--) + { + if (entries[i].id == parent_entry.id) + { + /* Insert at end, pruning anything previously above this. */ + entry_len = i + 2; + entries[i + 1] = entry; + break; + } + else if (i == 0 && entries[i].id == entry.id) + { + /* Insert at start. */ + entry_len++; + memmove (entries + 1, entries, + (entry_len - 1) * sizeof (*entries)); + entries[0] = parent_entry; + entries[1] = entry; + break; + } + } + } + } + + /* Now scan visible mounts for the ones we're interested in. */ + for (i = entry_len - 1; i >= 0; i--) + { + char **ret = NULL; + if (!*entries[i].device) + continue; + + if (grub_strcmp (entries[i].fstype, "fuse.zfs") == 0 + || grub_strcmp (entries[i].fstype, "zfs") == 0) + { + char *slash; + slash = strchr (entries[i].device, '/'); + if (slash) + *slash = 0; + ret = grub_util_find_root_devices_from_poolname (entries[i].device); + if (slash) + *slash = '/'; + if (relroot) + { + if (!slash) + *relroot = xasprintf ("/@%s", entries[i].enc_root); + else if (strchr (slash + 1, '@')) + *relroot = xasprintf ("/%s%s", slash + 1, entries[i].enc_root); + else + *relroot = xasprintf ("/%s@%s", slash + 1, entries[i].enc_root); + } + } + else if (grub_strcmp (entries[i].fstype, "btrfs") == 0) + { + ret = grub_find_root_devices_from_btrfs (dir); + if (relroot) + { + char *ptr; + *relroot = xmalloc (strlen (entries[i].enc_root) + + 2 + strlen (dir)); + ptr = grub_stpcpy (*relroot, entries[i].enc_root); + if (strlen (dir) > strlen (entries[i].enc_path)) + { + while (ptr > *relroot && *(ptr - 1) == '/') + ptr--; + if (dir[strlen (entries[i].enc_path)] != '/') + *ptr++ = '/'; + ptr = grub_stpcpy (ptr, dir + strlen (entries[i].enc_path)); + } + *ptr = 0; + } + } + if (!ret) + { + ret = xmalloc (2 * sizeof (ret[0])); + ret[0] = strdup (entries[i].device); + ret[1] = 0; + if (relroot) + *relroot = strdup (entries[i].enc_root); + } + free (buf); + free (entries); + fclose (fp); + return ret; + } + + free (buf); + free (entries); + fclose (fp); + return NULL; +} + +static char * +get_mdadm_uuid (const char *os_dev) +{ + char *argv[5]; + int fd; + pid_t pid; + FILE *mdadm; + char *buf = NULL; + size_t len = 0; + char *name = NULL; + + /* execvp has inconvenient types, hence the casts. None of these + strings will actually be modified. */ + argv[0] = (char *) "mdadm"; + argv[1] = (char *) "--detail"; + argv[2] = (char *) "--export"; + argv[3] = (char *) os_dev; + argv[4] = NULL; + + pid = grub_util_exec_pipe (argv, &fd); + + if (!pid) + return NULL; + + /* Parent. Read mdadm's output. */ + mdadm = fdopen (fd, "r"); + if (! mdadm) + { + grub_util_warn (_("Unable to open stream from %s: %s"), + "mdadm", strerror (errno)); + goto out; + } + + while (getline (&buf, &len, mdadm) > 0) + { + if (strncmp (buf, "MD_UUID=", sizeof ("MD_UUID=") - 1) == 0) + { + char *name_start, *ptri, *ptro; + + free (name); + name_start = buf + sizeof ("MD_UUID=") - 1; + ptro = name = xmalloc (strlen (name_start) + 1); + for (ptri = name_start; *ptri && *ptri != '\n' && *ptri != '\r'; + ptri++) + if ((*ptri >= '0' && *ptri <= '9') + || (*ptri >= 'a' && *ptri <= 'f') + || (*ptri >= 'A' && *ptri <= 'F')) + *ptro++ = *ptri; + *ptro = 0; + } + } + +out: + close (fd); + waitpid (pid, NULL, 0); + free (buf); + + return name; +} + +static int +grub_util_is_imsm (const char *os_dev) +{ + int retry; + int is_imsm = 0; + int container_seen = 0; + const char *dev = os_dev; + + do + { + char *argv[5]; + int fd; + pid_t pid; + FILE *mdadm; + char *buf = NULL; + size_t len = 0; + + retry = 0; /* We'll do one more pass if device is part of container */ + + /* execvp has inconvenient types, hence the casts. None of these + strings will actually be modified. */ + argv[0] = (char *) "mdadm"; + argv[1] = (char *) "--detail"; + argv[2] = (char *) "--export"; + argv[3] = (char *) dev; + argv[4] = NULL; + + pid = grub_util_exec_pipe (argv, &fd); + + if (!pid) + { + if (dev != os_dev) + free ((void *) dev); + return 0; + } + + /* Parent. Read mdadm's output. */ + mdadm = fdopen (fd, "r"); + if (! mdadm) + { + grub_util_warn (_("Unable to open stream from %s: %s"), + "mdadm", strerror (errno)); + close (fd); + waitpid (pid, NULL, 0); + if (dev != os_dev) + free ((void *) dev); + return 0; + } + + while (getline (&buf, &len, mdadm) > 0) + { + if (strncmp (buf, "MD_CONTAINER=", sizeof ("MD_CONTAINER=") - 1) == 0 + && !container_seen) + { + char *newdev, *ptr; + newdev = xstrdup (buf + sizeof ("MD_CONTAINER=") - 1); + ptr = newdev + strlen (newdev) - 1; + for (; ptr >= newdev && (*ptr == '\n' || *ptr == '\r'); ptr--); + ptr[1] = 0; + grub_util_info ("Container of %s is %s", dev, newdev); + dev = newdev; + container_seen = retry = 1; + break; + } + if (strncmp (buf, "MD_METADATA=imsm", + sizeof ("MD_METADATA=imsm") - 1) == 0) + { + is_imsm = 1; + grub_util_info ("%s is imsm", dev); + break; + } + } + + free (buf); + close (fd); + waitpid (pid, NULL, 0); + } + while (retry); + + if (dev != os_dev) + free ((void *) dev); + return is_imsm; +} + +char * +grub_util_part_to_disk (const char *os_dev, struct stat *st, + int *is_part) +{ + char *path = xmalloc (PATH_MAX); + + if (! realpath (os_dev, path)) + return NULL; + + if (strncmp ("/dev/", path, 5) == 0) + { + char *p = path + 5; + + /* If this is an IDE disk. */ + if (strncmp ("ide/", p, 4) == 0) + { + p = strstr (p, "part"); + if (p) + { + *is_part = 1; + strcpy (p, "disc"); + } + + return path; + } + + /* If this is a SCSI disk. */ + if (strncmp ("scsi/", p, 5) == 0) + { + p = strstr (p, "part"); + if (p) + { + *is_part = 1; + strcpy (p, "disc"); + } + + return path; + } + + /* If this is a DAC960 disk. */ + if (strncmp ("rd/c", p, 4) == 0) + { + /* /dev/rd/c[0-9]+d[0-9]+(p[0-9]+)? */ + p = strchr (p, 'p'); + if (p) + { + *is_part = 1; + *p = '\0'; + } + + return path; + } + + /* If this is a Mylex AcceleRAID Array. */ + if (strncmp ("rs/c", p, 4) == 0) + { + /* /dev/rd/c[0-9]+d[0-9]+(p[0-9]+)? */ + p = strchr (p, 'p'); + if (p) + { + *is_part = 1; + *p = '\0'; + } + + return path; + } + /* If this is a CCISS disk. */ + if (strncmp ("cciss/c", p, sizeof ("cciss/c") - 1) == 0) + { + /* /dev/cciss/c[0-9]+d[0-9]+(p[0-9]+)? */ + p = strchr (p, 'p'); + if (p) + { + *is_part = 1; + *p = '\0'; + } + + return path; + } + + /* If this is an AOE disk. */ + if (strncmp ("etherd/e", p, sizeof ("etherd/e") - 1) == 0) + { + /* /dev/etherd/e[0-9]+\.[0-9]+(p[0-9]+)? */ + p = strchr (p, 'p'); + if (p) + { + *is_part = 1; + *p = '\0'; + } + + return path; + } + + /* If this is a Compaq Intelligent Drive Array. */ + if (strncmp ("ida/c", p, sizeof ("ida/c") - 1) == 0) + { + /* /dev/ida/c[0-9]+d[0-9]+(p[0-9]+)? */ + p = strchr (p, 'p'); + if (p) + { + *is_part = 1; + *p = '\0'; + } + + return path; + } + + /* If this is an I2O disk. */ + if (strncmp ("i2o/hd", p, sizeof ("i2o/hd") - 1) == 0) + { + /* /dev/i2o/hd[a-z]([0-9]+)? */ + if (p[sizeof ("i2o/hda") - 1]) + *is_part = 1; + p[sizeof ("i2o/hda") - 1] = '\0'; + return path; + } + + /* If this is a MultiMediaCard (MMC). */ + if (strncmp ("mmcblk", p, sizeof ("mmcblk") - 1) == 0) + { + /* /dev/mmcblk[0-9]+(p[0-9]+)? */ + p = strchr (p, 'p'); + if (p) + { + *is_part = 1; + *p = '\0'; + } + + return path; + } + + if (strncmp ("md", p, 2) == 0 + && p[2] >= '0' && p[2] <= '9') + { + char *ptr = p + 2; + while (*ptr >= '0' && *ptr <= '9') + ptr++; + if (*ptr) + *is_part = 1; + *ptr = 0; + return path; + } + + if (strncmp ("nbd", p, 3) == 0 + && p[3] >= '0' && p[3] <= '9') + { + char *ptr = p + 3; + while (*ptr >= '0' && *ptr <= '9') + ptr++; + if (*ptr) + *is_part = 1; + *ptr = 0; + return path; + } + + /* If this is an IDE, SCSI or Virtio disk. */ + if (strncmp ("vdisk", p, 5) == 0 + && p[5] >= 'a' && p[5] <= 'z') + { + /* /dev/vdisk[a-z][0-9]* */ + if (p[6]) + *is_part = 1; + p[6] = '\0'; + return path; + } + if ((strncmp ("hd", p, 2) == 0 + || strncmp ("vd", p, 2) == 0 + || strncmp ("sd", p, 2) == 0) + && p[2] >= 'a' && p[2] <= 'z') + { + char *pp = p + 2; + while (*pp >= 'a' && *pp <= 'z') + pp++; + if (*pp) + *is_part = 1; + /* /dev/[hsv]d[a-z]+[0-9]* */ + *pp = '\0'; + return path; + } + + /* If this is a Xen virtual block device. */ + if ((strncmp ("xvd", p, 3) == 0) && p[3] >= 'a' && p[3] <= 'z') + { + char *pp = p + 3; + while (*pp >= 'a' && *pp <= 'z') + pp++; + if (*pp) + *is_part = 1; + /* /dev/xvd[a-z]+[0-9]* */ + *pp = '\0'; + return path; + } + } + + return path; +} + +char * +grub_util_get_raid_grub_dev (const char *os_dev) +{ + char *grub_dev = NULL; + if (os_dev[7] == '_' && os_dev[8] == 'd') + { + /* This a partitionable RAID device of the form /dev/md_dNNpMM. */ + + char *p, *q; + + p = strdup (os_dev + sizeof ("/dev/md_d") - 1); + + q = strchr (p, 'p'); + if (q) + *q = ','; + + grub_dev = xasprintf ("md%s", p); + free (p); + } + else if (os_dev[7] == '/' && os_dev[8] == 'd') + { + /* This a partitionable RAID device of the form /dev/md/dNNpMM. */ + + char *p, *q; + + p = strdup (os_dev + sizeof ("/dev/md/d") - 1); + + q = strchr (p, 'p'); + if (q) + *q = ','; + + grub_dev = xasprintf ("md%s", p); + free (p); + } + else if (os_dev[7] >= '0' && os_dev[7] <= '9') + { + char *p , *q; + + p = strdup (os_dev + sizeof ("/dev/md") - 1); + + q = strchr (p, 'p'); + if (q) + *q = ','; + + grub_dev = xasprintf ("md%s", p); + free (p); + } + else if (os_dev[7] == '/' && os_dev[8] >= '0' && os_dev[8] <= '9') + { + char *p , *q; + + p = strdup (os_dev + sizeof ("/dev/md/") - 1); + + q = strchr (p, 'p'); + if (q) + *q = ','; + + grub_dev = xasprintf ("md%s", p); + free (p); + } + else if (os_dev[7] == '/') + { + /* mdraid 1.x with a free name. */ + char *p , *q; + + p = strdup (os_dev + sizeof ("/dev/md/") - 1); + + q = strchr (p, 'p'); + if (q) + *q = ','; + + grub_dev = xasprintf ("md/%s", p); + free (p); + } + else + grub_util_error (_("unknown kind of RAID device `%s'"), os_dev); + + { + char *mdadm_name = get_mdadm_uuid (os_dev); + + if (mdadm_name) + { + const char *q; + + for (q = os_dev + strlen (os_dev) - 1; q >= os_dev + && grub_isdigit (*q); q--); + + if (q >= os_dev && *q == 'p') + { + free (grub_dev); + grub_dev = xasprintf ("mduuid/%s,%s", mdadm_name, q + 1); + goto done; + } + free (grub_dev); + grub_dev = xasprintf ("mduuid/%s", mdadm_name); + + done: + free (mdadm_name); + } + } + return grub_dev; +} + +enum grub_dev_abstraction_types +grub_util_get_dev_abstraction_os (const char *os_dev) +{ +#ifndef HAVE_DEVICE_MAPPER + if ((strncmp ("/dev/mapper/", os_dev, 12) == 0)) + return GRUB_DEV_ABSTRACTION_LVM; +#endif + + /* Check for RAID. */ + if (!strncmp (os_dev, "/dev/md", 7) && ! grub_util_device_is_mapped (os_dev) + && !grub_util_is_imsm (os_dev)) + return GRUB_DEV_ABSTRACTION_RAID; + return GRUB_DEV_ABSTRACTION_NONE; +} + +int +grub_util_pull_device_os (const char *os_dev, + enum grub_dev_abstraction_types ab) +{ + switch (ab) + { + case GRUB_DEV_ABSTRACTION_RAID: + { + char **devicelist = grub_util_raid_getmembers (os_dev, 0); + int i; + for (i = 0; devicelist[i];i++) + { + grub_util_pull_device (devicelist[i]); + free (devicelist[i]); + } + free (devicelist); + } + return 1; + default: + return 0; + } +} + +char * +grub_util_get_grub_dev_os (const char *os_dev) +{ + char *grub_dev = NULL; + + switch (grub_util_get_dev_abstraction (os_dev)) + { + /* Fallback for non-devmapper build. In devmapper-builds LVM is handled + in rub_util_get_devmapper_grub_dev and this point isn't reached. + */ + case GRUB_DEV_ABSTRACTION_LVM: + { + unsigned short len; + grub_size_t offset = sizeof (LVM_DEV_MAPPER_STRING) - 1; + + len = strlen (os_dev) - offset + 1; + grub_dev = xmalloc (len + sizeof ("lvm/")); + + grub_memcpy (grub_dev, "lvm/", sizeof ("lvm/") - 1); + grub_memcpy (grub_dev + sizeof ("lvm/") - 1, os_dev + offset, len); + } + break; + + case GRUB_DEV_ABSTRACTION_RAID: + grub_dev = grub_util_get_raid_grub_dev (os_dev); + break; + + default: /* GRUB_DEV_ABSTRACTION_NONE */ + break; + } + + return grub_dev; +} diff --git a/util/getroot_os.c b/util/getroot_os.c new file mode 100644 index 000000000..fee9f2100 --- /dev/null +++ b/util/getroot_os.c @@ -0,0 +1,18 @@ +#ifdef __linux__ +#include "getroot_linux.c" +#elif defined(__FreeBSD__) || defined(__FreeBSD_kernel__) +#include "getroot_freebsd.c" +#elif defined(__NetBSD__) || defined(__OpenBSD__) +#include "getroot_bsd.c" +#elif defined(__APPLE__) +#include "getroot_apple.c" +#elif defined(__sun__) +#include "getroot_sun.c" +#elif defined(__GNU__) +#include "getroot_hurd.c" +#elif defined(__CYGWIN__) +#include "getroot_cygwin.c" +#else +# warning "No getroot OS-specific functions is available for your system. Device detection may not work properly." +#include "getroot_basic.c" +#endif diff --git a/util/getroot_sun.c b/util/getroot_sun.c new file mode 100644 index 000000000..d63f5a69f --- /dev/null +++ b/util/getroot_sun.c @@ -0,0 +1,119 @@ +/* + * GRUB -- GRand Unified Bootloader + * Copyright (C) 1999,2000,2001,2002,2003,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 +#ifdef HAVE_LIMITS_H +#include +#endif + +#include + +#include +#include +#include +#include +#include +#include +#include + +#include + +# include +# include +# include + + +char * +grub_util_part_to_disk (const char *os_dev, + struct stat *st __attribute__ ((unused)), + int *is_part) +{ + char *colon = grub_strrchr (os_dev, ':'); + if (grub_memcmp (os_dev, "/devices", sizeof ("/devices") - 1) == 0 + && colon) + { + char *ret = xmalloc (colon - os_dev + sizeof (":q,raw")); + if (grub_strcmp (colon, ":q,raw") != 0) + *is_part = 1; + grub_memcpy (ret, os_dev, colon - os_dev); + grub_memcpy (ret + (colon - os_dev), ":q,raw", sizeof (":q,raw")); + return ret; + } + else + return xstrdup (os_dev); +} + +enum grub_dev_abstraction_types +grub_util_get_dev_abstraction_os (const char *os_dev __attribute__((unused))) +{ + return GRUB_DEV_ABSTRACTION_NONE; +} + +int +grub_util_pull_device_os (const char *os_dev __attribute__ ((unused)), + enum grub_dev_abstraction_types ab __attribute__ ((unused))) +{ + return 0; +} + +char * +grub_util_get_grub_dev_os (const char *os_dev __attribute__ ((unused))) +{ + return NULL; +} + +grub_disk_addr_t +grub_util_find_partition_start_os (const char *dev) +{ + int fd; + struct extpart_info pinfo; + + 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, DKIOCEXTPARTINFO, &pinfo)) + { + grub_error (GRUB_ERR_BAD_DEVICE, + "cannot get disk geometry of `%s'", dev); + close (fd); + return 0; + } + + close (fd); + + return pinfo.p_start; +}