merge mainline into zfs

This commit is contained in:
Vladimir 'phcoder' Serbinenko 2011-10-26 19:26:24 +02:00
commit 11e50e923a
553 changed files with 42706 additions and 8566 deletions

View file

@ -26,6 +26,8 @@
#include <grub/types.h>
#include <grub/fshelp.h>
GRUB_MOD_LICENSE ("GPLv3+");
/* The affs bootblock. */
struct grub_affs_bblock
{
@ -50,12 +52,20 @@ struct grub_affs_rblock
grub_uint32_t hashtable[1];
} __attribute__ ((packed));
struct grub_affs_time
{
grub_int32_t day;
grub_uint32_t min;
grub_uint32_t hz;
} __attribute__ ((packed));
/* The second part of a file header block. */
struct grub_affs_file
{
grub_uint8_t unused1[12];
grub_uint32_t size;
grub_uint8_t unused2[104];
grub_uint8_t unused2[92];
struct grub_affs_time mtime;
grub_uint8_t namelen;
grub_uint8_t name[30];
grub_uint8_t unused3[33];
@ -85,9 +95,9 @@ struct grub_affs_file
struct grub_fshelp_node
{
struct grub_affs_data *data;
int block;
int size;
int parent;
grub_disk_addr_t block;
struct grub_fshelp_node *parent;
struct grub_affs_file di;
};
/* Information about a "mounted" affs filesystem. */
@ -115,7 +125,7 @@ grub_affs_read_block (grub_fshelp_node_t node, grub_disk_addr_t fileblock)
int block = node->block;
struct grub_affs_file file;
struct grub_affs_data *data = node->data;
grub_uint32_t mod;
grub_uint64_t mod;
/* Find the block that points to the fileblock we are looking up by
following the chain until the right table is reached. */
@ -150,11 +160,11 @@ static grub_ssize_t
grub_affs_read_file (grub_fshelp_node_t node,
void NESTED_FUNC_ATTR (*read_hook) (grub_disk_addr_t sector,
unsigned offset, unsigned length),
int pos, grub_size_t len, char *buf)
grub_off_t pos, grub_size_t len, char *buf)
{
return grub_fshelp_read_file (node->data->disk, node, read_hook,
pos, len, buf, grub_affs_read_block,
node->size, 0);
grub_be_to_cpu32 (node->di.size), 0);
}
@ -166,7 +176,6 @@ grub_affs_mount (grub_disk_t disk)
struct grub_affs_rblock *rblock;
int checksum = 0;
int checksumr = 0;
int blocksize = 0;
data = grub_malloc (sizeof (struct grub_affs_data));
@ -208,7 +217,7 @@ grub_affs_mount (grub_disk_t disk)
rblock = (struct grub_affs_rblock *) rootblock;
/* Read the rootblock. */
grub_disk_read (disk, (disk->total_sectors >> 1) + blocksize, 0,
grub_disk_read (disk, grub_be_to_cpu32 (data->bblock.rootblock), 0,
GRUB_DISK_SECTOR_SIZE * 16, rootblock);
if (grub_errno)
goto fail;
@ -216,8 +225,6 @@ grub_affs_mount (grub_disk_t disk)
/* The filesystem blocksize is not stored anywhere in the filesystem
itself. One way to determine it is reading blocks for the
rootblock until the checksum is correct. */
checksumr = grub_be_to_cpu32 (rblock->checksum);
rblock->checksum = 0;
for (blocksize = 0; blocksize < 8; blocksize++)
{
grub_uint32_t *currblock = rootblock + GRUB_DISK_SECTOR_SIZE * blocksize;
@ -226,10 +233,10 @@ grub_affs_mount (grub_disk_t disk)
for (i = 0; i < GRUB_DISK_SECTOR_SIZE / sizeof (*currblock); i++)
checksum += grub_be_to_cpu32 (currblock[i]);
if (checksumr == -checksum)
if (checksum == 0)
break;
}
if (-checksum != checksumr)
if (checksum != 0)
{
grub_error (GRUB_ERR_BAD_FS, "AFFS blocksize couldn't be determined");
goto fail;
@ -240,7 +247,9 @@ grub_affs_mount (grub_disk_t disk)
data->disk = disk;
data->htsize = grub_be_to_cpu32 (rblock->htsize);
data->diropen.data = data;
data->diropen.block = (disk->total_sectors >> 1);
data->diropen.block = grub_be_to_cpu32 (data->bblock.rootblock);
data->diropen.parent = NULL;
grub_memcpy (&data->diropen.di, rootblock, sizeof (data->diropen.di));
grub_free (rootblock);
@ -291,12 +300,15 @@ grub_affs_iterate_dir (grub_fshelp_node_t dir,
struct grub_affs_data *data = dir->data;
grub_uint32_t *hashtable;
auto int NESTED_FUNC_ATTR grub_affs_create_node (const char *name, int block,
int size, int type);
auto int NESTED_FUNC_ATTR grub_affs_create_node (const char *name,
grub_disk_addr_t block,
const struct grub_affs_file *fil);
int NESTED_FUNC_ATTR grub_affs_create_node (const char *name, int block,
int size, int type)
int NESTED_FUNC_ATTR grub_affs_create_node (const char *name,
grub_disk_addr_t block,
const struct grub_affs_file *fil)
{
int type;
node = grub_malloc (sizeof (*node));
if (!node)
{
@ -304,10 +316,19 @@ grub_affs_iterate_dir (grub_fshelp_node_t dir,
return 1;
}
if ((int) grub_be_to_cpu32 (fil->type) == GRUB_AFFS_FILETYPE_DIR)
type = GRUB_FSHELP_REG;
else if (grub_be_to_cpu32 (fil->type) == GRUB_AFFS_FILETYPE_REG)
type = GRUB_FSHELP_DIR;
else if (grub_be_to_cpu32 (fil->type) == GRUB_AFFS_FILETYPE_SYMLINK)
type = GRUB_FSHELP_SYMLINK;
else
type = GRUB_FSHELP_UNKNOWN;
node->data = data;
node->size = size;
node->block = block;
node->parent = grub_be_to_cpu32 (file.parent);
node->di = *fil;
node->parent = dir;
if (hook (name, type, node))
{
@ -317,6 +338,24 @@ grub_affs_iterate_dir (grub_fshelp_node_t dir,
return 0;
}
/* Create the directory entries for `.' and `..'. */
node = grub_malloc (sizeof (*node));
if (!node)
return 1;
*node = *dir;
if (hook (".", GRUB_FSHELP_DIR, node))
return 1;
if (dir->parent)
{
node = grub_malloc (sizeof (*node));
if (!node)
return 1;
*node = *dir->parent;
if (hook ("..", GRUB_FSHELP_DIR, node))
return 1;
}
hashtable = grub_malloc (data->htsize * sizeof (*hashtable));
if (!hashtable)
return 1;
@ -326,16 +365,8 @@ grub_affs_iterate_dir (grub_fshelp_node_t dir,
if (grub_errno)
goto fail;
/* Create the directory entries for `.' and `..'. */
if (grub_affs_create_node (".", dir->block, dir->size, GRUB_FSHELP_DIR))
return 1;
if (grub_affs_create_node ("..", dir->parent ? dir->parent : dir->block,
dir->size, GRUB_FSHELP_DIR))
return 1;
for (i = 0; i < data->htsize; i++)
{
enum grub_fshelp_filetype type;
grub_uint64_t next;
if (!hashtable[i])
@ -356,17 +387,7 @@ grub_affs_iterate_dir (grub_fshelp_node_t dir,
file.name[file.namelen] = '\0';
if ((int) grub_be_to_cpu32 (file.type) == GRUB_AFFS_FILETYPE_DIR)
type = GRUB_FSHELP_REG;
else if (grub_be_to_cpu32 (file.type) == GRUB_AFFS_FILETYPE_REG)
type = GRUB_FSHELP_DIR;
else if (grub_be_to_cpu32 (file.type) == GRUB_AFFS_FILETYPE_SYMLINK)
type = GRUB_FSHELP_SYMLINK;
else
type = GRUB_FSHELP_UNKNOWN;
if (grub_affs_create_node ((char *) (file.name), next,
grub_be_to_cpu32 (file.size), type))
if (grub_affs_create_node ((char *) (file.name), next, &file))
return 1;
next = grub_be_to_cpu32 (file.next);
@ -401,7 +422,7 @@ grub_affs_open (struct grub_file *file, const char *name)
if (grub_errno)
goto fail;
file->size = fdiro->size;
file->size = grub_be_to_cpu32 (fdiro->di.size);
data->diropen = *fdiro;
grub_free (fdiro);
@ -465,6 +486,11 @@ grub_affs_dir (grub_device_t device, const char *path,
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_be_to_cpu32 (node->di.mtime.day) * 86400
+ grub_be_to_cpu32 (node->di.mtime.min) * 60
+ grub_be_to_cpu32 (node->di.mtime.hz) / 50
+ 8 * 365 * 86400 + 86400 * 2;
grub_free (node);
return hook (filename, &info);
}
@ -507,7 +533,7 @@ grub_affs_label (grub_device_t device, char **label)
{
/* The rootblock maps quite well on a file header block, it's
something we can use here. */
grub_disk_read (data->disk, disk->total_sectors >> 1,
grub_disk_read (data->disk, grub_be_to_cpu32 (data->bblock.rootblock),
data->blocksize * (GRUB_DISK_SECTOR_SIZE
- GRUB_AFFS_FILE_LOCATION),
sizeof (file), &file);
@ -535,6 +561,9 @@ static struct grub_fs grub_affs_fs =
.read = grub_affs_read,
.close = grub_affs_close,
.label = grub_affs_label,
#ifdef GRUB_UTIL
.reserved_first_sector = 0,
#endif
.next = 0
};

View file

@ -26,6 +26,8 @@
#include <grub/types.h>
#include <grub/fshelp.h>
GRUB_MOD_LICENSE ("GPLv3+");
#ifdef MODE_BIGENDIAN
#define GRUB_AFS_FSNAME_SUFFIX "_be"
#else
@ -334,7 +336,7 @@ static grub_ssize_t
grub_afs_read_file (grub_fshelp_node_t node,
void NESTED_FUNC_ATTR (*read_hook) (grub_disk_addr_t sector,
unsigned offset, unsigned length),
int pos, grub_size_t len, char *buf)
grub_off_t pos, grub_size_t len, char *buf)
{
return grub_fshelp_read_file (node->data->disk, node, read_hook,
pos, len, buf, grub_afs_read_block,

File diff suppressed because it is too large Load diff

View file

@ -23,6 +23,8 @@
#include <grub/disk.h>
#include <grub/dl.h>
GRUB_MOD_LICENSE ("GPLv3+");
#ifndef MODE_USTAR
/* cpio support */
#define MAGIC_BCPIO 070707
@ -69,76 +71,102 @@ struct head
struct grub_cpio_data
{
grub_disk_t disk;
grub_uint32_t hofs;
grub_uint32_t dofs;
grub_uint32_t size;
grub_off_t hofs;
grub_off_t dofs;
grub_off_t size;
};
static grub_dl_t my_mod;
static inline void
canonicalize (char *name)
{
char *iptr, *optr;
for (iptr = name, optr = name; *iptr; )
{
while (*iptr == '/')
iptr++;
if (iptr[0] == '.' && (iptr[1] == '/' || iptr[1] == 0))
{
iptr += 2;
continue;
}
while (*iptr && *iptr != '/')
*optr++ = *iptr++;
if (*iptr)
*optr++ = *iptr++;
}
*optr = 0;
}
static grub_err_t
grub_cpio_find_file (struct grub_cpio_data *data, char **name,
grub_uint32_t * ofs)
grub_int32_t *mtime, grub_disk_addr_t * ofs)
{
#ifndef MODE_USTAR
struct head hd;
struct head hd;
if (grub_disk_read
(data->disk, 0, data->hofs, sizeof (hd), &hd))
return grub_errno;
if (grub_disk_read (data->disk, 0, data->hofs, sizeof (hd), &hd))
return grub_errno;
if (hd.magic != MAGIC_BCPIO)
return grub_error (GRUB_ERR_BAD_FS, "invalid cpio archive");
if (hd.magic != MAGIC_BCPIO)
return grub_error (GRUB_ERR_BAD_FS, "invalid cpio archive");
data->size = (((grub_uint32_t) hd.filesize_1) << 16) + hd.filesize_2;
data->size = (((grub_uint32_t) hd.filesize_1) << 16) + hd.filesize_2;
if (mtime)
*mtime = (((grub_uint32_t) hd.mtime_1) << 16) + hd.mtime_2;
if (hd.namesize & 1)
hd.namesize++;
if (hd.namesize & 1)
hd.namesize++;
if ((*name = grub_malloc (hd.namesize)) == NULL)
return grub_errno;
if ((*name = grub_malloc (hd.namesize)) == NULL)
return grub_errno;
if (grub_disk_read (data->disk, 0, data->hofs + sizeof (hd),
hd.namesize, *name))
{
grub_free (*name);
return grub_errno;
}
if (grub_disk_read (data->disk, 0, data->hofs + sizeof (hd),
hd.namesize, *name))
{
grub_free (*name);
return grub_errno;
}
if (data->size == 0 && hd.mode == 0 && hd.namesize == 11 + 1
&& ! grub_memcmp(*name, "TRAILER!!!", 11))
{
*ofs = 0;
return GRUB_ERR_NONE;
}
if (data->size == 0 && hd.mode == 0 && hd.namesize == 11 + 1
&& ! grub_memcmp(*name, "TRAILER!!!", 11))
{
*ofs = 0;
return GRUB_ERR_NONE;
}
data->dofs = data->hofs + sizeof (hd) + hd.namesize;
*ofs = data->dofs + data->size;
if (data->size & 1)
(*ofs)++;
canonicalize (*name);
data->dofs = data->hofs + sizeof (hd) + hd.namesize;
*ofs = data->dofs + data->size;
if (data->size & 1)
(*ofs)++;
#else
struct head hd;
struct head hd;
if (grub_disk_read
(data->disk, 0, data->hofs, sizeof (hd), &hd))
return grub_errno;
if (grub_disk_read (data->disk, 0, data->hofs, sizeof (hd), &hd))
return grub_errno;
if (!hd.name[0])
{
*ofs = 0;
return GRUB_ERR_NONE;
}
if (!hd.name[0])
{
*ofs = 0;
return GRUB_ERR_NONE;
}
if (grub_memcmp (hd.magic, MAGIC_USTAR, sizeof (MAGIC_USTAR) - 1))
return grub_error (GRUB_ERR_BAD_FS, "invalid tar archive");
if (grub_memcmp (hd.magic, MAGIC_USTAR, sizeof (MAGIC_USTAR) - 1))
return grub_error (GRUB_ERR_BAD_FS, "invalid tar archive");
if ((*name = grub_strdup (hd.name)) == NULL)
return grub_errno;
if ((*name = grub_strdup (hd.name)) == NULL)
return grub_errno;
data->size = grub_strtoul (hd.size, NULL, 8);
data->dofs = data->hofs + GRUB_DISK_SECTOR_SIZE;
*ofs = data->dofs + ((data->size + GRUB_DISK_SECTOR_SIZE - 1) &
~(GRUB_DISK_SECTOR_SIZE - 1));
data->size = grub_strtoull (hd.size, NULL, 8);
data->dofs = data->hofs + GRUB_DISK_SECTOR_SIZE;
*ofs = data->dofs + ((data->size + GRUB_DISK_SECTOR_SIZE - 1) &
~(GRUB_DISK_SECTOR_SIZE - 1));
if (mtime)
*mtime = grub_strtoul (hd.mtime, NULL, 8);
canonicalize (*name);
#endif
return GRUB_ERR_NONE;
}
@ -185,10 +213,10 @@ grub_cpio_dir (grub_device_t device, const char *path,
const struct grub_dirhook_info *info))
{
struct grub_cpio_data *data;
grub_uint32_t ofs;
grub_disk_addr_t ofs;
char *prev, *name;
const char *np;
int len;
grub_size_t len;
grub_dl_ref (my_mod);
@ -204,7 +232,9 @@ grub_cpio_dir (grub_device_t device, const char *path,
data->hofs = 0;
while (1)
{
if (grub_cpio_find_file (data, &name, &ofs))
grub_int32_t mtime;
if (grub_cpio_find_file (data, &name, &mtime, &ofs))
goto fail;
if (!ofs)
@ -222,15 +252,16 @@ grub_cpio_dir (grub_device_t device, const char *path,
if (p)
*p = 0;
if ((!prev) || (grub_strcmp (prev, name) != 0))
if (((!prev) || (grub_strcmp (prev, name) != 0)) && name[len] != 0)
{
struct grub_dirhook_info info;
grub_memset (&info, 0, sizeof (info));
info.dir = (p != NULL);
info.mtime = mtime;
info.mtimeset = 1;
hook (name + len, &info);
if (prev)
grub_free (prev);
grub_free (prev);
prev = name;
}
else
@ -241,11 +272,8 @@ grub_cpio_dir (grub_device_t device, const char *path,
fail:
if (prev)
grub_free (prev);
if (data)
grub_free (data);
grub_free (prev);
grub_free (data);
grub_dl_unref (my_mod);
@ -256,7 +284,7 @@ static grub_err_t
grub_cpio_open (grub_file_t file, const char *name)
{
struct grub_cpio_data *data;
grub_uint32_t ofs;
grub_disk_addr_t ofs;
char *fn;
int i, j;
@ -269,7 +297,7 @@ grub_cpio_open (grub_file_t file, const char *name)
data->hofs = 0;
while (1)
{
if (grub_cpio_find_file (data, &fn, &ofs))
if (grub_cpio_find_file (data, &fn, NULL, &ofs))
goto fail;
if (!ofs)
@ -316,8 +344,7 @@ grub_cpio_open (grub_file_t file, const char *name)
fail:
if (data)
grub_free (data);
grub_free (data);
grub_dl_unref (my_mod);
@ -354,6 +381,9 @@ static struct grub_fs grub_cpio_fs = {
.open = grub_cpio_open,
.read = grub_cpio_read,
.close = grub_cpio_close,
#ifdef GRUB_UTIL
.reserved_first_sector = 0,
#endif
};
#ifdef MODE_USTAR

2
grub-core/fs/exfat.c Normal file
View file

@ -0,0 +1,2 @@
#define MODE_EXFAT 1
#include "fat.c"

View file

@ -51,6 +51,8 @@
#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)
@ -555,7 +557,7 @@ grub_ext2_read_inode (struct grub_ext2_data *data,
/* Read the inode. */
if (grub_disk_read (data->disk,
((grub_le_to_cpu32 (blkgrp.inode_table_id) + blkno)
(((grub_disk_addr_t) grub_le_to_cpu32 (blkgrp.inode_table_id) + blkno)
<< LOG2_EXT2_BLOCK_SIZE (data)),
EXT2_INODE_SIZE (data) * blkoff,
sizeof (struct grub_ext2_inode), inode))

View file

@ -26,75 +26,121 @@
#include <grub/err.h>
#include <grub/dl.h>
#include <grub/charset.h>
#include <grub/fat.h>
#define GRUB_FAT_DIR_ENTRY_SIZE 32
GRUB_MOD_LICENSE ("GPLv3+");
#define GRUB_FAT_ATTR_READ_ONLY 0x01
#define GRUB_FAT_ATTR_HIDDEN 0x02
#define GRUB_FAT_ATTR_SYSTEM 0x04
#define GRUB_FAT_ATTR_VOLUME_ID 0x08
#define GRUB_FAT_ATTR_DIRECTORY 0x10
#define GRUB_FAT_ATTR_ARCHIVE 0x20
enum
{
GRUB_FAT_ATTR_READ_ONLY = 0x01,
GRUB_FAT_ATTR_HIDDEN = 0x02,
GRUB_FAT_ATTR_SYSTEM = 0x04,
#ifndef MODE_EXFAT
GRUB_FAT_ATTR_VOLUME_ID = 0x08,
#endif
GRUB_FAT_ATTR_DIRECTORY = 0x10,
GRUB_FAT_ATTR_ARCHIVE = 0x20,
#define GRUB_FAT_MAXFILE 256
#ifndef MODE_EXFAT
GRUB_FAT_ATTR_LONG_NAME = (GRUB_FAT_ATTR_READ_ONLY
| GRUB_FAT_ATTR_HIDDEN
| GRUB_FAT_ATTR_SYSTEM
| GRUB_FAT_ATTR_VOLUME_ID),
#endif
GRUB_FAT_ATTR_VALID = (GRUB_FAT_ATTR_READ_ONLY
| GRUB_FAT_ATTR_HIDDEN
| GRUB_FAT_ATTR_SYSTEM
| GRUB_FAT_ATTR_DIRECTORY
| GRUB_FAT_ATTR_ARCHIVE
#ifndef MODE_EXFAT
| GRUB_FAT_ATTR_VOLUME_ID
#endif
)
};
#define GRUB_FAT_ATTR_LONG_NAME (GRUB_FAT_ATTR_READ_ONLY \
| GRUB_FAT_ATTR_HIDDEN \
| GRUB_FAT_ATTR_SYSTEM \
| GRUB_FAT_ATTR_VOLUME_ID)
#define GRUB_FAT_ATTR_VALID (GRUB_FAT_ATTR_READ_ONLY \
| GRUB_FAT_ATTR_HIDDEN \
| GRUB_FAT_ATTR_SYSTEM \
| GRUB_FAT_ATTR_DIRECTORY \
| GRUB_FAT_ATTR_ARCHIVE \
| GRUB_FAT_ATTR_VOLUME_ID)
struct grub_fat_bpb
#ifdef MODE_EXFAT
struct grub_exfat_bpb
{
grub_uint8_t jmp_boot[3];
grub_uint8_t oem_name[8];
grub_uint16_t bytes_per_sector;
grub_uint8_t sectors_per_cluster;
grub_uint16_t num_reserved_sectors;
grub_uint8_t mbz[53];
grub_uint64_t num_hidden_sectors;
grub_uint64_t num_total_sectors;
grub_uint32_t num_reserved_sectors;
grub_uint32_t sectors_per_fat;
grub_uint32_t cluster_offset;
grub_uint32_t cluster_count;
grub_uint32_t root_cluster;
grub_uint32_t num_serial;
grub_uint16_t fs_revision;
grub_uint16_t volume_flags;
grub_uint8_t bytes_per_sector_shift;
grub_uint8_t sectors_per_cluster_shift;
grub_uint8_t num_fats;
grub_uint16_t num_root_entries;
grub_uint16_t num_total_sectors_16;
grub_uint8_t media;
grub_uint16_t sectors_per_fat_16;
grub_uint16_t sectors_per_track;
grub_uint16_t num_heads;
grub_uint32_t num_hidden_sectors;
grub_uint32_t num_total_sectors_32;
grub_uint8_t num_ph_drive;
grub_uint8_t reserved[8];
} __attribute__ ((packed));
typedef struct grub_exfat_bpb grub_current_fat_bpb_t;
#else
typedef struct grub_fat_bpb grub_current_fat_bpb_t;
#endif
#ifdef MODE_EXFAT
struct grub_fat_dir_entry
{
grub_uint8_t entry_type;
union
{
struct
{
grub_uint8_t num_ph_drive;
grub_uint8_t reserved;
grub_uint8_t boot_sig;
grub_uint32_t num_serial;
grub_uint8_t label[11];
grub_uint8_t fstype[8];
} __attribute__ ((packed)) fat12_or_fat16;
struct
{
grub_uint32_t sectors_per_fat_32;
grub_uint16_t extended_flags;
grub_uint16_t fs_version;
grub_uint32_t root_cluster;
grub_uint16_t fs_info;
grub_uint16_t backup_boot_sector;
grub_uint8_t reserved[12];
grub_uint8_t num_ph_drive;
grub_uint8_t placeholder[31];
struct {
grub_uint8_t secondary_count;
grub_uint16_t checksum;
grub_uint16_t attr;
grub_uint16_t reserved1;
grub_uint32_t c_time;
grub_uint32_t m_time;
grub_uint32_t a_time;
grub_uint8_t c_time_tenth;
grub_uint8_t m_time_tenth;
grub_uint8_t a_time_tenth;
grub_uint8_t reserved2[9];
} __attribute__ ((packed)) file;
struct {
grub_uint8_t flags;
grub_uint8_t reserved1;
grub_uint8_t boot_sig;
grub_uint32_t num_serial;
grub_uint8_t label[11];
grub_uint8_t fstype[8];
} __attribute__ ((packed)) fat32;
} __attribute__ ((packed)) version_specific;
grub_uint8_t name_length;
grub_uint16_t name_hash;
grub_uint16_t reserved2;
grub_uint64_t valid_size;
grub_uint32_t reserved3;
grub_uint32_t first_cluster;
grub_uint64_t file_size;
} __attribute__ ((packed)) stream_extension;
struct {
grub_uint8_t flags;
grub_uint16_t str[15];
} __attribute__ ((packed)) file_name;
struct {
grub_uint8_t character_count;
grub_uint16_t str[11];
grub_uint8_t reserved[8];
} __attribute__ ((packed)) volume_label;
} __attribute__ ((packed)) type_specific;
} __attribute__ ((packed));
struct grub_fat_dir_node
{
grub_uint32_t attr;
grub_uint32_t first_cluster;
grub_uint64_t file_size;
grub_uint64_t valid_size;
int have_stream;
int is_label;
};
typedef struct grub_fat_dir_node grub_fat_dir_node_t;
#else
struct grub_fat_dir_entry
{
grub_uint8_t name[11];
@ -123,6 +169,10 @@ struct grub_fat_long_name_entry
grub_uint16_t name3[2];
} __attribute__ ((packed));
typedef struct grub_fat_dir_entry grub_fat_dir_node_t;
#endif
struct grub_fat_data
{
int logical_sector_bits;
@ -133,8 +183,10 @@ struct grub_fat_data
int fat_size;
grub_uint32_t root_cluster;
#ifndef MODE_EXFAT
grub_uint32_t root_sector;
grub_uint32_t num_root_sectors;
#endif
int cluster_bits;
grub_uint32_t cluster_eof_mark;
@ -152,6 +204,7 @@ struct grub_fat_data
static grub_dl_t my_mod;
#ifndef MODE_EXFAT
static int
fat_log2 (unsigned x)
{
@ -168,11 +221,12 @@ fat_log2 (unsigned x)
return i;
}
#endif
static struct grub_fat_data *
grub_fat_mount (grub_disk_t disk)
{
struct grub_fat_bpb bpb;
grub_current_fat_bpb_t bpb;
struct grub_fat_data *data = 0;
grub_uint32_t first_fat, magic;
@ -187,20 +241,35 @@ grub_fat_mount (grub_disk_t disk)
if (grub_disk_read (disk, 0, 0, sizeof (bpb), &bpb))
goto fail;
#ifdef MODE_EXFAT
if (grub_memcmp ((const char *) bpb.oem_name, "EXFAT ",
sizeof (bpb.oem_name)) != 0)
goto fail;
#else
if (grub_strncmp((const char *) bpb.version_specific.fat12_or_fat16.fstype, "FAT12", 5)
&& grub_strncmp((const char *) bpb.version_specific.fat12_or_fat16.fstype, "FAT16", 5)
&& grub_strncmp((const char *) bpb.version_specific.fat32.fstype, "FAT32", 5))
goto fail;
#endif
/* Get the sizes of logical sectors and clusters. */
#ifdef MODE_EXFAT
data->logical_sector_bits = bpb.bytes_per_sector_shift;
#else
data->logical_sector_bits =
fat_log2 (grub_le_to_cpu16 (bpb.bytes_per_sector));
if (data->logical_sector_bits < GRUB_DISK_SECTOR_BITS)
#endif
if (data->logical_sector_bits < GRUB_DISK_SECTOR_BITS
|| data->logical_sector_bits >= 16)
goto fail;
data->logical_sector_bits -= GRUB_DISK_SECTOR_BITS;
#ifdef MODE_EXFAT
data->cluster_bits = bpb.sectors_per_cluster_shift;
#else
data->cluster_bits = fat_log2 (bpb.sectors_per_cluster);
if (data->cluster_bits < 0)
#endif
if (data->cluster_bits < 0 || data->cluster_bits > 25)
goto fail;
data->cluster_bits += data->logical_sector_bits;
@ -210,18 +279,28 @@ grub_fat_mount (grub_disk_t disk)
if (data->fat_sector == 0)
goto fail;
#ifdef MODE_EXFAT
data->sectors_per_fat = (grub_le_to_cpu32 (bpb.sectors_per_fat)
<< data->logical_sector_bits);
#else
data->sectors_per_fat = ((bpb.sectors_per_fat_16
? grub_le_to_cpu16 (bpb.sectors_per_fat_16)
: grub_le_to_cpu32 (bpb.version_specific.fat32.sectors_per_fat_32))
<< data->logical_sector_bits);
#endif
if (data->sectors_per_fat == 0)
goto fail;
/* Get the number of sectors in this volume. */
#ifdef MODE_EXFAT
data->num_sectors = ((grub_le_to_cpu64 (bpb.num_total_sectors))
<< data->logical_sector_bits);
#else
data->num_sectors = ((bpb.num_total_sectors_16
? grub_le_to_cpu16 (bpb.num_total_sectors_16)
: grub_le_to_cpu32 (bpb.num_total_sectors_32))
<< data->logical_sector_bits);
#endif
if (data->num_sectors == 0)
goto fail;
@ -229,22 +308,44 @@ grub_fat_mount (grub_disk_t disk)
if (bpb.num_fats == 0)
goto fail;
#ifndef MODE_EXFAT
data->root_sector = data->fat_sector + bpb.num_fats * data->sectors_per_fat;
data->num_root_sectors
= ((((grub_uint32_t) grub_le_to_cpu16 (bpb.num_root_entries)
* GRUB_FAT_DIR_ENTRY_SIZE
* sizeof (struct grub_fat_dir_entry)
+ grub_le_to_cpu16 (bpb.bytes_per_sector) - 1)
>> (data->logical_sector_bits + GRUB_DISK_SECTOR_BITS))
<< (data->logical_sector_bits));
#endif
#ifdef MODE_EXFAT
data->cluster_sector = (grub_le_to_cpu32 (bpb.cluster_offset)
<< data->logical_sector_bits);
data->num_clusters = (grub_le_to_cpu32 (bpb.cluster_count)
<< data->logical_sector_bits);
#else
data->cluster_sector = data->root_sector + data->num_root_sectors;
data->num_clusters = (((data->num_sectors - data->cluster_sector)
>> (data->cluster_bits + data->logical_sector_bits))
+ 2);
#endif
if (data->num_clusters <= 2)
goto fail;
#ifdef MODE_EXFAT
{
/* exFAT. */
grub_uint16_t flags = grub_le_to_cpu16 (bpb.volume_flags);
data->root_cluster = grub_le_to_cpu32 (bpb.root_cluster);
data->fat_size = 32;
data->cluster_eof_mark = 0xffffffff;
if ((flags & 0x1) && bpb.num_fats > 1)
data->fat_sector += data->sectors_per_fat;
}
#else
if (! bpb.sectors_per_fat_16)
{
/* FAT32. */
@ -286,6 +387,7 @@ grub_fat_mount (grub_disk_t disk)
data->cluster_eof_mark = 0xfff8;
}
}
#endif
/* More sanity checks. */
if (data->num_sectors <= data->fat_sector)
@ -317,17 +419,25 @@ grub_fat_mount (grub_disk_t disk)
}
/* Serial number. */
#ifdef MODE_EXFAT
data->uuid = grub_le_to_cpu32 (bpb.num_serial);
#else
if (bpb.sectors_per_fat_16)
data->uuid = grub_le_to_cpu32 (bpb.version_specific.fat12_or_fat16.num_serial);
else
data->uuid = grub_le_to_cpu32 (bpb.version_specific.fat32.num_serial);
#endif
#ifndef MODE_EXFAT
/* Ignore the 3rd bit, because some BIOSes assigns 0xF0 to the media
descriptor, even if it is a so-called superfloppy (e.g. an USB key).
The check may be too strict for this kind of stupid BIOSes, as
they overwrite the media descriptor. */
if ((first_fat | 0x8) != (magic | bpb.media | 0x8))
goto fail;
#else
(void) magic;
#endif
/* Start from the root directory. */
data->file_cluster = data->root_cluster;
@ -354,6 +464,7 @@ grub_fat_read_data (grub_disk_t disk, struct grub_fat_data *data,
grub_ssize_t ret = 0;
unsigned long sector;
#ifndef MODE_EXFAT
/* This is a special case. FAT12 and FAT16 doesn't have the root directory
in clusters. */
if (data->file_cluster == ~0U)
@ -367,13 +478,14 @@ grub_fat_read_data (grub_disk_t disk, struct grub_fat_data *data,
return size;
}
#endif
/* Calculate the logical cluster number and offset. */
logical_cluster_bits = (data->cluster_bits
+ data->logical_sector_bits
+ GRUB_DISK_SECTOR_BITS);
logical_cluster = offset >> logical_cluster_bits;
offset &= (1 << logical_cluster_bits) - 1;
offset &= (1ULL << logical_cluster_bits) - 1;
if (logical_cluster < data->cur_cluster_num)
{
@ -465,13 +577,116 @@ grub_fat_read_data (grub_disk_t disk, struct grub_fat_data *data,
return ret;
}
#ifdef MODE_EXFAT
static grub_err_t
grub_fat_iterate_dir (grub_disk_t disk, struct grub_fat_data *data,
int (*hook) (const char *filename,
grub_fat_dir_node_t *node))
{
struct grub_fat_dir_entry dir;
grub_ssize_t offset = -sizeof(dir);
grub_uint16_t *unibuf;
char *filename;
unibuf = grub_malloc (15 * 256 * 2);
filename = grub_malloc (15 * 256 * 4 + 1);
while (1)
{
offset += sizeof (dir);
if (grub_fat_read_data (disk, data, 0,
offset, sizeof (dir), (char *) &dir)
!= sizeof (dir))
break;
if (dir.entry_type == 0)
break;
if (!(dir.entry_type & 0x80))
continue;
if (dir.entry_type == 0x85)
{
unsigned i, nsec, slots = 0;
grub_fat_dir_node_t node;
nsec = dir.type_specific.file.secondary_count;
node.attr = grub_cpu_to_le32 (dir.type_specific.file.attr);
node.have_stream = 0;
for (i = 0; i < nsec; i++)
{
struct grub_fat_dir_entry sec;
offset += sizeof (sec);
if (grub_fat_read_data (disk, data, 0,
offset, sizeof (sec), (char *) &sec)
!= sizeof (sec))
break;
if (!(sec.entry_type & 0x80))
continue;
if (!(sec.entry_type & 0x40))
break;
switch (sec.entry_type)
{
case 0xc0:
node.first_cluster = grub_cpu_to_le32 (sec.type_specific.stream_extension.first_cluster);
node.valid_size
= grub_cpu_to_le64 (sec.type_specific.stream_extension.valid_size);
node.file_size
= grub_cpu_to_le64 (sec.type_specific.stream_extension.file_size);
node.have_stream = 1;
break;
case 0xc1:
grub_memcpy (unibuf + slots * 15,
sec.type_specific.file_name.str, 30);
slots++;
break;
default:
grub_dprintf ("exfat", "unknown secondary type 0x%02x\n",
sec.entry_type);
}
}
if (i != nsec)
{
offset -= sizeof (dir);
continue;
}
*grub_utf16_to_utf8 ((grub_uint8_t *) filename, unibuf,
slots * 15) = '\0';
if (hook (filename, &node))
break;
continue;
}
/* Allocation bitmap. */
if (dir.entry_type == 0x81)
continue;
/* Upcase table. */
if (dir.entry_type == 0x82)
continue;
/* Volume label. */
if (dir.entry_type == 0x83)
continue;
grub_dprintf ("exfat", "unknown primary type 0x%02x\n", dir.entry_type);
}
grub_free (filename);
grub_free (unibuf);
return grub_errno;
}
#else
static grub_err_t
grub_fat_iterate_dir (grub_disk_t disk, struct grub_fat_data *data,
int (*hook) (const char *filename,
struct grub_fat_dir_entry *dir))
{
struct grub_fat_dir_entry dir;
char *filename, *filep = 0;
char *filename;
char *filep = 0;
grub_uint16_t *unibuf;
int slot = -1, slots = -1;
int checksum = -1;
@ -498,10 +713,11 @@ grub_fat_iterate_dir (grub_disk_t disk, struct grub_fat_data *data,
offset += sizeof (dir);
/* Read a directory entry. */
if ((grub_fat_read_data (disk, data, 0,
if (grub_fat_read_data (disk, data, 0,
offset, sizeof (dir), (char *) &dir)
!= sizeof (dir) || dir.name[0] == 0))
!= sizeof (dir) || dir.name[0] == 0)
break;
/* Handle long name entries. */
if (dir.attr == GRUB_FAT_ATTR_LONG_NAME)
{
@ -586,7 +802,6 @@ grub_fat_iterate_dir (grub_disk_t disk, struct grub_fat_data *data,
filep++;
}
*filep = '\0';
if (hook (filename, &dir))
break;
}
@ -596,7 +811,7 @@ grub_fat_iterate_dir (grub_disk_t disk, struct grub_fat_data *data,
return grub_errno;
}
#endif
/* Find the underlying directory or file in PATH and return the
next path. If there is no next path or an error occurs, return NULL.
@ -608,11 +823,12 @@ grub_fat_find_dir (grub_disk_t disk, struct grub_fat_data *data,
const struct grub_dirhook_info *info))
{
char *dirname, *dirp;
char *origpath = NULL;
int call_hook;
int found = 0;
auto int iter_hook (const char *filename, struct grub_fat_dir_entry *dir);
int iter_hook (const char *filename, struct grub_fat_dir_entry *dir)
auto int iter_hook (const char *filename, grub_fat_dir_node_t *dir);
int iter_hook (const char *filename, grub_fat_dir_node_t *dir)
{
struct grub_dirhook_info info;
grub_memset (&info, 0, sizeof (info));
@ -620,8 +836,13 @@ grub_fat_find_dir (grub_disk_t disk, struct grub_fat_data *data,
info.dir = !! (dir->attr & GRUB_FAT_ATTR_DIRECTORY);
info.case_insensitive = 1;
#ifdef MODE_EXFAT
if (!dir->have_stream)
return 0;
#else
if (dir->attr & GRUB_FAT_ATTR_VOLUME_ID)
return 0;
#endif
if (*dirname == '\0' && call_hook)
return hook (filename, &info);
@ -629,9 +850,14 @@ grub_fat_find_dir (grub_disk_t disk, struct grub_fat_data *data,
{
found = 1;
data->attr = dir->attr;
#ifdef MODE_EXFAT
data->file_size = dir->file_size;
data->file_cluster = dir->first_cluster;
#else
data->file_size = grub_le_to_cpu32 (dir->file_size);
data->file_cluster = ((grub_le_to_cpu16 (dir->first_cluster_high) << 16)
| grub_le_to_cpu16 (dir->first_cluster_low));
#endif
data->cur_cluster_num = ~0U;
if (call_hook)
@ -648,6 +874,10 @@ grub_fat_find_dir (grub_disk_t disk, struct grub_fat_data *data,
return 0;
}
origpath = grub_strdup (path);
if (!origpath)
return 0;
/* Extract a directory name. */
while (*path == '/')
path++;
@ -659,7 +889,7 @@ grub_fat_find_dir (grub_disk_t disk, struct grub_fat_data *data,
dirname = grub_malloc (len + 1);
if (! dirname)
return 0;
goto fail;
grub_memcpy (dirname, path, len);
dirname[len] = '\0';
@ -672,9 +902,11 @@ grub_fat_find_dir (grub_disk_t disk, struct grub_fat_data *data,
grub_fat_iterate_dir (disk, data, iter_hook);
if (grub_errno == GRUB_ERR_NONE && ! found && !call_hook)
grub_error (GRUB_ERR_FILE_NOT_FOUND, "file not found");
grub_error (GRUB_ERR_FILE_NOT_FOUND, "file `%s' not found", origpath);
fail:
grub_free (dirname);
grub_free (origpath);
return found ? dirp : 0;
}
@ -781,14 +1013,63 @@ grub_fat_close (grub_file_t file)
return grub_errno;
}
#ifdef MODE_EXFAT
static grub_err_t
grub_fat_label (grub_device_t device, char **label)
{
struct grub_fat_dir_entry dir;
grub_ssize_t offset = -sizeof(dir);
struct grub_fat_data *data;
grub_disk_t disk = device->disk;
data = grub_fat_mount (disk);
if (! data)
return grub_errno;
*label = NULL;
while (1)
{
offset += sizeof (dir);
if (grub_fat_read_data (disk, data, 0,
offset, sizeof (dir), (char *) &dir)
!= sizeof (dir))
break;
if (dir.entry_type == 0)
break;
if (!(dir.entry_type & 0x80))
continue;
/* Volume label. */
if (dir.entry_type == 0x83)
{
grub_size_t chc;
*label = grub_malloc (11 * 4 + 1);
if (!*label)
return grub_errno;
chc = dir.type_specific.volume_label.character_count;
if (chc > ARRAY_SIZE (dir.type_specific.volume_label.str))
chc = ARRAY_SIZE (dir.type_specific.volume_label.str);
*grub_utf16_to_utf8 ((grub_uint8_t *) *label,
dir.type_specific.volume_label.str, chc) = '\0';
}
}
return grub_errno;
}
#else
static grub_err_t
grub_fat_label (grub_device_t device, char **label)
{
struct grub_fat_data *data;
grub_disk_t disk = device->disk;
auto int iter_hook (const char *filename, struct grub_fat_dir_entry *dir);
int iter_hook (const char *filename, struct grub_fat_dir_entry *dir)
auto int iter_hook (const char *filename, grub_fat_dir_node_t *dir);
int iter_hook (const char *filename, grub_fat_dir_node_t *dir)
{
if (dir->attr == GRUB_FAT_ATTR_VOLUME_ID)
{
@ -823,6 +1104,8 @@ grub_fat_label (grub_device_t device, char **label)
return grub_errno;
}
#endif
static grub_err_t
grub_fat_uuid (grub_device_t device, char **uuid)
{
@ -834,9 +1117,12 @@ grub_fat_uuid (grub_device_t device, char **uuid)
data = grub_fat_mount (disk);
if (data)
{
char *ptr;
*uuid = grub_xasprintf ("%04x-%04x",
(grub_uint16_t) (data->uuid >> 16),
(grub_uint16_t) data->uuid);
for (ptr = *uuid; ptr && *ptr; ptr++)
*ptr = grub_toupper (*ptr);
}
else
*uuid = NULL;
@ -850,7 +1136,11 @@ grub_fat_uuid (grub_device_t device, char **uuid)
static struct grub_fs grub_fat_fs =
{
#ifdef MODE_EXFAT
.name = "exfat",
#else
.name = "fat",
#endif
.dir = grub_fat_dir,
.open = grub_fat_open,
.read = grub_fat_read,
@ -858,18 +1148,31 @@ static struct grub_fs grub_fat_fs =
.label = grub_fat_label,
.uuid = grub_fat_uuid,
#ifdef GRUB_UTIL
#ifdef MODE_EXFAT
/* ExFAT BPB is 30 larger than FAT32 one. */
.reserved_first_sector = 0,
#else
.reserved_first_sector = 1,
#endif
#endif
.next = 0
};
#ifdef MODE_EXFAT
GRUB_MOD_INIT(exfat)
#else
GRUB_MOD_INIT(fat)
#endif
{
COMPILE_TIME_ASSERT (sizeof (struct grub_fat_dir_entry) == 32);
grub_fs_register (&grub_fat_fs);
my_mod = mod;
}
#ifdef MODE_EXFAT
GRUB_MOD_FINI(exfat)
#else
GRUB_MOD_FINI(fat)
#endif
{
grub_fs_unregister (&grub_fat_fs);
}

View file

@ -22,7 +22,9 @@
#include <grub/misc.h>
#include <grub/disk.h>
#include <grub/fshelp.h>
#include <grub/dl.h>
GRUB_MOD_LICENSE ("GPLv3+");
/* Lookup the node PATH. The node ROOTNODE describes the root of the
directory tree. The node found is returned in FOUNDNODE, which is
@ -59,7 +61,6 @@ grub_fshelp_find_file (const char *path, grub_fshelp_node_t rootnode,
char fpath[grub_strlen (currpath) + 1];
char *name = fpath;
char *next;
// unsigned int pos = 0;
enum grub_fshelp_filetype type = GRUB_FSHELP_DIR;
grub_fshelp_node_t currnode = currroot;
grub_fshelp_node_t oldnode = currroot;
@ -195,7 +196,7 @@ grub_fshelp_find_file (const char *path, grub_fshelp_node_t rootnode,
name = next;
}
return grub_error (GRUB_ERR_FILE_NOT_FOUND, "file not found");
return grub_error (GRUB_ERR_FILE_NOT_FOUND, "file `%s' not found", path);
}
if (!path || path[0] != '/')

View file

@ -29,6 +29,8 @@
#include <grub/types.h>
#include <grub/hfs.h>
GRUB_MOD_LICENSE ("GPLv3+");
#define GRUB_HFS_SBLOCK 2
#define GRUB_HFS_EMBED_HFSPLUS_SIG 0x482B
@ -131,6 +133,8 @@ struct grub_hfs_dirrec
grub_uint8_t type;
grub_uint8_t unused[5];
grub_uint32_t dirid;
grub_uint32_t ctime;
grub_uint32_t mtime;
} __attribute__ ((packed));
/* Information about a file. */
@ -142,7 +146,9 @@ struct grub_hfs_filerec
grub_uint32_t fileid;
grub_uint8_t unused2[2];
grub_uint32_t size;
grub_uint8_t unused3[44];
grub_uint8_t unused3[18];
grub_uint32_t mtime;
grub_uint8_t unused4[22];
/* The first 3 extents of the file. The other extents can be found
in the extent overflow file. */
@ -238,22 +244,24 @@ static grub_ssize_t
grub_hfs_read_file (struct grub_hfs_data *data,
void NESTED_FUNC_ATTR (*read_hook) (grub_disk_addr_t sector,
unsigned offset, unsigned length),
int pos, grub_size_t len, char *buf)
grub_off_t pos, grub_size_t len, char *buf)
{
int i;
int blockcnt;
grub_off_t i;
grub_off_t blockcnt;
blockcnt = ((len + pos)
+ data->blksz - 1) / data->blksz;
blockcnt = grub_divmod64 (((len + pos)
+ data->blksz - 1), data->blksz, 0);
for (i = pos / data->blksz; i < blockcnt; i++)
for (i = grub_divmod64 (pos, data->blksz, 0); i < blockcnt; i++)
{
int blknr;
int blockoff = pos % data->blksz;
int blockend = data->blksz;
grub_disk_addr_t blknr;
grub_off_t blockoff;
grub_off_t blockend = data->blksz;
int skipfirst = 0;
grub_divmod64 (pos, data->blksz, &blockoff);
blknr = grub_hfs_block (data, data->extents, data->fileid, i, 1);
if (grub_errno)
return -1;
@ -261,7 +269,7 @@ grub_hfs_read_file (struct grub_hfs_data *data,
/* Last block. */
if (i == blockcnt - 1)
{
blockend = (len + pos) % data->blksz;
grub_divmod64 ((len + pos), data->blksz, &blockend);
/* The last portion is exactly EXT2_BLOCK_SIZE (data). */
if (! blockend)
@ -269,7 +277,7 @@ grub_hfs_read_file (struct grub_hfs_data *data,
}
/* First block. */
if (i == pos / data->blksz)
if (i == grub_divmod64 (pos, data->blksz, 0))
{
skipfirst = blockoff;
blockend -= skipfirst;
@ -915,7 +923,7 @@ grub_hfs_find_dir (struct grub_hfs_data *data, const char *path,
if (! grub_hfs_find_node (data, (char *) &key, data->cat_root,
0, (char *) &fdrec.frec, sizeof (fdrec.frec)))
{
grub_error (GRUB_ERR_FILE_NOT_FOUND, "file not found");
grub_error (GRUB_ERR_FILE_NOT_FOUND, "file `%s' not found", origpath);
goto fail;
}
@ -951,19 +959,29 @@ grub_hfs_dir (grub_device_t device, const char *path,
int dir_hook (struct grub_hfs_record *rec)
{
char fname[32] = { 0 };
char *filetype = rec->data;
struct grub_hfs_dirrec *drec = rec->data;
struct grub_hfs_filerec *frec = rec->data;
struct grub_hfs_catalog_key *ckey = rec->key;
struct grub_dirhook_info info;
grub_memset (&info, 0, sizeof (info));
grub_strncpy (fname, (char *) (ckey->str), ckey->strlen);
if (*filetype == GRUB_HFS_FILETYPE_DIR
|| *filetype == GRUB_HFS_FILETYPE_FILE)
if (drec->type == GRUB_HFS_FILETYPE_DIR)
{
info.dir = (*filetype == GRUB_HFS_FILETYPE_DIR);
info.dir = 1;
info.mtimeset = 1;
info.mtime = grub_be_to_cpu32 (drec->mtime) - 2082844800;
return hook (fname, &info);
}
if (frec->type == GRUB_HFS_FILETYPE_FILE)
{
info.dir = 0;
info.mtimeset = 1;
info.mtime = grub_be_to_cpu32 (frec->mtime) - 2082844800;
return hook (fname, &info);
}
return 0;
}
@ -1072,6 +1090,22 @@ grub_hfs_label (grub_device_t device, char **label)
return grub_errno;
}
static grub_err_t
grub_hfs_mtime (grub_device_t device, grub_int32_t *tm)
{
struct grub_hfs_data *data;
data = grub_hfs_mount (device->disk);
if (data)
*tm = grub_be_to_cpu32 (data->sblock.mtime) - 2082844800;
else
*tm = 0;
grub_free (data);
return grub_errno;
}
static grub_err_t
grub_hfs_uuid (grub_device_t device, char **uuid)
{
@ -1107,6 +1141,7 @@ static struct grub_fs grub_hfs_fs =
.close = grub_hfs_close,
.label = grub_hfs_label,
.uuid = grub_hfs_uuid,
.mtime = grub_hfs_mtime,
.next = 0
};

View file

@ -30,6 +30,8 @@
#include <grub/hfs.h>
#include <grub/charset.h>
GRUB_MOD_LICENSE ("GPLv3+");
#define GRUB_HFSPLUS_MAGIC 0x482B
#define GRUB_HFSPLUSX_MAGIC 0x4858
#define GRUB_HFSPLUS_SBLOCK 2
@ -178,7 +180,7 @@ enum grub_hfsplus_filetype
/* Internal representation of a catalog key. */
struct grub_hfsplus_catkey_internal
{
int parent;
grub_uint32_t parent;
char *name;
};
@ -212,7 +214,7 @@ struct grub_fshelp_node
struct grub_hfsplus_btree
{
grub_uint32_t root;
int nodesize;
grub_size_t nodesize;
/* Catalog file node. */
struct grub_fshelp_node file;
@ -234,7 +236,7 @@ struct grub_hfsplus_data
/* This is the offset into the physical disk for an embedded HFS+
filesystem (one inside a plain HFS wrapper). */
int embedded_offset;
grub_disk_addr_t embedded_offset;
int case_sensitive;
};
@ -243,7 +245,7 @@ static grub_dl_t my_mod;
/* Return the offset of the record with the index INDEX, in the node
NODE which is part of the B+ tree BTREE. */
static inline unsigned int
static inline grub_off_t
grub_hfsplus_btree_recoffset (struct grub_hfsplus_btree *btree,
struct grub_hfsplus_btnode *node, int index)
{
@ -261,7 +263,7 @@ grub_hfsplus_btree_recptr (struct grub_hfsplus_btree *btree,
struct grub_hfsplus_btnode *node, int index)
{
char *cnode = (char *) node;
unsigned int offset;
grub_off_t offset;
offset = grub_hfsplus_btree_recoffset (btree, node, index);
return (struct grub_hfsplus_key *) &cnode[offset];
}
@ -270,12 +272,12 @@ grub_hfsplus_btree_recptr (struct grub_hfsplus_btree *btree,
/* Find the extent that points to FILEBLOCK. If it is not in one of
the 8 extents described by EXTENT, return -1. In that case set
FILEBLOCK to the next block. */
static int
static grub_disk_addr_t
grub_hfsplus_find_block (struct grub_hfsplus_extent *extent,
int *fileblock)
grub_disk_addr_t *fileblock)
{
int i;
grub_size_t blksleft = *fileblock;
grub_disk_addr_t blksleft = *fileblock;
/* First lookup the file in the given extents. */
for (i = 0; i < 8; i++)
@ -286,7 +288,7 @@ grub_hfsplus_find_block (struct grub_hfsplus_extent *extent,
}
*fileblock = blksleft;
return -1;
return 0xffffffffffffffffULL;
}
static grub_err_t
@ -294,7 +296,8 @@ grub_hfsplus_btree_search (struct grub_hfsplus_btree *btree,
struct grub_hfsplus_key_internal *key,
int (*compare_keys) (struct grub_hfsplus_key *keya,
struct grub_hfsplus_key_internal *keyb),
struct grub_hfsplus_btnode **matchnode, int *keyoffset);
struct grub_hfsplus_btnode **matchnode,
grub_off_t *keyoffset);
static int grub_hfsplus_cmp_extkey (struct grub_hfsplus_key *keya,
struct grub_hfsplus_key_internal *keyb);
@ -305,15 +308,15 @@ static grub_disk_addr_t
grub_hfsplus_read_block (grub_fshelp_node_t node, grub_disk_addr_t fileblock)
{
struct grub_hfsplus_btnode *nnode = 0;
int blksleft = fileblock;
grub_disk_addr_t blksleft = fileblock;
struct grub_hfsplus_extent *extents = &node->extents[0];
while (1)
{
struct grub_hfsplus_extkey *key;
struct grub_hfsplus_extkey_internal extoverflow;
int blk;
int ptr;
grub_disk_addr_t blk;
grub_off_t ptr;
/* Try to find this block in the current set of extents. */
blk = grub_hfsplus_find_block (extents, &blksleft);
@ -323,7 +326,7 @@ grub_hfsplus_read_block (grub_fshelp_node_t node, grub_disk_addr_t fileblock)
grub_free (nnode);
nnode = 0;
if (blk != -1)
if (blk != 0xffffffffffffffffULL)
return (blk
+ (node->data->embedded_offset >> (node->data->log2blksize
- GRUB_DISK_SECTOR_BITS)));
@ -374,7 +377,7 @@ static grub_ssize_t
grub_hfsplus_read_file (grub_fshelp_node_t node,
void NESTED_FUNC_ATTR (*read_hook) (grub_disk_addr_t sector,
unsigned offset, unsigned length),
int pos, grub_size_t len, char *buf)
grub_off_t pos, grub_size_t len, char *buf)
{
return grub_fshelp_read_file (node->data->disk, node, read_hook,
pos, len, buf, grub_hfsplus_read_block,
@ -409,9 +412,9 @@ grub_hfsplus_mount (grub_disk_t disk)
data->embedded_offset = 0;
if (grub_be_to_cpu16 (volheader.hfs.magic) == GRUB_HFS_MAGIC)
{
int extent_start;
int ablk_size;
int ablk_start;
grub_disk_addr_t extent_start;
grub_disk_addr_t ablk_size;
grub_disk_addr_t ablk_start;
/* See if there's an embedded HFS+ filesystem. */
if (grub_be_to_cpu16 (volheader.hfs.embed_sig) != GRUB_HFSPLUS_MAGIC)
@ -520,9 +523,12 @@ grub_hfsplus_cmp_catkey (struct grub_hfsplus_key *keya,
int i;
int diff;
diff = grub_be_to_cpu32 (catkey_a->parent) - catkey_b->parent;
if (diff)
return diff;
/* Safe unsigned comparison */
grub_uint32_t aparent = grub_be_to_cpu32 (catkey_a->parent);
if (aparent > catkey_b->parent)
return 1;
if (aparent < catkey_b->parent)
return -1;
/* Change the filename in keya so the endianness is correct. */
for (i = 0; i < grub_be_to_cpu16 (catkey_a->namelen); i++)
@ -547,6 +553,25 @@ grub_hfsplus_cmp_catkey (struct grub_hfsplus_key *keya,
return diff;
}
/* Compare the on disk catalog key KEYA with the catalog key we are
looking for (KEYB). */
static int
grub_hfsplus_cmp_catkey_id (struct grub_hfsplus_key *keya,
struct grub_hfsplus_key_internal *keyb)
{
struct grub_hfsplus_catkey *catkey_a = &keya->catkey;
struct grub_hfsplus_catkey_internal *catkey_b = &keyb->catkey;
/* Safe unsigned comparison */
grub_uint32_t aparent = grub_be_to_cpu32 (catkey_a->parent);
if (aparent > catkey_b->parent)
return 1;
if (aparent < catkey_b->parent)
return -1;
return 0;
}
/* Compare the on disk extent overflow key KEYA with the extent
overflow key we are looking for (KEYB). */
static int
@ -555,15 +580,21 @@ grub_hfsplus_cmp_extkey (struct grub_hfsplus_key *keya,
{
struct grub_hfsplus_extkey *extkey_a = &keya->extkey;
struct grub_hfsplus_extkey_internal *extkey_b = &keyb->extkey;
int diff;
grub_uint32_t akey;
diff = grub_be_to_cpu32 (extkey_a->fileid) - extkey_b->fileid;
if (diff)
return diff;
diff = grub_be_to_cpu32 (extkey_a->start) - extkey_b->start;
return diff;
/* Safe unsigned comparison */
akey = grub_be_to_cpu32 (extkey_a->fileid);
if (akey > extkey_b->fileid)
return 1;
if (akey < extkey_b->fileid)
return -1;
akey = grub_be_to_cpu32 (extkey_a->start);
if (akey > extkey_b->start)
return 1;
if (akey < extkey_b->start)
return -1;
return 0;
}
static char *
@ -590,10 +621,10 @@ grub_hfsplus_read_symlink (grub_fshelp_node_t node)
static int
grub_hfsplus_btree_iterate_node (struct grub_hfsplus_btree *btree,
struct grub_hfsplus_btnode *first_node,
int first_rec,
grub_disk_addr_t first_rec,
int (*hook) (void *record))
{
int rec;
grub_disk_addr_t rec;
for (;;)
{
@ -631,12 +662,13 @@ grub_hfsplus_btree_search (struct grub_hfsplus_btree *btree,
struct grub_hfsplus_key_internal *key,
int (*compare_keys) (struct grub_hfsplus_key *keya,
struct grub_hfsplus_key_internal *keyb),
struct grub_hfsplus_btnode **matchnode, int *keyoffset)
struct grub_hfsplus_btnode **matchnode,
grub_off_t *keyoffset)
{
grub_uint64_t currnode;
char *node;
struct grub_hfsplus_btnode *nodedesc;
int rec;
grub_disk_addr_t rec;
node = grub_malloc (btree->nodesize);
if (! node)
@ -649,7 +681,8 @@ grub_hfsplus_btree_search (struct grub_hfsplus_btree *btree,
/* Read a node. */
if (grub_hfsplus_read_file (&btree->file, 0,
(long)currnode * (long)btree->nodesize,
(grub_disk_addr_t) currnode
* (grub_disk_addr_t) btree->nodesize,
btree->nodesize, (char *) node) <= 0)
{
grub_free (node);
@ -814,7 +847,7 @@ grub_hfsplus_iterate_dir (grub_fshelp_node_t dir,
struct grub_hfsplus_key_internal intern;
struct grub_hfsplus_btnode *node;
int ptr;
grub_disk_addr_t ptr;
/* Create a key that points to the first entry in the directory. */
intern.catkey.parent = dir->fileid;
@ -883,7 +916,6 @@ grub_hfsplus_close (grub_file_t file)
return GRUB_ERR_NONE;
}
/* Read LEN bytes data from FILE into BUF. */
static grub_ssize_t
grub_hfsplus_read (grub_file_t file, char *buf, grub_size_t len)
@ -891,13 +923,10 @@ grub_hfsplus_read (grub_file_t file, char *buf, grub_size_t len)
struct grub_hfsplus_data *data =
(struct grub_hfsplus_data *) file->data;
int size = grub_hfsplus_read_file (&data->opened_file, file->read_hook,
return grub_hfsplus_read_file (&data->opened_file, file->read_hook,
file->offset, len, buf);
return size;
}
static grub_err_t
grub_hfsplus_dir (grub_device_t device, const char *path,
int (*hook) (const char *filename,
@ -952,13 +981,66 @@ grub_hfsplus_dir (grub_device_t device, const char *path,
static grub_err_t
grub_hfsplus_label (grub_device_t device __attribute__((unused))
, char **label __attribute__((unused)))
grub_hfsplus_label (grub_device_t device, char **label)
{
/* XXX: It's not documented how to read a label. */
return grub_error (GRUB_ERR_NOT_IMPLEMENTED_YET,
"reading the label of a HFS+ "
"partition is not implemented");
struct grub_hfsplus_data *data;
grub_disk_t disk = device->disk;
struct grub_hfsplus_catkey *catkey;
int i, label_len;
struct grub_hfsplus_key_internal intern;
struct grub_hfsplus_btnode *node;
grub_disk_addr_t ptr;
*label = 0;
data = grub_hfsplus_mount (disk);
if (!data)
return grub_errno;
/* Create a key that points to the label. */
intern.catkey.parent = 1;
intern.catkey.name = "";
/* First lookup the first entry. */
if (grub_hfsplus_btree_search (&data->catalog_tree, &intern,
grub_hfsplus_cmp_catkey_id, &node, &ptr))
{
grub_free (data);
return 0;
}
catkey = (struct grub_hfsplus_catkey *)
grub_hfsplus_btree_recptr (&data->catalog_tree, node, 0);
label_len = grub_be_to_cpu16 (catkey->namelen);
for (i = 0; i < label_len; i++)
{
catkey->name[i] = grub_be_to_cpu16 (catkey->name[i]);
/* If the name is obviously invalid, skip this node. */
if (catkey->name[i] == 0)
return 0;
}
*label = grub_malloc (label_len + 1);
if (! *label)
return grub_errno;
if (! grub_utf16_to_utf8 ((grub_uint8_t *) (*label), catkey->name,
label_len))
{
grub_free (node);
grub_free (*label);
grub_free (data);
*label = 0;
return grub_errno;
}
(*label)[label_len] = '\0';
grub_free (node);
grub_free (data);
return GRUB_ERR_NONE;
}
/* Get mtime. */

View file

@ -1,641 +0,0 @@
/* pxe.c - Driver to provide access to the pxe filesystem */
/*
* GRUB -- GRand Unified Bootloader
* Copyright (C) 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/dl.h>
#include <grub/fs.h>
#include <grub/mm.h>
#include <grub/disk.h>
#include <grub/file.h>
#include <grub/misc.h>
#include <grub/bufio.h>
#include <grub/env.h>
#include <grub/machine/pxe.h>
#include <grub/machine/int.h>
#include <grub/machine/memory.h>
#define SEGMENT(x) ((x) >> 4)
#define OFFSET(x) ((x) & 0xF)
#define SEGOFS(x) ((SEGMENT(x) << 16) + OFFSET(x))
#define LINEAR(x) (void *) (((x >> 16) <<4) + (x & 0xFFFF))
struct grub_pxe_disk_data
{
grub_uint32_t server_ip;
grub_uint32_t gateway_ip;
};
struct grub_pxe_bangpxe *grub_pxe_pxenv;
static grub_uint32_t grub_pxe_your_ip;
static grub_uint32_t grub_pxe_default_server_ip;
static grub_uint32_t grub_pxe_default_gateway_ip;
static unsigned grub_pxe_blksize = GRUB_PXE_MIN_BLKSIZE;
static grub_file_t curr_file = 0;
struct grub_pxe_data
{
grub_uint32_t packet_number;
grub_uint32_t block_size;
char filename[0];
};
static grub_uint32_t pxe_rm_entry = 0;
static struct grub_pxe_bangpxe *
grub_pxe_scan (void)
{
struct grub_bios_int_registers regs;
struct grub_pxenv *pxenv;
struct grub_pxe_bangpxe *bangpxe;
regs.flags = GRUB_CPU_INT_FLAGS_DEFAULT;
regs.ebx = 0;
regs.ecx = 0;
regs.eax = 0x5650;
regs.es = 0;
grub_bios_interrupt (0x1a, &regs);
if ((regs.eax & 0xffff) != 0x564e)
return NULL;
pxenv = (struct grub_pxenv *) ((regs.es << 4) + (regs.ebx & 0xffff));
if (grub_memcmp (pxenv->signature, GRUB_PXE_SIGNATURE,
sizeof (pxenv->signature))
!= 0)
return NULL;
if (pxenv->version < 0x201)
return NULL;
bangpxe = (void *) ((((pxenv->pxe_ptr & 0xffff0000) >> 16) << 4)
+ (pxenv->pxe_ptr & 0xffff));
if (!bangpxe)
return NULL;
if (grub_memcmp (bangpxe->signature, GRUB_PXE_BANGPXE_SIGNATURE,
sizeof (bangpxe->signature)) != 0)
return NULL;
pxe_rm_entry = bangpxe->rm_entry;
return bangpxe;
}
static int
grub_pxe_iterate (int (*hook) (const char *name))
{
if (hook ("pxe"))
return 1;
return 0;
}
static grub_err_t
parse_ip (const char *val, grub_uint32_t *ip, const char **rest)
{
grub_uint32_t newip = 0;
unsigned long t;
int i;
const char *ptr = val;
for (i = 0; i < 4; i++)
{
t = grub_strtoul (ptr, (char **) &ptr, 0);
if (grub_errno)
return grub_errno;
if (t & ~0xff)
return grub_error (GRUB_ERR_OUT_OF_RANGE, "Invalid IP.");
newip >>= 8;
newip |= (t << 24);
if (i != 3 && *ptr != '.')
return grub_error (GRUB_ERR_OUT_OF_RANGE, "Invalid IP.");
ptr++;
}
*ip = newip;
if (rest)
*rest = ptr - 1;
return 0;
}
static grub_err_t
grub_pxe_open (const char *name, grub_disk_t disk)
{
struct grub_pxe_disk_data *data;
if (grub_strcmp (name, "pxe") != 0
&& grub_strncmp (name, "pxe:", sizeof ("pxe:") - 1) != 0)
return grub_error (GRUB_ERR_UNKNOWN_DEVICE, "not a pxe disk");
data = grub_malloc (sizeof (*data));
if (!data)
return grub_errno;
if (grub_strncmp (name, "pxe:", sizeof ("pxe:") - 1) == 0)
{
const char *ptr;
grub_err_t err;
ptr = name + sizeof ("pxe:") - 1;
err = parse_ip (ptr, &(data->server_ip), &ptr);
if (err)
return err;
if (*ptr == ':')
{
err = parse_ip (ptr + 1, &(data->gateway_ip), 0);
if (err)
return err;
}
else
data->gateway_ip = grub_pxe_default_gateway_ip;
}
else
{
data->server_ip = grub_pxe_default_server_ip;
data->gateway_ip = grub_pxe_default_gateway_ip;
}
disk->total_sectors = 0;
disk->id = (unsigned long) data;
disk->data = data;
return GRUB_ERR_NONE;
}
static void
grub_pxe_close (grub_disk_t disk)
{
grub_free (disk->data);
}
static grub_err_t
grub_pxe_read (grub_disk_t disk __attribute((unused)),
grub_disk_addr_t sector __attribute((unused)),
grub_size_t size __attribute((unused)),
char *buf __attribute((unused)))
{
return GRUB_ERR_OUT_OF_RANGE;
}
static grub_err_t
grub_pxe_write (grub_disk_t disk __attribute((unused)),
grub_disk_addr_t sector __attribute((unused)),
grub_size_t size __attribute((unused)),
const char *buf __attribute((unused)))
{
return GRUB_ERR_OUT_OF_RANGE;
}
static struct grub_disk_dev grub_pxe_dev =
{
.name = "pxe",
.id = GRUB_DISK_DEVICE_PXE_ID,
.iterate = grub_pxe_iterate,
.open = grub_pxe_open,
.close = grub_pxe_close,
.read = grub_pxe_read,
.write = grub_pxe_write,
.next = 0
};
static grub_err_t
grub_pxefs_dir (grub_device_t device,
const char *path __attribute__ ((unused)),
int (*hook) (const char *filename,
const struct grub_dirhook_info *info)
__attribute__ ((unused)))
{
if (device->disk->dev->id != GRUB_DISK_DEVICE_PXE_ID)
return grub_error (GRUB_ERR_IO, "not a pxe disk");
return GRUB_ERR_NONE;
}
static grub_err_t
grub_pxefs_open (struct grub_file *file, const char *name)
{
union
{
struct grub_pxenv_tftp_get_fsize c1;
struct grub_pxenv_tftp_open c2;
} c;
struct grub_pxe_data *data;
struct grub_pxe_disk_data *disk_data = file->device->disk->data;
grub_file_t file_int, bufio;
if (file->device->disk->dev->id != GRUB_DISK_DEVICE_PXE_ID)
return grub_error (GRUB_ERR_IO, "not a pxe disk");
if (curr_file != 0)
{
grub_pxe_call (GRUB_PXENV_TFTP_CLOSE, &c.c2, pxe_rm_entry);
curr_file = 0;
}
c.c1.server_ip = disk_data->server_ip;
c.c1.gateway_ip = disk_data->gateway_ip;
grub_strcpy ((char *)&c.c1.filename[0], name);
grub_pxe_call (GRUB_PXENV_TFTP_GET_FSIZE, &c.c1, pxe_rm_entry);
if (c.c1.status)
return grub_error (GRUB_ERR_FILE_NOT_FOUND, "file not found");
file->size = c.c1.file_size;
c.c2.tftp_port = grub_cpu_to_be16 (GRUB_PXE_TFTP_PORT);
c.c2.packet_size = grub_pxe_blksize;
grub_pxe_call (GRUB_PXENV_TFTP_OPEN, &c.c2, pxe_rm_entry);
if (c.c2.status)
return grub_error (GRUB_ERR_BAD_FS, "open fails");
data = grub_zalloc (sizeof (struct grub_pxe_data) + grub_strlen (name) + 1);
if (! data)
return grub_errno;
data->block_size = c.c2.packet_size;
grub_strcpy (data->filename, name);
file_int = grub_malloc (sizeof (*file_int));
if (! file_int)
{
grub_free (data);
return grub_errno;
}
file->data = data;
file->not_easly_seekable = 1;
grub_memcpy (file_int, file, sizeof (struct grub_file));
curr_file = file_int;
bufio = grub_bufio_open (file_int, data->block_size);
if (! bufio)
{
grub_free (file_int);
grub_free (data);
return grub_errno;
}
grub_memcpy (file, bufio, sizeof (struct grub_file));
return GRUB_ERR_NONE;
}
static grub_ssize_t
grub_pxefs_read (grub_file_t file, char *buf, grub_size_t len)
{
struct grub_pxenv_tftp_read c;
struct grub_pxe_data *data;
struct grub_pxe_disk_data *disk_data = file->device->disk->data;
grub_uint32_t pn, r;
data = file->data;
pn = grub_divmod64 (file->offset, data->block_size, &r);
if (r)
{
grub_error (GRUB_ERR_BAD_FS,
"read access must be aligned to packet size");
return -1;
}
if ((curr_file != file) || (data->packet_number > pn))
{
struct grub_pxenv_tftp_open o;
if (curr_file != 0)
grub_pxe_call (GRUB_PXENV_TFTP_CLOSE, &o, pxe_rm_entry);
o.server_ip = disk_data->server_ip;
o.gateway_ip = disk_data->gateway_ip;
grub_strcpy ((char *)&o.filename[0], data->filename);
o.tftp_port = grub_cpu_to_be16 (GRUB_PXE_TFTP_PORT);
o.packet_size = data->block_size;
grub_pxe_call (GRUB_PXENV_TFTP_OPEN, &o, pxe_rm_entry);
if (o.status)
{
grub_error (GRUB_ERR_BAD_FS, "open fails");
return -1;
}
data->block_size = o.packet_size;
data->packet_number = 0;
curr_file = file;
}
c.buffer = SEGOFS (GRUB_MEMORY_MACHINE_SCRATCH_ADDR);
while (pn >= data->packet_number)
{
c.buffer_size = data->block_size;
grub_pxe_call (GRUB_PXENV_TFTP_READ, &c, pxe_rm_entry);
if (c.status)
{
grub_error (GRUB_ERR_BAD_FS, "read fails");
return -1;
}
data->packet_number++;
}
grub_memcpy (buf, (char *) GRUB_MEMORY_MACHINE_SCRATCH_ADDR, len);
return len;
}
static grub_err_t
grub_pxefs_close (grub_file_t file)
{
struct grub_pxenv_tftp_close c;
if (curr_file == file)
{
grub_pxe_call (GRUB_PXENV_TFTP_CLOSE, &c, pxe_rm_entry);
curr_file = 0;
}
grub_free (file->data);
return GRUB_ERR_NONE;
}
static grub_err_t
grub_pxefs_label (grub_device_t device __attribute ((unused)),
char **label __attribute ((unused)))
{
*label = 0;
return GRUB_ERR_NONE;
}
static struct grub_fs grub_pxefs_fs =
{
.name = "pxefs",
.dir = grub_pxefs_dir,
.open = grub_pxefs_open,
.read = grub_pxefs_read,
.close = grub_pxefs_close,
.label = grub_pxefs_label,
.next = 0
};
static char *
grub_env_write_readonly (struct grub_env_var *var __attribute__ ((unused)),
const char *val __attribute__ ((unused)))
{
return NULL;
}
static void
set_mac_env (grub_uint8_t *mac_addr, grub_size_t mac_len)
{
char buf[(sizeof ("XX:") - 1) * mac_len + 1];
char *ptr = buf;
unsigned i;
for (i = 0; i < mac_len; i++)
{
grub_snprintf (ptr, sizeof (buf) - (ptr - buf),
"%02x:", mac_addr[i] & 0xff);
ptr += (sizeof ("XX:") - 1);
}
if (mac_len)
*(ptr - 1) = 0;
else
buf[0] = 0;
grub_env_set ("net_pxe_mac", buf);
/* XXX: Is it possible to change MAC in PXE? */
grub_register_variable_hook ("net_pxe_mac", 0, grub_env_write_readonly);
}
static void
set_env_limn_ro (const char *varname, char *value, grub_size_t len)
{
char c;
c = value[len];
value[len] = 0;
grub_env_set (varname, value);
value[len] = c;
grub_register_variable_hook (varname, 0, grub_env_write_readonly);
}
static void
parse_dhcp_vendor (void *vend, int limit)
{
grub_uint8_t *ptr, *ptr0;
ptr = ptr0 = vend;
if (grub_be_to_cpu32 (*(grub_uint32_t *) ptr) != 0x63825363)
return;
ptr = ptr + sizeof (grub_uint32_t);
while (ptr - ptr0 < limit)
{
grub_uint8_t tagtype;
grub_uint8_t taglength;
tagtype = *ptr++;
/* Pad tag. */
if (tagtype == 0)
continue;
/* End tag. */
if (tagtype == 0xff)
return;
taglength = *ptr++;
switch (tagtype)
{
case 12:
set_env_limn_ro ("net_pxe_hostname", (char *) ptr, taglength);
break;
case 15:
set_env_limn_ro ("net_pxe_domain", (char *) ptr, taglength);
break;
case 17:
set_env_limn_ro ("net_pxe_rootpath", (char *) ptr, taglength);
break;
case 18:
set_env_limn_ro ("net_pxe_extensionspath", (char *) ptr, taglength);
break;
/* If you need any other options please contact GRUB
developpement team. */
}
ptr += taglength;
}
}
static void
grub_pxe_detect (void)
{
struct grub_pxe_bangpxe *pxenv;
struct grub_pxenv_get_cached_info ci;
struct grub_pxenv_boot_player *bp;
pxenv = grub_pxe_scan ();
if (! pxenv)
return;
ci.packet_type = GRUB_PXENV_PACKET_TYPE_DHCP_ACK;
ci.buffer = 0;
ci.buffer_size = 0;
grub_pxe_call (GRUB_PXENV_GET_CACHED_INFO, &ci, pxe_rm_entry);
if (ci.status)
return;
bp = LINEAR (ci.buffer);
grub_pxe_your_ip = bp->your_ip;
grub_pxe_default_server_ip = bp->server_ip;
grub_pxe_default_gateway_ip = bp->gateway_ip;
set_mac_env (bp->mac_addr, bp->hw_len < sizeof (bp->mac_addr) ? bp->hw_len
: sizeof (bp->mac_addr));
set_env_limn_ro ("net_pxe_boot_file", (char *) bp->boot_file,
sizeof (bp->boot_file));
set_env_limn_ro ("net_pxe_dhcp_server_name", (char *) bp->server_name,
sizeof (bp->server_name));
parse_dhcp_vendor (&bp->vendor, sizeof (bp->vendor));
grub_pxe_pxenv = pxenv;
}
void
grub_pxe_unload (void)
{
if (grub_pxe_pxenv)
{
grub_fs_unregister (&grub_pxefs_fs);
grub_disk_dev_unregister (&grub_pxe_dev);
grub_pxe_pxenv = 0;
}
}
static void
set_ip_env (char *varname, grub_uint32_t ip)
{
char buf[sizeof ("XXX.XXX.XXX.XXX")];
grub_snprintf (buf, sizeof (buf), "%d.%d.%d.%d", (ip & 0xff),
(ip >> 8) & 0xff, (ip >> 16) & 0xff, (ip >> 24) & 0xff);
grub_env_set (varname, buf);
}
static char *
write_ip_env (grub_uint32_t *ip, const char *val)
{
char *buf;
grub_err_t err;
grub_uint32_t newip;
err = parse_ip (val, &newip, 0);
if (err)
return 0;
/* Normalize the IP. */
buf = grub_xasprintf ("%d.%d.%d.%d", (newip & 0xff), (newip >> 8) & 0xff,
(newip >> 16) & 0xff, (newip >> 24) & 0xff);
if (!buf)
return 0;
*ip = newip;
return buf;
}
static char *
grub_env_write_pxe_default_server (struct grub_env_var *var
__attribute__ ((unused)),
const char *val)
{
return write_ip_env (&grub_pxe_default_server_ip, val);
}
static char *
grub_env_write_pxe_default_gateway (struct grub_env_var *var
__attribute__ ((unused)),
const char *val)
{
return write_ip_env (&grub_pxe_default_gateway_ip, val);
}
static char *
grub_env_write_pxe_blocksize (struct grub_env_var *var __attribute__ ((unused)),
const char *val)
{
unsigned size;
char *buf;
size = grub_strtoul (val, 0, 0);
if (grub_errno)
return 0;
if (size < GRUB_PXE_MIN_BLKSIZE)
size = GRUB_PXE_MIN_BLKSIZE;
else if (size > GRUB_PXE_MAX_BLKSIZE)
size = GRUB_PXE_MAX_BLKSIZE;
buf = grub_xasprintf ("%d", size);
if (!buf)
return 0;
grub_pxe_blksize = size;
return buf;
}
GRUB_MOD_INIT(pxe)
{
grub_pxe_detect ();
if (grub_pxe_pxenv)
{
char *buf;
buf = grub_xasprintf ("%d", grub_pxe_blksize);
if (buf)
grub_env_set ("pxe_blksize", buf);
grub_free (buf);
set_ip_env ("pxe_default_server", grub_pxe_default_server_ip);
set_ip_env ("pxe_default_gateway", grub_pxe_default_gateway_ip);
set_ip_env ("net_pxe_ip", grub_pxe_your_ip);
grub_register_variable_hook ("pxe_default_server", 0,
grub_env_write_pxe_default_server);
grub_register_variable_hook ("pxe_default_gateway", 0,
grub_env_write_pxe_default_gateway);
/* XXX: Is it possible to change IP in PXE? */
grub_register_variable_hook ("net_pxe_ip", 0,
grub_env_write_readonly);
grub_register_variable_hook ("pxe_blksize", 0,
grub_env_write_pxe_blocksize);
grub_disk_dev_register (&grub_pxe_dev);
grub_fs_register (&grub_pxefs_fs);
}
}
GRUB_MOD_FINI(pxe)
{
grub_pxe_unload ();
}

View file

@ -27,6 +27,9 @@
#include <grub/types.h>
#include <grub/fshelp.h>
#include <grub/charset.h>
#include <grub/datetime.h>
GRUB_MOD_LICENSE ("GPLv3+");
#define GRUB_ISO9660_FSTYPE_DIR 0040000
#define GRUB_ISO9660_FSTYPE_REG 0100000
@ -53,6 +56,17 @@ struct grub_iso9660_voldesc
grub_uint8_t version;
} __attribute__ ((packed));
struct grub_iso9660_date2
{
grub_uint8_t year;
grub_uint8_t month;
grub_uint8_t day;
grub_uint8_t hour;
grub_uint8_t minute;
grub_uint8_t second;
grub_uint8_t offset;
} __attribute__ ((packed));
/* A directory entry. */
struct grub_iso9660_dir
{
@ -62,7 +76,7 @@ struct grub_iso9660_dir
grub_uint32_t first_sector_be;
grub_uint32_t size;
grub_uint32_t size_be;
grub_uint8_t unused1[7];
struct grub_iso9660_date2 mtime;
grub_uint8_t flags;
grub_uint8_t unused2[6];
grub_uint8_t namelen;
@ -135,53 +149,148 @@ struct grub_iso9660_data
{
struct grub_iso9660_primary_voldesc voldesc;
grub_disk_t disk;
unsigned int first_sector;
int rockridge;
int susp_skip;
int joliet;
struct grub_fshelp_node *node;
};
struct grub_fshelp_node
{
struct grub_iso9660_data *data;
unsigned int size;
unsigned int blk;
unsigned int dir_blk;
unsigned int dir_off;
grub_size_t have_dirents, alloc_dirents;
char *symlink;
struct grub_iso9660_dir dirents[8];
};
enum
{
FLAG_TYPE_PLAIN = 0,
FLAG_TYPE_DIR = 2,
FLAG_TYPE = 3,
FLAG_MORE_EXTENTS = 0x80
};
static grub_dl_t my_mod;
static grub_err_t
iso9660_to_unixtime (const struct grub_iso9660_date *i, grub_int32_t *nix)
{
struct grub_datetime datetime;
if (! i->year[0] && ! i->year[1]
&& ! i->year[2] && ! i->year[3]
&& ! i->month[0] && ! i->month[1]
&& ! i->day[0] && ! i->day[1]
&& ! i->hour[0] && ! i->hour[1]
&& ! i->minute[0] && ! i->minute[1]
&& ! i->second[0] && ! i->second[1]
&& ! i->hundredth[0] && ! i->hundredth[1])
return grub_error (GRUB_ERR_BAD_NUMBER, "empty date");
datetime.year = (i->year[0] - '0') * 1000 + (i->year[1] - '0') * 100
+ (i->year[2] - '0') * 10 + (i->year[3] - '0');
datetime.month = (i->month[0] - '0') * 10 + (i->month[1] - '0');
datetime.day = (i->day[0] - '0') * 10 + (i->day[1] - '0');
datetime.hour = (i->hour[0] - '0') * 10 + (i->hour[1] - '0');
datetime.minute = (i->minute[0] - '0') * 10 + (i->minute[1] - '0');
datetime.second = (i->second[0] - '0') * 10 + (i->second[1] - '0');
if (!grub_datetime2unixtime (&datetime, nix))
return grub_error (GRUB_ERR_BAD_NUMBER, "incorrect date");
*nix -= i->offset * 60 * 15;
return GRUB_ERR_NONE;
}
static int
iso9660_to_unixtime2 (const struct grub_iso9660_date2 *i, grub_int32_t *nix)
{
struct grub_datetime datetime;
datetime.year = i->year + 1900;
datetime.month = i->month;
datetime.day = i->day;
datetime.hour = i->hour;
datetime.minute = i->minute;
datetime.second = i->second;
if (!grub_datetime2unixtime (&datetime, nix))
return 0;
*nix -= i->offset * 60 * 15;
return 1;
}
static grub_err_t
read_node (grub_fshelp_node_t node, grub_off_t off, grub_size_t len, char *buf)
{
grub_size_t i = 0;
while (len > 0)
{
grub_size_t toread;
grub_err_t err;
while (i < node->have_dirents
&& off >= grub_le_to_cpu32 (node->dirents[i].size))
{
off -= grub_le_to_cpu32 (node->dirents[i].size);
i++;
}
if (i == node->have_dirents)
return grub_error (GRUB_ERR_OUT_OF_RANGE, "read out of range");
toread = grub_le_to_cpu32 (node->dirents[i].size);
if (toread > len)
toread = len;
err = grub_disk_read (node->data->disk,
((grub_disk_addr_t) grub_le_to_cpu32 (node->dirents[i].first_sector)) << GRUB_ISO9660_LOG2_BLKSZ,
off, toread, buf);
if (err)
return err;
len -= toread;
off += toread;
buf += toread;
}
return GRUB_ERR_NONE;
}
/* Iterate over the susp entries, starting with block SUA_BLOCK on the
offset SUA_POS with a size of SUA_SIZE bytes. Hook is called for
every entry. */
static grub_err_t
grub_iso9660_susp_iterate (struct grub_iso9660_data *data,
int sua_block, int sua_pos, int sua_size,
grub_iso9660_susp_iterate (grub_fshelp_node_t node, grub_off_t off,
grub_ssize_t sua_size,
grub_err_t (*hook)
(struct grub_iso9660_susp_entry *entry))
{
char *sua;
struct grub_iso9660_susp_entry *entry;
grub_disk_addr_t ce_block;
int is_ce = 0;
auto grub_err_t load_sua (void);
/* Load a part of the System Usage Area. */
grub_err_t load_sua (void)
{
grub_err_t err;
sua = grub_malloc (sua_size);
if (!sua)
return grub_errno;
if (grub_disk_read (data->disk, sua_block, sua_pos,
sua_size, sua))
return grub_errno;
if (is_ce)
err = grub_disk_read (node->data->disk, ce_block, off,
sua_size, sua);
else
err = read_node (node, off, sua_size, sua);
if (err)
return err;
entry = (struct grub_iso9660_susp_entry *) sua;
return 0;
}
if (sua_size <= 0)
return GRUB_ERR_NONE;
if (load_sua ())
return grub_errno;
@ -198,10 +307,11 @@ grub_iso9660_susp_iterate (struct grub_iso9660_data *data,
{
struct grub_iso9660_susp_ce *ce;
is_ce = 1;
ce = (struct grub_iso9660_susp_ce *) entry;
sua_size = grub_le_to_cpu32 (ce->len);
sua_pos = grub_le_to_cpu32 (ce->off);
sua_block = grub_le_to_cpu32 (ce->blk) << GRUB_ISO9660_LOG2_BLKSZ;
off = grub_le_to_cpu32 (ce->off);
ce_block = grub_le_to_cpu32 (ce->blk) << GRUB_ISO9660_LOG2_BLKSZ;
grub_free (sua);
if (load_sua ())
@ -322,6 +432,9 @@ grub_iso9660_mount (grub_disk_t disk)
+ (rootdir.namelen % 2) - 1);
sua_size = rootdir.len - sua_pos;
if (!sua_size)
return data;
sua = grub_malloc (sua_size);
if (! sua)
goto fail;
@ -339,6 +452,14 @@ grub_iso9660_mount (grub_disk_t disk)
/* Test if the SUSP protocol is used on this filesystem. */
if (grub_strncmp ((char *) entry->sig, "SP", 2) == 0)
{
struct grub_fshelp_node rootnode;
rootnode.data = data;
rootnode.alloc_dirents = 0;
rootnode.have_dirents = 1;
rootnode.symlink = 0;
rootnode.dirents[0] = data->voldesc.rootdir;
/* The 2nd data byte stored how many bytes are skipped every time
to get to the SUA (System Usage Area). */
data->susp_skip = entry->data[2];
@ -346,9 +467,7 @@ grub_iso9660_mount (grub_disk_t disk)
/* Iterate over the entries in the SUA area to detect
extensions. */
if (grub_iso9660_susp_iterate (data,
(grub_le_to_cpu32 (data->voldesc.rootdir.first_sector)
<< GRUB_ISO9660_LOG2_BLKSZ),
if (grub_iso9660_susp_iterate (&rootnode,
sua_pos, sua_size, susp_iterate))
goto fail;
}
@ -364,109 +483,19 @@ grub_iso9660_mount (grub_disk_t disk)
static char *
grub_iso9660_read_symlink (grub_fshelp_node_t node)
{
struct grub_iso9660_dir dirent;
int sua_off;
int sua_size;
char *symlink = 0;
int addslash = 0;
auto void add_part (const char *part, int len);
auto grub_err_t susp_iterate_sl (struct grub_iso9660_susp_entry *);
/* Extend the symlink. */
void add_part (const char *part, int len)
{
int size = grub_strlen (symlink);
symlink = grub_realloc (symlink, size + len + 1);
if (! symlink)
return;
grub_strncat (symlink, part, len);
}
/* Read in a symlink. */
grub_err_t susp_iterate_sl (struct grub_iso9660_susp_entry *entry)
{
if (grub_strncmp ("SL", (char *) entry->sig, 2) == 0)
{
unsigned int pos = 1;
/* The symlink is not stored as a POSIX symlink, translate it. */
while (pos < grub_le_to_cpu32 (entry->len))
{
if (addslash)
{
add_part ("/", 1);
addslash = 0;
}
/* The current position is the `Component Flag'. */
switch (entry->data[pos] & 30)
{
case 0:
{
/* The data on pos + 2 is the actual data, pos + 1
is the length. Both are part of the `Component
Record'. */
add_part ((char *) &entry->data[pos + 2],
entry->data[pos + 1]);
if ((entry->data[pos] & 1))
addslash = 1;
break;
}
case 2:
add_part ("./", 2);
break;
case 4:
add_part ("../", 3);
break;
case 8:
add_part ("/", 1);
break;
}
/* In pos + 1 the length of the `Component Record' is
stored. */
pos += entry->data[pos + 1] + 2;
}
/* Check if `grub_realloc' failed. */
if (grub_errno)
return grub_errno;
}
return 0;
}
if (grub_disk_read (node->data->disk, node->dir_blk, node->dir_off,
sizeof (dirent), (char *) &dirent))
return 0;
sua_off = (sizeof (dirent) + dirent.namelen + 1 - (dirent.namelen % 2)
+ node->data->susp_skip);
sua_size = dirent.len - sua_off;
symlink = grub_malloc (1);
if (!symlink)
return 0;
*symlink = '\0';
if (grub_iso9660_susp_iterate (node->data, node->dir_blk,
node->dir_off + sua_off,
sua_size, susp_iterate_sl))
{
grub_free (symlink);
return 0;
}
return symlink;
return node->symlink ? grub_strdup (node->symlink) : grub_strdup ("");
}
static grub_off_t
get_node_size (grub_fshelp_node_t node)
{
grub_off_t ret = 0;
grub_size_t i;
for (i = 0; i < node->have_dirents; i++)
ret += grub_le_to_cpu32 (node->dirents[i].size);
return ret;
}
static int
grub_iso9660_iterate_dir (grub_fshelp_node_t dir,
@ -476,10 +505,28 @@ grub_iso9660_iterate_dir (grub_fshelp_node_t dir,
grub_fshelp_node_t node))
{
struct grub_iso9660_dir dirent;
unsigned int offset = 0;
grub_off_t offset = 0;
char *filename;
int filename_alloc = 0;
enum grub_fshelp_filetype type;
grub_off_t len;
char *symlink = 0;
int addslash = 0;
auto void add_part (const char *part, int len);
/* Extend the symlink. */
void add_part (const char *part, int len2)
{
int size = symlink ? grub_strlen (symlink) : 0;
symlink = grub_realloc (symlink, size + len2 + 1);
if (! symlink)
return;
symlink[size] = 0;
grub_strncat (symlink, part, len2);
}
auto grub_err_t susp_iterate_dir (struct grub_iso9660_susp_entry *);
@ -537,17 +584,68 @@ grub_iso9660_iterate_dir (grub_fshelp_node_t dir,
type = GRUB_FSHELP_UNKNOWN;
}
}
else if (grub_strncmp ("SL", (char *) entry->sig, 2) == 0)
{
unsigned int pos = 1;
/* The symlink is not stored as a POSIX symlink, translate it. */
while (pos + sizeof (*entry) < grub_le_to_cpu32 (entry->len))
{
if (addslash)
{
add_part ("/", 1);
addslash = 0;
}
/* The current position is the `Component Flag'. */
switch (entry->data[pos] & 30)
{
case 0:
{
/* The data on pos + 2 is the actual data, pos + 1
is the length. Both are part of the `Component
Record'. */
add_part ((char *) &entry->data[pos + 2],
entry->data[pos + 1]);
if ((entry->data[pos] & 1))
addslash = 1;
break;
}
case 2:
add_part ("./", 2);
break;
case 4:
add_part ("../", 3);
break;
case 8:
add_part ("/", 1);
break;
}
/* In pos + 1 the length of the `Component Record' is
stored. */
pos += entry->data[pos + 1] + 2;
}
/* Check if `grub_realloc' failed. */
if (grub_errno)
return grub_errno;
}
return 0;
}
while (offset < dir->size)
len = get_node_size (dir);
for (; offset < len; offset += dirent.len)
{
if (grub_disk_read (dir->data->disk,
(dir->blk << GRUB_ISO9660_LOG2_BLKSZ)
+ offset / GRUB_DISK_SECTOR_SIZE,
offset % GRUB_DISK_SECTOR_SIZE,
sizeof (dirent), (char *) &dirent))
symlink = 0;
addslash = 0;
if (read_node (dir, offset, sizeof (dirent), (char *) &dirent))
return 0;
/* The end of the block, skip to the next one. */
@ -572,39 +670,30 @@ grub_iso9660_iterate_dir (grub_fshelp_node_t dir,
type = GRUB_FSHELP_UNKNOWN;
if (dir->data->rockridge
&& grub_iso9660_susp_iterate (dir->data,
(dir->blk << GRUB_ISO9660_LOG2_BLKSZ)
+ (sua_off
/ GRUB_DISK_SECTOR_SIZE),
sua_off % GRUB_DISK_SECTOR_SIZE,
sua_size, susp_iterate_dir))
&& grub_iso9660_susp_iterate (dir, sua_off, sua_size,
susp_iterate_dir))
return 0;
/* Read the name. */
if (grub_disk_read (dir->data->disk,
(dir->blk << GRUB_ISO9660_LOG2_BLKSZ)
+ nameoffset / GRUB_DISK_SECTOR_SIZE,
nameoffset % GRUB_DISK_SECTOR_SIZE,
dirent.namelen, (char *) name))
if (read_node (dir, nameoffset, dirent.namelen, (char *) name))
return 0;
node = grub_malloc (sizeof (struct grub_fshelp_node));
if (!node)
return 0;
node->alloc_dirents = ARRAY_SIZE (node->dirents);
node->have_dirents = 1;
/* Setup a new node. */
node->data = dir->data;
node->size = grub_le_to_cpu32 (dirent.size);
node->blk = grub_le_to_cpu32 (dirent.first_sector);
node->dir_blk = ((dir->blk << GRUB_ISO9660_LOG2_BLKSZ)
+ offset / GRUB_DISK_SECTOR_SIZE);
node->dir_off = offset % GRUB_DISK_SECTOR_SIZE;
node->symlink = symlink;
/* If the filetype was not stored using rockridge, use
whatever is stored in the iso9660 filesystem. */
if (type == GRUB_FSHELP_UNKNOWN)
{
if ((dirent.flags & 3) == 2)
if ((dirent.flags & FLAG_TYPE) == FLAG_TYPE_DIR)
type = GRUB_FSHELP_DIR;
else
type = GRUB_FSHELP_REG;
@ -619,10 +708,12 @@ grub_iso9660_iterate_dir (grub_fshelp_node_t dir,
if (filename)
*filename = '\0';
if (dirent.namelen == 1 && name[0] == 0)
filename = ".";
else if (dirent.namelen == 1 && name[0] == 1)
filename = "..";
/* . and .. */
if (dirent.namelen == 1 && (name[0] == 0 || name[0] == 1))
{
grub_free (node);
continue;
}
else
filename = name;
}
@ -645,6 +736,35 @@ grub_iso9660_iterate_dir (grub_fshelp_node_t dir,
filename_alloc = 1;
}
node->dirents[0] = dirent;
while (dirent.flags & FLAG_MORE_EXTENTS)
{
offset += dirent.len;
if (read_node (dir, offset, sizeof (dirent), (char *) &dirent))
{
if (filename_alloc)
grub_free (filename);
grub_free (node);
return 0;
}
if (node->have_dirents >= node->alloc_dirents)
{
struct grub_fshelp_node *new_node;
node->alloc_dirents *= 2;
new_node = grub_malloc (sizeof (struct grub_fshelp_node)
+ ((node->alloc_dirents
- ARRAY_SIZE (node->dirents))
* sizeof (node->dirents[0])));
if (!new_node)
{
if (filename_alloc)
grub_free (filename);
grub_free (node);
return 0;
}
}
node->dirents[node->have_dirents++] = dirent;
}
if (hook (filename, type, node))
{
if (filename_alloc)
@ -654,8 +774,6 @@ grub_iso9660_iterate_dir (grub_fshelp_node_t dir,
if (filename_alloc)
grub_free (filename);
}
offset += dirent.len;
}
return 0;
@ -683,6 +801,8 @@ grub_iso9660_dir (grub_device_t device, const char *path,
struct grub_dirhook_info info;
grub_memset (&info, 0, sizeof (info));
info.dir = ((filetype & GRUB_FSHELP_TYPE_MASK) == GRUB_FSHELP_DIR);
info.mtimeset = !!iso9660_to_unixtime2 (&node->dirents[0].mtime, &info.mtime);
grub_free (node);
return hook (filename, &info);
}
@ -694,8 +814,10 @@ grub_iso9660_dir (grub_device_t device, const char *path,
goto fail;
rootnode.data = data;
rootnode.blk = grub_le_to_cpu32 (data->voldesc.rootdir.first_sector);
rootnode.size = grub_le_to_cpu32 (data->voldesc.rootdir.size);
rootnode.alloc_dirents = 0;
rootnode.have_dirents = 1;
rootnode.symlink = 0;
rootnode.dirents[0] = data->voldesc.rootdir;
/* Use the fshelp function to traverse the path. */
if (grub_fshelp_find_file (path, &rootnode,
@ -735,8 +857,10 @@ grub_iso9660_open (struct grub_file *file, const char *name)
goto fail;
rootnode.data = data;
rootnode.blk = grub_le_to_cpu32 (data->voldesc.rootdir.first_sector);
rootnode.size = grub_le_to_cpu32 (data->voldesc.rootdir.size);
rootnode.alloc_dirents = 0;
rootnode.have_dirents = 1;
rootnode.symlink = 0;
rootnode.dirents[0] = data->voldesc.rootdir;
/* Use the fshelp function to traverse the path. */
if (grub_fshelp_find_file (name, &rootnode,
@ -746,10 +870,9 @@ grub_iso9660_open (struct grub_file *file, const char *name)
GRUB_FSHELP_REG))
goto fail;
data->first_sector = foundnode->blk;
data->node = foundnode;
file->data = data;
file->size = foundnode->size;
file->size = get_node_size (foundnode);
file->offset = 0;
return 0;
@ -771,10 +894,7 @@ grub_iso9660_read (grub_file_t file, char *buf, grub_size_t len)
/* XXX: The file is stored in as a single extent. */
data->disk->read_hook = file->read_hook;
grub_disk_read (data->disk,
data->first_sector << GRUB_ISO9660_LOG2_BLKSZ,
file->offset,
len, buf);
read_node (data->node, file->offset, len, buf);
data->disk->read_hook = NULL;
if (grub_errno)
@ -787,7 +907,10 @@ grub_iso9660_read (grub_file_t file, char *buf, grub_size_t len)
static grub_err_t
grub_iso9660_close (grub_file_t file)
{
grub_free (file->data);
struct grub_iso9660_data *data =
(struct grub_iso9660_data *) file->data;
grub_free (data->node);
grub_free (data);
grub_dl_unref (my_mod);
@ -808,6 +931,15 @@ grub_iso9660_label (grub_device_t device, char **label)
((grub_uint16_t *) &data->voldesc.volname, 16);
else
*label = grub_strndup ((char *) data->voldesc.volname, 32);
if (*label)
{
char *ptr;
for (ptr = *label; *ptr;ptr++);
ptr--;
while (ptr >= *label && *ptr == ' ')
*ptr-- = 0;
}
grub_free (data);
}
else
@ -871,6 +1003,32 @@ grub_iso9660_uuid (grub_device_t device, char **uuid)
return grub_errno;
}
/* Get writing time of filesystem. */
static grub_err_t
grub_iso9660_mtime (grub_device_t device, grub_int32_t *timebuf)
{
struct grub_iso9660_data *data;
grub_disk_t disk = device->disk;
grub_err_t err;
grub_dl_ref (my_mod);
data = grub_iso9660_mount (disk);
if (!data)
{
grub_dl_unref (my_mod);
return grub_errno;
}
err = iso9660_to_unixtime (&data->voldesc.modified, timebuf);
grub_dl_unref (my_mod);
grub_free (data);
return err;
}
static struct grub_fs grub_iso9660_fs =
@ -882,6 +1040,7 @@ static struct grub_fs grub_iso9660_fs =
.close = grub_iso9660_close,
.label = grub_iso9660_label,
.uuid = grub_iso9660_uuid,
.mtime = grub_iso9660_mtime,
.next = 0
};

View file

@ -26,6 +26,8 @@
#include <grub/types.h>
#include <grub/charset.h>
GRUB_MOD_LICENSE ("GPLv3+");
#define GRUB_JFS_MAX_SYMLNK_CNT 8
#define GRUB_JFS_FILETYPE_MASK 0170000
#define GRUB_JFS_FILETYPE_REG 0100000
@ -50,9 +52,9 @@ struct grub_jfs_sblock
grub_uint32_t blksz;
grub_uint16_t log2_blksz;
grub_uint8_t unused[71];
grub_uint8_t unused[79];
grub_uint8_t volname[11];
grub_uint8_t unused2[32];
grub_uint8_t unused2[24];
grub_uint8_t uuid[16];
};
@ -153,6 +155,12 @@ struct grub_jfs_leaf_next_dirent
grub_uint16_t namepart[15];
} __attribute__ ((packed));
struct grub_jfs_time
{
grub_int32_t sec;
grub_int32_t nanosec;
} __attribute__ ((packed));
struct grub_jfs_inode
{
grub_uint32_t stamp;
@ -162,7 +170,10 @@ struct grub_jfs_inode
grub_uint64_t size;
grub_uint8_t unused2[20];
grub_uint32_t mode;
grub_uint8_t unused3[72];
struct grub_jfs_time atime;
struct grub_jfs_time ctime;
struct grub_jfs_time mtime;
grub_uint8_t unused3[48];
grub_uint8_t unused4[96];
union
@ -217,12 +228,12 @@ struct grub_jfs_diropen
struct grub_jfs_tree_dir header;
struct grub_jfs_leaf_dirent dirent[0];
struct grub_jfs_leaf_next_dirent next_dirent[0];
char sorted[0];
grub_uint8_t sorted[0];
} *dirpage __attribute__ ((packed));
struct grub_jfs_data *data;
struct grub_jfs_inode *inode;
int count;
char *sorted;
grub_uint8_t *sorted;
struct grub_jfs_leaf_dirent *leaf;
struct grub_jfs_leaf_next_dirent *next_leaf;
@ -234,19 +245,19 @@ struct grub_jfs_diropen
static grub_dl_t my_mod;
static grub_err_t grub_jfs_lookup_symlink (struct grub_jfs_data *data, int ino);
static grub_err_t grub_jfs_lookup_symlink (struct grub_jfs_data *data, grub_uint32_t ino);
/* Get the block number for the block BLK in the node INODE in the
mounted filesystem DATA. */
static int
static grub_int64_t
grub_jfs_blkno (struct grub_jfs_data *data, struct grub_jfs_inode *inode,
unsigned int blk)
grub_uint64_t blk)
{
auto int getblk (struct grub_jfs_treehead *treehead,
struct grub_jfs_tree_extent *extents);
auto grub_int64_t getblk (struct grub_jfs_treehead *treehead,
struct grub_jfs_tree_extent *extents);
int getblk (struct grub_jfs_treehead *treehead,
struct grub_jfs_tree_extent *extents)
grub_int64_t getblk (struct grub_jfs_treehead *treehead,
struct grub_jfs_tree_extent *extents)
{
int found = -1;
int i;
@ -258,7 +269,7 @@ grub_jfs_blkno (struct grub_jfs_data *data, struct grub_jfs_inode *inode,
/* Read the leafnode. */
if (grub_le_to_cpu32 (extents[i].offset2) <= blk
&& ((grub_le_to_cpu16 (extents[i].extent.length))
+ (extents[i].extent.length2 << 8)
+ (extents[i].extent.length2 << 16)
+ grub_le_to_cpu32 (extents[i].offset2)) > blk)
return (blk - grub_le_to_cpu32 (extents[i].offset2)
+ grub_le_to_cpu32 (extents[i].extent.blk2));
@ -277,7 +288,7 @@ grub_jfs_blkno (struct grub_jfs_data *data, struct grub_jfs_inode *inode,
} tree;
if (grub_disk_read (data->disk,
grub_le_to_cpu32 (extents[found].extent.blk2)
((grub_disk_addr_t) grub_le_to_cpu32 (extents[found].extent.blk2))
<< (grub_le_to_cpu16 (data->sblock.log2_blksz)
- GRUB_DISK_SECTOR_BITS), 0,
sizeof (tree), (char *) &tree))
@ -294,15 +305,15 @@ grub_jfs_blkno (struct grub_jfs_data *data, struct grub_jfs_inode *inode,
static grub_err_t
grub_jfs_read_inode (struct grub_jfs_data *data, int ino,
grub_jfs_read_inode (struct grub_jfs_data *data, grub_uint32_t ino,
struct grub_jfs_inode *inode)
{
struct grub_jfs_iag iag;
int iagnum = ino / 4096;
int inoext = (ino % 4096) / 32;
int inonum = (ino % 4096) % 32;
grub_uint32_t iagblk;
grub_uint32_t inoblk;
grub_uint32_t iagnum = ino / 4096;
unsigned inoext = (ino % 4096) / 32;
unsigned inonum = (ino % 4096) % 32;
grub_uint64_t iagblk;
grub_uint64_t inoblk;
iagblk = grub_jfs_blkno (data, &data->fileset, iagnum + 1);
if (grub_errno)
@ -348,6 +359,13 @@ grub_jfs_mount (grub_disk_t disk)
goto fail;
}
if (grub_le_to_cpu32 (data->sblock.blksz)
!= (1U << grub_le_to_cpu16 (data->sblock.log2_blksz)))
{
grub_error (GRUB_ERR_BAD_FS, "not a JFS filesystem");
goto fail;
}
data->disk = disk;
data->pos = 0;
data->linknest = 0;
@ -374,7 +392,7 @@ grub_jfs_opendir (struct grub_jfs_data *data, struct grub_jfs_inode *inode)
{
struct grub_jfs_internal_dirent *de;
struct grub_jfs_diropen *diro;
int blk;
grub_disk_addr_t blk;
de = (struct grub_jfs_internal_dirent *) inode->dir.dirents;
@ -397,7 +415,7 @@ grub_jfs_opendir (struct grub_jfs_data *data, struct grub_jfs_inode *inode)
{
diro->leaf = inode->dir.dirents;
diro->next_leaf = (struct grub_jfs_leaf_next_dirent *) de;
diro->sorted = (char *) (inode->dir.header.sorted);
diro->sorted = inode->dir.header.sorted;
diro->count = inode->dir.header.count;
return diro;
@ -475,7 +493,7 @@ grub_jfs_getent (struct grub_jfs_diropen *diro)
/* The last node, read in more. */
if (diro->index == diro->count)
{
unsigned int next;
grub_disk_addr_t next;
/* If the inode contains the entry tree or if this was the last
node, there is nothing to read. */
@ -499,7 +517,7 @@ grub_jfs_getent (struct grub_jfs_diropen *diro)
diro->index = 0;
}
leaf = &diro->leaf[(int) diro->sorted[diro->index]];
leaf = &diro->leaf[diro->sorted[diro->index]];
next_leaf = &diro->next_leaf[diro->index];
len = leaf->len;
@ -540,21 +558,21 @@ static grub_ssize_t
grub_jfs_read_file (struct grub_jfs_data *data,
void NESTED_FUNC_ATTR (*read_hook) (grub_disk_addr_t sector,
unsigned offset, unsigned length),
int pos, grub_size_t len, char *buf)
grub_off_t pos, grub_size_t len, char *buf)
{
int i;
int blockcnt;
grub_off_t i;
grub_off_t blockcnt;
blockcnt = ((len + pos + grub_le_to_cpu32 (data->sblock.blksz) - 1)
/ grub_le_to_cpu32 (data->sblock.blksz));
blockcnt = (len + pos + grub_le_to_cpu32 (data->sblock.blksz) - 1)
>> grub_le_to_cpu16 (data->sblock.log2_blksz);
for (i = pos / grub_le_to_cpu32 (data->sblock.blksz); i < blockcnt; i++)
for (i = pos >> grub_le_to_cpu16 (data->sblock.log2_blksz); i < blockcnt; i++)
{
int blknr;
int blockoff = pos % grub_le_to_cpu32 (data->sblock.blksz);
int blockend = grub_le_to_cpu32 (data->sblock.blksz);
grub_disk_addr_t blknr;
grub_uint32_t blockoff = pos & (grub_le_to_cpu32 (data->sblock.blksz) - 1);
grub_uint32_t blockend = grub_le_to_cpu32 (data->sblock.blksz);
int skipfirst = 0;
grub_uint64_t skipfirst = 0;
blknr = grub_jfs_blkno (data, &data->currinode, i);
if (grub_errno)
@ -563,14 +581,14 @@ grub_jfs_read_file (struct grub_jfs_data *data,
/* Last block. */
if (i == blockcnt - 1)
{
blockend = (len + pos) % grub_le_to_cpu32 (data->sblock.blksz);
blockend = (len + pos) & (grub_le_to_cpu32 (data->sblock.blksz) - 1);
if (!blockend)
blockend = grub_le_to_cpu32 (data->sblock.blksz);
}
/* First block. */
if (i == (pos / (int) grub_le_to_cpu32 (data->sblock.blksz)))
if (i == (pos >> grub_le_to_cpu16 (data->sblock.log2_blksz)))
{
skipfirst = blockoff;
blockend -= skipfirst;
@ -596,7 +614,8 @@ grub_jfs_read_file (struct grub_jfs_data *data,
/* Find the file with the pathname PATH on the filesystem described by
DATA. */
static grub_err_t
grub_jfs_find_file (struct grub_jfs_data *data, const char *path)
grub_jfs_find_file (struct grub_jfs_data *data, const char *path,
grub_uint32_t start_ino)
{
char fpath[grub_strlen (path)];
char *name = fpath;
@ -605,7 +624,7 @@ grub_jfs_find_file (struct grub_jfs_data *data, const char *path)
grub_strncpy (fpath, path, grub_strlen (path) + 1);
if (grub_jfs_read_inode (data, GRUB_JFS_AGGR_INODE, &data->currinode))
if (grub_jfs_read_inode (data, start_ino, &data->currinode))
return grub_errno;
/* Skip the first slashes. */
@ -642,8 +661,8 @@ grub_jfs_find_file (struct grub_jfs_data *data, const char *path)
pathname. */
if (!grub_strcmp (name, diro->name))
{
int ino = diro->ino;
int dirino = grub_le_to_cpu32 (data->currinode.inode);
grub_uint32_t ino = diro->ino;
grub_uint32_t dirino = grub_le_to_cpu32 (data->currinode.inode);
grub_jfs_closedir (diro);
diro = 0;
@ -681,15 +700,15 @@ grub_jfs_find_file (struct grub_jfs_data *data, const char *path)
}
grub_jfs_closedir (diro);
grub_error (GRUB_ERR_FILE_NOT_FOUND, "file not found");
grub_error (GRUB_ERR_FILE_NOT_FOUND, "file `%s' not found", path);
return grub_errno;
}
static grub_err_t
grub_jfs_lookup_symlink (struct grub_jfs_data *data, int ino)
grub_jfs_lookup_symlink (struct grub_jfs_data *data, grub_uint32_t ino)
{
int size = grub_le_to_cpu64 (data->currinode.size);
grub_uint64_t size = grub_le_to_cpu64 (data->currinode.size);
char symlink[size + 1];
if (++data->linknest > GRUB_JFS_MAX_SYMLNK_CNT)
@ -706,11 +725,7 @@ grub_jfs_lookup_symlink (struct grub_jfs_data *data, int ino)
if (symlink[0] == '/')
ino = 2;
/* Now load in the old inode. */
if (grub_jfs_read_inode (data, ino, &data->currinode))
return grub_errno;
grub_jfs_find_file (data, symlink);
grub_jfs_find_file (data, symlink, ino);
if (grub_errno)
grub_error (grub_errno, "cannot follow symlink `%s'", symlink);
@ -732,7 +747,7 @@ grub_jfs_dir (grub_device_t device, const char *path,
if (!data)
goto fail;
if (grub_jfs_find_file (data, path))
if (grub_jfs_find_file (data, path, GRUB_JFS_AGGR_INODE))
goto fail;
diro = grub_jfs_opendir (data, &data->currinode);
@ -751,6 +766,8 @@ grub_jfs_dir (grub_device_t device, const char *path,
info.dir = (grub_le_to_cpu32 (inode.mode)
& GRUB_JFS_FILETYPE_MASK) == GRUB_JFS_FILETYPE_DIR;
info.mtimeset = 1;
info.mtime = grub_le_to_cpu32 (inode.mtime.sec);
if (hook (diro->name, &info))
goto fail;
}
@ -781,7 +798,7 @@ grub_jfs_open (struct grub_file *file, const char *name)
if (!data)
goto fail;
grub_jfs_find_file (data, name);
grub_jfs_find_file (data, name, GRUB_JFS_AGGR_INODE);
if (grub_errno)
goto fail;

View file

@ -25,14 +25,19 @@
#include <grub/dl.h>
#include <grub/types.h>
#ifdef MODE_MINIX2
GRUB_MOD_LICENSE ("GPLv3+");
#ifdef MODE_MINIX3
#define GRUB_MINIX_MAGIC 0x4D5A
#elif defined(MODE_MINIX2)
#define GRUB_MINIX_MAGIC 0x2468
#define GRUB_MINIX_MAGIC_30 0x2478
#else
#define GRUB_MINIX_MAGIC 0x137F
#define GRUB_MINIX_MAGIC_30 0x138F
#endif
#define GRUB_MINIX_BSIZE 1024U
#define GRUB_MINIX_INODE_DIR_BLOCKS 7
#define GRUB_MINIX_LOG2_BSIZE 1
#define GRUB_MINIX_ROOT_INODE 1
#define GRUB_MINIX_MAX_SYMLNK_CNT 8
@ -41,7 +46,7 @@
#define GRUB_MINIX_IFDIR 0040000U
#define GRUB_MINIX_IFLNK 0120000U
#ifdef MODE_MINIX2
#if defined(MODE_MINIX2) || defined(MODE_MINIX3)
typedef grub_uint32_t grub_minix_uintn_t;
#define grub_minix_le_to_cpu_n grub_le_to_cpu32
#else
@ -50,8 +55,15 @@ typedef grub_uint16_t grub_minix_uintn_t;
#endif
#define GRUB_MINIX_INODE_BLKSZ(data) sizeof (grub_minix_uintn_t)
#ifdef MODE_MINIX3
typedef grub_uint32_t grub_minix_ino_t;
#define grub_minix_le_to_cpu_ino grub_le_to_cpu32
#else
typedef grub_uint16_t grub_minix_ino_t;
#define grub_minix_le_to_cpu_ino grub_le_to_cpu16
#endif
#define GRUB_MINIX_INODE_SIZE(data) (grub_minix_le_to_cpu_n (data->inode.size))
#define GRUB_MINIX_INODE_SIZE(data) (grub_le_to_cpu32 (data->inode.size))
#define GRUB_MINIX_INODE_MODE(data) (grub_le_to_cpu16 (data->inode.mode))
#define GRUB_MINIX_INODE_DIR_ZONES(data,blk) (grub_minix_le_to_cpu_n \
(data->inode.dir_zones[blk]))
@ -60,11 +72,39 @@ typedef grub_uint16_t grub_minix_uintn_t;
#define GRUB_MINIX_INODE_DINDIR_ZONE(data) (grub_minix_le_to_cpu_n \
(data->inode.double_indir_zone))
#ifndef MODE_MINIX3
#define GRUB_MINIX_LOG2_ZONESZ (GRUB_MINIX_LOG2_BSIZE \
+ grub_le_to_cpu16 (sblock->log2_zone_size))
#define GRUB_MINIX_ZONESZ (GRUB_MINIX_BSIZE \
<< grub_le_to_cpu16 (sblock->log2_zone_size))
+ grub_le_to_cpu16 (data->sblock.log2_zone_size))
#endif
#define GRUB_MINIX_ZONESZ (data->block_size \
<< grub_le_to_cpu16 (data->sblock.log2_zone_size))
#ifdef MODE_MINIX3
#define GRUB_MINIX_ZONE2SECT(zone) ((zone) * (data->block_size / GRUB_DISK_SECTOR_SIZE))
#else
#define GRUB_MINIX_ZONE2SECT(zone) ((zone) << GRUB_MINIX_LOG2_ZONESZ)
#endif
#ifdef MODE_MINIX3
struct grub_minix_sblock
{
grub_uint32_t inode_cnt;
grub_uint16_t zone_cnt;
grub_uint16_t inode_bmap_size;
grub_uint16_t zone_bmap_size;
grub_uint16_t first_data_zone;
grub_uint16_t log2_zone_size;
grub_uint16_t pad;
grub_uint32_t max_file_size;
grub_uint32_t zones;
grub_uint16_t magic;
grub_uint16_t pad2;
grub_uint16_t block_size;
grub_uint8_t disk_version;
};
#else
struct grub_minix_sblock
{
grub_uint16_t inode_cnt;
@ -76,23 +116,9 @@ struct grub_minix_sblock
grub_uint32_t max_file_size;
grub_uint16_t magic;
};
#endif
#ifndef MODE_MINIX2
struct grub_minix_inode
{
grub_uint16_t mode;
grub_uint16_t uid;
grub_uint16_t size;
grub_uint32_t ctime;
grub_uint8_t gid;
grub_uint8_t nlinks;
grub_uint16_t dir_zones[7];
grub_uint16_t indir_zone;
grub_uint16_t double_indir_zone;
};
#else
#if defined(MODE_MINIX3) || defined(MODE_MINIX2)
struct grub_minix_inode
{
grub_uint16_t mode;
@ -106,8 +132,20 @@ struct grub_minix_inode
grub_uint32_t dir_zones[7];
grub_uint32_t indir_zone;
grub_uint32_t double_indir_zone;
grub_uint32_t unused;
grub_uint32_t triple_indir_zone;
};
#else
struct grub_minix_inode
{
grub_uint16_t mode;
grub_uint16_t uid;
grub_uint32_t size;
grub_uint32_t mtime;
grub_uint8_t gid;
grub_uint8_t nlinks;
grub_uint16_t dir_zones[7];
grub_uint16_t indir_zone;
grub_uint16_t double_indir_zone;
};
#endif
@ -121,6 +159,7 @@ struct grub_minix_data
int linknest;
grub_disk_t disk;
int filename_size;
grub_size_t block_size;
};
static grub_dl_t my_mod;
@ -128,50 +167,66 @@ static grub_dl_t my_mod;
static grub_err_t grub_minix_find_file (struct grub_minix_data *data,
const char *path);
static int
static grub_minix_uintn_t
grub_minix_get_file_block (struct grub_minix_data *data, unsigned int blk)
{
struct grub_minix_sblock *sblock = &data->sblock;
int indir;
grub_minix_uintn_t indir;
const grub_uint32_t block_per_zone = (GRUB_MINIX_ZONESZ
/ GRUB_MINIX_INODE_BLKSZ (data));
auto int grub_get_indir (int, int);
auto grub_minix_uintn_t grub_get_indir (grub_minix_uintn_t,
grub_minix_uintn_t);
/* Read the block pointer in ZONE, on the offset NUM. */
int grub_get_indir (int zone, int num)
grub_minix_uintn_t grub_get_indir (grub_minix_uintn_t zone,
grub_minix_uintn_t num)
{
grub_minix_uintn_t indirn;
grub_disk_read (data->disk,
zone << GRUB_MINIX_LOG2_ZONESZ,
GRUB_MINIX_ZONE2SECT(zone),
sizeof (grub_minix_uintn_t) * num,
sizeof (grub_minix_uintn_t), (char *) &indirn);
return grub_minix_le_to_cpu_n (indirn);
}
/* Direct block. */
if (blk < 7)
if (blk < GRUB_MINIX_INODE_DIR_BLOCKS)
return GRUB_MINIX_INODE_DIR_ZONES (data, blk);
/* Indirect block. */
blk -= 7;
if (blk < GRUB_MINIX_ZONESZ / GRUB_MINIX_INODE_BLKSZ (data))
blk -= GRUB_MINIX_INODE_DIR_BLOCKS;
if (blk < block_per_zone)
{
indir = grub_get_indir (GRUB_MINIX_INODE_INDIR_ZONE (data), blk);
return indir;
}
/* Double indirect block. */
blk -= GRUB_MINIX_ZONESZ / GRUB_MINIX_INODE_BLKSZ (data);
if (blk < (GRUB_MINIX_ZONESZ / GRUB_MINIX_INODE_BLKSZ (data))
* (GRUB_MINIX_ZONESZ / GRUB_MINIX_INODE_BLKSZ (data)))
blk -= block_per_zone;
if (blk < block_per_zone * block_per_zone)
{
indir = grub_get_indir (GRUB_MINIX_INODE_DINDIR_ZONE (data),
blk / GRUB_MINIX_ZONESZ);
blk / block_per_zone);
indir = grub_get_indir (indir, blk % GRUB_MINIX_ZONESZ);
indir = grub_get_indir (indir, blk % block_per_zone);
return indir;
}
#if defined (MODE_MINIX3) || defined (MODE_MINIX2)
blk -= block_per_zone * block_per_zone;
if (blk < ((grub_uint64_t) block_per_zone * (grub_uint64_t) block_per_zone
* (grub_uint64_t) block_per_zone))
{
indir = grub_get_indir (grub_minix_le_to_cpu_n (data->inode.triple_indir_zone),
(blk / block_per_zone) / block_per_zone);
indir = grub_get_indir (indir, (blk / block_per_zone) % block_per_zone);
indir = grub_get_indir (indir, blk % block_per_zone);
return indir;
}
#endif
/* This should never happen. */
grub_error (GRUB_ERR_OUT_OF_RANGE, "file bigger than maximum size");
@ -185,25 +240,26 @@ static grub_ssize_t
grub_minix_read_file (struct grub_minix_data *data,
void NESTED_FUNC_ATTR (*read_hook) (grub_disk_addr_t sector,
unsigned offset, unsigned length),
int pos, grub_disk_addr_t len, char *buf)
grub_off_t pos, grub_disk_addr_t len, char *buf)
{
struct grub_minix_sblock *sblock = &data->sblock;
int i;
int blockcnt;
grub_disk_addr_t i;
grub_disk_addr_t blockcnt;
grub_uint64_t posblock;
grub_uint64_t blockoff;
/* Adjust len so it we can't read past the end of the file. */
if (len + pos > GRUB_MINIX_INODE_SIZE (data))
len = GRUB_MINIX_INODE_SIZE (data) - pos;
blockcnt = (len + pos + GRUB_MINIX_BSIZE - 1) / GRUB_MINIX_BSIZE;
blockcnt = grub_divmod64 ((len + pos + data->block_size - 1),
data->block_size, 0);
posblock = grub_divmod64 (pos, data->block_size, &blockoff);
for (i = pos / GRUB_MINIX_BSIZE; i < blockcnt; i++)
for (i = posblock; i < blockcnt; i++)
{
int blknr;
int blockoff = pos % GRUB_MINIX_BSIZE;
int blockend = GRUB_MINIX_BSIZE;
int skipfirst = 0;
grub_disk_addr_t blknr;
grub_uint64_t blockend = data->block_size;
grub_off_t skipfirst = 0;
blknr = grub_minix_get_file_block (data, i);
if (grub_errno)
@ -212,28 +268,28 @@ grub_minix_read_file (struct grub_minix_data *data,
/* Last block. */
if (i == blockcnt - 1)
{
blockend = (len + pos) % GRUB_MINIX_BSIZE;
grub_divmod64 (len + pos, data->block_size, &blockend);
if (!blockend)
blockend = GRUB_MINIX_BSIZE;
blockend = data->block_size;
}
/* First block. */
if (i == (pos / (int) GRUB_MINIX_BSIZE))
if (i == posblock)
{
skipfirst = blockoff;
blockend -= skipfirst;
}
data->disk->read_hook = read_hook;
grub_disk_read (data->disk, blknr << GRUB_MINIX_LOG2_ZONESZ,
grub_disk_read (data->disk,
GRUB_MINIX_ZONE2SECT(blknr),
skipfirst, blockend, buf);
data->disk->read_hook = 0;
if (grub_errno)
return -1;
buf += GRUB_MINIX_BSIZE - skipfirst;
buf += data->block_size - skipfirst;
}
return len;
@ -248,16 +304,13 @@ grub_minix_read_inode (struct grub_minix_data *data, int ino)
struct grub_minix_sblock *sblock = &data->sblock;
/* Block in which the inode is stored. */
int block;
grub_disk_addr_t block;
data->ino = ino;
/* The first inode in minix is inode 1. */
ino--;
block = ((2 + grub_le_to_cpu16 (sblock->inode_bmap_size)
+ grub_le_to_cpu16 (sblock->zone_bmap_size))
<< GRUB_MINIX_LOG2_BSIZE);
block = GRUB_MINIX_ZONE2SECT (2 + grub_le_to_cpu16 (sblock->inode_bmap_size)
+ grub_le_to_cpu16 (sblock->zone_bmap_size));
block += ino / (GRUB_DISK_SECTOR_SIZE / sizeof (struct grub_minix_inode));
int offs = (ino % (GRUB_DISK_SECTOR_SIZE
/ sizeof (struct grub_minix_inode))
@ -333,7 +386,7 @@ grub_minix_find_file (struct grub_minix_data *data, const char *path)
do
{
grub_uint16_t ino;
grub_minix_ino_t ino;
char filename[data->filename_size + 1];
if (grub_strlen (name) == 0)
@ -353,7 +406,7 @@ grub_minix_find_file (struct grub_minix_data *data, const char *path)
if (!grub_strcmp (name, filename))
{
dirino = data->ino;
grub_minix_read_inode (data, grub_le_to_cpu16 (ino));
grub_minix_read_inode (data, grub_minix_le_to_cpu_ino (ino));
/* Follow the symlink. */
if ((GRUB_MINIX_INODE_MODE (data)
@ -387,7 +440,7 @@ grub_minix_find_file (struct grub_minix_data *data, const char *path)
pos += sizeof (ino) + data->filename_size;
} while (pos < GRUB_MINIX_INODE_SIZE (data));
grub_error (GRUB_ERR_FILE_NOT_FOUND, "file not found");
grub_error (GRUB_ERR_FILE_NOT_FOUND, "file `%s' not found", path);
return grub_errno;
}
@ -409,20 +462,35 @@ grub_minix_mount (grub_disk_t disk)
goto fail;
if (grub_le_to_cpu16 (data->sblock.magic) == GRUB_MINIX_MAGIC)
{
#if !defined(MODE_MINIX3)
data->filename_size = 14;
#else
data->filename_size = 60;
#endif
}
#if !defined(MODE_MINIX3)
else if (grub_le_to_cpu16 (data->sblock.magic) == GRUB_MINIX_MAGIC_30)
data->filename_size = 30;
#endif
else
goto fail;
data->disk = disk;
data->linknest = 0;
#ifdef MODE_MINIX3
data->block_size = grub_le_to_cpu16 (data->sblock.block_size);
#else
data->block_size = 1024U;
#endif
return data;
fail:
grub_free (data);
#ifdef MODE_MINIX2
#if defined(MODE_MINIX3)
grub_error (GRUB_ERR_BAD_FS, "not a minix3 filesystem");
#elif defined(MODE_MINIX2)
grub_error (GRUB_ERR_BAD_FS, "not a minix2 filesystem");
#else
grub_error (GRUB_ERR_BAD_FS, "not a minix filesystem");
@ -458,7 +526,7 @@ grub_minix_dir (grub_device_t device, const char *path,
while (pos < GRUB_MINIX_INODE_SIZE (data))
{
grub_uint16_t ino;
grub_minix_ino_t ino;
char filename[data->filename_size + 1];
int dirino = data->ino;
struct grub_dirhook_info info;
@ -474,12 +542,18 @@ grub_minix_dir (grub_device_t device, const char *path,
(char *) filename) < 0)
return grub_errno;
filename[data->filename_size] = '\0';
if (!ino)
{
pos += sizeof (ino) + data->filename_size;
continue;
}
/* The filetype is not stored in the dirent. Read the inode to
find out the filetype. This *REALLY* sucks. */
grub_minix_read_inode (data, grub_le_to_cpu16 (ino));
grub_minix_read_inode (data, grub_minix_le_to_cpu_ino (ino));
info.dir = ((GRUB_MINIX_INODE_MODE (data)
& GRUB_MINIX_IFDIR) == GRUB_MINIX_IFDIR);
info.mtimeset = 1;
info.mtime = grub_le_to_cpu32 (data->inode.mtime);
if (hook (filename, &info) ? 1 : 0)
break;
@ -556,7 +630,9 @@ grub_minix_close (grub_file_t file)
static struct grub_fs grub_minix_fs =
{
#ifdef MODE_MINIX2
#if defined(MODE_MINIX3)
.name = "minix3",
#elif defined(MODE_MINIX2)
.name = "minix2",
#else
.name = "minix",
@ -568,7 +644,9 @@ static struct grub_fs grub_minix_fs =
.next = 0
};
#ifdef MODE_MINIX2
#if defined(MODE_MINIX3)
GRUB_MOD_INIT(minix3)
#elif defined(MODE_MINIX2)
GRUB_MOD_INIT(minix2)
#else
GRUB_MOD_INIT(minix)
@ -578,7 +656,9 @@ GRUB_MOD_INIT(minix)
my_mod = mod;
}
#ifdef MODE_MINIX2
#if defined(MODE_MINIX3)
GRUB_MOD_FINI(minix3)
#elif defined(MODE_MINIX2)
GRUB_MOD_FINI(minix2)
#else
GRUB_MOD_FINI(minix)

2
grub-core/fs/minix3.c Normal file
View file

@ -0,0 +1,2 @@
#define MODE_MINIX3 1
#include "minix.c"

View file

@ -35,6 +35,8 @@
#include <grub/types.h>
#include <grub/fshelp.h>
GRUB_MOD_LICENSE ("GPLv3+");
#define NILFS_INODE_BMAP_SIZE 7
#define NILFS_SUPORT_REV 2
@ -301,7 +303,7 @@ grub_nilfs2_palloc_entries_per_group (struct grub_nilfs2_data *data)
static inline grub_uint64_t
grub_nilfs2_palloc_group (struct grub_nilfs2_data *data,
grub_uint64_t nr, grub_uint32_t * offset)
grub_uint64_t nr, grub_uint64_t * offset)
{
return grub_divmod64 (nr, grub_nilfs2_palloc_entries_per_group (data),
offset);
@ -366,13 +368,15 @@ grub_nilfs2_palloc_entry_offset (struct grub_nilfs2_data *data,
grub_uint64_t nr, unsigned long entry_size)
{
unsigned long group;
grub_uint32_t group_offset;
grub_uint64_t group_offset;
group = grub_nilfs2_palloc_group (data, nr, &group_offset);
return grub_nilfs2_palloc_bitmap_block_offset (data, group,
entry_size) + 1 +
group_offset / grub_nilfs2_entries_per_block (data, entry_size);
grub_divmod64 (group_offset, grub_nilfs2_entries_per_block (data,
entry_size),
NULL);
}
@ -575,7 +579,7 @@ grub_nilfs2_dat_translate (struct grub_nilfs2_data *data, grub_uint64_t key)
struct grub_nilfs2_dat_entry entry;
grub_disk_t disk = data->disk;
grub_uint64_t pptr;
grub_uint32_t blockno, offset;
grub_uint64_t blockno, offset;
unsigned int nilfs2_block_count = (1 << LOG2_NILFS2_BLOCK_SIZE (data));
blockno = grub_nilfs2_palloc_entry_offset (data, key,
@ -624,7 +628,7 @@ grub_nilfs2_read_file (grub_fshelp_node_t node,
sector,
unsigned offset,
unsigned length),
int pos, grub_size_t len, char *buf)
grub_off_t pos, grub_size_t len, char *buf)
{
return grub_fshelp_read_file (node->data->disk, node, read_hook,
pos, len, buf, grub_nilfs2_read_block,
@ -639,7 +643,7 @@ grub_nilfs2_read_checkpoint (struct grub_nilfs2_data *data,
struct grub_nilfs2_checkpoint *cpp)
{
grub_uint64_t blockno;
grub_uint32_t offset;
grub_uint64_t offset;
grub_uint64_t pptr;
grub_disk_t disk = data->disk;
unsigned int nilfs2_block_count = (1 << LOG2_NILFS2_BLOCK_SIZE (data));
@ -677,7 +681,7 @@ grub_nilfs2_read_inode (struct grub_nilfs2_data *data,
grub_uint64_t ino, struct grub_nilfs2_inode *inodep)
{
grub_uint64_t blockno;
unsigned int offset;
grub_uint64_t offset;
grub_uint64_t pptr;
grub_disk_t disk = data->disk;
unsigned int nilfs2_block_count = (1 << LOG2_NILFS2_BLOCK_SIZE (data));
@ -862,7 +866,7 @@ grub_nilfs2_iterate_dir (grub_fshelp_node_t dir,
enum grub_fshelp_filetype filetype,
grub_fshelp_node_t node))
{
unsigned int fpos = 0;
grub_off_t fpos = 0;
struct grub_fshelp_node *diro = (struct grub_fshelp_node *) dir;
if (!diro->inode_read)

View file

@ -26,6 +26,8 @@
#include <grub/ntfs.h>
#include <grub/charset.h>
GRUB_MOD_LICENSE ("GPLv3+");
static grub_dl_t my_mod;
ntfscomp_func_t grub_ntfscomp_func;
@ -429,7 +431,7 @@ read_data (struct grub_ntfs_attr *at, char *pa, char *dest,
if (at->flags & AF_GPOS)
{
grub_disk_addr_t st0, st1;
grub_uint32_t m;
grub_uint64_t m;
grub_divmod64 (ofs >> BLK_SHR, ctx->comp.spc, &m);
@ -610,6 +612,10 @@ list_file (struct grub_ntfs_file *diro, char *pos,
fdiro->data = diro->data;
fdiro->ino = u32at (pos, 0);
if (u64at (pos, 0x20) > u64at (pos, 0x28))
fdiro->mtime = u64at (pos, 0x20);
else
fdiro->mtime = u64at (pos, 0x28);
ustr = grub_malloc (ns * 4 + 1);
if (ustr == NULL)
@ -880,6 +886,10 @@ grub_ntfs_dir (grub_device_t device, const char *path,
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);
grub_free (node);
return hook (filename, &info);
}
@ -1077,14 +1087,15 @@ grub_ntfs_uuid (grub_device_t device, char **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);
grub_free (data);
return grub_errno;
}

View file

@ -24,6 +24,8 @@
#include <grub/fshelp.h>
#include <grub/ntfs.h>
GRUB_MOD_LICENSE ("GPLv3+");
static grub_err_t
decomp_nextvcn (struct grub_ntfs_comp *cc)
{

View file

@ -39,6 +39,8 @@
#include <grub/types.h>
#include <grub/fshelp.h>
GRUB_MOD_LICENSE ("GPLv3+");
#define MIN(a, b) \
({ typeof (a) _a = (a); \
typeof (b) _b = (b); \
@ -222,6 +224,7 @@ struct grub_fshelp_node
grub_uint32_t block_number; /* 0 if node is not found. */
grub_uint16_t block_position;
grub_uint64_t next_offset;
grub_int32_t mtime;
enum grub_reiserfs_item_type type; /* To know how to read the header. */
struct grub_reiserfs_item_header header;
};
@ -868,6 +871,7 @@ grub_reiserfs_iterate_dir (grub_fshelp_node_t item,
entry_v1_stat.rdev,
entry_v1_stat.first_direct_byte);
#endif
entry_item->mtime = grub_le_to_cpu32 (entry_v1_stat.mtime);
if ((grub_le_to_cpu16 (entry_v1_stat.mode) & S_IFLNK)
== S_IFLNK)
entry_type = GRUB_FSHELP_SYMLINK;
@ -914,6 +918,7 @@ grub_reiserfs_iterate_dir (grub_fshelp_node_t item,
entry_v2_stat.blocks,
entry_v2_stat.first_direct_byte);
#endif
entry_item->mtime = grub_le_to_cpu32 (entry_v2_stat.mtime);
if ((grub_le_to_cpu16 (entry_v2_stat.mode) & S_IFLNK)
== S_IFLNK)
entry_type = GRUB_FSHELP_SYMLINK;
@ -1276,6 +1281,8 @@ grub_reiserfs_dir (grub_device_t device, const char *path,
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 = node->mtime;
grub_free (node);
return hook (filename, &info);
}

461
grub-core/fs/romfs.c Normal file
View file

@ -0,0 +1,461 @@
/*
* GRUB -- GRand Unified Bootloader
* Copyright (C) 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/file.h>
#include <grub/types.h>
#include <grub/dl.h>
#include <grub/mm.h>
#include <grub/disk.h>
#include <grub/fs.h>
#include <grub/fshelp.h>
GRUB_MOD_LICENSE ("GPLv3+");
struct grub_romfs_superblock
{
char magic[8];
#define GRUB_ROMFS_MAGIC "-rom1fs-"
grub_uint32_t total_size;
grub_uint32_t chksum;
char label[0];
};
struct grub_romfs_file_header
{
grub_uint32_t next_file;
grub_uint32_t spec;
grub_uint32_t size;
grub_uint32_t chksum;
char name[0];
};
struct grub_romfs_data
{
grub_disk_addr_t first_file;
grub_disk_t disk;
};
struct grub_fshelp_node
{
grub_disk_addr_t addr;
struct grub_romfs_data *data;
grub_disk_addr_t data_addr;
/* Not filled for root. */
struct grub_romfs_file_header file;
};
#define GRUB_ROMFS_ALIGN 16
#define GRUB_ROMFS_TYPE_MASK 7
#define GRUB_ROMFS_TYPE_HARDLINK 0
#define GRUB_ROMFS_TYPE_DIRECTORY 1
#define GRUB_ROMFS_TYPE_REGULAR 2
#define GRUB_ROMFS_TYPE_SYMLINK 3
static grub_err_t
do_checksum (void *in, grub_size_t insize)
{
grub_uint32_t *a = in;
grub_size_t sz = insize / 4;
grub_uint32_t *b = a + sz;
grub_uint32_t csum = 0;
while (a < b)
csum += grub_be_to_cpu32 (*a++);
if (csum)
return grub_error (GRUB_ERR_BAD_FS, "invalid checksum");
return GRUB_ERR_NONE;
}
static struct grub_romfs_data *
grub_romfs_mount (grub_device_t dev)
{
union {
struct grub_romfs_superblock sb;
char d[512];
} sb;
grub_err_t err;
char *ptr;
grub_disk_addr_t sec = 0;
struct grub_romfs_data *data;
if (!dev->disk)
{
grub_error (GRUB_ERR_BAD_FS, "not a disk");
return NULL;
}
err = grub_disk_read (dev->disk, 0, 0, sizeof (sb), &sb);
if (err == GRUB_ERR_OUT_OF_RANGE)
err = grub_errno = GRUB_ERR_BAD_FS;
if (err)
return NULL;
if (grub_be_to_cpu32 (sb.sb.total_size) < sizeof (sb))
{
grub_error (GRUB_ERR_BAD_FS, "too short filesystem");
return NULL;
}
err = do_checksum (&sb, sizeof (sb) < grub_be_to_cpu32 (sb.sb.total_size) ?
sizeof (sb) : grub_be_to_cpu32 (sb.sb.total_size));
if (err)
return NULL;
for (ptr = sb.sb.label; (void *) ptr < (void *) (&sb + 1)
&& ptr < sb.d + grub_be_to_cpu32 (sb.sb.total_size); ptr++)
if (!*ptr)
break;
if ((void *) ptr == &sb + 1)
for (sec++; ; sec++)
{
err = grub_disk_read (dev->disk, sec, 0, sizeof (sb), &sb);
if (err == GRUB_ERR_OUT_OF_RANGE)
err = grub_errno = GRUB_ERR_BAD_FS;
if (err)
return NULL;
for (ptr = sb.d; (void *) ptr < (void *) (&sb + 1)
&& ptr < sb.d + grub_be_to_cpu32 (sb.sb.total_size); ptr++)
if (!*ptr)
break;
}
data = grub_malloc (sizeof (*data));
if (!data)
return NULL;
data->first_file = ALIGN_UP (ptr - sb.d, GRUB_ROMFS_ALIGN) + sec * 512;
data->disk = dev->disk;
return data;
}
static char *
grub_romfs_read_symlink (grub_fshelp_node_t node)
{
char *ret;
grub_err_t err;
ret = grub_malloc (grub_be_to_cpu32 (node->file.size) + 1);
if (!ret)
return NULL;
err = grub_disk_read (node->data->disk,
(node->data_addr) >> GRUB_DISK_SECTOR_BITS,
(node->data_addr) & (GRUB_DISK_SECTOR_SIZE - 1),
grub_be_to_cpu32 (node->file.size), ret);
if (err)
{
grub_free (ret);
return NULL;
}
ret[grub_be_to_cpu32 (node->file.size)] = 0;
return ret;
}
static int
grub_romfs_iterate_dir (grub_fshelp_node_t dir,
int NESTED_FUNC_ATTR
(*hook) (const char *filename,
enum grub_fshelp_filetype filetype,
grub_fshelp_node_t node))
{
grub_disk_addr_t caddr;
struct grub_romfs_file_header hdr;
grub_size_t a = 0;
char *name = NULL;
unsigned nptr;
unsigned i, j;
for (caddr = dir->data_addr; caddr;
caddr = grub_be_to_cpu32 (hdr.next_file) & ~(GRUB_ROMFS_ALIGN - 1))
{
grub_disk_addr_t naddr = caddr + sizeof (hdr);
grub_uint32_t csum = 0;
enum grub_fshelp_filetype filetype = GRUB_FSHELP_UNKNOWN;
struct grub_fshelp_node *node;
grub_err_t err;
err = grub_disk_read (dir->data->disk, caddr >> GRUB_DISK_SECTOR_BITS,
caddr & (GRUB_DISK_SECTOR_SIZE - 1),
sizeof (hdr), &hdr);
if (err)
{
grub_free (name);
return 1;
}
for (nptr = 0; ; nptr++, naddr += 16)
{
if (a >= nptr)
{
char *on;
a = 2 * (nptr + 1);
on = name;
name = grub_realloc (name, a * 16);
if (!name)
{
grub_free (on);
return 1;
}
}
err = grub_disk_read (dir->data->disk, naddr >> GRUB_DISK_SECTOR_BITS,
naddr & (GRUB_DISK_SECTOR_SIZE - 1),
16, name + 16 * nptr);
if (err)
return 1;
for (j = 0; j < 16; j++)
if (!name[16 * nptr + j])
break;
if (j != 16)
break;
}
for (i = 0; i < sizeof (hdr) / sizeof (grub_uint32_t); i++)
csum += grub_be_to_cpu32 (((grub_uint32_t *) &hdr)[i]);
for (i = 0; i < (nptr + 1) * 4; i++)
csum += grub_be_to_cpu32 (((grub_uint32_t *) name)[i]);
if (csum != 0)
{
grub_error (GRUB_ERR_BAD_FS, "invalid checksum");
grub_free (name);
return 1;
}
node = grub_malloc (sizeof (*node));
if (!node)
return 1;
node->addr = caddr;
node->data_addr = caddr + (nptr + 1) * 16 + sizeof (hdr);
node->data = dir->data;
node->file = hdr;
switch (grub_be_to_cpu32 (hdr.next_file) & GRUB_ROMFS_TYPE_MASK)
{
case GRUB_ROMFS_TYPE_REGULAR:
filetype = GRUB_FSHELP_REG;
break;
case GRUB_ROMFS_TYPE_SYMLINK:
filetype = GRUB_FSHELP_SYMLINK;
break;
case GRUB_ROMFS_TYPE_DIRECTORY:
node->data_addr = grub_be_to_cpu32 (hdr.spec);
filetype = GRUB_FSHELP_DIR;
break;
case GRUB_ROMFS_TYPE_HARDLINK:
{
grub_disk_addr_t laddr;
node->addr = laddr = grub_be_to_cpu32 (hdr.spec);
err = grub_disk_read (dir->data->disk,
laddr >> GRUB_DISK_SECTOR_BITS,
laddr & (GRUB_DISK_SECTOR_SIZE - 1),
sizeof (node->file), &node->file);
if (err)
return 1;
if ((grub_be_to_cpu32 (node->file.next_file) & GRUB_ROMFS_TYPE_MASK)
== GRUB_ROMFS_TYPE_REGULAR
|| (grub_be_to_cpu32 (node->file.next_file)
& GRUB_ROMFS_TYPE_MASK) == GRUB_ROMFS_TYPE_SYMLINK)
{
laddr += sizeof (hdr);
while (1)
{
char buf[16];
err = grub_disk_read (dir->data->disk,
laddr >> GRUB_DISK_SECTOR_BITS,
laddr & (GRUB_DISK_SECTOR_SIZE - 1),
16, buf);
if (err)
return 1;
for (i = 0; i < 16; i++)
if (!buf[i])
break;
if (i != 16)
break;
laddr += 16;
}
node->data_addr = laddr + 16;
}
if ((grub_be_to_cpu32 (node->file.next_file)
& GRUB_ROMFS_TYPE_MASK) == GRUB_ROMFS_TYPE_REGULAR)
filetype = GRUB_FSHELP_REG;
if ((grub_be_to_cpu32 (node->file.next_file)
& GRUB_ROMFS_TYPE_MASK) == GRUB_ROMFS_TYPE_SYMLINK)
filetype = GRUB_FSHELP_SYMLINK;
if ((grub_be_to_cpu32 (node->file.next_file) & GRUB_ROMFS_TYPE_MASK)
== GRUB_ROMFS_TYPE_DIRECTORY)
{
node->data_addr = grub_be_to_cpu32 (node->file.spec);
filetype = GRUB_FSHELP_DIR;
}
break;
}
}
if (hook (name, filetype, node))
{
grub_free (name);
return 1;
}
}
grub_free (name);
return 0;
}
static grub_err_t
grub_romfs_dir (grub_device_t device, const char *path,
int (*hook) (const char *filename,
const struct grub_dirhook_info *info))
{
struct grub_romfs_data *data = 0;
struct grub_fshelp_node *fdiro = 0, start;
auto int NESTED_FUNC_ATTR iterate (const char *filename,
enum grub_fshelp_filetype filetype,
grub_fshelp_node_t node);
int NESTED_FUNC_ATTR iterate (const char *filename,
enum grub_fshelp_filetype filetype,
grub_fshelp_node_t node)
{
struct grub_dirhook_info info;
grub_memset (&info, 0, sizeof (info));
info.dir = ((filetype & GRUB_FSHELP_TYPE_MASK) == GRUB_FSHELP_DIR);
grub_free (node);
return hook (filename, &info);
}
data = grub_romfs_mount (device);
if (! data)
goto fail;
start.addr = data->first_file;
start.data_addr = data->first_file;
start.data = data;
grub_fshelp_find_file (path, &start, &fdiro, grub_romfs_iterate_dir,
grub_romfs_read_symlink, GRUB_FSHELP_DIR);
if (grub_errno)
goto fail;
grub_romfs_iterate_dir (fdiro, iterate);
fail:
grub_free (data);
return grub_errno;
}
static grub_err_t
grub_romfs_open (struct grub_file *file, const char *name)
{
struct grub_romfs_data *data = 0;
struct grub_fshelp_node *fdiro = 0, start;
data = grub_romfs_mount (file->device);
if (! data)
goto fail;
start.addr = data->first_file;
start.data_addr = data->first_file;
start.data = data;
grub_fshelp_find_file (name, &start, &fdiro, grub_romfs_iterate_dir,
grub_romfs_read_symlink, GRUB_FSHELP_REG);
if (grub_errno)
goto fail;
file->size = grub_be_to_cpu32 (fdiro->file.size);
file->data = fdiro;
return GRUB_ERR_NONE;
fail:
grub_free (data);
return grub_errno;
}
static grub_ssize_t
grub_romfs_read (grub_file_t file, char *buf, grub_size_t len)
{
struct grub_fshelp_node *data = file->data;
/* XXX: The file is stored in as a single extent. */
data->data->disk->read_hook = file->read_hook;
grub_disk_read (data->data->disk,
(data->data_addr + file->offset) >> GRUB_DISK_SECTOR_BITS,
(data->data_addr + file->offset) & (GRUB_DISK_SECTOR_SIZE - 1),
len, buf);
data->data->disk->read_hook = NULL;
if (grub_errno)
return -1;
return len;
}
static grub_err_t
grub_romfs_close (grub_file_t file)
{
grub_free (file->data);
return GRUB_ERR_NONE;
}
static grub_err_t
grub_romfs_label (grub_device_t device, char **label)
{
struct grub_romfs_data *data;
grub_err_t err;
*label = NULL;
data = grub_romfs_mount (device);
if (!data)
return grub_errno;
*label = grub_malloc (data->first_file + 1
- sizeof (struct grub_romfs_superblock));
if (!*label)
{
grub_free (data);
return grub_errno;
}
err = grub_disk_read (device->disk, 0, sizeof (struct grub_romfs_superblock),
data->first_file
- sizeof (struct grub_romfs_superblock),
*label);
if (err)
{
grub_free (data);
grub_free (*label);
*label = NULL;
return err;
}
(*label)[data->first_file - sizeof (struct grub_romfs_superblock)] = 0;
return GRUB_ERR_NONE;
}
static struct grub_fs grub_romfs_fs =
{
.name = "romfs",
.dir = grub_romfs_dir,
.open = grub_romfs_open,
.read = grub_romfs_read,
.close = grub_romfs_close,
.label = grub_romfs_label,
#ifdef GRUB_UTIL
.reserved_first_sector = 0,
#endif
.next = 0
};
GRUB_MOD_INIT(romfs)
{
grub_fs_register (&grub_romfs_fs);
}
GRUB_MOD_FINI(romfs)
{
grub_fs_unregister (&grub_romfs_fs);
}

View file

@ -26,6 +26,8 @@
#include <grub/types.h>
#include <grub/fshelp.h>
GRUB_MOD_LICENSE ("GPLv3+");
/* The common header for a block. */
struct grub_sfs_bheader
{
@ -66,7 +68,7 @@ struct grub_sfs_obj
grub_uint32_t dir_objc;
} dir __attribute__ ((packed));
} file_dir;
grub_uint8_t unused3[4];
grub_uint32_t mtime;
grub_uint8_t type;
grub_uint8_t filename[1];
grub_uint8_t comment[1];
@ -119,6 +121,7 @@ struct grub_fshelp_node
struct grub_sfs_data *data;
int block;
int size;
grub_uint32_t mtime;
};
/* Information about a "mounted" sfs filesystem. */
@ -247,7 +250,7 @@ static grub_ssize_t
grub_sfs_read_file (grub_fshelp_node_t node,
void NESTED_FUNC_ATTR (*read_hook) (grub_disk_addr_t sector,
unsigned offset, unsigned length),
int pos, grub_size_t len, char *buf)
grub_off_t pos, grub_size_t len, char *buf)
{
return grub_fshelp_read_file (node->data->disk, node, read_hook,
pos, len, buf, grub_sfs_read_block,
@ -355,10 +358,12 @@ grub_sfs_iterate_dir (grub_fshelp_node_t dir,
int pos;
auto int NESTED_FUNC_ATTR grub_sfs_create_node (const char *name, int block,
int size, int type);
int size, int type,
grub_uint32_t mtime);
int NESTED_FUNC_ATTR grub_sfs_create_node (const char *name, int block,
int size, int type)
int size, int type,
grub_uint32_t mtime)
{
node = grub_malloc (sizeof (*node));
if (!node)
@ -367,6 +372,7 @@ grub_sfs_iterate_dir (grub_fshelp_node_t dir,
node->data = data;
node->size = size;
node->block = block;
node->mtime = mtime;
return hook (name, type, node);
}
@ -426,7 +432,7 @@ grub_sfs_iterate_dir (grub_fshelp_node_t dir,
if (grub_sfs_create_node (filename, block,
grub_be_to_cpu32 (obj->file_dir.file.size),
type))
type, grub_be_to_cpu32 (obj->mtime)))
{
grub_free (objc_data);
return 1;
@ -525,6 +531,8 @@ grub_sfs_dir (grub_device_t device, const char *path,
struct grub_dirhook_info info;
grub_memset (&info, 0, sizeof (info));
info.dir = ((filetype & GRUB_FSHELP_TYPE_MASK) == GRUB_FSHELP_DIR);
info.mtime = node->mtime + 8 * 365 * 86400 + 86400 * 2;
info.mtimeset = 1;
grub_free (node);
return hook (filename, &info);
}
@ -579,6 +587,9 @@ static struct grub_fs grub_sfs_fs =
.read = grub_sfs_read,
.close = grub_sfs_close,
.label = grub_sfs_label,
#ifdef GRUB_UTIL
.reserved_first_sector = 0,
#endif
.next = 0
};

700
grub-core/fs/squash4.c Normal file
View file

@ -0,0 +1,700 @@
/* squash4.c - SquashFS */
/*
* GRUB -- GRand Unified Bootloader
* Copyright (C) 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/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>
#include <grub/deflate.h>
GRUB_MOD_LICENSE ("GPLv3+");
/*
object format Pointed by
superblock RAW Fixed offset (0)
data RAW ? Fixed offset (60)
inode table Chunk superblock
dir table Chunk superblock
fragment table Chunk unk1
unk1 RAW, Chunk superblock
unk2 RAW superblock
UID/GID Chunk exttblptr
exttblptr RAW superblock
UID/GID table is the array ot uint32_t
unk1 contains pointer to fragment table followed by some chunk.
unk2 containts one uint64_t
*/
struct grub_squash_super
{
grub_uint32_t magic;
#define SQUASH_MAGIC 0x73717368
grub_uint32_t dummy1;
grub_uint32_t creation_time;
grub_uint32_t block_size;
grub_uint64_t dummy3;
grub_uint64_t dummy4;
grub_uint16_t root_ino_offset;
grub_uint32_t root_ino_chunk;
grub_uint16_t dummy5;
grub_uint64_t total_size;
grub_uint64_t exttbloffset;
grub_uint64_t dummy6;
grub_uint64_t inodeoffset;
grub_uint64_t diroffset;
grub_uint64_t unk1offset;
grub_uint64_t unk2offset;
} __attribute__ ((packed));
/* Chunk-based */
struct grub_squash_inode
{
/* Same values as direlem types. */
grub_uint16_t type;
grub_uint16_t dummy[3];
grub_uint32_t mtime;
union
{
struct {
grub_uint32_t dummy;
grub_uint32_t chunk;
grub_uint32_t fragment;
grub_uint32_t offset;
grub_uint32_t size;
grub_uint32_t block_size[0];
} __attribute__ ((packed)) file;
struct {
grub_uint32_t dummy;
grub_uint64_t chunk;
grub_uint64_t size;
grub_uint32_t dummy2[3];
grub_uint32_t fragment;
grub_uint32_t offset;
grub_uint32_t dummy3;
grub_uint32_t block_size[0];
} __attribute__ ((packed)) long_file;
struct {
grub_uint32_t dummy1;
grub_uint32_t chunk;
grub_uint32_t dummy2;
grub_uint16_t size;
grub_uint32_t offset;
grub_uint16_t dummy3;
} __attribute__ ((packed)) dir;
struct {
grub_uint64_t dummy;
grub_uint32_t namelen;
char name[0];
} __attribute__ ((packed)) symlink;
} __attribute__ ((packed));
} __attribute__ ((packed));
struct grub_squash_cache_inode
{
struct grub_squash_inode ino;
grub_disk_addr_t ino_chunk;
grub_uint16_t ino_offset;
grub_uint32_t *block_sizes;
grub_disk_addr_t *cumulated_block_sizes;
};
/* Chunk-based. */
struct grub_squash_dirent_header
{
/* Actually the value is the number of elements - 1. */
grub_uint32_t nelems;
grub_uint64_t ino_chunk;
} __attribute__ ((packed));
struct grub_squash_dirent
{
grub_uint16_t ino_offset;
grub_uint16_t dummy;
grub_uint16_t type;
/* Actually the value is the length of name - 1. */
grub_uint16_t namelen;
char name[0];
} __attribute__ ((packed));
enum
{
SQUASH_TYPE_DIR = 1,
SQUASH_TYPE_REGULAR = 2,
SQUASH_TYPE_SYMLINK = 3,
SQUASH_TYPE_LONG_REGULAR = 9,
};
struct grub_squash_frag_desc
{
grub_uint64_t offset;
grub_uint32_t size;
grub_uint32_t dummy;
} __attribute__ ((packed));
enum
{
SQUASH_CHUNK_FLAGS = 0x8000,
SQUASH_CHUNK_UNCOMPRESSED = 0x8000
};
enum
{
SQUASH_BLOCK_FLAGS = 0x1000000,
SQUASH_BLOCK_UNCOMPRESSED = 0x1000000
};
#define SQUASH_CHUNK_SIZE 0x2000
struct grub_squash_data
{
grub_disk_t disk;
struct grub_squash_super sb;
struct grub_squash_cache_inode ino;
grub_uint64_t fragments;
};
struct grub_fshelp_node
{
struct grub_squash_data *data;
struct grub_squash_inode ino;
grub_disk_addr_t ino_chunk;
grub_uint16_t ino_offset;
};
static grub_err_t
read_chunk (struct grub_squash_data *data, void *buf, grub_size_t len,
grub_uint64_t chunk, grub_off_t offset)
{
grub_uint64_t chunk_start;
chunk_start = grub_le_to_cpu64 (chunk);
while (len > 0)
{
grub_uint64_t csize;
grub_uint16_t d;
grub_err_t err;
while (1)
{
err = grub_disk_read (data->disk,
chunk_start >> GRUB_DISK_SECTOR_BITS,
chunk_start & (GRUB_DISK_SECTOR_SIZE - 1),
sizeof (d), &d);
if (err)
return err;
if (offset < SQUASH_CHUNK_SIZE)
break;
offset -= SQUASH_CHUNK_SIZE;
chunk_start += 2 + (grub_le_to_cpu16 (d) & ~SQUASH_CHUNK_FLAGS);
}
csize = SQUASH_CHUNK_SIZE - offset;
if (csize > len)
csize = len;
if (grub_le_to_cpu16 (d) & SQUASH_CHUNK_UNCOMPRESSED)
{
grub_disk_addr_t a = chunk_start + 2 + offset;
err = grub_disk_read (data->disk, (a >> GRUB_DISK_SECTOR_BITS),
a & (GRUB_DISK_SECTOR_SIZE - 1),
csize, buf);
if (err)
return err;
}
else
{
char *tmp;
grub_size_t bsize = grub_le_to_cpu16 (d) & ~SQUASH_CHUNK_FLAGS;
grub_disk_addr_t a = chunk_start + 2;
tmp = grub_malloc (bsize);
if (!tmp)
return grub_errno;
/* FIXME: buffer uncompressed data. */
err = grub_disk_read (data->disk, (a >> GRUB_DISK_SECTOR_BITS),
a & (GRUB_DISK_SECTOR_SIZE - 1),
bsize, tmp);
if (err)
{
grub_free (tmp);
return err;
}
if (grub_zlib_decompress (tmp, bsize, offset,
buf, csize) < 0)
{
grub_free (tmp);
return grub_errno;
}
grub_free (tmp);
}
len -= csize;
offset += csize;
buf = (char *) buf + csize;
}
return GRUB_ERR_NONE;
}
static struct grub_squash_data *
squash_mount (grub_disk_t disk)
{
struct grub_squash_super sb;
grub_err_t err;
struct grub_squash_data *data;
grub_uint64_t frag;
err = grub_disk_read (disk, 0, 0, sizeof (sb), &sb);
if (grub_errno == GRUB_ERR_OUT_OF_RANGE)
grub_error (GRUB_ERR_BAD_FS, "not a squash4");
if (err)
return NULL;
if (grub_le_to_cpu32 (sb.magic) != SQUASH_MAGIC)
{
grub_error (GRUB_ERR_BAD_FS, "not squash4");
return NULL;
}
err = grub_disk_read (disk,
grub_le_to_cpu64 (sb.unk1offset)
>> GRUB_DISK_SECTOR_BITS,
grub_le_to_cpu64 (sb.unk1offset)
& (GRUB_DISK_SECTOR_SIZE - 1), sizeof (frag), &frag);
if (grub_errno == GRUB_ERR_OUT_OF_RANGE)
grub_error (GRUB_ERR_BAD_FS, "not a squash4");
if (err)
return NULL;
data = grub_malloc (sizeof (*data));
if (!data)
return NULL;
data->sb = sb;
data->disk = disk;
data->fragments = grub_le_to_cpu64 (frag);
return data;
}
static char *
grub_squash_read_symlink (grub_fshelp_node_t node)
{
char *ret;
grub_err_t err;
ret = grub_malloc (grub_le_to_cpu32 (node->ino.symlink.namelen) + 1);
err = read_chunk (node->data, ret,
grub_le_to_cpu32 (node->ino.symlink.namelen),
grub_le_to_cpu64 (node->data->sb.inodeoffset)
+ node->ino_chunk,
node->ino_offset + (node->ino.symlink.name
- (char *) &node->ino));
if (err)
{
grub_free (ret);
return NULL;
}
ret[grub_le_to_cpu32 (node->ino.symlink.namelen)] = 0;
return ret;
}
static int
grub_squash_iterate_dir (grub_fshelp_node_t dir,
int NESTED_FUNC_ATTR
(*hook) (const char *filename,
enum grub_fshelp_filetype filetype,
grub_fshelp_node_t node))
{
grub_uint32_t off = grub_le_to_cpu16 (dir->ino.dir.offset);
grub_uint32_t endoff;
unsigned i;
/* FIXME: why - 3 ? */
endoff = grub_le_to_cpu32 (dir->ino.dir.size) + off - 3;
while (off < endoff)
{
struct grub_squash_dirent_header dh;
grub_err_t err;
err = read_chunk (dir->data, &dh, sizeof (dh),
grub_le_to_cpu64 (dir->data->sb.diroffset)
+ grub_le_to_cpu32 (dir->ino.dir.chunk), off);
if (err)
return 0;
off += sizeof (dh);
for (i = 0; i < (unsigned) grub_le_to_cpu16 (dh.nelems) + 1; i++)
{
char *buf;
int r;
struct grub_fshelp_node *node;
enum grub_fshelp_filetype filetype = GRUB_FSHELP_REG;
struct grub_squash_dirent di;
struct grub_squash_inode ino;
err = read_chunk (dir->data, &di, sizeof (di),
grub_le_to_cpu64 (dir->data->sb.diroffset)
+ grub_le_to_cpu32 (dir->ino.dir.chunk), off);
if (err)
return 0;
off += sizeof (di);
err = read_chunk (dir->data, &ino, sizeof (ino),
grub_le_to_cpu64 (dir->data->sb.inodeoffset)
+ grub_le_to_cpu32 (dh.ino_chunk),
grub_cpu_to_le16 (di.ino_offset));
if (err)
return 0;
buf = grub_malloc (grub_le_to_cpu16 (di.namelen) + 2);
if (!buf)
return 0;
err = read_chunk (dir->data, buf,
grub_le_to_cpu16 (di.namelen) + 1,
grub_le_to_cpu64 (dir->data->sb.diroffset)
+ grub_le_to_cpu32 (dir->ino.dir.chunk), off);
if (err)
return 0;
off += grub_le_to_cpu16 (di.namelen) + 1;
buf[grub_le_to_cpu16 (di.namelen) + 1] = 0;
if (grub_le_to_cpu16 (di.type) == SQUASH_TYPE_DIR)
filetype = GRUB_FSHELP_DIR;
if (grub_le_to_cpu16 (di.type) == SQUASH_TYPE_SYMLINK)
filetype = GRUB_FSHELP_SYMLINK;
node = grub_malloc (sizeof (*node));
if (! node)
return 0;
*node = *dir;
node->ino = ino;
node->ino_chunk = grub_le_to_cpu32 (dh.ino_chunk);
node->ino_offset = grub_le_to_cpu16 (di.ino_offset);
r = hook (buf, filetype, node);
grub_free (buf);
if (r)
return r;
}
}
return 0;
}
static grub_err_t
make_root_node (struct grub_squash_data *data, struct grub_fshelp_node *root)
{
grub_memset (root, 0, sizeof (*root));
root->data = data;
return read_chunk (data, &root->ino, sizeof (root->ino),
grub_le_to_cpu64 (data->sb.inodeoffset)
+ grub_le_to_cpu16 (data->sb.root_ino_chunk),
grub_cpu_to_le16 (data->sb.root_ino_offset));
}
static grub_err_t
grub_squash_dir (grub_device_t device, const char *path,
int (*hook) (const char *filename,
const struct grub_dirhook_info *info))
{
auto int NESTED_FUNC_ATTR iterate (const char *filename,
enum grub_fshelp_filetype filetype,
grub_fshelp_node_t node);
int NESTED_FUNC_ATTR iterate (const char *filename,
enum grub_fshelp_filetype filetype,
grub_fshelp_node_t node)
{
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_le_to_cpu32 (node->ino.mtime);
return hook (filename, &info);
}
struct grub_squash_data *data = 0;
struct grub_fshelp_node *fdiro = 0;
struct grub_fshelp_node root;
grub_err_t err;
data = squash_mount (device->disk);
if (! data)
return grub_errno;
err = make_root_node (data, &root);
if (err)
return err;
grub_fshelp_find_file (path, &root, &fdiro, grub_squash_iterate_dir,
grub_squash_read_symlink, GRUB_FSHELP_DIR);
if (!grub_errno)
grub_squash_iterate_dir (fdiro, iterate);
grub_free (data);
return grub_errno;
}
static grub_err_t
grub_squash_open (struct grub_file *file, const char *name)
{
struct grub_squash_data *data = 0;
struct grub_fshelp_node *fdiro = 0;
struct grub_fshelp_node root;
grub_err_t err;
data = squash_mount (file->device->disk);
if (! data)
return grub_errno;
err = make_root_node (data, &root);
if (err)
return err;
grub_fshelp_find_file (name, &root, &fdiro, grub_squash_iterate_dir,
grub_squash_read_symlink, GRUB_FSHELP_REG);
if (grub_errno)
{
grub_free (data);
return grub_errno;
}
file->data = data;
data->ino.ino = fdiro->ino;
data->ino.block_sizes = NULL;
data->ino.cumulated_block_sizes = NULL;
data->ino.ino_chunk = fdiro->ino_chunk;
data->ino.ino_offset = fdiro->ino_offset;
if (fdiro->ino.type
== grub_cpu_to_le16_compile_time (SQUASH_TYPE_LONG_REGULAR))
file->size = grub_le_to_cpu64 (fdiro->ino.long_file.size);
else
file->size = grub_le_to_cpu32 (fdiro->ino.file.size);
return GRUB_ERR_NONE;
}
static grub_ssize_t
direct_read (struct grub_squash_data *data,
struct grub_squash_cache_inode *ino,
grub_off_t off, char *buf, grub_size_t len)
{
grub_err_t err;
grub_off_t cumulated_uncompressed_size = 0;
grub_uint64_t a;
grub_size_t i;
grub_size_t origlen = len;
if (ino->ino.type == grub_cpu_to_le16_compile_time (SQUASH_TYPE_LONG_REGULAR))
a = grub_le_to_cpu64 (ino->ino.long_file.chunk);
else
a = grub_le_to_cpu32 (ino->ino.file.chunk);
if (!ino->block_sizes)
{
grub_off_t total_size;
grub_size_t total_blocks;
grub_size_t block_offset;
if (ino->ino.type
== grub_cpu_to_le16_compile_time (SQUASH_TYPE_LONG_REGULAR))
{
total_size = grub_le_to_cpu64 (ino->ino.long_file.size);
block_offset = ((char *) &ino->ino.long_file.block_size
- (char *) &ino->ino);
}
else
{
total_size = grub_le_to_cpu32 (ino->ino.file.size);
block_offset = ((char *) &ino->ino.file.block_size
- (char *) &ino->ino);
}
total_blocks = grub_divmod64 (total_size
+ grub_le_to_cpu32 (data->sb.block_size) - 1,
grub_le_to_cpu32 (data->sb.block_size),
0);
ino->block_sizes = grub_malloc (total_blocks
* sizeof (ino->block_sizes[0]));
ino->cumulated_block_sizes = grub_malloc (total_blocks
* sizeof (ino->cumulated_block_sizes[0]));
if (!ino->block_sizes || !ino->cumulated_block_sizes)
{
grub_free (ino->block_sizes);
grub_free (ino->cumulated_block_sizes);
ino->block_sizes = 0;
ino->cumulated_block_sizes = 0;
return -1;
}
err = read_chunk (data, ino->block_sizes,
total_blocks * sizeof (ino->block_sizes[0]),
grub_le_to_cpu64 (data->sb.inodeoffset)
+ ino->ino_chunk,
ino->ino_offset + block_offset);
if (err)
{
grub_free (ino->block_sizes);
grub_free (ino->cumulated_block_sizes);
ino->block_sizes = 0;
ino->cumulated_block_sizes = 0;
return -1;
}
ino->cumulated_block_sizes[0] = 0;
for (i = 1; i < total_blocks; i++)
ino->cumulated_block_sizes[i] = ino->cumulated_block_sizes[i - 1]
+ (grub_le_to_cpu32 (ino->block_sizes[i - 1]) & ~SQUASH_BLOCK_FLAGS);
}
if (a == 0)
a = sizeof (struct grub_squash_super);
i = grub_divmod64 (off, grub_le_to_cpu32 (data->sb.block_size), 0);
cumulated_uncompressed_size = grub_le_to_cpu32 (data->sb.block_size)
* (grub_disk_addr_t) i;
while (cumulated_uncompressed_size < off + len)
{
grub_size_t boff, read;
boff = off - cumulated_uncompressed_size;
read = grub_le_to_cpu32 (data->sb.block_size) - boff;
if (read > len)
read = len;
if (!(ino->block_sizes[i] & SQUASH_BLOCK_UNCOMPRESSED))
err = grub_zlib_disk_read (data->disk,
ino->cumulated_block_sizes[i] + a,
boff, buf, read);
else
err = grub_disk_read (data->disk,
(ino->cumulated_block_sizes[i] + a + boff)
>> GRUB_DISK_SECTOR_BITS,
(ino->cumulated_block_sizes[i] + a + boff)
& (GRUB_DISK_SECTOR_SIZE - 1),
read, buf);
if (err)
return -1;
off += read;
len -= read;
buf += read;
cumulated_uncompressed_size += grub_le_to_cpu32 (data->sb.block_size);
i++;
}
return origlen;
}
static grub_ssize_t
grub_squash_read_data (struct grub_squash_data *data,
struct grub_squash_cache_inode *ino,
grub_off_t off, char *buf, grub_size_t len)
{
grub_err_t err;
grub_uint64_t a, b;
grub_uint32_t fragment;
int compressed = 0;
struct grub_squash_frag_desc frag;
if (ino->ino.type == grub_cpu_to_le16_compile_time (SQUASH_TYPE_LONG_REGULAR))
{
a = grub_le_to_cpu64 (ino->ino.long_file.chunk);
fragment = grub_le_to_cpu32 (ino->ino.long_file.fragment);
}
else
{
a = grub_le_to_cpu32 (ino->ino.file.chunk);
fragment = grub_le_to_cpu32 (ino->ino.file.fragment);
}
if (fragment == 0xffffffff)
return direct_read (data, ino, off, buf, len);
err = read_chunk (data, &frag, sizeof (frag),
data->fragments, sizeof (frag) * fragment);
if (err)
return -1;
a += grub_le_to_cpu64 (frag.offset);
compressed = !(frag.size & SQUASH_BLOCK_UNCOMPRESSED);
if (ino->ino.type == grub_cpu_to_le16_compile_time (SQUASH_TYPE_LONG_REGULAR))
b = grub_le_to_cpu64 (ino->ino.long_file.offset) + off;
else
b = grub_le_to_cpu32 (ino->ino.file.offset) + off;
/* FIXME: cache uncompressed chunks. */
if (compressed)
err = grub_zlib_disk_read (data->disk, a, b, buf, len);
else
err = grub_disk_read (data->disk, (a + b) >> GRUB_DISK_SECTOR_BITS,
(a + b) & (GRUB_DISK_SECTOR_SIZE - 1), len, buf);
if (err)
return -1;
return len;
}
static grub_ssize_t
grub_squash_read (grub_file_t file, char *buf, grub_size_t len)
{
struct grub_squash_data *data = file->data;
return grub_squash_read_data (data, &data->ino,
file->offset, buf, len);
}
static grub_err_t
grub_squash_close (grub_file_t file)
{
grub_free (file->data);
return GRUB_ERR_NONE;
}
static grub_err_t
grub_squash_mtime (grub_device_t dev, grub_int32_t *tm)
{
struct grub_squash_data *data = 0;
data = squash_mount (dev->disk);
if (! data)
return grub_errno;
*tm = grub_le_to_cpu32 (data->sb.creation_time);
grub_free (data);
return GRUB_ERR_NONE;
}
static struct grub_fs grub_squash_fs =
{
.name = "squash4",
.dir = grub_squash_dir,
.open = grub_squash_open,
.read = grub_squash_read,
.close = grub_squash_close,
.mtime = grub_squash_mtime,
#ifdef GRUB_UTIL
.reserved_first_sector = 0,
#endif
.next = 0
};
GRUB_MOD_INIT(squash4)
{
grub_fs_register (&grub_squash_fs);
}
GRUB_MOD_FINI(squash4)
{
grub_fs_unregister (&grub_squash_fs);
}

View file

@ -26,6 +26,9 @@
#include <grub/types.h>
#include <grub/fshelp.h>
#include <grub/charset.h>
#include <grub/datetime.h>
GRUB_MOD_LICENSE ("GPLv3+");
#define GRUB_UDF_MAX_PDS 2
#define GRUB_UDF_MAX_PMS 6
@ -543,8 +546,7 @@ grub_udf_read_block (grub_fshelp_node_t node, grub_disk_addr_t fileblock)
}
fail:
if (buf)
grub_free (buf);
grub_free (buf);
return 0;
}
@ -788,6 +790,43 @@ fail:
return 0;
}
static char *
read_string (grub_uint8_t *raw, grub_size_t sz)
{
grub_uint16_t *utf16 = NULL;
char *ret;
grub_size_t utf16len = 0;
if (raw[0] != 8 && raw[0] != 16)
return NULL;
if (raw[0] == 8)
{
unsigned i;
utf16len = sz - 1;
utf16 = grub_malloc (utf16len * sizeof (utf16[0]));
if (!utf16)
return NULL;
for (i = 0; i < utf16len; i++)
utf16[i] = raw[i + 1];
}
if (raw[0] == 16)
{
unsigned i;
utf16len = (sz - 1) / 2;
utf16 = grub_malloc (utf16len * sizeof (utf16[0]));
if (!utf16)
return NULL;
for (i = 0; i < utf16len; i++)
utf16[i] = (raw[2 * i + 1] << 8) | raw[2*i + 2];
}
ret = grub_malloc (utf16len * 3 + 1);
if (ret)
*grub_utf16_to_utf8 ((grub_uint8_t *) ret, utf16, utf16len) = '\0';
grub_free (utf16);
return ret;
}
static int
grub_udf_iterate_dir (grub_fshelp_node_t dir,
int NESTED_FUNC_ATTR
@ -841,13 +880,13 @@ grub_udf_iterate_dir (grub_fshelp_node_t dir,
else
{
enum grub_fshelp_filetype type;
char *filename;
grub_uint8_t raw[dirent.file_ident_length];
grub_uint16_t utf16[dirent.file_ident_length - 1];
grub_uint8_t filename[dirent.file_ident_length * 2];
grub_size_t utf16len = 0;
type = ((dirent.characteristics & GRUB_UDF_FID_CHAR_DIRECTORY) ?
(GRUB_FSHELP_DIR) : (GRUB_FSHELP_REG));
if (child->fe.icbtag.file_type == GRUB_UDF_ICBTAG_TYPE_SYMLINK)
type = GRUB_FSHELP_SYMLINK;
if ((grub_udf_read_file (dir, 0, offset,
dirent.file_ident_length,
@ -855,27 +894,16 @@ grub_udf_iterate_dir (grub_fshelp_node_t dir,
!= dirent.file_ident_length)
return 0;
if (raw[0] == 8)
{
unsigned i;
utf16len = dirent.file_ident_length - 1;
for (i = 0; i < utf16len; i++)
utf16[i] = raw[i + 1];
}
if (raw[0] == 16)
{
unsigned i;
utf16len = (dirent.file_ident_length - 1) / 2;
for (i = 0; i < utf16len; i++)
utf16[i] = (raw[2 * i + 1] << 8) | raw[2*i + 2];
}
if (raw[0] == 8 || raw[0] == 16)
{
*grub_utf16_to_utf8 (filename, utf16, utf16len) = '\0';
filename = read_string (raw, dirent.file_ident_length);
if (!filename)
grub_print_error ();
if (hook ((char *) filename, type, child))
return 1;
if (filename && hook (filename, type, child))
{
grub_free (filename);
return 1;
}
grub_free (filename);
}
}
@ -886,6 +914,25 @@ grub_udf_iterate_dir (grub_fshelp_node_t dir,
return 0;
}
static char *
grub_ufs_read_symlink (grub_fshelp_node_t node)
{
grub_size_t sz = U64 (node->fe.file_size);
grub_uint8_t *raw;
char *ret;
if (sz < 4)
return NULL;
raw = grub_malloc (sz - 4);
if (!raw)
return NULL;
if (grub_udf_read_file (node, NULL, 4, sz - 4, (char *) raw) < 0)
return NULL;
ret = read_string (raw, sz - 4);
grub_free (raw);
return ret;
}
static grub_err_t
grub_udf_dir (grub_device_t device, const char *path,
int (*hook) (const char *filename,
@ -904,8 +951,36 @@ grub_udf_dir (grub_device_t device, const char *path,
grub_fshelp_node_t node)
{
struct grub_dirhook_info info;
const struct grub_udf_timestamp *tstamp = NULL;
grub_memset (&info, 0, sizeof (info));
info.dir = ((filetype & GRUB_FSHELP_TYPE_MASK) == GRUB_FSHELP_DIR);
if (U16 (node->fe.tag.tag_ident) == GRUB_UDF_TAG_IDENT_FE)
tstamp = &node->fe.modification_time;
else if (U16 (node->fe.tag.tag_ident) == GRUB_UDF_TAG_IDENT_EFE)
tstamp = &node->efe.modification_time;
if (tstamp && (U16 (tstamp->type_and_timezone) & 0xf000) == 0x1000)
{
grub_int16_t tz;
struct grub_datetime datetime;
datetime.year = U16 (tstamp->year);
datetime.month = tstamp->month;
datetime.day = tstamp->day;
datetime.hour = tstamp->hour;
datetime.minute = tstamp->minute;
datetime.second = tstamp->second;
tz = U16 (tstamp->type_and_timezone) & 0xfff;
if (tz & 0x800)
tz |= 0xf000;
if (tz == -2047)
tz = 0;
info.mtimeset = !!grub_datetime2unixtime (&datetime, &info.mtime);
info.mtime -= 60 * tz;
}
grub_free (node);
return hook (filename, &info);
}
@ -921,7 +996,8 @@ grub_udf_dir (grub_device_t device, const char *path,
if (grub_fshelp_find_file (path, &rootnode,
&foundnode,
grub_udf_iterate_dir, 0, GRUB_FSHELP_DIR))
grub_udf_iterate_dir, grub_ufs_read_symlink,
GRUB_FSHELP_DIR))
goto fail;
grub_udf_iterate_dir (foundnode, iterate);
@ -955,7 +1031,8 @@ grub_udf_open (struct grub_file *file, const char *name)
if (grub_fshelp_find_file (name, &rootnode,
&foundnode,
grub_udf_iterate_dir, 0, GRUB_FSHELP_REG))
grub_udf_iterate_dir, grub_ufs_read_symlink,
GRUB_FSHELP_REG))
goto fail;
file->data = foundnode;
@ -1004,7 +1081,7 @@ grub_udf_label (grub_device_t device, char **label)
if (data)
{
*label = grub_strdup ((char *) &data->lvd.ident[1]);
*label = read_string (data->lvd.ident, sizeof (data->lvd.ident));
grub_free (data);
}
else

View file

@ -25,6 +25,8 @@
#include <grub/dl.h>
#include <grub/types.h>
GRUB_MOD_LICENSE ("GPLv3+");
#ifdef MODE_UFS2
#define GRUB_UFS_MAGIC 0x19540119
#else
@ -283,26 +285,29 @@ static grub_ssize_t
grub_ufs_read_file (struct grub_ufs_data *data,
void NESTED_FUNC_ATTR (*read_hook) (grub_disk_addr_t sector,
unsigned offset, unsigned length),
int pos, grub_size_t len, char *buf)
grub_off_t pos, grub_size_t len, char *buf)
{
struct grub_ufs_sblock *sblock = &data->sblock;
int i;
int blockcnt;
grub_off_t i;
grub_off_t blockcnt;
/* Adjust len so it we can't read past the end of the file. */
if (len + pos > INODE_SIZE (data))
len = INODE_SIZE (data) - pos;
blockcnt = (len + pos + UFS_BLKSZ (sblock) - 1) / UFS_BLKSZ (sblock);
blockcnt = grub_divmod64 ((len + pos + UFS_BLKSZ (sblock) - 1),
UFS_BLKSZ (sblock), 0);
for (i = pos / UFS_BLKSZ (sblock); i < blockcnt; i++)
for (i = grub_divmod64 (pos, UFS_BLKSZ (sblock), 0); i < blockcnt; i++)
{
int blknr;
int blockoff = pos % UFS_BLKSZ (sblock);
int blockend = UFS_BLKSZ (sblock);
grub_disk_addr_t blknr;
grub_off_t blockoff;
grub_off_t blockend = UFS_BLKSZ (sblock);
int skipfirst = 0;
grub_divmod64 (pos, UFS_BLKSZ (sblock), &blockoff);
blknr = grub_ufs_get_file_block (data, i);
if (grub_errno)
return -1;
@ -310,14 +315,14 @@ grub_ufs_read_file (struct grub_ufs_data *data,
/* Last block. */
if (i == blockcnt - 1)
{
blockend = (len + pos) % UFS_BLKSZ (sblock);
grub_divmod64 (len + pos, UFS_BLKSZ (sblock), &blockend);
if (!blockend)
blockend = UFS_BLKSZ (sblock);
}
/* First block. */
if (i == (pos / (int) UFS_BLKSZ (sblock)))
if (i == grub_divmod64 (pos, UFS_BLKSZ (sblock), 0))
{
skipfirst = blockoff;
blockend -= skipfirst;
@ -512,7 +517,7 @@ grub_ufs_find_file (struct grub_ufs_data *data, const char *path)
pos += grub_le_to_cpu16 (dirent.direntlen);
} while (pos < INODE_SIZE (data));
grub_error (GRUB_ERR_FILE_NOT_FOUND, "file not found");
grub_error (GRUB_ERR_FILE_NOT_FOUND, "file `%s' not found", path);
return grub_errno;
}

View file

@ -26,6 +26,8 @@
#include <grub/types.h>
#include <grub/fshelp.h>
GRUB_MOD_LICENSE ("GPLv3+");
#define XFS_INODE_EXTENTS 9
#define XFS_INODE_FORMAT_INO 1
@ -98,18 +100,29 @@ struct grub_xfs_btree_root
grub_uint64_t keys[1];
} __attribute__ ((packed));
struct grub_xfs_time
{
grub_uint32_t sec;
grub_uint32_t nanosec;
} __attribute__ ((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[50];
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_uint8_t unused3[20];
grub_uint16_t unused3;
grub_uint8_t fork_offset;
grub_uint8_t unused4[17];
union
{
char raw[156];
@ -143,7 +156,7 @@ struct grub_xfs_data
grub_disk_t disk;
int pos;
int bsize;
int agsize;
grub_uint32_t agsize;
struct grub_fshelp_node diropen;
};
@ -157,33 +170,67 @@ static grub_dl_t my_mod;
#define FILETYPE_INO_DIRECTORY 0040000
#define FILETYPE_INO_SYMLINK 0120000
#define GRUB_XFS_INO_AGBITS(data) \
((data)->sblock.log2_agblk + (data)->sblock.log2_inop)
#define GRUB_XFS_INO_INOINAG(data, ino) \
(grub_be_to_cpu64 (ino) & ((1LL << GRUB_XFS_INO_AGBITS (data)) - 1))
#define GRUB_XFS_INO_AG(data,ino) \
(grub_be_to_cpu64 (ino) >> GRUB_XFS_INO_AGBITS (data))
static inline int
GRUB_XFS_INO_AGBITS(struct grub_xfs_data *data)
{
return ((data)->sblock.log2_agblk + (data)->sblock.log2_inop);
}
#define GRUB_XFS_FSB_TO_BLOCK(data, fsb) \
(((fsb) >> (data)->sblock.log2_agblk) * (data)->agsize \
+ ((fsb) & ((1LL << (data)->sblock.log2_agblk) - 1)))
static inline grub_uint64_t
GRUB_XFS_INO_INOINAG (struct grub_xfs_data *data,
grub_uint64_t ino)
{
return (grub_be_to_cpu64 (ino) & ((1LL << GRUB_XFS_INO_AGBITS (data)) - 1));
}
#define GRUB_XFS_EXTENT_OFFSET(exts,ex) \
((grub_be_to_cpu32 (exts[ex][0]) & ~(1 << 31)) << 23 \
| grub_be_to_cpu32 (exts[ex][1]) >> 9)
static inline grub_uint64_t
GRUB_XFS_INO_AG (struct grub_xfs_data *data,
grub_uint64_t ino)
{
return (grub_be_to_cpu64 (ino) >> GRUB_XFS_INO_AGBITS (data));
}
#define GRUB_XFS_EXTENT_BLOCK(exts,ex) \
((grub_uint64_t) (grub_be_to_cpu32 (exts[ex][1]) \
& (0x1ff)) << 43 \
| (grub_uint64_t) grub_be_to_cpu32 (exts[ex][2]) << 11 \
| grub_be_to_cpu32 (exts[ex][3]) >> 21)
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)));
}
#define GRUB_XFS_EXTENT_SIZE(exts,ex) \
(grub_be_to_cpu32 (exts[ex][3]) & ((1 << 20) - 1))
static inline grub_uint64_t
GRUB_XFS_EXTENT_OFFSET (grub_xfs_extent *exts, int ex)
{
return ((grub_be_to_cpu32 (exts[ex][0]) & ~(1 << 31)) << 23
| grub_be_to_cpu32 (exts[ex][1]) >> 9);
}
static inline grub_uint64_t
GRUB_XFS_EXTENT_BLOCK (grub_xfs_extent *exts, int ex)
{
return ((grub_uint64_t) (grub_be_to_cpu32 (exts[ex][1])
& (0x1ff)) << 43
| (grub_uint64_t) grub_be_to_cpu32 (exts[ex][2]) << 11
| grub_be_to_cpu32 (exts[ex][3]) >> 21);
}
static inline grub_uint64_t
GRUB_XFS_EXTENT_SIZE (grub_xfs_extent *exts, int ex)
{
return (grub_be_to_cpu32 (exts[ex][3]) & ((1 << 20) - 1));
}
static inline int
GRUB_XFS_ROUND_TO_DIRENT (int pos)
{
return ((((pos) + 8 - 1) / 8) * 8);
}
static inline int
GRUB_XFS_NEXT_DIRENT (int pos, int len)
{
return (pos) + GRUB_XFS_ROUND_TO_DIRENT (8 + 1 + len + 2);
}
#define GRUB_XFS_ROUND_TO_DIRENT(pos) ((((pos) + 8 - 1) / 8) * 8)
#define GRUB_XFS_NEXT_DIRENT(pos,len) \
(pos) + GRUB_XFS_ROUND_TO_DIRENT (8 + 1 + len + 2)
static inline grub_uint64_t
grub_xfs_inode_block (struct grub_xfs_data *data,
@ -239,13 +286,23 @@ grub_xfs_read_block (grub_fshelp_node_t node, grub_disk_addr_t fileblock)
if (node->inode.format == XFS_INODE_FORMAT_BTREE)
{
grub_uint64_t *keys;
int recoffset;
leaf = grub_malloc (node->data->sblock.bsize);
leaf = grub_malloc (node->data->bsize);
if (leaf == 0)
return 0;
nrec = grub_be_to_cpu16 (node->inode.data.btree.numrecs);
keys = &node->inode.data.btree.keys[0];
if (node->inode.fork_offset)
recoffset = (node->inode.fork_offset
- ((char *) &node->inode.data.btree.keys - (char *) &node->inode))
/ (2 * sizeof (grub_uint64_t));
else
recoffset = ((1 << node->data->sblock.log2_inode)
- ((char *) &node->inode.data.btree.keys
- (char *) &node->inode))
/ (2 * sizeof (grub_uint64_t));
do
{
int i;
@ -262,12 +319,9 @@ grub_xfs_read_block (grub_fshelp_node_t node, grub_disk_addr_t fileblock)
grub_free (leaf);
return 0;
}
if (grub_disk_read (node->data->disk,
grub_be_to_cpu64 (keys[i - 1 + nrec])
<< (node->data->sblock.log2_bsize
- GRUB_DISK_SECTOR_BITS),
0, node->data->sblock.bsize, leaf))
GRUB_XFS_FSB_TO_BLOCK (node->data, grub_be_to_cpu64 (keys[i - 1 + recoffset])) << (node->data->sblock.log2_bsize - GRUB_DISK_SECTOR_BITS),
0, node->data->bsize, leaf))
return 0;
if (grub_strncmp ((char *) leaf->magic, "BMAP", 4))
@ -279,7 +333,11 @@ grub_xfs_read_block (grub_fshelp_node_t node, grub_disk_addr_t fileblock)
nrec = grub_be_to_cpu16 (leaf->numrecs);
keys = &leaf->keys[0];
} while (leaf->level);
recoffset = ((node->data->bsize - ((char *) &leaf->keys
- (char *) leaf))
/ (2 * sizeof (grub_uint64_t)));
}
while (leaf->level);
exts = (grub_xfs_extent *) keys;
}
else if (node->inode.format == XFS_INODE_FORMAT_EXT)
@ -313,8 +371,7 @@ grub_xfs_read_block (grub_fshelp_node_t node, grub_disk_addr_t fileblock)
}
}
if (leaf)
grub_free (leaf);
grub_free (leaf);
return GRUB_XFS_FSB_TO_BLOCK(node->data, ret);
}
@ -326,7 +383,7 @@ static grub_ssize_t
grub_xfs_read_file (grub_fshelp_node_t node,
void NESTED_FUNC_ATTR (*read_hook) (grub_disk_addr_t sector,
unsigned offset, unsigned length),
int pos, grub_size_t len, char *buf)
grub_off_t pos, grub_size_t len, char *buf)
{
return grub_fshelp_read_file (node->data->disk, node, read_hook,
pos, len, buf, grub_xfs_read_block,
@ -451,18 +508,27 @@ grub_xfs_iterate_dir (grub_fshelp_node_t dir,
for (i = 0; i < diro->inode.data.dir.dirhead.count; i++)
{
grub_uint64_t ino;
void *inopos = (((char *) de)
grub_uint8_t *inopos = (((grub_uint8_t *) de)
+ sizeof (struct grub_xfs_dir_entry)
+ de->len - 1);
char name[de->len + 1];
/* inopos might be unaligned. */
if (smallino)
{
ino = grub_be_to_cpu32 (*(grub_uint32_t *) inopos);
ino = grub_cpu_to_be64 (ino);
}
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;
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);
ino = grub_cpu_to_be64 (ino);
grub_memcpy (name, de->name, de->len);
name[de->len] = '\0';
@ -643,6 +709,11 @@ grub_xfs_dir (grub_device_t device, const char *path,
{
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);
grub_free (node);
return hook (filename, &info);
@ -808,6 +879,9 @@ static struct grub_fs grub_xfs_fs =
.close = grub_xfs_close,
.label = grub_xfs_label,
.uuid = grub_xfs_uuid,
#ifdef GRUB_UTIL
.reserved_first_sector = 0,
#endif
.next = 0
};

View file

@ -53,6 +53,8 @@
#include <grub/zfs/dsl_dataset.h>
#include <grub/deflate.h>
GRUB_MOD_LICENSE ("GPLv3+");
#define ZPOOL_PROP_BOOTFS "bootfs"
#define MIN(a,b) (((a) < (b)) ? (a) : (b))
@ -196,7 +198,7 @@ zlib_decompress (void *s, void *d,
return GRUB_ERR_NONE;
}
decomp_entry_t decomp_table[ZIO_COMPRESS_FUNCTIONS] = {
static decomp_entry_t decomp_table[ZIO_COMPRESS_FUNCTIONS] = {
{"inherit", NULL}, /* ZIO_COMPRESS_INHERIT */
{"on", lzjb_decompress}, /* ZIO_COMPRESS_ON */
{"off", NULL}, /* ZIO_COMPRESS_OFF */
@ -244,7 +246,7 @@ zio_checksum_off (const void *buf __attribute__ ((unused)),
}
/* Checksum Table and Values */
zio_checksum_info_t zio_checksum_table[ZIO_CHECKSUM_FUNCTIONS] = {
static zio_checksum_info_t zio_checksum_table[ZIO_CHECKSUM_FUNCTIONS] = {
{NULL, 0, 0, "inherit"},
{NULL, 0, 0, "on"},
{zio_checksum_off, 0, 0, "off"},
@ -265,7 +267,7 @@ zio_checksum_info_t zio_checksum_table[ZIO_CHECKSUM_FUNCTIONS] = {
*/
static grub_err_t
zio_checksum_verify (zio_cksum_t zc, grub_uint32_t checksum,
grub_zfs_endian_t endian, char *buf, int size)
grub_zfs_endian_t endian, char *buf, grub_size_t size)
{
zio_eck_t *zec = (zio_eck_t *) (buf + size) - 1;
zio_checksum_info_t *ci = &zio_checksum_table[checksum];
@ -360,7 +362,7 @@ vdev_uberblock_compare (uberblock_t * ub1, uberblock_t * ub2)
*
*/
static grub_err_t
uberblock_verify (uberblock_phys_t * ub, int offset)
uberblock_verify (uberblock_phys_t * ub, grub_uint64_t offset)
{
uberblock_t *uber = &ub->ubp_uberblock;
grub_err_t err;
@ -890,7 +892,7 @@ read_device (grub_uint64_t offset, struct grub_zfs_device_desc *desc,
{
grub_size_t csize;
grub_uint64_t high;
grub_uint32_t devn;
grub_uint64_t devn;
grub_err_t err;
high = grub_divmod64 (sector + (asize > 2) + c, desc->n_children,
&devn);
@ -898,7 +900,8 @@ read_device (grub_uint64_t offset, struct grub_zfs_device_desc *desc,
if (csize > len)
csize = len;
grub_dprintf ("zfs", "RAIDZ mapping 0x%" PRIxGRUB_UINT64_T
"+%d+%u -> (0x%" PRIxGRUB_UINT64_T ", 0x%x)\n",
"+%d+%u -> (0x%" PRIxGRUB_UINT64_T ", 0x%"
PRIxGRUB_UINT64_T ")\n",
sector,(asize > 2), c, high, devn);
err = read_device (high << 9, &desc->children[devn],
bsize, csize, buf);
@ -1133,10 +1136,12 @@ static grub_err_t
dmu_read (dnode_end_t * dn, grub_uint64_t blkid, void **buf,
grub_zfs_endian_t *endian_out, struct grub_zfs_data *data)
{
int idx, level;
int level;
grub_off_t idx;
blkptr_t *bp_array = dn->dn.dn_blkptr;
int epbs = dn->dn.dn_indblkshift - SPA_BLKPTRSHIFT;
blkptr_t *bp, *tmpbuf = 0;
blkptr_t *bp;
void *tmpbuf = 0;
grub_zfs_endian_t endian;
grub_err_t err = GRUB_ERR_NONE;
@ -1179,7 +1184,7 @@ dmu_read (dnode_end_t * dn, grub_uint64_t blkid, void **buf,
break;
}
grub_dprintf ("zfs", "endian = %d\n", endian);
err = zio_read (bp, endian, (void **) &tmpbuf, 0, data);
err = zio_read (bp, endian, &tmpbuf, 0, data);
endian = (grub_zfs_to_cpu64 (bp->blk_prop, endian) >> 63) & 1;
if (err)
break;
@ -1369,7 +1374,6 @@ zap_leaf_lookup (zap_leaf_phys_t * l, grub_zfs_endian_t endian,
name))
{
struct zap_leaf_array *la;
grub_uint8_t *ip;
if (le->le_int_size != 8 || grub_zfs_to_cpu16 (le->le_value_length,
endian) != 1)
@ -1377,7 +1381,6 @@ zap_leaf_lookup (zap_leaf_phys_t * l, grub_zfs_endian_t endian,
/* get the uint64_t property value */
la = &ZAP_LEAF_CHUNK (l, blksft, le->le_value_chunk).l_array;
ip = la->la_array;
*value = grub_be_to_cpu64 (la->la_array64);
@ -1414,7 +1417,7 @@ static grub_err_t
fzap_lookup (dnode_end_t * zap_dnode, zap_phys_t * zap,
char *name, grub_uint64_t * value, struct grub_zfs_data *data)
{
zap_leaf_phys_t *l;
void *l;
grub_uint64_t hash, idx, blkid;
int blksft = zfs_log2 (grub_zfs_to_cpu16 (zap_dnode->dn.dn_datablkszsec,
zap_dnode->endian) << DNODE_SHIFT);
@ -1437,7 +1440,7 @@ fzap_lookup (dnode_end_t * zap_dnode, zap_phys_t * zap,
/* Get the leaf block */
if ((1U << blksft) < sizeof (zap_leaf_phys_t))
return grub_error (GRUB_ERR_BAD_FS, "ZAP leaf is too small");
err = dmu_read (zap_dnode, blkid, (void **) &l, &leafendian, data);
err = dmu_read (zap_dnode, blkid, &l, &leafendian, data);
if (err)
return err;
@ -1454,7 +1457,8 @@ fzap_iterate (dnode_end_t * zap_dnode, zap_phys_t * zap,
struct grub_zfs_data *data)
{
zap_leaf_phys_t *l;
grub_uint64_t idx, blkid;
void *l_in;
grub_uint64_t idx, idx2, blkid;
grub_uint16_t chunk;
int blksft = zfs_log2 (grub_zfs_to_cpu16 (zap_dnode->dn.dn_datablkszsec,
zap_dnode->endian) << DNODE_SHIFT);
@ -1477,13 +1481,19 @@ fzap_iterate (dnode_end_t * zap_dnode, zap_phys_t * zap,
grub_error (GRUB_ERR_BAD_FS, "ZAP leaf is too small");
return 0;
}
for (idx = 0; idx < grub_zfs_to_cpu64 (zap->zap_num_leafs,
zap_dnode->endian); idx++)
for (idx = 0; idx < (1ULL << zap->zap_ptrtbl.zt_shift); idx++)
{
blkid = grub_zfs_to_cpu64 (((grub_uint64_t *) zap)[idx + (1 << (blksft - 3 - 1))],
zap_dnode->endian);
err = dmu_read (zap_dnode, blkid, (void **) &l, &endian, data);
for (idx2 = 0; idx2 < idx; idx2++)
if (blkid == ((grub_uint64_t *) zap)[idx2 + (1 << (blksft - 3 - 1))])
break;
if (idx2 != idx)
continue;
err = dmu_read (zap_dnode, blkid, &l_in, &endian, data);
l = l_in;
if (err)
{
grub_errno = GRUB_ERR_NONE;
@ -1612,7 +1622,7 @@ zap_iterate (dnode_end_t * zap_dnode,
return 0;
block_type = grub_zfs_to_cpu64 (*((grub_uint64_t *) zapbuf), endian);
grub_dprintf ("zfs", "zap read\n");
grub_dprintf ("zfs", "zap iterate\n");
if (block_type == ZBT_MICRO)
{
@ -1649,7 +1659,7 @@ dnode_get (dnode_end_t * mdn, grub_uint64_t objnum, grub_uint8_t type,
grub_uint64_t blkid, blksz; /* the block id this object dnode is in */
int epbs; /* shift of number of dnodes in a block */
int idx; /* index within a block */
dnode_phys_t *dnbuf;
void *dnbuf;
grub_err_t err;
grub_zfs_endian_t endian;
@ -1672,7 +1682,7 @@ dnode_get (dnode_end_t * mdn, grub_uint64_t objnum, grub_uint8_t type,
grub_dprintf ("zfs", "endian = %d, blkid=%llx\n", mdn->endian,
(unsigned long long) blkid);
err = dmu_read (mdn, blkid, (void **) &dnbuf, &endian, data);
err = dmu_read (mdn, blkid, &dnbuf, &endian, data);
if (err)
return err;
grub_dprintf ("zfs", "alive\n");
@ -1694,7 +1704,7 @@ dnode_get (dnode_end_t * mdn, grub_uint64_t objnum, grub_uint8_t type,
data->dnode_endian = endian;
}
grub_memmove (&(buf->dn), &dnbuf[idx], DNODE_SIZE);
grub_memmove (&(buf->dn), (dnode_phys_t *) dnbuf + idx, DNODE_SIZE);
buf->endian = endian;
if (type && buf->dn.dn_type != type)
return grub_error(GRUB_ERR_BAD_FS, "incorrect dnode type");
@ -1831,52 +1841,53 @@ dnode_get_path (dnode_end_t * mdn, const char *path_in, dnode_end_t * dn,
*path = ch;
if (((grub_zfs_to_cpu64(((znode_phys_t *) DN_BONUS (&dnode_path->dn.dn))->zp_mode, dnode_path->dn.endian) >> 12) & 0xf) == 0xa)
{
char *sym_value;
grub_size_t avail_in_dnode;
grub_size_t sym_sz;
int free_symval = 0;
char *oldpath = path, *oldpathbuf = path_buf;
grub_size_t bonuslen;
bonuslen = (grub_uint8_t *) (&dnode_path->dn.dn + 1)
- (grub_uint8_t *) DN_BONUS (&dnode_path->dn.dn);
if (bonuslen <= sizeof (znode_phys_t))
err = grub_error (GRUB_ERR_BAD_FS,
"incorrect or unsupported symlink");
if (dnode_path->dn.dn.dn_flags & 1)
{
void *sbuf;
grub_size_t ssize;
err = zio_read (dnode_path->dn.dn.dn_blkptr,
dnode_path->dn.endian, &sbuf,
&ssize, data);
if (err)
{
grub_free (oldpathbuf);
return err;
}
sym_value = ((char *) DN_BONUS (&dnode_path->dn.dn) + sizeof (struct znode_phys));
avail_in_dnode = (char *) (&dnode_path->dn + 1) - sym_value;
path = path_buf = grub_malloc (ssize+ grub_strlen (oldpath) + 1);
if (!path_buf)
{
grub_free (oldpathbuf);
grub_free (sbuf);
return grub_errno;
}
grub_memcpy (path, sbuf, ssize);
path[ssize] = 0;
grub_free (sbuf);
}
else
sym_sz = grub_zfs_to_cpu64 (((znode_phys_t *) DN_BONUS (&dnode_path->dn.dn))->zp_size, dnode_path->dn.endian);
if (sym_sz > avail_in_dnode - 8)
{
path = path_buf
= grub_malloc (bonuslen - sizeof (znode_phys_t)
+ grub_strlen (oldpath) + 1);
if (!path_buf)
grub_size_t block;
grub_size_t blksz;
blksz = (grub_zfs_to_cpu16 (dnode_path->dn.dn.dn_datablkszsec,
dnode_path->dn.endian)
<< SPA_MINBLOCKSHIFT);
sym_value = grub_malloc (sym_sz);
if (!sym_value)
return grub_errno;
for (block = 0; block < (sym_sz + blksz - 1) / blksz; block++)
{
grub_free (oldpathbuf);
return grub_errno;
void *t;
grub_size_t movesize;
err = dmu_read (&(dnode_path->dn), block, &t, 0, data);
if (err)
return err;
movesize = MIN (sym_sz - block * blksz, blksz);
grub_memcpy (sym_value + block * blksz, t, movesize);
grub_free (t);
}
grub_memcpy (path, (char *) DN_BONUS(&dnode_path->dn.dn)
+ sizeof (znode_phys_t),
bonuslen - sizeof (znode_phys_t));
path[bonuslen - sizeof (znode_phys_t)] = 0;
free_symval = 1;
}
path = path_buf = grub_malloc (sym_sz + grub_strlen (oldpath) + 1);
if (!path_buf)
{
grub_free (oldpathbuf);
return grub_errno;
}
grub_memcpy (path, sym_value, sym_sz);
if (free_symval)
grub_free (sym_value);
path [sym_sz] = 0;
grub_memcpy (path + grub_strlen (path), oldpath,
grub_strlen (oldpath) + 1);
@ -2037,7 +2048,7 @@ get_filesystem_dnode (dnode_end_t * mosmdn, char *fsname,
static grub_err_t
make_mdn (dnode_end_t * mdn, struct grub_zfs_data *data)
{
objset_phys_t *osp;
void *osp;
blkptr_t *bp;
grub_size_t ospsize;
grub_err_t err;
@ -2045,7 +2056,7 @@ make_mdn (dnode_end_t * mdn, struct grub_zfs_data *data)
grub_dprintf ("zfs", "endian = %d\n", mdn->endian);
bp = &(((dsl_dataset_phys_t *) DN_BONUS (&mdn->dn))->ds_bp);
err = zio_read (bp, mdn->endian, (void **) &osp, &ospsize, data);
err = zio_read (bp, mdn->endian, &osp, &ospsize, data);
if (err)
return err;
if (ospsize < OBJSET_PHYS_SIZE_V14)
@ -2055,7 +2066,8 @@ make_mdn (dnode_end_t * mdn, struct grub_zfs_data *data)
}
mdn->endian = (grub_zfs_to_cpu64 (bp->blk_prop, mdn->endian)>>63) & 1;
grub_memmove ((char *) &(mdn->dn), (char *) &osp->os_meta_dnode, DNODE_SIZE);
grub_memmove ((char *) &(mdn->dn),
(char *) &((objset_phys_t *) osp)->os_meta_dnode, DNODE_SIZE);
grub_free (osp);
return GRUB_ERR_NONE;
}
@ -2633,7 +2645,7 @@ grub_zfs_open (struct grub_file *file, const char *fsfilename)
*/
if (data->dnode.dn.dn_bonustype == DMU_OT_SA)
{
sa_hdr_phys_t *sahdrp;
void *sahdrp;
int hdrsize;
if (data->dnode.dn.dn_bonuslen != 0)
@ -2644,7 +2656,7 @@ grub_zfs_open (struct grub_file *file, const char *fsfilename)
{
blkptr_t *bp = &data->dnode.dn.dn_spill;
err = zio_read (bp, data->dnode.endian, (void **) &sahdrp, NULL, data);
err = zio_read (bp, data->dnode.endian, &sahdrp, NULL, data);
if (err)
return err;
}
@ -2653,7 +2665,7 @@ grub_zfs_open (struct grub_file *file, const char *fsfilename)
return grub_error (GRUB_ERR_BAD_FS, "filesystem is corrupt");
}
hdrsize = SA_HDR_SIZE (sahdrp);
hdrsize = SA_HDR_SIZE (((sa_hdr_phys_t *) sahdrp));
file->size = *(grub_uint64_t *) ((char *) sahdrp + hdrsize + SA_SIZE_OFFSET);
}
else
@ -2675,7 +2687,7 @@ static grub_ssize_t
grub_zfs_read (grub_file_t file, char *buf, grub_size_t len)
{
struct grub_zfs_data *data = (struct grub_zfs_data *) file->data;
int blksz, movesize;
grub_size_t blksz, movesize;
grub_size_t length;
grub_size_t read;
grub_err_t err;
@ -2712,6 +2724,7 @@ grub_zfs_read (grub_file_t file, char *buf, grub_size_t len)
read = 0;
while (length)
{
void *t;
/*
* Find requested blkid and the offset within that block.
*/
@ -2719,15 +2732,16 @@ grub_zfs_read (grub_file_t file, char *buf, grub_size_t len)
grub_free (data->file_buf);
data->file_buf = 0;
err = dmu_read (&(data->dnode), blkid, (void **) &(data->file_buf),
err = dmu_read (&(data->dnode), blkid, &t,
0, data);
data->file_buf = t;
if (err)
return -1;
data->file_start = blkid * blksz;
data->file_end = data->file_start + blksz;
movesize = MIN (length, data->file_end - (int) file->offset - read);
movesize = MIN (length, data->file_end - file->offset - read);
grub_memmove (buf, data->file_buf + file->offset + read
- data->file_start, movesize);

View file

@ -26,6 +26,8 @@
#include <grub/dl.h>
#include <grub/env.h>
GRUB_MOD_LICENSE ("GPLv3+");
static inline void
print_tabs (int n)
{
@ -364,21 +366,16 @@ grub_cmd_zfs_bootfs (grub_command_t cmd __attribute__ ((unused)), int argc,
grub_free (nv);
grub_free (nvlist);
if (bootpath && devid)
{
bootfs = grub_xasprintf ("zfs-bootfs=%s/%llu bootpath=%s diskdevid=%s",
poolname, (unsigned long long) mdnobj,
bootpath, devid);
if (!bootfs)
return grub_errno;
}
else
{
bootfs = grub_xasprintf ("zfs-bootfs=%s/%llu",
poolname, (unsigned long long) mdnobj);
if (!bootfs)
return grub_errno;
}
bootfs = grub_xasprintf ("zfs-bootfs=%s/%llu%s%s%s%s%s%s",
poolname, (unsigned long long) mdnobj,
bootpath ? ",bootpath=\"" : "",
bootpath ? : "",
bootpath ? "\"" : "",
devid ? ",diskdevid=\"" : "",
devid ? : "",
devid ? "\"" : "");
if (!bootfs)
return grub_errno;
if (argc >= 2)
grub_env_set (args[1], bootfs);
else