diff --git a/grub-core/fs/hfs.c b/grub-core/fs/hfs.c index 06cf0c7ab..0ebc71b62 100644 --- a/grub-core/fs/hfs.c +++ b/grub-core/fs/hfs.c @@ -29,6 +29,7 @@ #include #include #include +#include GRUB_MOD_LICENSE ("GPLv3+"); @@ -1125,90 +1126,81 @@ utf8_to_macroman (grub_uint8_t *to, const char *from) return optr - to; } +union grub_hfs_anyrec { + struct grub_hfs_filerec frec; + struct grub_hfs_dirrec dir; +}; + +struct grub_fshelp_node +{ + struct grub_hfs_data *data; + union grub_hfs_anyrec fdrec; + grub_uint32_t inode; +}; + +static grub_err_t +lookup_file (grub_fshelp_node_t dir, + const char *name, + grub_fshelp_node_t *foundnode, + enum grub_fshelp_filetype *foundtype) +{ + struct grub_hfs_catalog_key key; + grub_ssize_t slen; + union grub_hfs_anyrec fdrec; + + key.parent_dir = grub_cpu_to_be32 (dir->inode); + slen = utf8_to_macroman (key.str, name); + if (slen < 0) + /* Not found */ + return GRUB_ERR_NONE; + key.strlen = slen; + + /* Lookup this node. */ + if (! grub_hfs_find_node (dir->data, (char *) &key, dir->data->cat_root, + 0, (char *) &fdrec.frec, sizeof (fdrec.frec))) + /* Not found */ + return GRUB_ERR_NONE; + + *foundnode = grub_malloc (sizeof (struct grub_fshelp_node)); + if (!*foundnode) + return grub_errno; + + (*foundnode)->inode = grub_be_to_cpu32 (fdrec.dir.dirid); + (*foundnode)->fdrec = fdrec; + (*foundnode)->data = dir->data; + *foundtype = (fdrec.frec.type == GRUB_HFS_FILETYPE_DIR) ? GRUB_FSHELP_DIR : GRUB_FSHELP_REG; + return GRUB_ERR_NONE; +} /* Find a file or directory with the pathname PATH in the filesystem DATA. Return the file record in RETDATA when it is non-zero. Return the directory number in RETINODE when it is non-zero. */ static grub_err_t grub_hfs_find_dir (struct grub_hfs_data *data, const char *path, - struct grub_hfs_filerec *retdata, int *retinode) + grub_fshelp_node_t *found, + enum grub_fshelp_filetype exptype) { - int inode = data->rootdir; - char *next; - char *origpath; - union { - struct grub_hfs_filerec frec; - struct grub_hfs_dirrec dir; - } fdrec; - - fdrec.frec.type = GRUB_HFS_FILETYPE_DIR; - - if (path[0] != '/') - { - grub_error (GRUB_ERR_BAD_FILENAME, N_("invalid file name `%s'"), path); - return 0; + struct grub_fshelp_node root = { + .data = data, + .inode = data->rootdir, + .fdrec = { + .frec = { + .type = GRUB_HFS_FILETYPE_DIR + } } + }; + grub_err_t err; - origpath = grub_strdup (path); - if (!origpath) - return grub_errno; + err = grub_fshelp_find_file_lookup (path, &root, found, lookup_file, NULL, exptype); - path = origpath; - while (*path == '/') - path++; - - while (path && grub_strlen (path)) + if (&root == *found) { - grub_ssize_t slen; - if (fdrec.frec.type != GRUB_HFS_FILETYPE_DIR) - { - grub_error (GRUB_ERR_BAD_FILE_TYPE, N_("not a directory")); - goto fail; - } - - /* Isolate a part of the path. */ - next = grub_strchr (path, '/'); - if (next) - { - while (*next == '/') - *(next++) = '\0'; - } - - struct grub_hfs_catalog_key key; - - key.parent_dir = grub_cpu_to_be32 (inode); - slen = utf8_to_macroman (key.str, path); - if (slen < 0) - { - grub_error (GRUB_ERR_FILE_NOT_FOUND, N_("file `%s' not found"), path); - goto fail; - } - key.strlen = slen; - - /* Lookup this node. */ - if (! grub_hfs_find_node (data, (char *) &key, data->cat_root, - 0, (char *) &fdrec.frec, sizeof (fdrec.frec))) - { - grub_error (GRUB_ERR_FILE_NOT_FOUND, N_("file `%s' not found"), origpath); - goto fail; - } - - if (grub_errno) - goto fail; - - inode = grub_be_to_cpu32 (fdrec.dir.dirid); - path = next; + *found = grub_malloc (sizeof (root)); + if (!*found) + return grub_errno; + grub_memcpy (*found, &root, sizeof (root)); } - - if (retdata) - grub_memcpy (retdata, &fdrec.frec, sizeof (fdrec.frec)); - - if (retinode) - *retinode = inode; - - fail: - grub_free (origpath); - return grub_errno; + return err; } struct grub_hfs_dir_hook_ctx @@ -1266,16 +1258,14 @@ static grub_err_t grub_hfs_dir (grub_device_t device, const char *path, grub_fs_dir_hook_t hook, void *hook_data) { - int inode; - struct grub_hfs_data *data; - struct grub_hfs_filerec frec; struct grub_hfs_dir_hook_ctx ctx = { .hook = hook, .hook_data = hook_data }; - + grub_fshelp_node_t found = NULL; + grub_dl_ref (my_mod); data = grub_hfs_mount (device->disk); @@ -1283,18 +1273,13 @@ grub_hfs_dir (grub_device_t device, const char *path, grub_fs_dir_hook_t hook, goto fail; /* First the directory ID for the directory. */ - if (grub_hfs_find_dir (data, path, &frec, &inode)) + if (grub_hfs_find_dir (data, path, &found, GRUB_FSHELP_DIR)) goto fail; - if (frec.type != GRUB_HFS_FILETYPE_DIR) - { - grub_error (GRUB_ERR_BAD_FILE_TYPE, N_("not a directory")); - goto fail; - } - - grub_hfs_iterate_dir (data, data->cat_root, inode, grub_hfs_dir_hook, &ctx); + grub_hfs_iterate_dir (data, data->cat_root, found->inode, grub_hfs_dir_hook, &ctx); fail: + grub_free (found); grub_free (data); grub_dl_unref (my_mod); @@ -1308,8 +1293,8 @@ static grub_err_t grub_hfs_open (struct grub_file *file, const char *name) { struct grub_hfs_data *data; - struct grub_hfs_filerec frec; - + grub_fshelp_node_t found = NULL; + grub_dl_ref (my_mod); data = grub_hfs_mount (file->device->disk); @@ -1320,29 +1305,23 @@ grub_hfs_open (struct grub_file *file, const char *name) return grub_errno; } - if (grub_hfs_find_dir (data, name, &frec, 0)) + if (grub_hfs_find_dir (data, name, &found, GRUB_FSHELP_REG)) { grub_free (data); grub_dl_unref (my_mod); return grub_errno; } - if (frec.type != GRUB_HFS_FILETYPE_FILE) - { - grub_free (data); - grub_error (GRUB_ERR_BAD_FILE_TYPE, N_("not a regular file")); - grub_dl_unref (my_mod); - return grub_errno; - } - - grub_memcpy (data->extents, frec.extents, sizeof (grub_hfs_datarecord_t)); - file->size = grub_be_to_cpu32 (frec.size); - data->size = grub_be_to_cpu32 (frec.size); - data->fileid = grub_be_to_cpu32 (frec.fileid); + grub_memcpy (data->extents, found->fdrec.frec.extents, sizeof (grub_hfs_datarecord_t)); + file->size = grub_be_to_cpu32 (found->fdrec.frec.size); + data->size = grub_be_to_cpu32 (found->fdrec.frec.size); + data->fileid = grub_be_to_cpu32 (found->fdrec.frec.fileid); file->offset = 0; file->data = data; + grub_free (found); + return 0; }