Commit 84c50066 authored by longpanda's avatar longpanda
Browse files

1.0.10 release

parent fe0dda69
......@@ -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;
}
......
/* 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;
/* 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;
}
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);
}
......@@ -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++;
......
/* 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);
}
......@@ -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);
}
......
/* 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;
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));
}
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;
}
/* 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;
}
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);
}
/*
* 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;
}
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;
}
......@@ -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 },
......
......@@ -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
......
......@@ -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);
}
/*
* 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 */
......@@ -28,6 +28,17 @@
#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
......
No preview for this file type
......@@ -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
......
......@@ -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.
......
......@@ -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)
......
No preview for this file type
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment