radare2/shlr/grub/fs/fb.c
2017-05-13 00:42:00 +02:00

256 lines
5.5 KiB
C

/*
* BURG - Brand-new Universal loadeR from GRUB
* Copyright 2010 Bean Lee - All Rights Reserved
*
* BURG 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.
*
* BURG 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 BURG. If not, see <http://www.gnu.org/licenses/>.
*/
#include <grub/dl.h>
#include <grub/fs.h>
#include <grub/mm.h>
#include <grub/disk.h>
#include <grub/file.h>
#include <grub/misc.h>
#include <grub/fbfs.h>
struct grub_fb_data
{
grub_uint32_t ofs;
grub_uint32_t pri_size;
struct fbm_file *ptr;
char fb_list[0];
};
static struct grub_fb_data *
grub_fbfs_mount (grub_disk_t disk)
{
struct fb_mbr *m;
struct fb_data *d;
char buf[512];
struct grub_fb_data *data;
int boot_base, boot_size, list_used, pri_size, ofs, i;
char *fb_list, *p1, *p2;
grub_uint32_t *grub32;
if (grub_disk_read (disk, 0, 0, sizeof (buf), buf))
goto fail;
m = (struct fb_mbr *) buf;
d = (struct fb_data *) buf;
grub32 = (grub_uint32_t *)&buf;
if (*grub32 == FB_AR_MAGIC_LONG)
{
ofs = 0;
boot_base = 0;
boot_size = 0;
pri_size = 0;
}
else
{
if ((m->fb_magic != FB_MAGIC_LONG) || (m->end_magic != 0xaa55))
goto fail;
ofs = m->lba;
boot_base = m->boot_base;
if (grub_disk_read (disk, boot_base + 1 - ofs, 0, sizeof (buf), buf))
goto fail;
boot_size = d->boot_size;
pri_size = d->pri_size;
}
if ((d->ver_major != FB_VER_MAJOR) || (d->ver_minor != FB_VER_MINOR))
goto fail;
list_used = d->list_used;
data = grub_malloc (sizeof (*data) + (list_used << 9));
if (! data)
goto fail;
fb_list = data->fb_list;
if (grub_disk_read (disk, boot_base + 1 + boot_size - ofs, 0,
(list_used << 9), fb_list))
{
grub_free (data);
goto fail;
}
p1 = p2 = fb_list;
for (i = 0; i < list_used - 1; i++)
{
p1 += 510;
p2 += 512;
grub_memcpy (p1, p2, 510);
}
data->ofs = ofs;
data->pri_size = pri_size;
return data;
fail:
grub_error (GRUB_ERR_BAD_FS, "not a fb filesystem");
return 0;
}
static grub_err_t
grub_fbfs_dir (grub_device_t device, const char *path,
int (*hook) (const char *filename,
const struct grub_dirhook_info *info,
void *closure),
void *closure)
{
struct grub_dirhook_info info;
struct fbm_file *p;
char *fn;
int len, ofs;
struct grub_fb_data *data;
data = grub_fbfs_mount (device->disk);
if (! data)
return grub_errno;
if (! hook)
return GRUB_ERR_NONE;
while (*path == '/')
path++;
len = grub_strlen (path);
fn = grub_strrchr (path, '/');
ofs = (fn) ? (fn + 1 - path) : 0;
grub_memset (&info, 0, sizeof (info));
info.mtimeset = 1;
p = (struct fbm_file *) data->fb_list;
while (p->size)
{
info.mtime = grub_le_to_cpu32 (p->data_time);
if ((! grub_memcmp (path, p->name, len)) &&
(hook (p->name + ofs, &info, closure)))
break;
p = (struct fbm_file *) ((char *) p + p->size + 2);
}
grub_free (data);
return GRUB_ERR_NONE;
}
static grub_err_t
grub_fbfs_open (struct grub_file *file, const char *name)
{
struct fbm_file *p;
struct grub_fb_data *data;
data = grub_fbfs_mount (file->device->disk);
if (! data)
return grub_errno;
while (*name == '/')
name++;
p = (struct fbm_file *) data->fb_list;
while (p->size)
{
if (! grub_strcasecmp (name, p->name))
{
file->data = data;
data->ptr = p;
file->size = p->data_size;
return GRUB_ERR_NONE;
}
p = (struct fbm_file *) ((char *) p + p->size + 2);
}
return grub_error (GRUB_ERR_FILE_NOT_FOUND, "file not found");
}
static grub_ssize_t
grub_fbfs_read (grub_file_t file, char *buf, grub_size_t len)
{
struct fbm_file *p;
grub_disk_t disk;
grub_uint32_t sector;
grub_size_t saved_len, ofs;
struct grub_fb_data *data;
disk = file->device->disk;
disk->read_hook = file->read_hook;
disk->closure = file->closure;
data = file->data;
p = data->ptr;
if (p->data_start >= data->pri_size)
{
grub_err_t err;
err = grub_disk_read_ex (disk, p->data_start - data->ofs,
file->offset, len, buf, file->flags);
disk->read_hook = 0;
return (err) ? -1 : (grub_ssize_t) len;
}
sector = p->data_start + ((grub_uint32_t) file->offset / 510) - data->ofs;
ofs = ((grub_uint32_t) file->offset % 510);
saved_len = len;
while (len)
{
int n;
n = len;
if (ofs + n > 510)
n = 510 - ofs;
if (grub_disk_read (disk, sector, ofs, n, buf))
{
saved_len = -1;
break;
}
sector++;
if (buf)
buf += n;
len -= n;
ofs = 0;
}
disk->read_hook = 0;
return saved_len;
}
static grub_err_t
grub_fbfs_close (grub_file_t file)
{
grub_free (file->data);
return GRUB_ERR_NONE;
}
static grub_err_t
grub_fbfs_label (grub_device_t device ,
char **label)
{
*label = 0;
return GRUB_ERR_NONE;
}
struct grub_fs grub_fb_fs =
{
.name = "fbfs",
.dir = grub_fbfs_dir,
.open = grub_fbfs_open,
.read = grub_fbfs_read,
.close = grub_fbfs_close,
.label = grub_fbfs_label,
.next = 0
};