]> git.ipfire.org Git - thirdparty/grub.git/commitdiff
Split out blocklist retrieving from setup.c to
authorVladimir 'phcoder' Serbinenko <phcoder@gmail.com>
Tue, 15 Oct 2013 15:02:26 +0000 (17:02 +0200)
committerVladimir 'phcoder' Serbinenko <phcoder@gmail.com>
Tue, 15 Oct 2013 15:02:26 +0000 (17:02 +0200)
grub-core/osdep/blocklist.c and add windows implementation since
generic version doesn't work on NTFS on Windows due to aggressive
unflushable cache.

13 files changed:
ChangeLog
Makefile.util.def
grub-core/fs/fat.c
grub-core/fs/udf.c
grub-core/osdep/blocklist.c [new file with mode: 0644]
grub-core/osdep/generic/blocklist.c [new file with mode: 0644]
grub-core/osdep/linux/blocklist.c [new file with mode: 0644]
grub-core/osdep/windows/blocklist.c [new file with mode: 0644]
include/grub/exfat.h [new file with mode: 0644]
include/grub/fat.h
include/grub/udf.h [new file with mode: 0644]
include/grub/util/install.h
util/setup.c

index 7e76edb2e4666a4d9d8db9b5f9d797455d8d79f3..92ef4b0b45aab01aa1791aaa501dc08eeb7be33b 100644 (file)
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,10 @@
+2013-10-15  Vladimir Serbinenko  <phcoder@gmail.com>
+
+       Split out blocklist retrieving from setup.c to
+       grub-core/osdep/blocklist.c and add windows implementation since
+       generic version doesn't work on NTFS on Windows due to aggressive
+       unflushable cache.
+
 2013-10-15  Vladimir Serbinenko  <phcoder@gmail.com>
 
        Split grub-setup.c into frontend (grub-setup.c) and backend (setup.c)
index 81bbb23ff04067cb44bcb0c6b932a95aded23742..0e7974ffe3e0bce8467922ae8e10574cc8426829 100644 (file)
@@ -320,6 +320,10 @@ program = {
   common = util/setup_bios.c;
   common = grub-core/kern/emu/argp_common.c;
   common = grub-core/lib/reed_solomon.c;
+  common = grub-core/osdep/blocklist.c;
+  extra_dist = grub-core/osdep/generic/blocklist.c;
+  extra_dist = grub-core/osdep/linux/blocklist.c;
+  extra_dist = grub-core/osdep/windows/blocklist.c;
   common = grub-core/osdep/init.c;
 
   ldadd = libgrubmods.a;
@@ -339,6 +343,7 @@ program = {
   common = grub-core/kern/emu/argp_common.c;
   common = grub-core/lib/reed_solomon.c;
   common = grub-core/osdep/ofpath.c;
+  common = grub-core/osdep/blocklist.c;
   common = grub-core/osdep/init.c;
 
   ldadd = libgrubmods.a;
index a26a9edf8697166a179c3c6b623d627327b777f1..e84f6c6722c04ea26ee3bd4b1fbc7d332c7a1381 100644 (file)
 #include <grub/err.h>
 #include <grub/dl.h>
 #include <grub/charset.h>
+#ifndef MODE_EXFAT
 #include <grub/fat.h>
+#else
+#include <grub/exfat.h>
+#endif
 #include <grub/i18n.h>
 
 GRUB_MOD_LICENSE ("GPLv3+");
@@ -60,27 +64,6 @@ enum
   };
 
 #ifdef MODE_EXFAT
-struct grub_exfat_bpb
-{
-  grub_uint8_t jmp_boot[3];
-  grub_uint8_t oem_name[8];
-  grub_uint8_t mbz[53];
-  grub_uint64_t num_hidden_sectors;
-  grub_uint64_t num_total_sectors;
-  grub_uint32_t num_reserved_sectors;
-  grub_uint32_t sectors_per_fat;
-  grub_uint32_t cluster_offset;
-  grub_uint32_t cluster_count;
-  grub_uint32_t root_cluster;
-  grub_uint32_t num_serial;
-  grub_uint16_t fs_revision;
-  grub_uint16_t volume_flags;
-  grub_uint8_t bytes_per_sector_shift;
-  grub_uint8_t sectors_per_cluster_shift;
-  grub_uint8_t num_fats;
-  grub_uint8_t num_ph_drive;
-  grub_uint8_t reserved[8];
-} __attribute__ ((packed));
 typedef struct grub_exfat_bpb grub_current_fat_bpb_t;
 #else
 typedef struct grub_fat_bpb grub_current_fat_bpb_t;
@@ -1208,6 +1191,29 @@ grub_fat_uuid (grub_device_t device, char **uuid)
   return grub_errno;
 }
 
+#ifdef GRUB_UTIL
+#ifndef MODE_EXFAT
+grub_disk_addr_t
+grub_fat_get_cluster_sector (grub_disk_t disk, grub_uint64_t *sec_per_lcn)
+#else
+grub_disk_addr_t
+  grub_exfat_get_cluster_sector (grub_disk_t disk, grub_uint64_t *sec_per_lcn)
+#endif
+{
+  grub_disk_addr_t ret;
+  struct grub_fat_data *data;
+  data = grub_fat_mount (disk);
+  if (!data)
+    return 0;
+  ret = data->cluster_sector;
+
+  *sec_per_lcn = 1ULL << data->cluster_bits;
+
+  grub_free (data);
+  return ret;
+}
+#endif
+
 static struct grub_fs grub_fat_fs =
   {
 #ifdef MODE_EXFAT
index 405935efbb7ee29ede03a2095cb777a67dc151d8..8f3305367aab19f73697120d16f4fe4118bc705f 100644 (file)
@@ -27,6 +27,7 @@
 #include <grub/fshelp.h>
 #include <grub/charset.h>
 #include <grub/datetime.h>
+#include <grub/udf.h>
 
 GRUB_MOD_LICENSE ("GPLv3+");
 
@@ -800,6 +801,24 @@ fail:
   return 0;
 }
 
+#ifdef GRUB_UTIL
+grub_disk_addr_t
+grub_udf_get_cluster_sector (grub_disk_t disk, grub_uint64_t *sec_per_lcn)
+{
+  grub_disk_addr_t ret;
+  static struct grub_udf_data *data;
+
+  data = grub_udf_mount (disk);
+  if (!data)
+    return 0;
+
+  ret = U32 (data->pds[data->pms[0]->type1.part_num].start);
+  *sec_per_lcn = 1ULL << data->lbshift;
+  grub_free (data);
+  return ret;
+}
+#endif
+
 static char *
 read_string (const grub_uint8_t *raw, grub_size_t sz, char *outbuf)
 {
diff --git a/grub-core/osdep/blocklist.c b/grub-core/osdep/blocklist.c
new file mode 100644 (file)
index 0000000..1f58b0f
--- /dev/null
@@ -0,0 +1,7 @@
+#ifdef __linux__
+#include "linux/blocklist.c"
+#elif defined (__MINGW32__) || defined (__CYGWIN__)
+#include "windows/blocklist.c"
+#else
+#include "generic/blocklist.c"
+#endif
diff --git a/grub-core/osdep/generic/blocklist.c b/grub-core/osdep/generic/blocklist.c
new file mode 100644 (file)
index 0000000..623243e
--- /dev/null
@@ -0,0 +1,132 @@
+/* grub-setup.c - make GRUB usable */
+/*
+ *  GRUB  --  GRand Unified Bootloader
+ *  Copyright (C) 1999,2000,2001,2002,2003,2004,2005,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 <http://www.gnu.org/licenses/>.
+ */
+
+#include <config.h>
+
+#include <grub/disk.h>
+#include <grub/file.h>
+#include <grub/partition.h>
+#include <grub/util/misc.h>
+#include <grub/util/install.h>
+#include <grub/emu/hostdisk.h>
+
+#define MAX_TRIES      5
+
+void
+grub_install_get_blocklist (grub_device_t root_dev,
+                           const char *core_path, const char *core_img,
+                           size_t core_size,
+                           void (*callback) (grub_disk_addr_t sector,
+                                             unsigned offset,
+                                             unsigned length,
+                                             void *data),
+                           void *hook_data)
+{
+  int i;
+  char *tmp_img;
+  char *core_path_dev;
+
+  core_path_dev = grub_make_system_path_relative_to_its_root (core_path);
+
+  /* Make sure that GRUB reads the identical image as the OS.  */
+  tmp_img = xmalloc (core_size);
+
+  for (i = 0; i < MAX_TRIES; i++)
+    {
+      grub_file_t file;
+
+      grub_util_info ((i == 0) ? _("attempting to read the core image `%s' from GRUB")
+                     : _("attempting to read the core image `%s' from GRUB again"),
+                     core_path_dev);
+
+      grub_disk_cache_invalidate_all ();
+
+      grub_file_filter_disable_compression ();
+      file = grub_file_open (core_path_dev);
+      if (file)
+       {
+         if (grub_file_size (file) != core_size)
+           grub_util_info ("succeeded in opening the core image but the size is different (%d != %d)",
+                           (int) grub_file_size (file), (int) core_size);
+         else if (grub_file_read (file, tmp_img, core_size)
+                  != (grub_ssize_t) core_size)
+           grub_util_info ("succeeded in opening the core image but cannot read %d bytes",
+                           (int) core_size);
+         else if (memcmp (core_img, tmp_img, core_size) != 0)
+           {
+#if 0
+             FILE *dump;
+             FILE *dump2;
+
+             dump = fopen ("dump.img", "wb");
+             if (dump)
+               {
+                 fwrite (tmp_img, 1, core_size, dump);
+                 fclose (dump);
+               }
+
+             dump2 = fopen ("dump2.img", "wb");
+             if (dump2)
+               {
+                 fwrite (core_img, 1, core_size, dump2);
+                 fclose (dump2);
+               }
+
+#endif
+             grub_util_info ("succeeded in opening the core image but the data is different");
+           }
+         else
+           {
+             grub_file_close (file);
+             break;
+           }
+
+         grub_file_close (file);
+       }
+      else
+       grub_util_info ("couldn't open the core image");
+
+      if (grub_errno)
+       grub_util_info ("error message = %s", grub_errmsg);
+
+      grub_errno = GRUB_ERR_NONE;
+      grub_util_biosdisk_flush (root_dev->disk);
+      sleep (1);
+    }
+
+  if (i == MAX_TRIES)
+    grub_util_error (_("cannot read `%s' correctly"), core_path_dev);
+
+  grub_file_t file;
+  /* Now read the core image to determine where the sectors are.  */
+  grub_file_filter_disable_compression ();
+  file = grub_file_open (core_path_dev);
+  if (! file)
+    grub_util_error ("%s", grub_errmsg);
+
+  file->read_hook = callback;
+  file->read_hook_data = hook_data;
+  if (grub_file_read (file, tmp_img, core_size) != (grub_ssize_t) core_size)
+    grub_util_error ("%s", _("failed to read the sectors of the core image"));
+
+  grub_file_close (file);
+  free (tmp_img);
+
+  free (core_path_dev);
+}
diff --git a/grub-core/osdep/linux/blocklist.c b/grub-core/osdep/linux/blocklist.c
new file mode 100644 (file)
index 0000000..b8aa2bf
--- /dev/null
@@ -0,0 +1,144 @@
+/* grub-setup.c - make GRUB usable */
+/*
+ *  GRUB  --  GRand Unified Bootloader
+ *  Copyright (C) 1999,2000,2001,2002,2003,2004,2005,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 <http://www.gnu.org/licenses/>.
+ */
+
+#include <config.h>
+
+#include <stdio.h>
+#include <unistd.h>
+#include <string.h>
+#include <stdlib.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <dirent.h>
+#include <assert.h>
+#include <sys/ioctl.h>
+#include <linux/fs.h>
+#include <linux/fiemap.h>
+
+#include <grub/disk.h>
+#include <grub/partition.h>
+#include <grub/util/misc.h>
+#include <grub/util/install.h>
+#include <errno.h>
+
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+
+void
+grub_install_get_blocklist (grub_device_t root_dev,
+                           const char *core_path,
+                           const char *core_img __attribute__ ((unused)),
+                           size_t core_size,
+                           void (*callback) (grub_disk_addr_t sector,
+                                             unsigned offset,
+                                             unsigned length,
+                                             void *data),
+                           void *hook_data)
+{
+  grub_partition_t container = root_dev->disk->partition;
+  grub_uint64_t container_start = grub_partition_get_start (container);
+  struct fiemap fie1;
+  int fd;
+
+  /* Write the first two sectors of the core image onto the disk.  */
+  grub_util_info ("opening the core image `%s'", core_path);
+  fd = open (core_path, O_RDONLY);
+  if (fd < 0)
+    grub_util_error (_("cannot open `%s': %s"), core_path,
+                    strerror (errno));
+
+  grub_memset (&fie1, 0, sizeof (fie1));
+  fie1.fm_length = core_size;
+  fie1.fm_flags = FIEMAP_FLAG_SYNC;
+
+  if (ioctl (fd, FS_IOC_FIEMAP, &fie1) < 0)
+    {
+      int nblocks, i, j;
+      int bsize;
+      int mul;
+
+      grub_util_info ("FIEMAP failed. Reverting to FIBMAP");
+
+      if (ioctl (fd, FIGETBSZ, &bsize) < 0)
+       grub_util_error (_("can't retrieve blocklists: %s"),
+                        strerror (errno));
+      if (bsize & (GRUB_DISK_SECTOR_SIZE - 1))
+       grub_util_error ("%s", _("blocksize is not divisible by 512"));
+      mul = bsize >> GRUB_DISK_SECTOR_BITS;
+      nblocks = (core_size + bsize - 1) / bsize;
+      if (mul == 0 || nblocks == 0)
+       grub_util_error ("%s", _("can't retrieve blocklists"));
+      for (i = 0; i < nblocks; i++)
+       {
+         unsigned blk = i;
+         if (ioctl (fd, FIBMAP, &blk) < 0)
+           grub_util_error (_("can't retrieve blocklists: %s"),
+                            strerror (errno));
+           
+         for (j = 0; j < mul; j++)
+           {
+             int rest = core_size - ((i * mul + j) << GRUB_DISK_SECTOR_BITS);
+             if (rest <= 0)
+               break;
+             if (rest > GRUB_DISK_SECTOR_SIZE)
+               rest = GRUB_DISK_SECTOR_SIZE;
+             callback (((grub_uint64_t) blk) * mul + j
+                       + container_start,
+                       0, rest, hook_data);
+           }
+       }
+    }
+  else
+    {
+      struct fiemap *fie2;
+      int i, j;
+      fie2 = xmalloc (sizeof (*fie2)
+                     + fie1.fm_mapped_extents
+                     * sizeof (fie1.fm_extents[1]));
+      memset (fie2, 0, sizeof (*fie2)
+             + fie1.fm_mapped_extents * sizeof (fie2->fm_extents[1]));
+      fie2->fm_length = core_size;
+      fie2->fm_flags = FIEMAP_FLAG_SYNC;
+      fie2->fm_extent_count = fie1.fm_mapped_extents;
+      if (ioctl (fd, FS_IOC_FIEMAP, fie2) < 0)
+       grub_util_error (_("can't retrieve blocklists: %s"),
+                        strerror (errno));
+      for (i = 0; i < fie2->fm_mapped_extents; i++)
+       {
+         for (j = 0;
+              j < ((fie2->fm_extents[i].fe_length
+                    + GRUB_DISK_SECTOR_SIZE - 1)
+                   >> GRUB_DISK_SECTOR_BITS);
+              j++)
+           {
+             size_t len = (fie2->fm_extents[i].fe_length
+                           - j * GRUB_DISK_SECTOR_SIZE);
+             if (len > GRUB_DISK_SECTOR_SIZE)
+               len = GRUB_DISK_SECTOR_SIZE;
+             callback ((fie2->fm_extents[i].fe_physical
+                        >> GRUB_DISK_SECTOR_BITS)
+                       + j + container_start,
+                       fie2->fm_extents[i].fe_physical
+                       & (GRUB_DISK_SECTOR_SIZE - 1), len, hook_data);
+           }
+       }
+    }
+  close (fd);
+}
diff --git a/grub-core/osdep/windows/blocklist.c b/grub-core/osdep/windows/blocklist.c
new file mode 100644 (file)
index 0000000..bddec36
--- /dev/null
@@ -0,0 +1,119 @@
+/*
+ *  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 <http://www.gnu.org/licenses/>.
+ */
+
+#include <config.h>
+
+#include <grub/disk.h>
+#include <grub/partition.h>
+#include <grub/fs.h>
+#include <grub/ntfs.h>
+#include <grub/fat.h>
+#include <grub/exfat.h>
+#include <grub/udf.h>
+#include <grub/util/misc.h>
+#include <grub/util/install.h>
+#include <grub/emu/getroot.h>
+#include <grub/emu/hostfile.h>
+
+#include <windows.h>
+#include <winioctl.h>
+
+void
+grub_install_get_blocklist (grub_device_t root_dev,
+                           const char *core_path,
+                           const char *core_img __attribute__ ((unused)),
+                           size_t core_size,
+                           void (*callback) (grub_disk_addr_t sector,
+                                             unsigned offset,
+                                             unsigned length,
+                                             void *data),
+                           void *hook_data)
+{
+  grub_disk_addr_t first_lcn = 0;
+  HANDLE filehd;
+  DWORD rets;
+  RETRIEVAL_POINTERS_BUFFER *extbuf;
+  size_t extbuf_size;
+  DWORD i, j, k;
+  grub_uint64_t sec_per_lcn;
+  grub_uint64_t curvcn = 0;
+  STARTING_VCN_INPUT_BUFFER start_vcn;
+  grub_fs_t fs;
+  grub_err_t err;
+
+  fs = grub_fs_probe (root_dev);
+  if (!fs)
+    grub_util_error ("%s", grub_errmsg);
+
+  /* This is ugly but windows doesn't give all needed data. Or does anyone
+     have a pointer how to retrieve it?
+   */
+  if (grub_strcmp (fs->name, "ntfs") == 0)
+    {
+      struct grub_ntfs_bpb bpb;
+      err = grub_disk_read (root_dev->disk, 0, 0, sizeof (bpb), &bpb);
+      if (err)
+       grub_util_error ("%s", grub_errmsg);
+      sec_per_lcn = ((grub_uint32_t) bpb.sectors_per_cluster
+                    * (grub_uint32_t) grub_le_to_cpu16 (bpb.bytes_per_sector))
+       >> 9;
+      first_lcn = 0;
+    }
+  else if (grub_strcmp (fs->name, "exfat") == 0)
+    first_lcn = grub_exfat_get_cluster_sector (root_dev->disk, &sec_per_lcn);
+  else if (grub_strcmp (fs->name, "fat") == 0)
+    first_lcn = grub_fat_get_cluster_sector (root_dev->disk, &sec_per_lcn);
+  else if (grub_strcmp (fs->name, "udf") == 0)
+    first_lcn = grub_udf_get_cluster_sector (root_dev->disk, &sec_per_lcn);
+  else
+    grub_util_error ("unsupported fs for blocklist under windows: %s",
+                    fs->name);
+
+  grub_util_info ("sec_per_lcn = %lld, first_lcn=%lld", sec_per_lcn, first_lcn);
+
+  first_lcn += grub_partition_get_start (root_dev->disk->partition);
+
+  start_vcn.StartingVcn.QuadPart = 0;
+
+  filehd = grub_util_fd_open (core_path, GRUB_UTIL_FD_O_RDONLY);
+  if (!GRUB_UTIL_FD_IS_VALID (filehd))
+    grub_util_error (_("cannot open `%s': %s"), core_path,
+                    grub_util_fd_strerror ());
+
+  extbuf_size = sizeof (*extbuf) + sizeof (extbuf->Extents[0])
+    * ((core_size + 511) / 512);
+  extbuf = xmalloc (extbuf_size);
+
+  if (!DeviceIoControl(filehd, FSCTL_GET_RETRIEVAL_POINTERS,
+                      &start_vcn, sizeof (start_vcn),
+                      extbuf, extbuf_size, &rets, NULL))
+    grub_util_error ("FSCTL_GET_RETRIEVAL_POINTERS fails: %s",
+                    grub_util_fd_strerror ());
+
+  CloseHandle (filehd);
+
+  for (i = 0; i < extbuf->ExtentCount; i++)
+    {
+      for (j = 0; j < extbuf->Extents[i].NextVcn.QuadPart - curvcn; j++)
+       for (k = 0; k < sec_per_lcn; k++)
+         callback ((extbuf->Extents[i].Lcn.QuadPart + j)
+                   * sec_per_lcn + first_lcn
+                   + k, 0, 512, hook_data);
+    }
+  free (extbuf);
+}
diff --git a/include/grub/exfat.h b/include/grub/exfat.h
new file mode 100644 (file)
index 0000000..e5cb82d
--- /dev/null
@@ -0,0 +1,53 @@
+/*
+ *  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 <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef GRUB_EXFAT_H
+#define GRUB_EXFAT_H   1
+
+#include <grub/types.h>
+
+struct grub_exfat_bpb
+{
+  grub_uint8_t jmp_boot[3];
+  grub_uint8_t oem_name[8];
+  grub_uint8_t mbz[53];
+  grub_uint64_t num_hidden_sectors;
+  grub_uint64_t num_total_sectors;
+  grub_uint32_t num_reserved_sectors;
+  grub_uint32_t sectors_per_fat;
+  grub_uint32_t cluster_offset;
+  grub_uint32_t cluster_count;
+  grub_uint32_t root_cluster;
+  grub_uint32_t num_serial;
+  grub_uint16_t fs_revision;
+  grub_uint16_t volume_flags;
+  grub_uint8_t bytes_per_sector_shift;
+  grub_uint8_t sectors_per_cluster_shift;
+  grub_uint8_t num_fats;
+  grub_uint8_t num_ph_drive;
+  grub_uint8_t reserved[8];
+} __attribute__ ((packed));
+
+#ifdef GRUB_UTIL
+#include <grub/disk.h>
+
+grub_disk_addr_t
+grub_exfat_get_cluster_sector (grub_disk_t disk, grub_uint64_t *sec_per_lcn);
+#endif
+
+#endif
index 491ddfdb07a1e40176674d1f8f3cee1b50aa4185..ae352c6d6ec94131b6456b6bfdad69b6c59071b3 100644 (file)
@@ -72,4 +72,11 @@ struct grub_fat_bpb
   } __attribute__ ((packed)) version_specific;
 } __attribute__ ((packed));
 
+#ifdef GRUB_UTIL
+#include <grub/disk.h>
+
+grub_disk_addr_t
+grub_fat_get_cluster_sector (grub_disk_t disk, grub_uint64_t *sec_per_lcn);
+#endif
+
 #endif
diff --git a/include/grub/udf.h b/include/grub/udf.h
new file mode 100644 (file)
index 0000000..9053069
--- /dev/null
@@ -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 <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef GRUB_UDF_H
+#define GRUB_UDF_H     1
+
+#include <grub/types.h>
+
+#ifdef GRUB_UTIL
+#include <grub/disk.h>
+
+grub_disk_addr_t
+grub_udf_get_cluster_sector (grub_disk_t disk, grub_uint64_t *sec_per_lcn);
+#endif
+#endif
index 4ce3e9d5d9cc44142be14fee24c7810d629394eb..36f84be5a681f9e6ea8cb3b6aa0e149165fc021e 100644 (file)
@@ -36,4 +36,15 @@ grub_util_sparc_setup (const char *dir,
                       const char *dest, int force,
                       int fs_probe, int allow_floppy);
 
+
+void
+grub_install_get_blocklist (grub_device_t root_dev,
+                           const char *core_path, const char *core_img,
+                           size_t core_size,
+                           void (*callback) (grub_disk_addr_t sector,
+                                             unsigned offset,
+                                             unsigned length,
+                                             void *data),
+                           void *hook_data);
+
 #endif
index 15db86b947dad7e28186406408ea64742b3a13db..5b7c38430ca11c1fc04a0a11641bc28268bb83e9 100644 (file)
 #include <grub/msdos_partition.h>
 #include <grub/crypto.h>
 #include <grub/util/install.h>
+#include <grub/emu/hostfile.h>
 
-#ifdef __linux__
-#include <sys/ioctl.h>
-#include <linux/fs.h>
-#include <linux/fiemap.h>
-#endif
-
-#define _GNU_SOURCE    1
-#include <argp.h>
+#include <errno.h>
 
 /* On SPARC this program fills in various fields inside of the 'boot' and 'core'
  * image files.
@@ -137,19 +131,6 @@ write_rootdev (grub_device_t root_dev,
 #endif
 
 /* Helper for setup.  */
-static void
-save_first_sector (grub_disk_addr_t sector, unsigned offset, unsigned length,
-                  void *data)
-{
-  grub_disk_addr_t *first_sector = data;
-  grub_util_info ("the first sector is <%" PRIuGRUB_UINT64_T ",%u,%u>",
-                 sector, offset, length);
-
-  if (offset != 0 || length != GRUB_DISK_SECTOR_SIZE)
-    grub_util_error ("%s", _("the first sector of the core file is not sector-aligned"));
-
-  *first_sector = sector;
-}
 
 struct blocklists
 {
@@ -158,6 +139,7 @@ struct blocklists
   grub_uint16_t current_segment;
 #endif
   grub_uint16_t last_length;
+  grub_disk_addr_t first_sector;
 };
 
 /* Helper for setup.  */
@@ -171,6 +153,15 @@ save_blocklists (grub_disk_addr_t sector, unsigned offset, unsigned length,
   grub_util_info ("saving <%" PRIuGRUB_UINT64_T ",%u,%u>",
                  sector, offset, length);
 
+  if (bl->first_sector == (grub_disk_addr_t) -1)
+    {
+      if (offset != 0 || length != GRUB_DISK_SECTOR_SIZE)
+       grub_util_error ("%s", _("the first sector of the core file is not sector-aligned"));
+
+      bl->first_sector = sector;
+      return;
+    }
+
   if (offset != 0 || bl->last_length != GRUB_DISK_SECTOR_SIZE)
     grub_util_error ("%s", _("non-sector-aligned data is found in the core file"));
 
@@ -252,18 +243,18 @@ SETUP (const char *dir,
        const char *dest, int force,
        int fs_probe, int allow_floppy)
 {
-  char *boot_path, *core_path, *core_path_dev, *core_path_dev_full;
-  char *boot_img, *core_img;
+  char *core_path;
+  char *boot_img, *core_img, *boot_path;
   char *root = 0;
   size_t boot_size, core_size;
 #ifdef GRUB_SETUP_BIOS
   grub_uint16_t core_sectors;
 #endif
   grub_device_t root_dev = 0, dest_dev, core_dev;
+  grub_util_fd_t fp;
   struct blocklists bl;
-  char *tmp_img;
-  grub_disk_addr_t first_sector = (grub_disk_addr_t)-1;
-  FILE *fp;
+
+  bl.first_sector = (grub_disk_addr_t) -1;
 
 #ifdef GRUB_SETUP_BIOS
   bl.current_segment =
@@ -357,16 +348,16 @@ SETUP (const char *dir,
   if (grub_env_set ("root", root) != GRUB_ERR_NONE)
     grub_util_error ("%s", grub_errmsg);
 
-#ifdef GRUB_SETUP_BIOS
-  /* Read the original sector from the disk.  */
-  tmp_img = xmalloc (GRUB_DISK_SECTOR_SIZE);
-  if (grub_disk_read (dest_dev->disk, 0, 0, GRUB_DISK_SECTOR_SIZE, tmp_img))
-    grub_util_error ("%s", grub_errmsg);
-#endif
-
 #ifdef GRUB_SETUP_BIOS
   {
+    char *tmp_img;
     grub_uint8_t *boot_drive_check;
+
+    /* Read the original sector from the disk.  */
+    tmp_img = xmalloc (GRUB_DISK_SECTOR_SIZE);
+    if (grub_disk_read (dest_dev->disk, 0, 0, GRUB_DISK_SECTOR_SIZE, tmp_img))
+      grub_util_error ("%s", grub_errmsg);
+    
     boot_drive_check = (grub_uint8_t *) (boot_img
                                          + GRUB_BOOT_MACHINE_DRIVE_CHECK);
     /* Copy the possible DOS BPB.  */
@@ -383,11 +374,7 @@ SETUP (const char *dir,
        boot_drive_check[0] = 0x90;
        boot_drive_check[1] = 0x90;
       }
-  }
-#endif
 
-#ifdef GRUB_SETUP_BIOS
-  {
     struct identify_partmap_ctx ctx = {
       .dest_partmap = NULL,
       .container = dest_dev->disk->partition,
@@ -535,11 +522,8 @@ SETUP (const char *dir,
          grub_util_error ("%s", _("no terminator in the core image"));
       }
 
-    save_first_sector (sectors[0] + grub_partition_get_start (ctx.container),
-                      0, GRUB_DISK_SECTOR_SIZE, &first_sector);
-
     bl.block = bl.first_block;
-    for (i = 1; i < nsec; i++)
+    for (i = 0; i < nsec; i++)
       save_blocklists (sectors[i] + grub_partition_get_start (ctx.container),
                       0, GRUB_DISK_SECTOR_SIZE, &bl);
 
@@ -550,7 +534,7 @@ SETUP (const char *dir,
     bl.block->len = 0;
     bl.block->segment = 0;
 
-    write_rootdev (root_dev, boot_img, first_sector);
+    write_rootdev (root_dev, boot_img, bl.first_sector);
 
     core_img = realloc (core_img, nsec * GRUB_DISK_SECTOR_SIZE);
     bl.first_block = (struct grub_boot_blocklist *) (core_img
@@ -628,88 +612,8 @@ unable_to_embed:
   /* The core image must be put on a filesystem unfortunately.  */
   grub_util_info ("will leave the core image on the filesystem");
 
-  /* Make sure that GRUB reads the identical image as the OS.  */
-  tmp_img = xmalloc (core_size);
-  core_path_dev_full = grub_util_get_path (dir, core_file);
-  core_path_dev = grub_make_system_path_relative_to_its_root (core_path_dev_full);
-  free (core_path_dev_full);
-
   grub_util_biosdisk_flush (root_dev->disk);
 
-#ifndef __linux__
-
-#define MAX_TRIES      5
-  {
-    int i;
-    for (i = 0; i < MAX_TRIES; i++)
-      {
-       grub_file_t file;
-
-       grub_util_info ((i == 0) ? _("attempting to read the core image `%s' from GRUB")
-                       : _("attempting to read the core image `%s' from GRUB again"),
-                       core_path_dev);
-
-       grub_disk_cache_invalidate_all ();
-
-       grub_file_filter_disable_compression ();
-       file = grub_file_open (core_path_dev);
-       if (file)
-         {
-           if (grub_file_size (file) != core_size)
-             grub_util_info ("succeeded in opening the core image but the size is different (%d != %d)",
-                             (int) grub_file_size (file), (int) core_size);
-           else if (grub_file_read (file, tmp_img, core_size)
-                    != (grub_ssize_t) core_size)
-             grub_util_info ("succeeded in opening the core image but cannot read %d bytes",
-                             (int) core_size);
-           else if (memcmp (core_img, tmp_img, core_size) != 0)
-             {
-#if 0
-               FILE *dump;
-               FILE *dump2;
-
-               dump = grub_util_fopen ("dump.img", "wb");
-               if (dump)
-                 {
-                   fwrite (tmp_img, 1, core_size, dump);
-                   fclose (dump);
-                 }
-
-               dump2 = grub_util_fopen ("dump2.img", "wb");
-               if (dump2)
-                 {
-                   fwrite (core_img, 1, core_size, dump2);
-                   fclose (dump2);
-                 }
-
-#endif
-               grub_util_info ("succeeded in opening the core image but the data is different");
-             }
-           else
-             {
-               grub_file_close (file);
-               break;
-             }
-
-           grub_file_close (file);
-         }
-       else
-         grub_util_info ("couldn't open the core image");
-
-       if (grub_errno)
-         grub_util_info ("error message = %s", grub_errmsg);
-
-       grub_errno = GRUB_ERR_NONE;
-       grub_util_biosdisk_flush (root_dev->disk);
-       sleep (1);
-      }
-
-    if (i == MAX_TRIES)
-      grub_util_error (_("cannot read `%s' correctly"), core_path_dev);
-  }
-
-#endif
-
   /* Clean out the blocklists.  */
   bl.block = bl.first_block;
   while (bl.block->len)
@@ -728,140 +632,11 @@ unable_to_embed:
 
   bl.block = bl.first_block;
 
-#ifdef __linux__
-  {
-    grub_partition_t container = root_dev->disk->partition;
-    grub_uint64_t container_start = grub_partition_get_start (container);
-    struct fiemap fie1;
-    int fd;
-
-    /* Write the first two sectors of the core image onto the disk.  */
-    grub_util_info ("opening the core image `%s'", core_path);
-    fp = grub_util_fopen (core_path, "rb");
-    if (! fp)
-      grub_util_error (_("cannot open `%s': %s"), core_path,
-                      strerror (errno));
-    fd = fileno (fp);
-
-    grub_memset (&fie1, 0, sizeof (fie1));
-    fie1.fm_length = core_size;
-    fie1.fm_flags = FIEMAP_FLAG_SYNC;
-
-    if (ioctl (fd, FS_IOC_FIEMAP, &fie1) < 0)
-      {
-       int nblocks, i, j;
-       int bsize;
-       int mul;
-
-       grub_util_info ("FIEMAP failed. Reverting to FIBMAP");
-
-       if (ioctl (fd, FIGETBSZ, &bsize) < 0)
-         grub_util_error (_("can't retrieve blocklists: %s"),
-                          strerror (errno));
-       if (bsize & (GRUB_DISK_SECTOR_SIZE - 1))
-         grub_util_error ("%s", _("blocksize is not divisible by 512"));
-       mul = bsize >> GRUB_DISK_SECTOR_BITS;
-       nblocks = (core_size + bsize - 1) / bsize;
-       if (mul == 0 || nblocks == 0)
-         grub_util_error ("%s", _("can't retrieve blocklists"));
-       for (i = 0; i < nblocks; i++)
-         {
-           unsigned blk = i;
-           if (ioctl (fd, FIBMAP, &blk) < 0)
-             grub_util_error (_("can't retrieve blocklists: %s"),
-                              strerror (errno));
-           
-           for (j = 0; j < mul; j++)
-             {
-               int rest = core_size - ((i * mul + j) << GRUB_DISK_SECTOR_BITS);
-               if (rest <= 0)
-                 break;
-               if (rest > GRUB_DISK_SECTOR_SIZE)
-                 rest = GRUB_DISK_SECTOR_SIZE;
-               if (i == 0 && j == 0)
-                 save_first_sector (((grub_uint64_t) blk) * mul
-                                    + container_start,
-                                    0, rest, &first_sector);
-               else
-                 save_blocklists (((grub_uint64_t) blk) * mul + j
-                                  + container_start,
-                                  0, rest, &bl);
-             }
-         }
-      }
-    else
-      {
-       struct fiemap *fie2;
-       int i, j;
-       fie2 = xmalloc (sizeof (*fie2)
-                       + fie1.fm_mapped_extents
-                       * sizeof (fie1.fm_extents[1]));
-       memset (fie2, 0, sizeof (*fie2)
-               + fie1.fm_mapped_extents * sizeof (fie2->fm_extents[1]));
-       fie2->fm_length = core_size;
-       fie2->fm_flags = FIEMAP_FLAG_SYNC;
-       fie2->fm_extent_count = fie1.fm_mapped_extents;
-       if (ioctl (fd, FS_IOC_FIEMAP, fie2) < 0)
-         grub_util_error (_("can't retrieve blocklists: %s"),
-                          strerror (errno));
-       for (i = 0; i < fie2->fm_mapped_extents; i++)
-         {
-           for (j = 0;
-                j < ((fie2->fm_extents[i].fe_length
-                      + GRUB_DISK_SECTOR_SIZE - 1)
-                     >> GRUB_DISK_SECTOR_BITS);
-                j++)
-             {
-               size_t len = (fie2->fm_extents[i].fe_length
-                             - j * GRUB_DISK_SECTOR_SIZE);
-               if (len > GRUB_DISK_SECTOR_SIZE)
-                 len = GRUB_DISK_SECTOR_SIZE;
-               if (first_sector == (grub_disk_addr_t)-1)
-                 save_first_sector ((fie2->fm_extents[i].fe_physical
-                                     >> GRUB_DISK_SECTOR_BITS)
-                                    + j + container_start,
-                                    fie2->fm_extents[i].fe_physical
-                                    & (GRUB_DISK_SECTOR_SIZE - 1), len,
-                                    &first_sector);
-               else
-                 save_blocklists ((fie2->fm_extents[i].fe_physical
-                                   >> GRUB_DISK_SECTOR_BITS)
-                                  + j + container_start,
-                                  fie2->fm_extents[i].fe_physical
-                                  & (GRUB_DISK_SECTOR_SIZE - 1), len, &bl);
-
-
-             }
-         }
-       if (first_sector == (grub_disk_addr_t)-1)
-         grub_util_error ("%s", _("can't retrieve blocklists"));
-      }
-    fclose (fp);
-  }
-#else
-  {
-    grub_file_t file;
-    /* Now read the core image to determine where the sectors are.  */
-    grub_file_filter_disable_compression ();
-    file = grub_file_open (core_path_dev);
-    if (! file)
-      grub_util_error ("%s", grub_errmsg);
+  grub_install_get_blocklist (root_dev, core_path, core_img, core_size,
+                             save_blocklists, &bl);
 
-    file->read_hook = save_first_sector;
-    file->read_hook_data = &first_sector;
-    if (grub_file_read (file, tmp_img, GRUB_DISK_SECTOR_SIZE)
-       != GRUB_DISK_SECTOR_SIZE)
-      grub_util_error ("%s", _("failed to read the first sector of the core image"));
-
-    bl.block = bl.first_block;
-    file->read_hook = save_blocklists;
-    file->read_hook_data = &bl;
-    if (grub_file_read (file, tmp_img, core_size - GRUB_DISK_SECTOR_SIZE)
-       != (grub_ssize_t) core_size - GRUB_DISK_SECTOR_SIZE)
-      grub_util_error ("%s", _("failed to read the rest sectors of the core image"));
-    grub_file_close (file);
-  }
-#endif
+  if (bl.first_sector == (grub_disk_addr_t)-1)
+    grub_util_error ("%s", _("can't retrieve blocklists"));
 
 #ifdef GRUB_SETUP_SPARC64
   {
@@ -892,22 +667,21 @@ unable_to_embed:
   }
 #endif
 
-  free (core_path_dev);
-  free (tmp_img);
-
-  write_rootdev (root_dev, boot_img, first_sector);
+  write_rootdev (root_dev, boot_img, bl.first_sector);
 
   /* Write the first two sectors of the core image onto the disk.  */
   grub_util_info ("opening the core image `%s'", core_path);
-  fp = grub_util_fopen (core_path, "r+b");
-  if (! fp)
+  fp = grub_util_fd_open (core_path, GRUB_UTIL_FD_O_WRONLY);
+  if (! GRUB_UTIL_FD_IS_VALID (fp))
     grub_util_error (_("cannot open `%s': %s"), core_path,
-                    strerror (errno));
-
-  grub_util_write_image (core_img, GRUB_DISK_SECTOR_SIZE * 2, fp, core_path);
-  fflush (fp);
-  fsync (fileno (fp));
-  fclose (fp);
+                    grub_util_fd_strerror ());
+
+  if (grub_util_fd_write (fp, core_img, GRUB_DISK_SECTOR_SIZE * 2)
+      != GRUB_DISK_SECTOR_SIZE * 2)
+    grub_util_error (_("cannot write to `%s': %s"),
+                    core_path, strerror (errno));
+  grub_util_fd_sync (fp);
+  grub_util_fd_close (fp);
   grub_util_biosdisk_flush (root_dev->disk);
 
   grub_disk_cache_invalidate_all ();
@@ -922,7 +696,7 @@ unable_to_embed:
     core_dev->disk->partition = 0;
 
     buf = xmalloc (core_size);
-    blk = first_sector;
+    blk = bl.first_sector;
     err = grub_disk_read (core_dev->disk, blk, 0, GRUB_DISK_SECTOR_SIZE, buf);
     if (err)
       grub_util_error (_("cannot read `%s': %s"), core_dev->disk->name,