diff --git a/ChangeLog b/ChangeLog index 2280fde58..9352cde14 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,7 @@ +2013-09-23 Vladimir Serbinenko + + Use Winapi on both cygwin and mingw32 to share more code between both. + 2013-09-22 Andrey Borzenkov * util/grub-install.in: Add --grub-editenv option. diff --git a/Makefile.util.def b/Makefile.util.def index 38ce370bf..c8c314dfd 100644 --- a/Makefile.util.def +++ b/Makefile.util.def @@ -10,12 +10,14 @@ library = { common = grub-core/kern/device.c; common = grub-core/kern/disk.c; common = util/getroot.c; + common = util/getroot_unix.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/hostdisk_unix.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 b666468e3..a32472556 100644 --- a/conf/Makefile.extra-dist +++ b/conf/Makefile.extra-dist @@ -65,17 +65,16 @@ 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_windows.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_windows.c EXTRA_DIST += util/getroot_freebsd.c EXTRA_DIST += util/getroot_hurd.c EXTRA_DIST += util/getroot_linux.c diff --git a/configure.ac b/configure.ac index e6ad049f1..01d11e8ee 100644 --- a/configure.ac +++ b/configure.ac @@ -182,7 +182,7 @@ case "$host_os" in netbsd*) host_kernel=netbsd ;; solaris*) host_kernel=illumos ;; darwin*) host_kernel=xnu ;; - cygwin) host_kernel=windows ;; + cygwin | windows* | mingw32*) host_kernel=windows ;; esac case "$platform" in @@ -333,6 +333,14 @@ AC_C_BIGENDIAN AC_CHECK_SIZEOF(void *) AC_CHECK_SIZEOF(long) +case "$host_os" in + cygwin | windows* | mingw32*) + HOST_CPPFLAGS="$HOST_CPPFLAGS -DUNICODE=1 -D_WIN32_WINNT=0x0500" + CPPFLAGS="$CPPFLAGS -DUNICODE=1 -D_WIN32_WINNT=0x0500" + AC_CHECK_SIZEOF(TCHAR,,[#include ]) + ;; +esac + grub_apple_cc if test x$grub_cv_apple_cc = xyes ; then HOST_CPPFLAGS="$HOST_CPPFLAGS -fnested-functions" diff --git a/grub-core/Makefile.core.def b/grub-core/Makefile.core.def index a4e6862bb..a7ff80e70 100644 --- a/grub-core/Makefile.core.def +++ b/grub-core/Makefile.core.def @@ -241,6 +241,7 @@ kernel = { emu = kern/emu/error.c; emu = kern/emu/cache_s.S; emu = kern/emu/hostdisk.c; + emu = kern/emu/hostdisk_unix.c; emu = kern/emu/hostdisk_devmapper.c; emu = kern/emu/hostdisk_os.c; emu = kern/emu/hostfs.c; diff --git a/grub-core/disk/geli.c b/grub-core/disk/geli.c index 2aa1ae08d..ce6bb3911 100644 --- a/grub-core/disk/geli.c +++ b/grub-core/disk/geli.c @@ -210,7 +210,7 @@ make_uuid (const struct grub_geli_phdr *header, char * grub_util_get_geli_uuid (const char *dev) { - int fd = open (dev, O_RDONLY); + grub_util_fd_t fd = grub_util_fd_open (dev, O_RDONLY); grub_uint64_t s; unsigned log_secsize; grub_uint8_t hdr[512]; @@ -218,7 +218,7 @@ grub_util_get_geli_uuid (const char *dev) char *uuid; gcry_err_code_t err; - if (fd < 0) + if (!GRUB_UTIL_FD_IS_VALID (fd)) return NULL; s = grub_util_get_fd_size (fd, dev, &log_secsize); diff --git a/grub-core/kern/emu/hostdisk.c b/grub-core/kern/emu/hostdisk.c index f1f757055..cbf50771f 100644 --- a/grub-core/kern/emu/hostdisk.c +++ b/grub-core/kern/emu/hostdisk.c @@ -46,11 +46,6 @@ #ifdef __linux__ # include /* ioctl */ # include -# if !defined(__GLIBC__) || \ - ((__GLIBC__ < 2) || ((__GLIBC__ == 2) && (__GLIBC_MINOR__ < 1))) -/* Maybe libc doesn't have large file support. */ -# include /* _llseek */ -# endif /* (GLIBC < 2) || ((__GLIBC__ == 2) && (__GLIBC_MINOR < 1)) */ # ifndef BLKFLSBUF # define BLKFLSBUF _IO (0x12,97) /* flush buffer cache */ # endif /* ! BLKFLSBUF */ @@ -68,15 +63,6 @@ static struct int device_map; } map[256]; -struct grub_util_biosdisk_data -{ - char *dev; - int access_mode; - int fd; - int is_disk; - int device_map; -}; - static int unescape_cmp (const char *a, const char *b_escaped) { @@ -141,81 +127,42 @@ 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, const char *name, - unsigned *log_secsize) -{ - return grub_util_get_fd_size_os (fd, name, log_secsize); -} - -#else - -grub_uint64_t -grub_util_get_fd_size (int fd, const char *name, unsigned *log_secsize) -{ - 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 GRUB_DISK_DEVS_ARE_CHAR - if (S_ISCHR (st.st_mode)) -#else - if (S_ISBLK (st.st_mode)) -#endif - ret = grub_util_get_fd_size_os (fd, name, log_secsize); - if (ret != -1LL) - return ret; - - if (log_secsize) - *log_secsize = 9; - - return st.st_size; -} - -#endif - static grub_err_t grub_util_biosdisk_open (const char *name, grub_disk_t disk) { int drive; struct stat st; - struct grub_util_biosdisk_data *data; + struct grub_util_hostdisk_data *data; drive = find_grub_drive (name); + grub_util_info ("drive = %d", drive); if (drive < 0) return grub_error (GRUB_ERR_UNKNOWN_DEVICE, "no mapping exists for `%s'", name); disk->id = drive; - disk->data = data = xmalloc (sizeof (struct grub_util_biosdisk_data)); + disk->data = data = xmalloc (sizeof (struct grub_util_hostdisk_data)); data->dev = NULL; data->access_mode = 0; - data->fd = -1; + data->fd = GRUB_UTIL_FD_INVALID; data->is_disk = 0; data->device_map = map[drive].device_map; /* Get the size. */ { - int fd; + grub_util_fd_t fd; -#if defined(__MINGW32__) - fd = -1; -#else - fd = open (map[drive].device, O_RDONLY); - if (fd == -1) + fd = grub_util_fd_open (map[drive].device, O_RDONLY); + + if (!GRUB_UTIL_FD_IS_VALID(fd)) return grub_error (GRUB_ERR_UNKNOWN_DEVICE, N_("cannot open `%s': %s"), map[drive].device, strerror (errno)); -#endif disk->total_sectors = grub_util_get_fd_size (fd, map[drive].device, &disk->log_sector_size); disk->total_sectors >>= disk->log_sector_size; -#if !defined(__MINGW32__) +#if !defined(__MINGW32__) && !defined(__CYGWIN__) # if GRUB_DISK_DEVS_ARE_CHAR if (fstat (fd, &st) < 0 || ! S_ISCHR (st.st_mode)) @@ -223,10 +170,10 @@ grub_util_biosdisk_open (const char *name, grub_disk_t disk) if (fstat (fd, &st) < 0 || ! S_ISBLK (st.st_mode)) # endif data->is_disk = 1; - - close (fd); #endif + grub_util_fd_close (fd); + grub_util_info ("the size of %s is %" PRIuGRUB_UINT64_T, name, disk->total_sectors); @@ -234,37 +181,6 @@ grub_util_biosdisk_open (const char *name, grub_disk_t disk) } } -#if defined(__linux__) && (!defined(__GLIBC__) || \ - ((__GLIBC__ < 2) || ((__GLIBC__ == 2) && (__GLIBC_MINOR__ < 1)))) - /* Maybe libc doesn't have large file support. */ -grub_err_t -grub_util_fd_seek (int fd, const char *name, grub_uint64_t off) -{ - loff_t offset, result; - static int _llseek (uint filedes, ulong hi, ulong lo, - loff_t *res, uint wh); - _syscall5 (int, _llseek, uint, filedes, ulong, hi, ulong, lo, - loff_t *, res, uint, wh); - - offset = (loff_t) off; - if (_llseek (fd, offset >> 32, offset & 0xffffffff, &result, SEEK_SET)) - return grub_error (GRUB_ERR_BAD_DEVICE, N_("cannot seek `%s': %s"), - name, strerror (errno)); - return GRUB_ERR_NONE; -} -#else -grub_err_t -grub_util_fd_seek (int fd, const char *name, grub_uint64_t off) -{ - off_t offset = (off_t) off; - - if (lseek (fd, offset, SEEK_SET) != offset) - return grub_error (GRUB_ERR_BAD_DEVICE, N_("cannot seek `%s': %s"), - name, strerror (errno)); - return 0; -} -#endif - const char * grub_hostdisk_os_dev_to_grub_drive (const char *os_disk, int add) { @@ -305,12 +221,13 @@ grub_hostdisk_os_dev_to_grub_drive (const char *os_disk, int add) return map[i].drive; } -static int -open_device (const grub_disk_t disk, grub_disk_addr_t sector, int flags, - grub_disk_addr_t *max) +#ifndef __linux__ +grub_util_fd_t +grub_util_fd_open_device (const grub_disk_t disk, grub_disk_addr_t sector, int flags, + grub_disk_addr_t *max) { - int fd; - struct grub_util_biosdisk_data *data = disk->data; + grub_util_fd_t fd; + struct grub_util_hostdisk_data *data = disk->data; *max = ~0ULL; @@ -327,87 +244,6 @@ open_device (const grub_disk_t disk, grub_disk_addr_t sector, int flags, flags |= O_BINARY; #endif -#ifdef __linux__ - /* Linux has a bug that the disk cache for a whole disk is not consistent - with the one for a partition of the disk. */ - { - int is_partition = 0; - char dev[PATH_MAX]; - grub_disk_addr_t part_start = 0; - - part_start = grub_partition_get_start (disk->partition); - - strcpy (dev, map[disk->id].device); - if (disk->partition - && strncmp (map[disk->id].device, "/dev/", 5) == 0) - { - if (sector >= part_start) - is_partition = grub_hostdisk_linux_find_partition (dev, part_start); - else - *max = part_start - sector; - } - - reopen: - - if (data->dev && strcmp (data->dev, dev) == 0 && - data->access_mode == (flags & O_ACCMODE)) - { - grub_dprintf ("hostdisk", "reusing open device `%s'\n", dev); - fd = data->fd; - } - else - { - free (data->dev); - data->dev = 0; - if (data->fd != -1) - { - if (data->access_mode == O_RDWR || data->access_mode == O_WRONLY) - { - fsync (data->fd); - if (data->is_disk) - ioctl (data->fd, BLKFLSBUF, 0); - } - - close (data->fd); - data->fd = -1; - } - - /* Open the partition. */ - grub_dprintf ("hostdisk", "opening the device `%s' in open_device()\n", dev); - fd = open (dev, flags); - if (fd < 0) - { - grub_error (GRUB_ERR_BAD_DEVICE, N_("cannot open `%s': %s"), - dev, strerror (errno)); - return -1; - } - - data->dev = xstrdup (dev); - data->access_mode = (flags & O_ACCMODE); - data->fd = fd; - - if (data->is_disk) - ioctl (data->fd, BLKFLSBUF, 0); - } - - if (is_partition) - { - *max = grub_util_get_fd_size (fd, dev, 0); - *max >>= disk->log_sector_size; - if (sector - part_start >= *max) - { - *max = disk->partition->len - (sector - part_start); - if (*max == 0) - *max = ~0ULL; - is_partition = 0; - strcpy (dev, map[disk->id].device); - goto reopen; - } - sector -= part_start; - *max -= sector; - } - } -#else /* ! __linux__ */ #if defined (__FreeBSD__) || defined(__FreeBSD_kernel__) int sysctl_flags, sysctl_oldflags; size_t sysctl_size = sizeof (sysctl_flags); @@ -415,14 +251,14 @@ open_device (const grub_disk_t disk, grub_disk_addr_t sector, int flags, if (sysctlbyname ("kern.geom.debugflags", &sysctl_oldflags, &sysctl_size, NULL, 0)) { grub_error (GRUB_ERR_BAD_DEVICE, "cannot get current flags of sysctl kern.geom.debugflags"); - return -1; + return GRUB_UTIL_FD_INVALID; } sysctl_flags = sysctl_oldflags | 0x10; if (! (sysctl_oldflags & 0x10) && sysctlbyname ("kern.geom.debugflags", NULL , 0, &sysctl_flags, sysctl_size)) { grub_error (GRUB_ERR_BAD_DEVICE, "cannot set flags of sysctl kern.geom.debugflags"); - return -1; + return GRUB_UTIL_FD_INVALID; } #endif @@ -436,18 +272,16 @@ open_device (const grub_disk_t disk, grub_disk_addr_t sector, int flags, { free (data->dev); data->dev = 0; - if (data->fd != -1) + if (GRUB_UTIL_FD_IS_VALID(data->fd)) { if (data->access_mode == O_RDWR || data->access_mode == O_WRONLY) - { - fsync (data->fd); - } - close (data->fd); - data->fd = -1; + grub_util_fd_sync (data->fd); + grub_util_fd_close (data->fd); + data->fd = GRUB_UTIL_FD_INVALID; } - fd = open (map[disk->id].device, flags); - if (fd >= 0) + fd = grub_util_fd_open (map[disk->id].device, flags); + if (GRUB_UTIL_FD_IS_VALID(fd)) { data->dev = xstrdup (map[disk->id].device); data->access_mode = (flags & O_ACCMODE); @@ -460,7 +294,7 @@ open_device (const grub_disk_t disk, grub_disk_addr_t sector, int flags, && sysctlbyname ("kern.geom.debugflags", NULL , 0, &sysctl_oldflags, sysctl_size)) { grub_error (GRUB_ERR_BAD_DEVICE, "cannot set flags back to the old value for sysctl kern.geom.debugflags"); - return -1; + return GRUB_UTIL_FD_INVALID; } #endif @@ -470,77 +304,26 @@ open_device (const grub_disk_t disk, grub_disk_addr_t sector, int flags, fd = open(map[disk->id].device, flags | O_SHLOCK); #endif - if (fd < 0) + if (!GRUB_UTIL_FD_IS_VALID(data->fd)) { grub_error (GRUB_ERR_BAD_DEVICE, N_("cannot open `%s': %s"), map[disk->id].device, strerror (errno)); - return -1; + return GRUB_UTIL_FD_INVALID; } -#endif /* ! __linux__ */ grub_hostdisk_configure_device_driver (fd); if (grub_util_fd_seek (fd, map[disk->id].device, sector << disk->log_sector_size)) { - close (fd); - return -1; + grub_util_fd_close (fd); + return GRUB_UTIL_FD_INVALID; } return fd; } +#endif -/* Read LEN bytes from FD in BUF. Return less than or equal to zero if an - error occurs, otherwise return LEN. */ -ssize_t -grub_util_fd_read (int fd, char *buf, size_t len) -{ - ssize_t size = len; - - while (len) - { - ssize_t ret = read (fd, buf, len); - - if (ret <= 0) - { - if (errno == EINTR) - continue; - else - return ret; - } - - len -= ret; - buf += ret; - } - - return size; -} - -/* Write LEN bytes from BUF to FD. Return less than or equal to zero if an - error occurs, otherwise return LEN. */ -ssize_t -grub_util_fd_write (int fd, const char *buf, size_t len) -{ - ssize_t size = len; - - while (len) - { - ssize_t ret = write (fd, buf, len); - - if (ret <= 0) - { - if (errno == EINTR) - continue; - else - return ret; - } - - len -= ret; - buf += ret; - } - - return size; -} static grub_err_t grub_util_biosdisk_read (grub_disk_t disk, grub_disk_addr_t sector, @@ -548,10 +331,10 @@ grub_util_biosdisk_read (grub_disk_t disk, grub_disk_addr_t sector, { while (size) { - int fd; + grub_util_fd_t fd; grub_disk_addr_t max = ~0ULL; - fd = open_device (disk, sector, O_RDONLY, &max); - if (fd < 0) + fd = grub_util_fd_open_device (disk, sector, O_RDONLY, &max); + if (!GRUB_UTIL_FD_IS_VALID (fd)) return grub_errno; #ifdef __linux__ @@ -583,10 +366,10 @@ grub_util_biosdisk_write (grub_disk_t disk, grub_disk_addr_t sector, { while (size) { - int fd; + grub_util_fd_t fd; grub_disk_addr_t max = ~0ULL; - fd = open_device (disk, sector, O_WRONLY, &max); - if (fd < 0) + fd = grub_util_fd_open_device (disk, sector, O_WRONLY, &max); + if (!GRUB_UTIL_FD_IS_VALID (fd)) return grub_errno; #ifdef __linux__ @@ -614,18 +397,18 @@ grub_util_biosdisk_write (grub_disk_t disk, grub_disk_addr_t sector, grub_err_t grub_util_biosdisk_flush (struct grub_disk *disk) { - struct grub_util_biosdisk_data *data = disk->data; + struct grub_util_hostdisk_data *data = disk->data; if (disk->dev->id != GRUB_DISK_DEVICE_BIOSDISK_ID) return GRUB_ERR_NONE; - if (data->fd == -1) + if (!GRUB_UTIL_FD_IS_VALID (data->fd)) { grub_disk_addr_t max; - data->fd = open_device (disk, 0, O_RDONLY, &max); - if (data->fd < 0) + data->fd = grub_util_fd_open_device (disk, 0, O_RDONLY, &max); + if (!GRUB_UTIL_FD_IS_VALID (data->fd)) return grub_errno; } - fsync (data->fd); + grub_util_fd_sync (data->fd); #ifdef __linux__ if (data->is_disk) ioctl (data->fd, BLKFLSBUF, 0); @@ -636,14 +419,14 @@ grub_util_biosdisk_flush (struct grub_disk *disk) static void grub_util_biosdisk_close (struct grub_disk *disk) { - struct grub_util_biosdisk_data *data = disk->data; + struct grub_util_hostdisk_data *data = disk->data; free (data->dev); - if (data->fd != -1) + if (!GRUB_UTIL_FD_IS_VALID (data->fd)) { if (data->access_mode == O_RDWR || data->access_mode == O_WRONLY) grub_util_biosdisk_flush (disk); - close (data->fd); + grub_util_fd_close (data->fd); } free (data); } @@ -660,13 +443,31 @@ static struct grub_disk_dev grub_util_biosdisk_dev = .next = 0 }; +static int +grub_util_check_file_presence (const char *p) +{ +#if defined (__MINGW32__) || defined(__CYGWIN__) + HANDLE h; + h = grub_util_fd_open (p, O_RDONLY); + if (!GRUB_UTIL_FD_IS_VALID(h)) + return 0; + CloseHandle (h); + return 1; +#else + struct stat st; + + if (stat (p, &st) == -1) + return 0; + return 1; +#endif +} + static void read_device_map (const char *dev_map) { FILE *fp; char buf[1024]; /* XXX */ int lineno = 0; - struct stat st; if (dev_map[0] == '\0') { @@ -770,12 +571,7 @@ read_device_map (const char *dev_map) e++; *e = '\0'; -#ifdef __MINGW32__ - (void) st; - if (grub_util_get_fd_size (-1, p, NULL) == -1LL) -#else - if (stat (p, &st) == -1) -#endif + if (!grub_util_check_file_presence (p)) { free (map[drive].drive); map[drive].drive = NULL; diff --git a/grub-core/kern/emu/hostdisk_apple.c b/grub-core/kern/emu/hostdisk_apple.c index 8ba785e2d..ac653bb6a 100644 --- a/grub-core/kern/emu/hostdisk_apple.c +++ b/grub-core/kern/emu/hostdisk_apple.c @@ -45,7 +45,7 @@ # include grub_uint64_t -grub_util_get_fd_size (int fd, const char *name, unsigned *log_secsize) +grub_util_get_fd_size (grub_util_fd_t fd, const char *name, unsigned *log_secsize) { unsigned long long nr; unsigned sector_size, log_sector_size; @@ -69,6 +69,6 @@ grub_util_get_fd_size (int fd, const char *name, unsigned *log_secsize) } void -grub_hostdisk_configure_device_driver (int fd __attribute__ ((unused))) +grub_hostdisk_configure_device_driver (grub_util_fd_t fd __attribute__ ((unused))) { } diff --git a/grub-core/kern/emu/hostdisk_basic.c b/grub-core/kern/emu/hostdisk_basic.c index a9342d017..7ea079557 100644 --- a/grub-core/kern/emu/hostdisk_basic.c +++ b/grub-core/kern/emu/hostdisk_basic.c @@ -43,7 +43,7 @@ #include grub_int64_t -grub_util_get_fd_size_os (int fd __attribute__ ((unused)), +grub_util_get_fd_size_os (grub_util_fd_t fd __attribute__ ((unused)), const char *name __attribute__ ((unused)), unsigned *log_secsize __attribute__ ((unused))) { @@ -53,7 +53,7 @@ grub_util_get_fd_size_os (int fd __attribute__ ((unused)), } void -grub_hostdisk_configure_device_driver (int fd __attribute__ ((unused))) +grub_hostdisk_configure_device_driver (grub_util_fd_t fd __attribute__ ((unused))) { } diff --git a/grub-core/kern/emu/hostdisk_bsd.c b/grub-core/kern/emu/hostdisk_bsd.c index b986f0321..a5dadf195 100644 --- a/grub-core/kern/emu/hostdisk_bsd.c +++ b/grub-core/kern/emu/hostdisk_bsd.c @@ -61,7 +61,7 @@ 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) +grub_hostdisk_configure_device_driver (grub_util_fd_t fd) { struct stat st; @@ -80,13 +80,13 @@ grub_hostdisk_configure_device_driver (int fd) } #else void -grub_hostdisk_configure_device_driver (int fd __attribute__ ((unused))) +grub_hostdisk_configure_device_driver (grub_util_fd_t fd __attribute__ ((unused))) { } #endif grub_int64_t -grub_util_get_fd_size_os (int fd, const char *name, unsigned *log_secsize) +grub_util_get_fd_size_os (grub_util_fd_t fd, const char *name, unsigned *log_secsize) { struct disklabel label; unsigned sector_size, log_sector_size; diff --git a/grub-core/kern/emu/hostdisk_cygwin.c b/grub-core/kern/emu/hostdisk_cygwin.c deleted file mode 100644 index c1e4bd56a..000000000 --- a/grub-core/kern/emu/hostdisk_cygwin.c +++ /dev/null @@ -1,84 +0,0 @@ -/* - * 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_freebsd.c b/grub-core/kern/emu/hostdisk_freebsd.c index 72ff59137..86d876c44 100644 --- a/grub-core/kern/emu/hostdisk_freebsd.c +++ b/grub-core/kern/emu/hostdisk_freebsd.c @@ -49,12 +49,12 @@ # include void -grub_hostdisk_configure_device_driver (int fd __attribute__ ((unused))) +grub_hostdisk_configure_device_driver (grub_util_fd_t fd __attribute__ ((unused))) { } grub_int64_t -grub_util_get_fd_size_os (int fd, const char *name, unsigned *log_secsize) +grub_util_get_fd_size_os (grub_util_fd_t fd, const char *name, unsigned *log_secsize) { unsigned long long nr; unsigned sector_size, log_sector_size; diff --git a/grub-core/kern/emu/hostdisk_hurd.c b/grub-core/kern/emu/hostdisk_hurd.c index 7a364ed2f..36ce09a0c 100644 --- a/grub-core/kern/emu/hostdisk_hurd.c +++ b/grub-core/kern/emu/hostdisk_hurd.c @@ -48,7 +48,7 @@ #include void -grub_hostdisk_configure_device_driver (int fd __attribute__ ((unused))) +grub_hostdisk_configure_device_driver (grub_util_fd_t fd __attribute__ ((unused))) { } @@ -124,7 +124,7 @@ grub_util_hurd_get_disk_info (const char *dev, grub_uint32_t *secsize, grub_disk } grub_int64_t -grub_util_get_fd_size_os (int fd, const char *name, unsigned *log_secsize) +grub_util_get_fd_size_os (grub_util_fd_t fd, const char *name, unsigned *log_secsize) { grub_uint32_t sector_size; grub_disk_addr_t size; diff --git a/grub-core/kern/emu/hostdisk_linux.c b/grub-core/kern/emu/hostdisk_linux.c index eef89ad42..fecc649e5 100644 --- a/grub-core/kern/emu/hostdisk_linux.c +++ b/grub-core/kern/emu/hostdisk_linux.c @@ -67,7 +67,7 @@ struct hd_geometry grub_int64_t -grub_util_get_fd_size_os (int fd, const char *name, unsigned *log_secsize) +grub_util_get_fd_size_os (grub_util_fd_t fd, const char *name, unsigned *log_secsize) { unsigned long long nr; unsigned sector_size, log_sector_size; @@ -96,7 +96,7 @@ grub_util_get_fd_size_os (int fd, const char *name, unsigned *log_secsize) grub_disk_addr_t grub_util_find_partition_start_os (const char *dev) { - int fd; + grub_util_fd_t fd; struct hd_geometry hdg; fd = open (dev, O_RDONLY); @@ -195,7 +195,7 @@ grub_hostdisk_linux_find_partition (char *dev, grub_disk_addr_t sector) for (i = 1; i < 10000; i++) { - int fd; + grub_util_fd_t fd; grub_disk_addr_t start; sprintf (p, format, i); @@ -239,7 +239,7 @@ grub_hostdisk_linux_find_partition (char *dev, grub_disk_addr_t sector) void grub_hostdisk_flush_initial_buffer (const char *os_dev) { - int fd; + grub_util_fd_t fd; struct stat st; fd = open (os_dev, O_RDONLY); @@ -249,7 +249,114 @@ grub_hostdisk_flush_initial_buffer (const char *os_dev) close (fd); } -void -grub_hostdisk_configure_device_driver (int fd __attribute__ ((unused))) +int +grub_util_fd_open_device (const grub_disk_t disk, grub_disk_addr_t sector, int flags, + grub_disk_addr_t *max) { + grub_util_fd_t fd; + struct grub_util_hostdisk_data *data = disk->data; + + *max = ~0ULL; + +#ifdef O_LARGEFILE + flags |= O_LARGEFILE; +#endif +#ifdef O_SYNC + flags |= O_SYNC; +#endif +#ifdef O_FSYNC + flags |= O_FSYNC; +#endif +#ifdef O_BINARY + flags |= O_BINARY; +#endif + + /* Linux has a bug that the disk cache for a whole disk is not consistent + with the one for a partition of the disk. */ + { + int is_partition = 0; + char dev[PATH_MAX]; + grub_disk_addr_t part_start = 0; + + part_start = grub_partition_get_start (disk->partition); + + strcpy (dev, grub_util_biosdisk_get_osdev (disk)); + if (disk->partition + && strncmp (dev, "/dev/", 5) == 0) + { + if (sector >= part_start) + is_partition = grub_hostdisk_linux_find_partition (dev, part_start); + else + *max = part_start - sector; + } + + reopen: + + if (data->dev && strcmp (data->dev, dev) == 0 && + data->access_mode == (flags & O_ACCMODE)) + { + grub_dprintf ("hostdisk", "reusing open device `%s'\n", dev); + fd = data->fd; + } + else + { + free (data->dev); + data->dev = 0; + if (data->fd != -1) + { + if (data->access_mode == O_RDWR || data->access_mode == O_WRONLY) + { + fsync (data->fd); + if (data->is_disk) + ioctl (data->fd, BLKFLSBUF, 0); + } + + close (data->fd); + data->fd = -1; + } + + /* Open the partition. */ + grub_dprintf ("hostdisk", "opening the device `%s' in open_device()\n", dev); + fd = open (dev, flags); + if (fd < 0) + { + grub_error (GRUB_ERR_BAD_DEVICE, N_("cannot open `%s': %s"), + dev, strerror (errno)); + return -1; + } + + data->dev = xstrdup (dev); + data->access_mode = (flags & O_ACCMODE); + data->fd = fd; + + if (data->is_disk) + ioctl (data->fd, BLKFLSBUF, 0); + } + + if (is_partition) + { + *max = grub_util_get_fd_size (fd, dev, 0); + *max >>= disk->log_sector_size; + if (sector - part_start >= *max) + { + *max = disk->partition->len - (sector - part_start); + if (*max == 0) + *max = ~0ULL; + is_partition = 0; + strcpy (dev, grub_util_biosdisk_get_osdev (disk)); + goto reopen; + } + sector -= part_start; + *max -= sector; + } + } + + if (grub_util_fd_seek (fd, grub_util_biosdisk_get_osdev (disk), + sector << disk->log_sector_size)) + { + close (fd); + return -1; + } + + return fd; } diff --git a/grub-core/kern/emu/hostdisk_mingw.c b/grub-core/kern/emu/hostdisk_mingw.c deleted file mode 100644 index eceb5d408..000000000 --- a/grub-core/kern/emu/hostdisk_mingw.c +++ /dev/null @@ -1,120 +0,0 @@ -/* - * 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 index c21b76439..1957510e2 100644 --- a/grub-core/kern/emu/hostdisk_os.c +++ b/grub-core/kern/emu/hostdisk_os.c @@ -10,10 +10,8 @@ #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" +#elif defined(__CYGWIN__) || defined(__MINGW32__) +#include "hostdisk_windows.c" #else # warning "No hostdisk OS-specific functions is available for your system. Device detection may not work properly." #include "hostdisk_basic.c" diff --git a/grub-core/kern/emu/hostdisk_sun.c b/grub-core/kern/emu/hostdisk_sun.c index 0232fa253..fa691a0c9 100644 --- a/grub-core/kern/emu/hostdisk_sun.c +++ b/grub-core/kern/emu/hostdisk_sun.c @@ -45,7 +45,7 @@ # include grub_int64_t -grub_util_get_fd_size_os (int fd, const char *name, unsigned *log_secsize) +grub_util_get_fd_size_os (grub_util_fd_t fd, const char *name, unsigned *log_secsize) { struct dk_minfo minfo; unsigned sector_size, log_sector_size; @@ -68,7 +68,7 @@ grub_util_get_fd_size_os (int fd, const char *name, unsigned *log_secsize) } void -grub_hostdisk_configure_device_driver (int fd __attribute__ ((unused))) +grub_hostdisk_configure_device_driver (grub_util_fd_t fd __attribute__ ((unused))) { } diff --git a/grub-core/kern/emu/hostdisk_unix.c b/grub-core/kern/emu/hostdisk_unix.c new file mode 100644 index 000000000..fcadbe355 --- /dev/null +++ b/grub-core/kern/emu/hostdisk_unix.c @@ -0,0 +1,169 @@ +/* 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 + +#if !defined (__CYGWIN__) && !defined (__MINGW32__) + +#ifdef __linux__ +# include /* ioctl */ +# include +# if !defined(__GLIBC__) || \ + ((__GLIBC__ < 2) || ((__GLIBC__ == 2) && (__GLIBC_MINOR__ < 1))) +/* Maybe libc doesn't have large file support. */ +# include /* _llseek */ +# endif /* (GLIBC < 2) || ((__GLIBC__ == 2) && (__GLIBC_MINOR < 1)) */ +# ifndef BLKFLSBUF +# define BLKFLSBUF _IO (0x12,97) /* flush buffer cache */ +# endif /* ! BLKFLSBUF */ +#endif /* __linux__ */ + +grub_uint64_t +grub_util_get_fd_size (grub_util_fd_t fd, const char *name, unsigned *log_secsize) +{ + 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 GRUB_DISK_DEVS_ARE_CHAR + if (S_ISCHR (st.st_mode)) +#else + if (S_ISBLK (st.st_mode)) +#endif + ret = grub_util_get_fd_size_os (fd, name, log_secsize); + if (ret != -1LL) + return ret; + + if (log_secsize) + *log_secsize = 9; + + return st.st_size; +} + +#if defined(__linux__) && (!defined(__GLIBC__) || \ + ((__GLIBC__ < 2) || ((__GLIBC__ == 2) && (__GLIBC_MINOR__ < 1)))) + /* Maybe libc doesn't have large file support. */ +grub_err_t +grub_util_fd_seek (grub_util_fd_t fd, const char *name, grub_uint64_t off) +{ + loff_t offset, result; + static int _llseek (uint filedes, ulong hi, ulong lo, + loff_t *res, uint wh); + _syscall5 (int, _llseek, uint, filedes, ulong, hi, ulong, lo, + loff_t *, res, uint, wh); + + offset = (loff_t) off; + if (_llseek (fd, offset >> 32, offset & 0xffffffff, &result, SEEK_SET)) + return grub_error (GRUB_ERR_BAD_DEVICE, N_("cannot seek `%s': %s"), + name, strerror (errno)); + return GRUB_ERR_NONE; +} +#else +grub_err_t +grub_util_fd_seek (grub_util_fd_t fd, const char *name, grub_uint64_t off) +{ + off_t offset = (off_t) off; + + if (lseek (fd, offset, SEEK_SET) != offset) + return grub_error (GRUB_ERR_BAD_DEVICE, N_("cannot seek `%s': %s"), + name, strerror (errno)); + return 0; +} +#endif + + +/* Read LEN bytes from FD in BUF. Return less than or equal to zero if an + error occurs, otherwise return LEN. */ +ssize_t +grub_util_fd_read (grub_util_fd_t fd, char *buf, size_t len) +{ + ssize_t size = len; + + while (len) + { + ssize_t ret = read (fd, buf, len); + + if (ret <= 0) + { + if (errno == EINTR) + continue; + else + return ret; + } + + len -= ret; + buf += ret; + } + + return size; +} + +/* Write LEN bytes from BUF to FD. Return less than or equal to zero if an + error occurs, otherwise return LEN. */ +ssize_t +grub_util_fd_write (grub_util_fd_t fd, const char *buf, size_t len) +{ + ssize_t size = len; + + while (len) + { + ssize_t ret = write (fd, buf, len); + + if (ret <= 0) + { + if (errno == EINTR) + continue; + else + return ret; + } + + len -= ret; + buf += ret; + } + + return size; +} + +#endif diff --git a/grub-core/kern/emu/hostdisk_windows.c b/grub-core/kern/emu/hostdisk_windows.c new file mode 100644 index 000000000..4ebb2f3c0 --- /dev/null +++ b/grub-core/kern/emu/hostdisk_windows.c @@ -0,0 +1,197 @@ +/* + * 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 + +#ifdef __CYGWIN__ +LPTSTR +grub_util_get_windows_path (const char *path) +{ + LPTSTR winpath; + winpath = xmalloc (sizeof (winpath[0]) * PATH_MAX); + if (cygwin_conv_path (sizeof (winpath[0]) == 1 ? CCP_POSIX_TO_WIN_A + : CCP_POSIX_TO_WIN_W, path, winpath, + sizeof (winpath[0]) * PATH_MAX)) + grub_util_error ("%s", _("cygwin_conv_path() failed")); + return winpath; +} +#else +LPTSTR +grub_util_get_windows_path (const char *path) +{ +#if SIZEOF_TCHAR == 1 + return xstrdup (path); +#elif SIZEOF_TCHAR == 2 + size_t ssz = strlen (path); + size_t tsz = 2 * (GRUB_MAX_UTF16_PER_UTF8 * ssz + 1); + LPTSTR ret = xmalloc (tsz); + tsz = grub_utf8_to_utf16 (ret, tsz, (const grub_uint8_t *) path, ssz, NULL); + ret[tsz] = 0; + return ret; +#else +#error SIZEOF_TCHAR +#error "Unsupported TCHAR size" +#endif +} +#endif + +grub_uint64_t +grub_util_get_fd_size (grub_util_fd_t hd, const char *name_in, + unsigned *log_secsize) +{ + grub_int64_t size = -1LL; + int log_sector_size = 9; + LPTSTR name = grub_util_get_windows_path (name_in); + + if (log_secsize) + *log_secsize = log_sector_size; + + if (((name[0] == '/') || (name[0] == '\\')) && + ((name[1] == '/') || (name[1] == '\\')) && + ((name[2] == '.') || (name[2] == '?')) && + ((name[3] == '/') || (name[3] == '\\'))) + { + 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); + + return size; +} + +void +grub_hostdisk_configure_device_driver (grub_util_fd_t fd __attribute__ ((unused))) +{ +} + +void +grub_hostdisk_flush_initial_buffer (const char *os_dev __attribute__ ((unused))) +{ +} + +grub_err_t +grub_util_fd_seek (grub_util_fd_t fd, const char *name, grub_uint64_t off) +{ + LARGE_INTEGER offset; + offset.QuadPart = off; + + if (!SetFilePointerEx (fd, offset, NULL, FILE_BEGIN)) + return grub_error (GRUB_ERR_BAD_DEVICE, N_("cannot seek `%s': %s"), + name, strerror (errno)); + return 0; +} + +grub_util_fd_t +grub_util_fd_open (const char *os_dev, int flags) +{ + DWORD flg = 0; + LPTSTR dev = grub_util_get_windows_path (os_dev); + grub_util_fd_t ret; + + if (flags & O_WRONLY) + flg |= GENERIC_WRITE; + if (flags & O_RDONLY) + flg |= GENERIC_READ; + flg = GENERIC_READ; + ret = CreateFile (dev, flg, FILE_SHARE_READ | FILE_SHARE_WRITE, + 0, OPEN_EXISTING, 0, 0); + free (dev); + grub_util_info ("handle = %p", ret); + return ret; +} + +ssize_t +grub_util_fd_read (grub_util_fd_t fd, char *buf, size_t len) +{ + DWORD real_read; + if (!ReadFile(fd, buf, len, &real_read, NULL)) + { + grub_util_info ("read err %x", (int) GetLastError ()); + return -1; + } + grub_util_info ("successful read"); + return real_read; +} + +ssize_t +grub_util_fd_write (grub_util_fd_t fd, const char *buf, size_t len) +{ + DWORD real_read; + if (!WriteFile(fd, buf, len, &real_read, NULL)) + { + grub_util_info ("write err %x", (int) GetLastError ()); + return -1; + } + + grub_util_info ("successful write"); + return real_read; +} diff --git a/grub-core/kern/emu/hostfs.c b/grub-core/kern/emu/hostfs.c index 727f7bece..ba309489f 100644 --- a/grub-core/kern/emu/hostfs.c +++ b/grub-core/kern/emu/hostfs.c @@ -132,7 +132,13 @@ grub_hostfs_open (struct grub_file *file, const char *name) file->data = data; +#if defined (__CYGWIN__) || defined (__MINGW32__) + fseek (f, 0, SEEK_END); + file->size = ftello (f); + fseek (f, 0, SEEK_SET); +#else file->size = grub_util_get_fd_size (fileno (f), name, NULL); +#endif return GRUB_ERR_NONE; } diff --git a/grub-core/kern/emu/main.c b/grub-core/kern/emu/main.c index 99ef90ce1..eea57f9ef 100644 --- a/grub-core/kern/emu/main.c +++ b/grub-core/kern/emu/main.c @@ -234,7 +234,7 @@ main (int argc, char *argv[]) return 0; } -#ifdef __MINGW32__ +#if defined (__MINGW32__) || defined (__CYGWIN__) void grub_millisleep (grub_uint32_t ms) diff --git a/include/grub/emu/getroot.h b/include/grub/emu/getroot.h index e4b903fa6..bbba7308c 100644 --- a/include/grub/emu/getroot.h +++ b/include/grub/emu/getroot.h @@ -75,10 +75,12 @@ char * grub_util_get_devmapper_grub_dev (const char *os_dev); /* Functions provided by getroot.c. */ -#if !defined (__MINGW32__) && !defined (__CYGWIN__) && !defined (__GNU__) +#if !defined (__MINGW32__) && !defined (__CYGWIN__) #include pid_t grub_util_exec_pipe (char **argv, int *fd); +void +grub_util_pull_lvm_by_command (const char *os_dev); #endif char ** grub_util_find_root_devices_from_poolname (char *poolname); diff --git a/include/grub/emu/hostdisk.h b/include/grub/emu/hostdisk.h index fd882dfad..ef27c6d7a 100644 --- a/include/grub/emu/hostdisk.h +++ b/include/grub/emu/hostdisk.h @@ -24,6 +24,28 @@ #include #include +#if defined (__CYGWIN__) || defined (__MINGW32__) +#include +typedef HANDLE grub_util_fd_t; +#define GRUB_UTIL_FD_INVALID INVALID_HANDLE_VALUE +#define GRUB_UTIL_FD_IS_VALID(x) ((x) != GRUB_UTIL_FD_INVALID) +#define grub_util_fd_close(x) CloseHandle(x) +#define grub_util_fd_sync(x) FlushFileBuffers(x) +grub_util_fd_t +grub_util_fd_open (const char *os_dev, int flags); +#else +typedef int grub_util_fd_t; +#define GRUB_UTIL_FD_INVALID -1 +#define GRUB_UTIL_FD_IS_VALID(x) ((x) >= 0) +#define grub_util_fd_close(x) close(x) +#define grub_util_fd_sync(x) fsync(x) +#define grub_util_fd_open(x,y) open(x,y) +#endif + +grub_util_fd_t +grub_util_fd_open_device (const grub_disk_t disk, grub_disk_addr_t sector, int flags, + grub_disk_addr_t *max); + void grub_util_biosdisk_init (const char *dev_map); void grub_util_biosdisk_fini (void); char *grub_util_biosdisk_get_grub_dev (const char *os_dev); @@ -34,9 +56,9 @@ const char * grub_util_biosdisk_get_compatibility_hint (grub_disk_t disk); grub_err_t grub_util_biosdisk_flush (struct grub_disk *disk); 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); -ssize_t grub_util_fd_write (int fd, const char *buf, size_t len); +grub_util_fd_seek (grub_util_fd_t fd, const char *name, grub_uint64_t sector); +ssize_t grub_util_fd_read (grub_util_fd_t fd, char *buf, size_t len); +ssize_t grub_util_fd_write (grub_util_fd_t fd, const char *buf, size_t len); grub_err_t grub_cryptodisk_cheat_mount (const char *sourcedev, const char *cheat); void grub_util_cryptodisk_print_uuid (grub_disk_t disk); @@ -55,7 +77,7 @@ const char * grub_hostdisk_os_dev_to_grub_drive (const char *os_dev, int add); grub_uint64_t -grub_util_get_fd_size (int fd, const char *name, unsigned *log_secsize); +grub_util_get_fd_size (grub_util_fd_t fd, const char *name, unsigned *log_secsize); char * grub_util_get_os_disk (const char *os_dev); @@ -73,7 +95,7 @@ grub_util_get_dm_node_linear_info (const char *dev, /* Supplied by hostdisk_*.c. */ grub_int64_t -grub_util_get_fd_size_os (int fd, const char *name, unsigned *log_secsize); +grub_util_get_fd_size_os (grub_util_fd_t 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); @@ -84,7 +106,7 @@ grub_hostdisk_find_partition_start_os (const char *dev); For now it's non-nop only on NetBSD. */ void -grub_hostdisk_configure_device_driver (int fd); +grub_hostdisk_configure_device_driver (grub_util_fd_t fd); void grub_hostdisk_flush_initial_buffer (const char *os_dev); @@ -94,11 +116,13 @@ grub_hostdisk_flush_initial_buffer (const char *os_dev); #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 +struct grub_util_hostdisk_data +{ + char *dev; + int access_mode; + grub_util_fd_t fd; + int is_disk; + int device_map; +}; #endif /* ! GRUB_BIOSDISK_MACHINE_UTIL_HEADER */ diff --git a/include/grub/util/windows.h b/include/grub/util/windows.h new file mode 100644 index 000000000..2645fccbf --- /dev/null +++ b/include/grub/util/windows.h @@ -0,0 +1,30 @@ +/* + * GRUB -- GRand Unified Bootloader + * Copyright (C) 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 . + */ + +#ifndef GRUB_WINDOWS_UTIL_HEADER +#define GRUB_WINDOWS_UTIL_HEADER 1 + +#include + +LPTSTR +grub_util_get_windows_path (const char *unix_path); + +char * +grub_util_tchar_to_utf8 (LPCTSTR in); + +#endif diff --git a/util/getroot.c b/util/getroot.c index eab6f24df..8673ede5f 100644 --- a/util/getroot.c +++ b/util/getroot.c @@ -54,13 +54,6 @@ #endif /* ! FLOPPY_MAJOR */ #endif -#ifdef __GNU__ -#include -#include -#include -#include -#endif - #include #if defined(HAVE_LIBZFS) && defined(HAVE_LIBNVPAIR) @@ -74,16 +67,6 @@ #include #include -#ifdef __CYGWIN__ -# include -# include /* BLKGETSIZE64 */ -# include /* HDIO_GETGEO */ -# include - -# define MAJOR(dev) ((unsigned) ((dev) >> 16)) -# define FLOPPY_MAJOR 2 -#endif - #if defined(__FreeBSD__) || defined(__FreeBSD_kernel__) # define MAJOR(dev) major(dev) # define FLOPPY_MAJOR 2 @@ -117,633 +100,6 @@ # endif /* ! RAW_FLOPPY_MAJOR */ #endif /* defined(__NetBSD__) */ -#if !defined (__MINGW32__) && !defined (__CYGWIN__) - -static void -pull_lvm_by_command (const char *os_dev); -#endif - -#if ! defined(__CYGWIN__) - -static void -strip_extra_slashes (char *dir) -{ - char *p = dir; - - while ((p = strchr (p, '/')) != 0) - { - if (p[1] == '/') - { - memmove (p, p + 1, strlen (p)); - continue; - } - else if (p[1] == '\0') - { - if (p > dir) - p[0] = '\0'; - break; - } - - p++; - } -} - -static char * -xgetcwd (void) -{ - size_t size = 10; - char *path; - - path = xmalloc (size); - while (! getcwd (path, size)) - { - size <<= 1; - path = xrealloc (path, size); - } - - return path; -} - -#endif - -#if !defined (__MINGW32__) && !defined (__CYGWIN__) - -#include -#include - -pid_t -grub_util_exec_pipe (char **argv, int *fd) -{ - int mdadm_pipe[2]; - pid_t mdadm_pid; - - *fd = 0; - - if (pipe (mdadm_pipe) < 0) - { - grub_util_warn (_("Unable to create pipe: %s"), - strerror (errno)); - return 0; - } - mdadm_pid = fork (); - if (mdadm_pid < 0) - grub_util_error (_("Unable to fork: %s"), strerror (errno)); - else if (mdadm_pid == 0) - { - /* Child. */ - - /* Close fd's. */ - grub_util_devmapper_cleanup (); - grub_diskfilter_fini (); - - /* Ensure child is not localised. */ - setenv ("LC_ALL", "C", 1); - - close (mdadm_pipe[0]); - dup2 (mdadm_pipe[1], STDOUT_FILENO); - close (mdadm_pipe[1]); - - execvp (argv[0], argv); - exit (127); - } - else - { - close (mdadm_pipe[1]); - *fd = mdadm_pipe[0]; - return mdadm_pid; - } -} - -#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; - size_t devices_allocated = 0; - -#if defined(HAVE_LIBZFS) && defined(HAVE_LIBNVPAIR) - zpool_handle_t *zpool; - libzfs_handle_t *libzfs; - nvlist_t *config, *vdev_tree; - nvlist_t **children; - unsigned int nvlist_count; - unsigned int i; - char *device = 0; - - libzfs = grub_get_libzfs_handle (); - if (! libzfs) - return NULL; - - zpool = zpool_open (libzfs, poolname); - config = zpool_get_config (zpool, NULL); - - if (nvlist_lookup_nvlist (config, "vdev_tree", &vdev_tree) != 0) - error (1, errno, "nvlist_lookup_nvlist (\"vdev_tree\")"); - - if (nvlist_lookup_nvlist_array (vdev_tree, "children", &children, &nvlist_count) != 0) - error (1, errno, "nvlist_lookup_nvlist_array (\"children\")"); - assert (nvlist_count > 0); - - while (nvlist_lookup_nvlist_array (children[0], "children", - &children, &nvlist_count) == 0) - assert (nvlist_count > 0); - - for (i = 0; i < nvlist_count; i++) - { - if (nvlist_lookup_string (children[i], "path", &device) != 0) - error (1, errno, "nvlist_lookup_string (\"path\")"); - - struct stat st; - if (stat (device, &st) == 0) - { -#ifdef __sun__ - if (grub_memcmp (device, "/dev/dsk/", sizeof ("/dev/dsk/") - 1) - == 0) - device = xasprintf ("/dev/rdsk/%s", - device + sizeof ("/dev/dsk/") - 1); - else if (grub_memcmp (device, "/devices", sizeof ("/devices") - 1) - == 0 - && grub_memcmp (device + strlen (device) - 4, - ",raw", 4) != 0) - device = xasprintf ("%s,raw", device); - else -#endif - device = xstrdup (device); - if (ndevices >= devices_allocated) - { - devices_allocated = 2 * (devices_allocated + 8); - devices = xrealloc (devices, sizeof (devices[0]) - * devices_allocated); - } - devices[ndevices++] = device; - } - - device = NULL; - } - - zpool_close (zpool); -#else - FILE *fp; - int ret; - char *line; - size_t len; - int st; - - char name[PATH_MAX + 1], state[257], readlen[257], writelen[257]; - char cksum[257], notes[257]; - unsigned int dummy; - char *argv[4]; - pid_t pid; - int fd; - - /* execvp has inconvenient types, hence the casts. None of these - strings will actually be modified. */ - argv[0] = (char *) "zpool"; - argv[1] = (char *) "status"; - argv[2] = (char *) poolname; - argv[3] = NULL; - - pid = grub_util_exec_pipe (argv, &fd); - if (!pid) - return NULL; - - fp = fdopen (fd, "r"); - if (!fp) - { - grub_util_warn (_("Unable to open stream from %s: %s"), - "zpool", strerror (errno)); - goto out; - } - - st = 0; - while (1) - { - line = NULL; - ret = getline (&line, &len, fp); - if (ret == -1) - break; - - if (sscanf (line, " %s %256s %256s %256s %256s %256s", - name, state, readlen, writelen, cksum, notes) >= 5) - switch (st) - { - case 0: - if (!strcmp (name, "NAME") - && !strcmp (state, "STATE") - && !strcmp (readlen, "READ") - && !strcmp (writelen, "WRITE") - && !strcmp (cksum, "CKSUM")) - st++; - break; - case 1: - { - char *ptr = line; - while (1) - { - if (strncmp (ptr, poolname, strlen (poolname)) == 0 - && grub_isspace(ptr[strlen (poolname)])) - st++; - if (!grub_isspace (*ptr)) - break; - ptr++; - } - } - break; - case 2: - if (strcmp (name, "mirror") && !sscanf (name, "mirror-%u", &dummy) - && !sscanf (name, "raidz%u", &dummy) - && !sscanf (name, "raidz1%u", &dummy) - && !sscanf (name, "raidz2%u", &dummy) - && !sscanf (name, "raidz3%u", &dummy) - && !strcmp (state, "ONLINE")) - { - if (ndevices >= devices_allocated) - { - devices_allocated = 2 * (devices_allocated + 8); - devices = xrealloc (devices, sizeof (devices[0]) - * devices_allocated); - } - if (name[0] == '/') - devices[ndevices++] = xstrdup (name); - else - devices[ndevices++] = xasprintf ("/dev/%s", name); - } - break; - } - - free (line); - } - - out: - close (fd); - waitpid (pid, NULL, 0); -#endif - if (devices) - { - if (ndevices >= devices_allocated) - { - devices_allocated = 2 * (devices_allocated + 8); - devices = xrealloc (devices, sizeof (devices[0]) - * devices_allocated); - } - devices[ndevices++] = 0; - } - return devices; -} - -#endif - -#if !defined (__MINGW32__) && !defined (__CYGWIN__) && !defined (__GNU__) - -static char ** -find_root_devices_from_libzfs (const char *dir) -{ - char **devices = NULL; - char *poolname; - char *poolfs; - - grub_find_zpool_from_dir (dir, &poolname, &poolfs); - if (! poolname) - return NULL; - - devices = grub_util_find_root_devices_from_poolname (poolname); - - free (poolname); - if (poolfs) - free (poolfs); - - return devices; -} - -#endif - -#ifdef __MINGW32__ - -char * -grub_find_device (const char *dir __attribute__ ((unused)), - dev_t dev __attribute__ ((unused))) -{ - return 0; -} - -#elif ! defined(__CYGWIN__) - -char * -grub_find_device (const char *dir, dev_t dev) -{ - DIR *dp; - char *saved_cwd; - struct dirent *ent; - - if (! dir) - { -#ifdef __CYGWIN__ - return NULL; -#else - dir = "/dev"; -#endif - } - - dp = opendir (dir); - if (! dp) - return 0; - - saved_cwd = xgetcwd (); - - grub_util_info ("changing current directory to %s", dir); - if (chdir (dir) < 0) - { - free (saved_cwd); - closedir (dp); - return 0; - } - - while ((ent = readdir (dp)) != 0) - { - struct stat st; - - /* Avoid: - - dotfiles (like "/dev/.tmp.md0") since they could be duplicates. - - dotdirs (like "/dev/.static") since they could contain duplicates. */ - if (ent->d_name[0] == '.') - continue; - - if (lstat (ent->d_name, &st) < 0) - /* Ignore any error. */ - continue; - - if (S_ISLNK (st.st_mode)) { -#ifdef __linux__ - if (strcmp (dir, "mapper") == 0 || strcmp (dir, "/dev/mapper") == 0) { - /* Follow symbolic links under /dev/mapper/; the canonical name - may be something like /dev/dm-0, but the names under - /dev/mapper/ are more human-readable and so we prefer them if - we can get them. */ - if (stat (ent->d_name, &st) < 0) - continue; - } else -#endif /* __linux__ */ - /* Don't follow other symbolic links. */ - continue; - } - - if (S_ISDIR (st.st_mode)) - { - /* Find it recursively. */ - char *res; - - res = grub_find_device (ent->d_name, dev); - - if (res) - { - if (chdir (saved_cwd) < 0) - grub_util_error ("%s", - _("cannot restore the original directory")); - - free (saved_cwd); - closedir (dp); - return res; - } - } - -#if defined(__FreeBSD__) || defined(__FreeBSD_kernel__) || defined(__APPLE__) - if (S_ISCHR (st.st_mode) && st.st_rdev == dev) -#else - if (S_ISBLK (st.st_mode) && st.st_rdev == dev) -#endif - { -#ifdef __linux__ - /* Skip device names like /dev/dm-0, which are short-hand aliases - to more descriptive device names, e.g. those under /dev/mapper */ - if (ent->d_name[0] == 'd' && - ent->d_name[1] == 'm' && - ent->d_name[2] == '-' && - ent->d_name[3] >= '0' && - ent->d_name[3] <= '9') - continue; -#endif - - /* Found! */ - char *res; - char *cwd; -#if defined(__NetBSD__) || defined(__OpenBSD__) - /* Convert this block device to its character (raw) device. */ - const char *template = "%s/r%s"; -#else - /* Keep the device name as it is. */ - const char *template = "%s/%s"; -#endif - - cwd = xgetcwd (); - res = xmalloc (strlen (cwd) + strlen (ent->d_name) + 3); - sprintf (res, template, cwd, ent->d_name); - strip_extra_slashes (res); - free (cwd); - - /* /dev/root is not a real block device keep looking, takes care - of situation where root filesystem is on the same partition as - grub files */ - - if (strcmp(res, "/dev/root") == 0) - { - free (res); - continue; - } - - if (chdir (saved_cwd) < 0) - grub_util_error ("%s", _("cannot restore the original directory")); - - free (saved_cwd); - closedir (dp); - return res; - } - } - - if (chdir (saved_cwd) < 0) - grub_util_error ("%s", _("cannot restore the original directory")); - - free (saved_cwd); - closedir (dp); - return 0; -} - -#else /* __CYGWIN__ */ - -/* Read drive/partition serial number from mbr/boot sector, - return 0 on read error, ~0 on unknown serial. */ -static unsigned -get_bootsec_serial (const char *os_dev, int mbr) -{ - /* Read boot sector. */ - int fd = open (os_dev, O_RDONLY); - if (fd < 0) - return 0; - unsigned char buf[0x200]; - int n = read (fd, buf, sizeof (buf)); - close (fd); - if (n != sizeof(buf)) - return 0; - - /* Check signature. */ - if (!(buf[0x1fe] == 0x55 && buf[0x1ff] == 0xaa)) - return ~0; - - /* Serial number offset depends on boot sector type. */ - if (mbr) - n = 0x1b8; - else if (memcmp (buf + 0x03, "NTFS", 4) == 0) - n = 0x048; - else if (memcmp (buf + 0x52, "FAT32", 5) == 0) - n = 0x043; - else if (memcmp (buf + 0x36, "FAT", 3) == 0) - n = 0x027; - else - return ~0; - - unsigned serial = *(unsigned *)(buf + n); - if (serial == 0) - return ~0; - return serial; -} - -#pragma GCC diagnostic warning "-Wdeprecated-declarations" - -char * -grub_find_device (const char *path, dev_t dev) -{ - /* No root device for /cygdrive. */ - if (dev == (DEV_CYGDRIVE_MAJOR << 16)) - return 0; - - /* Convert to full POSIX and Win32 path. */ - char fullpath[PATH_MAX], winpath[PATH_MAX]; - - cygwin_conv_path (CCP_WIN_A_TO_POSIX, path, fullpath, sizeof (fullpath)); - cygwin_conv_path (CCP_POSIX_TO_WIN_A, fullpath, winpath, sizeof (winpath)); - - /* If identical, this is no real filesystem path. */ - if (strcmp (fullpath, winpath) == 0) - return 0; - - /* Check for floppy drive letter. */ - if (winpath[0] && winpath[1] == ':' && strchr ("AaBb", winpath[0])) - return xstrdup (strchr ("Aa", winpath[0]) ? "/dev/fd0" : "/dev/fd1"); - - /* Cygwin returns the partition serial number in stat.st_dev. - This is never identical to the device number of the emulated - /dev/sdXN device, so above grub_find_device () does not work. - Search the partition with the same serial in boot sector instead. */ - char devpath[sizeof ("/dev/sda15") + 13]; /* Size + Paranoia. */ - int d; - for (d = 'a'; d <= 'z'; d++) - { - sprintf (devpath, "/dev/sd%c", d); - if (get_bootsec_serial (devpath, 1) == 0) - continue; - int p; - for (p = 1; p <= 15; p++) - { - sprintf (devpath, "/dev/sd%c%d", d, p); - unsigned ser = get_bootsec_serial (devpath, 0); - if (ser == 0) - break; - if (ser != (unsigned)~0 && dev == (dev_t)ser) - return xstrdup (devpath); - } - } - return 0; -} - -#endif /* __CYGWIN__ */ - -char ** -grub_guess_root_devices (const char *dir) -{ - char **os_dev = NULL; -#ifndef __GNU__ - struct stat st; - dev_t dev; - -#ifdef __linux__ - if (!os_dev) - os_dev = grub_find_root_devices_from_mountinfo (dir, NULL); -#endif /* __linux__ */ - -#if !defined (__MINGW32__) && !defined (__CYGWIN__) - if (!os_dev) - os_dev = find_root_devices_from_libzfs (dir); -#endif - - if (os_dev) - { - char **cur; - for (cur = os_dev; *cur; cur++) - { - char *tmp = *cur; - int root, dm; - if (strcmp (*cur, "/dev/root") == 0 - || strncmp (*cur, "/dev/dm-", sizeof ("/dev/dm-") - 1) == 0) - *cur = tmp; - else - { - *cur = canonicalize_file_name (tmp); - if (*cur == NULL) - grub_util_error (_("failed to get canonical path of `%s'"), tmp); - free (tmp); - } - root = (strcmp (*cur, "/dev/root") == 0); - dm = (strncmp (*cur, "/dev/dm-", sizeof ("/dev/dm-") - 1) == 0); - if (!dm && !root) - continue; - if (stat (*cur, &st) < 0) - break; - free (*cur); - dev = st.st_rdev; - *cur = grub_find_device (dm ? "/dev/mapper" : "/dev", dev); - } - if (!*cur) - return os_dev; - for (cur = os_dev; *cur; cur++) - free (*cur); - free (os_dev); - os_dev = 0; - } - - if (stat (dir, &st) < 0) - grub_util_error (_("cannot stat `%s': %s"), dir, strerror (errno)); - - dev = st.st_dev; -#endif /* !__GNU__ */ - - os_dev = xmalloc (2 * sizeof (os_dev[0])); - -#ifdef __CYGWIN__ - /* Cygwin specific function. */ - os_dev[0] = grub_find_device (dir, dev); - -#elif defined __GNU__ - /* GNU/Hurd specific function. */ - os_dev[0] = grub_util_find_hurd_root_device (dir); - -#else - - /* This might be truly slow, but is there any better way? */ - os_dev[0] = grub_find_device ("/dev", dev); -#endif - if (!os_dev[0]) - { - free (os_dev); - return 0; - } - - os_dev[1] = 0; - - return os_dev; -} - grub_disk_addr_t grub_util_find_partition_start (const char *dev) { @@ -764,7 +120,7 @@ grub_util_pull_device (const char *os_dev) { case GRUB_DEV_ABSTRACTION_LVM: #if !defined (__MINGW32__) && !defined (__CYGWIN__) - pull_lvm_by_command (os_dev); + grub_util_pull_lvm_by_command (os_dev); #endif /* Fallthrough in case that lvm-tools are unavailable. */ case GRUB_DEV_ABSTRACTION_LUKS: @@ -796,7 +152,6 @@ grub_util_get_grub_dev (const char *os_dev) return grub_util_biosdisk_get_grub_dev (os_dev); } - int grub_util_get_dev_abstraction (const char *os_dev) { @@ -815,151 +170,6 @@ grub_util_get_dev_abstraction (const char *os_dev) return grub_util_get_dev_abstraction_os (os_dev); } -#if !defined (__MINGW32__) && !defined (__CYGWIN__) - -static void -pull_lvm_by_command (const char *os_dev) -{ - char *argv[8]; - int fd; - pid_t pid; - FILE *mdadm; - char *buf = NULL; - size_t len = 0; - char *vgname = NULL; - const char *iptr; - char *optr; - char *vgid = NULL; - grub_size_t vgidlen = 0; - - vgid = grub_util_get_vg_uuid (os_dev); - if (vgid) - vgidlen = grub_strlen (vgid); - - if (!vgid) - { - if (strncmp (os_dev, LVM_DEV_MAPPER_STRING, - sizeof (LVM_DEV_MAPPER_STRING) - 1) - != 0) - return; - - vgname = xmalloc (strlen (os_dev + sizeof (LVM_DEV_MAPPER_STRING) - 1) + 1); - for (iptr = os_dev + sizeof (LVM_DEV_MAPPER_STRING) - 1, optr = vgname; *iptr; ) - if (*iptr != '-') - *optr++ = *iptr++; - else if (iptr[0] == '-' && iptr[1] == '-') - { - iptr += 2; - *optr++ = '-'; - } - else - break; - *optr = '\0'; - } - - /* execvp has inconvenient types, hence the casts. None of these - strings will actually be modified. */ - /* by default PV name is left aligned in 10 character field, meaning that - we do not know where name ends. Using dummy --separator disables - alignment. We have a single field, so separator itself is not output */ - argv[0] = (char *) "vgs"; - argv[1] = (char *) "--options"; - if (vgid) - argv[2] = (char *) "vg_uuid,pv_name"; - else - argv[2] = (char *) "pv_name"; - argv[3] = (char *) "--noheadings"; - argv[4] = (char *) "--separator"; - argv[5] = (char *) ":"; - argv[6] = vgname; - argv[7] = NULL; - - pid = grub_util_exec_pipe (argv, &fd); - free (vgname); - - if (!pid) - return; - - /* Parent. Read mdadm's output. */ - mdadm = fdopen (fd, "r"); - if (! mdadm) - { - grub_util_warn (_("Unable to open stream from %s: %s"), - "vgs", strerror (errno)); - goto out; - } - - while (getline (&buf, &len, mdadm) > 0) - { - char *ptr; - /* LVM adds two spaces as standard prefix */ - for (ptr = buf; ptr < buf + 2 && *ptr == ' '; ptr++); - - if (vgid && (grub_strncmp (vgid, ptr, vgidlen) != 0 - || ptr[vgidlen] != ':')) - continue; - if (vgid) - ptr += vgidlen + 1; - if (*ptr == '\0') - continue; - *(ptr + strlen (ptr) - 1) = '\0'; - grub_util_pull_device (ptr); - } - -out: - close (fd); - waitpid (pid, NULL, 0); - free (buf); -} - -#endif - -int -grub_util_biosdisk_is_floppy (grub_disk_t disk) -{ - struct stat st; - int fd; - const char *dname; - - dname = grub_util_biosdisk_get_osdev (disk); - - if (!dname) - return 0; - - fd = open (dname, O_RDONLY); - /* Shouldn't happen. */ - if (fd == -1) - return 0; - - /* Shouldn't happen either. */ - if (fstat (fd, &st) < 0) - { - close (fd); - return 0; - } - - close (fd); - -#if defined(__NetBSD__) - if (major(st.st_rdev) == RAW_FLOPPY_MAJOR) - return 1; -#endif - -#if defined(FLOPPY_MAJOR) - if (major(st.st_rdev) == FLOPPY_MAJOR) -#else - /* Some kernels (e.g. kFreeBSD) don't have a static major number - for floppies, but they still use a "fd[0-9]" pathname. */ - if (dname[5] == 'f' - && dname[6] == 'd' - && dname[7] >= '0' - && dname[7] <= '9') -#endif - return 1; - - return 0; -} - static char * convert_system_partition_to_system_disk (const char *os_dev, struct stat *st, int *is_part) @@ -1012,7 +222,7 @@ make_device_name (const char *drive, int dos_part, int bsd_part) ptr = ret; for (iptr = drive; *iptr; iptr++) { - if (*iptr == ',') + if (*iptr == ',' || *iptr == '\\') *ptr++ = '\\'; *ptr++ = *iptr; } @@ -1029,11 +239,13 @@ make_device_name (const char *drive, int dos_part, int bsd_part) char * grub_util_get_os_disk (const char *os_dev) { - struct stat st; int is_part; grub_util_info ("Looking for %s", os_dev); +#if !defined (__MINGW32__) && !defined (__CYGWIN__) + struct stat st; + if (stat (os_dev, &st) < 0) { const char *errstr = strerror (errno); @@ -1044,6 +256,9 @@ grub_util_get_os_disk (const char *os_dev) } return convert_system_partition_to_system_disk (os_dev, &st, &is_part); +#else + return convert_system_partition_to_system_disk (os_dev, NULL, &is_part); +#endif } #if !defined(__APPLE__) @@ -1079,13 +294,16 @@ find_partition (grub_disk_t dsk __attribute__ ((unused)), char * grub_util_biosdisk_get_grub_dev (const char *os_dev) { +#if !defined (__MINGW32__) && !defined (__CYGWIN__) struct stat st; +#endif const char *drive; char *sys_disk; int is_part; grub_util_info ("Looking for %s", os_dev); +#if !defined (__MINGW32__) && !defined (__CYGWIN__) if (stat (os_dev, &st) < 0) { const char *errstr = strerror (errno); @@ -1094,9 +312,21 @@ grub_util_biosdisk_get_grub_dev (const char *os_dev) grub_util_info (_("cannot stat `%s': %s"), os_dev, errstr); return 0; } - drive = find_system_device (os_dev, &st, 1, 1); + +#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); + sys_disk = convert_system_partition_to_system_disk (os_dev, &st, &is_part); +#else + drive = find_system_device (os_dev, NULL, 1, 1); + sys_disk = convert_system_partition_to_system_disk (os_dev, NULL, &is_part); +#endif + if (!sys_disk) return 0; grub_util_info ("%s is a parent of %s", sys_disk, os_dev); @@ -1107,13 +337,6 @@ grub_util_biosdisk_get_grub_dev (const char *os_dev) } free (sys_disk); -#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(__APPLE__) /* Apple uses "/dev/r?disk[0-9]+(s[0-9]+)?". */ { @@ -1228,7 +451,11 @@ grub_util_biosdisk_get_grub_dev (const char *os_dev) grub_errno = GRUB_ERR_NONE; canon = canonicalize_file_name (os_dev); +#if !defined (__MINGW32__) && !defined (__CYGWIN__) drive = find_system_device (canon ? : os_dev, &st, 0, 1); +#else + drive = find_system_device (canon ? : os_dev, NULL, 0, 1); +#endif if (canon) free (canon); return make_device_name (drive, -1, -1); @@ -1311,27 +538,6 @@ grub_util_check_char_device (const char *blk_dev) return 0; } -#ifdef __CYGWIN__ -/* Convert POSIX path to Win32 path, - remove drive letter, replace backslashes. */ -static char * -get_win32_path (const char *path) -{ - char winpath[PATH_MAX]; - if (cygwin_conv_path (CCP_POSIX_TO_WIN_A, path, winpath, sizeof(winpath))) - grub_util_error ("%s", _("cygwin_conv_path() failed")); - - int len = strlen (winpath); - int offs = (len > 2 && winpath[1] == ':' ? 2 : 0); - - int i; - for (i = offs; i < len; i++) - if (winpath[i] == '\\') - winpath[i] = '/'; - return xstrdup (winpath + offs); -} -#endif - #ifdef HAVE_LIBZFS static libzfs_handle_t *__libzfs_handle; @@ -1356,216 +562,3 @@ grub_get_libzfs_handle (void) } #endif /* HAVE_LIBZFS */ -#if !defined (__MINGW32__) && !defined (__CYGWIN__) -/* ZFS has similar problems to those of btrfs (see above). */ -void -grub_find_zpool_from_dir (const char *dir, char **poolname, char **poolfs) -{ - char *slash; - - *poolname = *poolfs = NULL; - -#if defined(HAVE_STRUCT_STATFS_F_FSTYPENAME) && defined(HAVE_STRUCT_STATFS_F_MNTFROMNAME) - /* FreeBSD and GNU/kFreeBSD. */ - { - struct statfs mnt; - - if (statfs (dir, &mnt) != 0) - return; - - if (strcmp (mnt.f_fstypename, "zfs") != 0) - return; - - *poolname = xstrdup (mnt.f_mntfromname); - } -#elif defined(HAVE_GETEXTMNTENT) - /* Solaris. */ - { - struct stat st; - struct extmnttab mnt; - - if (stat (dir, &st) != 0) - return; - - FILE *mnttab = fopen ("/etc/mnttab", "r"); - if (! mnttab) - return; - - while (getextmntent (mnttab, &mnt, sizeof (mnt)) == 0) - { - if (makedev (mnt.mnt_major, mnt.mnt_minor) == st.st_dev - && !strcmp (mnt.mnt_fstype, "zfs")) - { - *poolname = xstrdup (mnt.mnt_special); - break; - } - } - - fclose (mnttab); - } -#endif - - if (! *poolname) - return; - - slash = strchr (*poolname, '/'); - if (slash) - { - *slash = '\0'; - *poolfs = xstrdup (slash + 1); - } - else - *poolfs = xstrdup (""); -} -#endif - -/* This function never prints trailing slashes (so that its output - can be appended a slash unconditionally). */ -char * -grub_make_system_path_relative_to_its_root (const char *path) -{ - struct stat st; - char *p, *buf, *buf2, *buf3, *ret; - uintptr_t offset = 0; - dev_t num; - size_t len; - char *poolfs = NULL; - - /* canonicalize. */ - p = canonicalize_file_name (path); - if (p == NULL) - grub_util_error (_("failed to get canonical path of `%s'"), path); - - /* For ZFS sub-pool filesystems, could be extended to others (btrfs?). */ -#if !defined (__MINGW32__) && !defined (__CYGWIN__) - { - char *dummy; - grub_find_zpool_from_dir (p, &dummy, &poolfs); - } -#endif - - len = strlen (p) + 1; - buf = xstrdup (p); - free (p); - - if (stat (buf, &st) < 0) - grub_util_error (_("cannot stat `%s': %s"), buf, strerror (errno)); - - buf2 = xstrdup (buf); - num = st.st_dev; - - /* This loop sets offset to the number of chars of the root - directory we're inspecting. */ - while (1) - { - p = strrchr (buf, '/'); - if (p == NULL) - /* This should never happen. */ - grub_util_error ("%s", - /* TRANSLATORS: canonical pathname is the - complete one e.g. /etc/fstab. It has - to contain `/' normally, if it doesn't - we're in trouble and throw this error. */ - _("no `/' in canonical filename")); - if (p != buf) - *p = 0; - else - *++p = 0; - - if (stat (buf, &st) < 0) - grub_util_error (_("cannot stat `%s': %s"), buf, strerror (errno)); - - /* buf is another filesystem; we found it. */ - if (st.st_dev != num) - { - /* offset == 0 means path given is the mount point. - This works around special-casing of "/" in Un*x. This function never - prints trailing slashes (so that its output can be appended a slash - unconditionally). Each slash in is considered a preceding slash, and - therefore the root directory is an empty string. */ - if (offset == 0) - { - free (buf); -#ifdef __linux__ - { - char *bind; - grub_free (grub_find_root_devices_from_mountinfo (buf2, &bind)); - if (bind && bind[0] && bind[1]) - { - buf3 = bind; - goto parsedir; - } - grub_free (bind); - } -#endif - free (buf2); - if (poolfs) - return xasprintf ("/%s/@", poolfs); - return xstrdup (""); - } - else - break; - } - - offset = p - buf; - /* offset == 1 means root directory. */ - if (offset == 1) - { - /* Include leading slash. */ - offset = 0; - break; - } - } - free (buf); - buf3 = xstrdup (buf2 + offset); - buf2[offset] = 0; -#ifdef __linux__ - { - char *bind; - grub_free (grub_find_root_devices_from_mountinfo (buf2, &bind)); - if (bind && bind[0] && bind[1]) - { - char *temp = buf3; - buf3 = grub_xasprintf ("%s%s%s", bind, buf3[0] == '/' ?"":"/", buf3); - grub_free (temp); - } - grub_free (bind); - } -#endif - - free (buf2); - -#ifdef __CYGWIN__ - if (st.st_dev != (DEV_CYGDRIVE_MAJOR << 16)) - { - /* Reached some mount point not below /cygdrive. - GRUB does not know Cygwin's emulated mounts, - convert to Win32 path. */ - grub_util_info ("Cygwin path = %s\n", buf3); - char * temp = get_win32_path (buf3); - free (buf3); - buf3 = temp; - } -#endif - -#ifdef __linux__ - parsedir: -#endif - /* Remove trailing slashes, return empty string if root directory. */ - len = strlen (buf3); - while (len > 0 && buf3[len - 1] == '/') - { - buf3[len - 1] = '\0'; - len--; - } - - if (poolfs) - { - ret = xasprintf ("/%s/@%s", poolfs, buf3); - free (buf3); - } - else - ret = buf3; - - return ret; -} diff --git a/util/getroot_cygwin.c b/util/getroot_cygwin.c deleted file mode 100644 index 6407d2c1e..000000000 --- a/util/getroot_cygwin.c +++ /dev/null @@ -1,115 +0,0 @@ -/* - * 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_hurd.c b/util/getroot_hurd.c index f63f4de74..0cd29dc0e 100644 --- a/util/getroot_hurd.c +++ b/util/getroot_hurd.c @@ -52,8 +52,7 @@ #include #include - -char * +static char * grub_util_find_hurd_root_device (const char *path) { file_t file; @@ -219,3 +218,24 @@ grub_util_find_partition_start_os (const char *dev) free (path); return -1; } + +char ** +grub_guess_root_devices (const char *dir) +{ + char **os_dev = NULL; + + os_dev = xmalloc (2 * sizeof (os_dev[0])); + + /* GNU/Hurd specific function. */ + os_dev[0] = grub_util_find_hurd_root_device (dir); + + if (!os_dev[0]) + { + free (os_dev); + return 0; + } + + os_dev[1] = 0; + + return os_dev; +} diff --git a/util/getroot_os.c b/util/getroot_os.c index fee9f2100..a2ed13262 100644 --- a/util/getroot_os.c +++ b/util/getroot_os.c @@ -10,8 +10,8 @@ #include "getroot_sun.c" #elif defined(__GNU__) #include "getroot_hurd.c" -#elif defined(__CYGWIN__) -#include "getroot_cygwin.c" +#elif defined(__CYGWIN__) || defined (__MINGW32__) +#include "getroot_windows.c" #else # warning "No getroot OS-specific functions is available for your system. Device detection may not work properly." #include "getroot_basic.c" diff --git a/util/getroot_unix.c b/util/getroot_unix.c new file mode 100644 index 000000000..bdf60f040 --- /dev/null +++ b/util/getroot_unix.c @@ -0,0 +1,931 @@ +/* + * GRUB -- GRand Unified Bootloader + * Copyright (C) 1999,2000,2001,2002,2003,2006,2007,2008,2009,2010,2011 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 + +#if !defined (__MINGW32__) && !defined (__CYGWIN__) + +#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 + +#include + +#if defined(HAVE_LIBZFS) && defined(HAVE_LIBNVPAIR) +# include +# include +#endif + +#include +#include +#include +#include +#include + +#if defined(__FreeBSD__) || defined(__FreeBSD_kernel__) +# define MAJOR(dev) major(dev) +# define FLOPPY_MAJOR 2 +#endif + +#if defined (__FreeBSD__) || defined (__FreeBSD_kernel__) +#include +#endif + +#if defined(__NetBSD__) || defined(__OpenBSD__) +# include +# include /* struct disklabel */ +# include /* struct dkwedge_info */ +#include +#include +#endif /* defined(__NetBSD__) || defined(__FreeBSD__) || defined(__FreeBSD_kernel__) */ + +#if defined(__NetBSD__) || defined(__OpenBSD__) +# define MAJOR(dev) major(dev) +# ifdef HAVE_GETRAWPARTITION +# include /* getrawpartition */ +# endif /* HAVE_GETRAWPARTITION */ +#if defined(__NetBSD__) +# include +#endif +# ifndef FLOPPY_MAJOR +# define FLOPPY_MAJOR 2 +# endif /* ! FLOPPY_MAJOR */ +# ifndef RAW_FLOPPY_MAJOR +# define RAW_FLOPPY_MAJOR 9 +# endif /* ! RAW_FLOPPY_MAJOR */ +#endif /* defined(__NetBSD__) */ + +#include +#include + +static void +strip_extra_slashes (char *dir) +{ + char *p = dir; + + while ((p = strchr (p, '/')) != 0) + { + if (p[1] == '/') + { + memmove (p, p + 1, strlen (p)); + continue; + } + else if (p[1] == '\0') + { + if (p > dir) + p[0] = '\0'; + break; + } + + p++; + } +} + +static char * +xgetcwd (void) +{ + size_t size = 10; + char *path; + + path = xmalloc (size); + while (! getcwd (path, size)) + { + size <<= 1; + path = xrealloc (path, size); + } + + return path; +} + +pid_t +grub_util_exec_pipe (char **argv, int *fd) +{ + int mdadm_pipe[2]; + pid_t mdadm_pid; + + *fd = 0; + + if (pipe (mdadm_pipe) < 0) + { + grub_util_warn (_("Unable to create pipe: %s"), + strerror (errno)); + return 0; + } + mdadm_pid = fork (); + if (mdadm_pid < 0) + grub_util_error (_("Unable to fork: %s"), strerror (errno)); + else if (mdadm_pid == 0) + { + /* Child. */ + + /* Close fd's. */ + grub_util_devmapper_cleanup (); + grub_diskfilter_fini (); + + /* Ensure child is not localised. */ + setenv ("LC_ALL", "C", 1); + + close (mdadm_pipe[0]); + dup2 (mdadm_pipe[1], STDOUT_FILENO); + close (mdadm_pipe[1]); + + execvp (argv[0], argv); + exit (127); + } + else + { + close (mdadm_pipe[1]); + *fd = mdadm_pipe[0]; + return mdadm_pid; + } +} + +#if !defined (__GNU__) +char ** +grub_util_find_root_devices_from_poolname (char *poolname) +{ + char **devices = 0; + size_t ndevices = 0; + size_t devices_allocated = 0; + +#if defined(HAVE_LIBZFS) && defined(HAVE_LIBNVPAIR) + zpool_handle_t *zpool; + libzfs_handle_t *libzfs; + nvlist_t *config, *vdev_tree; + nvlist_t **children; + unsigned int nvlist_count; + unsigned int i; + char *device = 0; + + libzfs = grub_get_libzfs_handle (); + if (! libzfs) + return NULL; + + zpool = zpool_open (libzfs, poolname); + config = zpool_get_config (zpool, NULL); + + if (nvlist_lookup_nvlist (config, "vdev_tree", &vdev_tree) != 0) + error (1, errno, "nvlist_lookup_nvlist (\"vdev_tree\")"); + + if (nvlist_lookup_nvlist_array (vdev_tree, "children", &children, &nvlist_count) != 0) + error (1, errno, "nvlist_lookup_nvlist_array (\"children\")"); + assert (nvlist_count > 0); + + while (nvlist_lookup_nvlist_array (children[0], "children", + &children, &nvlist_count) == 0) + assert (nvlist_count > 0); + + for (i = 0; i < nvlist_count; i++) + { + if (nvlist_lookup_string (children[i], "path", &device) != 0) + error (1, errno, "nvlist_lookup_string (\"path\")"); + + struct stat st; + if (stat (device, &st) == 0) + { +#ifdef __sun__ + if (grub_memcmp (device, "/dev/dsk/", sizeof ("/dev/dsk/") - 1) + == 0) + device = xasprintf ("/dev/rdsk/%s", + device + sizeof ("/dev/dsk/") - 1); + else if (grub_memcmp (device, "/devices", sizeof ("/devices") - 1) + == 0 + && grub_memcmp (device + strlen (device) - 4, + ",raw", 4) != 0) + device = xasprintf ("%s,raw", device); + else +#endif + device = xstrdup (device); + if (ndevices >= devices_allocated) + { + devices_allocated = 2 * (devices_allocated + 8); + devices = xrealloc (devices, sizeof (devices[0]) + * devices_allocated); + } + devices[ndevices++] = device; + } + + device = NULL; + } + + zpool_close (zpool); +#else + FILE *fp; + int ret; + char *line; + size_t len; + int st; + + char name[PATH_MAX + 1], state[257], readlen[257], writelen[257]; + char cksum[257], notes[257]; + unsigned int dummy; + char *argv[4]; + pid_t pid; + int fd; + + /* execvp has inconvenient types, hence the casts. None of these + strings will actually be modified. */ + argv[0] = (char *) "zpool"; + argv[1] = (char *) "status"; + argv[2] = (char *) poolname; + argv[3] = NULL; + + pid = grub_util_exec_pipe (argv, &fd); + if (!pid) + return NULL; + + fp = fdopen (fd, "r"); + if (!fp) + { + grub_util_warn (_("Unable to open stream from %s: %s"), + "zpool", strerror (errno)); + goto out; + } + + st = 0; + while (1) + { + line = NULL; + ret = getline (&line, &len, fp); + if (ret == -1) + break; + + if (sscanf (line, " %s %256s %256s %256s %256s %256s", + name, state, readlen, writelen, cksum, notes) >= 5) + switch (st) + { + case 0: + if (!strcmp (name, "NAME") + && !strcmp (state, "STATE") + && !strcmp (readlen, "READ") + && !strcmp (writelen, "WRITE") + && !strcmp (cksum, "CKSUM")) + st++; + break; + case 1: + { + char *ptr = line; + while (1) + { + if (strncmp (ptr, poolname, strlen (poolname)) == 0 + && grub_isspace(ptr[strlen (poolname)])) + st++; + if (!grub_isspace (*ptr)) + break; + ptr++; + } + } + break; + case 2: + if (strcmp (name, "mirror") && !sscanf (name, "mirror-%u", &dummy) + && !sscanf (name, "raidz%u", &dummy) + && !sscanf (name, "raidz1%u", &dummy) + && !sscanf (name, "raidz2%u", &dummy) + && !sscanf (name, "raidz3%u", &dummy) + && !strcmp (state, "ONLINE")) + { + if (ndevices >= devices_allocated) + { + devices_allocated = 2 * (devices_allocated + 8); + devices = xrealloc (devices, sizeof (devices[0]) + * devices_allocated); + } + if (name[0] == '/') + devices[ndevices++] = xstrdup (name); + else + devices[ndevices++] = xasprintf ("/dev/%s", name); + } + break; + } + + free (line); + } + + out: + close (fd); + waitpid (pid, NULL, 0); +#endif + if (devices) + { + if (ndevices >= devices_allocated) + { + devices_allocated = 2 * (devices_allocated + 8); + devices = xrealloc (devices, sizeof (devices[0]) + * devices_allocated); + } + devices[ndevices++] = 0; + } + return devices; +} + +static char ** +find_root_devices_from_libzfs (const char *dir) +{ + char **devices = NULL; + char *poolname; + char *poolfs; + + grub_find_zpool_from_dir (dir, &poolname, &poolfs); + if (! poolname) + return NULL; + + devices = grub_util_find_root_devices_from_poolname (poolname); + + free (poolname); + if (poolfs) + free (poolfs); + + return devices; +} + +char * +grub_find_device (const char *dir, dev_t dev) +{ + DIR *dp; + char *saved_cwd; + struct dirent *ent; + + if (! dir) + dir = "/dev"; + + dp = opendir (dir); + if (! dp) + return 0; + + saved_cwd = xgetcwd (); + + grub_util_info ("changing current directory to %s", dir); + if (chdir (dir) < 0) + { + free (saved_cwd); + closedir (dp); + return 0; + } + + while ((ent = readdir (dp)) != 0) + { + struct stat st; + + /* Avoid: + - dotfiles (like "/dev/.tmp.md0") since they could be duplicates. + - dotdirs (like "/dev/.static") since they could contain duplicates. */ + if (ent->d_name[0] == '.') + continue; + + if (lstat (ent->d_name, &st) < 0) + /* Ignore any error. */ + continue; + + if (S_ISLNK (st.st_mode)) { +#ifdef __linux__ + if (strcmp (dir, "mapper") == 0 || strcmp (dir, "/dev/mapper") == 0) { + /* Follow symbolic links under /dev/mapper/; the canonical name + may be something like /dev/dm-0, but the names under + /dev/mapper/ are more human-readable and so we prefer them if + we can get them. */ + if (stat (ent->d_name, &st) < 0) + continue; + } else +#endif /* __linux__ */ + /* Don't follow other symbolic links. */ + continue; + } + + if (S_ISDIR (st.st_mode)) + { + /* Find it recursively. */ + char *res; + + res = grub_find_device (ent->d_name, dev); + + if (res) + { + if (chdir (saved_cwd) < 0) + grub_util_error ("%s", + _("cannot restore the original directory")); + + free (saved_cwd); + closedir (dp); + return res; + } + } + +#if defined(__FreeBSD__) || defined(__FreeBSD_kernel__) || defined(__APPLE__) + if (S_ISCHR (st.st_mode) && st.st_rdev == dev) +#else + if (S_ISBLK (st.st_mode) && st.st_rdev == dev) +#endif + { +#ifdef __linux__ + /* Skip device names like /dev/dm-0, which are short-hand aliases + to more descriptive device names, e.g. those under /dev/mapper */ + if (ent->d_name[0] == 'd' && + ent->d_name[1] == 'm' && + ent->d_name[2] == '-' && + ent->d_name[3] >= '0' && + ent->d_name[3] <= '9') + continue; +#endif + + /* Found! */ + char *res; + char *cwd; +#if defined(__NetBSD__) || defined(__OpenBSD__) + /* Convert this block device to its character (raw) device. */ + const char *template = "%s/r%s"; +#else + /* Keep the device name as it is. */ + const char *template = "%s/%s"; +#endif + + cwd = xgetcwd (); + res = xmalloc (strlen (cwd) + strlen (ent->d_name) + 3); + sprintf (res, template, cwd, ent->d_name); + strip_extra_slashes (res); + free (cwd); + + /* /dev/root is not a real block device keep looking, takes care + of situation where root filesystem is on the same partition as + grub files */ + + if (strcmp(res, "/dev/root") == 0) + { + free (res); + continue; + } + + if (chdir (saved_cwd) < 0) + grub_util_error ("%s", _("cannot restore the original directory")); + + free (saved_cwd); + closedir (dp); + return res; + } + } + + if (chdir (saved_cwd) < 0) + grub_util_error ("%s", _("cannot restore the original directory")); + + free (saved_cwd); + closedir (dp); + return 0; +} + +char ** +grub_guess_root_devices (const char *dir) +{ + char **os_dev = NULL; + struct stat st; + dev_t dev; + +#ifdef __linux__ + if (!os_dev) + os_dev = grub_find_root_devices_from_mountinfo (dir, NULL); +#endif /* __linux__ */ + + if (!os_dev) + os_dev = find_root_devices_from_libzfs (dir); + + if (os_dev) + { + char **cur; + for (cur = os_dev; *cur; cur++) + { + char *tmp = *cur; + int root, dm; + if (strcmp (*cur, "/dev/root") == 0 + || strncmp (*cur, "/dev/dm-", sizeof ("/dev/dm-") - 1) == 0) + *cur = tmp; + else + { + *cur = canonicalize_file_name (tmp); + if (*cur == NULL) + grub_util_error (_("failed to get canonical path of `%s'"), tmp); + free (tmp); + } + root = (strcmp (*cur, "/dev/root") == 0); + dm = (strncmp (*cur, "/dev/dm-", sizeof ("/dev/dm-") - 1) == 0); + if (!dm && !root) + continue; + if (stat (*cur, &st) < 0) + break; + free (*cur); + dev = st.st_rdev; + *cur = grub_find_device (dm ? "/dev/mapper" : "/dev", dev); + } + if (!*cur) + return os_dev; + for (cur = os_dev; *cur; cur++) + free (*cur); + free (os_dev); + os_dev = 0; + } + + if (stat (dir, &st) < 0) + grub_util_error (_("cannot stat `%s': %s"), dir, strerror (errno)); + + dev = st.st_dev; + + os_dev = xmalloc (2 * sizeof (os_dev[0])); + + /* This might be truly slow, but is there any better way? */ + os_dev[0] = grub_find_device ("/dev", dev); + + if (!os_dev[0]) + { + free (os_dev); + return 0; + } + + os_dev[1] = 0; + + return os_dev; +} + +#endif + +void +grub_util_pull_lvm_by_command (const char *os_dev) +{ + char *argv[8]; + int fd; + pid_t pid; + FILE *mdadm; + char *buf = NULL; + size_t len = 0; + char *vgname = NULL; + const char *iptr; + char *optr; + char *vgid = NULL; + grub_size_t vgidlen = 0; + + vgid = grub_util_get_vg_uuid (os_dev); + if (vgid) + vgidlen = grub_strlen (vgid); + + if (!vgid) + { + if (strncmp (os_dev, LVM_DEV_MAPPER_STRING, + sizeof (LVM_DEV_MAPPER_STRING) - 1) + != 0) + return; + + vgname = xmalloc (strlen (os_dev + sizeof (LVM_DEV_MAPPER_STRING) - 1) + 1); + for (iptr = os_dev + sizeof (LVM_DEV_MAPPER_STRING) - 1, optr = vgname; *iptr; ) + if (*iptr != '-') + *optr++ = *iptr++; + else if (iptr[0] == '-' && iptr[1] == '-') + { + iptr += 2; + *optr++ = '-'; + } + else + break; + *optr = '\0'; + } + + /* execvp has inconvenient types, hence the casts. None of these + strings will actually be modified. */ + /* by default PV name is left aligned in 10 character field, meaning that + we do not know where name ends. Using dummy --separator disables + alignment. We have a single field, so separator itself is not output */ + argv[0] = (char *) "vgs"; + argv[1] = (char *) "--options"; + if (vgid) + argv[2] = (char *) "vg_uuid,pv_name"; + else + argv[2] = (char *) "pv_name"; + argv[3] = (char *) "--noheadings"; + argv[4] = (char *) "--separator"; + argv[5] = (char *) ":"; + argv[6] = vgname; + argv[7] = NULL; + + pid = grub_util_exec_pipe (argv, &fd); + free (vgname); + + if (!pid) + return; + + /* Parent. Read mdadm's output. */ + mdadm = fdopen (fd, "r"); + if (! mdadm) + { + grub_util_warn (_("Unable to open stream from %s: %s"), + "vgs", strerror (errno)); + goto out; + } + + while (getline (&buf, &len, mdadm) > 0) + { + char *ptr; + /* LVM adds two spaces as standard prefix */ + for (ptr = buf; ptr < buf + 2 && *ptr == ' '; ptr++); + + if (vgid && (grub_strncmp (vgid, ptr, vgidlen) != 0 + || ptr[vgidlen] != ':')) + continue; + if (vgid) + ptr += vgidlen + 1; + if (*ptr == '\0') + continue; + *(ptr + strlen (ptr) - 1) = '\0'; + grub_util_pull_device (ptr); + } + +out: + close (fd); + waitpid (pid, NULL, 0); + free (buf); +} + +/* ZFS has similar problems to those of btrfs (see above). */ +void +grub_find_zpool_from_dir (const char *dir, char **poolname, char **poolfs) +{ + char *slash; + + *poolname = *poolfs = NULL; + +#if defined(HAVE_STRUCT_STATFS_F_FSTYPENAME) && defined(HAVE_STRUCT_STATFS_F_MNTFROMNAME) + /* FreeBSD and GNU/kFreeBSD. */ + { + struct statfs mnt; + + if (statfs (dir, &mnt) != 0) + return; + + if (strcmp (mnt.f_fstypename, "zfs") != 0) + return; + + *poolname = xstrdup (mnt.f_mntfromname); + } +#elif defined(HAVE_GETEXTMNTENT) + /* Solaris. */ + { + struct stat st; + struct extmnttab mnt; + + if (stat (dir, &st) != 0) + return; + + FILE *mnttab = fopen ("/etc/mnttab", "r"); + if (! mnttab) + return; + + while (getextmntent (mnttab, &mnt, sizeof (mnt)) == 0) + { + if (makedev (mnt.mnt_major, mnt.mnt_minor) == st.st_dev + && !strcmp (mnt.mnt_fstype, "zfs")) + { + *poolname = xstrdup (mnt.mnt_special); + break; + } + } + + fclose (mnttab); + } +#endif + + if (! *poolname) + return; + + slash = strchr (*poolname, '/'); + if (slash) + { + *slash = '\0'; + *poolfs = xstrdup (slash + 1); + } + else + *poolfs = xstrdup (""); +} + +/* This function never prints trailing slashes (so that its output + can be appended a slash unconditionally). */ +char * +grub_make_system_path_relative_to_its_root (const char *path) +{ + struct stat st; + char *p, *buf, *buf2, *buf3, *ret; + uintptr_t offset = 0; + dev_t num; + size_t len; + char *poolfs = NULL; + + /* canonicalize. */ + p = canonicalize_file_name (path); + if (p == NULL) + grub_util_error (_("failed to get canonical path of `%s'"), path); + + /* For ZFS sub-pool filesystems, could be extended to others (btrfs?). */ + { + char *dummy; + grub_find_zpool_from_dir (p, &dummy, &poolfs); + } + + len = strlen (p) + 1; + buf = xstrdup (p); + free (p); + + if (stat (buf, &st) < 0) + grub_util_error (_("cannot stat `%s': %s"), buf, strerror (errno)); + + buf2 = xstrdup (buf); + num = st.st_dev; + + /* This loop sets offset to the number of chars of the root + directory we're inspecting. */ + while (1) + { + p = strrchr (buf, '/'); + if (p == NULL) + /* This should never happen. */ + grub_util_error ("%s", + /* TRANSLATORS: canonical pathname is the + complete one e.g. /etc/fstab. It has + to contain `/' normally, if it doesn't + we're in trouble and throw this error. */ + _("no `/' in canonical filename")); + if (p != buf) + *p = 0; + else + *++p = 0; + + if (stat (buf, &st) < 0) + grub_util_error (_("cannot stat `%s': %s"), buf, strerror (errno)); + + /* buf is another filesystem; we found it. */ + if (st.st_dev != num) + { + /* offset == 0 means path given is the mount point. + This works around special-casing of "/" in Un*x. This function never + prints trailing slashes (so that its output can be appended a slash + unconditionally). Each slash in is considered a preceding slash, and + therefore the root directory is an empty string. */ + if (offset == 0) + { + free (buf); +#ifdef __linux__ + { + char *bind; + grub_free (grub_find_root_devices_from_mountinfo (buf2, &bind)); + if (bind && bind[0] && bind[1]) + { + buf3 = bind; + goto parsedir; + } + grub_free (bind); + } +#endif + free (buf2); + if (poolfs) + return xasprintf ("/%s/@", poolfs); + return xstrdup (""); + } + else + break; + } + + offset = p - buf; + /* offset == 1 means root directory. */ + if (offset == 1) + { + /* Include leading slash. */ + offset = 0; + break; + } + } + free (buf); + buf3 = xstrdup (buf2 + offset); + buf2[offset] = 0; +#ifdef __linux__ + { + char *bind; + grub_free (grub_find_root_devices_from_mountinfo (buf2, &bind)); + if (bind && bind[0] && bind[1]) + { + char *temp = buf3; + buf3 = grub_xasprintf ("%s%s%s", bind, buf3[0] == '/' ?"":"/", buf3); + grub_free (temp); + } + grub_free (bind); + } +#endif + + free (buf2); + +#ifdef __linux__ + parsedir: +#endif + /* Remove trailing slashes, return empty string if root directory. */ + len = strlen (buf3); + while (len > 0 && buf3[len - 1] == '/') + { + buf3[len - 1] = '\0'; + len--; + } + + if (poolfs) + { + ret = xasprintf ("/%s/@%s", poolfs, buf3); + free (buf3); + } + else + ret = buf3; + + return ret; +} + +int +grub_util_biosdisk_is_floppy (grub_disk_t disk) +{ + struct stat st; + int fd; + const char *dname; + + dname = grub_util_biosdisk_get_osdev (disk); + + if (!dname) + return 0; + + fd = open (dname, O_RDONLY); + /* Shouldn't happen. */ + if (fd == -1) + return 0; + + /* Shouldn't happen either. */ + if (fstat (fd, &st) < 0) + { + close (fd); + return 0; + } + + close (fd); + +#if defined(__NetBSD__) + if (major(st.st_rdev) == RAW_FLOPPY_MAJOR) + return 1; +#endif + +#if defined(FLOPPY_MAJOR) + if (major(st.st_rdev) == FLOPPY_MAJOR) +#else + /* Some kernels (e.g. kFreeBSD) don't have a static major number + for floppies, but they still use a "fd[0-9]" pathname. */ + if (dname[5] == 'f' + && dname[6] == 'd' + && dname[7] >= '0' + && dname[7] <= '9') +#endif + return 1; + + return 0; +} + +#endif diff --git a/util/getroot_windows.c b/util/getroot_windows.c new file mode 100644 index 000000000..b023af534 --- /dev/null +++ b/util/getroot_windows.c @@ -0,0 +1,346 @@ +/* + * 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_tchar_to_utf8 (LPCTSTR in) +{ +#if SIZEOF_TCHAR == 1 + return xstrdup (path); +#elif SIZEOF_TCHAR == 2 + size_t ssz; + for (ssz = 0; in[ssz]; ssz++); + + size_t tsz = GRUB_MAX_UTF8_PER_UTF16 * ssz + 1; + grub_uint8_t *ret = xmalloc (tsz); + *grub_utf16_to_utf8 (ret, in, ssz) = '\0'; + return (char *) ret; +#else +#error "Unsupported TCHAR size" +#endif +} + +#if SIZEOF_TCHAR == 1 +#define tcsnicmp strncasecmp +#define tclen strlen +#elif SIZEOF_TCHAR == 2 +#define tcsnicmp wcsnicmp +#define tclen wcslen +#endif + +char ** +grub_guess_root_devices (const char *dir) +{ + char **os_dev = NULL; + TCHAR *dirwindows, *mntpointwindows; + TCHAR *ptr; + TCHAR volumename[100]; + size_t mntpointwindows_sz; + + dirwindows = grub_util_get_windows_path (dir); + if (!dirwindows) + return 0; + + mntpointwindows_sz = strlen (dir) * 2 + 1; + mntpointwindows = xmalloc ((mntpointwindows_sz + 1) * sizeof (mntpointwindows[0])); + + if (!GetVolumePathName (dirwindows, + mntpointwindows, + mntpointwindows_sz)) + { + free (dirwindows); + free (mntpointwindows); + return 0; + } + + if (!mntpointwindows[0]) + { + free (dirwindows); + free (mntpointwindows); + return 0; + } + + for (ptr = mntpointwindows; *ptr; ptr++); + if (*(ptr - 1) != '\\') + { + *ptr = '\\'; + *(ptr + 1) = '\0'; + } + + if (!GetVolumeNameForVolumeMountPoint (mntpointwindows, + volumename, + ARRAY_SIZE (volumename))) + { + free (dirwindows); + free (mntpointwindows); + return 0; + } + os_dev = xmalloc (2 * sizeof (os_dev[0])); + + for (ptr = volumename; *ptr; ptr++); + while (ptr > volumename && *(ptr - 1) == '\\') + *--ptr = '\0'; + + os_dev[0] = grub_util_tchar_to_utf8 (volumename); + free (dirwindows); + free (mntpointwindows); + + if (!os_dev[0]) + { + free (os_dev); + return 0; + } + + os_dev[1] = 0; + + return os_dev; +} + +char * +grub_util_part_to_disk (const char *os_dev, + struct stat *st __attribute__ ((unused)), + int *is_part) +{ + HANDLE hd; + LPTSTR name = grub_util_get_windows_path (os_dev); + VOLUME_DISK_EXTENTS exts; + DWORD extsbytes; + char *ret; + + if (((name[0] == '/') || (name[0] == '\\')) && + ((name[1] == '/') || (name[1] == '\\')) && + ((name[2] == '.') || (name[2] == '?')) && + ((name[3] == '/') || (name[3] == '\\')) + && (tcsnicmp (name + 4, TEXT("PhysicalDrive"), sizeof ("PhysicalDrive") - 1) == 0 + || tcsnicmp (name + 4, TEXT("Harddisk"), sizeof ("Harddisk") - 1) == 0 + || ((name[4] == 'A' || name[4] == 'a' || name[4] == 'B' || name[4] == 'b') + && name[5] == ':' && name[6] == '\0'))) + { + grub_util_info ("Matches full disk pattern"); + ret = grub_util_tchar_to_utf8 (name); + free (name); + return ret; + } + + hd = CreateFile (name, GENERIC_READ, FILE_SHARE_READ | FILE_SHARE_WRITE, + 0, OPEN_EXISTING, 0, 0); + if (hd == INVALID_HANDLE_VALUE) + { + grub_util_info ("CreateFile failed"); + ret = grub_util_tchar_to_utf8 (name); + free (name); + return ret; + } + + if (!DeviceIoControl(hd, IOCTL_VOLUME_GET_VOLUME_DISK_EXTENTS, + NULL, 0, &exts, sizeof (exts), &extsbytes, NULL)) + { + grub_util_info ("IOCTL_VOLUME_GET_VOLUME_DISK_EXTENTS failed"); + ret = grub_util_tchar_to_utf8 (name); + CloseHandle (hd); + free (name); + return ret; + } + + CloseHandle (hd); + + *is_part = 1; + free (name); + return xasprintf ("\\\\?\\PhysicalDrive%lu", (unsigned long) exts.Extents[0].DiskNumber); +} + +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 *os_dev) +{ + HANDLE hd; + LPTSTR name = grub_util_get_windows_path (os_dev); + VOLUME_DISK_EXTENTS exts; + DWORD extsbytes; + char *ret; + + if (((name[0] == '/') || (name[0] == '\\')) && + ((name[1] == '/') || (name[1] == '\\')) && + ((name[2] == '.') || (name[2] == '?')) && + ((name[3] == '/') || (name[3] == '\\')) + && (tcsnicmp (name + 4, TEXT("PhysicalDrive"), sizeof ("PhysicalDrive") - 1) == 0 + || tcsnicmp (name + 4, TEXT("Harddisk"), sizeof ("Harddisk") - 1) == 0 + || ((name[4] == 'A' || name[4] == 'a' || name[4] == 'B' || name[4] == 'b') + && name[5] == ':' && name[6] == '\0'))) + { + ret = grub_util_tchar_to_utf8 (name); + free (name); + return 0; + } + + hd = CreateFile (name, GENERIC_READ, FILE_SHARE_READ | FILE_SHARE_WRITE, + 0, OPEN_EXISTING, 0, 0); + if (hd == INVALID_HANDLE_VALUE) + { + ret = grub_util_tchar_to_utf8 (name); + free (name); + return 0; + } + + if (!DeviceIoControl(hd, IOCTL_VOLUME_GET_VOLUME_DISK_EXTENTS, + NULL, 0, &exts, sizeof (exts), &extsbytes, NULL)) + { + ret = grub_util_tchar_to_utf8 (name); + CloseHandle (hd); + free (name); + return 0; + } + + CloseHandle (hd); + free (name); + return exts.Extents[0].StartingOffset.QuadPart / 512; +} + +char * +grub_make_system_path_relative_to_its_root (const char *path) +{ + TCHAR *dirwindows, *mntpointwindows; + TCHAR *ptr; + TCHAR volumename[100]; + size_t mntpointwindows_sz; + size_t offset, flen; + TCHAR *ret; + char *cret; + + dirwindows = grub_util_get_windows_path (path); + if (!dirwindows) + return xstrdup (path); + + mntpointwindows_sz = strlen (path) * 2 + 1; + mntpointwindows = xmalloc ((mntpointwindows_sz + 1) * sizeof (mntpointwindows[0])); + + if (!GetVolumePathName (dirwindows, + mntpointwindows, + mntpointwindows_sz)) + { + offset = 0; + if (dirwindows[0] && dirwindows[1] == ':') + offset = 2; + } + offset = tclen (mntpointwindows); + free (mntpointwindows); + flen = tclen (dirwindows); + if (offset > flen) + { + offset = 0; + if (dirwindows[0] && dirwindows[1] == ':') + offset = 2; + } + ret = xmalloc (sizeof (ret[0]) * (flen - offset + 2)); + if (dirwindows[offset] != '\\' + && dirwindows[offset] != '/' + && dirwindows[offset]) + { + ret[0] = '\\'; + memcpy (ret + 1, dirwindows + offset, (flen - offset + 1) * sizeof (ret[0])); + } + else + memcpy (ret, dirwindows + offset, (flen - offset + 1) * sizeof (ret[0])); + + free (dirwindows); + + for (ptr = ret; *ptr; ptr++) + if (*ptr == '\\') + *ptr = '/'; + + cret = grub_util_tchar_to_utf8 (ret); + free (ret); + + return cret; +} + +int +grub_util_biosdisk_is_floppy (grub_disk_t disk) +{ + int ret; + const char *dname; + LPTSTR name; + + dname = grub_util_biosdisk_get_osdev (disk); + + if (!dname) + return 0; + + name = grub_util_get_windows_path (dname); + + ret = (((name[0] == '/') || (name[0] == '\\')) && + ((name[1] == '/') || (name[1] == '\\')) && + ((name[2] == '.') || (name[2] == '?')) && + ((name[3] == '/') || (name[3] == '\\')) + && (name[4] == 'A' || name[4] == 'a' || name[4] == 'B' || name[4] == 'b') + && name[5] == ':' && name[6] == '\0'); + free (name); + + return ret; +}