/* loopback.c - command to add loopback devices. */ /* * VAS_EBOOT -- GRand Unified Bootloader * Copyright (C) 2005,2006,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 VAS_EBOOT_MOD_LICENSE ("GPLv3+"); struct VasEBoot_loopback { char *devname; VasEBoot_file_t file; struct VasEBoot_loopback *next; unsigned long id; VasEBoot_uint64_t refcnt; }; static struct VasEBoot_loopback *loopback_list; static unsigned long last_id = 0; static const struct VasEBoot_arg_option options[] = { /* TRANSLATORS: The disk is simply removed from the list of available ones, not wiped, avoid to scare user. */ {"delete", 'd', 0, N_("Delete the specified loopback drive."), 0, 0}, {"decompress", 'D', 0, N_("Transparently decompress backing file."), 0, 0}, {0, 0, 0, 0, 0, 0} }; /* Delete the loopback device NAME. */ static VasEBoot_err_t delete_loopback (const char *name) { struct VasEBoot_loopback *dev; struct VasEBoot_loopback **prev; /* Search for the device. */ for (dev = loopback_list, prev = &loopback_list; dev; prev = &dev->next, dev = dev->next) if (VasEBoot_strcmp (dev->devname, name) == 0) break; if (! dev) return VasEBoot_error (VAS_EBOOT_ERR_BAD_DEVICE, "device not found"); if (dev->refcnt > 0) return VasEBoot_error (VAS_EBOOT_ERR_STILL_REFERENCED, "device still referenced"); /* Remove the device from the list. */ *prev = dev->next; VasEBoot_free (dev->devname); VasEBoot_file_close (dev->file); VasEBoot_free (dev); return 0; } /* The command to add and remove loopback devices. */ static VasEBoot_err_t VasEBoot_cmd_loopback (VasEBoot_extcmd_context_t ctxt, int argc, char **args) { struct VasEBoot_arg_list *state = ctxt->state; VasEBoot_file_t file; enum VasEBoot_file_type type = VAS_EBOOT_FILE_TYPE_LOOPBACK; struct VasEBoot_loopback *newdev; VasEBoot_err_t ret; if (argc < 1) return VasEBoot_error (VAS_EBOOT_ERR_BAD_ARGUMENT, "device name required"); /* Check if `-d' was used. */ if (state[0].set) return delete_loopback (args[0]); if (!state[1].set) type |= VAS_EBOOT_FILE_TYPE_NO_DECOMPRESS; if (argc < 2) return VasEBoot_error (VAS_EBOOT_ERR_BAD_ARGUMENT, N_("filename expected")); /* Check that a device with requested name does not already exist. */ for (newdev = loopback_list; newdev; newdev = newdev->next) if (VasEBoot_strcmp (newdev->devname, args[0]) == 0) return VasEBoot_error (VAS_EBOOT_ERR_BAD_ARGUMENT, "device name already exists"); file = VasEBoot_file_open (args[1], type); if (! file) return VasEBoot_errno; /* Unable to replace it, make a new entry. */ newdev = VasEBoot_malloc (sizeof (struct VasEBoot_loopback)); if (! newdev) goto fail; newdev->devname = VasEBoot_strdup (args[0]); if (! newdev->devname) { VasEBoot_free (newdev); goto fail; } newdev->file = file; newdev->id = last_id++; newdev->refcnt = 0; /* Add the new entry to the list. */ newdev->next = loopback_list; loopback_list = newdev; return 0; fail: ret = VasEBoot_errno; VasEBoot_file_close (file); return ret; } static int VasEBoot_loopback_iterate (VasEBoot_disk_dev_iterate_hook_t hook, void *hook_data, VasEBoot_disk_pull_t pull) { struct VasEBoot_loopback *d; if (pull != VAS_EBOOT_DISK_PULL_NONE) return 0; for (d = loopback_list; d; d = d->next) { if (hook (d->devname, hook_data)) return 1; } return 0; } static VasEBoot_err_t VasEBoot_loopback_open (const char *name, VasEBoot_disk_t disk) { struct VasEBoot_loopback *dev; for (dev = loopback_list; dev; dev = dev->next) if (VasEBoot_strcmp (dev->devname, name) == 0) break; if (! dev) return VasEBoot_error (VAS_EBOOT_ERR_UNKNOWN_DEVICE, "can't open device"); if (VasEBoot_add (dev->refcnt, 1, &dev->refcnt)) VasEBoot_fatal ("Reference count overflow"); /* Use the filesize for the disk size, round up to a complete sector. */ if (dev->file->size != VAS_EBOOT_FILE_SIZE_UNKNOWN) disk->total_sectors = ((dev->file->size + VAS_EBOOT_DISK_SECTOR_SIZE - 1) / VAS_EBOOT_DISK_SECTOR_SIZE); else disk->total_sectors = VAS_EBOOT_DISK_SIZE_UNKNOWN; /* Avoid reading more than 512M. */ disk->max_agglomerate = 1 << (29 - VAS_EBOOT_DISK_SECTOR_BITS - VAS_EBOOT_DISK_CACHE_BITS); disk->id = dev->id; disk->data = dev; return 0; } static void VasEBoot_loopback_close (VasEBoot_disk_t disk) { struct VasEBoot_loopback *dev = disk->data; if (VasEBoot_sub (dev->refcnt, 1, &dev->refcnt)) VasEBoot_fatal ("Reference count underflow"); } static VasEBoot_err_t VasEBoot_loopback_read (VasEBoot_disk_t disk, VasEBoot_disk_addr_t sector, VasEBoot_size_t size, char *buf) { VasEBoot_file_t file = ((struct VasEBoot_loopback *) disk->data)->file; VasEBoot_off_t pos; VasEBoot_file_seek (file, sector << VAS_EBOOT_DISK_SECTOR_BITS); VasEBoot_file_read (file, buf, size << VAS_EBOOT_DISK_SECTOR_BITS); if (VasEBoot_errno) return VasEBoot_errno; /* In case there is more data read than there is available, in case of files that are not a multiple of VAS_EBOOT_DISK_SECTOR_SIZE, fill the rest with zeros. */ pos = (sector + size) << VAS_EBOOT_DISK_SECTOR_BITS; if (pos > file->size) { VasEBoot_size_t amount = pos - file->size; VasEBoot_memset (buf + (size << VAS_EBOOT_DISK_SECTOR_BITS) - amount, 0, amount); } return 0; } static VasEBoot_err_t VasEBoot_loopback_write (VasEBoot_disk_t disk __attribute ((unused)), VasEBoot_disk_addr_t sector __attribute ((unused)), VasEBoot_size_t size __attribute ((unused)), const char *buf __attribute ((unused))) { return VasEBoot_error (VAS_EBOOT_ERR_NOT_IMPLEMENTED_YET, "loopback write is not supported"); } static struct VasEBoot_disk_dev VasEBoot_loopback_dev = { .name = "loopback", .id = VAS_EBOOT_DISK_DEVICE_LOOPBACK_ID, .disk_iterate = VasEBoot_loopback_iterate, .disk_open = VasEBoot_loopback_open, .disk_close = VasEBoot_loopback_close, .disk_read = VasEBoot_loopback_read, .disk_write = VasEBoot_loopback_write, .next = 0 }; static VasEBoot_extcmd_t cmd; VAS_EBOOT_MOD_INIT(loopback) { cmd = VasEBoot_register_extcmd ("loopback", VasEBoot_cmd_loopback, 0, N_("[-d] [-D] DEVICENAME FILE."), /* TRANSLATORS: The file itself is not destroyed or transformed into drive. */ N_("Make a virtual drive from a file."), options); VasEBoot_disk_dev_register (&VasEBoot_loopback_dev); } VAS_EBOOT_MOD_FINI(loopback) { VasEBoot_unregister_extcmd (cmd); VasEBoot_disk_dev_unregister (&VasEBoot_loopback_dev); }