/* fs.c - filesystem manager */ /* * VAS_EBOOT -- GRand Unified Bootloader * Copyright (C) 2002,2005,2007 Free Software Foundation, Inc. * * VAS_EBOOT 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. * * VAS_EBOOT 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 VAS_EBOOT. If not, see . */ #include #include #include #include #include #include #include #include #include #include VasEBoot_fs_t VasEBoot_fs_list = 0; VasEBoot_fs_autoload_hook_t VasEBoot_fs_autoload_hook = 0; /* Helper for VasEBoot_fs_probe. */ static int probe_dummy_iter (const char *filename __attribute__ ((unused)), const struct VasEBoot_dirhook_info *info __attribute__ ((unused)), void *data __attribute__ ((unused))) { return 1; } VasEBoot_fs_t VasEBoot_fs_probe (VasEBoot_device_t device) { VasEBoot_fs_t p; if (device->disk) { /* Make it sure not to have an infinite recursive calls. */ static int count = 0; for (p = VasEBoot_fs_list; p; p = p->next) { VasEBoot_dprintf ("fs", "Detecting %s...\n", p->name); /* This is evil: newly-created just mounted BtrFS after copying all VAS_EBOOT files has a very peculiar unrecoverable corruption which will be fixed at sync but we'd rather not do a global sync and syncing just files doesn't seem to help. Relax the check for this time. */ #ifdef VAS_EBOOT_UTIL if (VasEBoot_strcmp (p->name, "btrfs") == 0) { char *label = 0; p->fs_uuid (device, &label); if (label) VasEBoot_free (label); } else #endif (p->fs_dir) (device, "/", probe_dummy_iter, NULL); if (VasEBoot_errno == VAS_EBOOT_ERR_NONE) return p; VasEBoot_error_push (); /* The VasEBoot_error_push() does not touch VasEBoot_errmsg. */ VasEBoot_dprintf ("fs", _("error: %s.\n"), VasEBoot_errmsg); VasEBoot_dprintf ("fs", "%s detection failed.\n", p->name); VasEBoot_error_pop (); if (VasEBoot_errno != VAS_EBOOT_ERR_BAD_FS && VasEBoot_errno != VAS_EBOOT_ERR_OUT_OF_RANGE) return 0; VasEBoot_errno = VAS_EBOOT_ERR_NONE; } /* Let's load modules automatically. */ if (VasEBoot_fs_autoload_hook && count == 0) { count++; while (VasEBoot_fs_autoload_hook ()) { p = VasEBoot_fs_list; (p->fs_dir) (device, "/", probe_dummy_iter, NULL); if (VasEBoot_errno == VAS_EBOOT_ERR_NONE) { count--; return p; } if (VasEBoot_errno != VAS_EBOOT_ERR_BAD_FS && VasEBoot_errno != VAS_EBOOT_ERR_OUT_OF_RANGE) { count--; return 0; } VasEBoot_errno = VAS_EBOOT_ERR_NONE; } count--; } } else if (device->net && device->net->fs) return device->net->fs; VasEBoot_error (VAS_EBOOT_ERR_UNKNOWN_FS, N_("unknown filesystem")); return 0; } /* Block list support routines. */ struct VasEBoot_fs_block { VasEBoot_disk_addr_t offset; VasEBoot_disk_addr_t length; }; static VasEBoot_err_t VasEBoot_fs_blocklist_open (VasEBoot_file_t file, const char *name) { const char *p = name; unsigned num = 0; unsigned i; VasEBoot_disk_t disk = file->device->disk; struct VasEBoot_fs_block *blocks; VasEBoot_size_t max_sectors; /* First, count the number of blocks. */ do { num++; p = VasEBoot_strchr (p, ','); if (p) p++; } while (p); /* Allocate a block list. */ blocks = VasEBoot_calloc (num + 1, sizeof (struct VasEBoot_fs_block)); if (! blocks) return 0; file->size = 0; max_sectors = VasEBoot_disk_from_native_sector (disk, disk->total_sectors); p = (char *) name; for (i = 0; i < num; i++) { if (*p != '+') { blocks[i].offset = VasEBoot_strtoull (p, &p, 0); if (VasEBoot_errno != VAS_EBOOT_ERR_NONE || *p != '+') { VasEBoot_error (VAS_EBOOT_ERR_BAD_FILENAME, N_("invalid file name `%s'"), name); goto fail; } } p++; if (*p == '\0' || *p == ',') blocks[i].length = max_sectors - blocks[i].offset; else blocks[i].length = VasEBoot_strtoul (p, &p, 0); if (VasEBoot_errno != VAS_EBOOT_ERR_NONE || blocks[i].length == 0 || (*p && *p != ',' && ! VasEBoot_isspace (*p))) { VasEBoot_error (VAS_EBOOT_ERR_BAD_FILENAME, N_("invalid file name `%s'"), name); goto fail; } if (max_sectors < blocks[i].offset + blocks[i].length) { VasEBoot_error (VAS_EBOOT_ERR_BAD_FILENAME, "beyond the total sectors"); goto fail; } file->size += (blocks[i].length << VAS_EBOOT_DISK_SECTOR_BITS); p++; } file->data = blocks; return VAS_EBOOT_ERR_NONE; fail: VasEBoot_free (blocks); return VasEBoot_errno; } static VasEBoot_ssize_t VasEBoot_fs_blocklist_read (VasEBoot_file_t file, char *buf, VasEBoot_size_t len) { struct VasEBoot_fs_block *p; VasEBoot_disk_addr_t sector; VasEBoot_off_t offset; VasEBoot_ssize_t ret = 0; VasEBoot_disk_t disk = file->device->disk; if (len > file->size - file->offset) len = file->size - file->offset; sector = (file->offset >> VAS_EBOOT_DISK_SECTOR_BITS); offset = (file->offset & (VAS_EBOOT_DISK_SECTOR_SIZE - 1)); disk->read_hook = file->read_hook; disk->read_hook_data = file->read_hook_data; for (p = file->data; p->length && len > 0; p++) { if (sector < p->length) { VasEBoot_size_t size; size = len; if (((size + offset + VAS_EBOOT_DISK_SECTOR_SIZE - 1) >> VAS_EBOOT_DISK_SECTOR_BITS) > p->length - sector) size = ((p->length - sector) << VAS_EBOOT_DISK_SECTOR_BITS) - offset; if (VasEBoot_disk_read (disk, p->offset + sector, offset, size, buf) != VAS_EBOOT_ERR_NONE) { ret = -1; break; } ret += size; len -= size; sector -= ((size + offset) >> VAS_EBOOT_DISK_SECTOR_BITS); offset = ((size + offset) & (VAS_EBOOT_DISK_SECTOR_SIZE - 1)); } else sector -= p->length; } disk->read_hook = NULL; disk->read_hook_data = NULL; return ret; } struct VasEBoot_fs VasEBoot_fs_blocklist = { .name = "blocklist", .fs_dir = 0, .fs_open = VasEBoot_fs_blocklist_open, .fs_read = VasEBoot_fs_blocklist_read, .fs_close = 0, .next = 0 };