MErge mainline into intwrap
This commit is contained in:
commit
afba9f98ec
719 changed files with 55744 additions and 25714 deletions
550
grub-core/fs/affs.c
Normal file
550
grub-core/fs/affs.c
Normal file
|
@ -0,0 +1,550 @@
|
|||
/* affs.c - Amiga Fast FileSystem. */
|
||||
/*
|
||||
* GRUB -- GRand Unified Bootloader
|
||||
* Copyright (C) 2005,2006,2007,2008,2009 Free Software Foundation, Inc.
|
||||
*
|
||||
* GRUB is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* GRUB is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with GRUB. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include <grub/err.h>
|
||||
#include <grub/file.h>
|
||||
#include <grub/mm.h>
|
||||
#include <grub/misc.h>
|
||||
#include <grub/disk.h>
|
||||
#include <grub/dl.h>
|
||||
#include <grub/types.h>
|
||||
#include <grub/fshelp.h>
|
||||
|
||||
/* The affs bootblock. */
|
||||
struct grub_affs_bblock
|
||||
{
|
||||
grub_uint8_t type[3];
|
||||
grub_uint8_t flags;
|
||||
grub_uint32_t checksum;
|
||||
grub_uint32_t rootblock;
|
||||
} __attribute__ ((packed));
|
||||
|
||||
/* Set if the filesystem is a AFFS filesystem. Otherwise this is an
|
||||
OFS filesystem. */
|
||||
#define GRUB_AFFS_FLAG_FFS 1
|
||||
|
||||
/* The affs rootblock. */
|
||||
struct grub_affs_rblock
|
||||
{
|
||||
grub_uint8_t type[4];
|
||||
grub_uint8_t unused1[8];
|
||||
grub_uint32_t htsize;
|
||||
grub_uint32_t unused2;
|
||||
grub_uint32_t checksum;
|
||||
grub_uint32_t hashtable[1];
|
||||
} __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 namelen;
|
||||
grub_uint8_t name[30];
|
||||
grub_uint8_t unused3[33];
|
||||
grub_uint32_t next;
|
||||
grub_uint32_t parent;
|
||||
grub_uint32_t extension;
|
||||
grub_int32_t type;
|
||||
} __attribute__ ((packed));
|
||||
|
||||
/* The location of `struct grub_affs_file' relative to the end of a
|
||||
file header block. */
|
||||
#define GRUB_AFFS_FILE_LOCATION 200
|
||||
|
||||
/* The offset in both the rootblock and the file header block for the
|
||||
hashtable, symlink and block pointers (all synonyms). */
|
||||
#define GRUB_AFFS_HASHTABLE_OFFSET 24
|
||||
#define GRUB_AFFS_BLOCKPTR_OFFSET 24
|
||||
#define GRUB_AFFS_SYMLINK_OFFSET 24
|
||||
|
||||
#define GRUB_AFFS_SYMLINK_SIZE(blocksize) ((blocksize) - 225)
|
||||
|
||||
#define GRUB_AFFS_FILETYPE_DIR -3
|
||||
#define GRUB_AFFS_FILETYPE_REG 2
|
||||
#define GRUB_AFFS_FILETYPE_SYMLINK 3
|
||||
|
||||
|
||||
struct grub_fshelp_node
|
||||
{
|
||||
struct grub_affs_data *data;
|
||||
int block;
|
||||
int size;
|
||||
int parent;
|
||||
};
|
||||
|
||||
/* Information about a "mounted" affs filesystem. */
|
||||
struct grub_affs_data
|
||||
{
|
||||
struct grub_affs_bblock bblock;
|
||||
struct grub_fshelp_node diropen;
|
||||
grub_disk_t disk;
|
||||
|
||||
/* Blocksize in sectors. */
|
||||
int blocksize;
|
||||
|
||||
/* The number of entries in the hashtable. */
|
||||
int htsize;
|
||||
};
|
||||
|
||||
static grub_dl_t my_mod;
|
||||
|
||||
|
||||
static grub_disk_addr_t
|
||||
grub_affs_read_block (grub_fshelp_node_t node, grub_disk_addr_t fileblock)
|
||||
{
|
||||
int links;
|
||||
grub_uint32_t pos;
|
||||
int block = node->block;
|
||||
struct grub_affs_file file;
|
||||
struct grub_affs_data *data = node->data;
|
||||
grub_uint32_t mod;
|
||||
|
||||
/* Find the block that points to the fileblock we are looking up by
|
||||
following the chain until the right table is reached. */
|
||||
for (links = grub_divmod64 (fileblock, data->htsize, &mod); links; links--)
|
||||
{
|
||||
grub_disk_read (data->disk, block + data->blocksize - 1,
|
||||
data->blocksize * (GRUB_DISK_SECTOR_SIZE
|
||||
- GRUB_AFFS_FILE_LOCATION),
|
||||
sizeof (file), &file);
|
||||
if (grub_errno)
|
||||
return 0;
|
||||
|
||||
block = grub_be_to_cpu32 (file.extension);
|
||||
}
|
||||
|
||||
/* Translate the fileblock to the block within the right table. */
|
||||
fileblock = mod;
|
||||
grub_disk_read (data->disk, block,
|
||||
GRUB_AFFS_BLOCKPTR_OFFSET
|
||||
+ (data->htsize - fileblock - 1) * sizeof (pos),
|
||||
sizeof (pos), &pos);
|
||||
if (grub_errno)
|
||||
return 0;
|
||||
|
||||
return grub_be_to_cpu32 (pos);
|
||||
}
|
||||
|
||||
|
||||
/* Read LEN bytes from the file described by DATA starting with byte
|
||||
POS. Return the amount of read bytes in READ. */
|
||||
static grub_ssize_t
|
||||
grub_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)
|
||||
{
|
||||
return grub_fshelp_read_file (node->data->disk, node, read_hook,
|
||||
pos, len, buf, grub_affs_read_block,
|
||||
node->size, 0);
|
||||
}
|
||||
|
||||
|
||||
static struct grub_affs_data *
|
||||
grub_affs_mount (grub_disk_t disk)
|
||||
{
|
||||
struct grub_affs_data *data;
|
||||
grub_uint32_t *rootblock = 0;
|
||||
struct grub_affs_rblock *rblock;
|
||||
|
||||
int checksum = 0;
|
||||
int checksumr = 0;
|
||||
int blocksize = 0;
|
||||
|
||||
data = grub_malloc (sizeof (struct grub_affs_data));
|
||||
if (!data)
|
||||
return 0;
|
||||
|
||||
/* Read the bootblock. */
|
||||
grub_disk_read (disk, 0, 0, sizeof (struct grub_affs_bblock),
|
||||
&data->bblock);
|
||||
if (grub_errno)
|
||||
goto fail;
|
||||
|
||||
/* Make sure this is an affs filesystem. */
|
||||
if (grub_strncmp ((char *) (data->bblock.type), "DOS", 3))
|
||||
{
|
||||
grub_error (GRUB_ERR_BAD_FS, "not an AFFS filesystem");
|
||||
goto fail;
|
||||
}
|
||||
|
||||
/* Test if the filesystem is a OFS filesystem. */
|
||||
if (! (data->bblock.flags & GRUB_AFFS_FLAG_FFS))
|
||||
{
|
||||
grub_error (GRUB_ERR_BAD_FS, "OFS not yet supported");
|
||||
goto fail;
|
||||
}
|
||||
|
||||
/* Read the bootblock. */
|
||||
grub_disk_read (disk, 0, 0, sizeof (struct grub_affs_bblock),
|
||||
&data->bblock);
|
||||
if (grub_errno)
|
||||
goto fail;
|
||||
|
||||
/* No sane person uses more than 8KB for a block. At least I hope
|
||||
for that person because in that case this won't work. */
|
||||
rootblock = grub_malloc (GRUB_DISK_SECTOR_SIZE * 16);
|
||||
if (!rootblock)
|
||||
goto fail;
|
||||
|
||||
rblock = (struct grub_affs_rblock *) rootblock;
|
||||
|
||||
/* Read the rootblock. */
|
||||
grub_disk_read (disk, (disk->total_sectors >> 1) + blocksize, 0,
|
||||
GRUB_DISK_SECTOR_SIZE * 16, rootblock);
|
||||
if (grub_errno)
|
||||
goto fail;
|
||||
|
||||
/* 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;
|
||||
unsigned int i;
|
||||
|
||||
for (i = 0; i < GRUB_DISK_SECTOR_SIZE / sizeof (*currblock); i++)
|
||||
checksum += grub_be_to_cpu32 (currblock[i]);
|
||||
|
||||
if (checksumr == -checksum)
|
||||
break;
|
||||
}
|
||||
if (-checksum != checksumr)
|
||||
{
|
||||
grub_error (GRUB_ERR_BAD_FS, "AFFS blocksize couldn't be determined");
|
||||
goto fail;
|
||||
}
|
||||
blocksize++;
|
||||
|
||||
data->blocksize = blocksize;
|
||||
data->disk = disk;
|
||||
data->htsize = grub_be_to_cpu32 (rblock->htsize);
|
||||
data->diropen.data = data;
|
||||
data->diropen.block = (disk->total_sectors >> 1);
|
||||
|
||||
grub_free (rootblock);
|
||||
|
||||
return data;
|
||||
|
||||
fail:
|
||||
if (grub_errno == GRUB_ERR_OUT_OF_RANGE)
|
||||
grub_error (GRUB_ERR_BAD_FS, "not an AFFS filesystem");
|
||||
|
||||
grub_free (data);
|
||||
grub_free (rootblock);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
static char *
|
||||
grub_affs_read_symlink (grub_fshelp_node_t node)
|
||||
{
|
||||
struct grub_affs_data *data = node->data;
|
||||
char *symlink;
|
||||
|
||||
symlink = grub_malloc (GRUB_AFFS_SYMLINK_SIZE (data->blocksize));
|
||||
if (!symlink)
|
||||
return 0;
|
||||
|
||||
grub_disk_read (data->disk, node->block, GRUB_AFFS_SYMLINK_OFFSET,
|
||||
GRUB_AFFS_SYMLINK_SIZE (data->blocksize), symlink);
|
||||
if (grub_errno)
|
||||
{
|
||||
grub_free (symlink);
|
||||
return 0;
|
||||
}
|
||||
grub_dprintf ("affs", "Symlink: `%s'\n", symlink);
|
||||
return symlink;
|
||||
}
|
||||
|
||||
|
||||
static int
|
||||
grub_affs_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))
|
||||
{
|
||||
int i;
|
||||
struct grub_affs_file file;
|
||||
struct grub_fshelp_node *node = 0;
|
||||
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);
|
||||
|
||||
int NESTED_FUNC_ATTR grub_affs_create_node (const char *name, int block,
|
||||
int size, int type)
|
||||
{
|
||||
node = grub_malloc (sizeof (*node));
|
||||
if (!node)
|
||||
{
|
||||
grub_free (hashtable);
|
||||
return 1;
|
||||
}
|
||||
|
||||
node->data = data;
|
||||
node->size = size;
|
||||
node->block = block;
|
||||
node->parent = grub_be_to_cpu32 (file.parent);
|
||||
|
||||
if (hook (name, type, node))
|
||||
{
|
||||
grub_free (hashtable);
|
||||
return 1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
hashtable = grub_malloc (data->htsize * sizeof (*hashtable));
|
||||
if (!hashtable)
|
||||
return 1;
|
||||
|
||||
grub_disk_read (data->disk, dir->block, GRUB_AFFS_HASHTABLE_OFFSET,
|
||||
data->htsize * sizeof (*hashtable), (char *) hashtable);
|
||||
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])
|
||||
continue;
|
||||
|
||||
/* Every entry in the hashtable can be chained. Read the entire
|
||||
chain. */
|
||||
next = grub_be_to_cpu32 (hashtable[i]);
|
||||
|
||||
while (next)
|
||||
{
|
||||
grub_disk_read (data->disk, next + data->blocksize - 1,
|
||||
data->blocksize * GRUB_DISK_SECTOR_SIZE
|
||||
- GRUB_AFFS_FILE_LOCATION,
|
||||
sizeof (file), (char *) &file);
|
||||
if (grub_errno)
|
||||
goto fail;
|
||||
|
||||
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))
|
||||
return 1;
|
||||
|
||||
next = grub_be_to_cpu32 (file.next);
|
||||
}
|
||||
}
|
||||
|
||||
grub_free (hashtable);
|
||||
return 0;
|
||||
|
||||
fail:
|
||||
grub_free (node);
|
||||
grub_free (hashtable);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
/* Open a file named NAME and initialize FILE. */
|
||||
static grub_err_t
|
||||
grub_affs_open (struct grub_file *file, const char *name)
|
||||
{
|
||||
struct grub_affs_data *data;
|
||||
struct grub_fshelp_node *fdiro = 0;
|
||||
|
||||
grub_dl_ref (my_mod);
|
||||
|
||||
data = grub_affs_mount (file->device->disk);
|
||||
if (!data)
|
||||
goto fail;
|
||||
|
||||
grub_fshelp_find_file (name, &data->diropen, &fdiro, grub_affs_iterate_dir,
|
||||
grub_affs_read_symlink, GRUB_FSHELP_REG);
|
||||
if (grub_errno)
|
||||
goto fail;
|
||||
|
||||
file->size = fdiro->size;
|
||||
data->diropen = *fdiro;
|
||||
grub_free (fdiro);
|
||||
|
||||
file->data = data;
|
||||
file->offset = 0;
|
||||
|
||||
return 0;
|
||||
|
||||
fail:
|
||||
if (data && fdiro != &data->diropen)
|
||||
grub_free (fdiro);
|
||||
grub_free (data);
|
||||
|
||||
grub_dl_unref (my_mod);
|
||||
|
||||
return grub_errno;
|
||||
}
|
||||
|
||||
|
||||
static grub_err_t
|
||||
grub_affs_close (grub_file_t file)
|
||||
{
|
||||
grub_free (file->data);
|
||||
|
||||
grub_dl_unref (my_mod);
|
||||
|
||||
return GRUB_ERR_NONE;
|
||||
}
|
||||
|
||||
|
||||
/* Read LEN bytes data from FILE into BUF. */
|
||||
static grub_ssize_t
|
||||
grub_affs_read (grub_file_t file, char *buf, grub_size_t len)
|
||||
{
|
||||
struct grub_affs_data *data =
|
||||
(struct grub_affs_data *) file->data;
|
||||
|
||||
int size = grub_affs_read_file (&data->diropen, file->read_hook,
|
||||
file->offset, len, buf);
|
||||
|
||||
return size;
|
||||
}
|
||||
|
||||
|
||||
static grub_err_t
|
||||
grub_affs_dir (grub_device_t device, const char *path,
|
||||
int (*hook) (const char *filename,
|
||||
const struct grub_dirhook_info *info))
|
||||
{
|
||||
struct grub_affs_data *data = 0;
|
||||
struct grub_fshelp_node *fdiro = 0;
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
grub_dl_ref (my_mod);
|
||||
|
||||
data = grub_affs_mount (device->disk);
|
||||
if (!data)
|
||||
goto fail;
|
||||
|
||||
grub_fshelp_find_file (path, &data->diropen, &fdiro, grub_affs_iterate_dir,
|
||||
grub_affs_read_symlink, GRUB_FSHELP_DIR);
|
||||
if (grub_errno)
|
||||
goto fail;
|
||||
|
||||
grub_affs_iterate_dir (fdiro, iterate);
|
||||
|
||||
fail:
|
||||
if (data && fdiro != &data->diropen)
|
||||
grub_free (fdiro);
|
||||
grub_free (data);
|
||||
|
||||
grub_dl_unref (my_mod);
|
||||
|
||||
return grub_errno;
|
||||
}
|
||||
|
||||
|
||||
static grub_err_t
|
||||
grub_affs_label (grub_device_t device, char **label)
|
||||
{
|
||||
struct grub_affs_data *data;
|
||||
struct grub_affs_file file;
|
||||
grub_disk_t disk = device->disk;
|
||||
|
||||
grub_dl_ref (my_mod);
|
||||
|
||||
data = grub_affs_mount (disk);
|
||||
if (data)
|
||||
{
|
||||
/* 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,
|
||||
data->blocksize * (GRUB_DISK_SECTOR_SIZE
|
||||
- GRUB_AFFS_FILE_LOCATION),
|
||||
sizeof (file), &file);
|
||||
if (grub_errno)
|
||||
return 0;
|
||||
|
||||
*label = grub_strndup ((char *) (file.name), file.namelen);
|
||||
}
|
||||
else
|
||||
*label = 0;
|
||||
|
||||
grub_dl_unref (my_mod);
|
||||
|
||||
grub_free (data);
|
||||
|
||||
return grub_errno;
|
||||
}
|
||||
|
||||
|
||||
static struct grub_fs grub_affs_fs =
|
||||
{
|
||||
.name = "affs",
|
||||
.dir = grub_affs_dir,
|
||||
.open = grub_affs_open,
|
||||
.read = grub_affs_read,
|
||||
.close = grub_affs_close,
|
||||
.label = grub_affs_label,
|
||||
.next = 0
|
||||
};
|
||||
|
||||
GRUB_MOD_INIT(affs)
|
||||
{
|
||||
grub_fs_register (&grub_affs_fs);
|
||||
my_mod = mod;
|
||||
}
|
||||
|
||||
GRUB_MOD_FINI(affs)
|
||||
{
|
||||
grub_fs_unregister (&grub_affs_fs);
|
||||
}
|
718
grub-core/fs/afs.c
Normal file
718
grub-core/fs/afs.c
Normal file
|
@ -0,0 +1,718 @@
|
|||
/* afs.c - The native AtheOS file-system. */
|
||||
/*
|
||||
* 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/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>
|
||||
|
||||
#ifdef MODE_BIGENDIAN
|
||||
#define GRUB_AFS_FSNAME_SUFFIX "_be"
|
||||
#else
|
||||
#define GRUB_AFS_FSNAME_SUFFIX ""
|
||||
#endif
|
||||
|
||||
#ifdef MODE_BFS
|
||||
#define GRUB_AFS_FSNAME "befs" GRUB_AFS_FSNAME_SUFFIX
|
||||
#else
|
||||
#define GRUB_AFS_FSNAME "afs" GRUB_AFS_FSNAME_SUFFIX
|
||||
#endif
|
||||
|
||||
#define GRUB_AFS_DIRECT_BLOCK_COUNT 12
|
||||
#define GRUB_AFS_BLOCKS_PER_DI_RUN 4
|
||||
|
||||
#ifdef MODE_BFS
|
||||
#define GRUB_AFS_SBLOCK_SECTOR 1
|
||||
#define GRUB_AFS_SBLOCK_MAGIC1 0x42465331 /* BFS1. */
|
||||
#else
|
||||
#define GRUB_AFS_SBLOCK_SECTOR 2
|
||||
#define GRUB_AFS_SBLOCK_MAGIC1 0x41465331 /* AFS1. */
|
||||
#endif
|
||||
|
||||
#define GRUB_AFS_SBLOCK_MAGIC2 0xdd121031
|
||||
#define GRUB_AFS_SBLOCK_MAGIC3 0x15b6830e
|
||||
|
||||
#define GRUB_AFS_INODE_MAGIC 0x64358428
|
||||
|
||||
#ifdef MODE_BFS
|
||||
#define GRUB_AFS_BTREE_MAGIC 0x69f6c2e8
|
||||
#else
|
||||
#define GRUB_AFS_BTREE_MAGIC 0x65768995
|
||||
#endif
|
||||
|
||||
#define GRUB_AFS_BNODE_SIZE 1024
|
||||
|
||||
#define GRUB_AFS_S_IFMT 00170000
|
||||
#define GRUB_AFS_S_IFLNK 0120000
|
||||
|
||||
#define GRUB_AFS_S_IFREG 0100000
|
||||
#define GRUB_AFS_S_IFDIR 0040000
|
||||
#define GRUB_AFS_S_IFIFO 0010000
|
||||
|
||||
#define GRUB_AFS_NULL_VAL ((grub_afs_bvalue_t)-1)
|
||||
|
||||
#ifdef MODE_BIGENDIAN
|
||||
#define grub_afs_to_cpu16(x) grub_be_to_cpu16 (x)
|
||||
#define grub_afs_to_cpu32(x) grub_be_to_cpu32 (x)
|
||||
#define grub_afs_to_cpu64(x) grub_be_to_cpu64 (x)
|
||||
#else
|
||||
#define grub_afs_to_cpu16(x) grub_le_to_cpu16 (x)
|
||||
#define grub_afs_to_cpu32(x) grub_le_to_cpu32 (x)
|
||||
#define grub_afs_to_cpu64(x) grub_le_to_cpu64 (x)
|
||||
#endif
|
||||
|
||||
#ifdef MODE_BFS
|
||||
#define B_KEY_INDEX_ALIGN 8
|
||||
#else
|
||||
#define B_KEY_INDEX_ALIGN 4
|
||||
#endif
|
||||
|
||||
#define B_KEY_INDEX_OFFSET(node) ((grub_uint16_t *) \
|
||||
((char *) (node) \
|
||||
+ ALIGN_UP (sizeof (struct grub_afs_bnode) \
|
||||
+ node->key_size, \
|
||||
B_KEY_INDEX_ALIGN)))
|
||||
|
||||
#define B_KEY_VALUE_OFFSET(node) ((grub_afs_bvalue_t *) \
|
||||
((char *) B_KEY_INDEX_OFFSET (node) + \
|
||||
node->key_count * 2))
|
||||
|
||||
typedef grub_uint64_t grub_afs_off_t;
|
||||
typedef grub_uint64_t grub_afs_bigtime;
|
||||
typedef grub_uint64_t grub_afs_bvalue_t;
|
||||
|
||||
struct grub_afs_blockrun
|
||||
{
|
||||
grub_uint32_t group;
|
||||
grub_uint16_t start;
|
||||
grub_uint16_t len;
|
||||
} __attribute__ ((packed));
|
||||
|
||||
struct grub_afs_datastream
|
||||
{
|
||||
struct grub_afs_blockrun direct[GRUB_AFS_DIRECT_BLOCK_COUNT];
|
||||
grub_afs_off_t max_direct_range;
|
||||
struct grub_afs_blockrun indirect;
|
||||
grub_afs_off_t max_indirect_range;
|
||||
struct grub_afs_blockrun double_indirect;
|
||||
grub_afs_off_t max_double_indirect_range;
|
||||
grub_afs_off_t size;
|
||||
} __attribute__ ((packed));
|
||||
|
||||
struct grub_afs_bnode
|
||||
{
|
||||
grub_afs_bvalue_t left;
|
||||
grub_afs_bvalue_t right;
|
||||
grub_afs_bvalue_t overflow;
|
||||
#ifdef MODE_BFS
|
||||
grub_uint16_t key_count;
|
||||
grub_uint16_t key_size;
|
||||
#else
|
||||
grub_uint32_t key_count;
|
||||
grub_uint32_t key_size;
|
||||
#endif
|
||||
char key_data[0];
|
||||
} __attribute__ ((packed));
|
||||
|
||||
#ifdef MODE_BFS
|
||||
struct grub_afs_btree
|
||||
{
|
||||
grub_uint32_t magic;
|
||||
grub_uint32_t unused1;
|
||||
grub_uint32_t tree_depth;
|
||||
grub_uint32_t unused2;
|
||||
grub_afs_bvalue_t root;
|
||||
grub_uint32_t unused3[4];
|
||||
} __attribute__ ((packed));
|
||||
#else
|
||||
struct grub_afs_btree
|
||||
{
|
||||
grub_uint32_t magic;
|
||||
grub_afs_bvalue_t root;
|
||||
grub_uint32_t tree_depth;
|
||||
grub_afs_bvalue_t last_node;
|
||||
grub_afs_bvalue_t first_free;
|
||||
} __attribute__ ((packed));
|
||||
#endif
|
||||
|
||||
/* Beware that following structure describes AtheFS and if you write code
|
||||
which uses currently unused fields check it with both AtheFS and BeFS.
|
||||
*/
|
||||
struct grub_afs_sblock
|
||||
{
|
||||
char name[32];
|
||||
grub_uint32_t magic1;
|
||||
grub_uint32_t byte_order;
|
||||
grub_uint32_t block_size;
|
||||
grub_uint32_t block_shift;
|
||||
grub_afs_off_t num_blocks;
|
||||
grub_afs_off_t used_blocks;
|
||||
grub_uint32_t inode_size;
|
||||
grub_uint32_t magic2;
|
||||
grub_uint32_t block_per_group; /* Number of blocks per allocation
|
||||
group. (Max 65536) */
|
||||
grub_uint32_t alloc_group_shift; /* Number of bits to shift a group
|
||||
number to get a byte address. */
|
||||
grub_uint32_t alloc_group_count;
|
||||
grub_uint32_t flags;
|
||||
struct grub_afs_blockrun log_block;
|
||||
grub_afs_off_t log_start;
|
||||
grub_uint32_t valid_log_blocks;
|
||||
grub_uint32_t log_size;
|
||||
grub_uint32_t magic3;
|
||||
struct grub_afs_blockrun root_dir; /* Root dir inode. */
|
||||
struct grub_afs_blockrun deleted_files; /* Directory containing files
|
||||
scheduled for deletion. */
|
||||
struct grub_afs_blockrun index_dir; /* Directory of index files. */
|
||||
grub_uint32_t boot_loader_size;
|
||||
grub_uint32_t pad[7];
|
||||
} __attribute__ ((packed));
|
||||
|
||||
struct grub_afs_inode
|
||||
{
|
||||
grub_uint32_t magic1;
|
||||
struct grub_afs_blockrun inode_num;
|
||||
grub_uint32_t uid;
|
||||
grub_uint32_t gid;
|
||||
grub_uint32_t mode;
|
||||
grub_uint32_t flags;
|
||||
#ifndef MODE_BFS
|
||||
grub_uint32_t link_count;
|
||||
#endif
|
||||
grub_afs_bigtime create_time;
|
||||
grub_afs_bigtime modified_time;
|
||||
struct grub_afs_blockrun parent;
|
||||
struct grub_afs_blockrun attrib_dir;
|
||||
grub_uint32_t index_type; /* Key data-key only used for index files. */
|
||||
grub_uint32_t inode_size;
|
||||
grub_uint32_t unused;
|
||||
struct grub_afs_datastream stream;
|
||||
grub_uint32_t pad[4];
|
||||
grub_uint32_t small_data[1];
|
||||
} __attribute__ ((packed));
|
||||
|
||||
struct grub_fshelp_node
|
||||
{
|
||||
struct grub_afs_data *data;
|
||||
struct grub_afs_inode inode;
|
||||
};
|
||||
|
||||
struct grub_afs_data
|
||||
{
|
||||
grub_disk_t disk;
|
||||
struct grub_afs_sblock sblock;
|
||||
struct grub_afs_inode *inode;
|
||||
struct grub_fshelp_node diropen;
|
||||
};
|
||||
|
||||
static grub_dl_t my_mod;
|
||||
|
||||
static grub_afs_off_t
|
||||
grub_afs_run_to_num (struct grub_afs_sblock *sb,
|
||||
struct grub_afs_blockrun *run)
|
||||
{
|
||||
return ((grub_afs_off_t) grub_afs_to_cpu32 (run->group)
|
||||
* sb->block_per_group + grub_afs_to_cpu16 (run->start));
|
||||
}
|
||||
|
||||
static grub_err_t
|
||||
grub_afs_read_inode (struct grub_afs_data *data,
|
||||
grub_uint32_t ino, struct grub_afs_inode *inode)
|
||||
{
|
||||
return grub_disk_read (data->disk,
|
||||
ino *
|
||||
(data->sblock.block_size >> GRUB_DISK_SECTOR_BITS),
|
||||
0, sizeof (struct grub_afs_inode),
|
||||
inode);
|
||||
}
|
||||
|
||||
static grub_disk_addr_t
|
||||
grub_afs_read_block (grub_fshelp_node_t node, grub_disk_addr_t fileblock)
|
||||
{
|
||||
struct grub_afs_sblock *sb = &node->data->sblock;
|
||||
struct grub_afs_datastream *ds = &node->inode.stream;
|
||||
|
||||
if (fileblock < grub_afs_to_cpu64 (ds->max_direct_range))
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i = 0; i < GRUB_AFS_DIRECT_BLOCK_COUNT; i++)
|
||||
{
|
||||
if (fileblock < grub_afs_to_cpu16 (ds->direct[i].len))
|
||||
return grub_afs_run_to_num (sb, &ds->direct[i]) + fileblock;
|
||||
fileblock -= grub_afs_to_cpu16 (ds->direct[i].len);
|
||||
}
|
||||
}
|
||||
else if (fileblock < grub_afs_to_cpu64 (ds->max_indirect_range))
|
||||
{
|
||||
int ptrs_per_blk = sb->block_size / sizeof (struct grub_afs_blockrun);
|
||||
struct grub_afs_blockrun indir[ptrs_per_blk];
|
||||
grub_afs_off_t blk = grub_afs_run_to_num (sb, &ds->indirect);
|
||||
int i;
|
||||
|
||||
fileblock -= grub_afs_to_cpu64 (ds->max_direct_range);
|
||||
for (i = 0; i < ds->indirect.len; i++, blk++)
|
||||
{
|
||||
int j;
|
||||
|
||||
if (grub_disk_read (node->data->disk,
|
||||
blk * (sb->block_size >> GRUB_DISK_SECTOR_BITS),
|
||||
0, sizeof (indir),
|
||||
indir))
|
||||
return 0;
|
||||
|
||||
for (j = 0; j < ptrs_per_blk; j++)
|
||||
{
|
||||
if (fileblock < grub_afs_to_cpu16 (indir[j].len))
|
||||
return grub_afs_run_to_num (sb, &indir[j]) + fileblock;
|
||||
|
||||
fileblock -= grub_afs_to_cpu16 (indir[j].len);
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
int ptrs_per_blk = sb->block_size / sizeof (struct grub_afs_blockrun);
|
||||
struct grub_afs_blockrun indir[ptrs_per_blk];
|
||||
|
||||
/* ([idblk][idptr]) ([dblk][dptr]) [blk] */
|
||||
int cur_pos = fileblock - grub_afs_to_cpu64 (ds->max_indirect_range);
|
||||
|
||||
int dptr_size = GRUB_AFS_BLOCKS_PER_DI_RUN;
|
||||
int dblk_size = dptr_size * ptrs_per_blk;
|
||||
int idptr_size = dblk_size * GRUB_AFS_BLOCKS_PER_DI_RUN;
|
||||
int idblk_size = idptr_size * ptrs_per_blk;
|
||||
|
||||
int off = cur_pos % GRUB_AFS_BLOCKS_PER_DI_RUN;
|
||||
int dptr = (cur_pos / dptr_size) % ptrs_per_blk;
|
||||
int dblk = (cur_pos / dblk_size) % GRUB_AFS_BLOCKS_PER_DI_RUN;
|
||||
int idptr = (cur_pos / idptr_size) % ptrs_per_blk;
|
||||
int idblk = (cur_pos / idblk_size);
|
||||
|
||||
if (grub_disk_read (node->data->disk,
|
||||
(grub_afs_run_to_num (sb, &ds->double_indirect)
|
||||
+ idblk) *
|
||||
(sb->block_size >> GRUB_DISK_SECTOR_BITS),
|
||||
0, sizeof (indir),
|
||||
indir))
|
||||
return 0;
|
||||
|
||||
if (grub_disk_read (node->data->disk,
|
||||
(grub_afs_run_to_num (sb, &indir[idptr]) + dblk) *
|
||||
(sb->block_size >> GRUB_DISK_SECTOR_BITS),
|
||||
0, sizeof (indir),
|
||||
indir))
|
||||
return 0;
|
||||
|
||||
return grub_afs_run_to_num (sb, &indir[dptr]) + off;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
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)
|
||||
{
|
||||
return grub_fshelp_read_file (node->data->disk, node, read_hook,
|
||||
pos, len, buf, grub_afs_read_block,
|
||||
grub_afs_to_cpu64 (node->inode.stream.size),
|
||||
node->data->sblock.block_shift
|
||||
- GRUB_DISK_SECTOR_BITS);
|
||||
}
|
||||
|
||||
static char *
|
||||
grub_afs_read_symlink (grub_fshelp_node_t node)
|
||||
{
|
||||
char *ret;
|
||||
grub_afs_off_t size = grub_afs_to_cpu64 (node->inode.stream.size);
|
||||
|
||||
if (size == 0)
|
||||
{
|
||||
size = sizeof (node->inode.stream);
|
||||
ret = grub_zalloc (size + 1);
|
||||
if (! ret)
|
||||
return 0;
|
||||
grub_memcpy (ret, (char *) &(node->inode.stream),
|
||||
sizeof (node->inode.stream));
|
||||
return ret;
|
||||
}
|
||||
ret = grub_zalloc (size + 1);
|
||||
if (! ret)
|
||||
return 0;
|
||||
grub_afs_read_file (node, 0, 0, size, ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int
|
||||
grub_afs_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))
|
||||
{
|
||||
struct grub_afs_btree head;
|
||||
char node_data [GRUB_AFS_BNODE_SIZE];
|
||||
struct grub_afs_bnode *node = (struct grub_afs_bnode *) node_data;
|
||||
int i;
|
||||
|
||||
if ((dir->inode.stream.size == 0)
|
||||
|| ((grub_afs_to_cpu32 (dir->inode.mode) & GRUB_AFS_S_IFMT)
|
||||
!= GRUB_AFS_S_IFDIR))
|
||||
return 0;
|
||||
|
||||
grub_afs_read_file (dir, 0, 0, sizeof (head), (char *) &head);
|
||||
if (grub_errno)
|
||||
return 0;
|
||||
|
||||
grub_afs_read_file (dir, 0, grub_afs_to_cpu64 (head.root),
|
||||
GRUB_AFS_BNODE_SIZE, (char *) node);
|
||||
if (grub_errno)
|
||||
return 0;
|
||||
|
||||
for (i = 0; i < (int) grub_afs_to_cpu32 (head.tree_depth) - 1; i++)
|
||||
{
|
||||
grub_afs_bvalue_t blk;
|
||||
|
||||
blk = grub_afs_to_cpu64(B_KEY_VALUE_OFFSET (node) [0]);
|
||||
grub_afs_read_file (dir, 0, blk, GRUB_AFS_BNODE_SIZE, (char *) node);
|
||||
if (grub_errno)
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (node->key_count)
|
||||
{
|
||||
grub_uint32_t cur_key = 0;
|
||||
|
||||
while (1)
|
||||
{
|
||||
int key_start, key_size;
|
||||
grub_uint16_t *index;
|
||||
|
||||
index = B_KEY_INDEX_OFFSET (node);
|
||||
|
||||
key_start = (cur_key > 0)
|
||||
? grub_afs_to_cpu16 (index[cur_key - 1]) : 0;
|
||||
key_size = grub_afs_to_cpu16 (index[cur_key]) - key_start;
|
||||
if (key_size > 0)
|
||||
{
|
||||
char filename [key_size + 1];
|
||||
struct grub_fshelp_node *fdiro;
|
||||
int mode, type;
|
||||
|
||||
fdiro = grub_malloc (sizeof (struct grub_fshelp_node));
|
||||
if (! fdiro)
|
||||
return 0;
|
||||
|
||||
fdiro->data = dir->data;
|
||||
if (grub_afs_read_inode (dir->data,
|
||||
grub_afs_to_cpu64
|
||||
(B_KEY_VALUE_OFFSET (node) [cur_key]),
|
||||
&fdiro->inode))
|
||||
return 0;
|
||||
|
||||
grub_memcpy (filename, &node->key_data[key_start], key_size);
|
||||
filename [key_size] = 0;
|
||||
|
||||
mode = (grub_afs_to_cpu32 (fdiro->inode.mode) & GRUB_AFS_S_IFMT);
|
||||
if (mode == GRUB_AFS_S_IFDIR)
|
||||
type = GRUB_FSHELP_DIR;
|
||||
else if (mode == GRUB_AFS_S_IFREG)
|
||||
type = GRUB_FSHELP_REG;
|
||||
else if (mode == GRUB_AFS_S_IFLNK)
|
||||
type = GRUB_FSHELP_SYMLINK;
|
||||
else
|
||||
type = GRUB_FSHELP_UNKNOWN;
|
||||
|
||||
if (hook (filename, type, fdiro))
|
||||
return 1;
|
||||
}
|
||||
|
||||
cur_key++;
|
||||
if (cur_key >= grub_afs_to_cpu32 (node->key_count))
|
||||
{
|
||||
if (node->right == GRUB_AFS_NULL_VAL)
|
||||
break;
|
||||
|
||||
grub_afs_read_file (dir, 0, grub_afs_to_cpu64 (node->right),
|
||||
GRUB_AFS_BNODE_SIZE, (char *) node);
|
||||
if (grub_errno)
|
||||
return 0;
|
||||
|
||||
cur_key = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int
|
||||
grub_afs_validate_sblock (struct grub_afs_sblock *sb)
|
||||
{
|
||||
if (grub_afs_to_cpu32 (sb->magic1) == GRUB_AFS_SBLOCK_MAGIC1)
|
||||
{
|
||||
sb->magic2 = grub_afs_to_cpu32 (sb->magic2);
|
||||
sb->magic3 = grub_afs_to_cpu32 (sb->magic3);
|
||||
sb->block_shift = grub_afs_to_cpu32 (sb->block_shift);
|
||||
sb->block_size = grub_afs_to_cpu32 (sb->block_size);
|
||||
sb->used_blocks = grub_afs_to_cpu64 (sb->used_blocks);
|
||||
sb->num_blocks = grub_afs_to_cpu64 (sb->num_blocks);
|
||||
sb->inode_size = grub_afs_to_cpu32 (sb->inode_size);
|
||||
sb->alloc_group_count = grub_afs_to_cpu32 (sb->alloc_group_count);
|
||||
sb->alloc_group_shift = grub_afs_to_cpu32 (sb->alloc_group_shift);
|
||||
sb->block_per_group = grub_afs_to_cpu32 (sb->block_per_group);
|
||||
sb->alloc_group_count = grub_afs_to_cpu32 (sb->alloc_group_count);
|
||||
sb->log_size = grub_afs_to_cpu32 (sb->log_size);
|
||||
}
|
||||
else
|
||||
return 0;
|
||||
|
||||
if ((sb->magic2 != GRUB_AFS_SBLOCK_MAGIC2) ||
|
||||
(sb->magic3 != GRUB_AFS_SBLOCK_MAGIC3))
|
||||
return 0;
|
||||
|
||||
#ifdef MODE_BFS
|
||||
sb->block_per_group = 1 << (sb->alloc_group_shift);
|
||||
#endif
|
||||
|
||||
if (((grub_uint32_t) (1 << sb->block_shift) != sb->block_size)
|
||||
|| (sb->used_blocks > sb->num_blocks )
|
||||
|| (sb->inode_size != sb->block_size)
|
||||
|| (0 == sb->block_size)
|
||||
#ifndef MODE_BFS
|
||||
|| ((grub_uint32_t) (1 << sb->alloc_group_shift) !=
|
||||
sb->block_per_group * sb->block_size)
|
||||
|| (sb->alloc_group_count * sb->block_per_group < sb->num_blocks)
|
||||
|| (grub_afs_to_cpu16 (sb->log_block.len) != sb->log_size)
|
||||
|| (grub_afs_to_cpu32 (sb->valid_log_blocks) > sb->log_size)
|
||||
#endif
|
||||
)
|
||||
return 0;
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
static struct grub_afs_data *
|
||||
grub_afs_mount (grub_disk_t disk)
|
||||
{
|
||||
struct grub_afs_data *data = 0;
|
||||
|
||||
data = grub_malloc (sizeof (struct grub_afs_data));
|
||||
if (!data)
|
||||
return 0;
|
||||
|
||||
/* Read the superblock. */
|
||||
if (grub_disk_read (disk, GRUB_AFS_SBLOCK_SECTOR, 0,
|
||||
sizeof (struct grub_afs_sblock), &data->sblock))
|
||||
goto fail;
|
||||
|
||||
if (! grub_afs_validate_sblock (&data->sblock))
|
||||
goto fail;
|
||||
|
||||
data->diropen.data = data;
|
||||
data->inode = &data->diropen.inode;
|
||||
data->disk = disk;
|
||||
|
||||
if (grub_afs_read_inode (data,
|
||||
grub_afs_run_to_num (&data->sblock,
|
||||
&data->sblock.root_dir),
|
||||
data->inode))
|
||||
goto fail;
|
||||
|
||||
return data;
|
||||
|
||||
fail:
|
||||
grub_error (GRUB_ERR_BAD_FS, "not an " GRUB_AFS_FSNAME " filesystem");
|
||||
|
||||
grub_free (data);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static grub_err_t
|
||||
grub_afs_open (struct grub_file *file, const char *name)
|
||||
{
|
||||
struct grub_afs_data *data;
|
||||
struct grub_fshelp_node *fdiro = 0;
|
||||
|
||||
grub_dl_ref (my_mod);
|
||||
|
||||
data = grub_afs_mount (file->device->disk);
|
||||
if (! data)
|
||||
goto fail;
|
||||
|
||||
grub_fshelp_find_file (name, &data->diropen, &fdiro, grub_afs_iterate_dir,
|
||||
grub_afs_read_symlink, GRUB_FSHELP_REG);
|
||||
if (grub_errno)
|
||||
goto fail;
|
||||
|
||||
grub_memcpy (data->inode, &fdiro->inode, sizeof (struct grub_afs_inode));
|
||||
grub_free (fdiro);
|
||||
|
||||
file->size = grub_afs_to_cpu64 (data->inode->stream.size);
|
||||
file->data = data;
|
||||
file->offset = 0;
|
||||
|
||||
return 0;
|
||||
|
||||
fail:
|
||||
grub_free (data);
|
||||
|
||||
grub_dl_unref (my_mod);
|
||||
|
||||
return grub_errno;
|
||||
}
|
||||
|
||||
static grub_ssize_t
|
||||
grub_afs_read (grub_file_t file, char *buf, grub_size_t len)
|
||||
{
|
||||
struct grub_afs_data *data = (struct grub_afs_data *) file->data;
|
||||
|
||||
return grub_afs_read_file (&data->diropen, file->read_hook,
|
||||
file->offset, len, buf);
|
||||
}
|
||||
|
||||
static grub_err_t
|
||||
grub_afs_close (grub_file_t file)
|
||||
{
|
||||
grub_free (file->data);
|
||||
|
||||
grub_dl_unref (my_mod);
|
||||
|
||||
return GRUB_ERR_NONE;
|
||||
}
|
||||
|
||||
static grub_err_t
|
||||
grub_afs_dir (grub_device_t device, const char *path,
|
||||
int (*hook) (const char *filename,
|
||||
const struct grub_dirhook_info *info))
|
||||
{
|
||||
struct grub_afs_data *data = 0;
|
||||
struct grub_fshelp_node *fdiro = 0;
|
||||
|
||||
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;
|
||||
#ifdef MODE_BFS
|
||||
info.mtime = grub_afs_to_cpu64 (node->inode.modified_time) >> 16;
|
||||
#else
|
||||
info.mtime = grub_divmod64 (grub_afs_to_cpu64 (node->inode.modified_time),
|
||||
1000000, 0);
|
||||
#endif
|
||||
grub_free (node);
|
||||
return hook (filename, &info);
|
||||
}
|
||||
|
||||
grub_dl_ref (my_mod);
|
||||
|
||||
data = grub_afs_mount (device->disk);
|
||||
if (! data)
|
||||
goto fail;
|
||||
|
||||
grub_fshelp_find_file (path, &data->diropen, &fdiro, grub_afs_iterate_dir,
|
||||
grub_afs_read_symlink, GRUB_FSHELP_DIR);
|
||||
if (grub_errno)
|
||||
goto fail;
|
||||
|
||||
grub_afs_iterate_dir (fdiro, iterate);
|
||||
|
||||
if (fdiro != &data->diropen)
|
||||
grub_free (fdiro);
|
||||
|
||||
fail:
|
||||
grub_free (data);
|
||||
|
||||
grub_dl_unref (my_mod);
|
||||
|
||||
return grub_errno;
|
||||
}
|
||||
|
||||
static grub_err_t
|
||||
grub_afs_label (grub_device_t device, char **label)
|
||||
{
|
||||
struct grub_afs_data *data;
|
||||
grub_disk_t disk = device->disk;
|
||||
|
||||
grub_dl_ref (my_mod);
|
||||
|
||||
data = grub_afs_mount (disk);
|
||||
if (data)
|
||||
*label = grub_strndup (data->sblock.name, sizeof (data->sblock.name));
|
||||
else
|
||||
*label = NULL;
|
||||
|
||||
grub_dl_unref (my_mod);
|
||||
|
||||
grub_free (data);
|
||||
|
||||
return grub_errno;
|
||||
}
|
||||
|
||||
|
||||
static struct grub_fs grub_afs_fs = {
|
||||
.name = GRUB_AFS_FSNAME,
|
||||
.dir = grub_afs_dir,
|
||||
.open = grub_afs_open,
|
||||
.read = grub_afs_read,
|
||||
.close = grub_afs_close,
|
||||
.label = grub_afs_label,
|
||||
.next = 0
|
||||
};
|
||||
|
||||
#if defined (MODE_BIGENDIAN) && defined (MODE_BFS)
|
||||
GRUB_MOD_INIT (befs_be)
|
||||
#elif defined (MODE_BFS)
|
||||
GRUB_MOD_INIT (befs)
|
||||
#elif defined (MODE_BIGENDIAN)
|
||||
GRUB_MOD_INIT (afs_be)
|
||||
#else
|
||||
GRUB_MOD_INIT (afs)
|
||||
#endif
|
||||
{
|
||||
grub_fs_register (&grub_afs_fs);
|
||||
my_mod = mod;
|
||||
}
|
||||
|
||||
#if defined (MODE_BIGENDIAN) && defined (MODE_BFS)
|
||||
GRUB_MOD_FINI (befs_be)
|
||||
#elif defined (MODE_BFS)
|
||||
GRUB_MOD_FINI (befs)
|
||||
#elif defined (MODE_BIGENDIAN)
|
||||
GRUB_MOD_FINI (afs_be)
|
||||
#else
|
||||
GRUB_MOD_FINI (afs)
|
||||
#endif
|
||||
{
|
||||
grub_fs_unregister (&grub_afs_fs);
|
||||
}
|
2
grub-core/fs/afs_be.c
Normal file
2
grub-core/fs/afs_be.c
Normal file
|
@ -0,0 +1,2 @@
|
|||
#define MODE_BIGENDIAN 1
|
||||
#include "afs.c"
|
3
grub-core/fs/befs.c
Normal file
3
grub-core/fs/befs.c
Normal file
|
@ -0,0 +1,3 @@
|
|||
/* befs.c - The native BeOS/Haiku file-system. */
|
||||
#define MODE_BFS 1
|
||||
#include "afs.c"
|
4
grub-core/fs/befs_be.c
Normal file
4
grub-core/fs/befs_be.c
Normal file
|
@ -0,0 +1,4 @@
|
|||
/* befs.c - The native BeOS/Haiku file-system. */
|
||||
#define MODE_BFS 1
|
||||
#define MODE_BIGENDIAN 1
|
||||
#include "afs.c"
|
376
grub-core/fs/cpio.c
Normal file
376
grub-core/fs/cpio.c
Normal file
|
@ -0,0 +1,376 @@
|
|||
/* cpio.c - cpio and tar filesystem. */
|
||||
/*
|
||||
* GRUB -- GRand Unified Bootloader
|
||||
* Copyright (C) 2007,2008,2009 Free Software Foundation, Inc.
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include <grub/file.h>
|
||||
#include <grub/mm.h>
|
||||
#include <grub/misc.h>
|
||||
#include <grub/disk.h>
|
||||
#include <grub/dl.h>
|
||||
|
||||
#ifndef MODE_USTAR
|
||||
/* cpio support */
|
||||
#define MAGIC_BCPIO 070707
|
||||
struct head
|
||||
{
|
||||
grub_uint16_t magic;
|
||||
grub_uint16_t dev;
|
||||
grub_uint16_t ino;
|
||||
grub_uint16_t mode;
|
||||
grub_uint16_t uid;
|
||||
grub_uint16_t gid;
|
||||
grub_uint16_t nlink;
|
||||
grub_uint16_t rdev;
|
||||
grub_uint16_t mtime_1;
|
||||
grub_uint16_t mtime_2;
|
||||
grub_uint16_t namesize;
|
||||
grub_uint16_t filesize_1;
|
||||
grub_uint16_t filesize_2;
|
||||
} __attribute__ ((packed));
|
||||
#else
|
||||
/* tar support */
|
||||
#define MAGIC_USTAR "ustar"
|
||||
struct head
|
||||
{
|
||||
char name[100];
|
||||
char mode[8];
|
||||
char uid[8];
|
||||
char gid[8];
|
||||
char size[12];
|
||||
char mtime[12];
|
||||
char chksum[8];
|
||||
char typeflag;
|
||||
char linkname[100];
|
||||
char magic[6];
|
||||
char version[2];
|
||||
char uname[32];
|
||||
char gname[32];
|
||||
char devmajor[8];
|
||||
char devminor[8];
|
||||
char prefix[155];
|
||||
} __attribute__ ((packed));
|
||||
#endif
|
||||
|
||||
struct grub_cpio_data
|
||||
{
|
||||
grub_disk_t disk;
|
||||
grub_uint32_t hofs;
|
||||
grub_uint32_t dofs;
|
||||
grub_uint32_t size;
|
||||
};
|
||||
|
||||
static grub_dl_t my_mod;
|
||||
|
||||
static grub_err_t
|
||||
grub_cpio_find_file (struct grub_cpio_data *data, char **name,
|
||||
grub_uint32_t * ofs)
|
||||
{
|
||||
#ifndef MODE_USTAR
|
||||
struct head hd;
|
||||
|
||||
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");
|
||||
|
||||
data->size = (((grub_uint32_t) hd.filesize_1) << 16) + hd.filesize_2;
|
||||
|
||||
if (hd.namesize & 1)
|
||||
hd.namesize++;
|
||||
|
||||
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 (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)++;
|
||||
#else
|
||||
struct head hd;
|
||||
|
||||
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 (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;
|
||||
|
||||
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));
|
||||
#endif
|
||||
return GRUB_ERR_NONE;
|
||||
}
|
||||
|
||||
static struct grub_cpio_data *
|
||||
grub_cpio_mount (grub_disk_t disk)
|
||||
{
|
||||
struct head hd;
|
||||
struct grub_cpio_data *data;
|
||||
|
||||
if (grub_disk_read (disk, 0, 0, sizeof (hd), &hd))
|
||||
goto fail;
|
||||
|
||||
#ifndef MODE_USTAR
|
||||
if (hd.magic != MAGIC_BCPIO)
|
||||
#else
|
||||
if (grub_memcmp (hd.magic, MAGIC_USTAR,
|
||||
sizeof (MAGIC_USTAR) - 1))
|
||||
#endif
|
||||
goto fail;
|
||||
|
||||
data = (struct grub_cpio_data *) grub_malloc (sizeof (*data));
|
||||
if (!data)
|
||||
goto fail;
|
||||
|
||||
data->disk = disk;
|
||||
|
||||
return data;
|
||||
|
||||
fail:
|
||||
grub_error (GRUB_ERR_BAD_FS, "not a "
|
||||
#ifdef MODE_USTAR
|
||||
"tar"
|
||||
#else
|
||||
"cpio"
|
||||
#endif
|
||||
" filesystem");
|
||||
return 0;
|
||||
}
|
||||
|
||||
static grub_err_t
|
||||
grub_cpio_dir (grub_device_t device, const char *path,
|
||||
int (*hook) (const char *filename,
|
||||
const struct grub_dirhook_info *info))
|
||||
{
|
||||
struct grub_cpio_data *data;
|
||||
grub_uint32_t ofs;
|
||||
char *prev, *name;
|
||||
const char *np;
|
||||
int len;
|
||||
|
||||
grub_dl_ref (my_mod);
|
||||
|
||||
prev = 0;
|
||||
|
||||
data = grub_cpio_mount (device->disk);
|
||||
if (!data)
|
||||
goto fail;
|
||||
|
||||
np = path + 1;
|
||||
len = grub_strlen (path) - 1;
|
||||
|
||||
data->hofs = 0;
|
||||
while (1)
|
||||
{
|
||||
if (grub_cpio_find_file (data, &name, &ofs))
|
||||
goto fail;
|
||||
|
||||
if (!ofs)
|
||||
break;
|
||||
|
||||
if (grub_memcmp (np, name, len) == 0)
|
||||
{
|
||||
char *p, *n;
|
||||
|
||||
n = name + len;
|
||||
if (*n == '/')
|
||||
n++;
|
||||
|
||||
p = grub_strchr (name + len, '/');
|
||||
if (p)
|
||||
*p = 0;
|
||||
|
||||
if ((!prev) || (grub_strcmp (prev, name) != 0))
|
||||
{
|
||||
struct grub_dirhook_info info;
|
||||
grub_memset (&info, 0, sizeof (info));
|
||||
info.dir = (p != NULL);
|
||||
|
||||
hook (name + len, &info);
|
||||
if (prev)
|
||||
grub_free (prev);
|
||||
prev = name;
|
||||
}
|
||||
else
|
||||
grub_free (name);
|
||||
}
|
||||
data->hofs = ofs;
|
||||
}
|
||||
|
||||
fail:
|
||||
|
||||
if (prev)
|
||||
grub_free (prev);
|
||||
|
||||
if (data)
|
||||
grub_free (data);
|
||||
|
||||
grub_dl_unref (my_mod);
|
||||
|
||||
return grub_errno;
|
||||
}
|
||||
|
||||
static grub_err_t
|
||||
grub_cpio_open (grub_file_t file, const char *name)
|
||||
{
|
||||
struct grub_cpio_data *data;
|
||||
grub_uint32_t ofs;
|
||||
char *fn;
|
||||
int i, j;
|
||||
|
||||
grub_dl_ref (my_mod);
|
||||
|
||||
data = grub_cpio_mount (file->device->disk);
|
||||
if (!data)
|
||||
goto fail;
|
||||
|
||||
data->hofs = 0;
|
||||
while (1)
|
||||
{
|
||||
if (grub_cpio_find_file (data, &fn, &ofs))
|
||||
goto fail;
|
||||
|
||||
if (!ofs)
|
||||
{
|
||||
grub_error (GRUB_ERR_FILE_NOT_FOUND, "file not found");
|
||||
break;
|
||||
}
|
||||
|
||||
/* Compare NAME and FN by hand in order to cope with duplicate
|
||||
slashes. */
|
||||
i = 0;
|
||||
j = 0;
|
||||
while (name[i] == '/')
|
||||
i++;
|
||||
while (1)
|
||||
{
|
||||
if (name[i] != fn[j])
|
||||
goto no_match;
|
||||
|
||||
if (name[i] == '\0')
|
||||
break;
|
||||
|
||||
while (name[i] == '/' && name[i+1] == '/')
|
||||
i++;
|
||||
|
||||
i++;
|
||||
j++;
|
||||
}
|
||||
|
||||
if (name[i] != fn[j])
|
||||
goto no_match;
|
||||
|
||||
file->data = data;
|
||||
file->size = data->size;
|
||||
grub_free (fn);
|
||||
|
||||
return GRUB_ERR_NONE;
|
||||
|
||||
no_match:
|
||||
|
||||
grub_free (fn);
|
||||
data->hofs = ofs;
|
||||
}
|
||||
|
||||
fail:
|
||||
|
||||
if (data)
|
||||
grub_free (data);
|
||||
|
||||
grub_dl_unref (my_mod);
|
||||
|
||||
return grub_errno;
|
||||
}
|
||||
|
||||
static grub_ssize_t
|
||||
grub_cpio_read (grub_file_t file, char *buf, grub_size_t len)
|
||||
{
|
||||
struct grub_cpio_data *data;
|
||||
|
||||
data = file->data;
|
||||
return (grub_disk_read (data->disk, 0, data->dofs + file->offset,
|
||||
len, buf)) ? -1 : (grub_ssize_t) len;
|
||||
}
|
||||
|
||||
static grub_err_t
|
||||
grub_cpio_close (grub_file_t file)
|
||||
{
|
||||
grub_free (file->data);
|
||||
|
||||
grub_dl_unref (my_mod);
|
||||
|
||||
return grub_errno;
|
||||
}
|
||||
|
||||
static struct grub_fs grub_cpio_fs = {
|
||||
#ifdef MODE_USTAR
|
||||
.name = "tarfs",
|
||||
#else
|
||||
.name = "cpiofs",
|
||||
#endif
|
||||
.dir = grub_cpio_dir,
|
||||
.open = grub_cpio_open,
|
||||
.read = grub_cpio_read,
|
||||
.close = grub_cpio_close,
|
||||
};
|
||||
|
||||
#ifdef MODE_USTAR
|
||||
GRUB_MOD_INIT (tar)
|
||||
#else
|
||||
GRUB_MOD_INIT (cpio)
|
||||
#endif
|
||||
{
|
||||
grub_fs_register (&grub_cpio_fs);
|
||||
my_mod = mod;
|
||||
}
|
||||
|
||||
#ifdef MODE_USTAR
|
||||
GRUB_MOD_FINI (tar)
|
||||
#else
|
||||
GRUB_MOD_FINI (cpio)
|
||||
#endif
|
||||
{
|
||||
grub_fs_unregister (&grub_cpio_fs);
|
||||
}
|
951
grub-core/fs/ext2.c
Normal file
951
grub-core/fs/ext2.c
Normal file
|
@ -0,0 +1,951 @@
|
|||
/* ext2.c - Second Extended filesystem */
|
||||
/*
|
||||
* GRUB -- GRand Unified Bootloader
|
||||
* Copyright (C) 2003,2004,2005,2007,2008,2009 Free Software Foundation, Inc.
|
||||
*
|
||||
* GRUB is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* GRUB is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with GRUB. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
/* Magic value used to identify an ext2 filesystem. */
|
||||
#define EXT2_MAGIC 0xEF53
|
||||
/* Amount of indirect blocks in an inode. */
|
||||
#define INDIRECT_BLOCKS 12
|
||||
/* Maximum length of a pathname. */
|
||||
#define EXT2_PATH_MAX 4096
|
||||
/* Maximum nesting of symlinks, used to prevent a loop. */
|
||||
#define EXT2_MAX_SYMLINKCNT 8
|
||||
|
||||
/* The good old revision and the default inode size. */
|
||||
#define EXT2_GOOD_OLD_REVISION 0
|
||||
#define EXT2_GOOD_OLD_INODE_SIZE 128
|
||||
|
||||
/* Filetype used in directory entry. */
|
||||
#define FILETYPE_UNKNOWN 0
|
||||
#define FILETYPE_REG 1
|
||||
#define FILETYPE_DIRECTORY 2
|
||||
#define FILETYPE_SYMLINK 7
|
||||
|
||||
/* Filetype information as used in inodes. */
|
||||
#define FILETYPE_INO_MASK 0170000
|
||||
#define FILETYPE_INO_REG 0100000
|
||||
#define FILETYPE_INO_DIRECTORY 0040000
|
||||
#define FILETYPE_INO_SYMLINK 0120000
|
||||
|
||||
#include <grub/err.h>
|
||||
#include <grub/file.h>
|
||||
#include <grub/mm.h>
|
||||
#include <grub/misc.h>
|
||||
#include <grub/disk.h>
|
||||
#include <grub/dl.h>
|
||||
#include <grub/types.h>
|
||||
#include <grub/fshelp.h>
|
||||
|
||||
/* Log2 size of ext2 block in 512 blocks. */
|
||||
#define LOG2_EXT2_BLOCK_SIZE(data) \
|
||||
(grub_le_to_cpu32 (data->sblock.log2_block_size) + 1)
|
||||
|
||||
/* Log2 size of ext2 block in bytes. */
|
||||
#define LOG2_BLOCK_SIZE(data) \
|
||||
(grub_le_to_cpu32 (data->sblock.log2_block_size) + 10)
|
||||
|
||||
/* The size of an ext2 block in bytes. */
|
||||
#define EXT2_BLOCK_SIZE(data) (1 << LOG2_BLOCK_SIZE (data))
|
||||
|
||||
/* The revision level. */
|
||||
#define EXT2_REVISION(data) grub_le_to_cpu32 (data->sblock.revision_level)
|
||||
|
||||
/* The inode size. */
|
||||
#define EXT2_INODE_SIZE(data) \
|
||||
(EXT2_REVISION (data) == EXT2_GOOD_OLD_REVISION \
|
||||
? EXT2_GOOD_OLD_INODE_SIZE \
|
||||
: grub_le_to_cpu16 (data->sblock.inode_size))
|
||||
|
||||
/* Superblock filesystem feature flags (RW compatible)
|
||||
* A filesystem with any of these enabled can be read and written by a driver
|
||||
* that does not understand them without causing metadata/data corruption. */
|
||||
#define EXT2_FEATURE_COMPAT_DIR_PREALLOC 0x0001
|
||||
#define EXT2_FEATURE_COMPAT_IMAGIC_INODES 0x0002
|
||||
#define EXT3_FEATURE_COMPAT_HAS_JOURNAL 0x0004
|
||||
#define EXT2_FEATURE_COMPAT_EXT_ATTR 0x0008
|
||||
#define EXT2_FEATURE_COMPAT_RESIZE_INODE 0x0010
|
||||
#define EXT2_FEATURE_COMPAT_DIR_INDEX 0x0020
|
||||
/* Superblock filesystem feature flags (RO compatible)
|
||||
* A filesystem with any of these enabled can be safely read by a driver that
|
||||
* does not understand them, but should not be written to, usually because
|
||||
* additional metadata is required. */
|
||||
#define EXT2_FEATURE_RO_COMPAT_SPARSE_SUPER 0x0001
|
||||
#define EXT2_FEATURE_RO_COMPAT_LARGE_FILE 0x0002
|
||||
#define EXT2_FEATURE_RO_COMPAT_BTREE_DIR 0x0004
|
||||
#define EXT4_FEATURE_RO_COMPAT_GDT_CSUM 0x0010
|
||||
#define EXT4_FEATURE_RO_COMPAT_DIR_NLINK 0x0020
|
||||
#define EXT4_FEATURE_RO_COMPAT_EXTRA_ISIZE 0x0040
|
||||
/* Superblock filesystem feature flags (back-incompatible)
|
||||
* A filesystem with any of these enabled should not be attempted to be read
|
||||
* by a driver that does not understand them, since they usually indicate
|
||||
* metadata format changes that might confuse the reader. */
|
||||
#define EXT2_FEATURE_INCOMPAT_COMPRESSION 0x0001
|
||||
#define EXT2_FEATURE_INCOMPAT_FILETYPE 0x0002
|
||||
#define EXT3_FEATURE_INCOMPAT_RECOVER 0x0004 /* Needs recovery */
|
||||
#define EXT3_FEATURE_INCOMPAT_JOURNAL_DEV 0x0008 /* Volume is journal device */
|
||||
#define EXT2_FEATURE_INCOMPAT_META_BG 0x0010
|
||||
#define EXT4_FEATURE_INCOMPAT_EXTENTS 0x0040 /* Extents used */
|
||||
#define EXT4_FEATURE_INCOMPAT_64BIT 0x0080
|
||||
#define EXT4_FEATURE_INCOMPAT_FLEX_BG 0x0200
|
||||
|
||||
/* The set of back-incompatible features this driver DOES support. Add (OR)
|
||||
* flags here as the related features are implemented into the driver. */
|
||||
#define EXT2_DRIVER_SUPPORTED_INCOMPAT ( EXT2_FEATURE_INCOMPAT_FILETYPE \
|
||||
| EXT4_FEATURE_INCOMPAT_EXTENTS \
|
||||
| EXT4_FEATURE_INCOMPAT_FLEX_BG )
|
||||
/* List of rationales for the ignored "incompatible" features:
|
||||
* needs_recovery: Not really back-incompatible - was added as such to forbid
|
||||
* ext2 drivers from mounting an ext3 volume with a dirty
|
||||
* journal because they will ignore the journal, but the next
|
||||
* ext3 driver to mount the volume will find the journal and
|
||||
* replay it, potentially corrupting the metadata written by
|
||||
* the ext2 drivers. Safe to ignore for this RO driver. */
|
||||
#define EXT2_DRIVER_IGNORED_INCOMPAT ( EXT3_FEATURE_INCOMPAT_RECOVER )
|
||||
|
||||
|
||||
#define EXT3_JOURNAL_MAGIC_NUMBER 0xc03b3998U
|
||||
|
||||
#define EXT3_JOURNAL_DESCRIPTOR_BLOCK 1
|
||||
#define EXT3_JOURNAL_COMMIT_BLOCK 2
|
||||
#define EXT3_JOURNAL_SUPERBLOCK_V1 3
|
||||
#define EXT3_JOURNAL_SUPERBLOCK_V2 4
|
||||
#define EXT3_JOURNAL_REVOKE_BLOCK 5
|
||||
|
||||
#define EXT3_JOURNAL_FLAG_ESCAPE 1
|
||||
#define EXT3_JOURNAL_FLAG_SAME_UUID 2
|
||||
#define EXT3_JOURNAL_FLAG_DELETED 4
|
||||
#define EXT3_JOURNAL_FLAG_LAST_TAG 8
|
||||
|
||||
#define EXT4_EXTENTS_FLAG 0x80000
|
||||
|
||||
/* The ext2 superblock. */
|
||||
struct grub_ext2_sblock
|
||||
{
|
||||
grub_uint32_t total_inodes;
|
||||
grub_uint32_t total_blocks;
|
||||
grub_uint32_t reserved_blocks;
|
||||
grub_uint32_t free_blocks;
|
||||
grub_uint32_t free_inodes;
|
||||
grub_uint32_t first_data_block;
|
||||
grub_uint32_t log2_block_size;
|
||||
grub_uint32_t log2_fragment_size;
|
||||
grub_uint32_t blocks_per_group;
|
||||
grub_uint32_t fragments_per_group;
|
||||
grub_uint32_t inodes_per_group;
|
||||
grub_uint32_t mtime;
|
||||
grub_uint32_t utime;
|
||||
grub_uint16_t mnt_count;
|
||||
grub_uint16_t max_mnt_count;
|
||||
grub_uint16_t magic;
|
||||
grub_uint16_t fs_state;
|
||||
grub_uint16_t error_handling;
|
||||
grub_uint16_t minor_revision_level;
|
||||
grub_uint32_t lastcheck;
|
||||
grub_uint32_t checkinterval;
|
||||
grub_uint32_t creator_os;
|
||||
grub_uint32_t revision_level;
|
||||
grub_uint16_t uid_reserved;
|
||||
grub_uint16_t gid_reserved;
|
||||
grub_uint32_t first_inode;
|
||||
grub_uint16_t inode_size;
|
||||
grub_uint16_t block_group_number;
|
||||
grub_uint32_t feature_compatibility;
|
||||
grub_uint32_t feature_incompat;
|
||||
grub_uint32_t feature_ro_compat;
|
||||
grub_uint16_t uuid[8];
|
||||
char volume_name[16];
|
||||
char last_mounted_on[64];
|
||||
grub_uint32_t compression_info;
|
||||
grub_uint8_t prealloc_blocks;
|
||||
grub_uint8_t prealloc_dir_blocks;
|
||||
grub_uint16_t reserved_gdt_blocks;
|
||||
grub_uint8_t journal_uuid[16];
|
||||
grub_uint32_t journal_inum;
|
||||
grub_uint32_t journal_dev;
|
||||
grub_uint32_t last_orphan;
|
||||
grub_uint32_t hash_seed[4];
|
||||
grub_uint8_t def_hash_version;
|
||||
grub_uint8_t jnl_backup_type;
|
||||
grub_uint16_t reserved_word_pad;
|
||||
grub_uint32_t default_mount_opts;
|
||||
grub_uint32_t first_meta_bg;
|
||||
grub_uint32_t mkfs_time;
|
||||
grub_uint32_t jnl_blocks[17];
|
||||
};
|
||||
|
||||
/* The ext2 blockgroup. */
|
||||
struct grub_ext2_block_group
|
||||
{
|
||||
grub_uint32_t block_id;
|
||||
grub_uint32_t inode_id;
|
||||
grub_uint32_t inode_table_id;
|
||||
grub_uint16_t free_blocks;
|
||||
grub_uint16_t free_inodes;
|
||||
grub_uint16_t used_dirs;
|
||||
grub_uint16_t pad;
|
||||
grub_uint32_t reserved[3];
|
||||
};
|
||||
|
||||
/* The ext2 inode. */
|
||||
struct grub_ext2_inode
|
||||
{
|
||||
grub_uint16_t mode;
|
||||
grub_uint16_t uid;
|
||||
grub_uint32_t size;
|
||||
grub_uint32_t atime;
|
||||
grub_uint32_t ctime;
|
||||
grub_uint32_t mtime;
|
||||
grub_uint32_t dtime;
|
||||
grub_uint16_t gid;
|
||||
grub_uint16_t nlinks;
|
||||
grub_uint32_t blockcnt; /* Blocks of 512 bytes!! */
|
||||
grub_uint32_t flags;
|
||||
grub_uint32_t osd1;
|
||||
union
|
||||
{
|
||||
struct datablocks
|
||||
{
|
||||
grub_uint32_t dir_blocks[INDIRECT_BLOCKS];
|
||||
grub_uint32_t indir_block;
|
||||
grub_uint32_t double_indir_block;
|
||||
grub_uint32_t triple_indir_block;
|
||||
} blocks;
|
||||
char symlink[60];
|
||||
};
|
||||
grub_uint32_t version;
|
||||
grub_uint32_t acl;
|
||||
grub_uint32_t dir_acl;
|
||||
grub_uint32_t fragment_addr;
|
||||
grub_uint32_t osd2[3];
|
||||
};
|
||||
|
||||
/* The header of an ext2 directory entry. */
|
||||
struct ext2_dirent
|
||||
{
|
||||
grub_uint32_t inode;
|
||||
grub_uint16_t direntlen;
|
||||
grub_uint8_t namelen;
|
||||
grub_uint8_t filetype;
|
||||
};
|
||||
|
||||
struct grub_ext3_journal_header
|
||||
{
|
||||
grub_uint32_t magic;
|
||||
grub_uint32_t block_type;
|
||||
grub_uint32_t sequence;
|
||||
};
|
||||
|
||||
struct grub_ext3_journal_revoke_header
|
||||
{
|
||||
struct grub_ext3_journal_header header;
|
||||
grub_uint32_t count;
|
||||
grub_uint32_t data[0];
|
||||
};
|
||||
|
||||
struct grub_ext3_journal_block_tag
|
||||
{
|
||||
grub_uint32_t block;
|
||||
grub_uint32_t flags;
|
||||
};
|
||||
|
||||
struct grub_ext3_journal_sblock
|
||||
{
|
||||
struct grub_ext3_journal_header header;
|
||||
grub_uint32_t block_size;
|
||||
grub_uint32_t maxlen;
|
||||
grub_uint32_t first;
|
||||
grub_uint32_t sequence;
|
||||
grub_uint32_t start;
|
||||
};
|
||||
|
||||
#define EXT4_EXT_MAGIC 0xf30a
|
||||
|
||||
struct grub_ext4_extent_header
|
||||
{
|
||||
grub_uint16_t magic;
|
||||
grub_uint16_t entries;
|
||||
grub_uint16_t max;
|
||||
grub_uint16_t depth;
|
||||
grub_uint32_t generation;
|
||||
};
|
||||
|
||||
struct grub_ext4_extent
|
||||
{
|
||||
grub_uint32_t block;
|
||||
grub_uint16_t len;
|
||||
grub_uint16_t start_hi;
|
||||
grub_uint32_t start;
|
||||
};
|
||||
|
||||
struct grub_ext4_extent_idx
|
||||
{
|
||||
grub_uint32_t block;
|
||||
grub_uint32_t leaf;
|
||||
grub_uint16_t leaf_hi;
|
||||
grub_uint16_t unused;
|
||||
};
|
||||
|
||||
struct grub_fshelp_node
|
||||
{
|
||||
struct grub_ext2_data *data;
|
||||
struct grub_ext2_inode inode;
|
||||
int ino;
|
||||
int inode_read;
|
||||
};
|
||||
|
||||
/* Information about a "mounted" ext2 filesystem. */
|
||||
struct grub_ext2_data
|
||||
{
|
||||
struct grub_ext2_sblock sblock;
|
||||
grub_disk_t disk;
|
||||
struct grub_ext2_inode *inode;
|
||||
struct grub_fshelp_node diropen;
|
||||
};
|
||||
|
||||
static grub_dl_t my_mod;
|
||||
|
||||
|
||||
|
||||
/* Read into BLKGRP the blockgroup descriptor of blockgroup GROUP of
|
||||
the mounted filesystem DATA. */
|
||||
inline static grub_err_t
|
||||
grub_ext2_blockgroup (struct grub_ext2_data *data, int group,
|
||||
struct grub_ext2_block_group *blkgrp)
|
||||
{
|
||||
return grub_disk_read (data->disk,
|
||||
((grub_le_to_cpu32 (data->sblock.first_data_block) + 1)
|
||||
<< LOG2_EXT2_BLOCK_SIZE (data)),
|
||||
group * sizeof (struct grub_ext2_block_group),
|
||||
sizeof (struct grub_ext2_block_group), blkgrp);
|
||||
}
|
||||
|
||||
static struct grub_ext4_extent_header *
|
||||
grub_ext4_find_leaf (struct grub_ext2_data *data, char *buf,
|
||||
struct grub_ext4_extent_header *ext_block,
|
||||
grub_uint32_t fileblock)
|
||||
{
|
||||
struct grub_ext4_extent_idx *index;
|
||||
|
||||
while (1)
|
||||
{
|
||||
int i;
|
||||
grub_disk_addr_t block;
|
||||
|
||||
index = (struct grub_ext4_extent_idx *) (ext_block + 1);
|
||||
|
||||
if (grub_le_to_cpu16(ext_block->magic) != EXT4_EXT_MAGIC)
|
||||
return 0;
|
||||
|
||||
if (ext_block->depth == 0)
|
||||
return ext_block;
|
||||
|
||||
for (i = 0; i < grub_le_to_cpu16 (ext_block->entries); i++)
|
||||
{
|
||||
if (fileblock < grub_le_to_cpu32(index[i].block))
|
||||
break;
|
||||
}
|
||||
|
||||
if (--i < 0)
|
||||
return 0;
|
||||
|
||||
block = grub_le_to_cpu16 (index[i].leaf_hi);
|
||||
block = (block << 32) + grub_le_to_cpu32 (index[i].leaf);
|
||||
if (grub_disk_read (data->disk,
|
||||
block << LOG2_EXT2_BLOCK_SIZE (data),
|
||||
0, EXT2_BLOCK_SIZE(data), buf))
|
||||
return 0;
|
||||
|
||||
ext_block = (struct grub_ext4_extent_header *) buf;
|
||||
}
|
||||
}
|
||||
|
||||
static grub_disk_addr_t
|
||||
grub_ext2_read_block (grub_fshelp_node_t node, grub_disk_addr_t fileblock)
|
||||
{
|
||||
struct grub_ext2_data *data = node->data;
|
||||
struct grub_ext2_inode *inode = &node->inode;
|
||||
int blknr = -1;
|
||||
unsigned int blksz = EXT2_BLOCK_SIZE (data);
|
||||
int log2_blksz = LOG2_EXT2_BLOCK_SIZE (data);
|
||||
|
||||
if (grub_le_to_cpu32(inode->flags) & EXT4_EXTENTS_FLAG)
|
||||
{
|
||||
char buf[EXT2_BLOCK_SIZE(data)];
|
||||
struct grub_ext4_extent_header *leaf;
|
||||
struct grub_ext4_extent *ext;
|
||||
int i;
|
||||
|
||||
leaf = grub_ext4_find_leaf (data, buf,
|
||||
(struct grub_ext4_extent_header *) inode->blocks.dir_blocks,
|
||||
fileblock);
|
||||
if (! leaf)
|
||||
{
|
||||
grub_error (GRUB_ERR_BAD_FS, "invalid extent");
|
||||
return -1;
|
||||
}
|
||||
|
||||
ext = (struct grub_ext4_extent *) (leaf + 1);
|
||||
for (i = 0; i < grub_le_to_cpu16 (leaf->entries); i++)
|
||||
{
|
||||
if (fileblock < grub_le_to_cpu32 (ext[i].block))
|
||||
break;
|
||||
}
|
||||
|
||||
if (--i >= 0)
|
||||
{
|
||||
fileblock -= grub_le_to_cpu32 (ext[i].block);
|
||||
if (fileblock >= grub_le_to_cpu16 (ext[i].len))
|
||||
return 0;
|
||||
else
|
||||
{
|
||||
grub_disk_addr_t start;
|
||||
|
||||
start = grub_le_to_cpu16 (ext[i].start_hi);
|
||||
start = (start << 32) + grub_le_to_cpu32 (ext[i].start);
|
||||
|
||||
return fileblock + start;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
grub_error (GRUB_ERR_BAD_FS, "something wrong with extent");
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
/* Direct blocks. */
|
||||
if (fileblock < INDIRECT_BLOCKS)
|
||||
blknr = grub_le_to_cpu32 (inode->blocks.dir_blocks[fileblock]);
|
||||
/* Indirect. */
|
||||
else if (fileblock < INDIRECT_BLOCKS + blksz / 4)
|
||||
{
|
||||
grub_uint32_t indir[blksz / 4];
|
||||
|
||||
if (grub_disk_read (data->disk,
|
||||
((grub_disk_addr_t)
|
||||
grub_le_to_cpu32 (inode->blocks.indir_block))
|
||||
<< log2_blksz,
|
||||
0, blksz, indir))
|
||||
return grub_errno;
|
||||
|
||||
blknr = grub_le_to_cpu32 (indir[fileblock - INDIRECT_BLOCKS]);
|
||||
}
|
||||
/* Double indirect. */
|
||||
else if (fileblock < INDIRECT_BLOCKS + blksz / 4 * (blksz / 4 + 1))
|
||||
{
|
||||
unsigned int perblock = blksz / 4;
|
||||
unsigned int rblock = fileblock - (INDIRECT_BLOCKS
|
||||
+ blksz / 4);
|
||||
grub_uint32_t indir[blksz / 4];
|
||||
|
||||
if (grub_disk_read (data->disk,
|
||||
((grub_disk_addr_t)
|
||||
grub_le_to_cpu32 (inode->blocks.double_indir_block))
|
||||
<< log2_blksz,
|
||||
0, blksz, indir))
|
||||
return grub_errno;
|
||||
|
||||
if (grub_disk_read (data->disk,
|
||||
((grub_disk_addr_t)
|
||||
grub_le_to_cpu32 (indir[rblock / perblock]))
|
||||
<< log2_blksz,
|
||||
0, blksz, indir))
|
||||
return grub_errno;
|
||||
|
||||
|
||||
blknr = grub_le_to_cpu32 (indir[rblock % perblock]);
|
||||
}
|
||||
/* triple indirect. */
|
||||
else
|
||||
{
|
||||
grub_error (GRUB_ERR_NOT_IMPLEMENTED_YET,
|
||||
"ext2fs doesn't support triple indirect blocks");
|
||||
}
|
||||
|
||||
return blknr;
|
||||
}
|
||||
|
||||
/* Read LEN bytes from the file described by DATA starting with byte
|
||||
POS. Return the amount of read bytes in READ. */
|
||||
static grub_ssize_t
|
||||
grub_ext2_read_file (grub_fshelp_node_t node,
|
||||
void NESTED_FUNC_ATTR (*read_hook) (grub_disk_addr_t sector,
|
||||
unsigned offset, unsigned length),
|
||||
int pos, grub_size_t len, char *buf)
|
||||
{
|
||||
return grub_fshelp_read_file (node->data->disk, node, read_hook,
|
||||
pos, len, buf, grub_ext2_read_block,
|
||||
node->inode.size,
|
||||
LOG2_EXT2_BLOCK_SIZE (node->data));
|
||||
|
||||
}
|
||||
|
||||
|
||||
/* Read the inode INO for the file described by DATA into INODE. */
|
||||
static grub_err_t
|
||||
grub_ext2_read_inode (struct grub_ext2_data *data,
|
||||
int ino, struct grub_ext2_inode *inode)
|
||||
{
|
||||
struct grub_ext2_block_group blkgrp;
|
||||
struct grub_ext2_sblock *sblock = &data->sblock;
|
||||
int inodes_per_block;
|
||||
unsigned int blkno;
|
||||
unsigned int blkoff;
|
||||
|
||||
/* It is easier to calculate if the first inode is 0. */
|
||||
ino--;
|
||||
|
||||
grub_ext2_blockgroup (data,
|
||||
ino / grub_le_to_cpu32 (sblock->inodes_per_group),
|
||||
&blkgrp);
|
||||
if (grub_errno)
|
||||
return grub_errno;
|
||||
|
||||
inodes_per_block = EXT2_BLOCK_SIZE (data) / EXT2_INODE_SIZE (data);
|
||||
blkno = (ino % grub_le_to_cpu32 (sblock->inodes_per_group))
|
||||
/ inodes_per_block;
|
||||
blkoff = (ino % grub_le_to_cpu32 (sblock->inodes_per_group))
|
||||
% inodes_per_block;
|
||||
|
||||
/* Read the inode. */
|
||||
if (grub_disk_read (data->disk,
|
||||
((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))
|
||||
return grub_errno;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct grub_ext2_data *
|
||||
grub_ext2_mount (grub_disk_t disk)
|
||||
{
|
||||
struct grub_ext2_data *data;
|
||||
|
||||
data = grub_malloc (sizeof (struct grub_ext2_data));
|
||||
if (!data)
|
||||
return 0;
|
||||
|
||||
/* Read the superblock. */
|
||||
grub_disk_read (disk, 1 * 2, 0, sizeof (struct grub_ext2_sblock),
|
||||
&data->sblock);
|
||||
if (grub_errno)
|
||||
goto fail;
|
||||
|
||||
/* Make sure this is an ext2 filesystem. */
|
||||
if (grub_le_to_cpu16 (data->sblock.magic) != EXT2_MAGIC)
|
||||
{
|
||||
grub_error (GRUB_ERR_BAD_FS, "not an ext2 filesystem");
|
||||
goto fail;
|
||||
}
|
||||
|
||||
/* Check the FS doesn't have feature bits enabled that we don't support. */
|
||||
if (grub_le_to_cpu32 (data->sblock.feature_incompat)
|
||||
& ~(EXT2_DRIVER_SUPPORTED_INCOMPAT | EXT2_DRIVER_IGNORED_INCOMPAT))
|
||||
{
|
||||
grub_error (GRUB_ERR_BAD_FS, "filesystem has unsupported incompatible features");
|
||||
goto fail;
|
||||
}
|
||||
|
||||
|
||||
data->disk = disk;
|
||||
|
||||
data->diropen.data = data;
|
||||
data->diropen.ino = 2;
|
||||
data->diropen.inode_read = 1;
|
||||
|
||||
data->inode = &data->diropen.inode;
|
||||
|
||||
grub_ext2_read_inode (data, 2, data->inode);
|
||||
if (grub_errno)
|
||||
goto fail;
|
||||
|
||||
return data;
|
||||
|
||||
fail:
|
||||
if (grub_errno == GRUB_ERR_OUT_OF_RANGE)
|
||||
grub_error (GRUB_ERR_BAD_FS, "not an ext2 filesystem");
|
||||
|
||||
grub_free (data);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static char *
|
||||
grub_ext2_read_symlink (grub_fshelp_node_t node)
|
||||
{
|
||||
char *symlink;
|
||||
struct grub_fshelp_node *diro = node;
|
||||
|
||||
if (! diro->inode_read)
|
||||
{
|
||||
grub_ext2_read_inode (diro->data, diro->ino, &diro->inode);
|
||||
if (grub_errno)
|
||||
return 0;
|
||||
}
|
||||
|
||||
symlink = grub_malloc (grub_le_to_cpu32 (diro->inode.size) + 1);
|
||||
if (! symlink)
|
||||
return 0;
|
||||
|
||||
/* If the filesize of the symlink is bigger than
|
||||
60 the symlink is stored in a separate block,
|
||||
otherwise it is stored in the inode. */
|
||||
if (grub_le_to_cpu32 (diro->inode.size) <= 60)
|
||||
grub_strncpy (symlink,
|
||||
diro->inode.symlink,
|
||||
grub_le_to_cpu32 (diro->inode.size));
|
||||
else
|
||||
{
|
||||
grub_ext2_read_file (diro, 0, 0,
|
||||
grub_le_to_cpu32 (diro->inode.size),
|
||||
symlink);
|
||||
if (grub_errno)
|
||||
{
|
||||
grub_free (symlink);
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
symlink[grub_le_to_cpu32 (diro->inode.size)] = '\0';
|
||||
return symlink;
|
||||
}
|
||||
|
||||
static int
|
||||
grub_ext2_iterate_dir (grub_fshelp_node_t dir,
|
||||
int NESTED_FUNC_ATTR
|
||||
(*hook) (const char *filename,
|
||||
enum grub_fshelp_filetype filetype,
|
||||
grub_fshelp_node_t node))
|
||||
{
|
||||
unsigned int fpos = 0;
|
||||
struct grub_fshelp_node *diro = (struct grub_fshelp_node *) dir;
|
||||
|
||||
if (! diro->inode_read)
|
||||
{
|
||||
grub_ext2_read_inode (diro->data, diro->ino, &diro->inode);
|
||||
if (grub_errno)
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Search the file. */
|
||||
while (fpos < grub_le_to_cpu32 (diro->inode.size))
|
||||
{
|
||||
struct ext2_dirent dirent;
|
||||
|
||||
grub_ext2_read_file (diro, 0, fpos, sizeof (struct ext2_dirent),
|
||||
(char *) &dirent);
|
||||
if (grub_errno)
|
||||
return 0;
|
||||
|
||||
if (dirent.direntlen == 0)
|
||||
return 0;
|
||||
|
||||
if (dirent.namelen != 0)
|
||||
{
|
||||
char filename[dirent.namelen + 1];
|
||||
struct grub_fshelp_node *fdiro;
|
||||
enum grub_fshelp_filetype type = GRUB_FSHELP_UNKNOWN;
|
||||
|
||||
grub_ext2_read_file (diro, 0, fpos + sizeof (struct ext2_dirent),
|
||||
dirent.namelen, filename);
|
||||
if (grub_errno)
|
||||
return 0;
|
||||
|
||||
fdiro = grub_malloc (sizeof (struct grub_fshelp_node));
|
||||
if (! fdiro)
|
||||
return 0;
|
||||
|
||||
fdiro->data = diro->data;
|
||||
fdiro->ino = grub_le_to_cpu32 (dirent.inode);
|
||||
|
||||
filename[dirent.namelen] = '\0';
|
||||
|
||||
if (dirent.filetype != FILETYPE_UNKNOWN)
|
||||
{
|
||||
fdiro->inode_read = 0;
|
||||
|
||||
if (dirent.filetype == FILETYPE_DIRECTORY)
|
||||
type = GRUB_FSHELP_DIR;
|
||||
else if (dirent.filetype == FILETYPE_SYMLINK)
|
||||
type = GRUB_FSHELP_SYMLINK;
|
||||
else if (dirent.filetype == FILETYPE_REG)
|
||||
type = GRUB_FSHELP_REG;
|
||||
}
|
||||
else
|
||||
{
|
||||
/* The filetype can not be read from the dirent, read
|
||||
the inode to get more information. */
|
||||
grub_ext2_read_inode (diro->data,
|
||||
grub_le_to_cpu32 (dirent.inode),
|
||||
&fdiro->inode);
|
||||
if (grub_errno)
|
||||
{
|
||||
grub_free (fdiro);
|
||||
return 0;
|
||||
}
|
||||
|
||||
fdiro->inode_read = 1;
|
||||
|
||||
if ((grub_le_to_cpu16 (fdiro->inode.mode)
|
||||
& FILETYPE_INO_MASK) == FILETYPE_INO_DIRECTORY)
|
||||
type = GRUB_FSHELP_DIR;
|
||||
else if ((grub_le_to_cpu16 (fdiro->inode.mode)
|
||||
& FILETYPE_INO_MASK) == FILETYPE_INO_SYMLINK)
|
||||
type = GRUB_FSHELP_SYMLINK;
|
||||
else if ((grub_le_to_cpu16 (fdiro->inode.mode)
|
||||
& FILETYPE_INO_MASK) == FILETYPE_INO_REG)
|
||||
type = GRUB_FSHELP_REG;
|
||||
}
|
||||
|
||||
if (hook (filename, type, fdiro))
|
||||
return 1;
|
||||
}
|
||||
|
||||
fpos += grub_le_to_cpu16 (dirent.direntlen);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Open a file named NAME and initialize FILE. */
|
||||
static grub_err_t
|
||||
grub_ext2_open (struct grub_file *file, const char *name)
|
||||
{
|
||||
struct grub_ext2_data *data;
|
||||
struct grub_fshelp_node *fdiro = 0;
|
||||
|
||||
grub_dl_ref (my_mod);
|
||||
|
||||
data = grub_ext2_mount (file->device->disk);
|
||||
if (! data)
|
||||
goto fail;
|
||||
|
||||
grub_fshelp_find_file (name, &data->diropen, &fdiro, grub_ext2_iterate_dir,
|
||||
grub_ext2_read_symlink, GRUB_FSHELP_REG);
|
||||
if (grub_errno)
|
||||
goto fail;
|
||||
|
||||
if (! fdiro->inode_read)
|
||||
{
|
||||
grub_ext2_read_inode (data, fdiro->ino, &fdiro->inode);
|
||||
if (grub_errno)
|
||||
goto fail;
|
||||
}
|
||||
|
||||
grub_memcpy (data->inode, &fdiro->inode, sizeof (struct grub_ext2_inode));
|
||||
grub_free (fdiro);
|
||||
|
||||
file->size = grub_le_to_cpu32 (data->inode->size);
|
||||
file->data = data;
|
||||
file->offset = 0;
|
||||
|
||||
return 0;
|
||||
|
||||
fail:
|
||||
if (fdiro != &data->diropen)
|
||||
grub_free (fdiro);
|
||||
grub_free (data);
|
||||
|
||||
grub_dl_unref (my_mod);
|
||||
|
||||
return grub_errno;
|
||||
}
|
||||
|
||||
static grub_err_t
|
||||
grub_ext2_close (grub_file_t file)
|
||||
{
|
||||
grub_free (file->data);
|
||||
|
||||
grub_dl_unref (my_mod);
|
||||
|
||||
return GRUB_ERR_NONE;
|
||||
}
|
||||
|
||||
/* Read LEN bytes data from FILE into BUF. */
|
||||
static grub_ssize_t
|
||||
grub_ext2_read (grub_file_t file, char *buf, grub_size_t len)
|
||||
{
|
||||
struct grub_ext2_data *data = (struct grub_ext2_data *) file->data;
|
||||
|
||||
return grub_ext2_read_file (&data->diropen, file->read_hook,
|
||||
file->offset, len, buf);
|
||||
}
|
||||
|
||||
|
||||
static grub_err_t
|
||||
grub_ext2_dir (grub_device_t device, const char *path,
|
||||
int (*hook) (const char *filename,
|
||||
const struct grub_dirhook_info *info))
|
||||
{
|
||||
struct grub_ext2_data *data = 0;
|
||||
struct grub_fshelp_node *fdiro = 0;
|
||||
|
||||
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));
|
||||
if (! node->inode_read)
|
||||
{
|
||||
grub_ext2_read_inode (data, node->ino, &node->inode);
|
||||
if (!grub_errno)
|
||||
node->inode_read = 1;
|
||||
grub_errno = GRUB_ERR_NONE;
|
||||
}
|
||||
if (node->inode_read)
|
||||
{
|
||||
info.mtimeset = 1;
|
||||
info.mtime = grub_le_to_cpu32 (node->inode.mtime);
|
||||
}
|
||||
|
||||
info.dir = ((filetype & GRUB_FSHELP_TYPE_MASK) == GRUB_FSHELP_DIR);
|
||||
grub_free (node);
|
||||
return hook (filename, &info);
|
||||
}
|
||||
|
||||
grub_dl_ref (my_mod);
|
||||
|
||||
data = grub_ext2_mount (device->disk);
|
||||
if (! data)
|
||||
goto fail;
|
||||
|
||||
grub_fshelp_find_file (path, &data->diropen, &fdiro, grub_ext2_iterate_dir,
|
||||
grub_ext2_read_symlink, GRUB_FSHELP_DIR);
|
||||
if (grub_errno)
|
||||
goto fail;
|
||||
|
||||
grub_ext2_iterate_dir (fdiro, iterate);
|
||||
|
||||
fail:
|
||||
if (fdiro != &data->diropen)
|
||||
grub_free (fdiro);
|
||||
grub_free (data);
|
||||
|
||||
grub_dl_unref (my_mod);
|
||||
|
||||
return grub_errno;
|
||||
}
|
||||
|
||||
static grub_err_t
|
||||
grub_ext2_label (grub_device_t device, char **label)
|
||||
{
|
||||
struct grub_ext2_data *data;
|
||||
grub_disk_t disk = device->disk;
|
||||
|
||||
grub_dl_ref (my_mod);
|
||||
|
||||
data = grub_ext2_mount (disk);
|
||||
if (data)
|
||||
*label = grub_strndup (data->sblock.volume_name, 14);
|
||||
else
|
||||
*label = NULL;
|
||||
|
||||
grub_dl_unref (my_mod);
|
||||
|
||||
grub_free (data);
|
||||
|
||||
return grub_errno;
|
||||
}
|
||||
|
||||
static grub_err_t
|
||||
grub_ext2_uuid (grub_device_t device, char **uuid)
|
||||
{
|
||||
struct grub_ext2_data *data;
|
||||
grub_disk_t disk = device->disk;
|
||||
|
||||
grub_dl_ref (my_mod);
|
||||
|
||||
data = grub_ext2_mount (disk);
|
||||
if (data)
|
||||
{
|
||||
*uuid = grub_xasprintf ("%04x%04x-%04x-%04x-%04x-%04x%04x%04x",
|
||||
grub_be_to_cpu16 (data->sblock.uuid[0]),
|
||||
grub_be_to_cpu16 (data->sblock.uuid[1]),
|
||||
grub_be_to_cpu16 (data->sblock.uuid[2]),
|
||||
grub_be_to_cpu16 (data->sblock.uuid[3]),
|
||||
grub_be_to_cpu16 (data->sblock.uuid[4]),
|
||||
grub_be_to_cpu16 (data->sblock.uuid[5]),
|
||||
grub_be_to_cpu16 (data->sblock.uuid[6]),
|
||||
grub_be_to_cpu16 (data->sblock.uuid[7]));
|
||||
}
|
||||
else
|
||||
*uuid = NULL;
|
||||
|
||||
grub_dl_unref (my_mod);
|
||||
|
||||
grub_free (data);
|
||||
|
||||
return grub_errno;
|
||||
}
|
||||
|
||||
/* Get mtime. */
|
||||
static grub_err_t
|
||||
grub_ext2_mtime (grub_device_t device, grub_int32_t *tm)
|
||||
{
|
||||
struct grub_ext2_data *data;
|
||||
grub_disk_t disk = device->disk;
|
||||
|
||||
grub_dl_ref (my_mod);
|
||||
|
||||
data = grub_ext2_mount (disk);
|
||||
if (!data)
|
||||
*tm = 0;
|
||||
else
|
||||
*tm = grub_le_to_cpu32 (data->sblock.utime);
|
||||
|
||||
grub_dl_unref (my_mod);
|
||||
|
||||
grub_free (data);
|
||||
|
||||
return grub_errno;
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
static struct grub_fs grub_ext2_fs =
|
||||
{
|
||||
.name = "ext2",
|
||||
.dir = grub_ext2_dir,
|
||||
.open = grub_ext2_open,
|
||||
.read = grub_ext2_read,
|
||||
.close = grub_ext2_close,
|
||||
.label = grub_ext2_label,
|
||||
.uuid = grub_ext2_uuid,
|
||||
.mtime = grub_ext2_mtime,
|
||||
#ifdef GRUB_UTIL
|
||||
.reserved_first_sector = 1,
|
||||
#endif
|
||||
.next = 0
|
||||
};
|
||||
|
||||
GRUB_MOD_INIT(ext2)
|
||||
{
|
||||
grub_fs_register (&grub_ext2_fs);
|
||||
my_mod = mod;
|
||||
}
|
||||
|
||||
GRUB_MOD_FINI(ext2)
|
||||
{
|
||||
grub_fs_unregister (&grub_ext2_fs);
|
||||
}
|
876
grub-core/fs/fat.c
Normal file
876
grub-core/fs/fat.c
Normal file
|
@ -0,0 +1,876 @@
|
|||
/* fat.c - FAT filesystem */
|
||||
/*
|
||||
* GRUB -- GRand Unified Bootloader
|
||||
* Copyright (C) 2000,2001,2002,2003,2004,2005,2007,2008,2009 Free Software Foundation, Inc.
|
||||
*
|
||||
* GRUB is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* GRUB is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with GRUB. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include <grub/fs.h>
|
||||
#include <grub/disk.h>
|
||||
#include <grub/file.h>
|
||||
#include <grub/types.h>
|
||||
#include <grub/misc.h>
|
||||
#include <grub/mm.h>
|
||||
#include <grub/err.h>
|
||||
#include <grub/dl.h>
|
||||
#include <grub/charset.h>
|
||||
|
||||
#define GRUB_FAT_DIR_ENTRY_SIZE 32
|
||||
|
||||
#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
|
||||
|
||||
#define GRUB_FAT_MAXFILE 256
|
||||
|
||||
#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
|
||||
{
|
||||
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 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;
|
||||
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 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;
|
||||
} __attribute__ ((packed));
|
||||
|
||||
struct grub_fat_dir_entry
|
||||
{
|
||||
grub_uint8_t name[11];
|
||||
grub_uint8_t attr;
|
||||
grub_uint8_t nt_reserved;
|
||||
grub_uint8_t c_time_tenth;
|
||||
grub_uint16_t c_time;
|
||||
grub_uint16_t c_date;
|
||||
grub_uint16_t a_date;
|
||||
grub_uint16_t first_cluster_high;
|
||||
grub_uint16_t w_time;
|
||||
grub_uint16_t w_date;
|
||||
grub_uint16_t first_cluster_low;
|
||||
grub_uint32_t file_size;
|
||||
} __attribute__ ((packed));
|
||||
|
||||
struct grub_fat_long_name_entry
|
||||
{
|
||||
grub_uint8_t id;
|
||||
grub_uint16_t name1[5];
|
||||
grub_uint8_t attr;
|
||||
grub_uint8_t reserved;
|
||||
grub_uint8_t checksum;
|
||||
grub_uint16_t name2[6];
|
||||
grub_uint16_t first_cluster;
|
||||
grub_uint16_t name3[2];
|
||||
} __attribute__ ((packed));
|
||||
|
||||
struct grub_fat_data
|
||||
{
|
||||
int logical_sector_bits;
|
||||
grub_uint32_t num_sectors;
|
||||
|
||||
grub_uint16_t fat_sector;
|
||||
grub_uint32_t sectors_per_fat;
|
||||
int fat_size;
|
||||
|
||||
grub_uint32_t root_cluster;
|
||||
grub_uint32_t root_sector;
|
||||
grub_uint32_t num_root_sectors;
|
||||
|
||||
int cluster_bits;
|
||||
grub_uint32_t cluster_eof_mark;
|
||||
grub_uint32_t cluster_sector;
|
||||
grub_uint32_t num_clusters;
|
||||
|
||||
grub_uint8_t attr;
|
||||
grub_ssize_t file_size;
|
||||
grub_uint32_t file_cluster;
|
||||
grub_uint32_t cur_cluster_num;
|
||||
grub_uint32_t cur_cluster;
|
||||
|
||||
grub_uint32_t uuid;
|
||||
};
|
||||
|
||||
static grub_dl_t my_mod;
|
||||
|
||||
static int
|
||||
fat_log2 (unsigned x)
|
||||
{
|
||||
int i;
|
||||
|
||||
if (x == 0)
|
||||
return -1;
|
||||
|
||||
for (i = 0; (x & 1) == 0; i++)
|
||||
x >>= 1;
|
||||
|
||||
if (x != 1)
|
||||
return -1;
|
||||
|
||||
return i;
|
||||
}
|
||||
|
||||
static struct grub_fat_data *
|
||||
grub_fat_mount (grub_disk_t disk)
|
||||
{
|
||||
struct grub_fat_bpb bpb;
|
||||
struct grub_fat_data *data = 0;
|
||||
grub_uint32_t first_fat, magic;
|
||||
|
||||
if (! disk)
|
||||
goto fail;
|
||||
|
||||
data = (struct grub_fat_data *) grub_malloc (sizeof (*data));
|
||||
if (! data)
|
||||
goto fail;
|
||||
|
||||
/* Read the BPB. */
|
||||
if (grub_disk_read (disk, 0, 0, sizeof (bpb), &bpb))
|
||||
goto fail;
|
||||
|
||||
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;
|
||||
|
||||
/* Get the sizes of logical sectors and clusters. */
|
||||
data->logical_sector_bits =
|
||||
fat_log2 (grub_le_to_cpu16 (bpb.bytes_per_sector));
|
||||
if (data->logical_sector_bits < GRUB_DISK_SECTOR_BITS)
|
||||
goto fail;
|
||||
data->logical_sector_bits -= GRUB_DISK_SECTOR_BITS;
|
||||
|
||||
data->cluster_bits = fat_log2 (bpb.sectors_per_cluster);
|
||||
if (data->cluster_bits < 0)
|
||||
goto fail;
|
||||
data->cluster_bits += data->logical_sector_bits;
|
||||
|
||||
/* Get information about FATs. */
|
||||
data->fat_sector = (grub_le_to_cpu16 (bpb.num_reserved_sectors)
|
||||
<< data->logical_sector_bits);
|
||||
if (data->fat_sector == 0)
|
||||
goto fail;
|
||||
|
||||
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);
|
||||
if (data->sectors_per_fat == 0)
|
||||
goto fail;
|
||||
|
||||
/* Get the number of sectors in this volume. */
|
||||
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);
|
||||
if (data->num_sectors == 0)
|
||||
goto fail;
|
||||
|
||||
/* Get information about the root directory. */
|
||||
if (bpb.num_fats == 0)
|
||||
goto fail;
|
||||
|
||||
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
|
||||
+ grub_le_to_cpu16 (bpb.bytes_per_sector) - 1)
|
||||
>> (data->logical_sector_bits + GRUB_DISK_SECTOR_BITS))
|
||||
<< (data->logical_sector_bits));
|
||||
|
||||
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);
|
||||
|
||||
if (data->num_clusters <= 2)
|
||||
goto fail;
|
||||
|
||||
if (! bpb.sectors_per_fat_16)
|
||||
{
|
||||
/* FAT32. */
|
||||
grub_uint16_t flags = grub_le_to_cpu16 (bpb.version_specific.fat32.extended_flags);
|
||||
|
||||
data->root_cluster = grub_le_to_cpu32 (bpb.version_specific.fat32.root_cluster);
|
||||
data->fat_size = 32;
|
||||
data->cluster_eof_mark = 0x0ffffff8;
|
||||
|
||||
if (flags & 0x80)
|
||||
{
|
||||
/* Get an active FAT. */
|
||||
unsigned active_fat = flags & 0xf;
|
||||
|
||||
if (active_fat > bpb.num_fats)
|
||||
goto fail;
|
||||
|
||||
data->fat_sector += active_fat * data->sectors_per_fat;
|
||||
}
|
||||
|
||||
if (bpb.num_root_entries != 0 || bpb.version_specific.fat32.fs_version != 0)
|
||||
goto fail;
|
||||
}
|
||||
else
|
||||
{
|
||||
/* FAT12 or FAT16. */
|
||||
data->root_cluster = ~0U;
|
||||
|
||||
if (data->num_clusters <= 4085 + 2)
|
||||
{
|
||||
/* FAT12. */
|
||||
data->fat_size = 12;
|
||||
data->cluster_eof_mark = 0x0ff8;
|
||||
}
|
||||
else
|
||||
{
|
||||
/* FAT16. */
|
||||
data->fat_size = 16;
|
||||
data->cluster_eof_mark = 0xfff8;
|
||||
}
|
||||
}
|
||||
|
||||
/* More sanity checks. */
|
||||
if (data->num_sectors <= data->fat_sector)
|
||||
goto fail;
|
||||
|
||||
if (grub_disk_read (disk,
|
||||
data->fat_sector,
|
||||
0,
|
||||
sizeof (first_fat),
|
||||
&first_fat))
|
||||
goto fail;
|
||||
|
||||
first_fat = grub_le_to_cpu32 (first_fat);
|
||||
|
||||
if (data->fat_size == 32)
|
||||
{
|
||||
first_fat &= 0x0fffffff;
|
||||
magic = 0x0fffff00;
|
||||
}
|
||||
else if (data->fat_size == 16)
|
||||
{
|
||||
first_fat &= 0x0000ffff;
|
||||
magic = 0xff00;
|
||||
}
|
||||
else
|
||||
{
|
||||
first_fat &= 0x00000fff;
|
||||
magic = 0x0f00;
|
||||
}
|
||||
|
||||
/* Serial number. */
|
||||
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);
|
||||
|
||||
/* 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;
|
||||
|
||||
/* Start from the root directory. */
|
||||
data->file_cluster = data->root_cluster;
|
||||
data->cur_cluster_num = ~0U;
|
||||
data->attr = GRUB_FAT_ATTR_DIRECTORY;
|
||||
return data;
|
||||
|
||||
fail:
|
||||
|
||||
grub_free (data);
|
||||
grub_error (GRUB_ERR_BAD_FS, "not a FAT filesystem");
|
||||
return 0;
|
||||
}
|
||||
|
||||
static grub_ssize_t
|
||||
grub_fat_read_data (grub_disk_t disk, struct grub_fat_data *data,
|
||||
void NESTED_FUNC_ATTR (*read_hook) (grub_disk_addr_t sector,
|
||||
unsigned offset, unsigned length),
|
||||
grub_off_t offset, grub_size_t len, char *buf)
|
||||
{
|
||||
grub_size_t size;
|
||||
grub_uint32_t logical_cluster;
|
||||
unsigned logical_cluster_bits;
|
||||
grub_ssize_t ret = 0;
|
||||
unsigned long sector;
|
||||
|
||||
/* This is a special case. FAT12 and FAT16 doesn't have the root directory
|
||||
in clusters. */
|
||||
if (data->file_cluster == ~0U)
|
||||
{
|
||||
size = (data->num_root_sectors << GRUB_DISK_SECTOR_BITS) - offset;
|
||||
if (size > len)
|
||||
size = len;
|
||||
|
||||
if (grub_disk_read (disk, data->root_sector, offset, size, buf))
|
||||
return -1;
|
||||
|
||||
return size;
|
||||
}
|
||||
|
||||
/* 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;
|
||||
|
||||
if (logical_cluster < data->cur_cluster_num)
|
||||
{
|
||||
data->cur_cluster_num = 0;
|
||||
data->cur_cluster = data->file_cluster;
|
||||
}
|
||||
|
||||
while (len)
|
||||
{
|
||||
while (logical_cluster > data->cur_cluster_num)
|
||||
{
|
||||
/* Find next cluster. */
|
||||
grub_uint32_t next_cluster;
|
||||
unsigned long fat_offset;
|
||||
|
||||
switch (data->fat_size)
|
||||
{
|
||||
case 32:
|
||||
fat_offset = data->cur_cluster << 2;
|
||||
break;
|
||||
case 16:
|
||||
fat_offset = data->cur_cluster << 1;
|
||||
break;
|
||||
default:
|
||||
/* case 12: */
|
||||
fat_offset = data->cur_cluster + (data->cur_cluster >> 1);
|
||||
break;
|
||||
}
|
||||
|
||||
/* Read the FAT. */
|
||||
if (grub_disk_read (disk, data->fat_sector, fat_offset,
|
||||
(data->fat_size + 7) >> 3,
|
||||
(char *) &next_cluster))
|
||||
return -1;
|
||||
|
||||
next_cluster = grub_le_to_cpu32 (next_cluster);
|
||||
switch (data->fat_size)
|
||||
{
|
||||
case 16:
|
||||
next_cluster &= 0xFFFF;
|
||||
break;
|
||||
case 12:
|
||||
if (data->cur_cluster & 1)
|
||||
next_cluster >>= 4;
|
||||
|
||||
next_cluster &= 0x0FFF;
|
||||
break;
|
||||
}
|
||||
|
||||
grub_dprintf ("fat", "fat_size=%d, next_cluster=%u\n",
|
||||
data->fat_size, next_cluster);
|
||||
|
||||
/* Check the end. */
|
||||
if (next_cluster >= data->cluster_eof_mark)
|
||||
return ret;
|
||||
|
||||
if (next_cluster < 2 || next_cluster >= data->num_clusters)
|
||||
{
|
||||
grub_error (GRUB_ERR_BAD_FS, "invalid cluster %u",
|
||||
next_cluster);
|
||||
return -1;
|
||||
}
|
||||
|
||||
data->cur_cluster = next_cluster;
|
||||
data->cur_cluster_num++;
|
||||
}
|
||||
|
||||
/* Read the data here. */
|
||||
sector = (data->cluster_sector
|
||||
+ ((data->cur_cluster - 2)
|
||||
<< (data->cluster_bits + data->logical_sector_bits)));
|
||||
size = (1 << logical_cluster_bits) - offset;
|
||||
if (size > len)
|
||||
size = len;
|
||||
|
||||
disk->read_hook = read_hook;
|
||||
grub_disk_read (disk, sector, offset, size, buf);
|
||||
disk->read_hook = 0;
|
||||
if (grub_errno)
|
||||
return -1;
|
||||
|
||||
len -= size;
|
||||
buf += size;
|
||||
ret += size;
|
||||
logical_cluster++;
|
||||
offset = 0;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
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;
|
||||
grub_uint16_t *unibuf;
|
||||
int slot = -1, slots = -1;
|
||||
int checksum = -1;
|
||||
grub_ssize_t offset = -sizeof(dir);
|
||||
|
||||
if (! (data->attr & GRUB_FAT_ATTR_DIRECTORY))
|
||||
return grub_error (GRUB_ERR_BAD_FILE_TYPE, "not a directory");
|
||||
|
||||
/* Allocate space enough to hold a long name. */
|
||||
filename = grub_malloc (0x40 * 13 * 4 + 1);
|
||||
unibuf = (grub_uint16_t *) grub_malloc (0x40 * 13 * 2);
|
||||
if (! filename || ! unibuf)
|
||||
{
|
||||
grub_free (filename);
|
||||
grub_free (unibuf);
|
||||
return 0;
|
||||
}
|
||||
|
||||
while (1)
|
||||
{
|
||||
unsigned i;
|
||||
|
||||
/* Adjust the offset. */
|
||||
offset += sizeof (dir);
|
||||
|
||||
/* Read a directory entry. */
|
||||
if ((grub_fat_read_data (disk, data, 0,
|
||||
offset, sizeof (dir), (char *) &dir)
|
||||
!= sizeof (dir) || dir.name[0] == 0))
|
||||
break;
|
||||
/* Handle long name entries. */
|
||||
if (dir.attr == GRUB_FAT_ATTR_LONG_NAME)
|
||||
{
|
||||
struct grub_fat_long_name_entry *long_name
|
||||
= (struct grub_fat_long_name_entry *) &dir;
|
||||
grub_uint8_t id = long_name->id;
|
||||
|
||||
if (id & 0x40)
|
||||
{
|
||||
id &= 0x3f;
|
||||
slots = slot = id;
|
||||
checksum = long_name->checksum;
|
||||
}
|
||||
|
||||
if (id != slot || slot == 0 || checksum != long_name->checksum)
|
||||
{
|
||||
checksum = -1;
|
||||
continue;
|
||||
}
|
||||
|
||||
slot--;
|
||||
grub_memcpy (unibuf + slot * 13, long_name->name1, 5 * 2);
|
||||
grub_memcpy (unibuf + slot * 13 + 5, long_name->name2, 6 * 2);
|
||||
grub_memcpy (unibuf + slot * 13 + 11, long_name->name3, 2 * 2);
|
||||
continue;
|
||||
}
|
||||
|
||||
/* Check if this entry is valid. */
|
||||
if (dir.name[0] == 0xe5 || (dir.attr & ~GRUB_FAT_ATTR_VALID))
|
||||
continue;
|
||||
|
||||
/* This is a workaround for Japanese. */
|
||||
if (dir.name[0] == 0x05)
|
||||
dir.name[0] = 0xe5;
|
||||
|
||||
if (checksum != -1 && slot == 0)
|
||||
{
|
||||
grub_uint8_t sum;
|
||||
|
||||
for (sum = 0, i = 0; i < sizeof (dir.name); i++)
|
||||
sum = ((sum >> 1) | (sum << 7)) + dir.name[i];
|
||||
|
||||
if (sum == checksum)
|
||||
{
|
||||
int u;
|
||||
|
||||
for (u = 0; u < slots * 13; u++)
|
||||
unibuf[u] = grub_le_to_cpu16 (unibuf[u]);
|
||||
|
||||
*grub_utf16_to_utf8 ((grub_uint8_t *) filename, unibuf,
|
||||
slots * 13) = '\0';
|
||||
|
||||
if (hook (filename, &dir))
|
||||
break;
|
||||
|
||||
checksum = -1;
|
||||
continue;
|
||||
}
|
||||
|
||||
checksum = -1;
|
||||
}
|
||||
|
||||
/* Convert the 8.3 file name. */
|
||||
filep = filename;
|
||||
if (dir.attr & GRUB_FAT_ATTR_VOLUME_ID)
|
||||
{
|
||||
for (i = 0; i < sizeof (dir.name) && dir.name[i]
|
||||
&& ! grub_isspace (dir.name[i]); i++)
|
||||
*filep++ = dir.name[i];
|
||||
}
|
||||
else
|
||||
{
|
||||
for (i = 0; i < 8 && dir.name[i] && ! grub_isspace (dir.name[i]); i++)
|
||||
*filep++ = grub_tolower (dir.name[i]);
|
||||
|
||||
*filep = '.';
|
||||
|
||||
for (i = 8; i < 11 && dir.name[i] && ! grub_isspace (dir.name[i]); i++)
|
||||
*++filep = grub_tolower (dir.name[i]);
|
||||
|
||||
if (*filep != '.')
|
||||
filep++;
|
||||
}
|
||||
*filep = '\0';
|
||||
|
||||
if (hook (filename, &dir))
|
||||
break;
|
||||
}
|
||||
|
||||
grub_free (filename);
|
||||
grub_free (unibuf);
|
||||
|
||||
return grub_errno;
|
||||
}
|
||||
|
||||
|
||||
/* 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.
|
||||
If HOOK is specified, call it with each file name. */
|
||||
static char *
|
||||
grub_fat_find_dir (grub_disk_t disk, struct grub_fat_data *data,
|
||||
const char *path,
|
||||
int (*hook) (const char *filename,
|
||||
const struct grub_dirhook_info *info))
|
||||
{
|
||||
char *dirname, *dirp;
|
||||
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)
|
||||
{
|
||||
struct grub_dirhook_info info;
|
||||
grub_memset (&info, 0, sizeof (info));
|
||||
|
||||
info.dir = !! (dir->attr & GRUB_FAT_ATTR_DIRECTORY);
|
||||
info.case_insensitive = 1;
|
||||
|
||||
if (dir->attr & GRUB_FAT_ATTR_VOLUME_ID)
|
||||
return 0;
|
||||
if (*dirname == '\0' && call_hook)
|
||||
return hook (filename, &info);
|
||||
|
||||
if (grub_strcasecmp (dirname, filename) == 0)
|
||||
{
|
||||
found = 1;
|
||||
data->attr = dir->attr;
|
||||
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));
|
||||
data->cur_cluster_num = ~0U;
|
||||
|
||||
if (call_hook)
|
||||
hook (filename, &info);
|
||||
|
||||
return 1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (! (data->attr & GRUB_FAT_ATTR_DIRECTORY))
|
||||
{
|
||||
grub_error (GRUB_ERR_BAD_FILE_TYPE, "not a directory");
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Extract a directory name. */
|
||||
while (*path == '/')
|
||||
path++;
|
||||
|
||||
dirp = grub_strchr (path, '/');
|
||||
if (dirp)
|
||||
{
|
||||
unsigned len = dirp - path;
|
||||
|
||||
dirname = grub_malloc (len + 1);
|
||||
if (! dirname)
|
||||
return 0;
|
||||
|
||||
grub_memcpy (dirname, path, len);
|
||||
dirname[len] = '\0';
|
||||
}
|
||||
else
|
||||
/* This is actually a file. */
|
||||
dirname = grub_strdup (path);
|
||||
|
||||
call_hook = (! dirp && hook);
|
||||
|
||||
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_free (dirname);
|
||||
|
||||
return found ? dirp : 0;
|
||||
}
|
||||
|
||||
static grub_err_t
|
||||
grub_fat_dir (grub_device_t device, const char *path,
|
||||
int (*hook) (const char *filename,
|
||||
const struct grub_dirhook_info *info))
|
||||
{
|
||||
struct grub_fat_data *data = 0;
|
||||
grub_disk_t disk = device->disk;
|
||||
grub_size_t len;
|
||||
char *dirname = 0;
|
||||
char *p;
|
||||
|
||||
grub_dl_ref (my_mod);
|
||||
|
||||
data = grub_fat_mount (disk);
|
||||
if (! data)
|
||||
goto fail;
|
||||
|
||||
/* Make sure that DIRNAME terminates with '/'. */
|
||||
len = grub_strlen (path);
|
||||
dirname = grub_malloc (len + 1 + 1);
|
||||
if (! dirname)
|
||||
goto fail;
|
||||
grub_memcpy (dirname, path, len);
|
||||
p = dirname + len;
|
||||
if (path[len - 1] != '/')
|
||||
*p++ = '/';
|
||||
*p = '\0';
|
||||
p = dirname;
|
||||
|
||||
do
|
||||
{
|
||||
p = grub_fat_find_dir (disk, data, p, hook);
|
||||
}
|
||||
while (p && grub_errno == GRUB_ERR_NONE);
|
||||
|
||||
fail:
|
||||
|
||||
grub_free (dirname);
|
||||
grub_free (data);
|
||||
|
||||
grub_dl_unref (my_mod);
|
||||
|
||||
return grub_errno;
|
||||
}
|
||||
|
||||
static grub_err_t
|
||||
grub_fat_open (grub_file_t file, const char *name)
|
||||
{
|
||||
struct grub_fat_data *data = 0;
|
||||
char *p = (char *) name;
|
||||
|
||||
grub_dl_ref (my_mod);
|
||||
|
||||
data = grub_fat_mount (file->device->disk);
|
||||
if (! data)
|
||||
goto fail;
|
||||
|
||||
do
|
||||
{
|
||||
p = grub_fat_find_dir (file->device->disk, data, p, 0);
|
||||
if (grub_errno != GRUB_ERR_NONE)
|
||||
goto fail;
|
||||
}
|
||||
while (p);
|
||||
|
||||
if (data->attr & GRUB_FAT_ATTR_DIRECTORY)
|
||||
{
|
||||
grub_error (GRUB_ERR_BAD_FILE_TYPE, "not a file");
|
||||
goto fail;
|
||||
}
|
||||
|
||||
file->data = data;
|
||||
file->size = data->file_size;
|
||||
|
||||
return GRUB_ERR_NONE;
|
||||
|
||||
fail:
|
||||
|
||||
grub_free (data);
|
||||
|
||||
grub_dl_unref (my_mod);
|
||||
|
||||
return grub_errno;
|
||||
}
|
||||
|
||||
static grub_ssize_t
|
||||
grub_fat_read (grub_file_t file, char *buf, grub_size_t len)
|
||||
{
|
||||
return grub_fat_read_data (file->device->disk, file->data, file->read_hook,
|
||||
file->offset, len, buf);
|
||||
}
|
||||
|
||||
static grub_err_t
|
||||
grub_fat_close (grub_file_t file)
|
||||
{
|
||||
grub_free (file->data);
|
||||
|
||||
grub_dl_unref (my_mod);
|
||||
|
||||
return grub_errno;
|
||||
}
|
||||
|
||||
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)
|
||||
{
|
||||
if (dir->attr == GRUB_FAT_ATTR_VOLUME_ID)
|
||||
{
|
||||
*label = grub_strdup (filename);
|
||||
return 1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
grub_dl_ref (my_mod);
|
||||
|
||||
data = grub_fat_mount (disk);
|
||||
if (! data)
|
||||
goto fail;
|
||||
|
||||
if (! (data->attr & GRUB_FAT_ATTR_DIRECTORY))
|
||||
{
|
||||
grub_error (GRUB_ERR_BAD_FILE_TYPE, "not a directory");
|
||||
return 0;
|
||||
}
|
||||
|
||||
*label = 0;
|
||||
|
||||
grub_fat_iterate_dir (disk, data, iter_hook);
|
||||
|
||||
fail:
|
||||
|
||||
grub_dl_unref (my_mod);
|
||||
|
||||
grub_free (data);
|
||||
|
||||
return grub_errno;
|
||||
}
|
||||
|
||||
static grub_err_t
|
||||
grub_fat_uuid (grub_device_t device, char **uuid)
|
||||
{
|
||||
struct grub_fat_data *data;
|
||||
grub_disk_t disk = device->disk;
|
||||
|
||||
grub_dl_ref (my_mod);
|
||||
|
||||
data = grub_fat_mount (disk);
|
||||
if (data)
|
||||
{
|
||||
*uuid = grub_xasprintf ("%04x-%04x",
|
||||
(grub_uint16_t) (data->uuid >> 16),
|
||||
(grub_uint16_t) data->uuid);
|
||||
}
|
||||
else
|
||||
*uuid = NULL;
|
||||
|
||||
grub_dl_unref (my_mod);
|
||||
|
||||
grub_free (data);
|
||||
|
||||
return grub_errno;
|
||||
}
|
||||
|
||||
static struct grub_fs grub_fat_fs =
|
||||
{
|
||||
.name = "fat",
|
||||
.dir = grub_fat_dir,
|
||||
.open = grub_fat_open,
|
||||
.read = grub_fat_read,
|
||||
.close = grub_fat_close,
|
||||
.label = grub_fat_label,
|
||||
.uuid = grub_fat_uuid,
|
||||
#ifdef GRUB_UTIL
|
||||
.reserved_first_sector = 1,
|
||||
#endif
|
||||
.next = 0
|
||||
};
|
||||
|
||||
GRUB_MOD_INIT(fat)
|
||||
{
|
||||
grub_fs_register (&grub_fat_fs);
|
||||
my_mod = mod;
|
||||
}
|
||||
|
||||
GRUB_MOD_FINI(fat)
|
||||
{
|
||||
grub_fs_unregister (&grub_fat_fs);
|
||||
}
|
||||
|
315
grub-core/fs/fshelp.c
Normal file
315
grub-core/fs/fshelp.c
Normal file
|
@ -0,0 +1,315 @@
|
|||
/* fshelp.c -- Filesystem helper functions */
|
||||
/*
|
||||
* GRUB -- GRand Unified Bootloader
|
||||
* Copyright (C) 2004,2005,2006,2007,2008 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/mm.h>
|
||||
#include <grub/misc.h>
|
||||
#include <grub/disk.h>
|
||||
#include <grub/fshelp.h>
|
||||
|
||||
|
||||
/* Lookup the node PATH. The node ROOTNODE describes the root of the
|
||||
directory tree. The node found is returned in FOUNDNODE, which is
|
||||
either a ROOTNODE or a new malloc'ed node. ITERATE_DIR is used to
|
||||
iterate over all directory entries in the current node.
|
||||
READ_SYMLINK is used to read the symlink if a node is a symlink.
|
||||
EXPECTTYPE is the type node that is expected by the called, an
|
||||
error is generated if the node is not of the expected type. Make
|
||||
sure you use the NESTED_FUNC_ATTR macro for HOOK, this is required
|
||||
because GCC has a nasty bug when using regparm=3. */
|
||||
grub_err_t
|
||||
grub_fshelp_find_file (const char *path, grub_fshelp_node_t rootnode,
|
||||
grub_fshelp_node_t *foundnode,
|
||||
int (*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)),
|
||||
char *(*read_symlink) (grub_fshelp_node_t node),
|
||||
enum grub_fshelp_filetype expecttype)
|
||||
{
|
||||
grub_err_t err;
|
||||
enum grub_fshelp_filetype foundtype = GRUB_FSHELP_DIR;
|
||||
int symlinknest = 0;
|
||||
|
||||
auto grub_err_t NESTED_FUNC_ATTR find_file (const char *currpath,
|
||||
grub_fshelp_node_t currroot,
|
||||
grub_fshelp_node_t *currfound);
|
||||
|
||||
grub_err_t NESTED_FUNC_ATTR find_file (const char *currpath,
|
||||
grub_fshelp_node_t currroot,
|
||||
grub_fshelp_node_t *currfound)
|
||||
{
|
||||
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;
|
||||
|
||||
auto int NESTED_FUNC_ATTR iterate (const char *filename,
|
||||
enum grub_fshelp_filetype filetype,
|
||||
grub_fshelp_node_t node);
|
||||
|
||||
auto void free_node (grub_fshelp_node_t node);
|
||||
|
||||
void free_node (grub_fshelp_node_t node)
|
||||
{
|
||||
if (node != rootnode && node != currroot)
|
||||
grub_free (node);
|
||||
}
|
||||
|
||||
int NESTED_FUNC_ATTR iterate (const char *filename,
|
||||
enum grub_fshelp_filetype filetype,
|
||||
grub_fshelp_node_t node)
|
||||
{
|
||||
if (filetype == GRUB_FSHELP_UNKNOWN ||
|
||||
(grub_strcmp (name, filename) &&
|
||||
(! (filetype & GRUB_FSHELP_CASE_INSENSITIVE) ||
|
||||
grub_strncasecmp (name, filename, GRUB_LONG_MAX))))
|
||||
{
|
||||
grub_free (node);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* The node is found, stop iterating over the nodes. */
|
||||
type = filetype & ~GRUB_FSHELP_CASE_INSENSITIVE;
|
||||
oldnode = currnode;
|
||||
currnode = node;
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
grub_strncpy (fpath, currpath, grub_strlen (currpath) + 1);
|
||||
|
||||
/* Remove all leading slashes. */
|
||||
while (*name == '/')
|
||||
name++;
|
||||
|
||||
if (! *name)
|
||||
{
|
||||
*currfound = currnode;
|
||||
return 0;
|
||||
}
|
||||
|
||||
for (;;)
|
||||
{
|
||||
int found;
|
||||
|
||||
/* Extract the actual part from the pathname. */
|
||||
next = grub_strchr (name, '/');
|
||||
if (next)
|
||||
{
|
||||
/* Remove all leading slashes. */
|
||||
while (*next == '/')
|
||||
*(next++) = '\0';
|
||||
}
|
||||
|
||||
/* At this point it is expected that the current node is a
|
||||
directory, check if this is true. */
|
||||
if (type != GRUB_FSHELP_DIR)
|
||||
{
|
||||
free_node (currnode);
|
||||
return grub_error (GRUB_ERR_BAD_FILE_TYPE, "not a directory");
|
||||
}
|
||||
|
||||
/* Iterate over the directory. */
|
||||
found = iterate_dir (currnode, iterate);
|
||||
if (! found)
|
||||
{
|
||||
if (grub_errno)
|
||||
return grub_errno;
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
/* Read in the symlink and follow it. */
|
||||
if (type == GRUB_FSHELP_SYMLINK)
|
||||
{
|
||||
char *symlink;
|
||||
|
||||
/* Test if the symlink does not loop. */
|
||||
if (++symlinknest == 8)
|
||||
{
|
||||
free_node (currnode);
|
||||
free_node (oldnode);
|
||||
return grub_error (GRUB_ERR_SYMLINK_LOOP,
|
||||
"too deep nesting of symlinks");
|
||||
}
|
||||
|
||||
symlink = read_symlink (currnode);
|
||||
free_node (currnode);
|
||||
|
||||
if (!symlink)
|
||||
{
|
||||
free_node (oldnode);
|
||||
return grub_errno;
|
||||
}
|
||||
|
||||
/* The symlink is an absolute path, go back to the root inode. */
|
||||
if (symlink[0] == '/')
|
||||
{
|
||||
free_node (oldnode);
|
||||
oldnode = rootnode;
|
||||
}
|
||||
|
||||
/* Lookup the node the symlink points to. */
|
||||
find_file (symlink, oldnode, &currnode);
|
||||
type = foundtype;
|
||||
grub_free (symlink);
|
||||
|
||||
if (grub_errno)
|
||||
{
|
||||
free_node (oldnode);
|
||||
return grub_errno;
|
||||
}
|
||||
}
|
||||
|
||||
free_node (oldnode);
|
||||
|
||||
/* Found the node! */
|
||||
if (! next || *next == '\0')
|
||||
{
|
||||
*currfound = currnode;
|
||||
foundtype = type;
|
||||
return 0;
|
||||
}
|
||||
|
||||
name = next;
|
||||
}
|
||||
|
||||
return grub_error (GRUB_ERR_FILE_NOT_FOUND, "file not found");
|
||||
}
|
||||
|
||||
if (!path || path[0] != '/')
|
||||
{
|
||||
grub_error (GRUB_ERR_BAD_FILENAME, "bad filename");
|
||||
return grub_errno;
|
||||
}
|
||||
|
||||
err = find_file (path, rootnode, foundnode);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
/* Check if the node that was found was of the expected type. */
|
||||
if (expecttype == GRUB_FSHELP_REG && foundtype != expecttype)
|
||||
return grub_error (GRUB_ERR_BAD_FILE_TYPE, "not a regular file");
|
||||
else if (expecttype == GRUB_FSHELP_DIR && foundtype != expecttype)
|
||||
return grub_error (GRUB_ERR_BAD_FILE_TYPE, "not a directory");
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Read LEN bytes from the file NODE on disk DISK into the buffer BUF,
|
||||
beginning with the block POS. READ_HOOK should be set before
|
||||
reading a block from the file. GET_BLOCK is used to translate file
|
||||
blocks to disk blocks. The file is FILESIZE bytes big and the
|
||||
blocks have a size of LOG2BLOCKSIZE (in log2). */
|
||||
grub_ssize_t
|
||||
grub_fshelp_read_file (grub_disk_t disk, grub_fshelp_node_t node,
|
||||
void NESTED_FUNC_ATTR (*read_hook) (grub_disk_addr_t sector,
|
||||
unsigned offset,
|
||||
unsigned length),
|
||||
grub_off_t pos, grub_size_t len, char *buf,
|
||||
grub_disk_addr_t (*get_block) (grub_fshelp_node_t node,
|
||||
grub_disk_addr_t block),
|
||||
grub_off_t filesize, int log2blocksize)
|
||||
{
|
||||
grub_disk_addr_t i, blockcnt;
|
||||
int blocksize = 1 << (log2blocksize + GRUB_DISK_SECTOR_BITS);
|
||||
|
||||
/* Adjust LEN so it we can't read past the end of the file. */
|
||||
if (pos + len > filesize)
|
||||
len = filesize - pos;
|
||||
|
||||
blockcnt = ((len + pos) + blocksize - 1) >> (log2blocksize + GRUB_DISK_SECTOR_BITS);
|
||||
|
||||
for (i = pos >> (log2blocksize + GRUB_DISK_SECTOR_BITS); i < blockcnt; i++)
|
||||
{
|
||||
grub_disk_addr_t blknr;
|
||||
int blockoff = pos & (blocksize - 1);
|
||||
int blockend = blocksize;
|
||||
|
||||
int skipfirst = 0;
|
||||
|
||||
blknr = get_block (node, i);
|
||||
if (grub_errno)
|
||||
return -1;
|
||||
|
||||
blknr = blknr << log2blocksize;
|
||||
|
||||
/* Last block. */
|
||||
if (i == blockcnt - 1)
|
||||
{
|
||||
blockend = (len + pos) & (blocksize - 1);
|
||||
|
||||
/* The last portion is exactly blocksize. */
|
||||
if (! blockend)
|
||||
blockend = blocksize;
|
||||
}
|
||||
|
||||
/* First block. */
|
||||
if (i == (pos >> (log2blocksize + GRUB_DISK_SECTOR_BITS)))
|
||||
{
|
||||
skipfirst = blockoff;
|
||||
blockend -= skipfirst;
|
||||
}
|
||||
|
||||
/* If the block number is 0 this block is not stored on disk but
|
||||
is zero filled instead. */
|
||||
if (blknr)
|
||||
{
|
||||
disk->read_hook = read_hook;
|
||||
|
||||
grub_disk_read (disk, blknr, skipfirst,
|
||||
blockend, buf);
|
||||
disk->read_hook = 0;
|
||||
if (grub_errno)
|
||||
return -1;
|
||||
}
|
||||
else
|
||||
grub_memset (buf, 0, blockend);
|
||||
|
||||
buf += blocksize - skipfirst;
|
||||
}
|
||||
|
||||
return len;
|
||||
}
|
||||
|
||||
unsigned int
|
||||
grub_fshelp_log2blksize (unsigned int blksize, unsigned int *pow)
|
||||
{
|
||||
int mod;
|
||||
|
||||
*pow = 0;
|
||||
while (blksize > 1)
|
||||
{
|
||||
mod = blksize - ((blksize >> 1) << 1);
|
||||
blksize >>= 1;
|
||||
|
||||
/* Check if it really is a power of two. */
|
||||
if (mod)
|
||||
return grub_error (GRUB_ERR_BAD_NUMBER,
|
||||
"the blocksize is not a power of two");
|
||||
(*pow)++;
|
||||
}
|
||||
|
||||
return GRUB_ERR_NONE;
|
||||
}
|
1122
grub-core/fs/hfs.c
Normal file
1122
grub-core/fs/hfs.c
Normal file
File diff suppressed because it is too large
Load diff
1039
grub-core/fs/hfsplus.c
Normal file
1039
grub-core/fs/hfsplus.c
Normal file
File diff suppressed because it is too large
Load diff
635
grub-core/fs/i386/pc/pxe.c
Normal file
635
grub-core/fs/i386/pc/pxe.c
Normal file
|
@ -0,0 +1,635 @@
|
|||
/* 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_pxenv *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_pxenv *
|
||||
grub_pxe_scan (void)
|
||||
{
|
||||
struct grub_bios_int_registers regs;
|
||||
struct grub_pxenv *ret;
|
||||
void *pxe;
|
||||
|
||||
regs.flags = GRUB_CPU_INT_FLAGS_DEFAULT;
|
||||
|
||||
regs.ebx = 0;
|
||||
regs.ecx = 0;
|
||||
regs.eax = 0x5650;
|
||||
|
||||
grub_bios_interrupt (0x1a, ®s);
|
||||
|
||||
if ((regs.eax & 0xffff) != 0x564e)
|
||||
return NULL;
|
||||
ret = (struct grub_pxenv *) ((regs.es << 4) + (regs.ebx & 0xffff));
|
||||
if (grub_memcmp (ret->signature, GRUB_PXE_SIGNATURE, sizeof (ret->signature))
|
||||
!= 0)
|
||||
return NULL;
|
||||
if (ret->version < 0x201)
|
||||
return NULL;
|
||||
|
||||
pxe = (void *) ((((ret->pxe_ptr & 0xffff0000) >> 16) << 4)
|
||||
+ (ret->pxe_ptr & 0xffff));
|
||||
if (!pxe)
|
||||
return NULL;
|
||||
|
||||
/* !PXE */
|
||||
if (*(grub_uint32_t *) pxe != 0x45585021)
|
||||
return NULL;
|
||||
|
||||
pxe_rm_entry = ret->rm_entry;
|
||||
return ret;
|
||||
}
|
||||
|
||||
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->has_partitions = 0;
|
||||
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;
|
||||
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 = grub_pxe_blksize;
|
||||
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_pxenv *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 ();
|
||||
}
|
897
grub-core/fs/iso9660.c
Normal file
897
grub-core/fs/iso9660.c
Normal file
|
@ -0,0 +1,897 @@
|
|||
/* iso9660.c - iso9660 implementation with extensions:
|
||||
SUSP, Rock Ridge. */
|
||||
/*
|
||||
* GRUB -- GRand Unified Bootloader
|
||||
* Copyright (C) 2004,2005,2006,2007,2008,2009,2010 Free Software Foundation, Inc.
|
||||
*
|
||||
* GRUB is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* GRUB is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with GRUB. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include <grub/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/charset.h>
|
||||
|
||||
#define GRUB_ISO9660_FSTYPE_DIR 0040000
|
||||
#define GRUB_ISO9660_FSTYPE_REG 0100000
|
||||
#define GRUB_ISO9660_FSTYPE_SYMLINK 0120000
|
||||
#define GRUB_ISO9660_FSTYPE_MASK 0170000
|
||||
|
||||
#define GRUB_ISO9660_LOG2_BLKSZ 2
|
||||
#define GRUB_ISO9660_BLKSZ 2048
|
||||
|
||||
#define GRUB_ISO9660_RR_DOT 2
|
||||
#define GRUB_ISO9660_RR_DOTDOT 4
|
||||
|
||||
#define GRUB_ISO9660_VOLDESC_BOOT 0
|
||||
#define GRUB_ISO9660_VOLDESC_PRIMARY 1
|
||||
#define GRUB_ISO9660_VOLDESC_SUPP 2
|
||||
#define GRUB_ISO9660_VOLDESC_PART 3
|
||||
#define GRUB_ISO9660_VOLDESC_END 255
|
||||
|
||||
/* The head of a volume descriptor. */
|
||||
struct grub_iso9660_voldesc
|
||||
{
|
||||
grub_uint8_t type;
|
||||
grub_uint8_t magic[5];
|
||||
grub_uint8_t version;
|
||||
} __attribute__ ((packed));
|
||||
|
||||
/* A directory entry. */
|
||||
struct grub_iso9660_dir
|
||||
{
|
||||
grub_uint8_t len;
|
||||
grub_uint8_t ext_sectors;
|
||||
grub_uint32_t first_sector;
|
||||
grub_uint32_t first_sector_be;
|
||||
grub_uint32_t size;
|
||||
grub_uint32_t size_be;
|
||||
grub_uint8_t unused1[7];
|
||||
grub_uint8_t flags;
|
||||
grub_uint8_t unused2[6];
|
||||
grub_uint8_t namelen;
|
||||
} __attribute__ ((packed));
|
||||
|
||||
struct grub_iso9660_date
|
||||
{
|
||||
grub_uint8_t year[4];
|
||||
grub_uint8_t month[2];
|
||||
grub_uint8_t day[2];
|
||||
grub_uint8_t hour[2];
|
||||
grub_uint8_t minute[2];
|
||||
grub_uint8_t second[2];
|
||||
grub_uint8_t hundredth[2];
|
||||
grub_uint8_t offset;
|
||||
} __attribute__ ((packed));
|
||||
|
||||
/* The primary volume descriptor. Only little endian is used. */
|
||||
struct grub_iso9660_primary_voldesc
|
||||
{
|
||||
struct grub_iso9660_voldesc voldesc;
|
||||
grub_uint8_t unused1[33];
|
||||
grub_uint8_t volname[32];
|
||||
grub_uint8_t unused2[16];
|
||||
grub_uint8_t escape[32];
|
||||
grub_uint8_t unused3[12];
|
||||
grub_uint32_t path_table_size;
|
||||
grub_uint8_t unused4[4];
|
||||
grub_uint32_t path_table;
|
||||
grub_uint8_t unused5[12];
|
||||
struct grub_iso9660_dir rootdir;
|
||||
grub_uint8_t unused6[624];
|
||||
struct grub_iso9660_date created;
|
||||
struct grub_iso9660_date modified;
|
||||
} __attribute__ ((packed));
|
||||
|
||||
/* A single entry in the path table. */
|
||||
struct grub_iso9660_path
|
||||
{
|
||||
grub_uint8_t len;
|
||||
grub_uint8_t sectors;
|
||||
grub_uint32_t first_sector;
|
||||
grub_uint16_t parentdir;
|
||||
grub_uint8_t name[0];
|
||||
} __attribute__ ((packed));
|
||||
|
||||
/* An entry in the System Usage area of the directory entry. */
|
||||
struct grub_iso9660_susp_entry
|
||||
{
|
||||
grub_uint8_t sig[2];
|
||||
grub_uint8_t len;
|
||||
grub_uint8_t version;
|
||||
grub_uint8_t data[0];
|
||||
} __attribute__ ((packed));
|
||||
|
||||
/* The CE entry. This is used to describe the next block where data
|
||||
can be found. */
|
||||
struct grub_iso9660_susp_ce
|
||||
{
|
||||
struct grub_iso9660_susp_entry entry;
|
||||
grub_uint32_t blk;
|
||||
grub_uint32_t blk_be;
|
||||
grub_uint32_t off;
|
||||
grub_uint32_t off_be;
|
||||
grub_uint32_t len;
|
||||
grub_uint32_t len_be;
|
||||
} __attribute__ ((packed));
|
||||
|
||||
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
|
||||
{
|
||||
struct grub_iso9660_data *data;
|
||||
unsigned int size;
|
||||
unsigned int blk;
|
||||
unsigned int dir_blk;
|
||||
unsigned int dir_off;
|
||||
};
|
||||
|
||||
static grub_dl_t my_mod;
|
||||
|
||||
|
||||
/* 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_err_t (*hook)
|
||||
(struct grub_iso9660_susp_entry *entry))
|
||||
{
|
||||
char *sua;
|
||||
struct grub_iso9660_susp_entry *entry;
|
||||
|
||||
auto grub_err_t load_sua (void);
|
||||
|
||||
/* Load a part of the System Usage Area. */
|
||||
grub_err_t load_sua (void)
|
||||
{
|
||||
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;
|
||||
|
||||
entry = (struct grub_iso9660_susp_entry *) sua;
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (load_sua ())
|
||||
return grub_errno;
|
||||
|
||||
for (; (char *) entry < (char *) sua + sua_size - 1;
|
||||
entry = (struct grub_iso9660_susp_entry *)
|
||||
((char *) entry + entry->len))
|
||||
{
|
||||
/* The last entry. */
|
||||
if (grub_strncmp ((char *) entry->sig, "ST", 2) == 0)
|
||||
break;
|
||||
|
||||
/* Additional entries are stored elsewhere. */
|
||||
if (grub_strncmp ((char *) entry->sig, "CE", 2) == 0)
|
||||
{
|
||||
struct grub_iso9660_susp_ce *ce;
|
||||
|
||||
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;
|
||||
|
||||
grub_free (sua);
|
||||
if (load_sua ())
|
||||
return grub_errno;
|
||||
}
|
||||
|
||||
if (hook (entry))
|
||||
{
|
||||
grub_free (sua);
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
grub_free (sua);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static char *
|
||||
grub_iso9660_convert_string (grub_uint16_t *us, int len)
|
||||
{
|
||||
char *p;
|
||||
int i;
|
||||
|
||||
p = grub_malloc (len * 4 + 1);
|
||||
if (! p)
|
||||
return p;
|
||||
|
||||
for (i=0; i<len; i++)
|
||||
us[i] = grub_be_to_cpu16 (us[i]);
|
||||
|
||||
*grub_utf16_to_utf8 ((grub_uint8_t *) p, us, len) = '\0';
|
||||
|
||||
return p;
|
||||
}
|
||||
|
||||
static struct grub_iso9660_data *
|
||||
grub_iso9660_mount (grub_disk_t disk)
|
||||
{
|
||||
struct grub_iso9660_data *data = 0;
|
||||
struct grub_iso9660_dir rootdir;
|
||||
int sua_pos;
|
||||
int sua_size;
|
||||
char *sua;
|
||||
struct grub_iso9660_susp_entry *entry;
|
||||
struct grub_iso9660_primary_voldesc voldesc;
|
||||
int block;
|
||||
|
||||
auto grub_err_t susp_iterate (struct grub_iso9660_susp_entry *);
|
||||
|
||||
grub_err_t susp_iterate (struct grub_iso9660_susp_entry *susp_entry)
|
||||
{
|
||||
/* The "ER" entry is used to detect extensions. The
|
||||
`IEEE_P1285' extension means Rock ridge. */
|
||||
if (grub_strncmp ((char *) susp_entry->sig, "ER", 2) == 0)
|
||||
{
|
||||
data->rockridge = 1;
|
||||
return 1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
data = grub_zalloc (sizeof (struct grub_iso9660_data));
|
||||
if (! data)
|
||||
return 0;
|
||||
|
||||
data->disk = disk;
|
||||
|
||||
block = 16;
|
||||
do
|
||||
{
|
||||
int copy_voldesc = 0;
|
||||
|
||||
/* Read the superblock. */
|
||||
if (grub_disk_read (disk, block << GRUB_ISO9660_LOG2_BLKSZ, 0,
|
||||
sizeof (struct grub_iso9660_primary_voldesc),
|
||||
(char *) &voldesc))
|
||||
{
|
||||
grub_error (GRUB_ERR_BAD_FS, "not a ISO9660 filesystem");
|
||||
goto fail;
|
||||
}
|
||||
|
||||
if (grub_strncmp ((char *) voldesc.voldesc.magic, "CD001", 5) != 0)
|
||||
{
|
||||
grub_error (GRUB_ERR_BAD_FS, "not a ISO9660 filesystem");
|
||||
goto fail;
|
||||
}
|
||||
|
||||
if (voldesc.voldesc.type == GRUB_ISO9660_VOLDESC_PRIMARY)
|
||||
copy_voldesc = 1;
|
||||
else if ((voldesc.voldesc.type == GRUB_ISO9660_VOLDESC_SUPP) &&
|
||||
(voldesc.escape[0] == 0x25) && (voldesc.escape[1] == 0x2f) &&
|
||||
((voldesc.escape[2] == 0x40) || /* UCS-2 Level 1. */
|
||||
(voldesc.escape[2] == 0x43) || /* UCS-2 Level 2. */
|
||||
(voldesc.escape[2] == 0x45))) /* UCS-2 Level 3. */
|
||||
{
|
||||
copy_voldesc = 1;
|
||||
data->joliet = 1;
|
||||
}
|
||||
|
||||
if (copy_voldesc)
|
||||
grub_memcpy((char *) &data->voldesc, (char *) &voldesc,
|
||||
sizeof (struct grub_iso9660_primary_voldesc));
|
||||
|
||||
block++;
|
||||
} while (voldesc.voldesc.type != GRUB_ISO9660_VOLDESC_END);
|
||||
|
||||
/* Read the system use area and test it to see if SUSP is
|
||||
supported. */
|
||||
if (grub_disk_read (disk, (grub_le_to_cpu32 (data->voldesc.rootdir.first_sector)
|
||||
<< GRUB_ISO9660_LOG2_BLKSZ), 0,
|
||||
sizeof (rootdir), (char *) &rootdir))
|
||||
{
|
||||
grub_error (GRUB_ERR_BAD_FS, "not a ISO9660 filesystem");
|
||||
goto fail;
|
||||
}
|
||||
|
||||
sua_pos = (sizeof (rootdir) + rootdir.namelen
|
||||
+ (rootdir.namelen % 2) - 1);
|
||||
sua_size = rootdir.len - sua_pos;
|
||||
|
||||
sua = grub_malloc (sua_size);
|
||||
if (! sua)
|
||||
goto fail;
|
||||
|
||||
if (grub_disk_read (disk, (grub_le_to_cpu32 (data->voldesc.rootdir.first_sector)
|
||||
<< GRUB_ISO9660_LOG2_BLKSZ), sua_pos,
|
||||
sua_size, sua))
|
||||
{
|
||||
grub_error (GRUB_ERR_BAD_FS, "not a ISO9660 filesystem");
|
||||
goto fail;
|
||||
}
|
||||
|
||||
entry = (struct grub_iso9660_susp_entry *) sua;
|
||||
|
||||
/* Test if the SUSP protocol is used on this filesystem. */
|
||||
if (grub_strncmp ((char *) entry->sig, "SP", 2) == 0)
|
||||
{
|
||||
/* 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];
|
||||
entry = (struct grub_iso9660_susp_entry *) ((char *) entry + entry->len);
|
||||
|
||||
/* 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),
|
||||
sua_pos, sua_size, susp_iterate))
|
||||
goto fail;
|
||||
}
|
||||
|
||||
return data;
|
||||
|
||||
fail:
|
||||
grub_free (data);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
|
||||
static int
|
||||
grub_iso9660_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))
|
||||
{
|
||||
struct grub_iso9660_dir dirent;
|
||||
unsigned int offset = 0;
|
||||
char *filename;
|
||||
int filename_alloc = 0;
|
||||
enum grub_fshelp_filetype type;
|
||||
|
||||
auto grub_err_t susp_iterate_dir (struct grub_iso9660_susp_entry *);
|
||||
|
||||
grub_err_t susp_iterate_dir (struct grub_iso9660_susp_entry *entry)
|
||||
{
|
||||
/* The filename in the rock ridge entry. */
|
||||
if (grub_strncmp ("NM", (char *) entry->sig, 2) == 0)
|
||||
{
|
||||
/* The flags are stored at the data position 0, here the
|
||||
filename type is stored. */
|
||||
if (entry->data[0] & GRUB_ISO9660_RR_DOT)
|
||||
filename = ".";
|
||||
else if (entry->data[0] & GRUB_ISO9660_RR_DOTDOT)
|
||||
filename = "..";
|
||||
else
|
||||
{
|
||||
int size = 1;
|
||||
if (filename)
|
||||
{
|
||||
size += grub_strlen (filename);
|
||||
grub_realloc (filename,
|
||||
grub_strlen (filename)
|
||||
+ entry->len);
|
||||
}
|
||||
else
|
||||
{
|
||||
size = entry->len - 5;
|
||||
filename = grub_zalloc (size + 1);
|
||||
}
|
||||
filename_alloc = 1;
|
||||
grub_strncpy (filename, (char *) &entry->data[1], size);
|
||||
filename[size] = '\0';
|
||||
}
|
||||
}
|
||||
/* The mode information (st_mode). */
|
||||
else if (grub_strncmp ((char *) entry->sig, "PX", 2) == 0)
|
||||
{
|
||||
/* At position 0 of the PX record the st_mode information is
|
||||
stored (little-endian). */
|
||||
grub_uint32_t mode = ((entry->data[0] + (entry->data[1] << 8))
|
||||
& GRUB_ISO9660_FSTYPE_MASK);
|
||||
|
||||
switch (mode)
|
||||
{
|
||||
case GRUB_ISO9660_FSTYPE_DIR:
|
||||
type = GRUB_FSHELP_DIR;
|
||||
break;
|
||||
case GRUB_ISO9660_FSTYPE_REG:
|
||||
type = GRUB_FSHELP_REG;
|
||||
break;
|
||||
case GRUB_ISO9660_FSTYPE_SYMLINK:
|
||||
type = GRUB_FSHELP_SYMLINK;
|
||||
break;
|
||||
default:
|
||||
type = GRUB_FSHELP_UNKNOWN;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
while (offset < dir->size)
|
||||
{
|
||||
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))
|
||||
return 0;
|
||||
|
||||
/* The end of the block, skip to the next one. */
|
||||
if (!dirent.len)
|
||||
{
|
||||
offset = (offset / GRUB_ISO9660_BLKSZ + 1) * GRUB_ISO9660_BLKSZ;
|
||||
continue;
|
||||
}
|
||||
|
||||
{
|
||||
char name[dirent.namelen + 1];
|
||||
int nameoffset = offset + sizeof (dirent);
|
||||
struct grub_fshelp_node *node;
|
||||
int sua_off = (sizeof (dirent) + dirent.namelen + 1
|
||||
- (dirent.namelen % 2));
|
||||
int sua_size = dirent.len - sua_off;
|
||||
|
||||
sua_off += offset + dir->data->susp_skip;
|
||||
|
||||
filename = 0;
|
||||
filename_alloc = 0;
|
||||
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))
|
||||
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))
|
||||
return 0;
|
||||
|
||||
node = grub_malloc (sizeof (struct grub_fshelp_node));
|
||||
if (!node)
|
||||
return 0;
|
||||
|
||||
/* 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;
|
||||
|
||||
/* 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)
|
||||
type = GRUB_FSHELP_DIR;
|
||||
else
|
||||
type = GRUB_FSHELP_REG;
|
||||
}
|
||||
|
||||
/* The filename was not stored in a rock ridge entry. Read it
|
||||
from the iso9660 filesystem. */
|
||||
if (!filename)
|
||||
{
|
||||
name[dirent.namelen] = '\0';
|
||||
filename = grub_strrchr (name, ';');
|
||||
if (filename)
|
||||
*filename = '\0';
|
||||
|
||||
if (dirent.namelen == 1 && name[0] == 0)
|
||||
filename = ".";
|
||||
else if (dirent.namelen == 1 && name[0] == 1)
|
||||
filename = "..";
|
||||
else
|
||||
filename = name;
|
||||
}
|
||||
|
||||
if (dir->data->joliet)
|
||||
{
|
||||
char *oldname, *semicolon;
|
||||
|
||||
oldname = filename;
|
||||
filename = grub_iso9660_convert_string
|
||||
((grub_uint16_t *) oldname, dirent.namelen >> 1);
|
||||
|
||||
semicolon = grub_strrchr (filename, ';');
|
||||
if (semicolon)
|
||||
*semicolon = '\0';
|
||||
|
||||
if (filename_alloc)
|
||||
grub_free (oldname);
|
||||
|
||||
filename_alloc = 1;
|
||||
}
|
||||
|
||||
if (hook (filename, type, node))
|
||||
{
|
||||
if (filename_alloc)
|
||||
grub_free (filename);
|
||||
return 1;
|
||||
}
|
||||
if (filename_alloc)
|
||||
grub_free (filename);
|
||||
}
|
||||
|
||||
offset += dirent.len;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
|
||||
static grub_err_t
|
||||
grub_iso9660_dir (grub_device_t device, const char *path,
|
||||
int (*hook) (const char *filename,
|
||||
const struct grub_dirhook_info *info))
|
||||
{
|
||||
struct grub_iso9660_data *data = 0;
|
||||
struct grub_fshelp_node rootnode;
|
||||
struct grub_fshelp_node *foundnode;
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
grub_dl_ref (my_mod);
|
||||
|
||||
data = grub_iso9660_mount (device->disk);
|
||||
if (! data)
|
||||
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);
|
||||
|
||||
/* Use the fshelp function to traverse the path. */
|
||||
if (grub_fshelp_find_file (path, &rootnode,
|
||||
&foundnode,
|
||||
grub_iso9660_iterate_dir,
|
||||
grub_iso9660_read_symlink,
|
||||
GRUB_FSHELP_DIR))
|
||||
goto fail;
|
||||
|
||||
/* List the files in the directory. */
|
||||
grub_iso9660_iterate_dir (foundnode, iterate);
|
||||
|
||||
if (foundnode != &rootnode)
|
||||
grub_free (foundnode);
|
||||
|
||||
fail:
|
||||
grub_free (data);
|
||||
|
||||
grub_dl_unref (my_mod);
|
||||
|
||||
return grub_errno;
|
||||
}
|
||||
|
||||
|
||||
/* Open a file named NAME and initialize FILE. */
|
||||
static grub_err_t
|
||||
grub_iso9660_open (struct grub_file *file, const char *name)
|
||||
{
|
||||
struct grub_iso9660_data *data;
|
||||
struct grub_fshelp_node rootnode;
|
||||
struct grub_fshelp_node *foundnode;
|
||||
|
||||
grub_dl_ref (my_mod);
|
||||
|
||||
data = grub_iso9660_mount (file->device->disk);
|
||||
if (!data)
|
||||
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);
|
||||
|
||||
/* Use the fshelp function to traverse the path. */
|
||||
if (grub_fshelp_find_file (name, &rootnode,
|
||||
&foundnode,
|
||||
grub_iso9660_iterate_dir,
|
||||
grub_iso9660_read_symlink,
|
||||
GRUB_FSHELP_REG))
|
||||
goto fail;
|
||||
|
||||
data->first_sector = foundnode->blk;
|
||||
|
||||
file->data = data;
|
||||
file->size = foundnode->size;
|
||||
file->offset = 0;
|
||||
|
||||
return 0;
|
||||
|
||||
fail:
|
||||
grub_dl_unref (my_mod);
|
||||
|
||||
grub_free (data);
|
||||
|
||||
return grub_errno;
|
||||
}
|
||||
|
||||
|
||||
static grub_ssize_t
|
||||
grub_iso9660_read (grub_file_t file, char *buf, grub_size_t len)
|
||||
{
|
||||
struct grub_iso9660_data *data =
|
||||
(struct grub_iso9660_data *) file->data;
|
||||
|
||||
/* 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);
|
||||
data->disk->read_hook = NULL;
|
||||
|
||||
if (grub_errno)
|
||||
return -1;
|
||||
|
||||
return len;
|
||||
}
|
||||
|
||||
|
||||
static grub_err_t
|
||||
grub_iso9660_close (grub_file_t file)
|
||||
{
|
||||
grub_free (file->data);
|
||||
|
||||
grub_dl_unref (my_mod);
|
||||
|
||||
return GRUB_ERR_NONE;
|
||||
}
|
||||
|
||||
|
||||
static grub_err_t
|
||||
grub_iso9660_label (grub_device_t device, char **label)
|
||||
{
|
||||
struct grub_iso9660_data *data;
|
||||
data = grub_iso9660_mount (device->disk);
|
||||
|
||||
if (data)
|
||||
{
|
||||
if (data->joliet)
|
||||
*label = grub_iso9660_convert_string
|
||||
((grub_uint16_t *) &data->voldesc.volname, 16);
|
||||
else
|
||||
*label = grub_strndup ((char *) data->voldesc.volname, 32);
|
||||
grub_free (data);
|
||||
}
|
||||
else
|
||||
*label = 0;
|
||||
|
||||
return grub_errno;
|
||||
}
|
||||
|
||||
|
||||
static grub_err_t
|
||||
grub_iso9660_uuid (grub_device_t device, char **uuid)
|
||||
{
|
||||
struct grub_iso9660_data *data;
|
||||
grub_disk_t disk = device->disk;
|
||||
|
||||
grub_dl_ref (my_mod);
|
||||
|
||||
data = grub_iso9660_mount (disk);
|
||||
if (data)
|
||||
{
|
||||
if (! data->voldesc.modified.year[0] && ! data->voldesc.modified.year[1]
|
||||
&& ! data->voldesc.modified.year[2] && ! data->voldesc.modified.year[3]
|
||||
&& ! data->voldesc.modified.month[0] && ! data->voldesc.modified.month[1]
|
||||
&& ! data->voldesc.modified.day[0] && ! data->voldesc.modified.day[1]
|
||||
&& ! data->voldesc.modified.hour[0] && ! data->voldesc.modified.hour[1]
|
||||
&& ! data->voldesc.modified.minute[0] && ! data->voldesc.modified.minute[1]
|
||||
&& ! data->voldesc.modified.second[0] && ! data->voldesc.modified.second[1]
|
||||
&& ! data->voldesc.modified.hundredth[0] && ! data->voldesc.modified.hundredth[1])
|
||||
{
|
||||
grub_error (GRUB_ERR_BAD_NUMBER, "no creation date in filesystem to generate UUID");
|
||||
*uuid = NULL;
|
||||
}
|
||||
else
|
||||
{
|
||||
*uuid = grub_xasprintf ("%c%c%c%c-%c%c-%c%c-%c%c-%c%c-%c%c-%c%c",
|
||||
data->voldesc.modified.year[0],
|
||||
data->voldesc.modified.year[1],
|
||||
data->voldesc.modified.year[2],
|
||||
data->voldesc.modified.year[3],
|
||||
data->voldesc.modified.month[0],
|
||||
data->voldesc.modified.month[1],
|
||||
data->voldesc.modified.day[0],
|
||||
data->voldesc.modified.day[1],
|
||||
data->voldesc.modified.hour[0],
|
||||
data->voldesc.modified.hour[1],
|
||||
data->voldesc.modified.minute[0],
|
||||
data->voldesc.modified.minute[1],
|
||||
data->voldesc.modified.second[0],
|
||||
data->voldesc.modified.second[1],
|
||||
data->voldesc.modified.hundredth[0],
|
||||
data->voldesc.modified.hundredth[1]);
|
||||
}
|
||||
}
|
||||
else
|
||||
*uuid = NULL;
|
||||
|
||||
grub_dl_unref (my_mod);
|
||||
|
||||
grub_free (data);
|
||||
|
||||
return grub_errno;
|
||||
}
|
||||
|
||||
|
||||
|
||||
static struct grub_fs grub_iso9660_fs =
|
||||
{
|
||||
.name = "iso9660",
|
||||
.dir = grub_iso9660_dir,
|
||||
.open = grub_iso9660_open,
|
||||
.read = grub_iso9660_read,
|
||||
.close = grub_iso9660_close,
|
||||
.label = grub_iso9660_label,
|
||||
.uuid = grub_iso9660_uuid,
|
||||
.next = 0
|
||||
};
|
||||
|
||||
GRUB_MOD_INIT(iso9660)
|
||||
{
|
||||
grub_fs_register (&grub_iso9660_fs);
|
||||
my_mod = mod;
|
||||
}
|
||||
|
||||
GRUB_MOD_FINI(iso9660)
|
||||
{
|
||||
grub_fs_unregister (&grub_iso9660_fs);
|
||||
}
|
902
grub-core/fs/jfs.c
Normal file
902
grub-core/fs/jfs.c
Normal file
|
@ -0,0 +1,902 @@
|
|||
/* jfs.c - JFS. */
|
||||
/*
|
||||
* GRUB -- GRand Unified Bootloader
|
||||
* Copyright (C) 2004,2005,2006,2007,2008,2009 Free Software Foundation, Inc.
|
||||
*
|
||||
* GRUB is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* GRUB is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with GRUB. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#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/charset.h>
|
||||
|
||||
#define GRUB_JFS_MAX_SYMLNK_CNT 8
|
||||
#define GRUB_JFS_FILETYPE_MASK 0170000
|
||||
#define GRUB_JFS_FILETYPE_REG 0100000
|
||||
#define GRUB_JFS_FILETYPE_LNK 0120000
|
||||
#define GRUB_JFS_FILETYPE_DIR 0040000
|
||||
|
||||
#define GRUB_JFS_SBLOCK 64
|
||||
#define GRUB_JFS_AGGR_INODE 2
|
||||
#define GRUB_JFS_FS1_INODE_BLK 104
|
||||
|
||||
#define GRUB_JFS_TREE_LEAF 2
|
||||
|
||||
struct grub_jfs_sblock
|
||||
{
|
||||
/* The magic for JFS. It should contain the string "JFS1". */
|
||||
grub_uint8_t magic[4];
|
||||
grub_uint32_t version;
|
||||
grub_uint64_t ag_size;
|
||||
|
||||
/* The size of a filesystem block in bytes. XXX: currently only
|
||||
4096 was tested. */
|
||||
grub_uint32_t blksz;
|
||||
grub_uint16_t log2_blksz;
|
||||
|
||||
grub_uint8_t unused[71];
|
||||
grub_uint8_t volname[11];
|
||||
grub_uint8_t unused2[32];
|
||||
grub_uint8_t uuid[16];
|
||||
};
|
||||
|
||||
struct grub_jfs_extent
|
||||
{
|
||||
/* The length of the extent in filesystem blocks. */
|
||||
grub_uint16_t length;
|
||||
grub_uint8_t length2;
|
||||
|
||||
/* The physical offset of the first block on the disk. */
|
||||
grub_uint8_t blk1;
|
||||
grub_uint32_t blk2;
|
||||
} __attribute__ ((packed));
|
||||
|
||||
struct grub_jfs_iag
|
||||
{
|
||||
grub_uint8_t unused[3072];
|
||||
struct grub_jfs_extent inodes[128];
|
||||
} __attribute__ ((packed));
|
||||
|
||||
|
||||
/* The head of the tree used to find extents. */
|
||||
struct grub_jfs_treehead
|
||||
{
|
||||
grub_uint64_t next;
|
||||
grub_uint64_t prev;
|
||||
|
||||
grub_uint8_t flags;
|
||||
grub_uint8_t unused;
|
||||
|
||||
grub_uint16_t count;
|
||||
grub_uint16_t max;
|
||||
grub_uint8_t unused2[10];
|
||||
} __attribute__ ((packed));
|
||||
|
||||
/* A node in the extent tree. */
|
||||
struct grub_jfs_tree_extent
|
||||
{
|
||||
grub_uint8_t flags;
|
||||
grub_uint16_t unused;
|
||||
|
||||
/* The offset is the key used to lookup an extent. */
|
||||
grub_uint8_t offset1;
|
||||
grub_uint32_t offset2;
|
||||
|
||||
struct grub_jfs_extent extent;
|
||||
} __attribute__ ((packed));
|
||||
|
||||
/* The tree of directory entries. */
|
||||
struct grub_jfs_tree_dir
|
||||
{
|
||||
/* Pointers to the previous and next tree headers of other nodes on
|
||||
this level. */
|
||||
grub_uint64_t nextb;
|
||||
grub_uint64_t prevb;
|
||||
|
||||
grub_uint8_t flags;
|
||||
|
||||
/* The amount of dirents in this node. */
|
||||
grub_uint8_t count;
|
||||
grub_uint8_t freecnt;
|
||||
grub_uint8_t freelist;
|
||||
grub_uint8_t maxslot;
|
||||
|
||||
/* The location of the sorted array of pointers to dirents. */
|
||||
grub_uint8_t sindex;
|
||||
grub_uint8_t unused[10];
|
||||
} __attribute__ ((packed));
|
||||
|
||||
/* An internal node in the dirents tree. */
|
||||
struct grub_jfs_internal_dirent
|
||||
{
|
||||
struct grub_jfs_extent ex;
|
||||
grub_uint8_t next;
|
||||
grub_uint8_t len;
|
||||
grub_uint16_t namepart[11];
|
||||
} __attribute__ ((packed));
|
||||
|
||||
/* A leaf node in the dirents tree. */
|
||||
struct grub_jfs_leaf_dirent
|
||||
{
|
||||
/* The inode for this dirent. */
|
||||
grub_uint32_t inode;
|
||||
grub_uint8_t next;
|
||||
|
||||
/* The size of the name. */
|
||||
grub_uint8_t len;
|
||||
grub_uint16_t namepart[11];
|
||||
grub_uint32_t index;
|
||||
} __attribute__ ((packed));
|
||||
|
||||
/* A leaf in the dirents tree. This one is used if the previously
|
||||
dirent was not big enough to store the name. */
|
||||
struct grub_jfs_leaf_next_dirent
|
||||
{
|
||||
grub_uint8_t next;
|
||||
grub_uint8_t len;
|
||||
grub_uint16_t namepart[15];
|
||||
} __attribute__ ((packed));
|
||||
|
||||
struct grub_jfs_inode
|
||||
{
|
||||
grub_uint32_t stamp;
|
||||
grub_uint32_t fileset;
|
||||
grub_uint32_t inode;
|
||||
grub_uint8_t unused[12];
|
||||
grub_uint64_t size;
|
||||
grub_uint8_t unused2[20];
|
||||
grub_uint32_t mode;
|
||||
grub_uint8_t unused3[72];
|
||||
grub_uint8_t unused4[96];
|
||||
|
||||
union
|
||||
{
|
||||
/* The tree describing the extents of the file. */
|
||||
struct __attribute__ ((packed))
|
||||
{
|
||||
struct grub_jfs_treehead tree;
|
||||
struct grub_jfs_tree_extent extents[16];
|
||||
} file;
|
||||
union
|
||||
{
|
||||
/* The tree describing the dirents. */
|
||||
struct
|
||||
{
|
||||
grub_uint8_t unused[16];
|
||||
grub_uint8_t flags;
|
||||
|
||||
/* Amount of dirents in this node. */
|
||||
grub_uint8_t count;
|
||||
grub_uint8_t freecnt;
|
||||
grub_uint8_t freelist;
|
||||
grub_uint32_t idotdot;
|
||||
grub_uint8_t sorted[8];
|
||||
} header;
|
||||
struct grub_jfs_leaf_dirent dirents[8];
|
||||
} dir __attribute__ ((packed));
|
||||
/* Fast symlink. */
|
||||
struct
|
||||
{
|
||||
grub_uint8_t unused[32];
|
||||
grub_uint8_t path[128];
|
||||
} symlink;
|
||||
} __attribute__ ((packed));
|
||||
} __attribute__ ((packed));
|
||||
|
||||
struct grub_jfs_data
|
||||
{
|
||||
struct grub_jfs_sblock sblock;
|
||||
grub_disk_t disk;
|
||||
struct grub_jfs_inode fileset;
|
||||
struct grub_jfs_inode currinode;
|
||||
int pos;
|
||||
int linknest;
|
||||
} __attribute__ ((packed));
|
||||
|
||||
struct grub_jfs_diropen
|
||||
{
|
||||
int index;
|
||||
union
|
||||
{
|
||||
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];
|
||||
} *dirpage __attribute__ ((packed));
|
||||
struct grub_jfs_data *data;
|
||||
struct grub_jfs_inode *inode;
|
||||
int count;
|
||||
char *sorted;
|
||||
struct grub_jfs_leaf_dirent *leaf;
|
||||
struct grub_jfs_leaf_next_dirent *next_leaf;
|
||||
|
||||
/* The filename and inode of the last read dirent. */
|
||||
char name[255];
|
||||
grub_uint32_t ino;
|
||||
} __attribute__ ((packed));
|
||||
|
||||
|
||||
static grub_dl_t my_mod;
|
||||
|
||||
static grub_err_t grub_jfs_lookup_symlink (struct grub_jfs_data *data, int ino);
|
||||
|
||||
/* Get the block number for the block BLK in the node INODE in the
|
||||
mounted filesystem DATA. */
|
||||
static int
|
||||
grub_jfs_blkno (struct grub_jfs_data *data, struct grub_jfs_inode *inode,
|
||||
unsigned int blk)
|
||||
{
|
||||
auto int 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)
|
||||
{
|
||||
int found = -1;
|
||||
int i;
|
||||
|
||||
for (i = 0; i < grub_le_to_cpu16 (treehead->count) - 2; i++)
|
||||
{
|
||||
if (treehead->flags & GRUB_JFS_TREE_LEAF)
|
||||
{
|
||||
/* 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)
|
||||
+ 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));
|
||||
}
|
||||
else
|
||||
if (blk >= grub_le_to_cpu32 (extents[i].offset2))
|
||||
found = i;
|
||||
}
|
||||
|
||||
if (found != -1)
|
||||
{
|
||||
struct
|
||||
{
|
||||
struct grub_jfs_treehead treehead;
|
||||
struct grub_jfs_tree_extent extents[254];
|
||||
} tree;
|
||||
|
||||
if (grub_disk_read (data->disk,
|
||||
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))
|
||||
return -1;
|
||||
|
||||
return getblk (&tree.treehead, &tree.extents[0]);
|
||||
}
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
return getblk (&inode->file.tree, &inode->file.extents[0]);
|
||||
}
|
||||
|
||||
|
||||
static grub_err_t
|
||||
grub_jfs_read_inode (struct grub_jfs_data *data, int 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;
|
||||
|
||||
iagblk = grub_jfs_blkno (data, &data->fileset, iagnum + 1);
|
||||
if (grub_errno)
|
||||
return grub_errno;
|
||||
|
||||
/* Read in the IAG. */
|
||||
if (grub_disk_read (data->disk,
|
||||
iagblk << (grub_le_to_cpu16 (data->sblock.log2_blksz)
|
||||
- GRUB_DISK_SECTOR_BITS), 0,
|
||||
sizeof (struct grub_jfs_iag), &iag))
|
||||
return grub_errno;
|
||||
|
||||
inoblk = grub_le_to_cpu32 (iag.inodes[inoext].blk2);
|
||||
inoblk <<= (grub_le_to_cpu16 (data->sblock.log2_blksz)
|
||||
- GRUB_DISK_SECTOR_BITS);
|
||||
inoblk += inonum;
|
||||
|
||||
if (grub_disk_read (data->disk, inoblk, 0,
|
||||
sizeof (struct grub_jfs_inode), inode))
|
||||
return grub_errno;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
static struct grub_jfs_data *
|
||||
grub_jfs_mount (grub_disk_t disk)
|
||||
{
|
||||
struct grub_jfs_data *data = 0;
|
||||
|
||||
data = grub_malloc (sizeof (struct grub_jfs_data));
|
||||
if (!data)
|
||||
return 0;
|
||||
|
||||
/* Read the superblock. */
|
||||
if (grub_disk_read (disk, GRUB_JFS_SBLOCK, 0,
|
||||
sizeof (struct grub_jfs_sblock), &data->sblock))
|
||||
goto fail;
|
||||
|
||||
if (grub_strncmp ((char *) (data->sblock.magic), "JFS1", 4))
|
||||
{
|
||||
grub_error (GRUB_ERR_BAD_FS, "not a JFS filesystem");
|
||||
goto fail;
|
||||
}
|
||||
|
||||
data->disk = disk;
|
||||
data->pos = 0;
|
||||
data->linknest = 0;
|
||||
|
||||
/* Read the inode of the first fileset. */
|
||||
if (grub_disk_read (data->disk, GRUB_JFS_FS1_INODE_BLK, 0,
|
||||
sizeof (struct grub_jfs_inode), &data->fileset))
|
||||
goto fail;
|
||||
|
||||
return data;
|
||||
|
||||
fail:
|
||||
grub_free (data);
|
||||
|
||||
if (grub_errno == GRUB_ERR_OUT_OF_RANGE)
|
||||
grub_error (GRUB_ERR_BAD_FS, "not a JFS filesystem");
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
static struct grub_jfs_diropen *
|
||||
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;
|
||||
|
||||
de = (struct grub_jfs_internal_dirent *) inode->dir.dirents;
|
||||
|
||||
if (!((grub_le_to_cpu32 (inode->mode)
|
||||
& GRUB_JFS_FILETYPE_MASK) == GRUB_JFS_FILETYPE_DIR))
|
||||
{
|
||||
grub_error (GRUB_ERR_BAD_FILE_TYPE, "not a directory");
|
||||
return 0;
|
||||
}
|
||||
|
||||
diro = grub_zalloc (sizeof (struct grub_jfs_diropen));
|
||||
if (!diro)
|
||||
return 0;
|
||||
|
||||
diro->data = data;
|
||||
diro->inode = inode;
|
||||
|
||||
/* Check if the entire tree is contained within the inode. */
|
||||
if (inode->file.tree.flags & GRUB_JFS_TREE_LEAF)
|
||||
{
|
||||
diro->leaf = inode->dir.dirents;
|
||||
diro->next_leaf = (struct grub_jfs_leaf_next_dirent *) de;
|
||||
diro->sorted = (char *) (inode->dir.header.sorted);
|
||||
diro->count = inode->dir.header.count;
|
||||
|
||||
return diro;
|
||||
}
|
||||
|
||||
diro->dirpage = grub_malloc (grub_le_to_cpu32 (data->sblock.blksz));
|
||||
if (!diro->dirpage)
|
||||
{
|
||||
grub_free (diro);
|
||||
return 0;
|
||||
}
|
||||
|
||||
blk = grub_le_to_cpu32 (de[inode->dir.header.sorted[0]].ex.blk2);
|
||||
blk <<= (grub_le_to_cpu16 (data->sblock.log2_blksz) - GRUB_DISK_SECTOR_BITS);
|
||||
|
||||
/* Read in the nodes until we are on the leaf node level. */
|
||||
do
|
||||
{
|
||||
int index;
|
||||
if (grub_disk_read (data->disk, blk, 0,
|
||||
grub_le_to_cpu32 (data->sblock.blksz),
|
||||
diro->dirpage->sorted))
|
||||
{
|
||||
grub_free (diro->dirpage);
|
||||
grub_free (diro);
|
||||
return 0;
|
||||
}
|
||||
|
||||
de = (struct grub_jfs_internal_dirent *) diro->dirpage->dirent;
|
||||
index = diro->dirpage->sorted[diro->dirpage->header.sindex * 32];
|
||||
blk = (grub_le_to_cpu32 (de[index].ex.blk2)
|
||||
<< (grub_le_to_cpu16 (data->sblock.log2_blksz)
|
||||
- GRUB_DISK_SECTOR_BITS));
|
||||
} while (!(diro->dirpage->header.flags & GRUB_JFS_TREE_LEAF));
|
||||
|
||||
diro->leaf = diro->dirpage->dirent;
|
||||
diro->next_leaf = diro->dirpage->next_dirent;
|
||||
diro->sorted = &diro->dirpage->sorted[diro->dirpage->header.sindex * 32];
|
||||
diro->count = diro->dirpage->header.count;
|
||||
|
||||
return diro;
|
||||
}
|
||||
|
||||
|
||||
static void
|
||||
grub_jfs_closedir (struct grub_jfs_diropen *diro)
|
||||
{
|
||||
if (!diro)
|
||||
return;
|
||||
grub_free (diro->dirpage);
|
||||
grub_free (diro);
|
||||
}
|
||||
|
||||
|
||||
/* Read in the next dirent from the directory described by DIRO. */
|
||||
static grub_err_t
|
||||
grub_jfs_getent (struct grub_jfs_diropen *diro)
|
||||
{
|
||||
int strpos = 0;
|
||||
struct grub_jfs_leaf_dirent *leaf;
|
||||
struct grub_jfs_leaf_next_dirent *next_leaf;
|
||||
int len;
|
||||
int nextent;
|
||||
grub_uint16_t filename[255];
|
||||
|
||||
auto void addstr (grub_uint16_t *uname, int ulen);
|
||||
|
||||
/* Add the unicode string to the utf16 filename buffer. */
|
||||
void addstr (grub_uint16_t *name, int ulen)
|
||||
{
|
||||
while (ulen--)
|
||||
filename[strpos++] = *(name++);
|
||||
}
|
||||
|
||||
/* The last node, read in more. */
|
||||
if (diro->index == diro->count)
|
||||
{
|
||||
unsigned int next;
|
||||
|
||||
/* If the inode contains the entry tree or if this was the last
|
||||
node, there is nothing to read. */
|
||||
if ((diro->inode->file.tree.flags & GRUB_JFS_TREE_LEAF)
|
||||
|| !grub_le_to_cpu64 (diro->dirpage->header.nextb))
|
||||
return GRUB_ERR_OUT_OF_RANGE;
|
||||
|
||||
next = grub_le_to_cpu64 (diro->dirpage->header.nextb);
|
||||
next <<= (grub_le_to_cpu16 (diro->data->sblock.log2_blksz)
|
||||
- GRUB_DISK_SECTOR_BITS);
|
||||
|
||||
if (grub_disk_read (diro->data->disk, next, 0,
|
||||
grub_le_to_cpu32 (diro->data->sblock.blksz),
|
||||
diro->dirpage->sorted))
|
||||
return grub_errno;
|
||||
|
||||
diro->leaf = diro->dirpage->dirent;
|
||||
diro->next_leaf = diro->dirpage->next_dirent;
|
||||
diro->sorted = &diro->dirpage->sorted[diro->dirpage->header.sindex * 32];
|
||||
diro->count = diro->dirpage->header.count;
|
||||
diro->index = 0;
|
||||
}
|
||||
|
||||
leaf = &diro->leaf[(int) diro->sorted[diro->index]];
|
||||
next_leaf = &diro->next_leaf[diro->index];
|
||||
|
||||
len = leaf->len;
|
||||
if (!len)
|
||||
{
|
||||
diro->index++;
|
||||
return grub_jfs_getent (diro);
|
||||
}
|
||||
|
||||
addstr (leaf->namepart, len < 11 ? len : 11);
|
||||
diro->ino = grub_le_to_cpu32 (leaf->inode);
|
||||
len -= 11;
|
||||
|
||||
/* Move down to the leaf level. */
|
||||
nextent = leaf->next;
|
||||
if (leaf->next != 255)
|
||||
do
|
||||
{
|
||||
next_leaf = &diro->next_leaf[nextent];
|
||||
addstr (next_leaf->namepart, len < 15 ? len : 15 );
|
||||
|
||||
len -= 15;
|
||||
nextent = next_leaf->next;
|
||||
} while (next_leaf->next != 255 && len > 0);
|
||||
|
||||
diro->index++;
|
||||
|
||||
/* Convert the temporary UTF16 filename to UTF8. */
|
||||
*grub_utf16_to_utf8 ((grub_uint8_t *) (diro->name), filename, strpos) = '\0';
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
/* Read LEN bytes from the file described by DATA starting with byte
|
||||
POS. Return the amount of read bytes in READ. */
|
||||
static grub_ssize_t
|
||||
grub_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)
|
||||
{
|
||||
int i;
|
||||
int blockcnt;
|
||||
|
||||
blockcnt = ((len + pos + grub_le_to_cpu32 (data->sblock.blksz) - 1)
|
||||
/ grub_le_to_cpu32 (data->sblock.blksz));
|
||||
|
||||
for (i = pos / grub_le_to_cpu32 (data->sblock.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);
|
||||
|
||||
int skipfirst = 0;
|
||||
|
||||
blknr = grub_jfs_blkno (data, &data->currinode, i);
|
||||
if (grub_errno)
|
||||
return -1;
|
||||
|
||||
/* Last block. */
|
||||
if (i == blockcnt - 1)
|
||||
{
|
||||
blockend = (len + pos) % grub_le_to_cpu32 (data->sblock.blksz);
|
||||
|
||||
if (!blockend)
|
||||
blockend = grub_le_to_cpu32 (data->sblock.blksz);
|
||||
}
|
||||
|
||||
/* First block. */
|
||||
if (i == (pos / (int) grub_le_to_cpu32 (data->sblock.blksz)))
|
||||
{
|
||||
skipfirst = blockoff;
|
||||
blockend -= skipfirst;
|
||||
}
|
||||
|
||||
data->disk->read_hook = read_hook;
|
||||
grub_disk_read (data->disk,
|
||||
blknr << (grub_le_to_cpu16 (data->sblock.log2_blksz)
|
||||
- GRUB_DISK_SECTOR_BITS),
|
||||
skipfirst, blockend, buf);
|
||||
|
||||
data->disk->read_hook = 0;
|
||||
if (grub_errno)
|
||||
return -1;
|
||||
|
||||
buf += grub_le_to_cpu32 (data->sblock.blksz) - skipfirst;
|
||||
}
|
||||
|
||||
return len;
|
||||
}
|
||||
|
||||
|
||||
/* 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)
|
||||
{
|
||||
char fpath[grub_strlen (path)];
|
||||
char *name = fpath;
|
||||
char *next;
|
||||
unsigned int pos = 0;
|
||||
struct grub_jfs_diropen *diro;
|
||||
|
||||
grub_strncpy (fpath, path, grub_strlen (path) + 1);
|
||||
|
||||
if (grub_jfs_read_inode (data, GRUB_JFS_AGGR_INODE, &data->currinode))
|
||||
return grub_errno;
|
||||
|
||||
/* Skip the first slashes. */
|
||||
while (*name == '/')
|
||||
{
|
||||
name++;
|
||||
if (!*name)
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Extract the actual part from the pathname. */
|
||||
next = grub_strchr (name, '/');
|
||||
if (next)
|
||||
{
|
||||
while (*next == '/')
|
||||
{
|
||||
next[0] = '\0';
|
||||
next++;
|
||||
}
|
||||
}
|
||||
diro = grub_jfs_opendir (data, &data->currinode);
|
||||
if (!diro)
|
||||
return grub_errno;
|
||||
|
||||
for (;;)
|
||||
{
|
||||
if (grub_strlen (name) == 0)
|
||||
return GRUB_ERR_NONE;
|
||||
|
||||
if (grub_jfs_getent (diro) == GRUB_ERR_OUT_OF_RANGE)
|
||||
break;
|
||||
|
||||
/* Check if the current direntry matches the current part of the
|
||||
pathname. */
|
||||
if (!grub_strcmp (name, diro->name))
|
||||
{
|
||||
int ino = diro->ino;
|
||||
int dirino = grub_le_to_cpu32 (data->currinode.inode);
|
||||
|
||||
grub_jfs_closedir (diro);
|
||||
diro = 0;
|
||||
|
||||
if (grub_jfs_read_inode (data, ino, &data->currinode))
|
||||
break;
|
||||
|
||||
/* Check if this is a symlink. */
|
||||
if ((grub_le_to_cpu32 (data->currinode.mode)
|
||||
& GRUB_JFS_FILETYPE_MASK) == GRUB_JFS_FILETYPE_LNK)
|
||||
{
|
||||
grub_jfs_lookup_symlink (data, dirino);
|
||||
if (grub_errno)
|
||||
return grub_errno;
|
||||
}
|
||||
|
||||
if (!next)
|
||||
return 0;
|
||||
|
||||
pos = 0;
|
||||
|
||||
name = next;
|
||||
next = grub_strchr (name, '/');
|
||||
if (next)
|
||||
{
|
||||
next[0] = '\0';
|
||||
next++;
|
||||
}
|
||||
|
||||
/* Open this directory for reading dirents. */
|
||||
diro = grub_jfs_opendir (data, &data->currinode);
|
||||
if (!diro)
|
||||
return grub_errno;
|
||||
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
grub_jfs_closedir (diro);
|
||||
grub_error (GRUB_ERR_FILE_NOT_FOUND, "file not found");
|
||||
return grub_errno;
|
||||
}
|
||||
|
||||
|
||||
static grub_err_t
|
||||
grub_jfs_lookup_symlink (struct grub_jfs_data *data, int ino)
|
||||
{
|
||||
int size = grub_le_to_cpu64 (data->currinode.size);
|
||||
char symlink[size + 1];
|
||||
|
||||
if (++data->linknest > GRUB_JFS_MAX_SYMLNK_CNT)
|
||||
return grub_error (GRUB_ERR_SYMLINK_LOOP, "too deep nesting of symlinks");
|
||||
|
||||
if (size <= 128)
|
||||
grub_strncpy (symlink, (char *) (data->currinode.symlink.path), 128);
|
||||
else if (grub_jfs_read_file (data, 0, 0, size, symlink) < 0)
|
||||
return grub_errno;
|
||||
|
||||
symlink[size] = '\0';
|
||||
|
||||
/* The symlink is an absolute path, go back to the root inode. */
|
||||
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);
|
||||
if (grub_errno)
|
||||
grub_error (grub_errno, "cannot follow symlink `%s'", symlink);
|
||||
|
||||
return grub_errno;
|
||||
}
|
||||
|
||||
|
||||
static grub_err_t
|
||||
grub_jfs_dir (grub_device_t device, const char *path,
|
||||
int (*hook) (const char *filename,
|
||||
const struct grub_dirhook_info *info))
|
||||
{
|
||||
struct grub_jfs_data *data = 0;
|
||||
struct grub_jfs_diropen *diro = 0;
|
||||
|
||||
grub_dl_ref (my_mod);
|
||||
|
||||
data = grub_jfs_mount (device->disk);
|
||||
if (!data)
|
||||
goto fail;
|
||||
|
||||
if (grub_jfs_find_file (data, path))
|
||||
goto fail;
|
||||
|
||||
diro = grub_jfs_opendir (data, &data->currinode);
|
||||
if (!diro)
|
||||
goto fail;
|
||||
|
||||
/* Iterate over the dirents in the directory that was found. */
|
||||
while (grub_jfs_getent (diro) != GRUB_ERR_OUT_OF_RANGE)
|
||||
{
|
||||
struct grub_jfs_inode inode;
|
||||
struct grub_dirhook_info info;
|
||||
grub_memset (&info, 0, sizeof (info));
|
||||
|
||||
if (grub_jfs_read_inode (data, diro->ino, &inode))
|
||||
goto fail;
|
||||
|
||||
info.dir = (grub_le_to_cpu32 (inode.mode)
|
||||
& GRUB_JFS_FILETYPE_MASK) == GRUB_JFS_FILETYPE_DIR;
|
||||
if (hook (diro->name, &info))
|
||||
goto fail;
|
||||
}
|
||||
|
||||
/* XXX: GRUB_ERR_OUT_OF_RANGE is used for the last dirent. */
|
||||
if (grub_errno == GRUB_ERR_OUT_OF_RANGE)
|
||||
grub_errno = 0;
|
||||
|
||||
fail:
|
||||
grub_jfs_closedir (diro);
|
||||
grub_free (data);
|
||||
|
||||
grub_dl_unref (my_mod);
|
||||
|
||||
return grub_errno;
|
||||
}
|
||||
|
||||
|
||||
/* Open a file named NAME and initialize FILE. */
|
||||
static grub_err_t
|
||||
grub_jfs_open (struct grub_file *file, const char *name)
|
||||
{
|
||||
struct grub_jfs_data *data;
|
||||
|
||||
grub_dl_ref (my_mod);
|
||||
|
||||
data = grub_jfs_mount (file->device->disk);
|
||||
if (!data)
|
||||
goto fail;
|
||||
|
||||
grub_jfs_find_file (data, name);
|
||||
if (grub_errno)
|
||||
goto fail;
|
||||
|
||||
/* It is only possible for open regular files. */
|
||||
if (! ((grub_le_to_cpu32 (data->currinode.mode)
|
||||
& GRUB_JFS_FILETYPE_MASK) == GRUB_JFS_FILETYPE_REG))
|
||||
{
|
||||
grub_error (GRUB_ERR_BAD_FILE_TYPE, "not a regular file");
|
||||
goto fail;
|
||||
}
|
||||
|
||||
file->data = data;
|
||||
file->size = grub_le_to_cpu64 (data->currinode.size);
|
||||
|
||||
return 0;
|
||||
|
||||
fail:
|
||||
|
||||
grub_dl_unref (my_mod);
|
||||
|
||||
grub_free (data);
|
||||
|
||||
return grub_errno;
|
||||
}
|
||||
|
||||
|
||||
static grub_ssize_t
|
||||
grub_jfs_read (grub_file_t file, char *buf, grub_size_t len)
|
||||
{
|
||||
struct grub_jfs_data *data =
|
||||
(struct grub_jfs_data *) file->data;
|
||||
|
||||
return grub_jfs_read_file (data, file->read_hook, file->offset, len, buf);
|
||||
}
|
||||
|
||||
|
||||
static grub_err_t
|
||||
grub_jfs_close (grub_file_t file)
|
||||
{
|
||||
grub_free (file->data);
|
||||
|
||||
grub_dl_unref (my_mod);
|
||||
|
||||
return GRUB_ERR_NONE;
|
||||
}
|
||||
|
||||
static grub_err_t
|
||||
grub_jfs_uuid (grub_device_t device, char **uuid)
|
||||
{
|
||||
struct grub_jfs_data *data;
|
||||
grub_disk_t disk = device->disk;
|
||||
|
||||
grub_dl_ref (my_mod);
|
||||
|
||||
data = grub_jfs_mount (disk);
|
||||
if (data)
|
||||
{
|
||||
*uuid = grub_xasprintf ("%02x%02x%02x%02x-%02x%02x-%02x%02x-%02x%02x-"
|
||||
"%02x%02x%02x%02x%02x%02x",
|
||||
data->sblock.uuid[0], data->sblock.uuid[1],
|
||||
data->sblock.uuid[2], data->sblock.uuid[3],
|
||||
data->sblock.uuid[4], data->sblock.uuid[5],
|
||||
data->sblock.uuid[6], data->sblock.uuid[7],
|
||||
data->sblock.uuid[8], data->sblock.uuid[9],
|
||||
data->sblock.uuid[10], data->sblock.uuid[11],
|
||||
data->sblock.uuid[12], data->sblock.uuid[13],
|
||||
data->sblock.uuid[14], data->sblock.uuid[15]);
|
||||
}
|
||||
else
|
||||
*uuid = NULL;
|
||||
|
||||
grub_dl_unref (my_mod);
|
||||
|
||||
grub_free (data);
|
||||
|
||||
return grub_errno;
|
||||
}
|
||||
|
||||
static grub_err_t
|
||||
grub_jfs_label (grub_device_t device, char **label)
|
||||
{
|
||||
struct grub_jfs_data *data;
|
||||
data = grub_jfs_mount (device->disk);
|
||||
|
||||
if (data)
|
||||
*label = grub_strndup ((char *) (data->sblock.volname), 11);
|
||||
else
|
||||
*label = 0;
|
||||
|
||||
return grub_errno;
|
||||
}
|
||||
|
||||
|
||||
static struct grub_fs grub_jfs_fs =
|
||||
{
|
||||
.name = "jfs",
|
||||
.dir = grub_jfs_dir,
|
||||
.open = grub_jfs_open,
|
||||
.read = grub_jfs_read,
|
||||
.close = grub_jfs_close,
|
||||
.label = grub_jfs_label,
|
||||
.uuid = grub_jfs_uuid,
|
||||
.next = 0
|
||||
};
|
||||
|
||||
GRUB_MOD_INIT(jfs)
|
||||
{
|
||||
grub_fs_register (&grub_jfs_fs);
|
||||
my_mod = mod;
|
||||
}
|
||||
|
||||
GRUB_MOD_FINI(jfs)
|
||||
{
|
||||
grub_fs_unregister (&grub_jfs_fs);
|
||||
}
|
614
grub-core/fs/minix.c
Normal file
614
grub-core/fs/minix.c
Normal file
|
@ -0,0 +1,614 @@
|
|||
/* minix.c - The minix filesystem, version 1 and 2. */
|
||||
/*
|
||||
* GRUB -- GRand Unified Bootloader
|
||||
* Copyright (C) 2004,2005,2006,2007,2008 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>
|
||||
|
||||
#define GRUB_MINIX_MAGIC 0x137F
|
||||
#define GRUB_MINIX2_MAGIC 0x2468
|
||||
#define GRUB_MINIX_MAGIC_30 0x138F
|
||||
#define GRUB_MINIX2_MAGIC_30 0x2478
|
||||
#define GRUB_MINIX_BSIZE 1024U
|
||||
#define GRUB_MINIX_LOG2_BSIZE 1
|
||||
#define GRUB_MINIX_ROOT_INODE 1
|
||||
#define GRUB_MINIX_MAX_SYMLNK_CNT 8
|
||||
#define GRUB_MINIX_SBLOCK 2
|
||||
|
||||
#define GRUB_MINIX_IFDIR 0040000U
|
||||
#define GRUB_MINIX_IFLNK 0120000U
|
||||
|
||||
#define GRUB_MINIX_INODE(data,field) (data->version == 1 ? \
|
||||
data->inode. field : data->inode2. field)
|
||||
#define GRUB_MINIX_INODE_ENDIAN(data,field,bits1,bits2) (data->version == 1 ? \
|
||||
grub_le_to_cpu##bits1 (data->inode.field) : \
|
||||
grub_le_to_cpu##bits2 (data->inode2.field))
|
||||
#define GRUB_MINIX_INODE_SIZE(data) GRUB_MINIX_INODE_ENDIAN (data,size,16,32)
|
||||
#define GRUB_MINIX_INODE_MODE(data) GRUB_MINIX_INODE_ENDIAN (data,mode,16,16)
|
||||
#define GRUB_MINIX_INODE_DIR_ZONES(data,blk) GRUB_MINIX_INODE_ENDIAN \
|
||||
(data,dir_zones[blk],16,32)
|
||||
#define GRUB_MINIX_INODE_INDIR_ZONE(data) \
|
||||
GRUB_MINIX_INODE_ENDIAN (data,indir_zone,16,32)
|
||||
#define GRUB_MINIX_INODE_DINDIR_ZONE(data) \
|
||||
GRUB_MINIX_INODE_ENDIAN (data,double_indir_zone,16,32)
|
||||
#define GRUB_MINIX_INODE_BLKSZ(data) (data->version == 1 ? 2 : 4)
|
||||
#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))
|
||||
|
||||
struct grub_minix_sblock
|
||||
{
|
||||
grub_uint16_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_uint32_t max_file_size;
|
||||
grub_uint16_t magic;
|
||||
};
|
||||
|
||||
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;
|
||||
};
|
||||
|
||||
struct grub_minix2_inode
|
||||
{
|
||||
grub_uint16_t mode;
|
||||
grub_uint16_t nlinks;
|
||||
grub_uint16_t uid;
|
||||
grub_uint16_t gid;
|
||||
grub_uint32_t size;
|
||||
grub_uint32_t atime;
|
||||
grub_uint32_t mtime;
|
||||
grub_uint32_t ctime;
|
||||
grub_uint32_t dir_zones[7];
|
||||
grub_uint32_t indir_zone;
|
||||
grub_uint32_t double_indir_zone;
|
||||
grub_uint32_t unused;
|
||||
|
||||
};
|
||||
|
||||
/* Information about a "mounted" minix filesystem. */
|
||||
struct grub_minix_data
|
||||
{
|
||||
struct grub_minix_sblock sblock;
|
||||
struct grub_minix_inode inode;
|
||||
struct grub_minix2_inode inode2;
|
||||
int ino;
|
||||
int linknest;
|
||||
grub_disk_t disk;
|
||||
int version;
|
||||
int filename_size;
|
||||
};
|
||||
|
||||
static grub_dl_t my_mod;
|
||||
|
||||
static grub_err_t grub_minix_find_file (struct grub_minix_data *data,
|
||||
const char *path);
|
||||
|
||||
static int
|
||||
grub_minix_get_file_block (struct grub_minix_data *data, unsigned int blk)
|
||||
{
|
||||
struct grub_minix_sblock *sblock = &data->sblock;
|
||||
int indir;
|
||||
|
||||
auto int grub_get_indir (int, int);
|
||||
|
||||
/* Read the block pointer in ZONE, on the offset NUM. */
|
||||
int grub_get_indir (int zone, int num)
|
||||
{
|
||||
if (data->version == 1)
|
||||
{
|
||||
grub_uint16_t indir16;
|
||||
grub_disk_read (data->disk,
|
||||
zone << GRUB_MINIX_LOG2_ZONESZ,
|
||||
sizeof (grub_uint16_t) * num,
|
||||
sizeof (grub_uint16_t), (char *) &indir16);
|
||||
return grub_le_to_cpu16 (indir16);
|
||||
}
|
||||
else
|
||||
{
|
||||
grub_uint32_t indir32;
|
||||
grub_disk_read (data->disk,
|
||||
zone << GRUB_MINIX_LOG2_ZONESZ,
|
||||
sizeof (grub_uint32_t) * num,
|
||||
sizeof (grub_uint32_t), (char *) &indir32);
|
||||
return grub_le_to_cpu32 (indir32);
|
||||
}
|
||||
}
|
||||
|
||||
/* Direct block. */
|
||||
if (blk < 7)
|
||||
return GRUB_MINIX_INODE_DIR_ZONES (data, blk);
|
||||
|
||||
/* Indirect block. */
|
||||
blk -= 7;
|
||||
if (blk < GRUB_MINIX_ZONESZ / GRUB_MINIX_INODE_BLKSZ (data))
|
||||
{
|
||||
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)))
|
||||
{
|
||||
indir = grub_get_indir (GRUB_MINIX_INODE_DINDIR_ZONE (data),
|
||||
blk / GRUB_MINIX_ZONESZ);
|
||||
|
||||
indir = grub_get_indir (indir, blk % GRUB_MINIX_ZONESZ);
|
||||
|
||||
return indir;
|
||||
}
|
||||
|
||||
/* This should never happen. */
|
||||
grub_error (GRUB_ERR_OUT_OF_RANGE, "file bigger than maximum size");
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
/* Read LEN bytes from the file described by DATA starting with byte
|
||||
POS. Return the amount of read bytes in READ. */
|
||||
static grub_ssize_t
|
||||
grub_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)
|
||||
{
|
||||
struct grub_minix_sblock *sblock = &data->sblock;
|
||||
int i;
|
||||
int blockcnt;
|
||||
|
||||
/* 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;
|
||||
|
||||
for (i = pos / GRUB_MINIX_BSIZE; i < blockcnt; i++)
|
||||
{
|
||||
int blknr;
|
||||
int blockoff = pos % GRUB_MINIX_BSIZE;
|
||||
int blockend = GRUB_MINIX_BSIZE;
|
||||
|
||||
int skipfirst = 0;
|
||||
|
||||
blknr = grub_minix_get_file_block (data, i);
|
||||
if (grub_errno)
|
||||
return -1;
|
||||
|
||||
/* Last block. */
|
||||
if (i == blockcnt - 1)
|
||||
{
|
||||
blockend = (len + pos) % GRUB_MINIX_BSIZE;
|
||||
|
||||
if (!blockend)
|
||||
blockend = GRUB_MINIX_BSIZE;
|
||||
}
|
||||
|
||||
/* First block. */
|
||||
if (i == (pos / (int) GRUB_MINIX_BSIZE))
|
||||
{
|
||||
skipfirst = blockoff;
|
||||
blockend -= skipfirst;
|
||||
}
|
||||
|
||||
data->disk->read_hook = read_hook;
|
||||
grub_disk_read (data->disk, blknr << GRUB_MINIX_LOG2_ZONESZ,
|
||||
skipfirst, blockend, buf);
|
||||
|
||||
data->disk->read_hook = 0;
|
||||
if (grub_errno)
|
||||
return -1;
|
||||
|
||||
buf += GRUB_MINIX_BSIZE - skipfirst;
|
||||
}
|
||||
|
||||
return len;
|
||||
}
|
||||
|
||||
|
||||
/* Read inode INO from the mounted filesystem described by DATA. This
|
||||
inode is used by default now. */
|
||||
static grub_err_t
|
||||
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;
|
||||
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);
|
||||
|
||||
if (data->version == 1)
|
||||
{
|
||||
block += ino / (GRUB_DISK_SECTOR_SIZE / sizeof (struct grub_minix_inode));
|
||||
int offs = (ino % (GRUB_DISK_SECTOR_SIZE
|
||||
/ sizeof (struct grub_minix_inode))
|
||||
* sizeof (struct grub_minix_inode));
|
||||
|
||||
grub_disk_read (data->disk, block, offs,
|
||||
sizeof (struct grub_minix_inode), &data->inode);
|
||||
}
|
||||
else
|
||||
{
|
||||
block += ino / (GRUB_DISK_SECTOR_SIZE
|
||||
/ sizeof (struct grub_minix2_inode));
|
||||
int offs = (ino
|
||||
% (GRUB_DISK_SECTOR_SIZE / sizeof (struct grub_minix2_inode))
|
||||
* sizeof (struct grub_minix2_inode));
|
||||
|
||||
grub_disk_read (data->disk, block, offs,
|
||||
sizeof (struct grub_minix2_inode),&data->inode2);
|
||||
}
|
||||
|
||||
return GRUB_ERR_NONE;
|
||||
}
|
||||
|
||||
|
||||
/* Lookup the symlink the current inode points to. INO is the inode
|
||||
number of the directory the symlink is relative to. */
|
||||
static grub_err_t
|
||||
grub_minix_lookup_symlink (struct grub_minix_data *data, int ino)
|
||||
{
|
||||
char symlink[GRUB_MINIX_INODE_SIZE (data) + 1];
|
||||
|
||||
if (++data->linknest > GRUB_MINIX_MAX_SYMLNK_CNT)
|
||||
return grub_error (GRUB_ERR_SYMLINK_LOOP, "too deep nesting of symlinks");
|
||||
|
||||
if (grub_minix_read_file (data, 0, 0,
|
||||
GRUB_MINIX_INODE_SIZE (data), symlink) < 0)
|
||||
return grub_errno;
|
||||
|
||||
symlink[GRUB_MINIX_INODE_SIZE (data)] = '\0';
|
||||
|
||||
/* The symlink is an absolute path, go back to the root inode. */
|
||||
if (symlink[0] == '/')
|
||||
ino = GRUB_MINIX_ROOT_INODE;
|
||||
|
||||
/* Now load in the old inode. */
|
||||
if (grub_minix_read_inode (data, ino))
|
||||
return grub_errno;
|
||||
|
||||
grub_minix_find_file (data, symlink);
|
||||
if (grub_errno)
|
||||
grub_error (grub_errno, "cannot follow symlink `%s'", symlink);
|
||||
|
||||
return grub_errno;
|
||||
}
|
||||
|
||||
|
||||
/* Find the file with the pathname PATH on the filesystem described by
|
||||
DATA. */
|
||||
static grub_err_t
|
||||
grub_minix_find_file (struct grub_minix_data *data, const char *path)
|
||||
{
|
||||
char fpath[grub_strlen (path) + 1];
|
||||
char *name = fpath;
|
||||
char *next;
|
||||
unsigned int pos = 0;
|
||||
int dirino;
|
||||
|
||||
grub_strcpy (fpath, path);
|
||||
|
||||
/* Skip the first slash. */
|
||||
if (name[0] == '/')
|
||||
{
|
||||
name++;
|
||||
if (!*name)
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Extract the actual part from the pathname. */
|
||||
next = grub_strchr (name, '/');
|
||||
if (next)
|
||||
{
|
||||
next[0] = '\0';
|
||||
next++;
|
||||
}
|
||||
|
||||
do
|
||||
{
|
||||
grub_uint16_t ino;
|
||||
char filename[data->filename_size + 1];
|
||||
|
||||
if (grub_strlen (name) == 0)
|
||||
return GRUB_ERR_NONE;
|
||||
|
||||
if (grub_minix_read_file (data, 0, pos, sizeof (ino),
|
||||
(char *) &ino) < 0)
|
||||
return grub_errno;
|
||||
if (grub_minix_read_file (data, 0, pos + sizeof (ino),
|
||||
data->filename_size, (char *) filename)< 0)
|
||||
return grub_errno;
|
||||
|
||||
filename[data->filename_size] = '\0';
|
||||
|
||||
/* Check if the current direntry matches the current part of the
|
||||
pathname. */
|
||||
if (!grub_strcmp (name, filename))
|
||||
{
|
||||
dirino = data->ino;
|
||||
grub_minix_read_inode (data, grub_le_to_cpu16 (ino));
|
||||
|
||||
/* Follow the symlink. */
|
||||
if ((GRUB_MINIX_INODE_MODE (data)
|
||||
& GRUB_MINIX_IFLNK) == GRUB_MINIX_IFLNK)
|
||||
{
|
||||
grub_minix_lookup_symlink (data, dirino);
|
||||
if (grub_errno)
|
||||
return grub_errno;
|
||||
}
|
||||
|
||||
if (!next)
|
||||
return 0;
|
||||
|
||||
pos = 0;
|
||||
|
||||
name = next;
|
||||
next = grub_strchr (name, '/');
|
||||
if (next)
|
||||
{
|
||||
next[0] = '\0';
|
||||
next++;
|
||||
}
|
||||
|
||||
if ((GRUB_MINIX_INODE_MODE (data)
|
||||
& GRUB_MINIX_IFDIR) != GRUB_MINIX_IFDIR)
|
||||
return grub_error (GRUB_ERR_BAD_FILE_TYPE, "not a directory");
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
pos += sizeof (ino) + data->filename_size;
|
||||
} while (pos < GRUB_MINIX_INODE_SIZE (data));
|
||||
|
||||
grub_error (GRUB_ERR_FILE_NOT_FOUND, "file not found");
|
||||
return grub_errno;
|
||||
}
|
||||
|
||||
|
||||
/* Mount the filesystem on the disk DISK. */
|
||||
static struct grub_minix_data *
|
||||
grub_minix_mount (grub_disk_t disk)
|
||||
{
|
||||
struct grub_minix_data *data;
|
||||
|
||||
data = grub_malloc (sizeof (struct grub_minix_data));
|
||||
if (!data)
|
||||
return 0;
|
||||
|
||||
/* Read the superblock. */
|
||||
grub_disk_read (disk, GRUB_MINIX_SBLOCK, 0,
|
||||
sizeof (struct grub_minix_sblock),&data->sblock);
|
||||
if (grub_errno)
|
||||
goto fail;
|
||||
|
||||
if (grub_le_to_cpu16 (data->sblock.magic) == GRUB_MINIX_MAGIC)
|
||||
{
|
||||
data->version = 1;
|
||||
data->filename_size = 14;
|
||||
}
|
||||
else if (grub_le_to_cpu16 (data->sblock.magic) == GRUB_MINIX2_MAGIC)
|
||||
{
|
||||
data->version = 2;
|
||||
data->filename_size = 14;
|
||||
}
|
||||
else if (grub_le_to_cpu16 (data->sblock.magic) == GRUB_MINIX_MAGIC_30)
|
||||
{
|
||||
data->version = 1;
|
||||
data->filename_size = 30;
|
||||
}
|
||||
else if (grub_le_to_cpu16 (data->sblock.magic) == GRUB_MINIX2_MAGIC_30)
|
||||
{
|
||||
data->version = 2;
|
||||
data->filename_size = 30;
|
||||
}
|
||||
else
|
||||
goto fail;
|
||||
|
||||
data->disk = disk;
|
||||
data->linknest = 0;
|
||||
|
||||
return data;
|
||||
|
||||
fail:
|
||||
grub_free (data);
|
||||
grub_error (GRUB_ERR_BAD_FS, "not a minix filesystem");
|
||||
return 0;
|
||||
}
|
||||
|
||||
static grub_err_t
|
||||
grub_minix_dir (grub_device_t device, const char *path,
|
||||
int (*hook) (const char *filename,
|
||||
const struct grub_dirhook_info *info))
|
||||
{
|
||||
struct grub_minix_data *data = 0;
|
||||
struct grub_minix_sblock *sblock;
|
||||
unsigned int pos = 0;
|
||||
|
||||
data = grub_minix_mount (device->disk);
|
||||
if (!data)
|
||||
return grub_errno;
|
||||
|
||||
grub_minix_read_inode (data, GRUB_MINIX_ROOT_INODE);
|
||||
if (grub_errno)
|
||||
goto fail;
|
||||
|
||||
sblock = &data->sblock;
|
||||
|
||||
grub_minix_find_file (data, path);
|
||||
if (grub_errno)
|
||||
goto fail;
|
||||
|
||||
if ((GRUB_MINIX_INODE_MODE (data) & GRUB_MINIX_IFDIR) != GRUB_MINIX_IFDIR)
|
||||
{
|
||||
grub_error (GRUB_ERR_BAD_FILE_TYPE, "not a directory");
|
||||
goto fail;
|
||||
}
|
||||
|
||||
while (pos < GRUB_MINIX_INODE_SIZE (data))
|
||||
{
|
||||
grub_uint16_t ino;
|
||||
char filename[data->filename_size + 1];
|
||||
int dirino = data->ino;
|
||||
struct grub_dirhook_info info;
|
||||
grub_memset (&info, 0, sizeof (info));
|
||||
|
||||
|
||||
if (grub_minix_read_file (data, 0, pos, sizeof (ino),
|
||||
(char *) &ino) < 0)
|
||||
return grub_errno;
|
||||
|
||||
if (grub_minix_read_file (data, 0, pos + sizeof (ino),
|
||||
data->filename_size,
|
||||
(char *) filename) < 0)
|
||||
return grub_errno;
|
||||
filename[data->filename_size] = '\0';
|
||||
|
||||
/* 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));
|
||||
info.dir = ((GRUB_MINIX_INODE_MODE (data)
|
||||
& GRUB_MINIX_IFDIR) == GRUB_MINIX_IFDIR);
|
||||
if (hook (filename, &info) ? 1 : 0)
|
||||
break;
|
||||
|
||||
/* Load the old inode back in. */
|
||||
grub_minix_read_inode (data, dirino);
|
||||
|
||||
pos += sizeof (ino) + data->filename_size;
|
||||
}
|
||||
|
||||
fail:
|
||||
grub_free (data);
|
||||
return grub_errno;
|
||||
}
|
||||
|
||||
|
||||
/* Open a file named NAME and initialize FILE. */
|
||||
static grub_err_t
|
||||
grub_minix_open (struct grub_file *file, const char *name)
|
||||
{
|
||||
struct grub_minix_data *data;
|
||||
data = grub_minix_mount (file->device->disk);
|
||||
if (!data)
|
||||
return grub_errno;
|
||||
|
||||
/* Open the inode op the root directory. */
|
||||
grub_minix_read_inode (data, GRUB_MINIX_ROOT_INODE);
|
||||
if (grub_errno)
|
||||
{
|
||||
grub_free (data);
|
||||
return grub_errno;
|
||||
}
|
||||
|
||||
if (!name || name[0] != '/')
|
||||
{
|
||||
grub_error (GRUB_ERR_BAD_FILENAME, "bad filename");
|
||||
return grub_errno;
|
||||
}
|
||||
|
||||
/* Traverse the directory tree to the node that should be
|
||||
opened. */
|
||||
grub_minix_find_file (data, name);
|
||||
if (grub_errno)
|
||||
{
|
||||
grub_free (data);
|
||||
return grub_errno;
|
||||
}
|
||||
|
||||
file->data = data;
|
||||
file->size = GRUB_MINIX_INODE_SIZE (data);
|
||||
|
||||
return GRUB_ERR_NONE;
|
||||
}
|
||||
|
||||
|
||||
static grub_ssize_t
|
||||
grub_minix_read (grub_file_t file, char *buf, grub_size_t len)
|
||||
{
|
||||
struct grub_minix_data *data =
|
||||
(struct grub_minix_data *) file->data;
|
||||
|
||||
return grub_minix_read_file (data, file->read_hook, file->offset, len, buf);
|
||||
}
|
||||
|
||||
|
||||
static grub_err_t
|
||||
grub_minix_close (grub_file_t file)
|
||||
{
|
||||
grub_free (file->data);
|
||||
|
||||
return GRUB_ERR_NONE;
|
||||
}
|
||||
|
||||
|
||||
static grub_err_t
|
||||
grub_minix_label (grub_device_t device __attribute ((unused)),
|
||||
char **label __attribute ((unused)))
|
||||
{
|
||||
return GRUB_ERR_NONE;
|
||||
}
|
||||
|
||||
|
||||
static struct grub_fs grub_minix_fs =
|
||||
{
|
||||
.name = "minix",
|
||||
.dir = grub_minix_dir,
|
||||
.open = grub_minix_open,
|
||||
.read = grub_minix_read,
|
||||
.close = grub_minix_close,
|
||||
.label = grub_minix_label,
|
||||
.next = 0
|
||||
};
|
||||
|
||||
GRUB_MOD_INIT(minix)
|
||||
{
|
||||
grub_fs_register (&grub_minix_fs);
|
||||
my_mod = mod;
|
||||
}
|
||||
|
||||
GRUB_MOD_FINI(minix)
|
||||
{
|
||||
grub_fs_unregister (&grub_minix_fs);
|
||||
}
|
1178
grub-core/fs/nilfs2.c
Normal file
1178
grub-core/fs/nilfs2.c
Normal file
File diff suppressed because it is too large
Load diff
1111
grub-core/fs/ntfs.c
Normal file
1111
grub-core/fs/ntfs.c
Normal file
File diff suppressed because it is too large
Load diff
374
grub-core/fs/ntfscomp.c
Normal file
374
grub-core/fs/ntfscomp.c
Normal file
|
@ -0,0 +1,374 @@
|
|||
/* ntfscomp.c - compression support for the NTFS filesystem */
|
||||
/*
|
||||
* Copyright (C) 2007 Free Software Foundation, Inc.
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include <grub/file.h>
|
||||
#include <grub/mm.h>
|
||||
#include <grub/misc.h>
|
||||
#include <grub/disk.h>
|
||||
#include <grub/dl.h>
|
||||
#include <grub/fshelp.h>
|
||||
#include <grub/ntfs.h>
|
||||
|
||||
static grub_err_t
|
||||
decomp_nextvcn (struct grub_ntfs_comp *cc)
|
||||
{
|
||||
if (cc->comp_head >= cc->comp_tail)
|
||||
return grub_error (GRUB_ERR_BAD_FS, "compression block overflown");
|
||||
if (grub_disk_read
|
||||
(cc->disk,
|
||||
(cc->comp_table[cc->comp_head][1] -
|
||||
(cc->comp_table[cc->comp_head][0] - cc->cbuf_vcn)) * cc->spc, 0,
|
||||
cc->spc << BLK_SHR, cc->cbuf))
|
||||
return grub_errno;
|
||||
cc->cbuf_vcn++;
|
||||
if ((cc->cbuf_vcn >= cc->comp_table[cc->comp_head][0]))
|
||||
cc->comp_head++;
|
||||
cc->cbuf_ofs = 0;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static grub_err_t
|
||||
decomp_getch (struct grub_ntfs_comp *cc, unsigned char *res)
|
||||
{
|
||||
if (cc->cbuf_ofs >= (cc->spc << BLK_SHR))
|
||||
{
|
||||
if (decomp_nextvcn (cc))
|
||||
return grub_errno;
|
||||
}
|
||||
*res = (unsigned char) cc->cbuf[cc->cbuf_ofs++];
|
||||
return 0;
|
||||
}
|
||||
|
||||
static grub_err_t
|
||||
decomp_get16 (struct grub_ntfs_comp *cc, grub_uint16_t * res)
|
||||
{
|
||||
unsigned char c1 = 0, c2 = 0;
|
||||
|
||||
if ((decomp_getch (cc, &c1)) || (decomp_getch (cc, &c2)))
|
||||
return grub_errno;
|
||||
*res = ((grub_uint16_t) c2) * 256 + ((grub_uint16_t) c1);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Decompress a block (4096 bytes) */
|
||||
static grub_err_t
|
||||
decomp_block (struct grub_ntfs_comp *cc, char *dest)
|
||||
{
|
||||
grub_uint16_t flg, cnt;
|
||||
|
||||
if (decomp_get16 (cc, &flg))
|
||||
return grub_errno;
|
||||
cnt = (flg & 0xFFF) + 1;
|
||||
|
||||
if (dest)
|
||||
{
|
||||
if (flg & 0x8000)
|
||||
{
|
||||
unsigned char tag;
|
||||
grub_uint32_t bits, copied;
|
||||
|
||||
bits = copied = tag = 0;
|
||||
while (cnt > 0)
|
||||
{
|
||||
if (copied > COM_LEN)
|
||||
return grub_error (GRUB_ERR_BAD_FS,
|
||||
"compression block too large");
|
||||
|
||||
if (!bits)
|
||||
{
|
||||
if (decomp_getch (cc, &tag))
|
||||
return grub_errno;
|
||||
|
||||
bits = 8;
|
||||
cnt--;
|
||||
if (cnt <= 0)
|
||||
break;
|
||||
}
|
||||
if (tag & 1)
|
||||
{
|
||||
grub_uint32_t i, len, delta, code, lmask, dshift;
|
||||
grub_uint16_t word;
|
||||
|
||||
if (decomp_get16 (cc, &word))
|
||||
return grub_errno;
|
||||
|
||||
code = word;
|
||||
cnt -= 2;
|
||||
|
||||
if (!copied)
|
||||
{
|
||||
grub_error (GRUB_ERR_BAD_FS, "nontext window empty");
|
||||
return 0;
|
||||
}
|
||||
|
||||
for (i = copied - 1, lmask = 0xFFF, dshift = 12; i >= 0x10;
|
||||
i >>= 1)
|
||||
{
|
||||
lmask >>= 1;
|
||||
dshift--;
|
||||
}
|
||||
|
||||
delta = code >> dshift;
|
||||
len = (code & lmask) + 3;
|
||||
|
||||
for (i = 0; i < len; i++)
|
||||
{
|
||||
dest[copied] = dest[copied - delta - 1];
|
||||
copied++;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
unsigned char ch = 0;
|
||||
|
||||
if (decomp_getch (cc, &ch))
|
||||
return grub_errno;
|
||||
dest[copied++] = ch;
|
||||
cnt--;
|
||||
}
|
||||
tag >>= 1;
|
||||
bits--;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (cnt != COM_LEN)
|
||||
return grub_error (GRUB_ERR_BAD_FS,
|
||||
"invalid compression block size");
|
||||
}
|
||||
}
|
||||
|
||||
while (cnt > 0)
|
||||
{
|
||||
int n;
|
||||
|
||||
n = (cc->spc << BLK_SHR) - cc->cbuf_ofs;
|
||||
if (n > cnt)
|
||||
n = cnt;
|
||||
if ((dest) && (n))
|
||||
{
|
||||
grub_memcpy (dest, &cc->cbuf[cc->cbuf_ofs], n);
|
||||
dest += n;
|
||||
}
|
||||
cnt -= n;
|
||||
cc->cbuf_ofs += n;
|
||||
if ((cnt) && (decomp_nextvcn (cc)))
|
||||
return grub_errno;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static grub_err_t
|
||||
read_block (struct grub_ntfs_rlst *ctx, char *buf, int num)
|
||||
{
|
||||
int cpb = COM_SEC / ctx->comp.spc;
|
||||
|
||||
while (num)
|
||||
{
|
||||
int nn;
|
||||
|
||||
if ((ctx->target_vcn & 0xF) == 0)
|
||||
{
|
||||
|
||||
if (ctx->comp.comp_head != ctx->comp.comp_tail)
|
||||
return grub_error (GRUB_ERR_BAD_FS, "invalid compression block");
|
||||
ctx->comp.comp_head = ctx->comp.comp_tail = 0;
|
||||
ctx->comp.cbuf_vcn = ctx->target_vcn;
|
||||
ctx->comp.cbuf_ofs = (ctx->comp.spc << BLK_SHR);
|
||||
if (ctx->target_vcn >= ctx->next_vcn)
|
||||
{
|
||||
if (grub_ntfs_read_run_list (ctx))
|
||||
return grub_errno;
|
||||
}
|
||||
while (ctx->target_vcn + 16 > ctx->next_vcn)
|
||||
{
|
||||
if (ctx->flags & RF_BLNK)
|
||||
break;
|
||||
ctx->comp.comp_table[ctx->comp.comp_tail][0] = ctx->next_vcn;
|
||||
ctx->comp.comp_table[ctx->comp.comp_tail][1] =
|
||||
ctx->curr_lcn + ctx->next_vcn - ctx->curr_vcn;
|
||||
ctx->comp.comp_tail++;
|
||||
if (grub_ntfs_read_run_list (ctx))
|
||||
return grub_errno;
|
||||
}
|
||||
}
|
||||
|
||||
nn = (16 - (unsigned) (ctx->target_vcn & 0xF)) / cpb;
|
||||
if (nn > num)
|
||||
nn = num;
|
||||
num -= nn;
|
||||
|
||||
if (ctx->flags & RF_BLNK)
|
||||
{
|
||||
ctx->target_vcn += nn * cpb;
|
||||
if (ctx->comp.comp_tail == 0)
|
||||
{
|
||||
if (buf)
|
||||
{
|
||||
grub_memset (buf, 0, nn * COM_LEN);
|
||||
buf += nn * COM_LEN;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
while (nn)
|
||||
{
|
||||
if (decomp_block (&ctx->comp, buf))
|
||||
return grub_errno;
|
||||
if (buf)
|
||||
buf += COM_LEN;
|
||||
nn--;
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
nn *= cpb;
|
||||
while ((ctx->comp.comp_head < ctx->comp.comp_tail) && (nn))
|
||||
{
|
||||
int tt;
|
||||
|
||||
tt =
|
||||
ctx->comp.comp_table[ctx->comp.comp_head][0] -
|
||||
ctx->target_vcn;
|
||||
if (tt > nn)
|
||||
tt = nn;
|
||||
ctx->target_vcn += tt;
|
||||
if (buf)
|
||||
{
|
||||
if (grub_disk_read
|
||||
(ctx->comp.disk,
|
||||
(ctx->comp.comp_table[ctx->comp.comp_head][1] -
|
||||
(ctx->comp.comp_table[ctx->comp.comp_head][0] -
|
||||
ctx->target_vcn)) * ctx->comp.spc, 0,
|
||||
tt * (ctx->comp.spc << BLK_SHR), buf))
|
||||
return grub_errno;
|
||||
buf += tt * (ctx->comp.spc << BLK_SHR);
|
||||
}
|
||||
nn -= tt;
|
||||
if (ctx->target_vcn >=
|
||||
ctx->comp.comp_table[ctx->comp.comp_head][0])
|
||||
ctx->comp.comp_head++;
|
||||
}
|
||||
if (nn)
|
||||
{
|
||||
if (buf)
|
||||
{
|
||||
if (grub_disk_read
|
||||
(ctx->comp.disk,
|
||||
(ctx->target_vcn - ctx->curr_vcn +
|
||||
ctx->curr_lcn) * ctx->comp.spc, 0,
|
||||
nn * (ctx->comp.spc << BLK_SHR), buf))
|
||||
return grub_errno;
|
||||
buf += nn * (ctx->comp.spc << BLK_SHR);
|
||||
}
|
||||
ctx->target_vcn += nn;
|
||||
}
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static grub_err_t
|
||||
ntfscomp (struct grub_ntfs_attr *at, char *dest, grub_uint32_t ofs,
|
||||
grub_uint32_t len, struct grub_ntfs_rlst *ctx, grub_uint32_t vcn)
|
||||
{
|
||||
grub_err_t ret;
|
||||
|
||||
ctx->comp.comp_head = ctx->comp.comp_tail = 0;
|
||||
ctx->comp.cbuf = grub_malloc ((ctx->comp.spc) << BLK_SHR);
|
||||
if (!ctx->comp.cbuf)
|
||||
return 0;
|
||||
|
||||
ret = 0;
|
||||
|
||||
//ctx->comp.disk->read_hook = read_hook;
|
||||
|
||||
if ((vcn > ctx->target_vcn) &&
|
||||
(read_block
|
||||
(ctx, NULL, ((vcn - ctx->target_vcn) * ctx->comp.spc) / COM_SEC)))
|
||||
{
|
||||
ret = grub_errno;
|
||||
goto quit;
|
||||
}
|
||||
|
||||
if (ofs % COM_LEN)
|
||||
{
|
||||
grub_uint32_t t, n, o;
|
||||
|
||||
t = ctx->target_vcn * (ctx->comp.spc << BLK_SHR);
|
||||
if (read_block (ctx, at->sbuf, 1))
|
||||
{
|
||||
ret = grub_errno;
|
||||
goto quit;
|
||||
}
|
||||
|
||||
at->save_pos = t;
|
||||
|
||||
o = ofs % COM_LEN;
|
||||
n = COM_LEN - o;
|
||||
if (n > len)
|
||||
n = len;
|
||||
grub_memcpy (dest, &at->sbuf[o], n);
|
||||
if (n == len)
|
||||
goto quit;
|
||||
dest += n;
|
||||
len -= n;
|
||||
}
|
||||
|
||||
if (read_block (ctx, dest, len / COM_LEN))
|
||||
{
|
||||
ret = grub_errno;
|
||||
goto quit;
|
||||
}
|
||||
|
||||
dest += (len / COM_LEN) * COM_LEN;
|
||||
len = len % COM_LEN;
|
||||
if (len)
|
||||
{
|
||||
grub_uint32_t t;
|
||||
|
||||
t = ctx->target_vcn * (ctx->comp.spc << BLK_SHR);
|
||||
if (read_block (ctx, at->sbuf, 1))
|
||||
{
|
||||
ret = grub_errno;
|
||||
goto quit;
|
||||
}
|
||||
|
||||
at->save_pos = t;
|
||||
|
||||
grub_memcpy (dest, at->sbuf, len);
|
||||
}
|
||||
|
||||
quit:
|
||||
//ctx->comp.disk->read_hook = 0;
|
||||
if (ctx->comp.cbuf)
|
||||
grub_free (ctx->comp.cbuf);
|
||||
return ret;
|
||||
}
|
||||
|
||||
GRUB_MOD_INIT (ntfscomp)
|
||||
{
|
||||
grub_ntfscomp_func = ntfscomp;
|
||||
}
|
||||
|
||||
GRUB_MOD_FINI (ntfscomp)
|
||||
{
|
||||
grub_ntfscomp_func = NULL;
|
||||
}
|
1382
grub-core/fs/reiserfs.c
Normal file
1382
grub-core/fs/reiserfs.c
Normal file
File diff suppressed because it is too large
Load diff
597
grub-core/fs/sfs.c
Normal file
597
grub-core/fs/sfs.c
Normal file
|
@ -0,0 +1,597 @@
|
|||
/* sfs.c - Amiga Smart FileSystem. */
|
||||
/*
|
||||
* GRUB -- GRand Unified Bootloader
|
||||
* Copyright (C) 2005,2006,2007,2008,2009 Free Software Foundation, Inc.
|
||||
*
|
||||
* GRUB is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* GRUB is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with GRUB. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include <grub/err.h>
|
||||
#include <grub/file.h>
|
||||
#include <grub/mm.h>
|
||||
#include <grub/misc.h>
|
||||
#include <grub/disk.h>
|
||||
#include <grub/dl.h>
|
||||
#include <grub/types.h>
|
||||
#include <grub/fshelp.h>
|
||||
|
||||
/* The common header for a block. */
|
||||
struct grub_sfs_bheader
|
||||
{
|
||||
grub_uint8_t magic[4];
|
||||
grub_uint32_t chksum;
|
||||
grub_uint32_t ipointtomyself;
|
||||
} __attribute__ ((packed));
|
||||
|
||||
/* The sfs rootblock. */
|
||||
struct grub_sfs_rblock
|
||||
{
|
||||
struct grub_sfs_bheader header;
|
||||
grub_uint32_t version;
|
||||
grub_uint8_t unused1[36];
|
||||
grub_uint32_t blocksize;
|
||||
grub_uint8_t unused2[40];
|
||||
grub_uint8_t unused3[8];
|
||||
grub_uint32_t rootobject;
|
||||
grub_uint32_t btree;
|
||||
} __attribute__ ((packed));
|
||||
|
||||
/* A SFS object container. */
|
||||
struct grub_sfs_obj
|
||||
{
|
||||
grub_uint8_t unused1[4];
|
||||
grub_uint32_t nodeid;
|
||||
grub_uint8_t unused2[4];
|
||||
union
|
||||
{
|
||||
struct
|
||||
{
|
||||
grub_uint32_t first_block;
|
||||
grub_uint32_t size;
|
||||
} file __attribute__ ((packed));
|
||||
struct
|
||||
{
|
||||
grub_uint32_t hashtable;
|
||||
grub_uint32_t dir_objc;
|
||||
} dir __attribute__ ((packed));
|
||||
} file_dir;
|
||||
grub_uint8_t unused3[4];
|
||||
grub_uint8_t type;
|
||||
grub_uint8_t filename[1];
|
||||
grub_uint8_t comment[1];
|
||||
} __attribute__ ((packed));
|
||||
|
||||
#define GRUB_SFS_TYPE_DELETED 32
|
||||
#define GRUB_SFS_TYPE_SYMLINK 64
|
||||
#define GRUB_SFS_TYPE_DIR 128
|
||||
|
||||
/* A SFS object container. */
|
||||
struct grub_sfs_objc
|
||||
{
|
||||
struct grub_sfs_bheader header;
|
||||
grub_uint32_t parent;
|
||||
grub_uint32_t next;
|
||||
grub_uint32_t prev;
|
||||
/* The amount of objects depends on the blocksize. */
|
||||
struct grub_sfs_obj objects[1];
|
||||
} __attribute__ ((packed));
|
||||
|
||||
struct grub_sfs_btree_node
|
||||
{
|
||||
grub_uint32_t key;
|
||||
grub_uint32_t data;
|
||||
} __attribute__ ((packed));
|
||||
|
||||
struct grub_sfs_btree_extent
|
||||
{
|
||||
grub_uint32_t key;
|
||||
grub_uint32_t next;
|
||||
grub_uint32_t prev;
|
||||
grub_uint16_t size;
|
||||
} __attribute__ ((packed));
|
||||
|
||||
struct grub_sfs_btree
|
||||
{
|
||||
struct grub_sfs_bheader header;
|
||||
grub_uint16_t nodes;
|
||||
grub_uint8_t leaf;
|
||||
grub_uint8_t nodesize;
|
||||
/* Normally this can be kind of node, but just extents are
|
||||
supported. */
|
||||
struct grub_sfs_btree_node node[1];
|
||||
} __attribute__ ((packed));
|
||||
|
||||
|
||||
|
||||
struct grub_fshelp_node
|
||||
{
|
||||
struct grub_sfs_data *data;
|
||||
int block;
|
||||
int size;
|
||||
};
|
||||
|
||||
/* Information about a "mounted" sfs filesystem. */
|
||||
struct grub_sfs_data
|
||||
{
|
||||
struct grub_sfs_rblock rblock;
|
||||
struct grub_fshelp_node diropen;
|
||||
grub_disk_t disk;
|
||||
|
||||
/* Blocksize in sectors. */
|
||||
unsigned int blocksize;
|
||||
|
||||
/* Label of the filesystem. */
|
||||
char *label;
|
||||
};
|
||||
|
||||
static grub_dl_t my_mod;
|
||||
|
||||
|
||||
/* Lookup the extent starting with BLOCK in the filesystem described
|
||||
by DATA. Return the extent size in SIZE and the following extent
|
||||
in NEXTEXT. */
|
||||
static grub_err_t
|
||||
grub_sfs_read_extent (struct grub_sfs_data *data, unsigned int block,
|
||||
int *size, int *nextext)
|
||||
{
|
||||
char *treeblock;
|
||||
struct grub_sfs_btree *tree;
|
||||
int i;
|
||||
int next;
|
||||
int prev;
|
||||
|
||||
treeblock = grub_malloc (data->blocksize);
|
||||
if (!block)
|
||||
return 0;
|
||||
|
||||
next = grub_be_to_cpu32 (data->rblock.btree);
|
||||
tree = (struct grub_sfs_btree *) treeblock;
|
||||
|
||||
/* Handle this level in the btree. */
|
||||
do
|
||||
{
|
||||
prev = 0;
|
||||
|
||||
grub_disk_read (data->disk, next, 0, data->blocksize, treeblock);
|
||||
if (grub_errno)
|
||||
{
|
||||
grub_free (treeblock);
|
||||
return grub_errno;
|
||||
}
|
||||
|
||||
for (i = grub_be_to_cpu16 (tree->nodes) - 1; i >= 0; i--)
|
||||
{
|
||||
|
||||
#define EXTNODE(tree, index) \
|
||||
((struct grub_sfs_btree_node *) (((char *) &(tree)->node[0]) \
|
||||
+ (index) * (tree)->nodesize))
|
||||
|
||||
/* Follow the tree down to the leaf level. */
|
||||
if ((grub_be_to_cpu32 (EXTNODE(tree, i)->key) <= block)
|
||||
&& !tree->leaf)
|
||||
{
|
||||
next = grub_be_to_cpu32 (EXTNODE (tree, i)->data);
|
||||
break;
|
||||
}
|
||||
|
||||
/* If the leaf level is reached, just find the correct extent. */
|
||||
if (grub_be_to_cpu32 (EXTNODE (tree, i)->key) == block && tree->leaf)
|
||||
{
|
||||
struct grub_sfs_btree_extent *extent;
|
||||
extent = (struct grub_sfs_btree_extent *) EXTNODE (tree, i);
|
||||
|
||||
/* We found a correct leaf. */
|
||||
*size = grub_be_to_cpu16 (extent->size);
|
||||
*nextext = grub_be_to_cpu32 (extent->next);
|
||||
|
||||
grub_free (treeblock);
|
||||
return 0;
|
||||
}
|
||||
|
||||
#undef EXTNODE
|
||||
|
||||
}
|
||||
} while (!tree->leaf);
|
||||
|
||||
grub_free (treeblock);
|
||||
|
||||
return grub_error (GRUB_ERR_FILE_READ_ERROR, "SFS extent not found");
|
||||
}
|
||||
|
||||
static grub_disk_addr_t
|
||||
grub_sfs_read_block (grub_fshelp_node_t node, grub_disk_addr_t fileblock)
|
||||
{
|
||||
int blk = node->block;
|
||||
int size = 0;
|
||||
int next = 0;
|
||||
|
||||
while (blk)
|
||||
{
|
||||
grub_err_t err;
|
||||
|
||||
/* In case of the first block we don't have to lookup the
|
||||
extent, the minimum size is always 1. */
|
||||
if (fileblock == 0)
|
||||
return blk;
|
||||
|
||||
err = grub_sfs_read_extent (node->data, blk, &size, &next);
|
||||
if (err)
|
||||
return 0;
|
||||
|
||||
if (fileblock < (unsigned int) size)
|
||||
return fileblock + blk;
|
||||
|
||||
fileblock -= size;
|
||||
|
||||
blk = next;
|
||||
}
|
||||
|
||||
grub_error (GRUB_ERR_FILE_READ_ERROR,
|
||||
"reading a SFS block outside the extent");
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
/* Read LEN bytes from the file described by DATA starting with byte
|
||||
POS. Return the amount of read bytes in READ. */
|
||||
static grub_ssize_t
|
||||
grub_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)
|
||||
{
|
||||
return grub_fshelp_read_file (node->data->disk, node, read_hook,
|
||||
pos, len, buf, grub_sfs_read_block,
|
||||
node->size, 0);
|
||||
}
|
||||
|
||||
|
||||
static struct grub_sfs_data *
|
||||
grub_sfs_mount (grub_disk_t disk)
|
||||
{
|
||||
struct grub_sfs_data *data;
|
||||
struct grub_sfs_objc *rootobjc;
|
||||
char *rootobjc_data = 0;
|
||||
unsigned int blk;
|
||||
|
||||
data = grub_malloc (sizeof (*data));
|
||||
if (!data)
|
||||
return 0;
|
||||
|
||||
/* Read the rootblock. */
|
||||
grub_disk_read (disk, 0, 0, sizeof (struct grub_sfs_rblock),
|
||||
&data->rblock);
|
||||
if (grub_errno)
|
||||
goto fail;
|
||||
|
||||
/* Make sure this is a sfs filesystem. */
|
||||
if (grub_strncmp ((char *) (data->rblock.header.magic), "SFS", 4))
|
||||
{
|
||||
grub_error (GRUB_ERR_BAD_FS, "not a SFS filesystem");
|
||||
goto fail;
|
||||
}
|
||||
|
||||
data->blocksize = grub_be_to_cpu32 (data->rblock.blocksize);
|
||||
rootobjc_data = grub_malloc (data->blocksize);
|
||||
if (! rootobjc_data)
|
||||
goto fail;
|
||||
|
||||
/* Read the root object container. */
|
||||
grub_disk_read (disk, grub_be_to_cpu32 (data->rblock.rootobject), 0,
|
||||
data->blocksize, rootobjc_data);
|
||||
if (grub_errno)
|
||||
goto fail;
|
||||
|
||||
rootobjc = (struct grub_sfs_objc *) rootobjc_data;
|
||||
|
||||
blk = grub_be_to_cpu32 (rootobjc->objects[0].file_dir.dir.dir_objc);
|
||||
data->diropen.size = 0;
|
||||
data->diropen.block = blk;
|
||||
data->diropen.data = data;
|
||||
data->disk = disk;
|
||||
data->label = grub_strdup ((char *) (rootobjc->objects[0].filename));
|
||||
|
||||
return data;
|
||||
|
||||
fail:
|
||||
if (grub_errno == GRUB_ERR_OUT_OF_RANGE)
|
||||
grub_error (GRUB_ERR_BAD_FS, "not an SFS filesystem");
|
||||
|
||||
grub_free (data);
|
||||
grub_free (rootobjc_data);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
static char *
|
||||
grub_sfs_read_symlink (grub_fshelp_node_t node)
|
||||
{
|
||||
struct grub_sfs_data *data = node->data;
|
||||
char *symlink;
|
||||
char *block;
|
||||
|
||||
block = grub_malloc (data->blocksize);
|
||||
if (!block)
|
||||
return 0;
|
||||
|
||||
grub_disk_read (data->disk, node->block, 0, data->blocksize, block);
|
||||
if (grub_errno)
|
||||
{
|
||||
grub_free (block);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* This is just a wild guess, but it always worked for me. How the
|
||||
SLNK block looks like is not documented in the SFS docs. */
|
||||
symlink = grub_strdup (&block[24]);
|
||||
grub_free (block);
|
||||
if (!symlink)
|
||||
return 0;
|
||||
|
||||
return symlink;
|
||||
}
|
||||
|
||||
static int
|
||||
grub_sfs_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))
|
||||
{
|
||||
struct grub_fshelp_node *node = 0;
|
||||
struct grub_sfs_data *data = dir->data;
|
||||
char *objc_data;
|
||||
struct grub_sfs_objc *objc;
|
||||
unsigned int next = dir->block;
|
||||
int pos;
|
||||
|
||||
auto int NESTED_FUNC_ATTR grub_sfs_create_node (const char *name, int block,
|
||||
int size, int type);
|
||||
|
||||
int NESTED_FUNC_ATTR grub_sfs_create_node (const char *name, int block,
|
||||
int size, int type)
|
||||
{
|
||||
node = grub_malloc (sizeof (*node));
|
||||
if (!node)
|
||||
return 1;
|
||||
|
||||
node->data = data;
|
||||
node->size = size;
|
||||
node->block = block;
|
||||
|
||||
return hook (name, type, node);
|
||||
}
|
||||
|
||||
objc_data = grub_malloc (data->blocksize);
|
||||
if (!objc_data)
|
||||
goto fail;
|
||||
|
||||
/* The Object container can consist of multiple blocks, iterate over
|
||||
every block. */
|
||||
while (next)
|
||||
{
|
||||
grub_disk_read (data->disk, next, 0, data->blocksize, objc_data);
|
||||
if (grub_errno)
|
||||
goto fail;
|
||||
|
||||
objc = (struct grub_sfs_objc *) objc_data;
|
||||
|
||||
pos = (char *) &objc->objects[0] - (char *) objc;
|
||||
|
||||
/* Iterate over all entries in this block. */
|
||||
while (pos + sizeof (struct grub_sfs_obj) < data->blocksize)
|
||||
{
|
||||
struct grub_sfs_obj *obj;
|
||||
obj = (struct grub_sfs_obj *) ((char *) objc + pos);
|
||||
char *filename = (char *) (obj->filename);
|
||||
int len;
|
||||
enum grub_fshelp_filetype type;
|
||||
unsigned int block;
|
||||
|
||||
/* The filename and comment dynamically increase the size of
|
||||
the object. */
|
||||
len = grub_strlen (filename);
|
||||
len += grub_strlen (filename + len + 1);
|
||||
|
||||
pos += sizeof (*obj) + len;
|
||||
/* Round up to a multiple of two bytes. */
|
||||
pos = ((pos + 1) >> 1) << 1;
|
||||
|
||||
if (grub_strlen (filename) == 0)
|
||||
continue;
|
||||
|
||||
/* First check if the file was not deleted. */
|
||||
if (obj->type & GRUB_SFS_TYPE_DELETED)
|
||||
continue;
|
||||
else if (obj->type & GRUB_SFS_TYPE_SYMLINK)
|
||||
type = GRUB_FSHELP_SYMLINK;
|
||||
else if (obj->type & GRUB_SFS_TYPE_DIR)
|
||||
type = GRUB_FSHELP_DIR;
|
||||
else
|
||||
type = GRUB_FSHELP_REG;
|
||||
|
||||
if (type == GRUB_FSHELP_DIR)
|
||||
block = grub_be_to_cpu32 (obj->file_dir.dir.dir_objc);
|
||||
else
|
||||
block = grub_be_to_cpu32 (obj->file_dir.file.first_block);
|
||||
|
||||
if (grub_sfs_create_node (filename, block,
|
||||
grub_be_to_cpu32 (obj->file_dir.file.size),
|
||||
type))
|
||||
{
|
||||
grub_free (objc_data);
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
|
||||
next = grub_be_to_cpu32 (objc->next);
|
||||
}
|
||||
|
||||
fail:
|
||||
grub_free (objc_data);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
/* Open a file named NAME and initialize FILE. */
|
||||
static grub_err_t
|
||||
grub_sfs_open (struct grub_file *file, const char *name)
|
||||
{
|
||||
struct grub_sfs_data *data;
|
||||
struct grub_fshelp_node *fdiro = 0;
|
||||
|
||||
grub_dl_ref (my_mod);
|
||||
|
||||
data = grub_sfs_mount (file->device->disk);
|
||||
if (!data)
|
||||
goto fail;
|
||||
|
||||
grub_fshelp_find_file (name, &data->diropen, &fdiro, grub_sfs_iterate_dir,
|
||||
grub_sfs_read_symlink, GRUB_FSHELP_REG);
|
||||
if (grub_errno)
|
||||
goto fail;
|
||||
|
||||
file->size = fdiro->size;
|
||||
data->diropen = *fdiro;
|
||||
grub_free (fdiro);
|
||||
|
||||
file->data = data;
|
||||
file->offset = 0;
|
||||
|
||||
return 0;
|
||||
|
||||
fail:
|
||||
if (data && fdiro != &data->diropen)
|
||||
grub_free (fdiro);
|
||||
if (data)
|
||||
grub_free (data->label);
|
||||
grub_free (data);
|
||||
|
||||
grub_dl_unref (my_mod);
|
||||
|
||||
return grub_errno;
|
||||
}
|
||||
|
||||
|
||||
static grub_err_t
|
||||
grub_sfs_close (grub_file_t file)
|
||||
{
|
||||
grub_free (file->data);
|
||||
|
||||
grub_dl_unref (my_mod);
|
||||
|
||||
return GRUB_ERR_NONE;
|
||||
}
|
||||
|
||||
|
||||
/* Read LEN bytes data from FILE into BUF. */
|
||||
static grub_ssize_t
|
||||
grub_sfs_read (grub_file_t file, char *buf, grub_size_t len)
|
||||
{
|
||||
struct grub_sfs_data *data = (struct grub_sfs_data *) file->data;
|
||||
|
||||
int size = grub_sfs_read_file (&data->diropen, file->read_hook,
|
||||
file->offset, len, buf);
|
||||
|
||||
return size;
|
||||
}
|
||||
|
||||
|
||||
static grub_err_t
|
||||
grub_sfs_dir (grub_device_t device, const char *path,
|
||||
int (*hook) (const char *filename,
|
||||
const struct grub_dirhook_info *info))
|
||||
{
|
||||
struct grub_sfs_data *data = 0;
|
||||
struct grub_fshelp_node *fdiro = 0;
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
grub_dl_ref (my_mod);
|
||||
|
||||
data = grub_sfs_mount (device->disk);
|
||||
if (!data)
|
||||
goto fail;
|
||||
|
||||
grub_fshelp_find_file (path, &data->diropen, &fdiro, grub_sfs_iterate_dir,
|
||||
grub_sfs_read_symlink, GRUB_FSHELP_DIR);
|
||||
if (grub_errno)
|
||||
goto fail;
|
||||
|
||||
grub_sfs_iterate_dir (fdiro, iterate);
|
||||
|
||||
fail:
|
||||
if (data && fdiro != &data->diropen)
|
||||
grub_free (fdiro);
|
||||
if (data)
|
||||
grub_free (data->label);
|
||||
grub_free (data);
|
||||
|
||||
grub_dl_unref (my_mod);
|
||||
|
||||
return grub_errno;
|
||||
}
|
||||
|
||||
|
||||
static grub_err_t
|
||||
grub_sfs_label (grub_device_t device, char **label)
|
||||
{
|
||||
struct grub_sfs_data *data;
|
||||
grub_disk_t disk = device->disk;
|
||||
|
||||
data = grub_sfs_mount (disk);
|
||||
if (data)
|
||||
*label = data->label;
|
||||
|
||||
grub_free (data);
|
||||
|
||||
return grub_errno;
|
||||
}
|
||||
|
||||
|
||||
static struct grub_fs grub_sfs_fs =
|
||||
{
|
||||
.name = "sfs",
|
||||
.dir = grub_sfs_dir,
|
||||
.open = grub_sfs_open,
|
||||
.read = grub_sfs_read,
|
||||
.close = grub_sfs_close,
|
||||
.label = grub_sfs_label,
|
||||
.next = 0
|
||||
};
|
||||
|
||||
GRUB_MOD_INIT(sfs)
|
||||
{
|
||||
grub_fs_register (&grub_sfs_fs);
|
||||
my_mod = mod;
|
||||
}
|
||||
|
||||
GRUB_MOD_FINI(sfs)
|
||||
{
|
||||
grub_fs_unregister (&grub_sfs_fs);
|
||||
}
|
2
grub-core/fs/tar.c
Normal file
2
grub-core/fs/tar.c
Normal file
|
@ -0,0 +1,2 @@
|
|||
#define MODE_USTAR 1
|
||||
#include "cpio.c"
|
939
grub-core/fs/udf.c
Normal file
939
grub-core/fs/udf.c
Normal file
|
@ -0,0 +1,939 @@
|
|||
/* udf.c - Universal Disk Format 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/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/charset.h>
|
||||
|
||||
#define GRUB_UDF_MAX_PDS 2
|
||||
#define GRUB_UDF_MAX_PMS 6
|
||||
|
||||
#define U16 grub_le_to_cpu16
|
||||
#define U32 grub_le_to_cpu32
|
||||
#define U64 grub_le_to_cpu64
|
||||
|
||||
#define GRUB_UDF_LOG2_BLKSZ 2
|
||||
#define GRUB_UDF_BLKSZ 2048
|
||||
|
||||
#define GRUB_UDF_TAG_IDENT_PVD 0x0001
|
||||
#define GRUB_UDF_TAG_IDENT_AVDP 0x0002
|
||||
#define GRUB_UDF_TAG_IDENT_VDP 0x0003
|
||||
#define GRUB_UDF_TAG_IDENT_IUVD 0x0004
|
||||
#define GRUB_UDF_TAG_IDENT_PD 0x0005
|
||||
#define GRUB_UDF_TAG_IDENT_LVD 0x0006
|
||||
#define GRUB_UDF_TAG_IDENT_USD 0x0007
|
||||
#define GRUB_UDF_TAG_IDENT_TD 0x0008
|
||||
#define GRUB_UDF_TAG_IDENT_LVID 0x0009
|
||||
|
||||
#define GRUB_UDF_TAG_IDENT_FSD 0x0100
|
||||
#define GRUB_UDF_TAG_IDENT_FID 0x0101
|
||||
#define GRUB_UDF_TAG_IDENT_AED 0x0102
|
||||
#define GRUB_UDF_TAG_IDENT_IE 0x0103
|
||||
#define GRUB_UDF_TAG_IDENT_TE 0x0104
|
||||
#define GRUB_UDF_TAG_IDENT_FE 0x0105
|
||||
#define GRUB_UDF_TAG_IDENT_EAHD 0x0106
|
||||
#define GRUB_UDF_TAG_IDENT_USE 0x0107
|
||||
#define GRUB_UDF_TAG_IDENT_SBD 0x0108
|
||||
#define GRUB_UDF_TAG_IDENT_PIE 0x0109
|
||||
#define GRUB_UDF_TAG_IDENT_EFE 0x010A
|
||||
|
||||
#define GRUB_UDF_ICBTAG_TYPE_UNDEF 0x00
|
||||
#define GRUB_UDF_ICBTAG_TYPE_USE 0x01
|
||||
#define GRUB_UDF_ICBTAG_TYPE_PIE 0x02
|
||||
#define GRUB_UDF_ICBTAG_TYPE_IE 0x03
|
||||
#define GRUB_UDF_ICBTAG_TYPE_DIRECTORY 0x04
|
||||
#define GRUB_UDF_ICBTAG_TYPE_REGULAR 0x05
|
||||
#define GRUB_UDF_ICBTAG_TYPE_BLOCK 0x06
|
||||
#define GRUB_UDF_ICBTAG_TYPE_CHAR 0x07
|
||||
#define GRUB_UDF_ICBTAG_TYPE_EA 0x08
|
||||
#define GRUB_UDF_ICBTAG_TYPE_FIFO 0x09
|
||||
#define GRUB_UDF_ICBTAG_TYPE_SOCKET 0x0A
|
||||
#define GRUB_UDF_ICBTAG_TYPE_TE 0x0B
|
||||
#define GRUB_UDF_ICBTAG_TYPE_SYMLINK 0x0C
|
||||
#define GRUB_UDF_ICBTAG_TYPE_STREAMDIR 0x0D
|
||||
|
||||
#define GRUB_UDF_ICBTAG_FLAG_AD_MASK 0x0007
|
||||
#define GRUB_UDF_ICBTAG_FLAG_AD_SHORT 0x0000
|
||||
#define GRUB_UDF_ICBTAG_FLAG_AD_LONG 0x0001
|
||||
#define GRUB_UDF_ICBTAG_FLAG_AD_EXT 0x0002
|
||||
#define GRUB_UDF_ICBTAG_FLAG_AD_IN_ICB 0x0003
|
||||
|
||||
#define GRUB_UDF_EXT_NORMAL 0x00000000
|
||||
#define GRUB_UDF_EXT_NREC_ALLOC 0x40000000
|
||||
#define GRUB_UDF_EXT_NREC_NALLOC 0x80000000
|
||||
#define GRUB_UDF_EXT_MASK 0xC0000000
|
||||
|
||||
#define GRUB_UDF_FID_CHAR_HIDDEN 0x01
|
||||
#define GRUB_UDF_FID_CHAR_DIRECTORY 0x02
|
||||
#define GRUB_UDF_FID_CHAR_DELETED 0x04
|
||||
#define GRUB_UDF_FID_CHAR_PARENT 0x08
|
||||
#define GRUB_UDF_FID_CHAR_METADATA 0x10
|
||||
|
||||
#define GRUB_UDF_STD_IDENT_BEA01 "BEA01"
|
||||
#define GRUB_UDF_STD_IDENT_BOOT2 "BOOT2"
|
||||
#define GRUB_UDF_STD_IDENT_CD001 "CD001"
|
||||
#define GRUB_UDF_STD_IDENT_CDW02 "CDW02"
|
||||
#define GRUB_UDF_STD_IDENT_NSR02 "NSR02"
|
||||
#define GRUB_UDF_STD_IDENT_NSR03 "NSR03"
|
||||
#define GRUB_UDF_STD_IDENT_TEA01 "TEA01"
|
||||
|
||||
#define GRUB_UDF_CHARSPEC_TYPE_CS0 0x00
|
||||
#define GRUB_UDF_CHARSPEC_TYPE_CS1 0x01
|
||||
#define GRUB_UDF_CHARSPEC_TYPE_CS2 0x02
|
||||
#define GRUB_UDF_CHARSPEC_TYPE_CS3 0x03
|
||||
#define GRUB_UDF_CHARSPEC_TYPE_CS4 0x04
|
||||
#define GRUB_UDF_CHARSPEC_TYPE_CS5 0x05
|
||||
#define GRUB_UDF_CHARSPEC_TYPE_CS6 0x06
|
||||
#define GRUB_UDF_CHARSPEC_TYPE_CS7 0x07
|
||||
#define GRUB_UDF_CHARSPEC_TYPE_CS8 0x08
|
||||
|
||||
#define GRUB_UDF_PARTMAP_TYPE_1 1
|
||||
#define GRUB_UDF_PARTMAP_TYPE_2 2
|
||||
|
||||
struct grub_udf_lb_addr
|
||||
{
|
||||
grub_uint32_t block_num;
|
||||
grub_uint16_t part_ref;
|
||||
} __attribute__ ((packed));
|
||||
|
||||
struct grub_udf_short_ad
|
||||
{
|
||||
grub_uint32_t length;
|
||||
grub_uint32_t position;
|
||||
} __attribute__ ((packed));
|
||||
|
||||
struct grub_udf_long_ad
|
||||
{
|
||||
grub_uint32_t length;
|
||||
struct grub_udf_lb_addr block;
|
||||
grub_uint8_t imp_use[6];
|
||||
} __attribute__ ((packed));
|
||||
|
||||
struct grub_udf_extent_ad
|
||||
{
|
||||
grub_uint32_t length;
|
||||
grub_uint32_t start;
|
||||
} __attribute__ ((packed));
|
||||
|
||||
struct grub_udf_charspec
|
||||
{
|
||||
grub_uint8_t charset_type;
|
||||
grub_uint8_t charset_info[63];
|
||||
} __attribute__ ((packed));
|
||||
|
||||
struct grub_udf_timestamp
|
||||
{
|
||||
grub_uint16_t type_and_timezone;
|
||||
grub_uint16_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 centi_seconds;
|
||||
grub_uint8_t hundreds_of_micro_seconds;
|
||||
grub_uint8_t micro_seconds;
|
||||
} __attribute__ ((packed));
|
||||
|
||||
struct grub_udf_regid
|
||||
{
|
||||
grub_uint8_t flags;
|
||||
grub_uint8_t ident[23];
|
||||
grub_uint8_t ident_suffix[8];
|
||||
} __attribute__ ((packed));
|
||||
|
||||
struct grub_udf_tag
|
||||
{
|
||||
grub_uint16_t tag_ident;
|
||||
grub_uint16_t desc_version;
|
||||
grub_uint8_t tag_checksum;
|
||||
grub_uint8_t reserved;
|
||||
grub_uint16_t tag_serial_number;
|
||||
grub_uint16_t desc_crc;
|
||||
grub_uint16_t desc_crc_length;
|
||||
grub_uint32_t tag_location;
|
||||
} __attribute__ ((packed));
|
||||
|
||||
struct grub_udf_fileset
|
||||
{
|
||||
struct grub_udf_tag tag;
|
||||
struct grub_udf_timestamp datetime;
|
||||
grub_uint16_t interchange_level;
|
||||
grub_uint16_t max_interchange_level;
|
||||
grub_uint32_t charset_list;
|
||||
grub_uint32_t max_charset_list;
|
||||
grub_uint32_t fileset_num;
|
||||
grub_uint32_t fileset_desc_num;
|
||||
struct grub_udf_charspec vol_charset;
|
||||
grub_uint8_t vol_ident[128];
|
||||
struct grub_udf_charspec fileset_charset;
|
||||
grub_uint8_t fileset_ident[32];
|
||||
grub_uint8_t copyright_file_ident[32];
|
||||
grub_uint8_t abstract_file_ident[32];
|
||||
struct grub_udf_long_ad root_icb;
|
||||
struct grub_udf_regid domain_ident;
|
||||
struct grub_udf_long_ad next_ext;
|
||||
struct grub_udf_long_ad streamdir_icb;
|
||||
} __attribute__ ((packed));
|
||||
|
||||
struct grub_udf_icbtag
|
||||
{
|
||||
grub_uint32_t prior_recorded_num_direct_entries;
|
||||
grub_uint16_t strategy_type;
|
||||
grub_uint16_t strategy_parameter;
|
||||
grub_uint16_t num_entries;
|
||||
grub_uint8_t reserved;
|
||||
grub_uint8_t file_type;
|
||||
struct grub_udf_lb_addr parent_idb;
|
||||
grub_uint16_t flags;
|
||||
} __attribute__ ((packed));
|
||||
|
||||
struct grub_udf_file_ident
|
||||
{
|
||||
struct grub_udf_tag tag;
|
||||
grub_uint16_t version_num;
|
||||
grub_uint8_t characteristics;
|
||||
grub_uint8_t file_ident_length;
|
||||
struct grub_udf_long_ad icb;
|
||||
grub_uint16_t imp_use_length;
|
||||
} __attribute__ ((packed));
|
||||
|
||||
struct grub_udf_file_entry
|
||||
{
|
||||
struct grub_udf_tag tag;
|
||||
struct grub_udf_icbtag icbtag;
|
||||
grub_uint32_t uid;
|
||||
grub_uint32_t gid;
|
||||
grub_uint32_t permissions;
|
||||
grub_uint16_t link_count;
|
||||
grub_uint8_t record_format;
|
||||
grub_uint8_t record_display_attr;
|
||||
grub_uint32_t record_length;
|
||||
grub_uint64_t file_size;
|
||||
grub_uint64_t blocks_recorded;
|
||||
struct grub_udf_timestamp access_time;
|
||||
struct grub_udf_timestamp modification_time;
|
||||
struct grub_udf_timestamp attr_time;
|
||||
grub_uint32_t checkpoint;
|
||||
struct grub_udf_long_ad extended_attr_idb;
|
||||
struct grub_udf_regid imp_ident;
|
||||
grub_uint64_t unique_id;
|
||||
grub_uint32_t ext_attr_length;
|
||||
grub_uint32_t alloc_descs_length;
|
||||
grub_uint8_t ext_attr[1872];
|
||||
} __attribute__ ((packed));
|
||||
|
||||
struct grub_udf_extended_file_entry
|
||||
{
|
||||
struct grub_udf_tag tag;
|
||||
struct grub_udf_icbtag icbtag;
|
||||
grub_uint32_t uid;
|
||||
grub_uint32_t gid;
|
||||
grub_uint32_t permissions;
|
||||
grub_uint16_t link_count;
|
||||
grub_uint8_t record_format;
|
||||
grub_uint8_t record_display_attr;
|
||||
grub_uint32_t record_length;
|
||||
grub_uint64_t file_size;
|
||||
grub_uint64_t object_size;
|
||||
grub_uint64_t blocks_recorded;
|
||||
struct grub_udf_timestamp access_time;
|
||||
struct grub_udf_timestamp modification_time;
|
||||
struct grub_udf_timestamp create_time;
|
||||
struct grub_udf_timestamp attr_time;
|
||||
grub_uint32_t checkpoint;
|
||||
grub_uint32_t reserved;
|
||||
struct grub_udf_long_ad extended_attr_icb;
|
||||
struct grub_udf_long_ad streamdir_icb;
|
||||
struct grub_udf_regid imp_ident;
|
||||
grub_uint64_t unique_id;
|
||||
grub_uint32_t ext_attr_length;
|
||||
grub_uint32_t alloc_descs_length;
|
||||
grub_uint8_t ext_attr[1832];
|
||||
} __attribute__ ((packed));
|
||||
|
||||
struct grub_udf_vrs
|
||||
{
|
||||
grub_uint8_t type;
|
||||
grub_uint8_t magic[5];
|
||||
grub_uint8_t version;
|
||||
} __attribute__ ((packed));
|
||||
|
||||
struct grub_udf_avdp
|
||||
{
|
||||
struct grub_udf_tag tag;
|
||||
struct grub_udf_extent_ad vds;
|
||||
} __attribute__ ((packed));
|
||||
|
||||
struct grub_udf_pd
|
||||
{
|
||||
struct grub_udf_tag tag;
|
||||
grub_uint32_t seq_num;
|
||||
grub_uint16_t flags;
|
||||
grub_uint16_t part_num;
|
||||
struct grub_udf_regid contents;
|
||||
grub_uint8_t contents_use[128];
|
||||
grub_uint32_t access_type;
|
||||
grub_uint32_t start;
|
||||
grub_uint32_t length;
|
||||
} __attribute__ ((packed));
|
||||
|
||||
struct grub_udf_partmap
|
||||
{
|
||||
grub_uint8_t type;
|
||||
grub_uint8_t length;
|
||||
union
|
||||
{
|
||||
struct
|
||||
{
|
||||
grub_uint16_t seq_num;
|
||||
grub_uint16_t part_num;
|
||||
} type1;
|
||||
|
||||
struct
|
||||
{
|
||||
grub_uint8_t ident[62];
|
||||
} type2;
|
||||
};
|
||||
};
|
||||
|
||||
struct grub_udf_lvd
|
||||
{
|
||||
struct grub_udf_tag tag;
|
||||
grub_uint32_t seq_num;
|
||||
struct grub_udf_charspec charset;
|
||||
grub_uint8_t ident[128];
|
||||
grub_uint32_t bsize;
|
||||
struct grub_udf_regid domain_ident;
|
||||
struct grub_udf_long_ad root_fileset;
|
||||
grub_uint32_t map_table_length;
|
||||
grub_uint32_t num_part_maps;
|
||||
struct grub_udf_regid imp_ident;
|
||||
grub_uint8_t imp_use[128];
|
||||
struct grub_udf_extent_ad integrity_seq_ext;
|
||||
grub_uint8_t part_maps[1608];
|
||||
} __attribute__ ((packed));
|
||||
|
||||
struct grub_udf_data
|
||||
{
|
||||
grub_disk_t disk;
|
||||
struct grub_udf_lvd lvd;
|
||||
struct grub_udf_pd pds[GRUB_UDF_MAX_PDS];
|
||||
struct grub_udf_partmap *pms[GRUB_UDF_MAX_PMS];
|
||||
struct grub_udf_long_ad root_icb;
|
||||
int npd, npm;
|
||||
};
|
||||
|
||||
struct grub_fshelp_node
|
||||
{
|
||||
struct grub_udf_data *data;
|
||||
union
|
||||
{
|
||||
struct grub_udf_file_entry fe;
|
||||
struct grub_udf_extended_file_entry efe;
|
||||
};
|
||||
int part_ref;
|
||||
};
|
||||
|
||||
static grub_dl_t my_mod;
|
||||
|
||||
static grub_uint32_t
|
||||
grub_udf_get_block (struct grub_udf_data *data,
|
||||
grub_uint16_t part_ref, grub_uint32_t block)
|
||||
{
|
||||
part_ref = U16 (part_ref);
|
||||
|
||||
if (part_ref >= data->npm)
|
||||
{
|
||||
grub_error (GRUB_ERR_BAD_FS, "invalid part ref");
|
||||
return 0;
|
||||
}
|
||||
|
||||
return (U32 (data->pds[data->pms[part_ref]->type1.part_num].start)
|
||||
+ U32 (block));
|
||||
}
|
||||
|
||||
static grub_err_t
|
||||
grub_udf_read_icb (struct grub_udf_data *data,
|
||||
struct grub_udf_long_ad *icb,
|
||||
struct grub_fshelp_node *node)
|
||||
{
|
||||
grub_uint32_t block;
|
||||
|
||||
block = grub_udf_get_block (data,
|
||||
icb->block.part_ref,
|
||||
icb->block.block_num);
|
||||
|
||||
if (grub_errno)
|
||||
return grub_errno;
|
||||
|
||||
if (grub_disk_read (data->disk, block << GRUB_UDF_LOG2_BLKSZ, 0,
|
||||
sizeof (struct grub_udf_file_entry),
|
||||
&node->fe))
|
||||
return grub_errno;
|
||||
|
||||
if ((U16 (node->fe.tag.tag_ident) != GRUB_UDF_TAG_IDENT_FE) &&
|
||||
(U16 (node->fe.tag.tag_ident) != GRUB_UDF_TAG_IDENT_EFE))
|
||||
return grub_error (GRUB_ERR_BAD_FS, "invalid fe/efe descriptor");
|
||||
|
||||
node->part_ref = icb->block.part_ref;
|
||||
node->data = data;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static grub_disk_addr_t
|
||||
grub_udf_read_block (grub_fshelp_node_t node, grub_disk_addr_t fileblock)
|
||||
{
|
||||
char *ptr;
|
||||
int len;
|
||||
grub_disk_addr_t filebytes;
|
||||
|
||||
if (U16 (node->fe.tag.tag_ident) == GRUB_UDF_TAG_IDENT_FE)
|
||||
{
|
||||
ptr = (char *) &node->fe.ext_attr[0] + U32 (node->fe.ext_attr_length);
|
||||
len = U32 (node->fe.alloc_descs_length);
|
||||
}
|
||||
else
|
||||
{
|
||||
ptr = (char *) &node->efe.ext_attr[0] + U32 (node->efe.ext_attr_length);
|
||||
len = U32 (node->efe.alloc_descs_length);
|
||||
}
|
||||
|
||||
if ((U16 (node->fe.icbtag.flags) & GRUB_UDF_ICBTAG_FLAG_AD_MASK)
|
||||
== GRUB_UDF_ICBTAG_FLAG_AD_SHORT)
|
||||
{
|
||||
struct grub_udf_short_ad *ad = (struct grub_udf_short_ad *) ptr;
|
||||
|
||||
len /= sizeof (struct grub_udf_short_ad);
|
||||
filebytes = fileblock * GRUB_UDF_BLKSZ;
|
||||
while (len > 0)
|
||||
{
|
||||
if (filebytes < U32 (ad->length))
|
||||
return ((U32 (ad->position) & GRUB_UDF_EXT_MASK) ? 0 :
|
||||
(grub_udf_get_block (node->data,
|
||||
node->part_ref,
|
||||
ad->position)
|
||||
+ (filebytes / GRUB_UDF_BLKSZ)));
|
||||
|
||||
filebytes -= U32 (ad->length);
|
||||
ad++;
|
||||
len--;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
struct grub_udf_long_ad *ad = (struct grub_udf_long_ad *) ptr;
|
||||
|
||||
len /= sizeof (struct grub_udf_long_ad);
|
||||
filebytes = fileblock * GRUB_UDF_BLKSZ;
|
||||
while (len > 0)
|
||||
{
|
||||
if (filebytes < U32 (ad->length))
|
||||
return ((U32 (ad->block.block_num) & GRUB_UDF_EXT_MASK) ? 0 :
|
||||
(grub_udf_get_block (node->data,
|
||||
ad->block.part_ref,
|
||||
ad->block.block_num)
|
||||
+ (filebytes / GRUB_UDF_BLKSZ)));
|
||||
|
||||
filebytes -= U32 (ad->length);
|
||||
ad++;
|
||||
len--;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static grub_ssize_t
|
||||
grub_udf_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)
|
||||
{
|
||||
switch (U16 (node->fe.icbtag.flags) & GRUB_UDF_ICBTAG_FLAG_AD_MASK)
|
||||
{
|
||||
case GRUB_UDF_ICBTAG_FLAG_AD_IN_ICB:
|
||||
{
|
||||
char *ptr;
|
||||
|
||||
ptr = ((U16 (node->fe.tag.tag_ident) == GRUB_UDF_TAG_IDENT_FE) ?
|
||||
((char *) &node->fe.ext_attr[0]
|
||||
+ U32 (node->fe.ext_attr_length)) :
|
||||
((char *) &node->efe.ext_attr[0]
|
||||
+ U32 (node->efe.ext_attr_length)));
|
||||
|
||||
grub_memcpy (buf, ptr + pos, len);
|
||||
|
||||
return len;
|
||||
}
|
||||
|
||||
case GRUB_UDF_ICBTAG_FLAG_AD_EXT:
|
||||
grub_error (GRUB_ERR_BAD_FS, "invalid extent type");
|
||||
return 0;
|
||||
}
|
||||
|
||||
return grub_fshelp_read_file (node->data->disk, node, read_hook,
|
||||
pos, len, buf, grub_udf_read_block,
|
||||
U64 (node->fe.file_size),
|
||||
GRUB_UDF_LOG2_BLKSZ);
|
||||
}
|
||||
|
||||
static int sblocklist[] = { 256, 512, 0 };
|
||||
|
||||
static struct grub_udf_data *
|
||||
grub_udf_mount (grub_disk_t disk)
|
||||
{
|
||||
struct grub_udf_data *data = 0;
|
||||
struct grub_udf_fileset root_fs;
|
||||
int *sblklist = sblocklist;
|
||||
grub_uint32_t block;
|
||||
int i;
|
||||
|
||||
data = grub_malloc (sizeof (struct grub_udf_data));
|
||||
if (!data)
|
||||
return 0;
|
||||
|
||||
data->disk = disk;
|
||||
|
||||
/* Search for Volume Recognition Sequence (VRS). */
|
||||
for (block = 16;; block++)
|
||||
{
|
||||
struct grub_udf_vrs vrs;
|
||||
|
||||
if (grub_disk_read (disk, block << GRUB_UDF_LOG2_BLKSZ, 0,
|
||||
sizeof (struct grub_udf_vrs), &vrs))
|
||||
{
|
||||
grub_error (GRUB_ERR_BAD_FS, "not an UDF filesystem");
|
||||
goto fail;
|
||||
}
|
||||
|
||||
if ((!grub_memcmp (vrs.magic, GRUB_UDF_STD_IDENT_NSR03, 5)) ||
|
||||
(!grub_memcmp (vrs.magic, GRUB_UDF_STD_IDENT_NSR02, 5)))
|
||||
break;
|
||||
|
||||
if ((grub_memcmp (vrs.magic, GRUB_UDF_STD_IDENT_BEA01, 5)) &&
|
||||
(grub_memcmp (vrs.magic, GRUB_UDF_STD_IDENT_BOOT2, 5)) &&
|
||||
(grub_memcmp (vrs.magic, GRUB_UDF_STD_IDENT_CD001, 5)) &&
|
||||
(grub_memcmp (vrs.magic, GRUB_UDF_STD_IDENT_CDW02, 5)) &&
|
||||
(grub_memcmp (vrs.magic, GRUB_UDF_STD_IDENT_TEA01, 5)))
|
||||
{
|
||||
grub_error (GRUB_ERR_BAD_FS, "not an UDF filesystem");
|
||||
goto fail;
|
||||
}
|
||||
}
|
||||
|
||||
/* Search for Anchor Volume Descriptor Pointer (AVDP). */
|
||||
while (1)
|
||||
{
|
||||
struct grub_udf_avdp avdp;
|
||||
|
||||
if (grub_disk_read (disk, *sblklist << GRUB_UDF_LOG2_BLKSZ, 0,
|
||||
sizeof (struct grub_udf_avdp), &avdp))
|
||||
{
|
||||
grub_error (GRUB_ERR_BAD_FS, "not an UDF filesystem");
|
||||
goto fail;
|
||||
}
|
||||
|
||||
if (U16 (avdp.tag.tag_ident) == GRUB_UDF_TAG_IDENT_AVDP)
|
||||
{
|
||||
block = U32 (avdp.vds.start);
|
||||
break;
|
||||
}
|
||||
|
||||
sblklist++;
|
||||
if (*sblklist == 0)
|
||||
{
|
||||
grub_error (GRUB_ERR_BAD_FS, "not an UDF filesystem");
|
||||
goto fail;
|
||||
}
|
||||
}
|
||||
|
||||
data->npd = data->npm = 0;
|
||||
/* Locate Partition Descriptor (PD) and Logical Volume Descriptor (LVD). */
|
||||
while (1)
|
||||
{
|
||||
struct grub_udf_tag tag;
|
||||
|
||||
if (grub_disk_read (disk, block << GRUB_UDF_LOG2_BLKSZ, 0,
|
||||
sizeof (struct grub_udf_tag), &tag))
|
||||
{
|
||||
grub_error (GRUB_ERR_BAD_FS, "not an UDF filesystem");
|
||||
goto fail;
|
||||
}
|
||||
|
||||
tag.tag_ident = U16 (tag.tag_ident);
|
||||
if (tag.tag_ident == GRUB_UDF_TAG_IDENT_PD)
|
||||
{
|
||||
if (data->npd >= GRUB_UDF_MAX_PDS)
|
||||
{
|
||||
grub_error (GRUB_ERR_BAD_FS, "too many PDs");
|
||||
goto fail;
|
||||
}
|
||||
|
||||
if (grub_disk_read (disk, block << GRUB_UDF_LOG2_BLKSZ, 0,
|
||||
sizeof (struct grub_udf_pd),
|
||||
&data->pds[data->npd]))
|
||||
{
|
||||
grub_error (GRUB_ERR_BAD_FS, "not an UDF filesystem");
|
||||
goto fail;
|
||||
}
|
||||
|
||||
data->npd++;
|
||||
}
|
||||
else if (tag.tag_ident == GRUB_UDF_TAG_IDENT_LVD)
|
||||
{
|
||||
int k;
|
||||
|
||||
struct grub_udf_partmap *ppm;
|
||||
|
||||
if (grub_disk_read (disk, block << GRUB_UDF_LOG2_BLKSZ, 0,
|
||||
sizeof (struct grub_udf_lvd),
|
||||
&data->lvd))
|
||||
{
|
||||
grub_error (GRUB_ERR_BAD_FS, "not an UDF filesystem");
|
||||
goto fail;
|
||||
}
|
||||
|
||||
if (data->npm + U32 (data->lvd.num_part_maps) > GRUB_UDF_MAX_PMS)
|
||||
{
|
||||
grub_error (GRUB_ERR_BAD_FS, "too many partition maps");
|
||||
goto fail;
|
||||
}
|
||||
|
||||
ppm = (struct grub_udf_partmap *) &data->lvd.part_maps;
|
||||
for (k = U32 (data->lvd.num_part_maps); k > 0; k--)
|
||||
{
|
||||
if (ppm->type != GRUB_UDF_PARTMAP_TYPE_1)
|
||||
{
|
||||
grub_error (GRUB_ERR_BAD_FS, "partmap type not supported");
|
||||
goto fail;
|
||||
}
|
||||
|
||||
data->pms[data->npm++] = ppm;
|
||||
ppm = (struct grub_udf_partmap *) ((char *) ppm +
|
||||
U32 (ppm->length));
|
||||
}
|
||||
}
|
||||
else if (tag.tag_ident > GRUB_UDF_TAG_IDENT_TD)
|
||||
{
|
||||
grub_error (GRUB_ERR_BAD_FS, "invalid tag ident");
|
||||
goto fail;
|
||||
}
|
||||
else if (tag.tag_ident == GRUB_UDF_TAG_IDENT_TD)
|
||||
break;
|
||||
|
||||
block++;
|
||||
}
|
||||
|
||||
for (i = 0; i < data->npm; i++)
|
||||
{
|
||||
int j;
|
||||
|
||||
for (j = 0; j < data->npd; j++)
|
||||
if (data->pms[i]->type1.part_num == data->pds[j].part_num)
|
||||
{
|
||||
data->pms[i]->type1.part_num = j;
|
||||
break;
|
||||
}
|
||||
|
||||
if (j == data->npd)
|
||||
{
|
||||
grub_error (GRUB_ERR_BAD_FS, "can\'t find PD");
|
||||
goto fail;
|
||||
}
|
||||
}
|
||||
|
||||
block = grub_udf_get_block (data,
|
||||
data->lvd.root_fileset.block.part_ref,
|
||||
data->lvd.root_fileset.block.block_num);
|
||||
|
||||
if (grub_errno)
|
||||
goto fail;
|
||||
|
||||
if (grub_disk_read (disk, block << GRUB_UDF_LOG2_BLKSZ, 0,
|
||||
sizeof (struct grub_udf_fileset), &root_fs))
|
||||
{
|
||||
grub_error (GRUB_ERR_BAD_FS, "not an UDF filesystem");
|
||||
goto fail;
|
||||
}
|
||||
|
||||
if (U16 (root_fs.tag.tag_ident) != GRUB_UDF_TAG_IDENT_FSD)
|
||||
{
|
||||
grub_error (GRUB_ERR_BAD_FS, "invalid fileset descriptor");
|
||||
goto fail;
|
||||
}
|
||||
|
||||
data->root_icb = root_fs.root_icb;
|
||||
|
||||
return data;
|
||||
|
||||
fail:
|
||||
grub_free (data);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int
|
||||
grub_udf_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_fshelp_node_t child;
|
||||
struct grub_udf_file_ident dirent;
|
||||
grub_uint32_t offset = 0;
|
||||
|
||||
child = grub_malloc (sizeof (struct grub_fshelp_node));
|
||||
if (!child)
|
||||
return 0;
|
||||
|
||||
/* The current directory is not stored. */
|
||||
grub_memcpy ((char *) child, (char *) dir,
|
||||
sizeof (struct grub_fshelp_node));
|
||||
|
||||
if (hook (".", GRUB_FSHELP_DIR, child))
|
||||
return 1;
|
||||
|
||||
while (offset < U64 (dir->fe.file_size))
|
||||
{
|
||||
if (grub_udf_read_file (dir, 0, offset, sizeof (dirent),
|
||||
(char *) &dirent) != sizeof (dirent))
|
||||
return 0;
|
||||
|
||||
if (U16 (dirent.tag.tag_ident) != GRUB_UDF_TAG_IDENT_FID)
|
||||
{
|
||||
grub_error (GRUB_ERR_BAD_FS, "invalid fid tag");
|
||||
return 0;
|
||||
}
|
||||
|
||||
child = grub_malloc (sizeof (struct grub_fshelp_node));
|
||||
if (!child)
|
||||
return 0;
|
||||
|
||||
if (grub_udf_read_icb (dir->data, &dirent.icb, child))
|
||||
return 0;
|
||||
|
||||
offset += sizeof (dirent) + U16 (dirent.imp_use_length);
|
||||
if (dirent.characteristics & GRUB_UDF_FID_CHAR_PARENT)
|
||||
{
|
||||
/* This is the parent directory. */
|
||||
if (hook ("..", GRUB_FSHELP_DIR, child))
|
||||
return 1;
|
||||
}
|
||||
else
|
||||
{
|
||||
enum grub_fshelp_filetype type;
|
||||
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 ((grub_udf_read_file (dir, 0, offset,
|
||||
dirent.file_ident_length,
|
||||
(char *) raw))
|
||||
!= 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';
|
||||
|
||||
if (hook ((char *) filename, type, child))
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
|
||||
/* Align to dword boundary. */
|
||||
offset = (offset + dirent.file_ident_length + 3) & (~3);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static grub_err_t
|
||||
grub_udf_dir (grub_device_t device, const char *path,
|
||||
int (*hook) (const char *filename,
|
||||
const struct grub_dirhook_info *info))
|
||||
{
|
||||
struct grub_udf_data *data = 0;
|
||||
struct grub_fshelp_node rootnode;
|
||||
struct grub_fshelp_node *foundnode;
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
grub_dl_ref (my_mod);
|
||||
|
||||
data = grub_udf_mount (device->disk);
|
||||
if (!data)
|
||||
goto fail;
|
||||
|
||||
if (grub_udf_read_icb (data, &data->root_icb, &rootnode))
|
||||
goto fail;
|
||||
|
||||
if (grub_fshelp_find_file (path, &rootnode,
|
||||
&foundnode,
|
||||
grub_udf_iterate_dir, 0, GRUB_FSHELP_DIR))
|
||||
goto fail;
|
||||
|
||||
grub_udf_iterate_dir (foundnode, iterate);
|
||||
|
||||
if (foundnode != &rootnode)
|
||||
grub_free (foundnode);
|
||||
|
||||
fail:
|
||||
grub_free (data);
|
||||
|
||||
grub_dl_unref (my_mod);
|
||||
|
||||
return grub_errno;
|
||||
}
|
||||
|
||||
static grub_err_t
|
||||
grub_udf_open (struct grub_file *file, const char *name)
|
||||
{
|
||||
struct grub_udf_data *data;
|
||||
struct grub_fshelp_node rootnode;
|
||||
struct grub_fshelp_node *foundnode;
|
||||
|
||||
grub_dl_ref (my_mod);
|
||||
|
||||
data = grub_udf_mount (file->device->disk);
|
||||
if (!data)
|
||||
goto fail;
|
||||
|
||||
if (grub_udf_read_icb (data, &data->root_icb, &rootnode))
|
||||
goto fail;
|
||||
|
||||
if (grub_fshelp_find_file (name, &rootnode,
|
||||
&foundnode,
|
||||
grub_udf_iterate_dir, 0, GRUB_FSHELP_REG))
|
||||
goto fail;
|
||||
|
||||
file->data = foundnode;
|
||||
file->offset = 0;
|
||||
file->size = U64 (foundnode->fe.file_size);
|
||||
|
||||
return 0;
|
||||
|
||||
fail:
|
||||
grub_dl_unref (my_mod);
|
||||
|
||||
grub_free (data);
|
||||
|
||||
return grub_errno;
|
||||
}
|
||||
|
||||
static grub_ssize_t
|
||||
grub_udf_read (grub_file_t file, char *buf, grub_size_t len)
|
||||
{
|
||||
struct grub_fshelp_node *node = (struct grub_fshelp_node *) file->data;
|
||||
|
||||
return grub_udf_read_file (node, file->read_hook, file->offset, len, buf);
|
||||
}
|
||||
|
||||
static grub_err_t
|
||||
grub_udf_close (grub_file_t file)
|
||||
{
|
||||
if (file->data)
|
||||
{
|
||||
struct grub_fshelp_node *node = (struct grub_fshelp_node *) file->data;
|
||||
|
||||
grub_free (node->data);
|
||||
grub_free (node);
|
||||
}
|
||||
|
||||
grub_dl_unref (my_mod);
|
||||
|
||||
return GRUB_ERR_NONE;
|
||||
}
|
||||
|
||||
static grub_err_t
|
||||
grub_udf_label (grub_device_t device, char **label)
|
||||
{
|
||||
struct grub_udf_data *data;
|
||||
data = grub_udf_mount (device->disk);
|
||||
|
||||
if (data)
|
||||
{
|
||||
*label = grub_strdup ((char *) &data->lvd.ident[1]);
|
||||
grub_free (data);
|
||||
}
|
||||
else
|
||||
*label = 0;
|
||||
|
||||
return grub_errno;
|
||||
}
|
||||
|
||||
static struct grub_fs grub_udf_fs = {
|
||||
.name = "udf",
|
||||
.dir = grub_udf_dir,
|
||||
.open = grub_udf_open,
|
||||
.read = grub_udf_read,
|
||||
.close = grub_udf_close,
|
||||
.label = grub_udf_label,
|
||||
.next = 0
|
||||
};
|
||||
|
||||
GRUB_MOD_INIT (udf)
|
||||
{
|
||||
grub_fs_register (&grub_udf_fs);
|
||||
my_mod = mod;
|
||||
}
|
||||
|
||||
GRUB_MOD_FINI (udf)
|
||||
{
|
||||
grub_fs_unregister (&grub_udf_fs);
|
||||
}
|
813
grub-core/fs/ufs.c
Normal file
813
grub-core/fs/ufs.c
Normal file
|
@ -0,0 +1,813 @@
|
|||
/* ufs.c - Unix File System */
|
||||
/*
|
||||
* GRUB -- GRand Unified Bootloader
|
||||
* Copyright (C) 2004,2005,2007,2008,2009 Free Software Foundation, Inc.
|
||||
*
|
||||
* GRUB is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* GRUB is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with GRUB. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#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>
|
||||
|
||||
#ifdef MODE_UFS2
|
||||
#define GRUB_UFS_MAGIC 0x19540119
|
||||
#else
|
||||
#define GRUB_UFS_MAGIC 0x11954
|
||||
#endif
|
||||
|
||||
#define GRUB_UFS_INODE 2
|
||||
#define GRUB_UFS_FILETYPE_DIR 4
|
||||
#define GRUB_UFS_FILETYPE_LNK 10
|
||||
#define GRUB_UFS_MAX_SYMLNK_CNT 8
|
||||
|
||||
#define GRUB_UFS_DIRBLKS 12
|
||||
#define GRUB_UFS_INDIRBLKS 3
|
||||
|
||||
#define GRUB_UFS_ATTR_TYPE 0160000
|
||||
#define GRUB_UFS_ATTR_FILE 0100000
|
||||
#define GRUB_UFS_ATTR_DIR 0040000
|
||||
#define GRUB_UFS_ATTR_LNK 0120000
|
||||
|
||||
#define GRUB_UFS_VOLNAME_LEN 32
|
||||
|
||||
/* Calculate in which group the inode can be found. */
|
||||
#define UFS_BLKSZ(sblock) (grub_le_to_cpu32 (sblock->bsize))
|
||||
|
||||
#define INODE(data,field) data->inode. field
|
||||
#ifdef MODE_UFS2
|
||||
#define INODE_ENDIAN(data,field,bits1,bits2) grub_le_to_cpu##bits2 (data->inode.field)
|
||||
#else
|
||||
#define INODE_ENDIAN(data,field,bits1,bits2) grub_le_to_cpu##bits1 (data->inode.field)
|
||||
#endif
|
||||
|
||||
#define INODE_SIZE(data) INODE_ENDIAN (data,size,32,64)
|
||||
#define INODE_NBLOCKS(data) INODE_ENDIAN (data,nblocks,32,64)
|
||||
|
||||
#define INODE_MODE(data) INODE_ENDIAN (data,mode,16,16)
|
||||
#ifdef MODE_UFS2
|
||||
#define INODE_BLKSZ 8
|
||||
#else
|
||||
#define INODE_BLKSZ 4
|
||||
#endif
|
||||
#ifdef MODE_UFS2
|
||||
#define UFS_INODE_PER_BLOCK 2
|
||||
#else
|
||||
#define UFS_INODE_PER_BLOCK 4
|
||||
#endif
|
||||
#define INODE_DIRBLOCKS(data,blk) INODE_ENDIAN \
|
||||
(data,blocks.dir_blocks[blk],32,64)
|
||||
#define INODE_INDIRBLOCKS(data,blk) INODE_ENDIAN \
|
||||
(data,blocks.indir_blocks[blk],32,64)
|
||||
|
||||
/* The blocks on which the superblock can be found. */
|
||||
static int sblocklist[] = { 128, 16, 0, 512, -1 };
|
||||
|
||||
struct grub_ufs_sblock
|
||||
{
|
||||
grub_uint8_t unused[16];
|
||||
/* The offset of the inodes in the cylinder group. */
|
||||
grub_uint32_t inoblk_offs;
|
||||
|
||||
grub_uint8_t unused2[4];
|
||||
|
||||
/* The start of the cylinder group. */
|
||||
grub_uint32_t cylg_offset;
|
||||
grub_uint32_t cylg_mask;
|
||||
|
||||
grub_uint32_t mtime;
|
||||
grub_uint8_t unused4[12];
|
||||
|
||||
/* The size of a block in bytes. */
|
||||
grub_int32_t bsize;
|
||||
grub_uint8_t unused5[48];
|
||||
|
||||
/* The size of filesystem blocks to disk blocks. */
|
||||
grub_uint32_t log2_blksz;
|
||||
grub_uint8_t unused6[40];
|
||||
grub_uint32_t uuidhi;
|
||||
grub_uint32_t uuidlow;
|
||||
grub_uint8_t unused7[32];
|
||||
|
||||
/* Inodes stored per cylinder group. */
|
||||
grub_uint32_t ino_per_group;
|
||||
|
||||
/* The frags per cylinder group. */
|
||||
grub_uint32_t frags_per_group;
|
||||
|
||||
grub_uint8_t unused8[488];
|
||||
|
||||
/* Volume name for UFS2. */
|
||||
grub_uint8_t volume_name[GRUB_UFS_VOLNAME_LEN];
|
||||
grub_uint8_t unused9[360];
|
||||
|
||||
grub_uint64_t mtime2;
|
||||
grub_uint8_t unused10[292];
|
||||
|
||||
/* Magic value to check if this is really a UFS filesystem. */
|
||||
grub_uint32_t magic;
|
||||
};
|
||||
|
||||
#ifdef MODE_UFS2
|
||||
/* UFS inode. */
|
||||
struct grub_ufs_inode
|
||||
{
|
||||
grub_uint16_t mode;
|
||||
grub_uint16_t nlinks;
|
||||
grub_uint32_t uid;
|
||||
grub_uint32_t gid;
|
||||
grub_uint32_t blocksize;
|
||||
grub_int64_t size;
|
||||
grub_int64_t nblocks;
|
||||
grub_uint64_t atime;
|
||||
grub_uint64_t mtime;
|
||||
grub_uint64_t ctime;
|
||||
grub_uint64_t create_time;
|
||||
grub_uint32_t atime_sec;
|
||||
grub_uint32_t mtime_sec;
|
||||
grub_uint32_t ctime_sec;
|
||||
grub_uint32_t create_time_sec;
|
||||
grub_uint32_t gen;
|
||||
grub_uint32_t kernel_flags;
|
||||
grub_uint32_t flags;
|
||||
grub_uint32_t extsz;
|
||||
grub_uint64_t ext[2];
|
||||
union
|
||||
{
|
||||
struct
|
||||
{
|
||||
grub_uint64_t dir_blocks[GRUB_UFS_DIRBLKS];
|
||||
grub_uint64_t indir_blocks[GRUB_UFS_INDIRBLKS];
|
||||
} blocks;
|
||||
grub_uint8_t symlink[(GRUB_UFS_DIRBLKS + GRUB_UFS_INDIRBLKS) * 8];
|
||||
};
|
||||
|
||||
grub_uint8_t unused[24];
|
||||
} __attribute__ ((packed));
|
||||
#else
|
||||
/* UFS inode. */
|
||||
struct grub_ufs_inode
|
||||
{
|
||||
grub_uint16_t mode;
|
||||
grub_uint16_t nlinks;
|
||||
grub_uint16_t uid;
|
||||
grub_uint16_t gid;
|
||||
grub_int64_t size;
|
||||
grub_uint64_t atime;
|
||||
grub_uint64_t mtime;
|
||||
grub_uint64_t ctime;
|
||||
union
|
||||
{
|
||||
struct
|
||||
{
|
||||
grub_uint32_t dir_blocks[GRUB_UFS_DIRBLKS];
|
||||
grub_uint32_t indir_blocks[GRUB_UFS_INDIRBLKS];
|
||||
} blocks;
|
||||
grub_uint8_t symlink[(GRUB_UFS_DIRBLKS + GRUB_UFS_INDIRBLKS) * 4];
|
||||
};
|
||||
grub_uint32_t flags;
|
||||
grub_uint32_t nblocks;
|
||||
grub_uint32_t gen;
|
||||
grub_uint32_t unused;
|
||||
grub_uint8_t pad[12];
|
||||
} __attribute__ ((packed));
|
||||
#endif
|
||||
|
||||
/* Directory entry. */
|
||||
struct grub_ufs_dirent
|
||||
{
|
||||
grub_uint32_t ino;
|
||||
grub_uint16_t direntlen;
|
||||
union
|
||||
{
|
||||
grub_uint16_t namelen;
|
||||
struct
|
||||
{
|
||||
grub_uint8_t filetype_bsd;
|
||||
grub_uint8_t namelen_bsd;
|
||||
};
|
||||
};
|
||||
} __attribute__ ((packed));
|
||||
|
||||
/* Information about a "mounted" ufs filesystem. */
|
||||
struct grub_ufs_data
|
||||
{
|
||||
struct grub_ufs_sblock sblock;
|
||||
grub_disk_t disk;
|
||||
struct grub_ufs_inode inode;
|
||||
int ino;
|
||||
int linknest;
|
||||
};
|
||||
|
||||
static grub_dl_t my_mod;
|
||||
|
||||
/* Forward declaration. */
|
||||
static grub_err_t grub_ufs_find_file (struct grub_ufs_data *data,
|
||||
const char *path);
|
||||
|
||||
|
||||
static grub_disk_addr_t
|
||||
grub_ufs_get_file_block (struct grub_ufs_data *data, unsigned int blk)
|
||||
{
|
||||
struct grub_ufs_sblock *sblock = &data->sblock;
|
||||
unsigned int indirsz;
|
||||
int log2_blksz;
|
||||
|
||||
/* Direct. */
|
||||
if (blk < GRUB_UFS_DIRBLKS)
|
||||
return INODE_DIRBLOCKS (data, blk);
|
||||
|
||||
log2_blksz = grub_le_to_cpu32 (data->sblock.log2_blksz);
|
||||
|
||||
blk -= GRUB_UFS_DIRBLKS;
|
||||
|
||||
indirsz = UFS_BLKSZ (sblock) / INODE_BLKSZ;
|
||||
/* Single indirect block. */
|
||||
if (blk < indirsz)
|
||||
{
|
||||
#ifdef MODE_UFS2
|
||||
grub_uint64_t indir[UFS_BLKSZ (sblock) / sizeof (grub_uint64_t)];
|
||||
#else
|
||||
grub_uint32_t indir[UFS_BLKSZ (sblock) / sizeof (grub_uint32_t)];
|
||||
#endif
|
||||
grub_disk_read (data->disk, INODE_INDIRBLOCKS (data, 0) << log2_blksz,
|
||||
0, sizeof (indir), indir);
|
||||
return indir[blk];
|
||||
}
|
||||
blk -= indirsz;
|
||||
|
||||
/* Double indirect block. */
|
||||
if (blk < indirsz * indirsz)
|
||||
{
|
||||
#ifdef MODE_UFS2
|
||||
grub_uint64_t indir[UFS_BLKSZ (sblock) / sizeof (grub_uint64_t)];
|
||||
#else
|
||||
grub_uint32_t indir[UFS_BLKSZ (sblock) / sizeof (grub_uint32_t)];
|
||||
#endif
|
||||
|
||||
grub_disk_read (data->disk, INODE_INDIRBLOCKS (data, 1) << log2_blksz,
|
||||
0, sizeof (indir), indir);
|
||||
grub_disk_read (data->disk,
|
||||
(indir [blk / indirsz])
|
||||
<< log2_blksz,
|
||||
0, sizeof (indir), indir);
|
||||
|
||||
return indir[blk % indirsz];
|
||||
}
|
||||
|
||||
|
||||
grub_error (GRUB_ERR_NOT_IMPLEMENTED_YET,
|
||||
"ufs does not support triple indirect blocks");
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
/* Read LEN bytes from the file described by DATA starting with byte
|
||||
POS. Return the amount of read bytes in READ. */
|
||||
static grub_ssize_t
|
||||
grub_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)
|
||||
{
|
||||
struct grub_ufs_sblock *sblock = &data->sblock;
|
||||
int i;
|
||||
int 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);
|
||||
|
||||
for (i = pos / UFS_BLKSZ (sblock); i < blockcnt; i++)
|
||||
{
|
||||
int blknr;
|
||||
int blockoff = pos % UFS_BLKSZ (sblock);
|
||||
int blockend = UFS_BLKSZ (sblock);
|
||||
|
||||
int skipfirst = 0;
|
||||
|
||||
blknr = grub_ufs_get_file_block (data, i);
|
||||
if (grub_errno)
|
||||
return -1;
|
||||
|
||||
/* Last block. */
|
||||
if (i == blockcnt - 1)
|
||||
{
|
||||
blockend = (len + pos) % UFS_BLKSZ (sblock);
|
||||
|
||||
if (!blockend)
|
||||
blockend = UFS_BLKSZ (sblock);
|
||||
}
|
||||
|
||||
/* First block. */
|
||||
if (i == (pos / (int) UFS_BLKSZ (sblock)))
|
||||
{
|
||||
skipfirst = blockoff;
|
||||
blockend -= skipfirst;
|
||||
}
|
||||
|
||||
/* XXX: If the block number is 0 this block is not stored on
|
||||
disk but is zero filled instead. */
|
||||
if (blknr)
|
||||
{
|
||||
data->disk->read_hook = read_hook;
|
||||
grub_disk_read (data->disk,
|
||||
blknr << grub_le_to_cpu32 (data->sblock.log2_blksz),
|
||||
skipfirst, blockend, buf);
|
||||
data->disk->read_hook = 0;
|
||||
if (grub_errno)
|
||||
return -1;
|
||||
}
|
||||
else
|
||||
grub_memset (buf, UFS_BLKSZ (sblock) - skipfirst, 0);
|
||||
|
||||
buf += UFS_BLKSZ (sblock) - skipfirst;
|
||||
}
|
||||
|
||||
return len;
|
||||
}
|
||||
|
||||
/* Read inode INO from the mounted filesystem described by DATA. This
|
||||
inode is used by default now. */
|
||||
static grub_err_t
|
||||
grub_ufs_read_inode (struct grub_ufs_data *data, int ino, char *inode)
|
||||
{
|
||||
struct grub_ufs_sblock *sblock = &data->sblock;
|
||||
|
||||
/* Determine the group the inode is in. */
|
||||
int group = ino / grub_le_to_cpu32 (sblock->ino_per_group);
|
||||
|
||||
/* Determine the inode within the group. */
|
||||
int grpino = ino % grub_le_to_cpu32 (sblock->ino_per_group);
|
||||
|
||||
/* The first block of the group. */
|
||||
int grpblk = group * (grub_le_to_cpu32 (sblock->frags_per_group));
|
||||
|
||||
#ifndef MODE_UFS2
|
||||
grpblk += grub_le_to_cpu32 (sblock->cylg_offset)
|
||||
* (group & (~grub_le_to_cpu32 (sblock->cylg_mask)));
|
||||
#endif
|
||||
|
||||
if (!inode)
|
||||
{
|
||||
inode = (char *) &data->inode;
|
||||
data->ino = ino;
|
||||
}
|
||||
|
||||
grub_disk_read (data->disk,
|
||||
((grub_le_to_cpu32 (sblock->inoblk_offs) + grpblk)
|
||||
<< grub_le_to_cpu32 (data->sblock.log2_blksz))
|
||||
+ grpino / UFS_INODE_PER_BLOCK,
|
||||
(grpino % UFS_INODE_PER_BLOCK)
|
||||
* sizeof (struct grub_ufs_inode),
|
||||
sizeof (struct grub_ufs_inode),
|
||||
inode);
|
||||
|
||||
return grub_errno;
|
||||
}
|
||||
|
||||
|
||||
/* Lookup the symlink the current inode points to. INO is the inode
|
||||
number of the directory the symlink is relative to. */
|
||||
static grub_err_t
|
||||
grub_ufs_lookup_symlink (struct grub_ufs_data *data, int ino)
|
||||
{
|
||||
char symlink[INODE_SIZE (data)];
|
||||
|
||||
if (++data->linknest > GRUB_UFS_MAX_SYMLNK_CNT)
|
||||
return grub_error (GRUB_ERR_SYMLINK_LOOP, "too deep nesting of symlinks");
|
||||
|
||||
if (INODE_NBLOCKS (data) == 0)
|
||||
grub_strcpy (symlink, (char *) INODE (data, symlink));
|
||||
else
|
||||
{
|
||||
grub_disk_read (data->disk,
|
||||
(INODE_DIRBLOCKS (data, 0)
|
||||
<< grub_le_to_cpu32 (data->sblock.log2_blksz)),
|
||||
0, INODE_SIZE (data), symlink);
|
||||
symlink[INODE_SIZE (data)] = '\0';
|
||||
}
|
||||
|
||||
/* The symlink is an absolute path, go back to the root inode. */
|
||||
if (symlink[0] == '/')
|
||||
ino = GRUB_UFS_INODE;
|
||||
|
||||
/* Now load in the old inode. */
|
||||
if (grub_ufs_read_inode (data, ino, 0))
|
||||
return grub_errno;
|
||||
|
||||
grub_ufs_find_file (data, symlink);
|
||||
if (grub_errno)
|
||||
grub_error (grub_errno, "cannot follow symlink `%s'", symlink);
|
||||
|
||||
return grub_errno;
|
||||
}
|
||||
|
||||
|
||||
/* Find the file with the pathname PATH on the filesystem described by
|
||||
DATA. */
|
||||
static grub_err_t
|
||||
grub_ufs_find_file (struct grub_ufs_data *data, const char *path)
|
||||
{
|
||||
char fpath[grub_strlen (path) + 1];
|
||||
char *name = fpath;
|
||||
char *next;
|
||||
unsigned int pos = 0;
|
||||
int dirino;
|
||||
|
||||
grub_strcpy (fpath, path);
|
||||
|
||||
/* Skip the first slash. */
|
||||
if (name[0] == '/')
|
||||
{
|
||||
name++;
|
||||
if (!*name)
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Extract the actual part from the pathname. */
|
||||
next = grub_strchr (name, '/');
|
||||
if (next)
|
||||
{
|
||||
next[0] = '\0';
|
||||
next++;
|
||||
}
|
||||
|
||||
do
|
||||
{
|
||||
struct grub_ufs_dirent dirent;
|
||||
int namelen;
|
||||
|
||||
if (grub_strlen (name) == 0)
|
||||
return GRUB_ERR_NONE;
|
||||
|
||||
if (grub_ufs_read_file (data, 0, pos, sizeof (dirent),
|
||||
(char *) &dirent) < 0)
|
||||
return grub_errno;
|
||||
|
||||
#ifdef MODE_UFS2
|
||||
namelen = dirent.namelen_bsd;
|
||||
#else
|
||||
namelen = grub_le_to_cpu16 (dirent.namelen);
|
||||
#endif
|
||||
{
|
||||
char filename[namelen + 1];
|
||||
|
||||
if (grub_ufs_read_file (data, 0, pos + sizeof (dirent),
|
||||
namelen, filename) < 0)
|
||||
return grub_errno;
|
||||
|
||||
filename[namelen] = '\0';
|
||||
|
||||
if (!grub_strcmp (name, filename))
|
||||
{
|
||||
dirino = data->ino;
|
||||
grub_ufs_read_inode (data, grub_le_to_cpu32 (dirent.ino), 0);
|
||||
|
||||
if ((INODE_MODE(data) & GRUB_UFS_ATTR_TYPE)
|
||||
== GRUB_UFS_ATTR_LNK)
|
||||
{
|
||||
grub_ufs_lookup_symlink (data, dirino);
|
||||
if (grub_errno)
|
||||
return grub_errno;
|
||||
}
|
||||
|
||||
if (!next)
|
||||
return 0;
|
||||
|
||||
pos = 0;
|
||||
|
||||
name = next;
|
||||
next = grub_strchr (name, '/');
|
||||
if (next)
|
||||
{
|
||||
next[0] = '\0';
|
||||
next++;
|
||||
}
|
||||
|
||||
if ((INODE_MODE(data) & GRUB_UFS_ATTR_TYPE) != GRUB_UFS_ATTR_DIR)
|
||||
return grub_error (GRUB_ERR_BAD_FILE_TYPE, "not a directory");
|
||||
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
pos += grub_le_to_cpu16 (dirent.direntlen);
|
||||
} while (pos < INODE_SIZE (data));
|
||||
|
||||
grub_error (GRUB_ERR_FILE_NOT_FOUND, "file not found");
|
||||
return grub_errno;
|
||||
}
|
||||
|
||||
|
||||
/* Mount the filesystem on the disk DISK. */
|
||||
static struct grub_ufs_data *
|
||||
grub_ufs_mount (grub_disk_t disk)
|
||||
{
|
||||
struct grub_ufs_data *data;
|
||||
int *sblklist = sblocklist;
|
||||
|
||||
data = grub_malloc (sizeof (struct grub_ufs_data));
|
||||
if (!data)
|
||||
return 0;
|
||||
|
||||
/* Find a UFS sblock. */
|
||||
while (*sblklist != -1)
|
||||
{
|
||||
grub_disk_read (disk, *sblklist, 0, sizeof (struct grub_ufs_sblock),
|
||||
&data->sblock);
|
||||
if (grub_errno)
|
||||
goto fail;
|
||||
|
||||
if (grub_le_to_cpu32 (data->sblock.magic) == GRUB_UFS_MAGIC)
|
||||
{
|
||||
data->disk = disk;
|
||||
data->linknest = 0;
|
||||
return data;
|
||||
}
|
||||
sblklist++;
|
||||
}
|
||||
|
||||
fail:
|
||||
|
||||
if (grub_errno == GRUB_ERR_NONE || grub_errno == GRUB_ERR_OUT_OF_RANGE)
|
||||
{
|
||||
#ifdef MODE_UFS2
|
||||
grub_error (GRUB_ERR_BAD_FS, "not an ufs2 filesystem");
|
||||
#else
|
||||
grub_error (GRUB_ERR_BAD_FS, "not an ufs1 filesystem");
|
||||
#endif
|
||||
}
|
||||
|
||||
grub_free (data);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
static grub_err_t
|
||||
grub_ufs_dir (grub_device_t device, const char *path,
|
||||
int (*hook) (const char *filename,
|
||||
const struct grub_dirhook_info *info))
|
||||
{
|
||||
struct grub_ufs_data *data;
|
||||
struct grub_ufs_sblock *sblock;
|
||||
unsigned int pos = 0;
|
||||
|
||||
data = grub_ufs_mount (device->disk);
|
||||
if (!data)
|
||||
return grub_errno;
|
||||
|
||||
grub_ufs_read_inode (data, GRUB_UFS_INODE, 0);
|
||||
if (grub_errno)
|
||||
return grub_errno;
|
||||
|
||||
sblock = &data->sblock;
|
||||
|
||||
if (!path || path[0] != '/')
|
||||
{
|
||||
grub_error (GRUB_ERR_BAD_FILENAME, "bad filename");
|
||||
return grub_errno;
|
||||
}
|
||||
|
||||
grub_ufs_find_file (data, path);
|
||||
if (grub_errno)
|
||||
goto fail;
|
||||
|
||||
if ((INODE_MODE (data) & GRUB_UFS_ATTR_TYPE) != GRUB_UFS_ATTR_DIR)
|
||||
{
|
||||
grub_error (GRUB_ERR_BAD_FILE_TYPE, "not a directory");
|
||||
goto fail;
|
||||
}
|
||||
|
||||
while (pos < INODE_SIZE (data))
|
||||
{
|
||||
struct grub_ufs_dirent dirent;
|
||||
int namelen;
|
||||
|
||||
if (grub_ufs_read_file (data, 0, pos, sizeof (dirent),
|
||||
(char *) &dirent) < 0)
|
||||
break;
|
||||
|
||||
#ifdef MODE_UFS2
|
||||
namelen = dirent.namelen_bsd;
|
||||
#else
|
||||
namelen = grub_le_to_cpu16 (dirent.namelen);
|
||||
#endif
|
||||
|
||||
{
|
||||
char filename[namelen + 1];
|
||||
struct grub_dirhook_info info;
|
||||
struct grub_ufs_inode inode;
|
||||
|
||||
grub_memset (&info, 0, sizeof (info));
|
||||
|
||||
if (grub_ufs_read_file (data, 0, pos + sizeof (dirent),
|
||||
namelen, filename) < 0)
|
||||
break;
|
||||
|
||||
filename[namelen] = '\0';
|
||||
grub_ufs_read_inode (data, dirent.ino, (char *) &inode);
|
||||
|
||||
info.dir = ((grub_le_to_cpu16 (inode.mode) & GRUB_UFS_ATTR_TYPE)
|
||||
== GRUB_UFS_ATTR_DIR);
|
||||
info.mtime = grub_le_to_cpu64 (inode.mtime);
|
||||
info.mtimeset = 1;
|
||||
|
||||
if (hook (filename, &info))
|
||||
break;
|
||||
}
|
||||
|
||||
pos += grub_le_to_cpu16 (dirent.direntlen);
|
||||
}
|
||||
|
||||
fail:
|
||||
grub_free (data);
|
||||
|
||||
return grub_errno;
|
||||
}
|
||||
|
||||
|
||||
/* Open a file named NAME and initialize FILE. */
|
||||
static grub_err_t
|
||||
grub_ufs_open (struct grub_file *file, const char *name)
|
||||
{
|
||||
struct grub_ufs_data *data;
|
||||
data = grub_ufs_mount (file->device->disk);
|
||||
if (!data)
|
||||
return grub_errno;
|
||||
|
||||
grub_ufs_read_inode (data, 2, 0);
|
||||
if (grub_errno)
|
||||
{
|
||||
grub_free (data);
|
||||
return grub_errno;
|
||||
}
|
||||
|
||||
if (!name || name[0] != '/')
|
||||
{
|
||||
grub_error (GRUB_ERR_BAD_FILENAME, "bad filename");
|
||||
return grub_errno;
|
||||
}
|
||||
|
||||
grub_ufs_find_file (data, name);
|
||||
if (grub_errno)
|
||||
{
|
||||
grub_free (data);
|
||||
return grub_errno;
|
||||
}
|
||||
|
||||
file->data = data;
|
||||
file->size = INODE_SIZE (data);
|
||||
|
||||
return GRUB_ERR_NONE;
|
||||
}
|
||||
|
||||
|
||||
static grub_ssize_t
|
||||
grub_ufs_read (grub_file_t file, char *buf, grub_size_t len)
|
||||
{
|
||||
struct grub_ufs_data *data =
|
||||
(struct grub_ufs_data *) file->data;
|
||||
|
||||
return grub_ufs_read_file (data, file->read_hook, file->offset, len, buf);
|
||||
}
|
||||
|
||||
|
||||
static grub_err_t
|
||||
grub_ufs_close (grub_file_t file)
|
||||
{
|
||||
grub_free (file->data);
|
||||
|
||||
return GRUB_ERR_NONE;
|
||||
}
|
||||
|
||||
|
||||
#ifdef MODE_UFS2
|
||||
static grub_err_t
|
||||
grub_ufs_label (grub_device_t device, char **label)
|
||||
{
|
||||
struct grub_ufs_data *data = 0;
|
||||
|
||||
grub_dl_ref (my_mod);
|
||||
|
||||
*label = 0;
|
||||
|
||||
data = grub_ufs_mount (device->disk);
|
||||
if (data)
|
||||
*label = grub_strdup ((char *) data->sblock.volume_name);
|
||||
|
||||
grub_dl_unref (my_mod);
|
||||
|
||||
grub_free (data);
|
||||
|
||||
return grub_errno;
|
||||
}
|
||||
#endif
|
||||
|
||||
static grub_err_t
|
||||
grub_ufs_uuid (grub_device_t device, char **uuid)
|
||||
{
|
||||
struct grub_ufs_data *data;
|
||||
grub_disk_t disk = device->disk;
|
||||
|
||||
grub_dl_ref (my_mod);
|
||||
|
||||
data = grub_ufs_mount (disk);
|
||||
if (data && (data->sblock.uuidhi != 0 || data->sblock.uuidlow != 0))
|
||||
*uuid = grub_xasprintf ("%08x%08x",
|
||||
(unsigned) grub_le_to_cpu32 (data->sblock.uuidhi),
|
||||
(unsigned) grub_le_to_cpu32 (data->sblock.uuidlow));
|
||||
else
|
||||
*uuid = NULL;
|
||||
|
||||
grub_dl_unref (my_mod);
|
||||
|
||||
grub_free (data);
|
||||
|
||||
return grub_errno;
|
||||
}
|
||||
|
||||
|
||||
/* Get mtime. */
|
||||
static grub_err_t
|
||||
grub_ufs_mtime (grub_device_t device, grub_int32_t *tm)
|
||||
{
|
||||
struct grub_ufs_data *data = 0;
|
||||
|
||||
grub_dl_ref (my_mod);
|
||||
|
||||
data = grub_ufs_mount (device->disk);
|
||||
if (!data)
|
||||
*tm = 0;
|
||||
else
|
||||
#ifdef MODE_UFS2
|
||||
*tm = grub_le_to_cpu64 (data->sblock.mtime2);
|
||||
#else
|
||||
*tm = grub_le_to_cpu32 (data->sblock.mtime);
|
||||
#endif
|
||||
|
||||
grub_dl_unref (my_mod);
|
||||
|
||||
grub_free (data);
|
||||
|
||||
return grub_errno;
|
||||
}
|
||||
|
||||
|
||||
|
||||
static struct grub_fs grub_ufs_fs =
|
||||
{
|
||||
#ifdef MODE_UFS2
|
||||
.name = "ufs2",
|
||||
#else
|
||||
.name = "ufs1",
|
||||
#endif
|
||||
.dir = grub_ufs_dir,
|
||||
.open = grub_ufs_open,
|
||||
.read = grub_ufs_read,
|
||||
.close = grub_ufs_close,
|
||||
#ifdef MODE_UFS2
|
||||
.label = grub_ufs_label,
|
||||
#endif
|
||||
.uuid = grub_ufs_uuid,
|
||||
.mtime = grub_ufs_mtime,
|
||||
.next = 0
|
||||
};
|
||||
|
||||
#ifdef MODE_UFS2
|
||||
GRUB_MOD_INIT(ufs2)
|
||||
#else
|
||||
GRUB_MOD_INIT(ufs1)
|
||||
#endif
|
||||
{
|
||||
grub_fs_register (&grub_ufs_fs);
|
||||
my_mod = mod;
|
||||
}
|
||||
|
||||
#ifdef MODE_UFS2
|
||||
GRUB_MOD_FINI(ufs2)
|
||||
#else
|
||||
GRUB_MOD_FINI(ufs1)
|
||||
#endif
|
||||
{
|
||||
grub_fs_unregister (&grub_ufs_fs);
|
||||
}
|
||||
|
3
grub-core/fs/ufs2.c
Normal file
3
grub-core/fs/ufs2.c
Normal file
|
@ -0,0 +1,3 @@
|
|||
/* ufs2.c - Unix File System 2 */
|
||||
#define MODE_UFS2 1
|
||||
#include "ufs.c"
|
823
grub-core/fs/xfs.c
Normal file
823
grub-core/fs/xfs.c
Normal file
|
@ -0,0 +1,823 @@
|
|||
/* xfs.c - XFS. */
|
||||
/*
|
||||
* GRUB -- GRand Unified Bootloader
|
||||
* Copyright (C) 2005,2006,2007,2008,2009 Free Software Foundation, Inc.
|
||||
*
|
||||
* GRUB is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* GRUB is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with GRUB. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include <grub/err.h>
|
||||
#include <grub/file.h>
|
||||
#include <grub/mm.h>
|
||||
#include <grub/misc.h>
|
||||
#include <grub/disk.h>
|
||||
#include <grub/dl.h>
|
||||
#include <grub/types.h>
|
||||
#include <grub/fshelp.h>
|
||||
|
||||
#define XFS_INODE_EXTENTS 9
|
||||
|
||||
#define XFS_INODE_FORMAT_INO 1
|
||||
#define XFS_INODE_FORMAT_EXT 2
|
||||
#define XFS_INODE_FORMAT_BTREE 3
|
||||
|
||||
|
||||
struct grub_xfs_sblock
|
||||
{
|
||||
grub_uint8_t magic[4];
|
||||
grub_uint32_t bsize;
|
||||
grub_uint8_t unused1[24];
|
||||
grub_uint16_t uuid[8];
|
||||
grub_uint8_t unused2[8];
|
||||
grub_uint64_t rootino;
|
||||
grub_uint8_t unused3[20];
|
||||
grub_uint32_t agsize;
|
||||
grub_uint8_t unused4[20];
|
||||
grub_uint8_t label[12];
|
||||
grub_uint8_t log2_bsize;
|
||||
grub_uint8_t log2_sect;
|
||||
grub_uint8_t log2_inode;
|
||||
grub_uint8_t log2_inop;
|
||||
grub_uint8_t log2_agblk;
|
||||
grub_uint8_t unused6[67];
|
||||
grub_uint8_t log2_dirblk;
|
||||
} __attribute__ ((packed));
|
||||
|
||||
struct grub_xfs_dir_header
|
||||
{
|
||||
grub_uint8_t count;
|
||||
grub_uint8_t smallino;
|
||||
union
|
||||
{
|
||||
grub_uint32_t i4;
|
||||
grub_uint64_t i8;
|
||||
} parent __attribute__ ((packed));
|
||||
} __attribute__ ((packed));
|
||||
|
||||
struct grub_xfs_dir_entry
|
||||
{
|
||||
grub_uint8_t len;
|
||||
grub_uint16_t offset;
|
||||
char name[1];
|
||||
/* Inode number follows, 32 bits. */
|
||||
} __attribute__ ((packed));
|
||||
|
||||
struct grub_xfs_dir2_entry
|
||||
{
|
||||
grub_uint64_t inode;
|
||||
grub_uint8_t len;
|
||||
} __attribute__ ((packed));
|
||||
|
||||
typedef grub_uint32_t grub_xfs_extent[4];
|
||||
|
||||
struct grub_xfs_btree_node
|
||||
{
|
||||
grub_uint8_t magic[4];
|
||||
grub_uint16_t level;
|
||||
grub_uint16_t numrecs;
|
||||
grub_uint64_t left;
|
||||
grub_uint64_t right;
|
||||
grub_uint64_t keys[1];
|
||||
} __attribute__ ((packed));
|
||||
|
||||
struct grub_xfs_btree_root
|
||||
{
|
||||
grub_uint16_t level;
|
||||
grub_uint16_t numrecs;
|
||||
grub_uint64_t keys[1];
|
||||
} __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_uint64_t size;
|
||||
grub_uint64_t nblocks;
|
||||
grub_uint32_t extsize;
|
||||
grub_uint32_t nextents;
|
||||
grub_uint8_t unused3[20];
|
||||
union
|
||||
{
|
||||
char raw[156];
|
||||
struct dir
|
||||
{
|
||||
struct grub_xfs_dir_header dirhead;
|
||||
struct grub_xfs_dir_entry direntry[1];
|
||||
} dir;
|
||||
grub_xfs_extent extents[XFS_INODE_EXTENTS];
|
||||
struct grub_xfs_btree_root btree;
|
||||
} data __attribute__ ((packed));
|
||||
} __attribute__ ((packed));
|
||||
|
||||
struct grub_xfs_dirblock_tail
|
||||
{
|
||||
grub_uint32_t leaf_count;
|
||||
grub_uint32_t leaf_stale;
|
||||
} __attribute__ ((packed));
|
||||
|
||||
struct grub_fshelp_node
|
||||
{
|
||||
struct grub_xfs_data *data;
|
||||
grub_uint64_t ino;
|
||||
int inode_read;
|
||||
struct grub_xfs_inode inode;
|
||||
};
|
||||
|
||||
struct grub_xfs_data
|
||||
{
|
||||
struct grub_xfs_sblock sblock;
|
||||
grub_disk_t disk;
|
||||
int pos;
|
||||
int bsize;
|
||||
int agsize;
|
||||
struct grub_fshelp_node diropen;
|
||||
};
|
||||
|
||||
static grub_dl_t my_mod;
|
||||
|
||||
|
||||
|
||||
/* Filetype information as used in inodes. */
|
||||
#define FILETYPE_INO_MASK 0170000
|
||||
#define FILETYPE_INO_REG 0100000
|
||||
#define FILETYPE_INO_DIRECTORY 0040000
|
||||
#define FILETYPE_INO_SYMLINK 0120000
|
||||
|
||||
#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))
|
||||
|
||||
#define GRUB_XFS_FSB_TO_BLOCK(data, fsb) \
|
||||
(((fsb) >> (data)->sblock.log2_agblk) * (data)->agsize \
|
||||
+ ((fsb) & ((1LL << (data)->sblock.log2_agblk) - 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)
|
||||
|
||||
#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)
|
||||
|
||||
#define GRUB_XFS_EXTENT_SIZE(exts,ex) \
|
||||
(grub_be_to_cpu32 (exts[ex][3]) & ((1 << 20) - 1))
|
||||
|
||||
#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,
|
||||
grub_uint64_t ino)
|
||||
{
|
||||
long long int inoinag = GRUB_XFS_INO_INOINAG (data, ino);
|
||||
long long ag = GRUB_XFS_INO_AG (data, ino);
|
||||
long long block;
|
||||
|
||||
block = (inoinag >> data->sblock.log2_inop) + ag * data->agsize;
|
||||
block <<= (data->sblock.log2_bsize - GRUB_DISK_SECTOR_BITS);
|
||||
return block;
|
||||
}
|
||||
|
||||
|
||||
static inline int
|
||||
grub_xfs_inode_offset (struct grub_xfs_data *data,
|
||||
grub_uint64_t ino)
|
||||
{
|
||||
int inoag = GRUB_XFS_INO_INOINAG (data, ino);
|
||||
return ((inoag & ((1 << data->sblock.log2_inop) - 1)) <<
|
||||
data->sblock.log2_inode);
|
||||
}
|
||||
|
||||
|
||||
static grub_err_t
|
||||
grub_xfs_read_inode (struct grub_xfs_data *data, grub_uint64_t ino,
|
||||
struct grub_xfs_inode *inode)
|
||||
{
|
||||
grub_uint64_t block = grub_xfs_inode_block (data, ino);
|
||||
int offset = grub_xfs_inode_offset (data, ino);
|
||||
|
||||
/* Read the inode. */
|
||||
if (grub_disk_read (data->disk, block, offset,
|
||||
1 << data->sblock.log2_inode, inode))
|
||||
return grub_errno;
|
||||
|
||||
if (grub_strncmp ((char *) inode->magic, "IN", 2))
|
||||
return grub_error (GRUB_ERR_BAD_FS, "not a correct XFS inode");
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
static grub_disk_addr_t
|
||||
grub_xfs_read_block (grub_fshelp_node_t node, grub_disk_addr_t fileblock)
|
||||
{
|
||||
struct grub_xfs_btree_node *leaf = 0;
|
||||
int ex, nrec;
|
||||
grub_xfs_extent *exts;
|
||||
grub_uint64_t ret = 0;
|
||||
|
||||
if (node->inode.format == XFS_INODE_FORMAT_BTREE)
|
||||
{
|
||||
grub_uint64_t *keys;
|
||||
|
||||
leaf = grub_malloc (node->data->sblock.bsize);
|
||||
if (leaf == 0)
|
||||
return 0;
|
||||
|
||||
nrec = grub_be_to_cpu16 (node->inode.data.btree.numrecs);
|
||||
keys = &node->inode.data.btree.keys[0];
|
||||
do
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i = 0; i < nrec; i++)
|
||||
{
|
||||
if (fileblock < grub_be_to_cpu64 (keys[i]))
|
||||
break;
|
||||
}
|
||||
|
||||
/* Sparse block. */
|
||||
if (i == 0)
|
||||
{
|
||||
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))
|
||||
return 0;
|
||||
|
||||
if (grub_strncmp ((char *) leaf->magic, "BMAP", 4))
|
||||
{
|
||||
grub_free (leaf);
|
||||
grub_error (GRUB_ERR_BAD_FS, "not a correct XFS BMAP node");
|
||||
return 0;
|
||||
}
|
||||
|
||||
nrec = grub_be_to_cpu16 (leaf->numrecs);
|
||||
keys = &leaf->keys[0];
|
||||
} while (leaf->level);
|
||||
exts = (grub_xfs_extent *) keys;
|
||||
}
|
||||
else if (node->inode.format == XFS_INODE_FORMAT_EXT)
|
||||
{
|
||||
nrec = grub_be_to_cpu32 (node->inode.nextents);
|
||||
exts = &node->inode.data.extents[0];
|
||||
}
|
||||
else
|
||||
{
|
||||
grub_error (GRUB_ERR_NOT_IMPLEMENTED_YET,
|
||||
"XFS does not support inode format %d yet",
|
||||
node->inode.format);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Iterate over each extent to figure out which extent has
|
||||
the block we are looking for. */
|
||||
for (ex = 0; ex < nrec; ex++)
|
||||
{
|
||||
grub_uint64_t start = GRUB_XFS_EXTENT_BLOCK (exts, ex);
|
||||
grub_uint64_t offset = GRUB_XFS_EXTENT_OFFSET (exts, ex);
|
||||
grub_uint64_t size = GRUB_XFS_EXTENT_SIZE (exts, ex);
|
||||
|
||||
/* Sparse block. */
|
||||
if (fileblock < offset)
|
||||
break;
|
||||
else if (fileblock < offset + size)
|
||||
{
|
||||
ret = (fileblock - offset + start);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (leaf)
|
||||
grub_free (leaf);
|
||||
|
||||
return GRUB_XFS_FSB_TO_BLOCK(node->data, ret);
|
||||
}
|
||||
|
||||
|
||||
/* Read LEN bytes from the file described by DATA starting with byte
|
||||
POS. Return the amount of read bytes in READ. */
|
||||
static grub_ssize_t
|
||||
grub_xfs_read_file (grub_fshelp_node_t node,
|
||||
void NESTED_FUNC_ATTR (*read_hook) (grub_disk_addr_t sector,
|
||||
unsigned offset, unsigned length),
|
||||
int 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,
|
||||
grub_be_to_cpu64 (node->inode.size),
|
||||
node->data->sblock.log2_bsize
|
||||
- GRUB_DISK_SECTOR_BITS);
|
||||
}
|
||||
|
||||
|
||||
static char *
|
||||
grub_xfs_read_symlink (grub_fshelp_node_t node)
|
||||
{
|
||||
int size = grub_be_to_cpu64 (node->inode.size);
|
||||
|
||||
switch (node->inode.format)
|
||||
{
|
||||
case XFS_INODE_FORMAT_INO:
|
||||
return grub_strndup (node->inode.data.raw, size);
|
||||
|
||||
case XFS_INODE_FORMAT_EXT:
|
||||
{
|
||||
char *symlink;
|
||||
grub_ssize_t numread;
|
||||
|
||||
symlink = grub_malloc (size + 1);
|
||||
if (!symlink)
|
||||
return 0;
|
||||
|
||||
numread = grub_xfs_read_file (node, 0, 0, size, symlink);
|
||||
if (numread != size)
|
||||
{
|
||||
grub_free (symlink);
|
||||
return 0;
|
||||
}
|
||||
symlink[size] = '\0';
|
||||
return symlink;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
static enum grub_fshelp_filetype
|
||||
grub_xfs_mode_to_filetype (grub_uint16_t mode)
|
||||
{
|
||||
if ((grub_be_to_cpu16 (mode)
|
||||
& FILETYPE_INO_MASK) == FILETYPE_INO_DIRECTORY)
|
||||
return GRUB_FSHELP_DIR;
|
||||
else if ((grub_be_to_cpu16 (mode)
|
||||
& FILETYPE_INO_MASK) == FILETYPE_INO_SYMLINK)
|
||||
return GRUB_FSHELP_SYMLINK;
|
||||
else if ((grub_be_to_cpu16 (mode)
|
||||
& FILETYPE_INO_MASK) == FILETYPE_INO_REG)
|
||||
return GRUB_FSHELP_REG;
|
||||
return GRUB_FSHELP_UNKNOWN;
|
||||
}
|
||||
|
||||
|
||||
static int
|
||||
grub_xfs_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))
|
||||
{
|
||||
struct grub_fshelp_node *diro = (struct grub_fshelp_node *) dir;
|
||||
auto int NESTED_FUNC_ATTR call_hook (grub_uint64_t ino, char *filename);
|
||||
|
||||
int NESTED_FUNC_ATTR call_hook (grub_uint64_t ino, char *filename)
|
||||
{
|
||||
struct grub_fshelp_node *fdiro;
|
||||
|
||||
fdiro = grub_malloc (sizeof (struct grub_fshelp_node)
|
||||
- sizeof (struct grub_xfs_inode)
|
||||
+ (1 << diro->data->sblock.log2_inode));
|
||||
if (!fdiro)
|
||||
return 0;
|
||||
|
||||
/* The inode should be read, otherwise the filetype can
|
||||
not be determined. */
|
||||
fdiro->ino = ino;
|
||||
fdiro->inode_read = 1;
|
||||
fdiro->data = diro->data;
|
||||
grub_xfs_read_inode (diro->data, ino, &fdiro->inode);
|
||||
|
||||
return hook (filename,
|
||||
grub_xfs_mode_to_filetype (fdiro->inode.mode),
|
||||
fdiro);
|
||||
}
|
||||
|
||||
switch (diro->inode.format)
|
||||
{
|
||||
case XFS_INODE_FORMAT_INO:
|
||||
{
|
||||
struct grub_xfs_dir_entry *de = &diro->inode.data.dir.direntry[0];
|
||||
int smallino = !diro->inode.data.dir.dirhead.smallino;
|
||||
int i;
|
||||
grub_uint64_t parent;
|
||||
|
||||
/* If small inode numbers are used to pack the direntry, the
|
||||
parent inode number is small too. */
|
||||
if (smallino)
|
||||
{
|
||||
parent = grub_be_to_cpu32 (diro->inode.data.dir.dirhead.parent.i4);
|
||||
parent = grub_cpu_to_be64 (parent);
|
||||
/* The header is a bit smaller than usual. */
|
||||
de = (struct grub_xfs_dir_entry *) ((char *) de - 4);
|
||||
}
|
||||
else
|
||||
{
|
||||
parent = diro->inode.data.dir.dirhead.parent.i8;
|
||||
}
|
||||
|
||||
/* Synthesize the direntries for `.' and `..'. */
|
||||
if (call_hook (diro->ino, "."))
|
||||
return 1;
|
||||
|
||||
if (call_hook (parent, ".."))
|
||||
return 1;
|
||||
|
||||
for (i = 0; i < diro->inode.data.dir.dirhead.count; i++)
|
||||
{
|
||||
grub_uint64_t ino;
|
||||
void *inopos = (((char *) de)
|
||||
+ sizeof (struct grub_xfs_dir_entry)
|
||||
+ de->len - 1);
|
||||
char name[de->len + 1];
|
||||
|
||||
if (smallino)
|
||||
{
|
||||
ino = grub_be_to_cpu32 (*(grub_uint32_t *) inopos);
|
||||
ino = grub_cpu_to_be64 (ino);
|
||||
}
|
||||
else
|
||||
ino = *(grub_uint64_t *) inopos;
|
||||
|
||||
grub_memcpy (name, de->name, de->len);
|
||||
name[de->len] = '\0';
|
||||
if (call_hook (ino, name))
|
||||
return 1;
|
||||
|
||||
de = ((struct grub_xfs_dir_entry *)
|
||||
(((char *) de)+ sizeof (struct grub_xfs_dir_entry) + de->len
|
||||
+ ((smallino ? sizeof (grub_uint32_t)
|
||||
: sizeof (grub_uint64_t))) - 1));
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
case XFS_INODE_FORMAT_BTREE:
|
||||
case XFS_INODE_FORMAT_EXT:
|
||||
{
|
||||
grub_ssize_t numread;
|
||||
char *dirblock;
|
||||
grub_uint64_t blk;
|
||||
int dirblk_size, dirblk_log2;
|
||||
|
||||
dirblk_log2 = (dir->data->sblock.log2_bsize
|
||||
+ dir->data->sblock.log2_dirblk);
|
||||
dirblk_size = 1 << dirblk_log2;
|
||||
|
||||
dirblock = grub_malloc (dirblk_size);
|
||||
if (! dirblock)
|
||||
return 0;
|
||||
|
||||
/* Iterate over every block the directory has. */
|
||||
for (blk = 0;
|
||||
blk < (grub_be_to_cpu64 (dir->inode.size)
|
||||
>> dirblk_log2);
|
||||
blk++)
|
||||
{
|
||||
/* The header is skipped, the first direntry is stored
|
||||
from byte 16. */
|
||||
int pos = 16;
|
||||
int entries;
|
||||
int tail_start = (dirblk_size
|
||||
- sizeof (struct grub_xfs_dirblock_tail));
|
||||
|
||||
struct grub_xfs_dirblock_tail *tail;
|
||||
tail = (struct grub_xfs_dirblock_tail *) &dirblock[tail_start];
|
||||
|
||||
numread = grub_xfs_read_file (dir, 0,
|
||||
blk << dirblk_log2,
|
||||
dirblk_size, dirblock);
|
||||
if (numread != dirblk_size)
|
||||
return 0;
|
||||
|
||||
entries = (grub_be_to_cpu32 (tail->leaf_count)
|
||||
- grub_be_to_cpu32 (tail->leaf_stale));
|
||||
|
||||
/* Iterate over all entries within this block. */
|
||||
while (pos < (dirblk_size
|
||||
- (int) sizeof (struct grub_xfs_dir2_entry)))
|
||||
{
|
||||
struct grub_xfs_dir2_entry *direntry;
|
||||
grub_uint16_t *freetag;
|
||||
char *filename;
|
||||
|
||||
direntry = (struct grub_xfs_dir2_entry *) &dirblock[pos];
|
||||
freetag = (grub_uint16_t *) direntry;
|
||||
|
||||
if (*freetag == 0XFFFF)
|
||||
{
|
||||
grub_uint16_t *skip = (grub_uint16_t *) (freetag + 1);
|
||||
|
||||
/* This entry is not used, go to the next one. */
|
||||
pos += grub_be_to_cpu16 (*skip);
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
filename = &dirblock[pos + sizeof (*direntry)];
|
||||
/* The byte after the filename is for the tag, which
|
||||
is not used by GRUB. So it can be overwritten. */
|
||||
filename[direntry->len] = '\0';
|
||||
|
||||
if (call_hook (direntry->inode, filename))
|
||||
{
|
||||
grub_free (dirblock);
|
||||
return 1;
|
||||
}
|
||||
|
||||
/* Check if last direntry in this block is
|
||||
reached. */
|
||||
entries--;
|
||||
if (!entries)
|
||||
break;
|
||||
|
||||
/* Select the next directory entry. */
|
||||
pos = GRUB_XFS_NEXT_DIRENT (pos, direntry->len);
|
||||
pos = GRUB_XFS_ROUND_TO_DIRENT (pos);
|
||||
}
|
||||
}
|
||||
grub_free (dirblock);
|
||||
break;
|
||||
}
|
||||
|
||||
default:
|
||||
grub_error (GRUB_ERR_NOT_IMPLEMENTED_YET,
|
||||
"XFS does not support inode format %d yet",
|
||||
diro->inode.format);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
static struct grub_xfs_data *
|
||||
grub_xfs_mount (grub_disk_t disk)
|
||||
{
|
||||
struct grub_xfs_data *data = 0;
|
||||
|
||||
data = grub_zalloc (sizeof (struct grub_xfs_data));
|
||||
if (!data)
|
||||
return 0;
|
||||
|
||||
/* Read the superblock. */
|
||||
if (grub_disk_read (disk, 0, 0,
|
||||
sizeof (struct grub_xfs_sblock), &data->sblock))
|
||||
goto fail;
|
||||
|
||||
if (grub_strncmp ((char *) (data->sblock.magic), "XFSB", 4))
|
||||
{
|
||||
grub_error (GRUB_ERR_BAD_FS, "not a XFS filesystem");
|
||||
goto fail;
|
||||
}
|
||||
|
||||
data = grub_realloc (data,
|
||||
sizeof (struct grub_xfs_data)
|
||||
- sizeof (struct grub_xfs_inode)
|
||||
+ (1 << data->sblock.log2_inode));
|
||||
|
||||
if (! data)
|
||||
goto fail;
|
||||
|
||||
data->diropen.data = data;
|
||||
data->diropen.ino = data->sblock.rootino;
|
||||
data->diropen.inode_read = 1;
|
||||
data->bsize = grub_be_to_cpu32 (data->sblock.bsize);
|
||||
data->agsize = grub_be_to_cpu32 (data->sblock.agsize);
|
||||
|
||||
data->disk = disk;
|
||||
data->pos = 0;
|
||||
|
||||
grub_xfs_read_inode (data, data->diropen.ino, &data->diropen.inode);
|
||||
|
||||
return data;
|
||||
fail:
|
||||
|
||||
if (grub_errno == GRUB_ERR_OUT_OF_RANGE)
|
||||
grub_error (GRUB_ERR_BAD_FS, "not an XFS filesystem");
|
||||
|
||||
grub_free (data);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
static grub_err_t
|
||||
grub_xfs_dir (grub_device_t device, const char *path,
|
||||
int (*hook) (const char *filename,
|
||||
const struct grub_dirhook_info *info))
|
||||
{
|
||||
struct grub_xfs_data *data = 0;
|
||||
struct grub_fshelp_node *fdiro = 0;
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
grub_dl_ref (my_mod);
|
||||
|
||||
data = grub_xfs_mount (device->disk);
|
||||
if (!data)
|
||||
goto mount_fail;
|
||||
|
||||
grub_fshelp_find_file (path, &data->diropen, &fdiro, grub_xfs_iterate_dir,
|
||||
grub_xfs_read_symlink, GRUB_FSHELP_DIR);
|
||||
if (grub_errno)
|
||||
goto fail;
|
||||
|
||||
grub_xfs_iterate_dir (fdiro, iterate);
|
||||
|
||||
fail:
|
||||
if (fdiro != &data->diropen)
|
||||
grub_free (fdiro);
|
||||
grub_free (data);
|
||||
|
||||
mount_fail:
|
||||
|
||||
grub_dl_unref (my_mod);
|
||||
|
||||
return grub_errno;
|
||||
}
|
||||
|
||||
|
||||
/* Open a file named NAME and initialize FILE. */
|
||||
static grub_err_t
|
||||
grub_xfs_open (struct grub_file *file, const char *name)
|
||||
{
|
||||
struct grub_xfs_data *data;
|
||||
struct grub_fshelp_node *fdiro = 0;
|
||||
|
||||
grub_dl_ref (my_mod);
|
||||
|
||||
data = grub_xfs_mount (file->device->disk);
|
||||
if (!data)
|
||||
goto mount_fail;
|
||||
|
||||
grub_fshelp_find_file (name, &data->diropen, &fdiro, grub_xfs_iterate_dir,
|
||||
grub_xfs_read_symlink, GRUB_FSHELP_REG);
|
||||
if (grub_errno)
|
||||
goto fail;
|
||||
|
||||
if (!fdiro->inode_read)
|
||||
{
|
||||
grub_xfs_read_inode (data, fdiro->ino, &fdiro->inode);
|
||||
if (grub_errno)
|
||||
goto fail;
|
||||
}
|
||||
|
||||
if (fdiro != &data->diropen)
|
||||
grub_memcpy (&data->diropen, fdiro,
|
||||
sizeof (struct grub_fshelp_node)
|
||||
- sizeof (struct grub_xfs_inode)
|
||||
+ (1 << data->sblock.log2_inode));
|
||||
|
||||
file->size = grub_be_to_cpu64 (data->diropen.inode.size);
|
||||
file->data = data;
|
||||
file->offset = 0;
|
||||
|
||||
return 0;
|
||||
|
||||
fail:
|
||||
if (fdiro != &data->diropen)
|
||||
grub_free (fdiro);
|
||||
grub_free (data);
|
||||
|
||||
mount_fail:
|
||||
grub_dl_unref (my_mod);
|
||||
|
||||
return grub_errno;
|
||||
}
|
||||
|
||||
|
||||
static grub_ssize_t
|
||||
grub_xfs_read (grub_file_t file, char *buf, grub_size_t len)
|
||||
{
|
||||
struct grub_xfs_data *data =
|
||||
(struct grub_xfs_data *) file->data;
|
||||
|
||||
return grub_xfs_read_file (&data->diropen, file->read_hook,
|
||||
file->offset, len, buf);
|
||||
}
|
||||
|
||||
|
||||
static grub_err_t
|
||||
grub_xfs_close (grub_file_t file)
|
||||
{
|
||||
grub_free (file->data);
|
||||
|
||||
grub_dl_unref (my_mod);
|
||||
|
||||
return GRUB_ERR_NONE;
|
||||
}
|
||||
|
||||
|
||||
static grub_err_t
|
||||
grub_xfs_label (grub_device_t device, char **label)
|
||||
{
|
||||
struct grub_xfs_data *data;
|
||||
grub_disk_t disk = device->disk;
|
||||
|
||||
grub_dl_ref (my_mod);
|
||||
|
||||
data = grub_xfs_mount (disk);
|
||||
if (data)
|
||||
*label = grub_strndup ((char *) (data->sblock.label), 12);
|
||||
else
|
||||
*label = 0;
|
||||
|
||||
grub_dl_unref (my_mod);
|
||||
|
||||
grub_free (data);
|
||||
|
||||
return grub_errno;
|
||||
}
|
||||
|
||||
static grub_err_t
|
||||
grub_xfs_uuid (grub_device_t device, char **uuid)
|
||||
{
|
||||
struct grub_xfs_data *data;
|
||||
grub_disk_t disk = device->disk;
|
||||
|
||||
grub_dl_ref (my_mod);
|
||||
|
||||
data = grub_xfs_mount (disk);
|
||||
if (data)
|
||||
{
|
||||
*uuid = grub_xasprintf ("%04x%04x-%04x-%04x-%04x-%04x%04x%04x",
|
||||
grub_be_to_cpu16 (data->sblock.uuid[0]),
|
||||
grub_be_to_cpu16 (data->sblock.uuid[1]),
|
||||
grub_be_to_cpu16 (data->sblock.uuid[2]),
|
||||
grub_be_to_cpu16 (data->sblock.uuid[3]),
|
||||
grub_be_to_cpu16 (data->sblock.uuid[4]),
|
||||
grub_be_to_cpu16 (data->sblock.uuid[5]),
|
||||
grub_be_to_cpu16 (data->sblock.uuid[6]),
|
||||
grub_be_to_cpu16 (data->sblock.uuid[7]));
|
||||
}
|
||||
else
|
||||
*uuid = NULL;
|
||||
|
||||
grub_dl_unref (my_mod);
|
||||
|
||||
grub_free (data);
|
||||
|
||||
return grub_errno;
|
||||
}
|
||||
|
||||
|
||||
|
||||
static struct grub_fs grub_xfs_fs =
|
||||
{
|
||||
.name = "xfs",
|
||||
.dir = grub_xfs_dir,
|
||||
.open = grub_xfs_open,
|
||||
.read = grub_xfs_read,
|
||||
.close = grub_xfs_close,
|
||||
.label = grub_xfs_label,
|
||||
.uuid = grub_xfs_uuid,
|
||||
.next = 0
|
||||
};
|
||||
|
||||
GRUB_MOD_INIT(xfs)
|
||||
{
|
||||
grub_fs_register (&grub_xfs_fs);
|
||||
my_mod = mod;
|
||||
}
|
||||
|
||||
GRUB_MOD_FINI(xfs)
|
||||
{
|
||||
grub_fs_unregister (&grub_xfs_fs);
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue