/** * ext2file.c - devoptab file routines for EXT2-based devices. * * Copyright (c) 2006 Michael "Chishm" Chisholm * Copyright (c) 2009 Rhys "Shareese" Koedijk * Copyright (c) 2010 Dimok * * This program/include file 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 2 of the License, or * (at your option) any later version. * * This program/include file 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 this program; if not, write to the Free Software Foundation, * Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #include #include #include "ext2_fs.h" #include "ext2fs.h" #include "ext2_internal.h" #include "gekko_io.h" #include "mem_allocate.h" #include "partitions.h" bool ext2Mount(const char *name, const DISC_INTERFACE *interface, sec_t startSector, u32 cachePageCount, u32 cachePageSize, u32 flags) { errcode_t retval = -1; ext2_filsys fs = NULL; io_channel io_chan = NULL; gekko_fd *fd = NULL; ext2_vd * vd = NULL; // Sanity check if (!name || !interface) { errno = EINVAL; return false; } // Allocate the device driver descriptor fd = (gekko_fd*) mem_alloc(sizeof(gekko_fd)); if (!fd) goto cleanup; memset(fd, 0, sizeof(gekko_fd)); // Setup the device driver descriptor fd->interface = interface; fd->startSector = startSector; fd->sectorSize = 0; fd->sectorCount = 0; fd->cachePageCount = cachePageCount; fd->cachePageSize = cachePageSize; fs = mem_alloc(sizeof(struct struct_ext2_filsys)); if (!fs) { ext2_log_trace("no memory for fs\n"); errno = ENOMEM; goto cleanup; } memset(fs, 0, sizeof(struct struct_ext2_filsys)); io_chan = mem_alloc(sizeof(struct struct_io_channel)); if (!io_chan) { ext2_log_trace("no memory for io_chan\n"); errno = ENOMEM; goto cleanup; } memset(io_chan, 0, sizeof(struct struct_io_channel)); io_chan->magic = EXT2_ET_MAGIC_IO_CHANNEL; io_chan->manager = gekko_io_manager; io_chan->name = strdup(name); if(!io_chan->name) goto cleanup; io_chan->block_size = 1024; io_chan->read_error = 0; io_chan->write_error = 0; io_chan->refcount = 1; io_chan->private_data = fd; io_chan->flags = flags; retval = ext2fs_open2(io_chan->name, 0, io_chan->flags, 0, 0, io_chan, &fs); if(retval) { ext2_log_trace("error mounting %i\n", (int) retval); goto cleanup; } vd = mem_alloc(sizeof(ext2_vd)); if(!vd) { ext2_log_trace("no memory for vd\n"); goto cleanup; } // Initialise the volume descriptor ext2InitVolume(vd); vd->fs = fs; vd->io = io_chan; vd->root = EXT2_ROOT_INO; // Add the device to the devoptab table if (ext2AddDevice(name, vd)) { ext2DeinitVolume(vd); goto cleanup; } return true; cleanup: if(fd) mem_free(fd); if(io_chan) mem_free(io_chan); if(vd) mem_free(vd); if(fs) { ext2fs_close(fs); ext2fs_free(fs); } return false; } void ext2Unmount(const char *name) { ext2_vd *vd = NULL; // Get the devices volume descriptor vd = ext2GetVolume(name); if (!vd) return; // Remove the device from the devoptab table ext2RemoveDevice(name); // Deinitialise the volume descriptor ext2DeinitVolume(vd); // Unmount the volume ext2fs_close(vd->fs); ext2fs_free(vd->fs); //Free the io manager mem_free(vd->io->private_data); mem_free(vd->io); // Free the volume descriptor mem_free(vd); return; } const char *ext2GetVolumeName (const char *name) { if (!name) { errno = EINVAL; return NULL; } // Get the devices volume descriptor ext2_vd *vd = ext2GetVolume(name); if (!vd) { errno = ENODEV; return NULL; } return vd->fs->super->s_volume_name; } bool ext2SetVolumeName (const char *name, const char *volumeName) { // Sanity check if (!name || !volumeName) { errno = EINVAL; return false; } // Get the devices volume descriptor ext2_vd *vd = ext2GetVolume(name); if (!vd) { errno = ENODEV; return false; } // Lock ext2Lock(vd); int i; for(i = 0; i < 15 && *volumeName != 0; ++i, volumeName++) vd->fs->super->s_volume_name[i] = *volumeName; vd->fs->super->s_volume_name[i] = '\0'; ext2fs_mark_super_dirty(vd->fs); ext2Sync(vd, NULL); // Unlock ext2Unlock(vd); return true; } int ext2FindPartitions (const DISC_INTERFACE *interface, sec_t **out_partitions) { MASTER_BOOT_RECORD mbr; PARTITION_RECORD *partition = NULL; int partition_count = 0, ret = -1; sec_t part_lba = 0; sec_t * partitions = NULL; int i; union { u8 buffer[MAX_SECTOR_SIZE]; MASTER_BOOT_RECORD mbr; EXTENDED_BOOT_RECORD ebr; } sector; // Sanity check if (!interface) { errno = EINVAL; return -1; } if(!out_partitions) { errno = EINVAL; return -1; } // Start the device and check that it is inserted if (!interface->startup()) { errno = EIO; return -1; } if (!interface->isInserted()) { errno = EIO; return 0; } // Allocate 4 x max sector size in case of 4096 sector size u8 *buffer = (u8 *) mem_alloc(4 * MAX_SECTOR_SIZE); if(!buffer) { ext2_log_trace("no memory for superblock"); errno = ENOMEM; return -1; } // Super is always at offset SUPERBLOCK_OFFSET struct ext2_super_block * super = (struct ext2_super_block *) (buffer + SUPERBLOCK_OFFSET); //1024 bytes partitions = (sec_t *) malloc(sizeof(sec_t)); if(!partitions) { ext2_log_trace("no memory for partitions"); errno = ENOMEM; mem_free(buffer); return -1; } // Read the first sector on the device if (!interface->readSectors(0, 1, §or.buffer)) { errno = EIO; mem_free(partitions); mem_free(buffer); return -1; } // If this is the devices master boot record if (sector.mbr.signature == MBR_SIGNATURE) { memcpy(&mbr, §or, sizeof(MASTER_BOOT_RECORD)); // Search the partition table for all EXT2/3/4 partitions (max. 4 primary partitions) for (i = 0; i < 4; i++) { partition = &mbr.partitions[i]; part_lba = ext2fs_le32_to_cpu(mbr.partitions[i].lba_start); // Figure out what type of partition this is switch (partition->type) { // Ignore empty partitions case PARTITION_TYPE_EMPTY: continue; // EXT2/3/4 partition case PARTITION_TYPE_LINUX: // Read and validate the EXT partition if (interface->readSectors(part_lba, 4, buffer)) { if (ext2fs_le16_to_cpu(super->s_magic) == EXT2_SUPER_MAGIC) { partition_count++; sec_t * tmp = (sec_t *) realloc(partitions, partition_count*sizeof(sec_t)); if(!tmp) goto cleanup; partitions = tmp; partitions[partition_count-1] = part_lba; } } break; // DOS 3.3+ or Windows 95 extended partition case PARTITION_TYPE_DOS33_EXTENDED: case PARTITION_TYPE_WIN95_EXTENDED: { ext2_log_trace("Partition %i: Claims to be Extended\n", i + 1); // Walk the extended partition chain, finding all EXT partitions within it sec_t ebr_lba = part_lba; sec_t next_erb_lba = 0; do { // Read and validate the extended boot record if (interface->readSectors(ebr_lba + next_erb_lba, 1, §or)) { if (sector.ebr.signature == EBR_SIGNATURE) { ext2_log_trace("Logical Partition @ %d: %s type 0x%x\n", ebr_lba + next_erb_lba, sector.ebr.partition.status == PARTITION_STATUS_BOOTABLE ? "bootable (active)" : "non-bootable", sector.ebr.partition.type); // Get the start sector of the current partition // and the next extended boot record in the chain part_lba = ebr_lba + next_erb_lba + ext2fs_le32_to_cpu(sector.ebr.partition.lba_start); next_erb_lba = ext2fs_le32_to_cpu(sector.ebr.next_ebr.lba_start); // Check if this partition has a valid EXT boot record if (interface->readSectors(part_lba, 4, buffer)) { if (ext2fs_le16_to_cpu(super->s_magic) == EXT2_SUPER_MAGIC) { partition_count++; sec_t * tmp = (sec_t *) realloc(partitions, partition_count*sizeof(sec_t)); if(!tmp) goto cleanup; partitions = tmp; partitions[partition_count-1] = part_lba; } } } else next_erb_lba = 0; } } while (next_erb_lba); break; } // Unknown or unsupported partition type default: { // Check if this partition has a valid EXT boot record anyway, // it might be misrepresented due to a lazy partition editor if (interface->readSectors(part_lba, 4, buffer)) { if (ext2fs_le16_to_cpu(super->s_magic) == EXT2_SUPER_MAGIC) { partition_count++; sec_t * tmp = (sec_t *) realloc(partitions, partition_count*sizeof(sec_t)); if(!tmp) goto cleanup; partitions = tmp; partitions[partition_count-1] = part_lba; } } break; } } } // Else it is assumed this device has no master boot record } else { ext2_log_trace("No Master Boot Record was found!\n"); // As a last-ditched effort, search the first 64 sectors of the device for stray EXT partitions for (i = 1; i < 64; i++) { if (interface->readSectors(i, 4, buffer)) { if (ext2fs_le16_to_cpu(super->s_magic) == EXT2_SUPER_MAGIC) { partition_count++; sec_t * tmp = (sec_t *) realloc(partitions, partition_count*sizeof(sec_t)); if(!tmp) goto cleanup; partitions = tmp; partitions[partition_count-1] = i; } } } } // Return the found partitions (if any) if (partition_count > 0) { *out_partitions = partitions; ret = partition_count; } cleanup: if(partitions && partition_count == 0) mem_free(partitions); if(buffer) mem_free(buffer); return ret; }