FAT: Convert to fshelp.

exFAT doesn't handle "." and ".." correctly, convert it to fshelp to
reuse the same logic.
This commit is contained in:
Vladimir Serbinenko 2015-07-27 12:49:26 +02:00
parent d1d3a60b71
commit bfb5b33e96

View file

@ -31,6 +31,7 @@
#else
#include <grub/exfat.h>
#endif
#include <grub/fshelp.h>
#include <grub/i18n.h>
GRUB_MOD_LICENSE ("GPLv3+");
@ -173,8 +174,6 @@ struct grub_fat_data
#ifndef MODE_EXFAT
grub_uint32_t root_sector;
grub_uint32_t num_root_sectors;
#else
int is_contiguous;
#endif
int cluster_bits;
@ -182,13 +181,22 @@ struct grub_fat_data
grub_uint32_t cluster_sector;
grub_uint32_t num_clusters;
grub_uint32_t uuid;
};
struct grub_fshelp_node {
grub_disk_t disk;
struct grub_fat_data *data;
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;
#ifdef MODE_EXFAT
int is_contiguous;
#endif
};
static grub_dl_t my_mod;
@ -427,13 +435,6 @@ grub_fat_mount (grub_disk_t disk)
(void) magic;
#endif
/* Start from the root directory. */
data->file_cluster = data->root_cluster;
data->cur_cluster_num = ~0U;
data->attr = GRUB_FAT_ATTR_DIRECTORY;
#ifdef MODE_EXFAT
data->is_contiguous = 0;
#endif
return data;
fail:
@ -444,7 +445,7 @@ grub_fat_mount (grub_disk_t disk)
}
static grub_ssize_t
grub_fat_read_data (grub_disk_t disk, struct grub_fat_data *data,
grub_fat_read_data (grub_disk_t disk, grub_fshelp_node_t node,
grub_disk_read_hook_t read_hook, void *read_hook_data,
grub_off_t offset, grub_size_t len, char *buf)
{
@ -457,13 +458,13 @@ grub_fat_read_data (grub_disk_t disk, struct grub_fat_data *data,
#ifndef MODE_EXFAT
/* This is a special case. FAT12 and FAT16 doesn't have the root directory
in clusters. */
if (data->file_cluster == ~0U)
if (node->file_cluster == ~0U)
{
size = (data->num_root_sectors << GRUB_DISK_SECTOR_BITS) - offset;
size = (node->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))
if (grub_disk_read (disk, node->data->root_sector, offset, size, buf))
return -1;
return size;
@ -471,12 +472,12 @@ grub_fat_read_data (grub_disk_t disk, struct grub_fat_data *data,
#endif
#ifdef MODE_EXFAT
if (data->is_contiguous)
if (node->is_contiguous)
{
/* Read the data here. */
sector = (data->cluster_sector
+ ((data->file_cluster - 2)
<< data->cluster_bits));
sector = (node->data->cluster_sector
+ ((node->file_cluster - 2)
<< node->data->cluster_bits));
disk->read_hook = read_hook;
disk->read_hook_data = read_hook_data;
@ -491,53 +492,53 @@ grub_fat_read_data (grub_disk_t disk, struct grub_fat_data *data,
#endif
/* Calculate the logical cluster number and offset. */
logical_cluster_bits = (data->cluster_bits
logical_cluster_bits = (node->data->cluster_bits
+ GRUB_DISK_SECTOR_BITS);
logical_cluster = offset >> logical_cluster_bits;
offset &= (1ULL << logical_cluster_bits) - 1;
if (logical_cluster < data->cur_cluster_num)
if (logical_cluster < node->cur_cluster_num)
{
data->cur_cluster_num = 0;
data->cur_cluster = data->file_cluster;
node->cur_cluster_num = 0;
node->cur_cluster = node->file_cluster;
}
while (len)
{
while (logical_cluster > data->cur_cluster_num)
while (logical_cluster > node->cur_cluster_num)
{
/* Find next cluster. */
grub_uint32_t next_cluster;
grub_uint32_t fat_offset;
switch (data->fat_size)
switch (node->data->fat_size)
{
case 32:
fat_offset = data->cur_cluster << 2;
fat_offset = node->cur_cluster << 2;
break;
case 16:
fat_offset = data->cur_cluster << 1;
fat_offset = node->cur_cluster << 1;
break;
default:
/* case 12: */
fat_offset = data->cur_cluster + (data->cur_cluster >> 1);
fat_offset = node->cur_cluster + (node->cur_cluster >> 1);
break;
}
/* Read the FAT. */
if (grub_disk_read (disk, data->fat_sector, fat_offset,
(data->fat_size + 7) >> 3,
if (grub_disk_read (disk, node->data->fat_sector, fat_offset,
(node->data->fat_size + 7) >> 3,
(char *) &next_cluster))
return -1;
next_cluster = grub_le_to_cpu32 (next_cluster);
switch (data->fat_size)
switch (node->data->fat_size)
{
case 16:
next_cluster &= 0xFFFF;
break;
case 12:
if (data->cur_cluster & 1)
if (node->cur_cluster & 1)
next_cluster >>= 4;
next_cluster &= 0x0FFF;
@ -545,27 +546,27 @@ grub_fat_read_data (grub_disk_t disk, struct grub_fat_data *data,
}
grub_dprintf ("fat", "fat_size=%d, next_cluster=%u\n",
data->fat_size, next_cluster);
node->data->fat_size, next_cluster);
/* Check the end. */
if (next_cluster >= data->cluster_eof_mark)
if (next_cluster >= node->data->cluster_eof_mark)
return ret;
if (next_cluster < 2 || next_cluster >= data->num_clusters)
if (next_cluster < 2 || next_cluster >= node->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++;
node->cur_cluster = next_cluster;
node->cur_cluster_num++;
}
/* Read the data here. */
sector = (data->cluster_sector
+ ((data->cur_cluster - 2)
<< data->cluster_bits));
sector = (node->data->cluster_sector
+ ((node->cur_cluster - 2)
<< node->data->cluster_bits));
size = (1 << logical_cluster_bits) - offset;
if (size > len)
size = len;
@ -631,7 +632,7 @@ grub_fat_iterate_fini (struct grub_fat_iterate_context *ctxt)
#ifdef MODE_EXFAT
static grub_err_t
grub_fat_iterate_dir_next (grub_disk_t disk, struct grub_fat_data *data,
grub_fat_iterate_dir_next (grub_fshelp_node_t node,
struct grub_fat_iterate_context *ctxt)
{
grub_memset (&ctxt->dir, 0, sizeof (ctxt->dir));
@ -641,7 +642,7 @@ grub_fat_iterate_dir_next (grub_disk_t disk, struct grub_fat_data *data,
ctxt->offset += sizeof (dir);
if (grub_fat_read_data (disk, data, 0, 0, ctxt->offset, sizeof (dir),
if (grub_fat_read_data (node->disk, node, 0, 0, ctxt->offset, sizeof (dir),
(char *) &dir)
!= sizeof (dir))
break;
@ -663,7 +664,7 @@ grub_fat_iterate_dir_next (grub_disk_t disk, struct grub_fat_data *data,
{
struct grub_fat_dir_entry sec;
ctxt->offset += sizeof (sec);
if (grub_fat_read_data (disk, data, 0, 0,
if (grub_fat_read_data (node->disk, node, 0, 0,
ctxt->offset, sizeof (sec), (char *) &sec)
!= sizeof (sec))
break;
@ -727,7 +728,7 @@ grub_fat_iterate_dir_next (grub_disk_t disk, struct grub_fat_data *data,
#else
static grub_err_t
grub_fat_iterate_dir_next (grub_disk_t disk, struct grub_fat_data *data,
grub_fat_iterate_dir_next (grub_fshelp_node_t node,
struct grub_fat_iterate_context *ctxt)
{
char *filep = 0;
@ -742,7 +743,7 @@ grub_fat_iterate_dir_next (grub_disk_t disk, struct grub_fat_data *data,
ctxt->offset += sizeof (ctxt->dir);
/* Read a directory entry. */
if (grub_fat_read_data (disk, data, 0, 0,
if (grub_fat_read_data (node->disk, node, 0, 0,
ctxt->offset, sizeof (ctxt->dir),
(char *) &ctxt->dir)
!= sizeof (ctxt->dir) || ctxt->dir.name[0] == 0)
@ -853,84 +854,20 @@ grub_fat_iterate_dir_next (grub_disk_t disk, struct grub_fat_data *data,
#endif
/* Find the underlying directory or file in PATH and return the
next path. If there is no next path or an error occurs, return NULL.
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, const char *origpath,
grub_fs_dir_hook_t hook, void *hook_data)
static grub_err_t lookup_file (grub_fshelp_node_t node,
const char *name,
grub_fshelp_node_t *foundnode,
enum grub_fshelp_filetype *foundtype)
{
char *dirname, *dirp;
int call_hook;
int found = 0;
struct grub_fat_iterate_context ctxt;
grub_err_t err;
if (! (data->attr & GRUB_FAT_ATTR_DIRECTORY))
{
grub_error (GRUB_ERR_BAD_FILE_TYPE, N_("not a directory"));
return 0;
}
do
{
/* Extract a directory name. */
while (*path == '/')
path++;
/* Emulate special "." and ".." entries in root directory */
if (data->file_cluster == data->root_cluster)
{
if (*path != '.')
break;
if (!path[1] || path[1] == '/')
{
path++;
continue;
}
if (path[1] == '.' && (!path[2] || path[2] == '/'))
{
path += 2;
continue;
}
}
break;
}
while (1);
dirp = grub_strchr (path, '/');
if (dirp)
{
unsigned len = dirp - path;
dirname = grub_malloc (len + 1);
if (! dirname)
goto fail;
grub_memcpy (dirname, path, len);
dirname[len] = '\0';
}
else
/* This is actually a file. */
dirname = grub_strdup (path);
call_hook = (! dirp && hook);
struct grub_fat_iterate_context ctxt;
err = grub_fat_iterate_init (&ctxt);
if (err)
{
grub_free (dirname);
return 0;
}
return err;
while (!(err = grub_fat_iterate_dir_next (disk, data, &ctxt)))
while (!(err = grub_fat_iterate_dir_next (node, &ctxt)))
{
struct grub_dirhook_info info;
grub_memset (&info, 0, sizeof (info));
info.dir = !! (ctxt.dir.attr & GRUB_FAT_ATTR_DIRECTORY);
info.case_insensitive = 1;
#ifdef MODE_EXFAT
if (!ctxt.dir.have_stream)
@ -939,36 +876,33 @@ grub_fat_find_dir (grub_disk_t disk, struct grub_fat_data *data,
if (ctxt.dir.attr & GRUB_FAT_ATTR_VOLUME_ID)
continue;
#endif
if (*dirname == '\0' && call_hook)
{
if (hook (ctxt.filename, &info, hook_data))
break;
else
continue;
}
if (grub_strcasecmp (dirname, ctxt.filename) == 0)
if (grub_strcasecmp (name, ctxt.filename) == 0)
{
found = 1;
data->attr = ctxt.dir.attr;
*foundnode = grub_malloc (sizeof (struct grub_fshelp_node));
if (!*foundnode)
return grub_errno;
(*foundnode)->attr = ctxt.dir.attr;
#ifdef MODE_EXFAT
data->file_size = ctxt.dir.file_size;
data->file_cluster = ctxt.dir.first_cluster;
data->is_contiguous = ctxt.dir.is_contiguous;
(*foundnode)->file_size = ctxt.dir.file_size;
(*foundnode)->file_cluster = ctxt.dir.first_cluster;
(*foundnode)->is_contiguous = ctxt.dir.is_contiguous;
#else
data->file_size = grub_le_to_cpu32 (ctxt.dir.file_size);
data->file_cluster = ((grub_le_to_cpu16 (ctxt.dir.first_cluster_high) << 16)
(*foundnode)->file_size = grub_le_to_cpu32 (ctxt.dir.file_size);
(*foundnode)->file_cluster = ((grub_le_to_cpu16 (ctxt.dir.first_cluster_high) << 16)
| grub_le_to_cpu16 (ctxt.dir.first_cluster_low));
/* If directory points to root, starting cluster is 0 */
if (!data->file_cluster)
data->file_cluster = data->root_cluster;
if (!(*foundnode)->file_cluster)
(*foundnode)->file_cluster = node->data->root_cluster;
#endif
data->cur_cluster_num = ~0U;
(*foundnode)->cur_cluster_num = ~0U;
(*foundnode)->data = node->data;
(*foundnode)->disk = node->disk;
if (call_hook)
hook (ctxt.filename, &info, hook_data);
*foundtype = ((*foundnode)->attr & GRUB_FAT_ATTR_DIRECTORY) ? GRUB_FSHELP_DIR : GRUB_FSHELP_REG;
break;
grub_fat_iterate_fini (&ctxt);
return GRUB_ERR_NONE;
}
}
@ -976,13 +910,8 @@ grub_fat_find_dir (grub_disk_t disk, struct grub_fat_data *data,
if (err == GRUB_ERR_EOF)
err = 0;
if (grub_errno == GRUB_ERR_NONE && ! found && !call_hook)
grub_error (GRUB_ERR_FILE_NOT_FOUND, N_("file `%s' not found"), origpath);
return err;
fail:
grub_free (dirname);
return found ? dirp : 0;
}
static grub_err_t
@ -991,9 +920,9 @@ grub_fat_dir (grub_device_t device, const char *path, grub_fs_dir_hook_t hook,
{
struct grub_fat_data *data = 0;
grub_disk_t disk = device->disk;
grub_size_t len;
char *dirname = 0;
char *p;
grub_fshelp_node_t found = NULL;
grub_err_t err;
struct grub_fat_iterate_context ctxt;
grub_dl_ref (my_mod);
@ -1001,27 +930,53 @@ grub_fat_dir (grub_device_t device, const char *path, grub_fs_dir_hook_t hook,
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;
struct grub_fshelp_node root = {
.data = data,
.disk = disk,
.attr = GRUB_FAT_ATTR_DIRECTORY,
.file_size = 0,
.file_cluster = data->root_cluster,
.cur_cluster_num = ~0U,
.cur_cluster = 0,
#ifdef MODE_EXFAT
.is_contiguous = 0,
#endif
};
do
err = grub_fshelp_find_file_lookup (path, &root, &found, lookup_file, NULL, GRUB_FSHELP_DIR);
if (err)
goto fail;
err = grub_fat_iterate_init (&ctxt);
if (err)
goto fail;
while (!(err = grub_fat_iterate_dir_next (found, &ctxt)))
{
p = grub_fat_find_dir (disk, data, p, path, hook, hook_data);
struct grub_dirhook_info info;
grub_memset (&info, 0, sizeof (info));
info.dir = !! (ctxt.dir.attr & GRUB_FAT_ATTR_DIRECTORY);
info.case_insensitive = 1;
#ifdef MODE_EXFAT
if (!ctxt.dir.have_stream)
continue;
#else
if (ctxt.dir.attr & GRUB_FAT_ATTR_VOLUME_ID)
continue;
#endif
if (hook (ctxt.filename, &info, hook_data))
break;
}
while (p && grub_errno == GRUB_ERR_NONE);
grub_fat_iterate_fini (&ctxt);
if (err == GRUB_ERR_EOF)
err = 0;
fail:
if (found != &root)
grub_free (found);
grub_free (dirname);
grub_free (data);
grub_dl_unref (my_mod);
@ -1033,35 +988,43 @@ 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_fshelp_node_t found = NULL;
grub_err_t err;
grub_disk_t disk = file->device->disk;
grub_dl_ref (my_mod);
data = grub_fat_mount (file->device->disk);
data = grub_fat_mount (disk);
if (! data)
goto fail;
do
{
p = grub_fat_find_dir (file->device->disk, data, p, name, 0, 0);
if (grub_errno != GRUB_ERR_NONE)
goto fail;
}
while (p);
struct grub_fshelp_node root = {
.data = data,
.disk = disk,
.attr = GRUB_FAT_ATTR_DIRECTORY,
.file_size = 0,
.file_cluster = data->root_cluster,
.cur_cluster_num = ~0U,
.cur_cluster = 0,
#ifdef MODE_EXFAT
.is_contiguous = 0,
#endif
};
if (data->attr & GRUB_FAT_ATTR_DIRECTORY)
{
grub_error (GRUB_ERR_BAD_FILE_TYPE, N_("not a regular file"));
goto fail;
}
err = grub_fshelp_find_file_lookup (name, &root, &found, lookup_file, NULL, GRUB_FSHELP_REG);
if (err)
goto fail;
file->data = data;
file->size = data->file_size;
file->data = found;
file->size = found->file_size;
return GRUB_ERR_NONE;
fail:
if (found != &root)
grub_free (found);
grub_free (data);
grub_dl_unref (my_mod);
@ -1080,7 +1043,10 @@ grub_fat_read (grub_file_t file, char *buf, grub_size_t len)
static grub_err_t
grub_fat_close (grub_file_t file)
{
grub_free (file->data);
grub_fshelp_node_t node = file->data;
grub_free (node->data);
grub_free (node);
grub_dl_unref (my_mod);
@ -1093,20 +1059,29 @@ grub_fat_label (grub_device_t device, char **label)
{
struct grub_fat_dir_entry dir;
grub_ssize_t offset = -sizeof(dir);
struct grub_fat_data *data;
grub_disk_t disk = device->disk;
struct grub_fshelp_node root = {
.disk = disk,
.attr = GRUB_FAT_ATTR_DIRECTORY,
.file_size = 0,
.cur_cluster_num = ~0U,
.cur_cluster = 0,
.is_contiguous = 0,
};
data = grub_fat_mount (disk);
if (! data)
root.data = grub_fat_mount (disk);
if (! root.data)
return grub_errno;
root.file_cluster = root.data->root_cluster;
*label = NULL;
while (1)
{
offset += sizeof (dir);
if (grub_fat_read_data (disk, data, 0, 0,
if (grub_fat_read_data (disk, &root, 0, 0,
offset, sizeof (dir), (char *) &dir)
!= sizeof (dir))
break;
@ -1126,7 +1101,7 @@ grub_fat_label (grub_device_t device, char **label)
* GRUB_MAX_UTF8_PER_UTF16 + 1);
if (!*label)
{
grub_free (data);
grub_free (root.data);
return grub_errno;
}
chc = dir.type_specific.volume_label.character_count;
@ -1138,7 +1113,7 @@ grub_fat_label (grub_device_t device, char **label)
}
}
grub_free (data);
grub_free (root.data);
return grub_errno;
}
@ -1147,30 +1122,32 @@ grub_fat_label (grub_device_t device, char **label)
static grub_err_t
grub_fat_label (grub_device_t device, char **label)
{
struct grub_fat_data *data;
grub_disk_t disk = device->disk;
grub_err_t err;
struct grub_fat_iterate_context ctxt;
struct grub_fshelp_node root = {
.disk = disk,
.attr = GRUB_FAT_ATTR_DIRECTORY,
.file_size = 0,
.cur_cluster_num = ~0U,
.cur_cluster = 0,
};
*label = 0;
grub_dl_ref (my_mod);
data = grub_fat_mount (disk);
if (! data)
root.data = grub_fat_mount (disk);
if (! root.data)
goto fail;
if (! (data->attr & GRUB_FAT_ATTR_DIRECTORY))
{
grub_error (GRUB_ERR_BAD_FILE_TYPE, N_("not a directory"));
goto fail;
}
root.file_cluster = root.data->root_cluster;
err = grub_fat_iterate_init (&ctxt);
if (err)
goto fail;
while (!(err = grub_fat_iterate_dir_next (disk, data, &ctxt)))
while (!(err = grub_fat_iterate_dir_next (&root, &ctxt)))
if ((ctxt.dir.attr & ~GRUB_FAT_ATTR_ARCHIVE) == GRUB_FAT_ATTR_VOLUME_ID)
{
*label = grub_strdup (ctxt.filename);
@ -1183,7 +1160,7 @@ grub_fat_label (grub_device_t device, char **label)
grub_dl_unref (my_mod);
grub_free (data);
grub_free (root.data);
return grub_errno;
}