diff --git a/grub-core/fs/fat.c b/grub-core/fs/fat.c index 827708c18..aa2145cf2 100644 --- a/grub-core/fs/fat.c +++ b/grub-core/fs/fat.c @@ -31,6 +31,7 @@ #else #include #endif +#include #include 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; }