| /* |
| * GRUB -- GRand Unified Bootloader |
| * Copyright (C) 2013 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/file.h> |
| #include <grub/dl.h> |
| |
| GRUB_MOD_LICENSE ("GPLv3+"); |
| |
| struct grub_offset_file |
| { |
| grub_file_t parent; |
| grub_off_t off; |
| }; |
| |
| static grub_ssize_t |
| grub_offset_read (grub_file_t file, char *buf, grub_size_t len) |
| { |
| struct grub_offset_file *data = file->data; |
| if (grub_file_seek (data->parent, data->off + file->offset) == (grub_off_t) -1) |
| return -1; |
| return grub_file_read (data->parent, buf, len); |
| } |
| |
| static grub_err_t |
| grub_offset_close (grub_file_t file) |
| { |
| struct grub_offset_file *data = file->data; |
| |
| if (data->parent) |
| grub_file_close (data->parent); |
| |
| /* No need to close the same device twice. */ |
| file->device = 0; |
| |
| return 0; |
| } |
| |
| static struct grub_fs grub_offset_fs = { |
| .name = "offset", |
| .dir = 0, |
| .open = 0, |
| .read = grub_offset_read, |
| .close = grub_offset_close, |
| .label = 0, |
| .next = 0 |
| }; |
| |
| void |
| grub_file_offset_close (grub_file_t file) |
| { |
| struct grub_offset_file *off_data = file->data; |
| off_data->parent = NULL; |
| grub_file_close (file); |
| } |
| |
| grub_file_t |
| grub_file_offset_open (grub_file_t parent, enum grub_file_type type, |
| grub_off_t start, grub_off_t size) |
| { |
| struct grub_offset_file *off_data; |
| grub_file_t off_file, last_off_file; |
| grub_file_filter_id_t filter; |
| |
| off_file = grub_zalloc (sizeof (*off_file)); |
| off_data = grub_zalloc (sizeof (*off_data)); |
| if (!off_file || !off_data) |
| { |
| grub_free (off_file); |
| grub_free (off_data); |
| return 0; |
| } |
| |
| off_data->off = start; |
| off_data->parent = parent; |
| |
| off_file->device = parent->device; |
| off_file->data = off_data; |
| off_file->fs = &grub_offset_fs; |
| off_file->size = size; |
| |
| last_off_file = NULL; |
| for (filter = GRUB_FILE_FILTER_COMPRESSION_FIRST; |
| off_file && filter <= GRUB_FILE_FILTER_COMPRESSION_LAST; filter++) |
| if (grub_file_filters[filter]) |
| { |
| last_off_file = off_file; |
| off_file = grub_file_filters[filter] (off_file, type); |
| } |
| |
| if (!off_file) |
| { |
| off_data->parent = NULL; |
| grub_file_close (last_off_file); |
| return 0; |
| } |
| return off_file; |
| } |