2N/A/* fs.c - filesystem manager */
2N/A/*
2N/A * GRUB -- GRand Unified Bootloader
2N/A * Copyright (C) 2002,2005,2007 Free Software Foundation, Inc.
2N/A *
2N/A * GRUB is free software: you can redistribute it and/or modify
2N/A * it under the terms of the GNU General Public License as published by
2N/A * the Free Software Foundation, either version 3 of the License, or
2N/A * (at your option) any later version.
2N/A *
2N/A * GRUB is distributed in the hope that it will be useful,
2N/A * but WITHOUT ANY WARRANTY; without even the implied warranty of
2N/A * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
2N/A * GNU General Public License for more details.
2N/A *
2N/A * You should have received a copy of the GNU General Public License
2N/A * along with GRUB. If not, see <http://www.gnu.org/licenses/>.
2N/A */
2N/A
2N/A#include <grub/disk.h>
2N/A#include <grub/net.h>
2N/A#include <grub/fs.h>
2N/A#include <grub/file.h>
2N/A#include <grub/err.h>
2N/A#include <grub/misc.h>
2N/A#include <grub/types.h>
2N/A#include <grub/mm.h>
2N/A#include <grub/term.h>
2N/A
2N/Agrub_fs_t grub_fs_list = 0;
2N/A
2N/Agrub_fs_autoload_hook_t grub_fs_autoload_hook = 0;
2N/A
2N/Agrub_fs_t
2N/Agrub_fs_probe (grub_device_t device)
2N/A{
2N/A grub_fs_t p;
2N/A auto int dummy_func (const char *filename,
2N/A const struct grub_dirhook_info *info);
2N/A
2N/A int dummy_func (const char *filename __attribute__ ((unused)),
2N/A const struct grub_dirhook_info *info __attribute__ ((unused)))
2N/A {
2N/A return 1;
2N/A }
2N/A
2N/A if (device->disk)
2N/A {
2N/A /* Make it sure not to have an infinite recursive calls. */
2N/A static int count = 0;
2N/A
2N/A for (p = grub_fs_list; p; p = p->next)
2N/A {
2N/A grub_dprintf ("fs", "Detecting %s...\n", p->name);
2N/A (p->dir) (device, "/", dummy_func);
2N/A if (grub_errno == GRUB_ERR_NONE)
2N/A return p;
2N/A
2N/A grub_error_push ();
2N/A grub_dprintf ("fs", "%s detection failed.\n", p->name);
2N/A grub_error_pop ();
2N/A
2N/A if (grub_errno != GRUB_ERR_BAD_FS)
2N/A return 0;
2N/A
2N/A grub_errno = GRUB_ERR_NONE;
2N/A }
2N/A
2N/A /* Let's load modules automatically. */
2N/A if (grub_fs_autoload_hook && count == 0)
2N/A {
2N/A count++;
2N/A
2N/A while (grub_fs_autoload_hook ())
2N/A {
2N/A p = grub_fs_list;
2N/A
2N/A (p->dir) (device, "/", dummy_func);
2N/A if (grub_errno == GRUB_ERR_NONE)
2N/A {
2N/A count--;
2N/A return p;
2N/A }
2N/A
2N/A if (grub_errno != GRUB_ERR_BAD_FS)
2N/A {
2N/A count--;
2N/A return 0;
2N/A }
2N/A
2N/A grub_errno = GRUB_ERR_NONE;
2N/A }
2N/A
2N/A count--;
2N/A }
2N/A }
2N/A else if (device->net && device->net->fs)
2N/A return device->net->fs;
2N/A
2N/A grub_error (GRUB_ERR_UNKNOWN_FS, "unknown filesystem");
2N/A return 0;
2N/A}
2N/A
2N/A
2N/A
2N/A/* Block list support routines. */
2N/A
2N/Astruct grub_fs_block
2N/A{
2N/A grub_disk_addr_t offset;
2N/A unsigned long length;
2N/A};
2N/A
2N/Astatic grub_err_t
2N/Agrub_fs_blocklist_open (grub_file_t file, const char *name)
2N/A{
2N/A char *p = (char *) name;
2N/A unsigned num = 0;
2N/A unsigned i;
2N/A grub_disk_t disk = file->device->disk;
2N/A struct grub_fs_block *blocks;
2N/A
2N/A /* First, count the number of blocks. */
2N/A do
2N/A {
2N/A num++;
2N/A p = grub_strchr (p, ',');
2N/A if (p)
2N/A p++;
2N/A }
2N/A while (p);
2N/A
2N/A /* Allocate a block list. */
2N/A blocks = grub_zalloc (sizeof (struct grub_fs_block) * (num + 1));
2N/A if (! blocks)
2N/A return 0;
2N/A
2N/A file->size = 0;
2N/A p = (char *) name;
2N/A for (i = 0; i < num; i++)
2N/A {
2N/A if (*p != '+')
2N/A {
2N/A blocks[i].offset = grub_strtoull (p, &p, 0);
2N/A if (grub_errno != GRUB_ERR_NONE || *p != '+')
2N/A {
2N/A grub_error (GRUB_ERR_BAD_FILENAME,
2N/A "invalid file name `%s'", name);
2N/A goto fail;
2N/A }
2N/A }
2N/A
2N/A p++;
2N/A blocks[i].length = grub_strtoul (p, &p, 0);
2N/A if (grub_errno != GRUB_ERR_NONE
2N/A || blocks[i].length == 0
2N/A || (*p && *p != ',' && ! grub_isspace (*p)))
2N/A {
2N/A grub_error (GRUB_ERR_BAD_FILENAME,
2N/A "invalid file name `%s'", name);
2N/A goto fail;
2N/A }
2N/A
2N/A if (disk->total_sectors < blocks[i].offset + blocks[i].length)
2N/A {
2N/A grub_error (GRUB_ERR_BAD_FILENAME, "beyond the total sectors");
2N/A goto fail;
2N/A }
2N/A
2N/A file->size += (blocks[i].length << GRUB_DISK_SECTOR_BITS);
2N/A p++;
2N/A }
2N/A
2N/A file->data = blocks;
2N/A
2N/A return GRUB_ERR_NONE;
2N/A
2N/A fail:
2N/A grub_free (blocks);
2N/A return grub_errno;
2N/A}
2N/A
2N/Astatic grub_ssize_t
2N/Agrub_fs_blocklist_read (grub_file_t file, char *buf, grub_size_t len)
2N/A{
2N/A struct grub_fs_block *p;
2N/A grub_disk_addr_t sector;
2N/A grub_off_t offset;
2N/A grub_ssize_t ret = 0;
2N/A
2N/A if (len > file->size - file->offset)
2N/A len = file->size - file->offset;
2N/A
2N/A sector = (file->offset >> GRUB_DISK_SECTOR_BITS);
2N/A offset = (file->offset & (GRUB_DISK_SECTOR_SIZE - 1));
2N/A for (p = file->data; p->length && len > 0; p++)
2N/A {
2N/A if (sector < p->length)
2N/A {
2N/A grub_size_t size;
2N/A
2N/A size = len;
2N/A if (((size + offset + GRUB_DISK_SECTOR_SIZE - 1)
2N/A >> GRUB_DISK_SECTOR_BITS) > p->length - sector)
2N/A size = ((p->length - sector) << GRUB_DISK_SECTOR_BITS) - offset;
2N/A
2N/A if (grub_disk_read (file->device->disk, p->offset + sector, offset,
2N/A size, buf) != GRUB_ERR_NONE)
2N/A return -1;
2N/A
2N/A ret += size;
2N/A len -= size;
2N/A sector -= ((size + offset) >> GRUB_DISK_SECTOR_BITS);
2N/A offset = ((size + offset) & (GRUB_DISK_SECTOR_SIZE - 1));
2N/A }
2N/A else
2N/A sector -= p->length;
2N/A }
2N/A
2N/A return ret;
2N/A}
2N/A
2N/Astruct grub_fs grub_fs_blocklist =
2N/A {
2N/A .name = "blocklist",
2N/A .dir = 0,
2N/A .open = grub_fs_blocklist_open,
2N/A .read = grub_fs_blocklist_read,
2N/A .close = 0,
2N/A .next = 0
2N/A };