#else
#include <grub/exfat.h>
#endif
+#include <grub/fshelp.h>
#include <grub/i18n.h>
GRUB_MOD_LICENSE ("GPLv3+");
#ifndef MODE_EXFAT
grub_uint32_t root_sector;
grub_uint32_t num_root_sectors;
-#else
- int is_contiguous;
#endif
int cluster_bits;
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;
(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:
}
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)
{
#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;
#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;
#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;
}
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;
#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));
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;
{
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;
#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;
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)
#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)
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;
}
}
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
{
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);
if (! data)
goto fail;
- /* Make sure that DIRNAME terminates with '/'. */
- len = grub_strlen (path);
- dirname = grub_malloc (len + 1 + 1);
- if (! 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
+ };
+
+ 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;
- grub_memcpy (dirname, path, len);
- p = dirname + len;
- if (path[len - 1] != '/')
- *p++ = '/';
- *p = '\0';
- p = dirname;
-
- do
+
+ 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);
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);
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);
{
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;
* 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;
}
}
- grub_free (data);
+ grub_free (root.data);
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;
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);
grub_dl_unref (my_mod);
- grub_free (data);
+ grub_free (root.data);
return grub_errno;
}