]> glassweightruler.freedombox.rocks Git - Ventoy.git/commitdiff
1.0.10 release v1.0.10
authorlongpanda <admin@ventoy.net>
Sat, 16 May 2020 06:17:36 +0000 (14:17 +0800)
committerlongpanda <admin@ventoy.net>
Sat, 16 May 2020 06:17:46 +0000 (14:17 +0800)
27 files changed:
GRUB2/grub-2.04/grub-core/disk/i386/pc/biosdisk.c
GRUB2/grub-2.04/grub-core/fs/ext2.c [new file with mode: 0644]
GRUB2/grub-2.04/grub-core/fs/fat.c
GRUB2/grub-2.04/grub-core/fs/ntfs.c [new file with mode: 0644]
GRUB2/grub-2.04/grub-core/fs/udf.c
GRUB2/grub-2.04/grub-core/fs/xfs.c [new file with mode: 0644]
GRUB2/grub-2.04/grub-core/kern/disk.c [new file with mode: 0644]
GRUB2/grub-2.04/grub-core/ventoy/ventoy.c
GRUB2/grub-2.04/grub-core/ventoy/ventoy_def.h
GRUB2/grub-2.04/grub-core/ventoy/ventoy_windows.c
GRUB2/grub-2.04/include/grub/disk.h [new file with mode: 0644]
GRUB2/grub-2.04/include/grub/ventoy.h
INSTALL/EFI/BOOT/grubx64_real.efi
INSTALL/Ventoy2Disk.exe
INSTALL/Ventoy2Disk.sh
INSTALL/grub/grub.cfg
INSTALL/grub/i386-pc/core.img
INSTALL/tool/ventoy_lib.sh
INSTALL/ventoy/ipxe.krn
INSTALL/ventoy/ventoy.cpio
IPXE/ipxe-3fe683e/src/arch/x86/core/runtime.c
IPXE/ipxe-3fe683e/src/arch/x86/interface/pcbios/hidemem.c
LANGUAGES/languages.ini
Ventoy2Disk/Ventoy2Disk/Ventoy2Disk.c
VtoyTool/vtoydump.c
VtoyTool/vtoytool/00/vtoytool_32
VtoyTool/vtoytool/00/vtoytool_64

index ee2ebe9ce2390458b1112193d1aa33d283edff95..a5146ff1aa29239487e9c15fbe23b42156890ee1 100644 (file)
@@ -374,7 +374,7 @@ static int ventoy_is_mbr_match(ventoy_mbr_head *head)
         return 0;
     }
 
-    if (head->PartTbl[0].FsFlag != 0x07 || head->PartTbl[0].StartSectorId != 2048) {
+    if (head->PartTbl[0].StartSectorId != 2048) {
         return 0;
     }
 
diff --git a/GRUB2/grub-2.04/grub-core/fs/ext2.c b/GRUB2/grub-2.04/grub-core/fs/ext2.c
new file mode 100644 (file)
index 0000000..eb0dae5
--- /dev/null
@@ -0,0 +1,1100 @@
+/* ext2.c - Second Extended filesystem */
+/*
+ *  GRUB  --  GRand Unified Bootloader
+ *  Copyright (C) 2003,2004,2005,2007,2008,2009  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/>.
+ */
+
+/* Magic value used to identify an ext2 filesystem.  */
+#define        EXT2_MAGIC              0xEF53
+/* Amount of indirect blocks in an inode.  */
+#define INDIRECT_BLOCKS                12
+
+/* The good old revision and the default inode size.  */
+#define EXT2_GOOD_OLD_REVISION         0
+#define EXT2_GOOD_OLD_INODE_SIZE       128
+
+/* Filetype used in directory entry.  */
+#define        FILETYPE_UNKNOWN        0
+#define        FILETYPE_REG            1
+#define        FILETYPE_DIRECTORY      2
+#define        FILETYPE_SYMLINK        7
+
+/* Filetype information as used in inodes.  */
+#define FILETYPE_INO_MASK      0170000
+#define FILETYPE_INO_REG       0100000
+#define FILETYPE_INO_DIRECTORY 0040000
+#define FILETYPE_INO_SYMLINK   0120000
+
+#include <grub/err.h>
+#include <grub/file.h>
+#include <grub/mm.h>
+#include <grub/misc.h>
+#include <grub/disk.h>
+#include <grub/dl.h>
+#include <grub/types.h>
+#include <grub/fshelp.h>
+
+GRUB_MOD_LICENSE ("GPLv3+");
+
+/* Log2 size of ext2 block in 512 blocks.  */
+#define LOG2_EXT2_BLOCK_SIZE(data)                     \
+       (grub_le_to_cpu32 (data->sblock.log2_block_size) + 1)
+
+/* Log2 size of ext2 block in bytes.  */
+#define LOG2_BLOCK_SIZE(data)                                  \
+       (grub_le_to_cpu32 (data->sblock.log2_block_size) + 10)
+
+/* The size of an ext2 block in bytes.  */
+#define EXT2_BLOCK_SIZE(data)          (1U << LOG2_BLOCK_SIZE (data))
+
+/* The revision level.  */
+#define EXT2_REVISION(data)    grub_le_to_cpu32 (data->sblock.revision_level)
+
+/* The inode size.  */
+#define EXT2_INODE_SIZE(data)  \
+  (data->sblock.revision_level \
+   == grub_cpu_to_le32_compile_time (EXT2_GOOD_OLD_REVISION)   \
+         ? EXT2_GOOD_OLD_INODE_SIZE \
+         : grub_le_to_cpu16 (data->sblock.inode_size))
+
+/* Superblock filesystem feature flags (RW compatible)
+ * A filesystem with any of these enabled can be read and written by a driver
+ * that does not understand them without causing metadata/data corruption.  */
+#define EXT2_FEATURE_COMPAT_DIR_PREALLOC       0x0001
+#define EXT2_FEATURE_COMPAT_IMAGIC_INODES      0x0002
+#define EXT3_FEATURE_COMPAT_HAS_JOURNAL                0x0004
+#define EXT2_FEATURE_COMPAT_EXT_ATTR           0x0008
+#define EXT2_FEATURE_COMPAT_RESIZE_INODE       0x0010
+#define EXT2_FEATURE_COMPAT_DIR_INDEX          0x0020
+/* Superblock filesystem feature flags (RO compatible)
+ * A filesystem with any of these enabled can be safely read by a driver that
+ * does not understand them, but should not be written to, usually because
+ * additional metadata is required.  */
+#define EXT2_FEATURE_RO_COMPAT_SPARSE_SUPER    0x0001
+#define EXT2_FEATURE_RO_COMPAT_LARGE_FILE      0x0002
+#define EXT2_FEATURE_RO_COMPAT_BTREE_DIR       0x0004
+#define EXT4_FEATURE_RO_COMPAT_GDT_CSUM                0x0010
+#define EXT4_FEATURE_RO_COMPAT_DIR_NLINK       0x0020
+#define EXT4_FEATURE_RO_COMPAT_EXTRA_ISIZE     0x0040
+/* Superblock filesystem feature flags (back-incompatible)
+ * A filesystem with any of these enabled should not be attempted to be read
+ * by a driver that does not understand them, since they usually indicate
+ * metadata format changes that might confuse the reader.  */
+#define EXT2_FEATURE_INCOMPAT_COMPRESSION      0x0001
+#define EXT2_FEATURE_INCOMPAT_FILETYPE         0x0002
+#define EXT3_FEATURE_INCOMPAT_RECOVER          0x0004 /* Needs recovery */
+#define EXT3_FEATURE_INCOMPAT_JOURNAL_DEV      0x0008 /* Volume is journal device */
+#define EXT2_FEATURE_INCOMPAT_META_BG          0x0010
+#define EXT4_FEATURE_INCOMPAT_EXTENTS          0x0040 /* Extents used */
+#define EXT4_FEATURE_INCOMPAT_64BIT            0x0080
+#define EXT4_FEATURE_INCOMPAT_MMP              0x0100
+#define EXT4_FEATURE_INCOMPAT_FLEX_BG          0x0200
+#define EXT4_FEATURE_INCOMPAT_ENCRYPT          0x10000
+
+/* The set of back-incompatible features this driver DOES support. Add (OR)
+ * flags here as the related features are implemented into the driver.  */
+#define EXT2_DRIVER_SUPPORTED_INCOMPAT ( EXT2_FEATURE_INCOMPAT_FILETYPE \
+                                       | EXT4_FEATURE_INCOMPAT_EXTENTS  \
+                                       | EXT4_FEATURE_INCOMPAT_FLEX_BG \
+                                       | EXT2_FEATURE_INCOMPAT_META_BG \
+                                       | EXT4_FEATURE_INCOMPAT_64BIT \
+                                       | EXT4_FEATURE_INCOMPAT_ENCRYPT)
+/* List of rationales for the ignored "incompatible" features:
+ * needs_recovery: Not really back-incompatible - was added as such to forbid
+ *                 ext2 drivers from mounting an ext3 volume with a dirty
+ *                 journal because they will ignore the journal, but the next
+ *                 ext3 driver to mount the volume will find the journal and
+ *                 replay it, potentially corrupting the metadata written by
+ *                 the ext2 drivers. Safe to ignore for this RO driver.
+ * mmp:            Not really back-incompatible - was added as such to
+ *                 avoid multiple read-write mounts. Safe to ignore for this
+ *                 RO driver.
+ */
+#define EXT2_DRIVER_IGNORED_INCOMPAT ( EXT3_FEATURE_INCOMPAT_RECOVER \
+                                    | EXT4_FEATURE_INCOMPAT_MMP)
+
+
+#define EXT3_JOURNAL_MAGIC_NUMBER      0xc03b3998U
+
+#define EXT3_JOURNAL_DESCRIPTOR_BLOCK  1
+#define EXT3_JOURNAL_COMMIT_BLOCK      2
+#define EXT3_JOURNAL_SUPERBLOCK_V1     3
+#define EXT3_JOURNAL_SUPERBLOCK_V2     4
+#define EXT3_JOURNAL_REVOKE_BLOCK      5
+
+#define EXT3_JOURNAL_FLAG_ESCAPE       1
+#define EXT3_JOURNAL_FLAG_SAME_UUID    2
+#define EXT3_JOURNAL_FLAG_DELETED      4
+#define EXT3_JOURNAL_FLAG_LAST_TAG     8
+
+#define EXT4_ENCRYPT_FLAG              0x800
+#define EXT4_EXTENTS_FLAG              0x80000
+
+/* The ext2 superblock.  */
+struct grub_ext2_sblock
+{
+  grub_uint32_t total_inodes;
+  grub_uint32_t total_blocks;
+  grub_uint32_t reserved_blocks;
+  grub_uint32_t free_blocks;
+  grub_uint32_t free_inodes;
+  grub_uint32_t first_data_block;
+  grub_uint32_t log2_block_size;
+  grub_uint32_t log2_fragment_size;
+  grub_uint32_t blocks_per_group;
+  grub_uint32_t fragments_per_group;
+  grub_uint32_t inodes_per_group;
+  grub_uint32_t mtime;
+  grub_uint32_t utime;
+  grub_uint16_t mnt_count;
+  grub_uint16_t max_mnt_count;
+  grub_uint16_t magic;
+  grub_uint16_t fs_state;
+  grub_uint16_t error_handling;
+  grub_uint16_t minor_revision_level;
+  grub_uint32_t lastcheck;
+  grub_uint32_t checkinterval;
+  grub_uint32_t creator_os;
+  grub_uint32_t revision_level;
+  grub_uint16_t uid_reserved;
+  grub_uint16_t gid_reserved;
+  grub_uint32_t first_inode;
+  grub_uint16_t inode_size;
+  grub_uint16_t block_group_number;
+  grub_uint32_t feature_compatibility;
+  grub_uint32_t feature_incompat;
+  grub_uint32_t feature_ro_compat;
+  grub_uint16_t uuid[8];
+  char volume_name[16];
+  char last_mounted_on[64];
+  grub_uint32_t compression_info;
+  grub_uint8_t prealloc_blocks;
+  grub_uint8_t prealloc_dir_blocks;
+  grub_uint16_t reserved_gdt_blocks;
+  grub_uint8_t journal_uuid[16];
+  grub_uint32_t journal_inum;
+  grub_uint32_t journal_dev;
+  grub_uint32_t last_orphan;
+  grub_uint32_t hash_seed[4];
+  grub_uint8_t def_hash_version;
+  grub_uint8_t jnl_backup_type;
+  grub_uint16_t group_desc_size;
+  grub_uint32_t default_mount_opts;
+  grub_uint32_t first_meta_bg;
+  grub_uint32_t mkfs_time;
+  grub_uint32_t jnl_blocks[17];
+};
+
+/* The ext2 blockgroup.  */
+struct grub_ext2_block_group
+{
+  grub_uint32_t block_id;
+  grub_uint32_t inode_id;
+  grub_uint32_t inode_table_id;
+  grub_uint16_t free_blocks;
+  grub_uint16_t free_inodes;
+  grub_uint16_t used_dirs;
+  grub_uint16_t pad;
+  grub_uint32_t reserved[3];
+  grub_uint32_t block_id_hi;
+  grub_uint32_t inode_id_hi;
+  grub_uint32_t inode_table_id_hi;
+  grub_uint16_t free_blocks_hi;
+  grub_uint16_t free_inodes_hi;
+  grub_uint16_t used_dirs_hi;
+  grub_uint16_t pad2;
+  grub_uint32_t reserved2[3];
+};
+
+/* The ext2 inode.  */
+struct grub_ext2_inode
+{
+  grub_uint16_t mode;
+  grub_uint16_t uid;
+  grub_uint32_t size;
+  grub_uint32_t atime;
+  grub_uint32_t ctime;
+  grub_uint32_t mtime;
+  grub_uint32_t dtime;
+  grub_uint16_t gid;
+  grub_uint16_t nlinks;
+  grub_uint32_t blockcnt;  /* Blocks of 512 bytes!! */
+  grub_uint32_t flags;
+  grub_uint32_t osd1;
+  union
+  {
+    struct datablocks
+    {
+      grub_uint32_t dir_blocks[INDIRECT_BLOCKS];
+      grub_uint32_t indir_block;
+      grub_uint32_t double_indir_block;
+      grub_uint32_t triple_indir_block;
+    } blocks;
+    char symlink[60];
+  };
+  grub_uint32_t version;
+  grub_uint32_t acl;
+  grub_uint32_t size_high;
+  grub_uint32_t fragment_addr;
+  grub_uint32_t osd2[3];
+};
+
+/* The header of an ext2 directory entry.  */
+struct ext2_dirent
+{
+  grub_uint32_t inode;
+  grub_uint16_t direntlen;
+#define MAX_NAMELEN 255
+  grub_uint8_t namelen;
+  grub_uint8_t filetype;
+};
+
+struct grub_ext3_journal_header
+{
+  grub_uint32_t magic;
+  grub_uint32_t block_type;
+  grub_uint32_t sequence;
+};
+
+struct grub_ext3_journal_revoke_header
+{
+  struct grub_ext3_journal_header header;
+  grub_uint32_t count;
+  grub_uint32_t data[0];
+};
+
+struct grub_ext3_journal_block_tag
+{
+  grub_uint32_t block;
+  grub_uint32_t flags;
+};
+
+struct grub_ext3_journal_sblock
+{
+  struct grub_ext3_journal_header header;
+  grub_uint32_t block_size;
+  grub_uint32_t maxlen;
+  grub_uint32_t first;
+  grub_uint32_t sequence;
+  grub_uint32_t start;
+};
+
+#define EXT4_EXT_MAGIC         0xf30a
+
+struct grub_ext4_extent_header
+{
+  grub_uint16_t magic;
+  grub_uint16_t entries;
+  grub_uint16_t max;
+  grub_uint16_t depth;
+  grub_uint32_t generation;
+};
+
+struct grub_ext4_extent
+{
+  grub_uint32_t block;
+  grub_uint16_t len;
+  grub_uint16_t start_hi;
+  grub_uint32_t start;
+};
+
+struct grub_ext4_extent_idx
+{
+  grub_uint32_t block;
+  grub_uint32_t leaf;
+  grub_uint16_t leaf_hi;
+  grub_uint16_t unused;
+};
+
+struct grub_fshelp_node
+{
+  struct grub_ext2_data *data;
+  struct grub_ext2_inode inode;
+  int ino;
+  int inode_read;
+};
+
+/* Information about a "mounted" ext2 filesystem.  */
+struct grub_ext2_data
+{
+  struct grub_ext2_sblock sblock;
+  int log_group_desc_size;
+  grub_disk_t disk;
+  struct grub_ext2_inode *inode;
+  struct grub_fshelp_node diropen;
+};
+
+static grub_dl_t my_mod;
+
+\f
+
+/* Check is a = b^x for some x.  */
+static inline int
+is_power_of (grub_uint64_t a, grub_uint32_t b)
+{
+  grub_uint64_t c;
+  /* Prevent overflow assuming b < 8.  */
+  if (a >= (1LL << 60))
+    return 0;
+  for (c = 1; c <= a; c *= b);
+  return (c == a);
+}
+
+
+static inline int
+group_has_super_block (struct grub_ext2_data *data, grub_uint64_t group)
+{
+  if (!(data->sblock.feature_ro_compat
+       & grub_cpu_to_le32_compile_time(EXT2_FEATURE_RO_COMPAT_SPARSE_SUPER)))
+    return 1;
+  /* Algorithm looked up in Linux source.  */
+  if (group <= 1)
+    return 1;
+  /* Even number is never a power of odd number.  */
+  if (!(group & 1))
+    return 0;
+  return (is_power_of(group, 7) || is_power_of(group, 5) ||
+         is_power_of(group, 3));
+}
+
+/* Read into BLKGRP the blockgroup descriptor of blockgroup GROUP of
+   the mounted filesystem DATA.  */
+inline static grub_err_t
+grub_ext2_blockgroup (struct grub_ext2_data *data, grub_uint64_t group,
+                     struct grub_ext2_block_group *blkgrp)
+{
+  grub_uint64_t full_offset = (group << data->log_group_desc_size);
+  grub_uint64_t block, offset;
+  block = (full_offset >> LOG2_BLOCK_SIZE (data));
+  offset = (full_offset & ((1 << LOG2_BLOCK_SIZE (data)) - 1));
+  if ((data->sblock.feature_incompat
+       & grub_cpu_to_le32_compile_time (EXT2_FEATURE_INCOMPAT_META_BG))
+      && block >= grub_le_to_cpu32(data->sblock.first_meta_bg))
+    {
+      grub_uint64_t first_block_group;
+      /* Find the first block group for which a descriptor
+        is stored in given block. */
+      first_block_group = (block << (LOG2_BLOCK_SIZE (data)
+                                    - data->log_group_desc_size));
+
+      block = (first_block_group
+              * grub_le_to_cpu32(data->sblock.blocks_per_group));
+
+      if (group_has_super_block (data, first_block_group))
+       block++;
+    }
+  else
+    /* Superblock.  */
+    block++;
+  return grub_disk_read (data->disk,
+                         ((grub_le_to_cpu32 (data->sblock.first_data_block)
+                          + block)
+                          << LOG2_EXT2_BLOCK_SIZE (data)), offset,
+                        sizeof (struct grub_ext2_block_group), blkgrp);
+}
+
+static struct grub_ext4_extent_header *
+grub_ext4_find_leaf (struct grub_ext2_data *data,
+                     struct grub_ext4_extent_header *ext_block,
+                     grub_uint32_t fileblock)
+{
+  struct grub_ext4_extent_idx *index;
+  void *buf = NULL;
+
+  while (1)
+    {
+      int i;
+      grub_disk_addr_t block;
+
+      index = (struct grub_ext4_extent_idx *) (ext_block + 1);
+
+      if (ext_block->magic != grub_cpu_to_le16_compile_time (EXT4_EXT_MAGIC))
+       goto fail;
+
+      if (ext_block->depth == 0)
+        return ext_block;
+
+      for (i = 0; i < grub_le_to_cpu16 (ext_block->entries); i++)
+        {
+          if (fileblock < grub_le_to_cpu32(index[i].block))
+            break;
+        }
+
+      if (--i < 0)
+       goto fail;
+
+      block = grub_le_to_cpu16 (index[i].leaf_hi);
+      block = (block << 32) | grub_le_to_cpu32 (index[i].leaf);
+      if (!buf)
+       buf = grub_malloc (EXT2_BLOCK_SIZE(data));
+      if (!buf)
+       goto fail;
+      if (grub_disk_read (data->disk,
+                          block << LOG2_EXT2_BLOCK_SIZE (data),
+                          0, EXT2_BLOCK_SIZE(data), buf))
+       goto fail;
+
+      ext_block = buf;
+    }
+ fail:
+  grub_free (buf);
+  return 0;
+}
+
+static grub_disk_addr_t
+grub_ext2_read_block (grub_fshelp_node_t node, grub_disk_addr_t fileblock)
+{
+  struct grub_ext2_data *data = node->data;
+  struct grub_ext2_inode *inode = &node->inode;
+  unsigned int blksz = EXT2_BLOCK_SIZE (data);
+  grub_disk_addr_t blksz_quarter = blksz / 4;
+  int log2_blksz = LOG2_EXT2_BLOCK_SIZE (data);
+  int log_perblock = log2_blksz + 9 - 2;
+  grub_uint32_t indir;
+  int shift;
+
+  if (inode->flags & grub_cpu_to_le32_compile_time (EXT4_EXTENTS_FLAG))
+    {
+      struct grub_ext4_extent_header *leaf;
+      struct grub_ext4_extent *ext;
+      int i;
+      grub_disk_addr_t ret;
+
+      leaf = grub_ext4_find_leaf (data, (struct grub_ext4_extent_header *) inode->blocks.dir_blocks, fileblock);
+      if (! leaf)
+        {
+          grub_error (GRUB_ERR_BAD_FS, "invalid extent");
+          return -1;
+        }
+
+      ext = (struct grub_ext4_extent *) (leaf + 1);
+      for (i = 0; i < grub_le_to_cpu16 (leaf->entries); i++)
+        {
+          if (fileblock < grub_le_to_cpu32 (ext[i].block))
+            break;
+        }
+
+      if (--i >= 0)
+        {
+          fileblock -= grub_le_to_cpu32 (ext[i].block);
+          if (fileblock >= grub_le_to_cpu16 (ext[i].len))
+           ret = 0;
+          else
+            {
+              grub_disk_addr_t start;
+
+              start = grub_le_to_cpu16 (ext[i].start_hi);
+              start = (start << 32) + grub_le_to_cpu32 (ext[i].start);
+
+              ret = fileblock + start;
+            }
+        }
+      else
+        {
+          grub_error (GRUB_ERR_BAD_FS, "something wrong with extent");
+         ret = -1;
+        }
+
+      if (leaf != (struct grub_ext4_extent_header *) inode->blocks.dir_blocks)
+       grub_free (leaf);
+
+      return ret;
+    }
+
+  /* Direct blocks.  */
+  if (fileblock < INDIRECT_BLOCKS)
+    return grub_le_to_cpu32 (inode->blocks.dir_blocks[fileblock]);
+  fileblock -= INDIRECT_BLOCKS;
+  /* Indirect.  */
+  if (fileblock < blksz_quarter)
+    {
+      indir = inode->blocks.indir_block;
+      shift = 0;
+      goto indirect;
+    }
+  fileblock -= blksz_quarter;
+  /* Double indirect.  */
+  if (fileblock < blksz_quarter * blksz_quarter)
+    {
+      indir = inode->blocks.double_indir_block;
+      shift = 1;
+      goto indirect;
+    }
+  fileblock -= blksz_quarter * blksz_quarter;
+  /* Triple indirect.  */
+  if (fileblock < blksz_quarter * blksz_quarter * (blksz_quarter + 1))
+    {
+      indir = inode->blocks.triple_indir_block;
+      shift = 2;
+      goto indirect;
+    }
+  grub_error (GRUB_ERR_BAD_FS,
+             "ext2fs doesn't support quadruple indirect blocks");
+  return -1;
+
+indirect:
+  do {
+    /* If the indirect block is zero, all child blocks are absent
+       (i.e. filled with zeros.) */
+    if (indir == 0)
+      return 0;
+    if (grub_disk_read (data->disk,
+                       ((grub_disk_addr_t) grub_le_to_cpu32 (indir))
+                       << log2_blksz,
+                       ((fileblock >> (log_perblock * shift))
+                        & ((1 << log_perblock) - 1))
+                       * sizeof (indir),
+                       sizeof (indir), &indir))
+      return -1;
+  } while (shift--);
+
+  return grub_le_to_cpu32 (indir);
+}
+
+/* Read LEN bytes from the file described by DATA starting with byte
+   POS.  Return the amount of read bytes in READ.  */
+static grub_ssize_t
+grub_ext2_read_file (grub_fshelp_node_t node,
+                    grub_disk_read_hook_t read_hook, void *read_hook_data,
+                    grub_off_t pos, grub_size_t len, char *buf)
+{
+  return grub_fshelp_read_file (node->data->disk, node,
+                               read_hook, read_hook_data,
+                               pos, len, buf, grub_ext2_read_block,
+                               grub_cpu_to_le32 (node->inode.size)
+                               | (((grub_off_t) grub_cpu_to_le32 (node->inode.size_high)) << 32),
+                               LOG2_EXT2_BLOCK_SIZE (node->data), 0);
+
+}
+
+
+/* Read the inode INO for the file described by DATA into INODE.  */
+static grub_err_t
+grub_ext2_read_inode (struct grub_ext2_data *data,
+                     int ino, struct grub_ext2_inode *inode)
+{
+  struct grub_ext2_block_group blkgrp;
+  struct grub_ext2_sblock *sblock = &data->sblock;
+  int inodes_per_block;
+  unsigned int blkno;
+  unsigned int blkoff;
+  grub_disk_addr_t base;
+
+  /* It is easier to calculate if the first inode is 0.  */
+  ino--;
+
+  grub_ext2_blockgroup (data,
+                        ino / grub_le_to_cpu32 (sblock->inodes_per_group),
+                       &blkgrp);
+  if (grub_errno)
+    return grub_errno;
+
+  inodes_per_block = EXT2_BLOCK_SIZE (data) / EXT2_INODE_SIZE (data);
+  blkno = (ino % grub_le_to_cpu32 (sblock->inodes_per_group))
+    / inodes_per_block;
+  blkoff = (ino % grub_le_to_cpu32 (sblock->inodes_per_group))
+    % inodes_per_block;
+
+  base = grub_le_to_cpu32 (blkgrp.inode_table_id);
+  if (data->log_group_desc_size >= 6)
+    base |= (((grub_disk_addr_t) grub_le_to_cpu32 (blkgrp.inode_table_id_hi))
+            << 32);
+
+  /* Read the inode.  */
+  if (grub_disk_read (data->disk,
+                     ((base + blkno) << LOG2_EXT2_BLOCK_SIZE (data)),
+                     EXT2_INODE_SIZE (data) * blkoff,
+                     sizeof (struct grub_ext2_inode), inode))
+    return grub_errno;
+
+  return 0;
+}
+
+static struct grub_ext2_data *
+grub_ext2_mount (grub_disk_t disk)
+{
+  struct grub_ext2_data *data;
+
+  data = grub_malloc (sizeof (struct grub_ext2_data));
+  if (!data)
+    return 0;
+
+  /* Read the superblock.  */
+  grub_disk_read (disk, 1 * 2, 0, sizeof (struct grub_ext2_sblock),
+                  &data->sblock);
+  if (grub_errno)
+    goto fail;
+
+  /* Make sure this is an ext2 filesystem.  */
+  if (data->sblock.magic != grub_cpu_to_le16_compile_time (EXT2_MAGIC)
+      || grub_le_to_cpu32 (data->sblock.log2_block_size) >= 16
+      || data->sblock.inodes_per_group == 0
+      /* 20 already means 1GiB blocks. We don't want to deal with blocks overflowing int32. */
+      || grub_le_to_cpu32 (data->sblock.log2_block_size) > 20
+      || EXT2_INODE_SIZE (data) == 0
+      || EXT2_BLOCK_SIZE (data) / EXT2_INODE_SIZE (data) == 0)
+    {
+      grub_error (GRUB_ERR_BAD_FS, "not an ext2 filesystem");
+      goto fail;
+    }
+
+  /* Check the FS doesn't have feature bits enabled that we don't support. */
+  if (data->sblock.revision_level != grub_cpu_to_le32_compile_time (EXT2_GOOD_OLD_REVISION)
+      && (data->sblock.feature_incompat
+         & grub_cpu_to_le32_compile_time (~(EXT2_DRIVER_SUPPORTED_INCOMPAT
+                                            | EXT2_DRIVER_IGNORED_INCOMPAT))))
+    {
+      grub_error (GRUB_ERR_BAD_FS, "filesystem has unsupported incompatible features");
+      goto fail;
+    }
+
+  if (data->sblock.revision_level != grub_cpu_to_le32_compile_time (EXT2_GOOD_OLD_REVISION)
+      && (data->sblock.feature_incompat
+         & grub_cpu_to_le32_compile_time (EXT4_FEATURE_INCOMPAT_64BIT))
+      && data->sblock.group_desc_size != 0
+      && ((data->sblock.group_desc_size & (data->sblock.group_desc_size - 1))
+         == 0)
+      && (data->sblock.group_desc_size & grub_cpu_to_le16_compile_time (0x1fe0)))
+    {
+      grub_uint16_t b = grub_le_to_cpu16 (data->sblock.group_desc_size);
+      for (data->log_group_desc_size = 0; b != (1 << data->log_group_desc_size);
+          data->log_group_desc_size++);
+    }
+  else
+    data->log_group_desc_size = 5;
+
+  data->disk = disk;
+
+  data->diropen.data = data;
+  data->diropen.ino = 2;
+  data->diropen.inode_read = 1;
+
+  data->inode = &data->diropen.inode;
+
+  grub_ext2_read_inode (data, 2, data->inode);
+  if (grub_errno)
+    goto fail;
+
+  return data;
+
+ fail:
+  if (grub_errno == GRUB_ERR_OUT_OF_RANGE)
+    grub_error (GRUB_ERR_BAD_FS, "not an ext2 filesystem");
+
+  grub_free (data);
+  return 0;
+}
+
+static char *
+grub_ext2_read_symlink (grub_fshelp_node_t node)
+{
+  char *symlink;
+  struct grub_fshelp_node *diro = node;
+
+  if (! diro->inode_read)
+    {
+      grub_ext2_read_inode (diro->data, diro->ino, &diro->inode);
+      if (grub_errno)
+       return 0;
+
+      if (diro->inode.flags & grub_cpu_to_le32_compile_time (EXT4_ENCRYPT_FLAG))
+       {
+         grub_error (GRUB_ERR_NOT_IMPLEMENTED_YET, "symlink is encrypted");
+         return 0;
+       }
+    }
+
+  symlink = grub_malloc (grub_le_to_cpu32 (diro->inode.size) + 1);
+  if (! symlink)
+    return 0;
+
+  /* If the filesize of the symlink is bigger than
+     60 the symlink is stored in a separate block,
+     otherwise it is stored in the inode.  */
+  if (grub_le_to_cpu32 (diro->inode.size) <= sizeof (diro->inode.symlink))
+    grub_memcpy (symlink,
+                diro->inode.symlink,
+                grub_le_to_cpu32 (diro->inode.size));
+  else
+    {
+      grub_ext2_read_file (diro, 0, 0, 0,
+                          grub_le_to_cpu32 (diro->inode.size),
+                          symlink);
+      if (grub_errno)
+       {
+         grub_free (symlink);
+         return 0;
+       }
+    }
+
+  symlink[grub_le_to_cpu32 (diro->inode.size)] = '\0';
+  return symlink;
+}
+
+static int
+grub_ext2_iterate_dir (grub_fshelp_node_t dir,
+                      grub_fshelp_iterate_dir_hook_t hook, void *hook_data)
+{
+  unsigned int fpos = 0;
+  struct grub_fshelp_node *diro = (struct grub_fshelp_node *) dir;
+
+  if (! diro->inode_read)
+    {
+      grub_ext2_read_inode (diro->data, diro->ino, &diro->inode);
+      if (grub_errno)
+       return 0;
+    }
+
+  if (diro->inode.flags & grub_cpu_to_le32_compile_time (EXT4_ENCRYPT_FLAG))
+    {
+      grub_error (GRUB_ERR_NOT_IMPLEMENTED_YET, "directory is encrypted");
+      return 0;
+    }
+
+  /* Search the file.  */
+  while (fpos < grub_le_to_cpu32 (diro->inode.size))
+    {
+      struct ext2_dirent dirent;
+
+      grub_ext2_read_file (diro, 0, 0, fpos, sizeof (struct ext2_dirent),
+                          (char *) &dirent);
+      if (grub_errno)
+       return 0;
+
+      if (dirent.direntlen == 0)
+        return 0;
+
+      if (dirent.inode != 0 && dirent.namelen != 0)
+       {
+         char filename[MAX_NAMELEN + 1];
+         struct grub_fshelp_node *fdiro;
+         enum grub_fshelp_filetype type = GRUB_FSHELP_UNKNOWN;
+
+         grub_ext2_read_file (diro, 0, 0, fpos + sizeof (struct ext2_dirent),
+                              dirent.namelen, filename);
+         if (grub_errno)
+           return 0;
+
+         fdiro = grub_malloc (sizeof (struct grub_fshelp_node));
+         if (! fdiro)
+           return 0;
+
+         fdiro->data = diro->data;
+         fdiro->ino = grub_le_to_cpu32 (dirent.inode);
+
+         filename[dirent.namelen] = '\0';
+
+         if (dirent.filetype != FILETYPE_UNKNOWN)
+           {
+             fdiro->inode_read = 0;
+
+             if (dirent.filetype == FILETYPE_DIRECTORY)
+               type = GRUB_FSHELP_DIR;
+             else if (dirent.filetype == FILETYPE_SYMLINK)
+               type = GRUB_FSHELP_SYMLINK;
+             else if (dirent.filetype == FILETYPE_REG)
+               type = GRUB_FSHELP_REG;
+           }
+         else
+           {
+             /* The filetype can not be read from the dirent, read
+                the inode to get more information.  */
+             grub_ext2_read_inode (diro->data,
+                                    grub_le_to_cpu32 (dirent.inode),
+                                   &fdiro->inode);
+             if (grub_errno)
+               {
+                 grub_free (fdiro);
+                 return 0;
+               }
+
+             fdiro->inode_read = 1;
+
+             if ((grub_le_to_cpu16 (fdiro->inode.mode)
+                  & FILETYPE_INO_MASK) == FILETYPE_INO_DIRECTORY)
+               type = GRUB_FSHELP_DIR;
+             else if ((grub_le_to_cpu16 (fdiro->inode.mode)
+                       & FILETYPE_INO_MASK) == FILETYPE_INO_SYMLINK)
+               type = GRUB_FSHELP_SYMLINK;
+             else if ((grub_le_to_cpu16 (fdiro->inode.mode)
+                       & FILETYPE_INO_MASK) == FILETYPE_INO_REG)
+               type = GRUB_FSHELP_REG;
+           }
+
+         if (hook (filename, type, fdiro, hook_data))
+           return 1;
+       }
+
+      fpos += grub_le_to_cpu16 (dirent.direntlen);
+    }
+
+  return 0;
+}
+
+/* Open a file named NAME and initialize FILE.  */
+static grub_err_t
+grub_ext2_open (struct grub_file *file, const char *name)
+{
+  struct grub_ext2_data *data;
+  struct grub_fshelp_node *fdiro = 0;
+  grub_err_t err;
+
+  grub_dl_ref (my_mod);
+
+  data = grub_ext2_mount (file->device->disk);
+  if (! data)
+    {
+      err = grub_errno;
+      goto fail;
+    }
+
+  err = grub_fshelp_find_file (name, &data->diropen, &fdiro,
+                              grub_ext2_iterate_dir,
+                              grub_ext2_read_symlink, GRUB_FSHELP_REG);
+  if (err)
+    goto fail;
+
+  if (! fdiro->inode_read)
+    {
+      err = grub_ext2_read_inode (data, fdiro->ino, &fdiro->inode);
+      if (err)
+       goto fail;
+    }
+
+  if (fdiro->inode.flags & grub_cpu_to_le32_compile_time (EXT4_ENCRYPT_FLAG))
+    {
+      err = grub_error (GRUB_ERR_NOT_IMPLEMENTED_YET, "file is encrypted");
+      goto fail;
+    }
+
+  grub_memcpy (data->inode, &fdiro->inode, sizeof (struct grub_ext2_inode));
+  grub_free (fdiro);
+
+  file->size = grub_le_to_cpu32 (data->inode->size);
+  file->size |= ((grub_off_t) grub_le_to_cpu32 (data->inode->size_high)) << 32;
+  file->data = data;
+  file->offset = 0;
+
+  return 0;
+
+ fail:
+  if (fdiro != &data->diropen)
+    grub_free (fdiro);
+  grub_free (data);
+
+  grub_dl_unref (my_mod);
+
+  return err;
+}
+
+static grub_err_t
+grub_ext2_close (grub_file_t file)
+{
+  grub_free (file->data);
+
+  grub_dl_unref (my_mod);
+
+  return GRUB_ERR_NONE;
+}
+
+/* Read LEN bytes data from FILE into BUF.  */
+static grub_ssize_t
+grub_ext2_read (grub_file_t file, char *buf, grub_size_t len)
+{
+  struct grub_ext2_data *data = (struct grub_ext2_data *) file->data;
+
+  return grub_ext2_read_file (&data->diropen,
+                             file->read_hook, file->read_hook_data,
+                             file->offset, len, buf);
+}
+
+
+/* Context for grub_ext2_dir.  */
+struct grub_ext2_dir_ctx
+{
+  grub_fs_dir_hook_t hook;
+  void *hook_data;
+  struct grub_ext2_data *data;
+};
+
+/* Helper for grub_ext2_dir.  */
+static int
+grub_ext2_dir_iter (const char *filename, enum grub_fshelp_filetype filetype,
+                   grub_fshelp_node_t node, void *data)
+{
+  struct grub_ext2_dir_ctx *ctx = data;
+  struct grub_dirhook_info info;
+
+  grub_memset (&info, 0, sizeof (info));
+  if (! node->inode_read)
+    {
+      grub_ext2_read_inode (ctx->data, node->ino, &node->inode);
+      if (!grub_errno)
+       node->inode_read = 1;
+      grub_errno = GRUB_ERR_NONE;
+    }
+  if (node->inode_read)
+    {
+      info.mtimeset = 1;
+      info.mtime = grub_le_to_cpu32 (node->inode.mtime);
+    }
+
+  info.dir = ((filetype & GRUB_FSHELP_TYPE_MASK) == GRUB_FSHELP_DIR);
+  if (!info.dir)
+    info.size = (((grub_off_t) grub_le_to_cpu32 (node->inode.size_high)) << 32) | grub_le_to_cpu32 (node->inode.size);
+  grub_free (node);
+  return ctx->hook (filename, &info, ctx->hook_data);
+}
+
+static grub_err_t
+grub_ext2_dir (grub_device_t device, const char *path, grub_fs_dir_hook_t hook,
+              void *hook_data)
+{
+  struct grub_ext2_dir_ctx ctx = {
+    .hook = hook,
+    .hook_data = hook_data
+  };
+  struct grub_fshelp_node *fdiro = 0;
+
+  grub_dl_ref (my_mod);
+
+  ctx.data = grub_ext2_mount (device->disk);
+  if (! ctx.data)
+    goto fail;
+
+  grub_fshelp_find_file (path, &ctx.data->diropen, &fdiro,
+                        grub_ext2_iterate_dir, grub_ext2_read_symlink,
+                        GRUB_FSHELP_DIR);
+  if (grub_errno)
+    goto fail;
+
+  grub_ext2_iterate_dir (fdiro, grub_ext2_dir_iter, &ctx);
+
+ fail:
+  if (fdiro != &ctx.data->diropen)
+    grub_free (fdiro);
+  grub_free (ctx.data);
+
+  grub_dl_unref (my_mod);
+
+  return grub_errno;
+}
+
+static grub_err_t
+grub_ext2_label (grub_device_t device, char **label)
+{
+  struct grub_ext2_data *data;
+  grub_disk_t disk = device->disk;
+
+  grub_dl_ref (my_mod);
+
+  data = grub_ext2_mount (disk);
+  if (data)
+    *label = grub_strndup (data->sblock.volume_name,
+                          sizeof (data->sblock.volume_name));
+  else
+    *label = NULL;
+
+  grub_dl_unref (my_mod);
+
+  grub_free (data);
+
+  return grub_errno;
+}
+
+static grub_err_t
+grub_ext2_uuid (grub_device_t device, char **uuid)
+{
+  struct grub_ext2_data *data;
+  grub_disk_t disk = device->disk;
+
+  grub_dl_ref (my_mod);
+
+  data = grub_ext2_mount (disk);
+  if (data)
+    {
+      *uuid = grub_xasprintf ("%04x%04x-%04x-%04x-%04x-%04x%04x%04x",
+                            grub_be_to_cpu16 (data->sblock.uuid[0]),
+                            grub_be_to_cpu16 (data->sblock.uuid[1]),
+                            grub_be_to_cpu16 (data->sblock.uuid[2]),
+                            grub_be_to_cpu16 (data->sblock.uuid[3]),
+                            grub_be_to_cpu16 (data->sblock.uuid[4]),
+                            grub_be_to_cpu16 (data->sblock.uuid[5]),
+                            grub_be_to_cpu16 (data->sblock.uuid[6]),
+                            grub_be_to_cpu16 (data->sblock.uuid[7]));
+    }
+  else
+    *uuid = NULL;
+
+  grub_dl_unref (my_mod);
+
+  grub_free (data);
+
+  return grub_errno;
+}
+
+/* Get mtime.  */
+static grub_err_t
+grub_ext2_mtime (grub_device_t device, grub_int32_t *tm)
+{
+  struct grub_ext2_data *data;
+  grub_disk_t disk = device->disk;
+
+  grub_dl_ref (my_mod);
+
+  data = grub_ext2_mount (disk);
+  if (!data)
+    *tm = 0;
+  else
+    *tm = grub_le_to_cpu32 (data->sblock.utime);
+
+  grub_dl_unref (my_mod);
+
+  grub_free (data);
+
+  return grub_errno;
+
+}
+
+
+\f
+static struct grub_fs grub_ext2_fs =
+  {
+    .name = "ext2",
+    .fs_dir = grub_ext2_dir,
+    .fs_open = grub_ext2_open,
+    .fs_read = grub_ext2_read,
+    .fs_close = grub_ext2_close,
+    .fs_label = grub_ext2_label,
+    .fs_uuid = grub_ext2_uuid,
+    .fs_mtime = grub_ext2_mtime,
+#ifdef GRUB_UTIL
+    .reserved_first_sector = 1,
+    .blocklist_install = 1,
+#endif
+    .next = 0
+  };
+
+GRUB_MOD_INIT(ext2)
+{
+  grub_fs_register (&grub_ext2_fs);
+  my_mod = mod;
+}
+
+GRUB_MOD_FINI(ext2)
+{
+  grub_fs_unregister (&grub_ext2_fs);
+}
index 6d4ce0d5f75af1fc0264e44af58032cb42e41a4b..439ec41000c32ffad7100bcdb770d292337f5e7e 100644 (file)
@@ -965,10 +965,8 @@ grub_fat_dir (grub_device_t device, const char *path, grub_fs_dir_hook_t hook,
       info.dir = !! (ctxt.dir.attr & GRUB_FAT_ATTR_DIRECTORY);
       info.case_insensitive = 1;
 
-      #ifdef MODE_EXFAT
       if (!info.dir)
          info.size = ctxt.dir.file_size;
-      #endif
       
 #ifdef MODE_EXFAT
       if (!ctxt.dir.have_stream)
@@ -1276,54 +1274,6 @@ GRUB_MOD_FINI(fat)
 
 #ifdef MODE_EXFAT
 
-static int grub_fat_add_chunk(ventoy_img_chunk_list *chunk_list, grub_uint64_t sector, grub_uint64_t size, grub_uint32_t log_sector_size)
-{
-    ventoy_img_chunk *last_chunk;
-    ventoy_img_chunk *new_chunk;
-    
-    if (chunk_list->cur_chunk == 0)
-    {
-        chunk_list->chunk[0].img_start_sector = 0;
-        chunk_list->chunk[0].img_end_sector = (size >> 11) - 1;
-        chunk_list->chunk[0].disk_start_sector = sector;
-        chunk_list->chunk[0].disk_end_sector = sector + (size >> log_sector_size) - 1;
-        chunk_list->cur_chunk = 1;
-        return 0;
-    }
-
-    last_chunk = chunk_list->chunk + chunk_list->cur_chunk - 1;
-    if (last_chunk->disk_end_sector + 1 == sector)
-    {
-        last_chunk->img_end_sector  += (size >> 11);
-        last_chunk->disk_end_sector += (size >> log_sector_size);
-        return 0;
-    }
-
-    if (chunk_list->cur_chunk == chunk_list->max_chunk)
-    {
-        new_chunk = grub_realloc(chunk_list->chunk, chunk_list->max_chunk * 2 * sizeof(ventoy_img_chunk));
-        if (NULL == new_chunk)
-        {
-            return -1;
-        }
-        chunk_list->chunk = new_chunk;
-        chunk_list->max_chunk *= 2;
-
-        /* issue: update last_chunk */
-        last_chunk = chunk_list->chunk + chunk_list->cur_chunk - 1;
-    }
-
-    new_chunk = chunk_list->chunk + chunk_list->cur_chunk;
-    new_chunk->img_start_sector = last_chunk->img_end_sector + 1;
-    new_chunk->img_end_sector = new_chunk->img_start_sector + (size >> 11) - 1;
-    new_chunk->disk_start_sector = sector;
-    new_chunk->disk_end_sector = sector + (size >> log_sector_size) - 1;
-
-    chunk_list->cur_chunk++;
-
-    return 0;
-}
-
 int grub_fat_get_file_chunk(grub_uint64_t part_start, grub_file_t file, ventoy_img_chunk_list *chunk_list)
 {
     grub_size_t size;
@@ -1433,7 +1383,7 @@ int grub_fat_get_file_chunk(grub_uint64_t part_start, grub_file_t file, ventoy_i
       if (size > len)
            size = len;
 
-    grub_fat_add_chunk(chunk_list, sector, size, disk->log_sector_size);
+    grub_disk_blocklist_read(chunk_list, sector, size, disk->log_sector_size);
 
       len -= size;
       logical_cluster++;
diff --git a/GRUB2/grub-2.04/grub-core/fs/ntfs.c b/GRUB2/grub-2.04/grub-core/fs/ntfs.c
new file mode 100644 (file)
index 0000000..e28b82a
--- /dev/null
@@ -0,0 +1,1239 @@
+/* ntfs.c - NTFS filesystem */
+/*
+ *  GRUB  --  GRand Unified Bootloader
+ *  Copyright (C) 2007,2008,2009 Free Software Foundation, Inc.
+ *
+ *  This program 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.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#define grub_fshelp_node grub_ntfs_file 
+
+#include <grub/file.h>
+#include <grub/mm.h>
+#include <grub/misc.h>
+#include <grub/disk.h>
+#include <grub/dl.h>
+#include <grub/fshelp.h>
+#include <grub/ntfs.h>
+#include <grub/charset.h>
+
+GRUB_MOD_LICENSE ("GPLv3+");
+
+static grub_dl_t my_mod;
+
+#define grub_fshelp_node grub_ntfs_file 
+
+static inline grub_uint16_t
+u16at (void *ptr, grub_size_t ofs)
+{
+  return grub_le_to_cpu16 (grub_get_unaligned16 ((char *) ptr + ofs));
+}
+
+static inline grub_uint32_t
+u32at (void *ptr, grub_size_t ofs)
+{
+  return grub_le_to_cpu32 (grub_get_unaligned32 ((char *) ptr + ofs));
+}
+
+static inline grub_uint64_t
+u64at (void *ptr, grub_size_t ofs)
+{
+  return grub_le_to_cpu64 (grub_get_unaligned64 ((char *) ptr + ofs));
+}
+
+grub_ntfscomp_func_t grub_ntfscomp_func;
+
+static grub_err_t
+fixup (grub_uint8_t *buf, grub_size_t len, const grub_uint8_t *magic)
+{
+  grub_uint16_t ss;
+  grub_uint8_t *pu;
+  grub_uint16_t us;
+
+  COMPILE_TIME_ASSERT ((1 << GRUB_NTFS_BLK_SHR) == GRUB_DISK_SECTOR_SIZE);
+
+  if (grub_memcmp (buf, magic, 4))
+    return grub_error (GRUB_ERR_BAD_FS, "%s label not found", magic);
+
+  ss = u16at (buf, 6) - 1;
+  if (ss != len)
+    return grub_error (GRUB_ERR_BAD_FS, "size not match");
+  pu = buf + u16at (buf, 4);
+  us = u16at (pu, 0);
+  buf -= 2;
+  while (ss > 0)
+    {
+      buf += GRUB_DISK_SECTOR_SIZE;
+      pu += 2;
+      if (u16at (buf, 0) != us)
+       return grub_error (GRUB_ERR_BAD_FS, "fixup signature not match");
+      buf[0] = pu[0];
+      buf[1] = pu[1];
+      ss--;
+    }
+
+  return 0;
+}
+
+static grub_err_t read_mft (struct grub_ntfs_data *data, grub_uint8_t *buf,
+                           grub_uint64_t mftno);
+static grub_err_t read_attr (struct grub_ntfs_attr *at, grub_uint8_t *dest,
+                            grub_disk_addr_t ofs, grub_size_t len,
+                            int cached,
+                            grub_disk_read_hook_t read_hook,
+                            void *read_hook_data);
+
+static grub_err_t read_data (struct grub_ntfs_attr *at, grub_uint8_t *pa,
+                            grub_uint8_t *dest,
+                            grub_disk_addr_t ofs, grub_size_t len,
+                            int cached,
+                            grub_disk_read_hook_t read_hook,
+                            void *read_hook_data);
+
+static void
+init_attr (struct grub_ntfs_attr *at, struct grub_ntfs_file *mft)
+{
+  at->mft = mft;
+  at->flags = (mft == &mft->data->mmft) ? GRUB_NTFS_AF_MMFT : 0;
+  at->attr_nxt = mft->buf + u16at (mft->buf, 0x14);
+  at->attr_end = at->emft_buf = at->edat_buf = at->sbuf = NULL;
+}
+
+static void
+free_attr (struct grub_ntfs_attr *at)
+{
+  grub_free (at->emft_buf);
+  grub_free (at->edat_buf);
+  grub_free (at->sbuf);
+}
+
+static grub_uint8_t *
+find_attr (struct grub_ntfs_attr *at, grub_uint8_t attr)
+{
+  if (at->flags & GRUB_NTFS_AF_ALST)
+    {
+    retry:
+      while (at->attr_nxt < at->attr_end)
+       {
+         at->attr_cur = at->attr_nxt;
+         at->attr_nxt += u16at (at->attr_cur, 4);
+         if ((*at->attr_cur == attr) || (attr == 0))
+           {
+             grub_uint8_t *new_pos;
+
+             if (at->flags & GRUB_NTFS_AF_MMFT)
+               {
+                 if ((grub_disk_read
+                      (at->mft->data->disk, u32at (at->attr_cur, 0x10), 0,
+                       512, at->emft_buf))
+                     ||
+                     (grub_disk_read
+                      (at->mft->data->disk, u32at (at->attr_cur, 0x14), 0,
+                       512, at->emft_buf + 512)))
+                   return NULL;
+
+                 if (fixup (at->emft_buf, at->mft->data->mft_size,
+                            (const grub_uint8_t *) "FILE"))
+                   return NULL;
+               }
+             else
+               {
+                 if (read_mft (at->mft->data, at->emft_buf,
+                               u32at (at->attr_cur, 0x10)))
+                   return NULL;
+               }
+
+             new_pos = &at->emft_buf[u16at (at->emft_buf, 0x14)];
+             while (*new_pos != 0xFF)
+               {
+                 if ((*new_pos == *at->attr_cur)
+                     && (u16at (new_pos, 0xE) == u16at (at->attr_cur, 0x18)))
+                   {
+                     return new_pos;
+                   }
+                 new_pos += u16at (new_pos, 4);
+               }
+             grub_error (GRUB_ERR_BAD_FS,
+                         "can\'t find 0x%X in attribute list",
+                         (unsigned char) *at->attr_cur);
+             return NULL;
+           }
+       }
+      return NULL;
+    }
+  at->attr_cur = at->attr_nxt;
+  while (*at->attr_cur != 0xFF)
+    {
+      at->attr_nxt += u16at (at->attr_cur, 4);
+      if (*at->attr_cur == GRUB_NTFS_AT_ATTRIBUTE_LIST)
+       at->attr_end = at->attr_cur;
+      if ((*at->attr_cur == attr) || (attr == 0))
+       return at->attr_cur;
+      at->attr_cur = at->attr_nxt;
+    }
+  if (at->attr_end)
+    {
+      grub_uint8_t *pa;
+
+      at->emft_buf = grub_malloc (at->mft->data->mft_size << GRUB_NTFS_BLK_SHR);
+      if (at->emft_buf == NULL)
+       return NULL;
+
+      pa = at->attr_end;
+      if (pa[8])
+       {
+          grub_uint32_t n;
+
+          n = ((u32at (pa, 0x30) + GRUB_DISK_SECTOR_SIZE - 1)
+               & (~(GRUB_DISK_SECTOR_SIZE - 1)));
+         at->attr_cur = at->attr_end;
+         at->edat_buf = grub_malloc (n);
+         if (!at->edat_buf)
+           return NULL;
+         if (read_data (at, pa, at->edat_buf, 0, n, 0, 0, 0))
+           {
+             grub_error (GRUB_ERR_BAD_FS,
+                         "fail to read non-resident attribute list");
+             return NULL;
+           }
+         at->attr_nxt = at->edat_buf;
+         at->attr_end = at->edat_buf + u32at (pa, 0x30);
+       }
+      else
+       {
+         at->attr_nxt = at->attr_end + u16at (pa, 0x14);
+         at->attr_end = at->attr_end + u32at (pa, 4);
+       }
+      at->flags |= GRUB_NTFS_AF_ALST;
+      while (at->attr_nxt < at->attr_end)
+       {
+         if ((*at->attr_nxt == attr) || (attr == 0))
+           break;
+         at->attr_nxt += u16at (at->attr_nxt, 4);
+       }
+      if (at->attr_nxt >= at->attr_end)
+       return NULL;
+
+      if ((at->flags & GRUB_NTFS_AF_MMFT) && (attr == GRUB_NTFS_AT_DATA))
+       {
+         at->flags |= GRUB_NTFS_AF_GPOS;
+         at->attr_cur = at->attr_nxt;
+         pa = at->attr_cur;
+         grub_set_unaligned32 ((char *) pa + 0x10,
+                               grub_cpu_to_le32 (at->mft->data->mft_start));
+         grub_set_unaligned32 ((char *) pa + 0x14,
+                               grub_cpu_to_le32 (at->mft->data->mft_start
+                                                 + 1));
+         pa = at->attr_nxt + u16at (pa, 4);
+         while (pa < at->attr_end)
+           {
+             if (*pa != attr)
+               break;
+             if (read_attr
+                 (at, pa + 0x10,
+                  u32at (pa, 0x10) * (at->mft->data->mft_size << GRUB_NTFS_BLK_SHR),
+                  at->mft->data->mft_size << GRUB_NTFS_BLK_SHR, 0, 0, 0))
+               return NULL;
+             pa += u16at (pa, 4);
+           }
+         at->attr_nxt = at->attr_cur;
+         at->flags &= ~GRUB_NTFS_AF_GPOS;
+       }
+      goto retry;
+    }
+  return NULL;
+}
+
+static grub_uint8_t *
+locate_attr (struct grub_ntfs_attr *at, struct grub_ntfs_file *mft,
+            grub_uint8_t attr)
+{
+  grub_uint8_t *pa;
+
+  init_attr (at, mft);
+  pa = find_attr (at, attr);
+  if (pa == NULL)
+    return NULL;
+  if ((at->flags & GRUB_NTFS_AF_ALST) == 0)
+    {
+      while (1)
+       {
+         pa = find_attr (at, attr);
+         if (pa == NULL)
+           break;
+         if (at->flags & GRUB_NTFS_AF_ALST)
+           return pa;
+       }
+      grub_errno = GRUB_ERR_NONE;
+      free_attr (at);
+      init_attr (at, mft);
+      pa = find_attr (at, attr);
+    }
+  return pa;
+}
+
+static grub_disk_addr_t
+read_run_data (const grub_uint8_t *run, int nn, int sig)
+{
+  grub_uint64_t r = 0;
+
+  if (sig && nn && (run[nn - 1] & 0x80))
+    r = -1;
+
+  grub_memcpy (&r, run, nn);
+
+  return grub_le_to_cpu64 (r);
+}
+
+grub_err_t
+grub_ntfs_read_run_list (struct grub_ntfs_rlst * ctx)
+{
+  grub_uint8_t c1, c2;
+  grub_disk_addr_t val;
+  grub_uint8_t *run;
+
+  run = ctx->cur_run;
+retry:
+  c1 = ((*run) & 0x7);
+  c2 = ((*run) >> 4) & 0x7;
+  run++;
+  if (!c1)
+    {
+      if ((ctx->attr) && (ctx->attr->flags & GRUB_NTFS_AF_ALST))
+       {
+         grub_disk_read_hook_t save_hook;
+
+         save_hook = ctx->comp.disk->read_hook;
+         ctx->comp.disk->read_hook = 0;
+         run = find_attr (ctx->attr, *ctx->attr->attr_cur);
+         ctx->comp.disk->read_hook = save_hook;
+         if (run)
+           {
+             if (run[8] == 0)
+               return grub_error (GRUB_ERR_BAD_FS,
+                                  "$DATA should be non-resident");
+
+             run += u16at (run, 0x20);
+             ctx->curr_lcn = 0;
+             goto retry;
+           }
+       }
+      return grub_error (GRUB_ERR_BAD_FS, "run list overflown");
+    }
+  ctx->curr_vcn = ctx->next_vcn;
+  ctx->next_vcn += read_run_data (run, c1, 0); /* length of current VCN */
+  run += c1;
+  val = read_run_data (run, c2, 1);    /* offset to previous LCN */
+  run += c2;
+  ctx->curr_lcn += val;
+  if (val == 0)
+    ctx->flags |= GRUB_NTFS_RF_BLNK;
+  else
+    ctx->flags &= ~GRUB_NTFS_RF_BLNK;
+  ctx->cur_run = run;
+  return 0;
+}
+
+static grub_disk_addr_t
+grub_ntfs_read_block (grub_fshelp_node_t node, grub_disk_addr_t block)
+{
+  struct grub_ntfs_rlst *ctx;
+
+  ctx = (struct grub_ntfs_rlst *) node;
+  if (block >= ctx->next_vcn)
+    {
+      if (grub_ntfs_read_run_list (ctx))
+       return -1;
+      return ctx->curr_lcn;
+    }
+  else
+    return (ctx->flags & GRUB_NTFS_RF_BLNK) ? 0 : (block -
+                                        ctx->curr_vcn + ctx->curr_lcn);
+}
+
+static grub_err_t
+read_data (struct grub_ntfs_attr *at, grub_uint8_t *pa, grub_uint8_t *dest,
+          grub_disk_addr_t ofs, grub_size_t len, int cached,
+          grub_disk_read_hook_t read_hook, void *read_hook_data)
+{
+  struct grub_ntfs_rlst cc, *ctx;
+
+  if (len == 0)
+    return 0;
+
+  grub_memset (&cc, 0, sizeof (cc));
+  ctx = &cc;
+  ctx->attr = at;
+  ctx->comp.log_spc = at->mft->data->log_spc;
+  ctx->comp.disk = at->mft->data->disk;
+
+  if (read_hook == grub_file_progress_hook)
+    ctx->file = read_hook_data;
+
+  if (pa[8] == 0)
+    {
+      if (ofs + len > u32at (pa, 0x10))
+       return grub_error (GRUB_ERR_BAD_FS, "read out of range");
+      grub_memcpy (dest, pa + u32at (pa, 0x14) + ofs, len);
+      return 0;
+    }
+
+  ctx->cur_run = pa + u16at (pa, 0x20);
+
+  ctx->next_vcn = u32at (pa, 0x10);
+  ctx->curr_lcn = 0;
+
+  if ((pa[0xC] & GRUB_NTFS_FLAG_COMPRESSED)
+      && !(at->flags & GRUB_NTFS_AF_GPOS))
+    {
+      if (!cached)
+       return grub_error (GRUB_ERR_BAD_FS, "attribute can\'t be compressed");
+
+      return (grub_ntfscomp_func) ? grub_ntfscomp_func (dest, ofs, len, ctx)
+       : grub_error (GRUB_ERR_BAD_FS, N_("module `%s' isn't loaded"),
+                     "ntfscomp");
+    }
+
+  ctx->target_vcn = ofs >> (GRUB_NTFS_BLK_SHR + ctx->comp.log_spc);
+  while (ctx->next_vcn <= ctx->target_vcn)
+    {
+      if (grub_ntfs_read_run_list (ctx))
+       return grub_errno;
+    }
+
+  if (at->flags & GRUB_NTFS_AF_GPOS)
+    {
+      grub_disk_addr_t st0, st1;
+      grub_uint64_t m;
+
+      m = (ofs >> GRUB_NTFS_BLK_SHR) & ((1 << ctx->comp.log_spc) - 1);
+
+      st0 =
+       ((ctx->target_vcn - ctx->curr_vcn + ctx->curr_lcn) << ctx->comp.log_spc) + m;
+      st1 = st0 + 1;
+      if (st1 ==
+         (ctx->next_vcn - ctx->curr_vcn + ctx->curr_lcn) << ctx->comp.log_spc)
+       {
+         if (grub_ntfs_read_run_list (ctx))
+           return grub_errno;
+         st1 = ctx->curr_lcn << ctx->comp.log_spc;
+       }
+      grub_set_unaligned32 (dest, grub_cpu_to_le32 (st0));
+      grub_set_unaligned32 (dest + 4, grub_cpu_to_le32 (st1));
+      return 0;
+    }
+
+  grub_fshelp_read_file (ctx->comp.disk, (grub_fshelp_node_t) ctx,
+                        read_hook, read_hook_data, ofs, len,
+                        (char *) dest,
+                        grub_ntfs_read_block, ofs + len,
+                        ctx->comp.log_spc, 0);
+  return grub_errno;
+}
+
+static grub_err_t
+read_attr (struct grub_ntfs_attr *at, grub_uint8_t *dest, grub_disk_addr_t ofs,
+          grub_size_t len, int cached,
+          grub_disk_read_hook_t read_hook, void *read_hook_data)
+{
+  grub_uint8_t *save_cur;
+  grub_uint8_t attr;
+  grub_uint8_t *pp;
+  grub_err_t ret;
+
+  save_cur = at->attr_cur;
+  at->attr_nxt = at->attr_cur;
+  attr = *at->attr_nxt;
+  if (at->flags & GRUB_NTFS_AF_ALST)
+    {
+      grub_uint8_t *pa;
+      grub_disk_addr_t vcn;
+
+      /* If compression is possible make sure that we include possible
+        compressed block size.  */
+      if (GRUB_NTFS_LOG_COM_SEC >= at->mft->data->log_spc)
+       vcn = ((ofs >> GRUB_NTFS_COM_LOG_LEN)
+              << (GRUB_NTFS_LOG_COM_SEC - at->mft->data->log_spc)) & ~0xFULL;
+      else
+       vcn = ofs >> (at->mft->data->log_spc + GRUB_NTFS_BLK_SHR);
+      pa = at->attr_nxt + u16at (at->attr_nxt, 4);
+      while (pa < at->attr_end)
+       {
+         if (*pa != attr)
+           break;
+         if (u32at (pa, 8) > vcn)
+           break;
+         at->attr_nxt = pa;
+         pa += u16at (pa, 4);
+       }
+    }
+  pp = find_attr (at, attr);
+  if (pp)
+    ret = read_data (at, pp, dest, ofs, len, cached,
+                    read_hook, read_hook_data);
+  else
+    ret =
+      (grub_errno) ? grub_errno : grub_error (GRUB_ERR_BAD_FS,
+                                             "attribute not found");
+  at->attr_cur = save_cur;
+  return ret;
+}
+
+static grub_err_t
+read_mft (struct grub_ntfs_data *data, grub_uint8_t *buf, grub_uint64_t mftno)
+{
+  if (read_attr
+      (&data->mmft.attr, buf, mftno * ((grub_disk_addr_t) data->mft_size << GRUB_NTFS_BLK_SHR),
+       data->mft_size << GRUB_NTFS_BLK_SHR, 0, 0, 0))
+    return grub_error (GRUB_ERR_BAD_FS, "read MFT 0x%llx fails", (unsigned long long) mftno);
+  return fixup (buf, data->mft_size, (const grub_uint8_t *) "FILE");
+}
+
+static grub_err_t
+init_file (struct grub_ntfs_file *mft, grub_uint64_t mftno)
+{
+  unsigned short flag;
+
+  mft->inode_read = 1;
+
+  mft->buf = grub_malloc (mft->data->mft_size << GRUB_NTFS_BLK_SHR);
+  if (mft->buf == NULL)
+    return grub_errno;
+
+  if (read_mft (mft->data, mft->buf, mftno))
+    return grub_errno;
+
+  flag = u16at (mft->buf, 0x16);
+  if ((flag & 1) == 0)
+    return grub_error (GRUB_ERR_BAD_FS, "MFT 0x%llx is not in use",
+                      (unsigned long long) mftno);
+
+  if ((flag & 2) == 0)
+    {
+      grub_uint8_t *pa;
+
+      pa = locate_attr (&mft->attr, mft, GRUB_NTFS_AT_DATA);
+      if (pa == NULL)
+       return grub_error (GRUB_ERR_BAD_FS, "no $DATA in MFT 0x%llx",
+                          (unsigned long long) mftno);
+
+      if (!pa[8])
+       mft->size = u32at (pa, 0x10);
+      else
+       mft->size = u64at (pa, 0x30);
+
+      if ((mft->attr.flags & GRUB_NTFS_AF_ALST) == 0)
+       mft->attr.attr_end = 0; /*  Don't jump to attribute list */
+    }
+  else
+    init_attr (&mft->attr, mft);
+
+  return 0;
+}
+
+static void
+free_file (struct grub_ntfs_file *mft)
+{
+  free_attr (&mft->attr);
+  grub_free (mft->buf);
+}
+
+static char *
+get_utf8 (grub_uint8_t *in, grub_size_t len)
+{
+  grub_uint8_t *buf;
+  grub_uint16_t *tmp;
+  grub_size_t i;
+
+  buf = grub_malloc (len * GRUB_MAX_UTF8_PER_UTF16 + 1);
+  tmp = grub_malloc (len * sizeof (tmp[0]));
+  if (!buf || !tmp)
+    {
+      grub_free (buf);
+      grub_free (tmp);
+      return NULL;
+    }
+  for (i = 0; i < len; i++)
+    tmp[i] = grub_le_to_cpu16 (grub_get_unaligned16 (in + 2 * i));
+  *grub_utf16_to_utf8 (buf, tmp, len) = '\0';
+  grub_free (tmp);
+  return (char *) buf;
+}
+
+static int
+list_file (struct grub_ntfs_file *diro, grub_uint8_t *pos,
+          grub_fshelp_iterate_dir_hook_t hook, void *hook_data)
+{
+  grub_uint8_t *np;
+  int ns;
+
+  while (1)
+    {
+      grub_uint8_t namespace;
+      char *ustr;
+
+      if (pos[0xC] & 2)                /* end signature */
+       break;
+
+      np = pos + 0x50;
+      ns = *(np++);
+      namespace = *(np++);
+
+      /*
+       *  Ignore files in DOS namespace, as they will reappear as Win32
+       *  names.
+       */
+      if ((ns) && (namespace != 2))
+       {
+         enum grub_fshelp_filetype type;
+         struct grub_ntfs_file *fdiro;
+         grub_uint32_t attr;
+
+         attr = u32at (pos, 0x48);
+         if (attr & GRUB_NTFS_ATTR_REPARSE)
+           type = GRUB_FSHELP_SYMLINK;
+         else if (attr & GRUB_NTFS_ATTR_DIRECTORY)
+           type = GRUB_FSHELP_DIR;
+         else
+           type = GRUB_FSHELP_REG;
+
+         fdiro = grub_zalloc (sizeof (struct grub_ntfs_file));
+         if (!fdiro)
+           return 0;
+
+         fdiro->data = diro->data;
+         fdiro->ino = u64at (pos, 0) & 0xffffffffffffULL;
+         fdiro->mtime = u64at (pos, 0x20);
+
+         ustr = get_utf8 (np, ns);
+         if (ustr == NULL)
+           {
+             grub_free (fdiro);
+             return 0;
+           }
+          if (namespace)
+            type |= GRUB_FSHELP_CASE_INSENSITIVE;
+
+         if (hook (ustr, type, fdiro, hook_data))
+           {
+             grub_free (ustr);
+             return 1;
+           }
+
+         grub_free (ustr);
+       }
+      pos += u16at (pos, 8);
+    }
+  return 0;
+}
+
+struct symlink_descriptor
+{
+  grub_uint32_t type;
+  grub_uint32_t total_len;
+  grub_uint16_t off1;
+  grub_uint16_t len1;
+  grub_uint16_t off2;
+  grub_uint16_t len2;
+} GRUB_PACKED;
+
+static char *
+grub_ntfs_read_symlink (grub_fshelp_node_t node)
+{
+  struct grub_ntfs_file *mft;
+  struct symlink_descriptor symdesc;
+  grub_err_t err;
+  grub_uint8_t *buf16;
+  char *buf, *end;
+  grub_size_t len;
+  grub_uint8_t *pa;
+  grub_size_t off;
+
+  mft = (struct grub_ntfs_file *) node;
+
+  mft->buf = grub_malloc (mft->data->mft_size << GRUB_NTFS_BLK_SHR);
+  if (mft->buf == NULL)
+    return NULL;
+
+  if (read_mft (mft->data, mft->buf, mft->ino))
+    return NULL;
+
+  pa = locate_attr (&mft->attr, mft, GRUB_NTFS_AT_SYMLINK);
+  if (pa == NULL)
+    {
+      grub_error (GRUB_ERR_BAD_FS, "no $SYMLINK in MFT 0x%llx",
+                 (unsigned long long) mft->ino);
+      return NULL;
+    }
+
+  err = read_attr (&mft->attr, (grub_uint8_t *) &symdesc, 0,
+                  sizeof (struct symlink_descriptor), 1, 0, 0);
+  if (err)
+    return NULL;
+
+  switch (grub_cpu_to_le32 (symdesc.type))
+    {
+    case 0xa000000c:
+      off = (sizeof (struct symlink_descriptor) + 4
+            + grub_cpu_to_le32 (symdesc.off1));
+      len = grub_cpu_to_le32 (symdesc.len1);
+      break;
+    case 0xa0000003:
+      off = (sizeof (struct symlink_descriptor)
+            + grub_cpu_to_le32 (symdesc.off1));
+      len = grub_cpu_to_le32 (symdesc.len1);
+      break;
+    default:
+      grub_error (GRUB_ERR_BAD_FS, "symlink type invalid (%x)",
+                 grub_cpu_to_le32 (symdesc.type));
+      return NULL;
+    }
+
+  buf16 = grub_malloc (len);
+  if (!buf16)
+    return NULL;
+
+  err = read_attr (&mft->attr, buf16, off, len, 1, 0, 0);
+  if (err)
+    return NULL;
+
+  buf = get_utf8 (buf16, len / 2);
+  if (!buf)
+    {
+      grub_free (buf16);
+      return NULL;
+    }
+  grub_free (buf16);
+
+  for (end = buf; *end; end++)
+    if (*end == '\\')
+      *end = '/';
+
+  /* Split the sequence to avoid GCC thinking that this is a trigraph.  */
+  if (grub_memcmp (buf, "/?" "?/", 4) == 0 && buf[5] == ':' && buf[6] == '/'
+      && grub_isalpha (buf[4]))
+    {
+      grub_memmove (buf, buf + 6, end - buf + 1 - 6);
+      end -= 6; 
+    }
+  return buf;
+}
+
+static int
+grub_ntfs_iterate_dir (grub_fshelp_node_t dir,
+                      grub_fshelp_iterate_dir_hook_t hook, void *hook_data)
+{
+  grub_uint8_t *bitmap;
+  struct grub_ntfs_attr attr, *at;
+  grub_uint8_t *cur_pos, *indx, *bmp;
+  int ret = 0;
+  grub_size_t bitmap_len;
+  struct grub_ntfs_file *mft;
+
+  mft = (struct grub_ntfs_file *) dir;
+
+  if (!mft->inode_read)
+    {
+      if (init_file (mft, mft->ino))
+       return 0;
+    }
+
+  indx = NULL;
+  bmp = NULL;
+
+  at = &attr;
+  init_attr (at, mft);
+  while (1)
+    {
+      cur_pos = find_attr (at, GRUB_NTFS_AT_INDEX_ROOT);
+      if (cur_pos == NULL)
+       {
+         grub_error (GRUB_ERR_BAD_FS, "no $INDEX_ROOT");
+         goto done;
+       }
+
+      /* Resident, Namelen=4, Offset=0x18, Flags=0x00, Name="$I30" */
+      if ((u32at (cur_pos, 8) != 0x180400) ||
+         (u32at (cur_pos, 0x18) != 0x490024) ||
+         (u32at (cur_pos, 0x1C) != 0x300033))
+       continue;
+      cur_pos += u16at (cur_pos, 0x14);
+      if (*cur_pos != 0x30)    /* Not filename index */
+       continue;
+      break;
+    }
+
+  cur_pos += 0x10;             /* Skip index root */
+  ret = list_file (mft, cur_pos + u16at (cur_pos, 0), hook, hook_data);
+  if (ret)
+    goto done;
+
+  bitmap = NULL;
+  bitmap_len = 0;
+  free_attr (at);
+  init_attr (at, mft);
+  while ((cur_pos = find_attr (at, GRUB_NTFS_AT_BITMAP)) != NULL)
+    {
+      int ofs;
+
+      ofs = cur_pos[0xA];
+      /* Namelen=4, Name="$I30" */
+      if ((cur_pos[9] == 4) &&
+         (u32at (cur_pos, ofs) == 0x490024) &&
+         (u32at (cur_pos, ofs + 4) == 0x300033))
+       {
+          int is_resident = (cur_pos[8] == 0);
+
+          bitmap_len = ((is_resident) ? u32at (cur_pos, 0x10) :
+                        u32at (cur_pos, 0x28));
+
+          bmp = grub_malloc (bitmap_len);
+          if (bmp == NULL)
+            goto done;
+
+         if (is_resident)
+           {
+              grub_memcpy (bmp, cur_pos + u16at (cur_pos, 0x14),
+                           bitmap_len);
+           }
+          else
+            {
+              if (read_data (at, cur_pos, bmp, 0, bitmap_len, 0, 0, 0))
+                {
+                  grub_error (GRUB_ERR_BAD_FS,
+                              "fails to read non-resident $BITMAP");
+                  goto done;
+                }
+              bitmap_len = u32at (cur_pos, 0x30);
+            }
+
+          bitmap = bmp;
+         break;
+       }
+    }
+
+  free_attr (at);
+  cur_pos = locate_attr (at, mft, GRUB_NTFS_AT_INDEX_ALLOCATION);
+  while (cur_pos != NULL)
+    {
+      /* Non-resident, Namelen=4, Offset=0x40, Flags=0, Name="$I30" */
+      if ((u32at (cur_pos, 8) == 0x400401) &&
+         (u32at (cur_pos, 0x40) == 0x490024) &&
+         (u32at (cur_pos, 0x44) == 0x300033))
+       break;
+      cur_pos = find_attr (at, GRUB_NTFS_AT_INDEX_ALLOCATION);
+    }
+
+  if ((!cur_pos) && (bitmap))
+    {
+      grub_error (GRUB_ERR_BAD_FS, "$BITMAP without $INDEX_ALLOCATION");
+      goto done;
+    }
+
+  if (bitmap)
+    {
+      grub_disk_addr_t i;
+      grub_uint8_t v;
+
+      indx = grub_malloc (mft->data->idx_size << GRUB_NTFS_BLK_SHR);
+      if (indx == NULL)
+       goto done;
+
+      v = 1;
+      for (i = 0; i < (grub_disk_addr_t)bitmap_len * 8; i++)
+       {
+         if (*bitmap & v)
+           {
+             if ((read_attr
+                  (at, indx, i * (mft->data->idx_size << GRUB_NTFS_BLK_SHR),
+                   (mft->data->idx_size << GRUB_NTFS_BLK_SHR), 0, 0, 0))
+                 || (fixup (indx, mft->data->idx_size,
+                            (const grub_uint8_t *) "INDX")))
+               goto done;
+             ret = list_file (mft, &indx[0x18 + u16at (indx, 0x18)],
+                              hook, hook_data);
+             if (ret)
+               goto done;
+           }
+         v <<= 1;
+         if (!v)
+           {
+             v = 1;
+             bitmap++;
+           }
+       }
+    }
+
+done:
+  free_attr (at);
+  grub_free (indx);
+  grub_free (bmp);
+
+  return ret;
+}
+
+static struct grub_ntfs_data *
+grub_ntfs_mount (grub_disk_t disk)
+{
+  struct grub_ntfs_bpb bpb;
+  struct grub_ntfs_data *data = 0;
+  grub_uint32_t spc;
+
+  if (!disk)
+    goto fail;
+
+  data = (struct grub_ntfs_data *) grub_zalloc (sizeof (*data));
+  if (!data)
+    goto fail;
+
+  data->disk = disk;
+
+  /* Read the BPB.  */
+  if (grub_disk_read (disk, 0, 0, sizeof (bpb), &bpb))
+    goto fail;
+
+  if (grub_memcmp ((char *) &bpb.oem_name, "NTFS", 4) != 0
+      || bpb.sectors_per_cluster == 0
+      || (bpb.sectors_per_cluster & (bpb.sectors_per_cluster - 1)) != 0
+      || bpb.bytes_per_sector == 0
+      || (bpb.bytes_per_sector & (bpb.bytes_per_sector - 1)) != 0)
+    goto fail;
+
+  spc = (((grub_uint32_t) bpb.sectors_per_cluster
+         * (grub_uint32_t) grub_le_to_cpu16 (bpb.bytes_per_sector))
+        >> GRUB_NTFS_BLK_SHR);
+  if (spc == 0)
+    goto fail;
+
+  for (data->log_spc = 0; (1U << data->log_spc) < spc; data->log_spc++);
+
+  if (bpb.clusters_per_mft > 0)
+    data->mft_size = ((grub_disk_addr_t) bpb.clusters_per_mft) << data->log_spc;
+  else if (-bpb.clusters_per_mft < GRUB_NTFS_BLK_SHR || -bpb.clusters_per_mft >= 31)
+    goto fail;
+  else
+    data->mft_size = 1ULL << (-bpb.clusters_per_mft - GRUB_NTFS_BLK_SHR);
+
+  if (bpb.clusters_per_index > 0)
+    data->idx_size = (((grub_disk_addr_t) bpb.clusters_per_index)
+                     << data->log_spc);
+  else if (-bpb.clusters_per_index < GRUB_NTFS_BLK_SHR || -bpb.clusters_per_index >= 31)
+    goto fail;
+  else
+    data->idx_size = 1ULL << (-bpb.clusters_per_index - GRUB_NTFS_BLK_SHR);
+
+  data->mft_start = grub_le_to_cpu64 (bpb.mft_lcn) << data->log_spc;
+
+  if ((data->mft_size > GRUB_NTFS_MAX_MFT) || (data->idx_size > GRUB_NTFS_MAX_IDX))
+    goto fail;
+
+  data->mmft.data = data;
+  data->cmft.data = data;
+
+  data->mmft.buf = grub_malloc (data->mft_size << GRUB_NTFS_BLK_SHR);
+  if (!data->mmft.buf)
+    goto fail;
+
+  if (grub_disk_read
+      (disk, data->mft_start, 0, data->mft_size << GRUB_NTFS_BLK_SHR, data->mmft.buf))
+    goto fail;
+
+  data->uuid = grub_le_to_cpu64 (bpb.num_serial);
+
+  if (fixup (data->mmft.buf, data->mft_size, (const grub_uint8_t *) "FILE"))
+    goto fail;
+
+  if (!locate_attr (&data->mmft.attr, &data->mmft, GRUB_NTFS_AT_DATA))
+    goto fail;
+
+  if (init_file (&data->cmft, GRUB_NTFS_FILE_ROOT))
+    goto fail;
+
+  return data;
+
+fail:
+  grub_error (GRUB_ERR_BAD_FS, "not an ntfs filesystem");
+
+  if (data)
+    {
+      free_file (&data->mmft);
+      free_file (&data->cmft);
+      grub_free (data);
+    }
+  return 0;
+}
+
+/* Context for grub_ntfs_dir.  */
+struct grub_ntfs_dir_ctx
+{
+  grub_fs_dir_hook_t hook;
+  void *hook_data;
+};
+
+/* Helper for grub_ntfs_dir.  */
+static int
+grub_ntfs_dir_iter (const char *filename, enum grub_fshelp_filetype filetype,
+                   grub_fshelp_node_t node, void *data)
+{
+  struct grub_ntfs_dir_ctx *ctx = data;
+  struct grub_dirhook_info info;
+
+  grub_memset (&info, 0, sizeof (info));
+  info.dir = ((filetype & GRUB_FSHELP_TYPE_MASK) == GRUB_FSHELP_DIR);
+  info.mtimeset = 1;
+  info.mtime = grub_divmod64 (node->mtime, 10000000, 0) 
+    - 86400ULL * 365 * (1970 - 1601)
+    - 86400ULL * ((1970 - 1601) / 4) + 86400ULL * ((1970 - 1601) / 100);
+  if (!info.dir) 
+    info.size = node->size;
+  grub_free (node);
+  return ctx->hook (filename, &info, ctx->hook_data);
+}
+
+static grub_err_t
+grub_ntfs_dir (grub_device_t device, const char *path,
+              grub_fs_dir_hook_t hook, void *hook_data)
+{
+  struct grub_ntfs_dir_ctx ctx = { hook, hook_data };
+  struct grub_ntfs_data *data = 0;
+  struct grub_fshelp_node *fdiro = 0;
+
+  grub_dl_ref (my_mod);
+
+  data = grub_ntfs_mount (device->disk);
+  if (!data)
+    goto fail;
+
+  grub_fshelp_find_file (path, &data->cmft, &fdiro, grub_ntfs_iterate_dir,
+                        grub_ntfs_read_symlink, GRUB_FSHELP_DIR);
+
+  if (grub_errno)
+    goto fail;
+
+  grub_ntfs_iterate_dir (fdiro, grub_ntfs_dir_iter, &ctx);
+
+fail:
+  if ((fdiro) && (fdiro != &data->cmft))
+    {
+      free_file (fdiro);
+      grub_free (fdiro);
+    }
+  if (data)
+    {
+      free_file (&data->mmft);
+      free_file (&data->cmft);
+      grub_free (data);
+    }
+
+  grub_dl_unref (my_mod);
+
+  return grub_errno;
+}
+
+static grub_err_t
+grub_ntfs_open (grub_file_t file, const char *name)
+{
+  struct grub_ntfs_data *data = 0;
+  struct grub_fshelp_node *mft = 0;
+
+  grub_dl_ref (my_mod);
+
+  data = grub_ntfs_mount (file->device->disk);
+  if (!data)
+    goto fail;
+
+  grub_fshelp_find_file (name, &data->cmft, &mft, grub_ntfs_iterate_dir,
+                        grub_ntfs_read_symlink, GRUB_FSHELP_REG);
+
+  if (grub_errno)
+    goto fail;
+
+  if (mft != &data->cmft)
+    {
+      free_file (&data->cmft);
+      grub_memcpy (&data->cmft, mft, sizeof (*mft));
+      grub_free (mft);
+      if (!data->cmft.inode_read)
+       {
+         if (init_file (&data->cmft, data->cmft.ino))
+           goto fail;
+       }
+    }
+
+  file->size = data->cmft.size;
+  file->data = data;
+  file->offset = 0;
+
+  return 0;
+
+fail:
+  if (data)
+    {
+      free_file (&data->mmft);
+      free_file (&data->cmft);
+      grub_free (data);
+    }
+
+  grub_dl_unref (my_mod);
+
+  return grub_errno;
+}
+
+static grub_ssize_t
+grub_ntfs_read (grub_file_t file, char *buf, grub_size_t len)
+{
+  struct grub_ntfs_file *mft;
+
+  mft = &((struct grub_ntfs_data *) file->data)->cmft;
+  if (file->read_hook)
+    mft->attr.save_pos = 1;
+
+  read_attr (&mft->attr, (grub_uint8_t *) buf, file->offset, len, 1,
+            file->read_hook, file->read_hook_data);
+  return (grub_errno) ? -1 : (grub_ssize_t) len;
+}
+
+static grub_err_t
+grub_ntfs_close (grub_file_t file)
+{
+  struct grub_ntfs_data *data;
+
+  data = file->data;
+
+  if (data)
+    {
+      free_file (&data->mmft);
+      free_file (&data->cmft);
+      grub_free (data);
+    }
+
+  grub_dl_unref (my_mod);
+
+  return grub_errno;
+}
+
+static grub_err_t
+grub_ntfs_label (grub_device_t device, char **label)
+{
+  struct grub_ntfs_data *data = 0;
+  struct grub_fshelp_node *mft = 0;
+  grub_uint8_t *pa;
+
+  grub_dl_ref (my_mod);
+
+  *label = 0;
+
+  data = grub_ntfs_mount (device->disk);
+  if (!data)
+    goto fail;
+
+  grub_fshelp_find_file ("/$Volume", &data->cmft, &mft, grub_ntfs_iterate_dir,
+                        0, GRUB_FSHELP_REG);
+
+  if (grub_errno)
+    goto fail;
+
+  if (!mft->inode_read)
+    {
+      mft->buf = grub_malloc (mft->data->mft_size << GRUB_NTFS_BLK_SHR);
+      if (mft->buf == NULL)
+       goto fail;
+
+      if (read_mft (mft->data, mft->buf, mft->ino))
+       goto fail;
+    }
+
+  init_attr (&mft->attr, mft);
+  pa = find_attr (&mft->attr, GRUB_NTFS_AT_VOLUME_NAME);
+  if ((pa) && (pa[8] == 0) && (u32at (pa, 0x10)))
+    {
+      int len;
+
+      len = u32at (pa, 0x10) / 2;
+      pa += u16at (pa, 0x14);
+      *label = get_utf8 (pa, len);
+    }
+
+fail:
+  if ((mft) && (mft != &data->cmft))
+    {
+      free_file (mft);
+      grub_free (mft);
+    }
+  if (data)
+    {
+      free_file (&data->mmft);
+      free_file (&data->cmft);
+      grub_free (data);
+    }
+
+  grub_dl_unref (my_mod);
+
+  return grub_errno;
+}
+
+static grub_err_t
+grub_ntfs_uuid (grub_device_t device, char **uuid)
+{
+  struct grub_ntfs_data *data;
+  grub_disk_t disk = device->disk;
+
+  grub_dl_ref (my_mod);
+
+  data = grub_ntfs_mount (disk);
+  if (data)
+    {
+      char *ptr;
+      *uuid = grub_xasprintf ("%016llx", (unsigned long long) data->uuid);
+      if (*uuid)
+       for (ptr = *uuid; *ptr; ptr++)
+         *ptr = grub_toupper (*ptr);
+      free_file (&data->mmft);
+      free_file (&data->cmft);
+      grub_free (data);
+    }
+  else
+    *uuid = NULL;
+
+  grub_dl_unref (my_mod);
+
+  return grub_errno;
+}
+
+static struct grub_fs grub_ntfs_fs =
+  {
+    .name = "ntfs",
+    .fs_dir = grub_ntfs_dir,
+    .fs_open = grub_ntfs_open,
+    .fs_read = grub_ntfs_read,
+    .fs_close = grub_ntfs_close,
+    .fs_label = grub_ntfs_label,
+    .fs_uuid = grub_ntfs_uuid,
+#ifdef GRUB_UTIL
+    .reserved_first_sector = 1,
+    .blocklist_install = 1,
+#endif
+    .next = 0
+};
+
+GRUB_MOD_INIT (ntfs)
+{
+  grub_fs_register (&grub_ntfs_fs);
+  my_mod = mod;
+}
+
+GRUB_MOD_FINI (ntfs)
+{
+  grub_fs_unregister (&grub_ntfs_fs);
+}
index c5ea54e4cab15d3cb913dc64d63d436ff58def0c..dab046b6ead21c880bdc9689cb3544f65b0be962 100644 (file)
@@ -1152,6 +1152,8 @@ grub_udf_dir_iter (const char *filename, enum grub_fshelp_filetype filetype,
 
       info.mtime -= 60 * tz;
     }
+  if (!info.dir)
+    info.size = U64 (node->block.fe.file_size);
   grub_free (node);
   return ctx->hook (filename, &info, ctx->hook_data);
 }
diff --git a/GRUB2/grub-2.04/grub-core/fs/xfs.c b/GRUB2/grub-2.04/grub-core/fs/xfs.c
new file mode 100644 (file)
index 0000000..a5c8d93
--- /dev/null
@@ -0,0 +1,1164 @@
+/* xfs.c - XFS.  */
+/*
+ *  GRUB  --  GRand Unified Bootloader
+ *  Copyright (C) 2005,2006,2007,2008,2009  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 <grub/err.h>
+#include <grub/file.h>
+#include <grub/mm.h>
+#include <grub/misc.h>
+#include <grub/disk.h>
+#include <grub/dl.h>
+#include <grub/types.h>
+#include <grub/fshelp.h>
+
+GRUB_MOD_LICENSE ("GPLv3+");
+
+#define        XFS_INODE_EXTENTS       9
+
+#define XFS_INODE_FORMAT_INO   1
+#define XFS_INODE_FORMAT_EXT   2
+#define XFS_INODE_FORMAT_BTREE 3
+
+/* Superblock version field flags */
+#define XFS_SB_VERSION_NUMBITS         0x000f
+#define        XFS_SB_VERSION_ATTRBIT          0x0010
+#define        XFS_SB_VERSION_NLINKBIT         0x0020
+#define        XFS_SB_VERSION_QUOTABIT         0x0040
+#define        XFS_SB_VERSION_ALIGNBIT         0x0080
+#define        XFS_SB_VERSION_DALIGNBIT        0x0100
+#define        XFS_SB_VERSION_LOGV2BIT         0x0400
+#define        XFS_SB_VERSION_SECTORBIT        0x0800
+#define        XFS_SB_VERSION_EXTFLGBIT        0x1000
+#define        XFS_SB_VERSION_DIRV2BIT         0x2000
+#define XFS_SB_VERSION_MOREBITSBIT     0x8000
+#define XFS_SB_VERSION_BITS_SUPPORTED \
+       (XFS_SB_VERSION_NUMBITS | \
+        XFS_SB_VERSION_ATTRBIT | \
+        XFS_SB_VERSION_NLINKBIT | \
+        XFS_SB_VERSION_QUOTABIT | \
+        XFS_SB_VERSION_ALIGNBIT | \
+        XFS_SB_VERSION_DALIGNBIT | \
+        XFS_SB_VERSION_LOGV2BIT | \
+        XFS_SB_VERSION_SECTORBIT | \
+        XFS_SB_VERSION_EXTFLGBIT | \
+        XFS_SB_VERSION_DIRV2BIT | \
+        XFS_SB_VERSION_MOREBITSBIT)
+
+/* Recognized xfs format versions */
+#define XFS_SB_VERSION_4               4       /* Good old XFS filesystem */
+#define XFS_SB_VERSION_5               5       /* CRC enabled filesystem */
+
+/* features2 field flags */
+#define XFS_SB_VERSION2_LAZYSBCOUNTBIT 0x00000002      /* Superblk counters */
+#define XFS_SB_VERSION2_ATTR2BIT       0x00000008      /* Inline attr rework */
+#define XFS_SB_VERSION2_PROJID32BIT    0x00000080      /* 32-bit project ids */
+#define XFS_SB_VERSION2_FTYPE          0x00000200      /* inode type in dir */
+#define XFS_SB_VERSION2_BITS_SUPPORTED \
+       (XFS_SB_VERSION2_LAZYSBCOUNTBIT | \
+        XFS_SB_VERSION2_ATTR2BIT | \
+        XFS_SB_VERSION2_PROJID32BIT | \
+        XFS_SB_VERSION2_FTYPE)
+
+/* incompat feature flags */
+#define XFS_SB_FEAT_INCOMPAT_FTYPE      (1 << 0)        /* filetype in dirent */
+#define XFS_SB_FEAT_INCOMPAT_SPINODES   (1 << 1)        /* sparse inode chunks */
+#define XFS_SB_FEAT_INCOMPAT_META_UUID  (1 << 2)        /* metadata UUID */
+
+/*
+ * Directory entries with ftype are explicitly handled by GRUB code.
+ *
+ * We do not currently read the inode btrees, so it is safe to read filesystems
+ * with the XFS_SB_FEAT_INCOMPAT_SPINODES feature.
+ *
+ * We do not currently verify metadata UUID, so it is safe to read filesystems
+ * with the XFS_SB_FEAT_INCOMPAT_META_UUID feature.
+ */
+#define XFS_SB_FEAT_INCOMPAT_SUPPORTED \
+       (XFS_SB_FEAT_INCOMPAT_FTYPE | \
+        XFS_SB_FEAT_INCOMPAT_SPINODES | \
+        XFS_SB_FEAT_INCOMPAT_META_UUID)
+
+struct grub_xfs_sblock
+{
+  grub_uint8_t magic[4];
+  grub_uint32_t bsize;
+  grub_uint8_t unused1[24];
+  grub_uint16_t uuid[8];
+  grub_uint8_t unused2[8];
+  grub_uint64_t rootino;
+  grub_uint8_t unused3[20];
+  grub_uint32_t agsize;
+  grub_uint8_t unused4[12];
+  grub_uint16_t version;
+  grub_uint8_t unused5[6];
+  grub_uint8_t label[12];
+  grub_uint8_t log2_bsize;
+  grub_uint8_t log2_sect;
+  grub_uint8_t log2_inode;
+  grub_uint8_t log2_inop;
+  grub_uint8_t log2_agblk;
+  grub_uint8_t unused6[67];
+  grub_uint8_t log2_dirblk;
+  grub_uint8_t unused7[7];
+  grub_uint32_t features2;
+  grub_uint8_t unused8[4];
+  grub_uint32_t sb_features_compat;
+  grub_uint32_t sb_features_ro_compat;
+  grub_uint32_t sb_features_incompat;
+  grub_uint32_t sb_features_log_incompat;
+} GRUB_PACKED;
+
+struct grub_xfs_dir_header
+{
+  grub_uint8_t count;
+  grub_uint8_t largeino;
+  union
+  {
+    grub_uint32_t i4;
+    grub_uint64_t i8;
+  } GRUB_PACKED parent;
+} GRUB_PACKED;
+
+/* Structure for directory entry inlined in the inode */
+struct grub_xfs_dir_entry
+{
+  grub_uint8_t len;
+  grub_uint16_t offset;
+  char name[1];
+  /* Inode number follows, 32 / 64 bits.  */
+} GRUB_PACKED;
+
+/* Structure for directory entry in a block */
+struct grub_xfs_dir2_entry
+{
+  grub_uint64_t inode;
+  grub_uint8_t len;
+} GRUB_PACKED;
+
+struct grub_xfs_extent
+{
+  /* This should be a bitfield but bietfields are unportable, so just have
+     a raw array and functions extracting useful info from it.
+   */
+  grub_uint32_t raw[4];
+} GRUB_PACKED;
+
+struct grub_xfs_btree_node
+{
+  grub_uint8_t magic[4];
+  grub_uint16_t level;
+  grub_uint16_t numrecs;
+  grub_uint64_t left;
+  grub_uint64_t right;
+  /* In V5 here follow crc, uuid, etc. */
+  /* Then follow keys and block pointers */
+} GRUB_PACKED;
+
+struct grub_xfs_btree_root
+{
+  grub_uint16_t level;
+  grub_uint16_t numrecs;
+  grub_uint64_t keys[1];
+} GRUB_PACKED;
+
+struct grub_xfs_time
+{
+  grub_uint32_t sec;
+  grub_uint32_t nanosec;
+} GRUB_PACKED;
+
+struct grub_xfs_inode
+{
+  grub_uint8_t magic[2];
+  grub_uint16_t mode;
+  grub_uint8_t version;
+  grub_uint8_t format;
+  grub_uint8_t unused2[26];
+  struct grub_xfs_time atime;
+  struct grub_xfs_time mtime;
+  struct grub_xfs_time ctime;
+  grub_uint64_t size;
+  grub_uint64_t nblocks;
+  grub_uint32_t extsize;
+  grub_uint32_t nextents;
+  grub_uint16_t unused3;
+  grub_uint8_t fork_offset;
+  grub_uint8_t unused4[17];
+} GRUB_PACKED;
+
+#define XFS_V2_INODE_SIZE sizeof(struct grub_xfs_inode)
+#define XFS_V3_INODE_SIZE (XFS_V2_INODE_SIZE + 76)
+
+struct grub_xfs_dirblock_tail
+{
+  grub_uint32_t leaf_count;
+  grub_uint32_t leaf_stale;
+} GRUB_PACKED;
+
+struct grub_fshelp_node
+{
+  struct grub_xfs_data *data;
+  grub_uint64_t ino;
+  int inode_read;
+  struct grub_xfs_inode inode;
+};
+
+struct grub_xfs_data
+{
+  struct grub_xfs_sblock sblock;
+  grub_disk_t disk;
+  int pos;
+  int bsize;
+  grub_uint32_t agsize;
+  unsigned int hasftype:1;
+  unsigned int hascrc:1;
+  struct grub_fshelp_node diropen;
+};
+
+static grub_dl_t my_mod;
+
+\f
+
+static int grub_xfs_sb_hascrc(struct grub_xfs_data *data)
+{
+  return (data->sblock.version & grub_cpu_to_be16_compile_time(XFS_SB_VERSION_NUMBITS)) ==
+         grub_cpu_to_be16_compile_time(XFS_SB_VERSION_5);
+}
+
+static int grub_xfs_sb_hasftype(struct grub_xfs_data *data)
+{
+  if ((data->sblock.version & grub_cpu_to_be16_compile_time(XFS_SB_VERSION_NUMBITS)) ==
+       grub_cpu_to_be16_compile_time(XFS_SB_VERSION_5) &&
+      data->sblock.sb_features_incompat & grub_cpu_to_be32_compile_time(XFS_SB_FEAT_INCOMPAT_FTYPE))
+    return 1;
+  if (data->sblock.version & grub_cpu_to_be16_compile_time(XFS_SB_VERSION_MOREBITSBIT) &&
+      data->sblock.features2 & grub_cpu_to_be32_compile_time(XFS_SB_VERSION2_FTYPE))
+    return 1;
+  return 0;
+}
+
+static int grub_xfs_sb_valid(struct grub_xfs_data *data)
+{
+  grub_dprintf("xfs", "Validating superblock\n");
+  if (grub_strncmp ((char *) (data->sblock.magic), "XFSB", 4)
+      || data->sblock.log2_bsize < GRUB_DISK_SECTOR_BITS
+      || ((int) data->sblock.log2_bsize
+         + (int) data->sblock.log2_dirblk) >= 27)
+    {
+      grub_error (GRUB_ERR_BAD_FS, "not a XFS filesystem");
+      return 0;
+    }
+  if ((data->sblock.version & grub_cpu_to_be16_compile_time(XFS_SB_VERSION_NUMBITS)) ==
+       grub_cpu_to_be16_compile_time(XFS_SB_VERSION_5))
+    {
+      grub_dprintf("xfs", "XFS v5 superblock detected\n");
+      if (data->sblock.sb_features_incompat &
+          grub_cpu_to_be32_compile_time(~XFS_SB_FEAT_INCOMPAT_SUPPORTED))
+        {
+         grub_error (GRUB_ERR_BAD_FS, "XFS filesystem has unsupported "
+                     "incompatible features");
+         return 0;
+        }
+      return 1;
+    }
+  else if ((data->sblock.version & grub_cpu_to_be16_compile_time(XFS_SB_VERSION_NUMBITS)) ==
+          grub_cpu_to_be16_compile_time(XFS_SB_VERSION_4))
+    {
+      grub_dprintf("xfs", "XFS v4 superblock detected\n");
+      if (!(data->sblock.version & grub_cpu_to_be16_compile_time(XFS_SB_VERSION_DIRV2BIT)))
+       {
+         grub_error (GRUB_ERR_BAD_FS, "XFS filesystem without V2 directories "
+                     "is unsupported");
+         return 0;
+       }
+      if (data->sblock.version & grub_cpu_to_be16_compile_time(~XFS_SB_VERSION_BITS_SUPPORTED) ||
+         (data->sblock.version & grub_cpu_to_be16_compile_time(XFS_SB_VERSION_MOREBITSBIT) &&
+          data->sblock.features2 & grub_cpu_to_be16_compile_time(~XFS_SB_VERSION2_BITS_SUPPORTED)))
+       {
+         grub_error (GRUB_ERR_BAD_FS, "XFS filesystem has unsupported version "
+                     "bits");
+         return 0;
+       }
+      return 1;
+    }
+  return 0;
+}
+
+/* Filetype information as used in inodes.  */
+#define FILETYPE_INO_MASK      0170000
+#define FILETYPE_INO_REG       0100000
+#define FILETYPE_INO_DIRECTORY 0040000
+#define FILETYPE_INO_SYMLINK   0120000
+
+static inline int
+GRUB_XFS_INO_AGBITS(struct grub_xfs_data *data)
+{
+  return ((data)->sblock.log2_agblk + (data)->sblock.log2_inop);
+}
+
+static inline grub_uint64_t
+GRUB_XFS_INO_INOINAG (struct grub_xfs_data *data,
+                     grub_uint64_t ino)
+{
+  return (ino & ((1LL << GRUB_XFS_INO_AGBITS (data)) - 1));
+}
+
+static inline grub_uint64_t
+GRUB_XFS_INO_AG (struct grub_xfs_data *data,
+                grub_uint64_t ino)
+{
+  return (ino >> GRUB_XFS_INO_AGBITS (data));
+}
+
+static inline grub_disk_addr_t
+GRUB_XFS_FSB_TO_BLOCK (struct grub_xfs_data *data, grub_disk_addr_t fsb)
+{
+  return ((fsb >> data->sblock.log2_agblk) * data->agsize
+         + (fsb & ((1LL << data->sblock.log2_agblk) - 1)));
+}
+
+static inline grub_uint64_t
+GRUB_XFS_EXTENT_OFFSET (struct grub_xfs_extent *exts, int ex)
+{
+  return ((grub_be_to_cpu32 (exts[ex].raw[0]) & ~(1 << 31)) << 23
+         | grub_be_to_cpu32 (exts[ex].raw[1]) >> 9);
+}
+
+static inline grub_uint64_t
+GRUB_XFS_EXTENT_BLOCK (struct grub_xfs_extent *exts, int ex)
+{
+  return ((grub_uint64_t) (grub_be_to_cpu32 (exts[ex].raw[1])
+                          & (0x1ff)) << 43
+         | (grub_uint64_t) grub_be_to_cpu32 (exts[ex].raw[2]) << 11
+         | grub_be_to_cpu32 (exts[ex].raw[3]) >> 21);
+}
+
+static inline grub_uint64_t
+GRUB_XFS_EXTENT_SIZE (struct grub_xfs_extent *exts, int ex)
+{
+  return (grub_be_to_cpu32 (exts[ex].raw[3]) & ((1 << 21) - 1));
+}
+
+\f
+static inline grub_uint64_t
+grub_xfs_inode_block (struct grub_xfs_data *data,
+                     grub_uint64_t ino)
+{
+  long long int inoinag = GRUB_XFS_INO_INOINAG (data, ino);
+  long long ag = GRUB_XFS_INO_AG (data, ino);
+  long long block;
+
+  block = (inoinag >> data->sblock.log2_inop) + ag * data->agsize;
+  block <<= (data->sblock.log2_bsize - GRUB_DISK_SECTOR_BITS);
+  return block;
+}
+
+
+static inline int
+grub_xfs_inode_offset (struct grub_xfs_data *data,
+                      grub_uint64_t ino)
+{
+  int inoag = GRUB_XFS_INO_INOINAG (data, ino);
+  return ((inoag & ((1 << data->sblock.log2_inop) - 1)) <<
+         data->sblock.log2_inode);
+}
+
+static inline grub_size_t
+grub_xfs_inode_size(struct grub_xfs_data *data)
+{
+  return (grub_size_t)1 << data->sblock.log2_inode;
+}
+
+/*
+ * Returns size occupied by XFS inode stored in memory - we store struct
+ * grub_fshelp_node there but on disk inode size may be actually larger than
+ * struct grub_xfs_inode so we need to account for that so that we can read
+ * from disk directly into in-memory structure.
+ */
+static inline grub_size_t
+grub_xfs_fshelp_size(struct grub_xfs_data *data)
+{
+  return sizeof (struct grub_fshelp_node) - sizeof (struct grub_xfs_inode)
+              + grub_xfs_inode_size(data);
+}
+
+/* This should return void * but XFS code is error-prone with alignment, so
+   return char to retain cast-align.
+ */
+static char *
+grub_xfs_inode_data(struct grub_xfs_inode *inode)
+{
+       if (inode->version <= 2)
+               return ((char *)inode) + XFS_V2_INODE_SIZE;
+       return ((char *)inode) + XFS_V3_INODE_SIZE;
+}
+
+static struct grub_xfs_dir_entry *
+grub_xfs_inline_de(struct grub_xfs_dir_header *head)
+{
+  /*
+    With small inode numbers the header is 4 bytes smaller because of
+    smaller parent pointer
+  */
+  return (struct grub_xfs_dir_entry *)
+    (((char *) head) + sizeof(struct grub_xfs_dir_header) -
+     (head->largeino ? 0 : sizeof(grub_uint32_t)));
+}
+
+static grub_uint8_t *
+grub_xfs_inline_de_inopos(struct grub_xfs_data *data,
+                         struct grub_xfs_dir_entry *de)
+{
+  return ((grub_uint8_t *)(de + 1)) + de->len - 1 + (data->hasftype ? 1 : 0);
+}
+
+static struct grub_xfs_dir_entry *
+grub_xfs_inline_next_de(struct grub_xfs_data *data,
+                       struct grub_xfs_dir_header *head,
+                       struct grub_xfs_dir_entry *de)
+{
+  char *p = (char *)de + sizeof(struct grub_xfs_dir_entry) - 1 + de->len;
+
+  p += head->largeino ? sizeof(grub_uint64_t) : sizeof(grub_uint32_t);
+  if (data->hasftype)
+    p++;
+
+  return (struct grub_xfs_dir_entry *)p;
+}
+
+static struct grub_xfs_dirblock_tail *
+grub_xfs_dir_tail(struct grub_xfs_data *data, void *dirblock)
+{
+  int dirblksize = 1 << (data->sblock.log2_bsize + data->sblock.log2_dirblk);
+
+  return (struct grub_xfs_dirblock_tail *)
+    ((char *)dirblock + dirblksize - sizeof (struct grub_xfs_dirblock_tail));
+}
+
+static struct grub_xfs_dir2_entry *
+grub_xfs_first_de(struct grub_xfs_data *data, void *dirblock)
+{
+  if (data->hascrc)
+    return (struct grub_xfs_dir2_entry *)((char *)dirblock + 64);
+  return (struct grub_xfs_dir2_entry *)((char *)dirblock + 16);
+}
+
+static struct grub_xfs_dir2_entry *
+grub_xfs_next_de(struct grub_xfs_data *data, struct grub_xfs_dir2_entry *de)
+{
+  int size = sizeof (struct grub_xfs_dir2_entry) + de->len + 2 /* Tag */;
+
+  if (data->hasftype)
+    size++;            /* File type */
+  return (struct grub_xfs_dir2_entry *)(((char *)de) + ALIGN_UP(size, 8));
+}
+
+/* This should return void * but XFS code is error-prone with alignment, so
+   return char to retain cast-align.
+ */
+static char *
+grub_xfs_btree_keys(struct grub_xfs_data *data,
+                   struct grub_xfs_btree_node *leaf)
+{
+  char *keys = (char *)(leaf + 1);
+
+  if (data->hascrc)
+    keys += 48;        /* skip crc, uuid, ... */
+  return keys;
+}
+
+static grub_err_t
+grub_xfs_read_inode (struct grub_xfs_data *data, grub_uint64_t ino,
+                    struct grub_xfs_inode *inode)
+{
+  grub_uint64_t block = grub_xfs_inode_block (data, ino);
+  int offset = grub_xfs_inode_offset (data, ino);
+
+  grub_dprintf("xfs", "Reading inode (%"PRIuGRUB_UINT64_T") - %"PRIuGRUB_UINT64_T", %d\n",
+              ino, block, offset);
+  /* Read the inode.  */
+  if (grub_disk_read (data->disk, block, offset, grub_xfs_inode_size(data),
+                     inode))
+    return grub_errno;
+
+  if (grub_strncmp ((char *) inode->magic, "IN", 2))
+    return grub_error (GRUB_ERR_BAD_FS, "not a correct XFS inode");
+
+  return 0;
+}
+
+static grub_uint64_t
+get_fsb (const void *keys, int idx)
+{
+  const char *p = (const char *) keys + sizeof(grub_uint64_t) * idx;
+  return grub_be_to_cpu64 (grub_get_unaligned64 (p));
+}
+
+static grub_disk_addr_t
+grub_xfs_read_block (grub_fshelp_node_t node, grub_disk_addr_t fileblock)
+{
+  struct grub_xfs_btree_node *leaf = 0;
+  int ex, nrec;
+  struct grub_xfs_extent *exts;
+  grub_uint64_t ret = 0;
+
+  if (node->inode.format == XFS_INODE_FORMAT_BTREE)
+    {
+      struct grub_xfs_btree_root *root;
+      const char *keys;
+      int recoffset;
+
+      leaf = grub_malloc (node->data->bsize);
+      if (leaf == 0)
+        return 0;
+
+      root = (struct grub_xfs_btree_root *) grub_xfs_inode_data(&node->inode);
+      nrec = grub_be_to_cpu16 (root->numrecs);
+      keys = (char *) &root->keys[0];
+      if (node->inode.fork_offset)
+       recoffset = (node->inode.fork_offset - 1) / 2;
+      else
+       recoffset = (grub_xfs_inode_size(node->data)
+                    - ((char *) keys - (char *) &node->inode))
+                               / (2 * sizeof (grub_uint64_t));
+      do
+        {
+          int i;
+
+          for (i = 0; i < nrec; i++)
+            {
+              if (fileblock < get_fsb(keys, i))
+                break;
+            }
+
+          /* Sparse block.  */
+          if (i == 0)
+            {
+              grub_free (leaf);
+              return 0;
+            }
+
+          if (grub_disk_read (node->data->disk,
+                              GRUB_XFS_FSB_TO_BLOCK (node->data, get_fsb (keys, i - 1 + recoffset)) << (node->data->sblock.log2_bsize - GRUB_DISK_SECTOR_BITS),
+                              0, node->data->bsize, leaf))
+            return 0;
+
+         if ((!node->data->hascrc &&
+              grub_strncmp ((char *) leaf->magic, "BMAP", 4)) ||
+             (node->data->hascrc &&
+              grub_strncmp ((char *) leaf->magic, "BMA3", 4)))
+            {
+              grub_free (leaf);
+              grub_error (GRUB_ERR_BAD_FS, "not a correct XFS BMAP node");
+              return 0;
+            }
+
+          nrec = grub_be_to_cpu16 (leaf->numrecs);
+          keys = grub_xfs_btree_keys(node->data, leaf);
+         recoffset = ((node->data->bsize - ((char *) keys
+                                            - (char *) leaf))
+                      / (2 * sizeof (grub_uint64_t)));
+       }
+      while (leaf->level);
+      exts = (struct grub_xfs_extent *) keys;
+    }
+  else if (node->inode.format == XFS_INODE_FORMAT_EXT)
+    {
+      nrec = grub_be_to_cpu32 (node->inode.nextents);
+      exts = (struct grub_xfs_extent *) grub_xfs_inode_data(&node->inode);
+    }
+  else
+    {
+      grub_error (GRUB_ERR_NOT_IMPLEMENTED_YET,
+                 "XFS does not support inode format %d yet",
+                 node->inode.format);
+      return 0;
+    }
+
+  /* Iterate over each extent to figure out which extent has
+     the block we are looking for.  */
+  for (ex = 0; ex < nrec; ex++)
+    {
+      grub_uint64_t start = GRUB_XFS_EXTENT_BLOCK (exts, ex);
+      grub_uint64_t offset = GRUB_XFS_EXTENT_OFFSET (exts, ex);
+      grub_uint64_t size = GRUB_XFS_EXTENT_SIZE (exts, ex);
+
+      /* Sparse block.  */
+      if (fileblock < offset)
+        break;
+      else if (fileblock < offset + size)
+        {
+          ret = (fileblock - offset + start);
+          break;
+        }
+    }
+
+  grub_free (leaf);
+
+  return GRUB_XFS_FSB_TO_BLOCK(node->data, ret);
+}
+
+
+/* Read LEN bytes from the file described by DATA starting with byte
+   POS.  Return the amount of read bytes in READ.  */
+static grub_ssize_t
+grub_xfs_read_file (grub_fshelp_node_t node,
+                   grub_disk_read_hook_t read_hook, void *read_hook_data,
+                   grub_off_t pos, grub_size_t len, char *buf, grub_uint32_t header_size)
+{
+  return grub_fshelp_read_file (node->data->disk, node,
+                               read_hook, read_hook_data,
+                               pos, len, buf, grub_xfs_read_block,
+                               grub_be_to_cpu64 (node->inode.size) + header_size,
+                               node->data->sblock.log2_bsize
+                               - GRUB_DISK_SECTOR_BITS, 0);
+}
+
+
+static char *
+grub_xfs_read_symlink (grub_fshelp_node_t node)
+{
+  grub_ssize_t size = grub_be_to_cpu64 (node->inode.size);
+
+  if (size < 0)
+    {
+      grub_error (GRUB_ERR_BAD_FS, "invalid symlink");
+      return 0;
+    }
+
+  switch (node->inode.format)
+    {
+    case XFS_INODE_FORMAT_INO:
+      return grub_strndup (grub_xfs_inode_data(&node->inode), size);
+
+    case XFS_INODE_FORMAT_EXT:
+      {
+       char *symlink;
+       grub_ssize_t numread;
+       int off = 0;
+
+       if (node->data->hascrc)
+         off = 56;
+
+       symlink = grub_malloc (size + 1);
+       if (!symlink)
+         return 0;
+
+       node->inode.size = grub_be_to_cpu64 (size + off);
+       numread = grub_xfs_read_file (node, 0, 0, off, size, symlink, off);
+       if (numread != size)
+         {
+           grub_free (symlink);
+           return 0;
+         }
+       symlink[size] = '\0';
+       return symlink;
+      }
+    }
+
+  return 0;
+}
+
+
+static enum grub_fshelp_filetype
+grub_xfs_mode_to_filetype (grub_uint16_t mode)
+{
+  if ((grub_be_to_cpu16 (mode)
+       & FILETYPE_INO_MASK) == FILETYPE_INO_DIRECTORY)
+    return GRUB_FSHELP_DIR;
+  else if ((grub_be_to_cpu16 (mode)
+           & FILETYPE_INO_MASK) == FILETYPE_INO_SYMLINK)
+    return GRUB_FSHELP_SYMLINK;
+  else if ((grub_be_to_cpu16 (mode)
+           & FILETYPE_INO_MASK) == FILETYPE_INO_REG)
+    return GRUB_FSHELP_REG;
+  return GRUB_FSHELP_UNKNOWN;
+}
+
+
+/* Context for grub_xfs_iterate_dir.  */
+struct grub_xfs_iterate_dir_ctx
+{
+  grub_fshelp_iterate_dir_hook_t hook;
+  void *hook_data;
+  struct grub_fshelp_node *diro;
+};
+
+/* Helper for grub_xfs_iterate_dir.  */
+static int iterate_dir_call_hook (grub_uint64_t ino, const char *filename,
+                                 struct grub_xfs_iterate_dir_ctx *ctx)
+{
+  struct grub_fshelp_node *fdiro;
+  grub_err_t err;
+
+  fdiro = grub_malloc (grub_xfs_fshelp_size(ctx->diro->data) + 1);
+  if (!fdiro)
+    {
+      grub_print_error ();
+      return 0;
+    }
+
+  /* The inode should be read, otherwise the filetype can
+     not be determined.  */
+  fdiro->ino = ino;
+  fdiro->inode_read = 1;
+  fdiro->data = ctx->diro->data;
+  err = grub_xfs_read_inode (ctx->diro->data, ino, &fdiro->inode);
+  if (err)
+    {
+      grub_print_error ();
+      return 0;
+    }
+
+  return ctx->hook (filename, grub_xfs_mode_to_filetype (fdiro->inode.mode),
+                   fdiro, ctx->hook_data);
+}
+
+static int
+grub_xfs_iterate_dir (grub_fshelp_node_t dir,
+                     grub_fshelp_iterate_dir_hook_t hook, void *hook_data)
+{
+  struct grub_fshelp_node *diro = (struct grub_fshelp_node *) dir;
+  struct grub_xfs_iterate_dir_ctx ctx = {
+    .hook = hook,
+    .hook_data = hook_data,
+    .diro = diro
+  };
+
+  switch (diro->inode.format)
+    {
+    case XFS_INODE_FORMAT_INO:
+      {
+       struct grub_xfs_dir_header *head = (struct grub_xfs_dir_header *) grub_xfs_inode_data(&diro->inode);
+       struct grub_xfs_dir_entry *de = grub_xfs_inline_de(head);
+       int smallino = !head->largeino;
+       int i;
+       grub_uint64_t parent;
+
+       /* If small inode numbers are used to pack the direntry, the
+          parent inode number is small too.  */
+       if (smallino)
+         parent = grub_be_to_cpu32 (head->parent.i4);
+       else
+         parent = grub_be_to_cpu64 (head->parent.i8);
+
+       /* Synthesize the direntries for `.' and `..'.  */
+       if (iterate_dir_call_hook (diro->ino, ".", &ctx))
+         return 1;
+
+       if (iterate_dir_call_hook (parent, "..", &ctx))
+         return 1;
+
+       for (i = 0; i < head->count; i++)
+         {
+           grub_uint64_t ino;
+           grub_uint8_t *inopos = grub_xfs_inline_de_inopos(dir->data, de);
+           grub_uint8_t c;
+
+           /* inopos might be unaligned.  */
+           if (smallino)
+             ino = (((grub_uint32_t) inopos[0]) << 24)
+               | (((grub_uint32_t) inopos[1]) << 16)
+               | (((grub_uint32_t) inopos[2]) << 8)
+               | (((grub_uint32_t) inopos[3]) << 0);
+           else
+             ino = (((grub_uint64_t) inopos[0]) << 56)
+               | (((grub_uint64_t) inopos[1]) << 48)
+               | (((grub_uint64_t) inopos[2]) << 40)
+               | (((grub_uint64_t) inopos[3]) << 32)
+               | (((grub_uint64_t) inopos[4]) << 24)
+               | (((grub_uint64_t) inopos[5]) << 16)
+               | (((grub_uint64_t) inopos[6]) << 8)
+               | (((grub_uint64_t) inopos[7]) << 0);
+
+           c = de->name[de->len];
+           de->name[de->len] = '\0';
+           if (iterate_dir_call_hook (ino, de->name, &ctx))
+             {
+               de->name[de->len] = c;
+               return 1;
+             }
+           de->name[de->len] = c;
+
+           de = grub_xfs_inline_next_de(dir->data, head, de);
+         }
+       break;
+      }
+
+    case XFS_INODE_FORMAT_BTREE:
+    case XFS_INODE_FORMAT_EXT:
+      {
+       grub_ssize_t numread;
+       char *dirblock;
+       grub_uint64_t blk;
+        int dirblk_size, dirblk_log2;
+
+        dirblk_log2 = (dir->data->sblock.log2_bsize
+                       + dir->data->sblock.log2_dirblk);
+        dirblk_size = 1 << dirblk_log2;
+
+       dirblock = grub_malloc (dirblk_size);
+       if (! dirblock)
+         return 0;
+
+       /* Iterate over every block the directory has.  */
+       for (blk = 0;
+            blk < (grub_be_to_cpu64 (dir->inode.size)
+                   >> dirblk_log2);
+            blk++)
+         {
+           struct grub_xfs_dir2_entry *direntry =
+                                       grub_xfs_first_de(dir->data, dirblock);
+           int entries;
+           struct grub_xfs_dirblock_tail *tail =
+                                       grub_xfs_dir_tail(dir->data, dirblock);
+
+           numread = grub_xfs_read_file (dir, 0, 0,
+                                         blk << dirblk_log2,
+                                         dirblk_size, dirblock, 0);
+           if (numread != dirblk_size)
+             return 0;
+
+           entries = (grub_be_to_cpu32 (tail->leaf_count)
+                      - grub_be_to_cpu32 (tail->leaf_stale));
+
+           if (!entries)
+             continue;
+
+           /* Iterate over all entries within this block.  */
+           while ((char *)direntry < (char *)tail)
+             {
+               grub_uint8_t *freetag;
+               char *filename;
+
+               freetag = (grub_uint8_t *) direntry;
+
+               if (grub_get_unaligned16 (freetag) == 0XFFFF)
+                 {
+                   grub_uint8_t *skip = (freetag + sizeof (grub_uint16_t));
+
+                   /* This entry is not used, go to the next one.  */
+                   direntry = (struct grub_xfs_dir2_entry *)
+                               (((char *)direntry) +
+                               grub_be_to_cpu16 (grub_get_unaligned16 (skip)));
+
+                   continue;
+                 }
+
+               filename = (char *)(direntry + 1);
+               /* The byte after the filename is for the filetype, padding, or
+                  tag, which is not used by GRUB.  So it can be overwritten. */
+               filename[direntry->len] = '\0';
+
+               if (iterate_dir_call_hook (grub_be_to_cpu64(direntry->inode), 
+                                          filename, &ctx))
+                 {
+                   grub_free (dirblock);
+                   return 1;
+                 }
+
+               /* Check if last direntry in this block is
+                  reached.  */
+               entries--;
+               if (!entries)
+                 break;
+
+               /* Select the next directory entry.  */
+               direntry = grub_xfs_next_de(dir->data, direntry);
+             }
+         }
+       grub_free (dirblock);
+       break;
+      }
+
+    default:
+      grub_error (GRUB_ERR_NOT_IMPLEMENTED_YET,
+                 "XFS does not support inode format %d yet",
+                 diro->inode.format);
+    }
+  return 0;
+}
+
+
+static struct grub_xfs_data *
+grub_xfs_mount (grub_disk_t disk)
+{
+  struct grub_xfs_data *data = 0;
+
+  data = grub_zalloc (sizeof (struct grub_xfs_data));
+  if (!data)
+    return 0;
+
+  grub_dprintf("xfs", "Reading sb\n");
+  /* Read the superblock.  */
+  if (grub_disk_read (disk, 0, 0,
+                     sizeof (struct grub_xfs_sblock), &data->sblock))
+    goto fail;
+
+  if (!grub_xfs_sb_valid(data))
+    goto fail;
+
+  data = grub_realloc (data,
+                      sizeof (struct grub_xfs_data)
+                      - sizeof (struct grub_xfs_inode)
+                      + grub_xfs_inode_size(data) + 1);
+
+  if (! data)
+    goto fail;
+
+  data->diropen.data = data;
+  data->diropen.ino = grub_be_to_cpu64(data->sblock.rootino);
+  data->diropen.inode_read = 1;
+  data->bsize = grub_be_to_cpu32 (data->sblock.bsize);
+  data->agsize = grub_be_to_cpu32 (data->sblock.agsize);
+  data->hasftype = grub_xfs_sb_hasftype(data);
+  data->hascrc = grub_xfs_sb_hascrc(data);
+
+  data->disk = disk;
+  data->pos = 0;
+  grub_dprintf("xfs", "Reading root ino %"PRIuGRUB_UINT64_T"\n",
+              grub_cpu_to_be64(data->sblock.rootino));
+
+  grub_xfs_read_inode (data, data->diropen.ino, &data->diropen.inode);
+
+  return data;
+ fail:
+
+  if (grub_errno == GRUB_ERR_OUT_OF_RANGE)
+    grub_error (GRUB_ERR_BAD_FS, "not an XFS filesystem");
+
+  grub_free (data);
+
+  return 0;
+}
+
+\f
+/* Context for grub_xfs_dir.  */
+struct grub_xfs_dir_ctx
+{
+  grub_fs_dir_hook_t hook;
+  void *hook_data;
+};
+
+/* Helper for grub_xfs_dir.  */
+static int
+grub_xfs_dir_iter (const char *filename, enum grub_fshelp_filetype filetype,
+                  grub_fshelp_node_t node, void *data)
+{
+  struct grub_xfs_dir_ctx *ctx = data;
+  struct grub_dirhook_info info;
+
+  grub_memset (&info, 0, sizeof (info));
+  if (node->inode_read)
+    {
+      info.mtimeset = 1;
+      info.mtime = grub_be_to_cpu32 (node->inode.mtime.sec);
+    }
+  info.dir = ((filetype & GRUB_FSHELP_TYPE_MASK) == GRUB_FSHELP_DIR);
+  if (!info.dir)
+    info.size = node->inode.size;
+  grub_free (node);
+  return ctx->hook (filename, &info, ctx->hook_data);
+}
+
+static grub_err_t
+grub_xfs_dir (grub_device_t device, const char *path,
+             grub_fs_dir_hook_t hook, void *hook_data)
+{
+  struct grub_xfs_dir_ctx ctx = { hook, hook_data };
+  struct grub_xfs_data *data = 0;
+  struct grub_fshelp_node *fdiro = 0;
+
+  grub_dl_ref (my_mod);
+
+  data = grub_xfs_mount (device->disk);
+  if (!data)
+    goto mount_fail;
+
+  grub_fshelp_find_file (path, &data->diropen, &fdiro, grub_xfs_iterate_dir,
+                        grub_xfs_read_symlink, GRUB_FSHELP_DIR);
+  if (grub_errno)
+    goto fail;
+
+  grub_xfs_iterate_dir (fdiro, grub_xfs_dir_iter, &ctx);
+
+ fail:
+  if (fdiro != &data->diropen)
+    grub_free (fdiro);
+  grub_free (data);
+
+ mount_fail:
+
+  grub_dl_unref (my_mod);
+
+  return grub_errno;
+}
+
+
+/* Open a file named NAME and initialize FILE.  */
+static grub_err_t
+grub_xfs_open (struct grub_file *file, const char *name)
+{
+  struct grub_xfs_data *data;
+  struct grub_fshelp_node *fdiro = 0;
+
+  grub_dl_ref (my_mod);
+
+  data = grub_xfs_mount (file->device->disk);
+  if (!data)
+    goto mount_fail;
+
+  grub_fshelp_find_file (name, &data->diropen, &fdiro, grub_xfs_iterate_dir,
+                        grub_xfs_read_symlink, GRUB_FSHELP_REG);
+  if (grub_errno)
+    goto fail;
+
+  if (!fdiro->inode_read)
+    {
+      grub_xfs_read_inode (data, fdiro->ino, &fdiro->inode);
+      if (grub_errno)
+       goto fail;
+    }
+
+  if (fdiro != &data->diropen)
+    {
+      grub_memcpy (&data->diropen, fdiro, grub_xfs_fshelp_size(data));
+      grub_free (fdiro);
+    }
+
+  file->size = grub_be_to_cpu64 (data->diropen.inode.size);
+  file->data = data;
+  file->offset = 0;
+
+  return 0;
+
+ fail:
+  if (fdiro != &data->diropen)
+    grub_free (fdiro);
+  grub_free (data);
+
+ mount_fail:
+  grub_dl_unref (my_mod);
+
+  return grub_errno;
+}
+
+
+static grub_ssize_t
+grub_xfs_read (grub_file_t file, char *buf, grub_size_t len)
+{
+  struct grub_xfs_data *data =
+    (struct grub_xfs_data *) file->data;
+
+  return grub_xfs_read_file (&data->diropen,
+                            file->read_hook, file->read_hook_data,
+                            file->offset, len, buf, 0);
+}
+
+
+static grub_err_t
+grub_xfs_close (grub_file_t file)
+{
+  grub_free (file->data);
+
+  grub_dl_unref (my_mod);
+
+  return GRUB_ERR_NONE;
+}
+
+
+static grub_err_t
+grub_xfs_label (grub_device_t device, char **label)
+{
+  struct grub_xfs_data *data;
+  grub_disk_t disk = device->disk;
+
+  grub_dl_ref (my_mod);
+
+  data = grub_xfs_mount (disk);
+  if (data)
+    *label = grub_strndup ((char *) (data->sblock.label), 12);
+  else
+    *label = 0;
+
+  grub_dl_unref (my_mod);
+
+  grub_free (data);
+
+  return grub_errno;
+}
+
+static grub_err_t
+grub_xfs_uuid (grub_device_t device, char **uuid)
+{
+  struct grub_xfs_data *data;
+  grub_disk_t disk = device->disk;
+
+  grub_dl_ref (my_mod);
+
+  data = grub_xfs_mount (disk);
+  if (data)
+    {
+      *uuid = grub_xasprintf ("%04x%04x-%04x-%04x-%04x-%04x%04x%04x",
+                            grub_be_to_cpu16 (data->sblock.uuid[0]),
+                            grub_be_to_cpu16 (data->sblock.uuid[1]),
+                            grub_be_to_cpu16 (data->sblock.uuid[2]),
+                            grub_be_to_cpu16 (data->sblock.uuid[3]),
+                            grub_be_to_cpu16 (data->sblock.uuid[4]),
+                            grub_be_to_cpu16 (data->sblock.uuid[5]),
+                            grub_be_to_cpu16 (data->sblock.uuid[6]),
+                            grub_be_to_cpu16 (data->sblock.uuid[7]));
+    }
+  else
+    *uuid = NULL;
+
+  grub_dl_unref (my_mod);
+
+  grub_free (data);
+
+  return grub_errno;
+}
+
+\f
+
+static struct grub_fs grub_xfs_fs =
+  {
+    .name = "xfs",
+    .fs_dir = grub_xfs_dir,
+    .fs_open = grub_xfs_open,
+    .fs_read = grub_xfs_read,
+    .fs_close = grub_xfs_close,
+    .fs_label = grub_xfs_label,
+    .fs_uuid = grub_xfs_uuid,
+#ifdef GRUB_UTIL
+    .reserved_first_sector = 0,
+    .blocklist_install = 1,
+#endif
+    .next = 0
+  };
+
+GRUB_MOD_INIT(xfs)
+{
+  grub_fs_register (&grub_xfs_fs);
+  my_mod = mod;
+}
+
+GRUB_MOD_FINI(xfs)
+{
+  grub_fs_unregister (&grub_xfs_fs);
+}
diff --git a/GRUB2/grub-2.04/grub-core/kern/disk.c b/GRUB2/grub-2.04/grub-core/kern/disk.c
new file mode 100644 (file)
index 0000000..e5c6f20
--- /dev/null
@@ -0,0 +1,600 @@
+/*
+ *  GRUB  --  GRand Unified Bootloader
+ *  Copyright (C) 2002,2003,2004,2006,2007,2008,2009,2010  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 <grub/disk.h>
+#include <grub/err.h>
+#include <grub/mm.h>
+#include <grub/types.h>
+#include <grub/partition.h>
+#include <grub/misc.h>
+#include <grub/time.h>
+#include <grub/file.h>
+#include <grub/i18n.h>
+#include <grub/ventoy.h>
+
+#define        GRUB_CACHE_TIMEOUT      2
+
+/* The last time the disk was used.  */
+static grub_uint64_t grub_last_time = 0;
+
+struct grub_disk_cache grub_disk_cache_table[GRUB_DISK_CACHE_NUM];
+
+void (*grub_disk_firmware_fini) (void);
+int grub_disk_firmware_is_tainted;
+
+#if DISK_CACHE_STATS
+static unsigned long grub_disk_cache_hits;
+static unsigned long grub_disk_cache_misses;
+
+void
+grub_disk_cache_get_performance (unsigned long *hits, unsigned long *misses)
+{
+  *hits = grub_disk_cache_hits;
+  *misses = grub_disk_cache_misses;
+}
+#endif
+
+grub_err_t (*grub_disk_write_weak) (grub_disk_t disk,
+                                   grub_disk_addr_t sector,
+                                   grub_off_t offset,
+                                   grub_size_t size,
+                                   const void *buf);
+#include "disk_common.c"
+
+void
+grub_disk_cache_invalidate_all (void)
+{
+  unsigned i;
+
+  for (i = 0; i < GRUB_DISK_CACHE_NUM; i++)
+    {
+      struct grub_disk_cache *cache = grub_disk_cache_table + i;
+
+      if (cache->data && ! cache->lock)
+       {
+         grub_free (cache->data);
+         cache->data = 0;
+       }
+    }
+}
+
+static char *
+grub_disk_cache_fetch (unsigned long dev_id, unsigned long disk_id,
+                      grub_disk_addr_t sector)
+{
+  struct grub_disk_cache *cache;
+  unsigned cache_index;
+
+  cache_index = grub_disk_cache_get_index (dev_id, disk_id, sector);
+  cache = grub_disk_cache_table + cache_index;
+
+  if (cache->dev_id == dev_id && cache->disk_id == disk_id
+      && cache->sector == sector)
+    {
+      cache->lock = 1;
+#if DISK_CACHE_STATS
+      grub_disk_cache_hits++;
+#endif
+      return cache->data;
+    }
+
+#if DISK_CACHE_STATS
+  grub_disk_cache_misses++;
+#endif
+
+  return 0;
+}
+
+static void
+grub_disk_cache_unlock (unsigned long dev_id, unsigned long disk_id,
+                       grub_disk_addr_t sector)
+{
+  struct grub_disk_cache *cache;
+  unsigned cache_index;
+
+  cache_index = grub_disk_cache_get_index (dev_id, disk_id, sector);
+  cache = grub_disk_cache_table + cache_index;
+
+  if (cache->dev_id == dev_id && cache->disk_id == disk_id
+      && cache->sector == sector)
+    cache->lock = 0;
+}
+
+static grub_err_t
+grub_disk_cache_store (unsigned long dev_id, unsigned long disk_id,
+                      grub_disk_addr_t sector, const char *data)
+{
+  unsigned cache_index;
+  struct grub_disk_cache *cache;
+
+  cache_index = grub_disk_cache_get_index (dev_id, disk_id, sector);
+  cache = grub_disk_cache_table + cache_index;
+
+  cache->lock = 1;
+  grub_free (cache->data);
+  cache->data = 0;
+  cache->lock = 0;
+
+  cache->data = grub_malloc (GRUB_DISK_SECTOR_SIZE << GRUB_DISK_CACHE_BITS);
+  if (! cache->data)
+    return grub_errno;
+
+  grub_memcpy (cache->data, data,
+              GRUB_DISK_SECTOR_SIZE << GRUB_DISK_CACHE_BITS);
+  cache->dev_id = dev_id;
+  cache->disk_id = disk_id;
+  cache->sector = sector;
+
+  return GRUB_ERR_NONE;
+}
+
+\f
+
+grub_disk_dev_t grub_disk_dev_list;
+
+void
+grub_disk_dev_register (grub_disk_dev_t dev)
+{
+  dev->next = grub_disk_dev_list;
+  grub_disk_dev_list = dev;
+}
+
+void
+grub_disk_dev_unregister (grub_disk_dev_t dev)
+{
+  grub_disk_dev_t *p, q;
+
+  for (p = &grub_disk_dev_list, q = *p; q; p = &(q->next), q = q->next)
+    if (q == dev)
+      {
+        *p = q->next;
+       break;
+      }
+}
+
+/* Return the location of the first ',', if any, which is not
+   escaped by a '\'.  */
+static const char *
+find_part_sep (const char *name)
+{
+  const char *p = name;
+  char c;
+
+  while ((c = *p++) != '\0')
+    {
+      if (c == '\\' && *p == ',')
+       p++;
+      else if (c == ',')
+       return p - 1;
+    }
+  return NULL;
+}
+
+grub_disk_t
+grub_disk_open (const char *name)
+{
+  const char *p;
+  grub_disk_t disk;
+  grub_disk_dev_t dev;
+  char *raw = (char *) name;
+  grub_uint64_t current_time;
+
+  grub_dprintf ("disk", "Opening `%s'...\n", name);
+
+  disk = (grub_disk_t) grub_zalloc (sizeof (*disk));
+  if (! disk)
+    return 0;
+  disk->log_sector_size = GRUB_DISK_SECTOR_BITS;
+  /* Default 1MiB of maximum agglomerate.  */
+  disk->max_agglomerate = 1048576 >> (GRUB_DISK_SECTOR_BITS
+                                     + GRUB_DISK_CACHE_BITS);
+
+  p = find_part_sep (name);
+  if (p)
+    {
+      grub_size_t len = p - name;
+
+      raw = grub_malloc (len + 1);
+      if (! raw)
+       goto fail;
+
+      grub_memcpy (raw, name, len);
+      raw[len] = '\0';
+      disk->name = grub_strdup (raw);
+    }
+  else
+    disk->name = grub_strdup (name);
+  if (! disk->name)
+    goto fail;
+
+  for (dev = grub_disk_dev_list; dev; dev = dev->next)
+    {
+      if ((dev->disk_open) (raw, disk) == GRUB_ERR_NONE)
+       break;
+      else if (grub_errno == GRUB_ERR_UNKNOWN_DEVICE)
+       grub_errno = GRUB_ERR_NONE;
+      else
+       goto fail;
+    }
+
+  if (! dev)
+    {
+      grub_error (GRUB_ERR_UNKNOWN_DEVICE, N_("disk `%s' not found"),
+                 name);
+      goto fail;
+    }
+  if (disk->log_sector_size > GRUB_DISK_CACHE_BITS + GRUB_DISK_SECTOR_BITS
+      || disk->log_sector_size < GRUB_DISK_SECTOR_BITS)
+    {
+      grub_error (GRUB_ERR_NOT_IMPLEMENTED_YET,
+                 "sector sizes of %d bytes aren't supported yet",
+                 (1 << disk->log_sector_size));
+      goto fail;
+    }
+
+  disk->dev = dev;
+
+  if (p)
+    {
+      disk->partition = grub_partition_probe (disk, p + 1);
+      if (! disk->partition)
+       {
+         /* TRANSLATORS: It means that the specified partition e.g.
+            hd0,msdos1=/dev/sda1 doesn't exist.  */
+         grub_error (GRUB_ERR_UNKNOWN_DEVICE, N_("no such partition"));
+         goto fail;
+       }
+    }
+
+  /* The cache will be invalidated about 2 seconds after a device was
+     closed.  */
+  current_time = grub_get_time_ms ();
+
+  if (current_time > (grub_last_time
+                     + GRUB_CACHE_TIMEOUT * 1000))
+    grub_disk_cache_invalidate_all ();
+
+  grub_last_time = current_time;
+
+ fail:
+
+  if (raw && raw != name)
+    grub_free (raw);
+
+  if (grub_errno != GRUB_ERR_NONE)
+    {
+      grub_error_push ();
+      grub_dprintf ("disk", "Opening `%s' failed.\n", name);
+      grub_error_pop ();
+
+      grub_disk_close (disk);
+      return 0;
+    }
+
+  return disk;
+}
+
+void
+grub_disk_close (grub_disk_t disk)
+{
+  grub_partition_t part;
+  grub_dprintf ("disk", "Closing `%s'.\n", disk->name);
+
+  if (disk->dev && disk->dev->disk_close)
+    (disk->dev->disk_close) (disk);
+
+  /* Reset the timer.  */
+  grub_last_time = grub_get_time_ms ();
+
+  while (disk->partition)
+    {
+      part = disk->partition->parent;
+      grub_free (disk->partition);
+      disk->partition = part;
+    }
+  grub_free ((void *) disk->name);
+  grub_free (disk);
+}
+
+/* Small read (less than cache size and not pass across cache unit boundaries).
+   sector is already adjusted and is divisible by cache unit size.
+ */
+static grub_err_t
+grub_disk_read_small_real (grub_disk_t disk, grub_disk_addr_t sector,
+                          grub_off_t offset, grub_size_t size, void *buf)
+{
+  char *data;
+  char *tmp_buf;
+
+  /* Fetch the cache.  */
+  data = grub_disk_cache_fetch (disk->dev->id, disk->id, sector);
+  if (data)
+    {
+      /* Just copy it!  */
+      grub_memcpy (buf, data + offset, size);
+      grub_disk_cache_unlock (disk->dev->id, disk->id, sector);
+      return GRUB_ERR_NONE;
+    }
+
+  /* Allocate a temporary buffer.  */
+  tmp_buf = grub_malloc (GRUB_DISK_SECTOR_SIZE << GRUB_DISK_CACHE_BITS);
+  if (! tmp_buf)
+    return grub_errno;
+
+  /* Otherwise read data from the disk actually.  */
+  if (disk->total_sectors == GRUB_DISK_SIZE_UNKNOWN
+      || sector + GRUB_DISK_CACHE_SIZE
+      < (disk->total_sectors << (disk->log_sector_size - GRUB_DISK_SECTOR_BITS)))
+    {
+      grub_err_t err;
+      err = (disk->dev->disk_read) (disk, transform_sector (disk, sector),
+                                   1U << (GRUB_DISK_CACHE_BITS
+                                          + GRUB_DISK_SECTOR_BITS
+                                          - disk->log_sector_size), tmp_buf);
+      if (!err)
+       {
+         /* Copy it and store it in the disk cache.  */
+         grub_memcpy (buf, tmp_buf + offset, size);
+         grub_disk_cache_store (disk->dev->id, disk->id,
+                                sector, tmp_buf);
+         grub_free (tmp_buf);
+         return GRUB_ERR_NONE;
+       }
+    }
+
+  grub_free (tmp_buf);
+  grub_errno = GRUB_ERR_NONE;
+
+  {
+    /* Uggh... Failed. Instead, just read necessary data.  */
+    unsigned num;
+    grub_disk_addr_t aligned_sector;
+
+    sector += (offset >> GRUB_DISK_SECTOR_BITS);
+    offset &= ((1 << GRUB_DISK_SECTOR_BITS) - 1);
+    aligned_sector = (sector & ~((1ULL << (disk->log_sector_size
+                                          - GRUB_DISK_SECTOR_BITS))
+                                - 1));
+    offset += ((sector - aligned_sector) << GRUB_DISK_SECTOR_BITS);
+    num = ((size + offset + (1ULL << (disk->log_sector_size))
+           - 1) >> (disk->log_sector_size));
+
+    tmp_buf = grub_malloc (num << disk->log_sector_size);
+    if (!tmp_buf)
+      return grub_errno;
+    
+    if ((disk->dev->disk_read) (disk, transform_sector (disk, aligned_sector),
+                               num, tmp_buf))
+      {
+       grub_error_push ();
+       grub_dprintf ("disk", "%s read failed\n", disk->name);
+       grub_error_pop ();
+       grub_free (tmp_buf);
+       return grub_errno;
+      }
+    grub_memcpy (buf, tmp_buf + offset, size);
+    grub_free (tmp_buf);
+    return GRUB_ERR_NONE;
+  }
+}
+
+static grub_err_t
+grub_disk_read_small (grub_disk_t disk, grub_disk_addr_t sector,
+                     grub_off_t offset, grub_size_t size, void *buf)
+{
+  grub_err_t err;
+
+  err = grub_disk_read_small_real (disk, sector, offset, size, buf);
+  if (err)
+    return err;
+  if (disk->read_hook)
+    (disk->read_hook) (sector + (offset >> GRUB_DISK_SECTOR_BITS),
+                      offset & (GRUB_DISK_SECTOR_SIZE - 1),
+                      size, disk->read_hook_data);
+  return GRUB_ERR_NONE;
+}
+
+grub_err_t grub_disk_blocklist_read(void *chunklist, grub_uint64_t sector, 
+    grub_uint64_t size, grub_uint32_t log_sector_size)
+{
+    ventoy_img_chunk *last_chunk = NULL;
+    ventoy_img_chunk *new_chunk = NULL;
+    ventoy_img_chunk_list *chunk_list = (ventoy_img_chunk_list *)chunklist;
+
+    if (chunk_list->cur_chunk == 0)
+    {
+        chunk_list->chunk[0].img_start_sector = 0;
+        chunk_list->chunk[0].img_end_sector = (size >> 11) - 1;
+        chunk_list->chunk[0].disk_start_sector = sector;
+        chunk_list->chunk[0].disk_end_sector = sector + (size >> log_sector_size) - 1;
+        chunk_list->cur_chunk = 1;
+        return 0;
+    }
+
+    last_chunk = chunk_list->chunk + chunk_list->cur_chunk - 1;
+    if (last_chunk->disk_end_sector + 1 == sector)
+    {
+        last_chunk->img_end_sector  += (size >> 11);
+        last_chunk->disk_end_sector += (size >> log_sector_size);
+        return 0;
+    }
+
+    if (chunk_list->cur_chunk == chunk_list->max_chunk)
+    {
+        new_chunk = grub_realloc(chunk_list->chunk, chunk_list->max_chunk * 2 * sizeof(ventoy_img_chunk));
+        if (NULL == new_chunk)
+        {
+            return -1;
+        }
+        chunk_list->chunk = new_chunk;
+        chunk_list->max_chunk *= 2;
+
+        /* issue: update last_chunk */
+        last_chunk = chunk_list->chunk + chunk_list->cur_chunk - 1;
+    }
+
+    new_chunk = chunk_list->chunk + chunk_list->cur_chunk;
+    new_chunk->img_start_sector = last_chunk->img_end_sector + 1;
+    new_chunk->img_end_sector = new_chunk->img_start_sector + (size >> 11) - 1;
+    new_chunk->disk_start_sector = sector;
+    new_chunk->disk_end_sector = sector + (size >> log_sector_size) - 1;
+
+    chunk_list->cur_chunk++;
+
+    return 0;
+}
+
+/* Read data from the disk.  */
+grub_err_t
+grub_disk_read (grub_disk_t disk, grub_disk_addr_t sector,
+               grub_off_t offset, grub_size_t size, void *buf)
+{
+    if (disk->read_hook == (grub_disk_read_hook_t)grub_disk_blocklist_read)
+    {
+        return grub_disk_blocklist_read((ventoy_img_chunk_list *)disk->read_hook_data, sector, size, disk->log_sector_size);
+    }
+
+  /* First of all, check if the region is within the disk.  */
+  if (grub_disk_adjust_range (disk, &sector, &offset, size) != GRUB_ERR_NONE)
+    {
+      grub_error_push ();
+      grub_dprintf ("disk", "Read out of range: sector 0x%llx (%s).\n",
+                   (unsigned long long) sector, grub_errmsg);
+      grub_error_pop ();
+      return grub_errno;
+    }
+
+  /* First read until first cache boundary.   */
+  if (offset || (sector & (GRUB_DISK_CACHE_SIZE - 1)))
+    {
+      grub_disk_addr_t start_sector;
+      grub_size_t pos;
+      grub_err_t err;
+      grub_size_t len;
+
+      start_sector = sector & ~((grub_disk_addr_t) GRUB_DISK_CACHE_SIZE - 1);
+      pos = (sector - start_sector) << GRUB_DISK_SECTOR_BITS;
+      len = ((GRUB_DISK_SECTOR_SIZE << GRUB_DISK_CACHE_BITS)
+            - pos - offset);
+      if (len > size)
+       len = size;
+      err = grub_disk_read_small (disk, start_sector,
+                                 offset + pos, len, buf);
+      if (err)
+       return err;
+      buf = (char *) buf + len;
+      size -= len;
+      offset += len;
+      sector += (offset >> GRUB_DISK_SECTOR_BITS);
+      offset &= ((1 << GRUB_DISK_SECTOR_BITS) - 1);
+    }
+
+  /* Until SIZE is zero...  */
+  while (size >= (GRUB_DISK_CACHE_SIZE << GRUB_DISK_SECTOR_BITS))
+    {
+      char *data = NULL;
+      grub_disk_addr_t agglomerate;
+      grub_err_t err;
+
+      /* agglomerate read until we find a first cached entry.  */
+      for (agglomerate = 0; agglomerate
+            < (size >> (GRUB_DISK_SECTOR_BITS + GRUB_DISK_CACHE_BITS))
+            && agglomerate < disk->max_agglomerate;
+          agglomerate++)
+       {
+         data = grub_disk_cache_fetch (disk->dev->id, disk->id,
+                                       sector + (agglomerate
+                                                 << GRUB_DISK_CACHE_BITS));
+         if (data)
+           break;
+       }
+
+      if (data)
+       {
+         grub_memcpy ((char *) buf
+                      + (agglomerate << (GRUB_DISK_CACHE_BITS
+                                         + GRUB_DISK_SECTOR_BITS)),
+                      data, GRUB_DISK_CACHE_SIZE << GRUB_DISK_SECTOR_BITS);
+         grub_disk_cache_unlock (disk->dev->id, disk->id,
+                                 sector + (agglomerate
+                                           << GRUB_DISK_CACHE_BITS));
+       }
+
+      if (agglomerate)
+       {
+         grub_disk_addr_t i;
+
+         err = (disk->dev->disk_read) (disk, transform_sector (disk, sector),
+                                       agglomerate << (GRUB_DISK_CACHE_BITS
+                                                       + GRUB_DISK_SECTOR_BITS
+                                                       - disk->log_sector_size),
+                                       buf);
+         if (err)
+           return err;
+         
+         for (i = 0; i < agglomerate; i ++)
+           grub_disk_cache_store (disk->dev->id, disk->id,
+                                  sector + (i << GRUB_DISK_CACHE_BITS),
+                                  (char *) buf
+                                  + (i << (GRUB_DISK_CACHE_BITS
+                                           + GRUB_DISK_SECTOR_BITS)));
+
+
+         if (disk->read_hook)
+           (disk->read_hook) (sector, 0, agglomerate << (GRUB_DISK_CACHE_BITS + GRUB_DISK_SECTOR_BITS),
+                              disk->read_hook_data);
+
+         sector += agglomerate << GRUB_DISK_CACHE_BITS;
+         size -= agglomerate << (GRUB_DISK_CACHE_BITS + GRUB_DISK_SECTOR_BITS);
+         buf = (char *) buf 
+           + (agglomerate << (GRUB_DISK_CACHE_BITS + GRUB_DISK_SECTOR_BITS));
+       }
+
+      if (data)
+       {
+         if (disk->read_hook)
+           (disk->read_hook) (sector, 0, (GRUB_DISK_CACHE_SIZE << GRUB_DISK_SECTOR_BITS),
+                              disk->read_hook_data);
+         sector += GRUB_DISK_CACHE_SIZE;
+         buf = (char *) buf + (GRUB_DISK_CACHE_SIZE << GRUB_DISK_SECTOR_BITS);
+         size -= (GRUB_DISK_CACHE_SIZE << GRUB_DISK_SECTOR_BITS);
+       }
+    }
+
+  /* And now read the last part.  */
+  if (size)
+    {
+      grub_err_t err;
+      err = grub_disk_read_small (disk, sector, 0, size, buf);
+      if (err)
+       return err;
+    }
+
+  return grub_errno;
+}
+
+grub_uint64_t
+grub_disk_get_size (grub_disk_t disk)
+{
+  if (disk->partition)
+    return grub_partition_get_len (disk->partition);
+  else if (disk->total_sectors != GRUB_DISK_SIZE_UNKNOWN)
+    return disk->total_sectors << (disk->log_sector_size - GRUB_DISK_SECTOR_BITS);
+  else
+    return GRUB_DISK_SIZE_UNKNOWN;
+}
index 459464d1bcd3491e5c13a14fbb10795c87d8632a..1978daefb2e26284956a41fea2ed457fa78b9ede 100644 (file)
@@ -37,6 +37,7 @@
 #include <grub/efi/efi.h>
 #endif
 #include <grub/time.h>
+#include <grub/relocator.h>
 #include <grub/ventoy.h>
 #include "ventoy_def.h"
 
@@ -100,6 +101,36 @@ int ventoy_is_efi_os(void)
     return g_efi_os;
 }
 
+static int ventoy_get_fs_type(const char *fs)
+{
+    if (NULL == fs)
+    {
+        return ventoy_fs_max;
+    }
+    else if (grub_strncmp(fs, "exfat", 5) == 0)
+    {
+        return ventoy_fs_exfat;
+    }
+    else if (grub_strncmp(fs, "ntfs", 4) == 0)
+    {
+        return ventoy_fs_ntfs;
+    }
+    else if (grub_strncmp(fs, "ext", 3) == 0)
+    {
+        return ventoy_fs_ext;
+    }
+    else if (grub_strncmp(fs, "xfs", 3) == 0)
+    {
+        return ventoy_fs_xfs;
+    }
+    else if (grub_strncmp(fs, "udf", 3) == 0)
+    {
+        return ventoy_fs_udf;
+    }
+
+    return ventoy_fs_max;
+}
+
 static int ventoy_string_check(const char *str, grub_char_check_func check)
 {
     if (!str)
@@ -942,6 +973,12 @@ static grub_err_t ventoy_cmd_list_img(grub_extcmd_context_t ctxt, int argc, char
         goto fail;
     }
 
+    if (ventoy_get_fs_type(fs->name) >= ventoy_fs_max)
+    {
+        debug("unsupported fs:<%s>\n", fs->name);
+        goto fail;
+    }
+
     grub_memset(&g_img_iterator_head, 0, sizeof(g_img_iterator_head));
 
     g_img_iterator_head.dirlen = 1;
@@ -1212,19 +1249,7 @@ void ventoy_fill_os_param(grub_file_t file, ventoy_os_param *param)
 
     param->vtoy_disk_size = disk->total_sectors * (1 << disk->log_sector_size);
     param->vtoy_disk_part_id = disk->partition->number + 1;
-
-    if (grub_strcmp(file->fs->name, "exfat") == 0)
-    {
-        param->vtoy_disk_part_type = 0;
-    }
-    else if (grub_strcmp(file->fs->name, "ntfs") == 0)
-    {
-        param->vtoy_disk_part_type = 1;
-    }
-    else
-    {
-        param->vtoy_disk_part_type = 0xFFFF;
-    }
+    param->vtoy_disk_part_type = ventoy_get_fs_type(file->fs->name);
 
     pos = grub_strstr(file->name, "/");
     if (!pos)
@@ -1251,6 +1276,52 @@ void ventoy_fill_os_param(grub_file_t file, ventoy_os_param *param)
     return;
 }
 
+static int ventoy_get_block_list(grub_file_t file, ventoy_img_chunk_list *chunklist, grub_disk_addr_t start)
+{
+    int fs_type;
+    grub_uint32_t i = 0;
+    grub_uint32_t sector = 0;
+    grub_uint32_t count = 0;
+    grub_off_t size = 0;
+    grub_off_t read = 0;
+
+    fs_type = ventoy_get_fs_type(file->fs->name);
+    if (fs_type == ventoy_fs_exfat)
+    {
+        grub_fat_get_file_chunk(start, file, chunklist);        
+    }
+    else
+    {
+        file->read_hook = (grub_disk_read_hook_t)grub_disk_blocklist_read;
+        file->read_hook_data = chunklist;
+
+        for (size = file->size; size > 0; size -= read)
+        {
+            read = (size > VTOY_SIZE_1GB) ? VTOY_SIZE_1GB : size;
+            grub_file_read(file, NULL, read);
+        }
+
+        for (i = 0; start > 0 && i < chunklist->cur_chunk; i++)
+        {
+            chunklist->chunk[i].disk_start_sector += start;
+            chunklist->chunk[i].disk_end_sector += start;
+        }
+
+        if (ventoy_fs_udf == fs_type)
+        {
+            for (i = 0; i < chunklist->cur_chunk; i++)
+            {
+                count = (chunklist->chunk[i].disk_end_sector + 1 - chunklist->chunk[i].disk_start_sector) >> 2;
+                chunklist->chunk[i].img_start_sector = sector;
+                chunklist->chunk[i].img_end_sector = sector + count - 1;
+                sector += count;
+            }
+        }
+    }
+
+    return 0;
+}
+
 static grub_err_t ventoy_cmd_img_sector(grub_extcmd_context_t ctxt, int argc, char **args)
 {
     grub_file_t file;
@@ -1280,8 +1351,7 @@ static grub_err_t ventoy_cmd_img_sector(grub_extcmd_context_t ctxt, int argc, ch
     g_img_chunk_list.max_chunk = DEFAULT_CHUNK_NUM;
     g_img_chunk_list.cur_chunk = 0;
 
-    debug("get fat file chunk part start:%llu\n", (unsigned long long)file->device->disk->partition->start);
-    grub_fat_get_file_chunk(file->device->disk->partition->start, file, &g_img_chunk_list);
+    ventoy_get_block_list(file, &g_img_chunk_list, file->device->disk->partition->start);
 
     grub_file_close(file);
 
@@ -1311,6 +1381,121 @@ static grub_err_t ventoy_cmd_dump_img_sector(grub_extcmd_context_t ctxt, int arg
     VENTOY_CMD_RETURN(GRUB_ERR_NONE);
 }
 
+#ifdef GRUB_MACHINE_EFI
+static grub_err_t ventoy_cmd_relocator_chaindata(grub_extcmd_context_t ctxt, int argc, char **args)
+{
+    (void)ctxt;
+    (void)argc;
+    (void)args;
+    return 0;
+}
+#else
+static grub_err_t ventoy_cmd_relocator_chaindata(grub_extcmd_context_t ctxt, int argc, char **args)
+{
+    int rc = 0;
+    ulong chain_len = 0;
+    char *chain_data = NULL;
+    char *relocator_addr = NULL;
+    grub_relocator_chunk_t ch;
+    struct grub_relocator *relocator = NULL;
+    char envbuf[64] = { 0 };
+
+    (void)ctxt;
+    (void)argc;
+    (void)args;
+    
+    if (argc != 2)
+    {
+        return 1;
+    }
+
+    chain_data = (char *)grub_strtoul(args[0], NULL, 16);
+    chain_len = grub_strtoul(args[1], NULL, 10);
+
+    relocator = grub_relocator_new ();
+    if (!relocator)
+    {
+        debug("grub_relocator_new failed %p %lu\n", chain_data, chain_len);
+        return 1;
+    }
+
+    rc = grub_relocator_alloc_chunk_addr (relocator, &ch,
+                                          0x100000, // GRUB_LINUX_BZIMAGE_ADDR,
+                                          chain_len);
+    if (rc)
+    {
+        debug("grub_relocator_alloc_chunk_addr failed %d %p %lu\n", rc, chain_data, chain_len);
+        grub_relocator_unload (relocator);
+        return 1;
+    }
+
+    relocator_addr = get_virtual_current_address(ch);
+
+    grub_memcpy(relocator_addr, chain_data, chain_len);
+    
+    grub_relocator_unload (relocator);
+
+    grub_snprintf(envbuf, sizeof(envbuf), "0x%lx", (unsigned long)relocator_addr);
+    grub_env_set("vtoy_chain_relocator_addr", envbuf);
+
+    VENTOY_CMD_RETURN(GRUB_ERR_NONE);
+}
+#endif
+
+static grub_err_t ventoy_cmd_test_block_list(grub_extcmd_context_t ctxt, int argc, char **args)
+{
+    grub_uint32_t i;
+    grub_file_t file;
+    ventoy_img_chunk_list chunklist;
+    
+    (void)ctxt;
+    (void)argc;
+
+    file = ventoy_grub_file_open(VENTOY_FILE_TYPE, "%s", args[0]);
+    if (!file)
+    {
+        return grub_error(GRUB_ERR_BAD_ARGUMENT, "Can't open file %s\n", args[0]); 
+    }
+
+    /* get image chunk data */
+    grub_memset(&chunklist, 0, sizeof(chunklist));
+    chunklist.chunk = grub_malloc(sizeof(ventoy_img_chunk) * DEFAULT_CHUNK_NUM);
+    if (NULL == chunklist.chunk)
+    {
+        return grub_error(GRUB_ERR_OUT_OF_MEMORY, "Can't allocate image chunk memoty\n");
+    }
+    
+    chunklist.max_chunk = DEFAULT_CHUNK_NUM;
+    chunklist.cur_chunk = 0;
+
+    ventoy_get_block_list(file, &chunklist, 0);
+
+    grub_file_close(file);
+
+    grub_printf("filesystem: <%s> entry number:<%u>\n", file->fs->name, chunklist.cur_chunk);
+
+    for (i = 0; i < chunklist.cur_chunk; i++)
+    {
+        grub_printf("%llu+%llu,", (ulonglong)chunklist.chunk[i].disk_start_sector,
+            (ulonglong)(chunklist.chunk[i].disk_end_sector + 1 - chunklist.chunk[i].disk_start_sector));
+    }
+
+    grub_printf("\n==================================\n");
+    for (i = 0; i < chunklist.cur_chunk; i++)
+    {
+        grub_printf("%2u: [%llu %llu] - [%llu %llu]\n", i, 
+            (ulonglong)chunklist.chunk[i].img_start_sector,
+            (ulonglong)chunklist.chunk[i].img_end_sector,
+            (ulonglong)chunklist.chunk[i].disk_start_sector,
+            (ulonglong)chunklist.chunk[i].disk_end_sector
+            );
+    }
+
+    grub_free(chunklist.chunk);
+
+    VENTOY_CMD_RETURN(GRUB_ERR_NONE);
+}
+
 static grub_err_t ventoy_cmd_add_replace_file(grub_extcmd_context_t ctxt, int argc, char **args)
 {
     int i;
@@ -1412,7 +1597,7 @@ static grub_err_t ventoy_cmd_dynamic_menu(grub_extcmd_context_t ctxt, int argc,
 
     if (argc != 2)
     {
-        debug("Invlaid argc %d\n", argc);
+        debug("Invalid argc %d\n", argc);
         return 0;
     }    
 
@@ -1635,6 +1820,8 @@ static cmd_para ventoy_cmds[] =
     { "vt_windows_chain_data", ventoy_cmd_windows_chain_data, 0, NULL, "", "", NULL },
 
     { "vt_add_replace_file", ventoy_cmd_add_replace_file, 0, NULL, "", "", NULL },
+    { "vt_relocator_chaindata", ventoy_cmd_relocator_chaindata, 0, NULL, "", "", NULL },
+    { "vt_test_block_list", ventoy_cmd_test_block_list, 0, NULL, "", "", NULL },
 
     
     { "vt_load_plugin", ventoy_cmd_load_plugin, 0, NULL, "", "", NULL },
index 085fd1e8ba6722f90928447ee9e4feec447395d3..c045623666909d9c4213c5b07f61bcb31c5c124b 100644 (file)
@@ -23,6 +23,8 @@
 
 #define VTOY_MAX_SCRIPT_BUF    (4 * 1024 * 1024)
 
+#define VTOY_SIZE_1GB  1073741824
+
 #define JSON_SUCCESS    0
 #define JSON_FAILED     1
 #define JSON_NOT_FOUND  2
index e2b4eed9de2c1ad65a5f8b0ddd8a93aed1ee33b5..529d30864b2630dee3b2285016061492e8f78e3c 100644 (file)
@@ -401,7 +401,7 @@ static int ventoy_update_all_hash(void *meta_data, wim_directory_entry *dir)
         return 0;
     }
 
-    if (dir->len == 0)
+    if (dir->len < sizeof(wim_directory_entry))
     {
         return 0;
     }
@@ -420,7 +420,7 @@ static int ventoy_update_all_hash(void *meta_data, wim_directory_entry *dir)
         }
     
         dir = (wim_directory_entry *)((char *)dir + dir->len);
-    } while (dir->len);
+    } while (dir->len >= sizeof(wim_directory_entry));
 
     return 0;
 }
@@ -957,4 +957,3 @@ grub_err_t ventoy_cmd_windows_chain_data(grub_extcmd_context_t ctxt, int argc, c
     VENTOY_CMD_RETURN(GRUB_ERR_NONE);
 }
 
-
diff --git a/GRUB2/grub-2.04/include/grub/disk.h b/GRUB2/grub-2.04/include/grub/disk.h
new file mode 100644 (file)
index 0000000..ef4e55c
--- /dev/null
@@ -0,0 +1,265 @@
+/*
+ *  GRUB  --  GRand Unified Bootloader
+ *  Copyright (C) 2002,2003,2004,2005,2006,2007,2008,2009  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_DISK_HEADER
+#define GRUB_DISK_HEADER       1
+
+#include <config.h>
+
+#include <grub/symbol.h>
+#include <grub/err.h>
+#include <grub/types.h>
+#include <grub/device.h>
+/* For NULL.  */
+#include <grub/mm.h>
+
+/* These are used to set a device id. When you add a new disk device,
+   you must define a new id for it here.  */
+enum grub_disk_dev_id
+  {
+    GRUB_DISK_DEVICE_BIOSDISK_ID,
+    GRUB_DISK_DEVICE_OFDISK_ID,
+    GRUB_DISK_DEVICE_LOOPBACK_ID,
+    GRUB_DISK_DEVICE_EFIDISK_ID,
+    GRUB_DISK_DEVICE_DISKFILTER_ID,
+    GRUB_DISK_DEVICE_HOST_ID,
+    GRUB_DISK_DEVICE_ATA_ID,
+    GRUB_DISK_DEVICE_MEMDISK_ID,
+    GRUB_DISK_DEVICE_NAND_ID,
+    GRUB_DISK_DEVICE_SCSI_ID,
+    GRUB_DISK_DEVICE_CRYPTODISK_ID,
+    GRUB_DISK_DEVICE_ARCDISK_ID,
+    GRUB_DISK_DEVICE_HOSTDISK_ID,
+    GRUB_DISK_DEVICE_PROCFS_ID,
+    GRUB_DISK_DEVICE_CBFSDISK_ID,
+    GRUB_DISK_DEVICE_UBOOTDISK_ID,
+    GRUB_DISK_DEVICE_XEN,
+    GRUB_DISK_DEVICE_OBDISK_ID,
+  };
+
+struct grub_disk;
+#ifdef GRUB_UTIL
+struct grub_disk_memberlist;
+#endif
+
+typedef enum
+  { 
+    GRUB_DISK_PULL_NONE,
+    GRUB_DISK_PULL_REMOVABLE,
+    GRUB_DISK_PULL_RESCAN,
+    GRUB_DISK_PULL_MAX
+  } grub_disk_pull_t;
+
+typedef int (*grub_disk_dev_iterate_hook_t) (const char *name, void *data);
+
+/* Disk device.  */
+struct grub_disk_dev
+{
+  /* The device name.  */
+  const char *name;
+
+  /* The device id used by the cache manager.  */
+  enum grub_disk_dev_id id;
+
+  /* Call HOOK with each device name, until HOOK returns non-zero.  */
+  int (*disk_iterate) (grub_disk_dev_iterate_hook_t hook, void *hook_data,
+                 grub_disk_pull_t pull);
+
+  /* Open the device named NAME, and set up DISK.  */
+  grub_err_t (*disk_open) (const char *name, struct grub_disk *disk);
+
+  /* Close the disk DISK.  */
+  void (*disk_close) (struct grub_disk *disk);
+
+  /* Read SIZE sectors from the sector SECTOR of the disk DISK into BUF.  */
+  grub_err_t (*disk_read) (struct grub_disk *disk, grub_disk_addr_t sector,
+                     grub_size_t size, char *buf);
+
+  /* Write SIZE sectors from BUF into the sector SECTOR of the disk DISK.  */
+  grub_err_t (*disk_write) (struct grub_disk *disk, grub_disk_addr_t sector,
+                      grub_size_t size, const char *buf);
+
+#ifdef GRUB_UTIL
+  struct grub_disk_memberlist *(*disk_memberlist) (struct grub_disk *disk);
+  const char * (*disk_raidname) (struct grub_disk *disk);
+#endif
+
+  /* The next disk device.  */
+  struct grub_disk_dev *next;
+};
+typedef struct grub_disk_dev *grub_disk_dev_t;
+
+extern grub_disk_dev_t EXPORT_VAR (grub_disk_dev_list);
+
+struct grub_partition;
+
+typedef void (*grub_disk_read_hook_t) (grub_disk_addr_t sector,
+                                      unsigned offset, unsigned length,
+                                      void *data);
+
+/* Disk.  */
+struct grub_disk
+{
+  /* The disk name.  */
+  const char *name;
+
+  /* The underlying disk device.  */
+  grub_disk_dev_t dev;
+
+  /* The total number of sectors.  */
+  grub_uint64_t total_sectors;
+
+  /* Logarithm of sector size.  */
+  unsigned int log_sector_size;
+
+  /* Maximum number of sectors read divided by GRUB_DISK_CACHE_SIZE.  */
+  unsigned int max_agglomerate;
+
+  /* The id used by the disk cache manager.  */
+  unsigned long id;
+
+  /* The partition information. This is machine-specific.  */
+  struct grub_partition *partition;
+
+  /* Called when a sector was read. OFFSET is between 0 and
+     the sector size minus 1, and LENGTH is between 0 and the sector size.  */
+  grub_disk_read_hook_t read_hook;
+
+  /* Caller-specific data passed to the read hook.  */
+  void *read_hook_data;
+
+  /* Device-specific data.  */
+  void *data;
+};
+typedef struct grub_disk *grub_disk_t;
+
+#ifdef GRUB_UTIL
+struct grub_disk_memberlist
+{
+  grub_disk_t disk;
+  struct grub_disk_memberlist *next;
+};
+typedef struct grub_disk_memberlist *grub_disk_memberlist_t;
+#endif
+
+/* The sector size.  */
+#define GRUB_DISK_SECTOR_SIZE  0x200
+#define GRUB_DISK_SECTOR_BITS  9
+
+/* The maximum number of disk caches.  */
+#define GRUB_DISK_CACHE_NUM    1021
+
+/* The size of a disk cache in 512B units. Must be at least as big as the
+   largest supported sector size, currently 16K.  */
+#define GRUB_DISK_CACHE_BITS   6
+#define GRUB_DISK_CACHE_SIZE   (1 << GRUB_DISK_CACHE_BITS)
+
+#define GRUB_DISK_MAX_MAX_AGGLOMERATE ((1 << (30 - GRUB_DISK_CACHE_BITS - GRUB_DISK_SECTOR_BITS)) - 1)
+
+/* Return value of grub_disk_get_size() in case disk size is unknown. */
+#define GRUB_DISK_SIZE_UNKNOWN  0xffffffffffffffffULL
+
+/* This is called from the memory manager.  */
+void grub_disk_cache_invalidate_all (void);
+
+void EXPORT_FUNC(grub_disk_dev_register) (grub_disk_dev_t dev);
+void EXPORT_FUNC(grub_disk_dev_unregister) (grub_disk_dev_t dev);
+static inline int
+grub_disk_dev_iterate (grub_disk_dev_iterate_hook_t hook, void *hook_data)
+{
+  grub_disk_dev_t p;
+  grub_disk_pull_t pull;
+
+  for (pull = 0; pull < GRUB_DISK_PULL_MAX; pull++)
+    for (p = grub_disk_dev_list; p; p = p->next)
+      if (p->disk_iterate && (p->disk_iterate) (hook, hook_data, pull))
+       return 1;
+
+  return 0;
+}
+
+grub_disk_t EXPORT_FUNC(grub_disk_open) (const char *name);
+void EXPORT_FUNC(grub_disk_close) (grub_disk_t disk);
+grub_err_t EXPORT_FUNC(grub_disk_blocklist_read)(void *chunklist, grub_uint64_t sector, 
+    grub_uint64_t size, grub_uint32_t log_sector_size);
+
+grub_err_t EXPORT_FUNC(grub_disk_read) (grub_disk_t disk,
+                                       grub_disk_addr_t sector,
+                                       grub_off_t offset,
+                                       grub_size_t size,
+                                       void *buf);
+grub_err_t grub_disk_write (grub_disk_t disk,
+                           grub_disk_addr_t sector,
+                           grub_off_t offset,
+                           grub_size_t size,
+                           const void *buf);
+extern grub_err_t (*EXPORT_VAR(grub_disk_write_weak)) (grub_disk_t disk,
+                                                      grub_disk_addr_t sector,
+                                                      grub_off_t offset,
+                                                      grub_size_t size,
+                                                      const void *buf);
+
+
+grub_uint64_t EXPORT_FUNC(grub_disk_get_size) (grub_disk_t disk);
+
+#if DISK_CACHE_STATS
+void
+EXPORT_FUNC(grub_disk_cache_get_performance) (unsigned long *hits, unsigned long *misses);
+#endif
+
+extern void (* EXPORT_VAR(grub_disk_firmware_fini)) (void);
+extern int EXPORT_VAR(grub_disk_firmware_is_tainted);
+
+static inline void
+grub_stop_disk_firmware (void)
+{
+  /* To prevent two drivers operating on the same disks.  */
+  grub_disk_firmware_is_tainted = 1;
+  if (grub_disk_firmware_fini)
+    {
+      grub_disk_firmware_fini ();
+      grub_disk_firmware_fini = NULL;
+    }
+}
+
+/* Disk cache.  */
+struct grub_disk_cache
+{
+  enum grub_disk_dev_id dev_id;
+  unsigned long disk_id;
+  grub_disk_addr_t sector;
+  char *data;
+  int lock;
+};
+
+extern struct grub_disk_cache EXPORT_VAR(grub_disk_cache_table)[GRUB_DISK_CACHE_NUM];
+
+#if defined (GRUB_UTIL)
+void grub_lvm_init (void);
+void grub_ldm_init (void);
+void grub_mdraid09_init (void);
+void grub_mdraid1x_init (void);
+void grub_diskfilter_init (void);
+void grub_lvm_fini (void);
+void grub_ldm_fini (void);
+void grub_mdraid09_fini (void);
+void grub_mdraid1x_fini (void);
+void grub_diskfilter_fini (void);
+#endif
+
+#endif /* ! GRUB_DISK_HEADER */
index 4f3de66583ff35250fd38aa9da04e596c7b24603..205ba1d19ea9ce77496b07ce719cc1bd06a2c769 100644 (file)
 
 #define VENTOY_GUID { 0x77772020, 0x2e77, 0x6576, { 0x6e, 0x74, 0x6f, 0x79, 0x2e, 0x6e, 0x65, 0x74 }}
 
+typedef enum ventoy_fs_type
+{
+    ventoy_fs_exfat = 0, /* 0: exfat */
+    ventoy_fs_ntfs,      /* 1: NTFS */
+    ventoy_fs_ext,       /* 2: ext2/ext3/ext4 */
+    ventoy_fs_xfs,       /* 3: XFS */
+    ventoy_fs_udf,       /* 4: UDF */
+
+    ventoy_fs_max
+}ventoy_fs_type;
+
 #pragma pack(1)
 
 typedef struct ventoy_guid
index 9d9ce3b9c869833b9a5064cc100dfeaad02995ba..e0624c0c3c97b4dfec15e18da48d760d20bfa924 100644 (file)
Binary files a/INSTALL/EFI/BOOT/grubx64_real.efi and b/INSTALL/EFI/BOOT/grubx64_real.efi differ
index 441c04c7b4442f1d6b90124eaba519822cd3714a..38cbf07e7ad048bc833769639ac59a22c3b005af 100644 (file)
Binary files a/INSTALL/Ventoy2Disk.exe and b/INSTALL/Ventoy2Disk.exe differ
index 29e30710d05c8434c62ff3a4f1c85c09efe49474..6432217849da4cde9c9453158022e94bb4784cd9 100644 (file)
@@ -9,12 +9,16 @@ fi
 . ./tool/ventoy_lib.sh
 
 print_usage() {
-    echo 'Usage:  VentoyInstaller.sh OPTION /dev/sdX'
-    echo '  OPTION:'
+    echo 'Usage:  Ventoy2Disk.sh CMD [ OPTION ] /dev/sdX'
+    echo '  CMD:'
     echo '   -i  install ventoy to sdX (fail if disk already installed with ventoy)'
     echo '   -u  update ventoy in sdX'
     echo '   -I  force install ventoy to sdX (no matter installed or not)'
     echo ''
+    echo '  OPTION: (optional)'
+    echo '   -s  enable secure boot support (default is disabled)'
+    echo ''
+    
 }
 
 echo ''
@@ -38,6 +42,7 @@ while [ -n "$1" ]; do
         SECUREBOOT="YES"
     else
         if ! [ -b "$1" ]; then
+            vterr "$1 is NOT a valid device"
             print_usage
             cd $OLDDIR
             exit 1
@@ -54,13 +59,25 @@ if [ -z "$MODE" ]; then
     exit 1
 fi
 
-if [ -z "$SUDO_USER" ]; then
-    if [ "$USER" != "root" ]; then
-        vterr "EUID is $EUID root permission is required."
-        echo ''
-        cd $OLDDIR
-        exit 1
-    fi
+if ! [ -b "$DISK" ]; then
+    vterr "Disk $DISK does not exist"
+    cd $OLDDIR
+    exit 1
+fi
+
+if [ -e /sys/class/block/${DISK#/dev/}/start ]; then
+    vterr "$DISK is a partition, please use the whole disk"
+    cd $OLDDIR
+    exit 1
+fi
+
+if dd if="$DISK" of=/dev/null bs=1 count=1 >/dev/null 2>&1; then
+    vtdebug "root permission check ok ..."
+else
+    vterr "Failed to access $DISK, maybe root privilege is needed!"
+    echo ''
+    cd $OLDDIR
+    exit 1
 fi
 
 vtdebug "MODE=$MODE FORCE=$FORCE"
@@ -93,20 +110,6 @@ if ! check_tool_work_ok; then
     exit 1
 fi
 
-
-if ! [ -b "$DISK" ]; then
-    vterr "Disk $DISK does not exist"
-    cd $OLDDIR
-    exit 1
-fi
-
-
-if [ -e /sys/class/block/${DISK#/dev/}/start ]; then
-    vterr "$DISK is a partition, please use the whole disk"
-    cd $OLDDIR
-    exit 1
-fi
-
 grep "^$DISK" /proc/mounts | while read mtline; do
     mtpnt=$(echo $mtline | awk '{print $DISK}')
     vtdebug "Trying to umount $mtpnt ..."
@@ -242,6 +245,7 @@ if [ "$MODE" = "install" ]; then
         rm -f ./tmp_mnt/EFI/BOOT/BOOTX64.EFI
         rm -f ./tmp_mnt/EFI/BOOT/grubx64.efi
         rm -f ./tmp_mnt/EFI/BOOT/MokManager.efi
+        rm -f ./tmp_mnt/ENROLL_THIS_KEY_IN_MOKMANAGER.cer
         mv ./tmp_mnt/EFI/BOOT/grubx64_real.efi  ./tmp_mnt/EFI/BOOT/BOOTX64.EFI
         
         umount ./tmp_mnt
@@ -305,6 +309,7 @@ else
         rm -f ./tmp_mnt/EFI/BOOT/BOOTX64.EFI
         rm -f ./tmp_mnt/EFI/BOOT/grubx64.efi
         rm -f ./tmp_mnt/EFI/BOOT/MokManager.efi
+        rm -f ./tmp_mnt/ENROLL_THIS_KEY_IN_MOKMANAGER.cer
         mv ./tmp_mnt/EFI/BOOT/grubx64_real.efi  ./tmp_mnt/EFI/BOOT/BOOTX64.EFI
         
         umount ./tmp_mnt
index da81008a19597eb49520c4dbaf734f11108a6247..763fed82c56a10d34c2233b2a21f6125d6b103ec 100644 (file)
@@ -50,6 +50,21 @@ function get_os_type {
     fi
 }
 
+function vt_check_pe {
+    unset VT_PE_SUPPORT
+
+    if [ -f $1/HBCD_PE.ini ]; then
+        set ventoy_compatible=YES
+        set VT_PE_SUPPORT=YES
+    elif [ -f $1/easyu.flg ]; then
+        set VT_PE_SUPPORT=YES
+    elif [ -f $1/USM.ICO ]; then
+        set VT_PE_SUPPORT=YES
+    elif [ -d $1/USM_TOOL ]; then
+        set VT_PE_SUPPORT=YES
+    fi
+}
+
 function locate_initrd {
     vt_linux_locate_initrd 
 
@@ -62,7 +77,8 @@ function locate_initrd {
 function find_wim_file {
     unset ventoy_wim_file
     
-    for file in "sources/boot.wim" "sources/BOOT.WIM" "Sources/Win10PEx64.WIM" "boot/BOOT.WIM" "winpe_x64.wim"; do
+    for file in "sources/boot.wim" "sources/BOOT.WIM" "Sources/Win10PEx64.WIM" "boot/BOOT.WIM" \
+                "winpe_x64.wim" "boot/10pex64.wim" "BOOT/USM1PE6L.WIM" "BOOT/USM1PE6F.WIM"; do
         if [ -e $1/$file ]; then
             set ventoy_wim_file=$1/$file
             break
@@ -114,6 +130,8 @@ function distro_specify_initrd_file_phase2 {
         vt_linux_specify_initrd_file /isolinux/initramfs
     elif [ -f (loop)/boot/iniramfs.igz ]; then
         vt_linux_specify_initrd_file /boot/iniramfs.igz
+    elif [ -f (loop)/initrd-x86_64 ]; then
+        vt_linux_specify_initrd_file /initrd-x86_64
         
     fi
 }
@@ -248,10 +266,11 @@ function uefi_iso_menu_func {
     vt_img_sector ${1}${chosen_path}
     
     if [ "$vtoy_os" = "Windows" ]; then
-        if [ "$ventoy_fs_probe" = "iso9660" ]; then
-            set ventoy_compatible=YES
-        elif [ -f (loop)/HBCD_PE.ini ]; then
-            set ventoy_compatible=YES
+        vt_check_pe (loop)        
+        if [ "$VT_PE_SUPPORT" != "YES" ]; then
+            if [ "$ventoy_fs_probe" = "iso9660" ]; then        
+                set ventoy_compatible=YES
+            fi
         fi
     
         uefi_windows_menu_func  $1 ${chosen_path}
@@ -293,8 +312,7 @@ function legacy_windows_menu_func {
     fi
     
     if [ -n "$vtoy_chain_mem_addr" ]; then
-        linux16   $vtoy_path/ipxe.krn ${vtdebug_flag} ibft
-        initrd16  mem:${vtoy_chain_mem_addr}:size:${vtoy_chain_mem_size}        
+        linux16   $vtoy_path/ipxe.krn ${vtdebug_flag} ibft mem:${vtoy_chain_mem_addr}:size:${vtoy_chain_mem_size}
         boot
     else
         echo "chain empty failed"
@@ -349,9 +367,8 @@ function legacy_linux_menu_func {
         sleep 5
     fi
     
-    if [ -n "$vtoy_chain_mem_addr" ]; then        
-        linux16   $vtoy_path/ipxe.krn ${vtdebug_flag}
-        initrd16  mem:${vtoy_chain_mem_addr}:size:${vtoy_chain_mem_size}        
+    if [ -n "$vtoy_chain_mem_addr" ]; then
+        linux16   $vtoy_path/ipxe.krn ${vtdebug_flag}  mem:${vtoy_chain_mem_addr}:size:${vtoy_chain_mem_size}
         boot
     else
         echo "chain empty failed"
@@ -389,10 +406,11 @@ function legacy_iso_menu_func {
     vt_img_sector ${1}${chosen_path}
 
     if [ "$vtoy_os" = "Windows" ]; then
-        if [ "$ventoy_fs_probe" = "iso9660" ]; then
-            set ventoy_compatible=YES
-        elif [ -f (loop)/HBCD_PE.ini ]; then
-            set ventoy_compatible=YES
+        vt_check_pe (loop)        
+        if [ "$VT_PE_SUPPORT" != "YES" ]; then
+            if [ "$ventoy_fs_probe" = "iso9660" ]; then        
+                set ventoy_compatible=YES
+            fi
         fi
         
         legacy_windows_menu_func  $1 ${chosen_path}
@@ -435,7 +453,7 @@ function common_menuentry {
 #############################################################
 #############################################################
 
-set VENTOY_VERSION="1.0.09"
+set VENTOY_VERSION="1.0.9Y"
 
 
 # Default menu display mode, you can change it as you want.
index a9464bc9958ca259d112fad8131f1b63eeb69835..4874c6a0978ed04f823bc103f3a0b26b7cb29c8f 100644 (file)
Binary files a/INSTALL/grub/i386-pc/core.img and b/INSTALL/grub/i386-pc/core.img differ
index 3562927c8b2e15853bd1308173cad8333810de3f..86317c78a79aed6ad991bd00e3e781b3eb0863ff 100644 (file)
@@ -14,16 +14,6 @@ ventoy_true() {
 }
 
 ventoy_is_linux64() {
-    if [ -e /lib64 ]; then
-        ventoy_true
-        return
-    fi
-    
-    if [ -e /usr/lib64 ]; then
-        ventoy_true
-        return
-    fi
-    
     if uname -a | egrep -q 'x86_64|amd64'; then
         ventoy_true
         return
@@ -165,12 +155,12 @@ is_disk_contains_ventoy() {
         return
     fi
     
-    PART1_TYPE=$(dd if=$DISK bs=1 count=1 skip=450 status=none | ./tool/hexdump -n1 -e  '1/1 "%02X"')
-    if [ "$PART1_TYPE" != "07" ]; then
-        vtdebug "part1 type is $PART2_TYPE not 07"
-        ventoy_false
-        return
-    fi
+    PART1_TYPE=$(dd if=$DISK bs=1 count=1 skip=450 status=none | ./tool/hexdump -n1 -e  '1/1 "%02X"')
+    if [ "$PART1_TYPE" != "07" ]; then
+        vtdebug "part1 type is $PART2_TYPE not 07"
+        ventoy_false
+        return
+    fi
     
     if [ -e /sys/class/block/${PART1#/dev/}/start ]; then
         PART1_START=$(cat /sys/class/block/${PART1#/dev/}/start)
index 5c0fa25697b1dfd65e15877d785fca89679bfb2d..d5ae13edef4d1b63da002fbc962d2fc40e03e54d 100644 (file)
Binary files a/INSTALL/ventoy/ipxe.krn and b/INSTALL/ventoy/ipxe.krn differ
index dd750ec0cad9d566c72caab797d3eeb8781c7ef8..4b2dd15061aff11c7150bd73d7142b8d54325e1b 100644 (file)
Binary files a/INSTALL/ventoy/ventoy.cpio and b/INSTALL/ventoy/ventoy.cpio differ
index ed15de5025bc2ef60a5d3c9e3ef68f6ce7a3a6b4..049f14beab157ffbc042a73561823e7fb6d1a450 100644 (file)
@@ -117,6 +117,10 @@ static void cmdline_strip ( char *cmdline, const char *cruft ) {
  */
 static int cmdline_init ( void ) {
        userptr_t cmdline_user;
+       userptr_t chainaddr;
+       char *pos1;
+       char *pos2;
+    int chainlen;
        char *cmdline;
        size_t len;
        int rc;
@@ -129,6 +133,23 @@ static int cmdline_init ( void ) {
        cmdline_user = phys_to_user ( cmdline_phys );
        len = ( strlen_user ( cmdline_user, 0 ) + 1 /* NUL */ );
 
+    pos1 = strstr((char *)cmdline_user, "mem:");
+    if (pos1)
+    {
+        pos2 = strstr(pos1, ":size:");
+        if (pos2)
+        {
+            *pos2 = 0;
+            chainaddr = phys_to_user(strtoul(pos1 + 4 + 2, NULL, 16)); // skip 0x prefix in hex number
+            chainlen = (int)strtoul(pos2 + 6, NULL, 10);
+            *pos2 = ':';
+
+            g_initrd_addr = (void *)umalloc(chainlen);  
+            g_initrd_len = chainlen;
+            memcpy_user((userptr_t)g_initrd_addr, 0, chainaddr, 0, chainlen);
+        }
+    }
+
        /* Allocate and copy command line */
        cmdline_copy = malloc ( len );
        if ( ! cmdline_copy ) {
@@ -137,6 +158,9 @@ static int cmdline_init ( void ) {
                rc = -ENOMEM;
                goto err_alloc_cmdline_copy;
        }
+
+    g_cmdline_copy = cmdline_copy;
+    
        cmdline = cmdline_copy;
        copy_from_user ( cmdline, cmdline_user, 0, len );
        DBGC ( colour, "RUNTIME found command line \"%s\" at %08x\n",
@@ -221,11 +245,6 @@ static int initrd_init ( void ) {
        memcpy_user ( image->data, 0, phys_to_user ( initrd_phys ), 0,
                      initrd_len );
 
-    g_initrd_addr = (void *)image->data;  
-    g_initrd_len = image->len;  
-    g_cmdline_copy = cmdline_copy;
-
-
        /* Mark initrd as consumed */
        initrd_phys = 0;
 
index a8085afd1bc4a6f7656d8be38866ddb58384c494..1a3022c5d7819c83e837847b131eae57d38c3ef1 100644 (file)
@@ -126,11 +126,8 @@ void hide_umalloc ( physaddr_t start, physaddr_t end ) {
  *
  */
 void hide_textdata ( void ) {
-/* Deleted by longpanda */
-#if 0
        hide_region ( &hidemem_textdata, virt_to_phys ( _textdata ),
                      virt_to_phys ( _etextdata ) );
-#endif
 }
 
 /**
index f520df2efae5fabee31874a54ce41bf894c5ee1c..688237695d7e0c292640e36bf800dd71bafe72c6 100644 (file)
Binary files a/LANGUAGES/languages.ini and b/LANGUAGES/languages.ini differ
index a3dfb776864a1278ffa9907f41dd074b0d1f40d8..0a84ce2ff771129b0dad18f1fe6ead9a9874f2ec 100644 (file)
@@ -130,8 +130,7 @@ static BOOL IsVentoyPhyDrive(int PhyDrive, UINT64 SizeBytes)
     PartStartSector = 2048;
     PartSectorCount = (UINT32)((SizeBytes - VENTOY_EFI_PART_SIZE - SIZE_1MB) / 512);
 
-    if (MBR.PartTbl[0].FsFlag != 0x07 ||
-        MBR.PartTbl[0].StartSectorId != PartStartSector ||
+    if (MBR.PartTbl[0].StartSectorId != PartStartSector ||
         MBR.PartTbl[0].SectorCount != PartSectorCount)
     {
         Log("Part1 not match %u %u", PartStartSector, PartSectorCount);
index fd557cf96e57b77ac4a447612ba5250e7ee5b6a4..e494598ad5605df3553841774c4bcd1f2347595c 100644 (file)
@@ -43,6 +43,17 @@ typedef unsigned char   uint8_t;
 
 #define VENTOY_GUID { 0x77772020, 0x2e77, 0x6576, { 0x6e, 0x74, 0x6f, 0x79, 0x2e, 0x6e, 0x65, 0x74 }}
 
+typedef enum ventoy_fs_type
+{
+    ventoy_fs_exfat = 0, /* 0: exfat */
+    ventoy_fs_ntfs,      /* 1: NTFS */
+    ventoy_fs_ext,       /* 2: ext2/ext3/ext4 */
+    ventoy_fs_xfs,       /* 3: XFS */
+    ventoy_fs_udf,       /* 4: UDF */
+
+    ventoy_fs_max
+}ventoy_fs_type;
+
 #pragma pack(1)
 
 typedef struct ventoy_guid
@@ -130,6 +141,11 @@ static int verbose = 0;
 
 static ventoy_guid vtoy_guid = VENTOY_GUID;
 
+static const char *g_ventoy_fs[ventoy_fs_max] = 
+{
+    "exfat", "ntfs", "ext*", "xfs", "udf"
+};
+
 static int vtoy_check_os_param(ventoy_os_param *param)
 {
     uint32_t i;
@@ -410,14 +426,10 @@ static int vtoy_print_os_param(ventoy_os_param *param, char *diskname)
         cnt = vtoy_find_disk_by_guid(param->vtoy_disk_guid, diskname);
         debug("find 0 disk by size, try with guid cnt=%d...\n", cnt);
     }
-    
-    if (param->vtoy_disk_part_type == 0)
-    {
-        fs = "exfat";
-    }
-    else if (param->vtoy_disk_part_type == 0)
+
+    if (param->vtoy_disk_part_type < ventoy_fs_max)
     {
-        fs = "ntfs";
+        fs = g_ventoy_fs[param->vtoy_disk_part_type];
     }
     else
     {
index d2a0c363746757a481bd54f1fff81cba6924c13f..1f9773b2de7826b88eed4397fc47fc4bea71c240 100644 (file)
Binary files a/VtoyTool/vtoytool/00/vtoytool_32 and b/VtoyTool/vtoytool/00/vtoytool_32 differ
index bb9d703f6808603f213465ee8b4d46efd22bf140..03cfc6ebc91547da90194c622160e315093759b6 100644 (file)
Binary files a/VtoyTool/vtoytool/00/vtoytool_64 and b/VtoyTool/vtoytool/00/vtoytool_64 differ