2012-01-21 21:57:41 +01:00
|
|
|
/**
|
|
|
|
* 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 <errno.h>
|
|
|
|
#include <string.h>
|
|
|
|
#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;
|
|
|
|
|
2012-04-22 19:40:21 +02:00
|
|
|
retval = ext2fs_open2(io_chan->name, 0, io_chan->flags, 0, 0, io_chan, &fs);
|
2012-01-21 21:57:41 +01:00
|
|
|
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 {
|
2012-04-22 19:40:21 +02:00
|
|
|
u8 buffer[MAX_SECTOR_SIZE];
|
2012-01-21 21:57:41 +01:00
|
|
|
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;
|
|
|
|
}
|
|
|
|
|
2012-04-22 19:40:21 +02:00
|
|
|
// Allocate 4 x max sector size in case of 4096 sector size
|
|
|
|
u8 *buffer = (u8 *) mem_alloc(4 * MAX_SECTOR_SIZE);
|
|
|
|
if(!buffer)
|
2012-01-21 21:57:41 +01:00
|
|
|
{
|
|
|
|
ext2_log_trace("no memory for superblock");
|
|
|
|
errno = ENOMEM;
|
|
|
|
return -1;
|
|
|
|
}
|
2012-04-22 19:40:21 +02:00
|
|
|
// Super is always at offset SUPERBLOCK_OFFSET
|
|
|
|
struct ext2_super_block * super = (struct ext2_super_block *) (buffer + SUPERBLOCK_OFFSET); //1024 bytes
|
2012-01-21 21:57:41 +01:00
|
|
|
|
|
|
|
partitions = (sec_t *) malloc(sizeof(sec_t));
|
|
|
|
if(!partitions)
|
|
|
|
{
|
|
|
|
ext2_log_trace("no memory for partitions");
|
|
|
|
errno = ENOMEM;
|
2012-04-22 19:40:21 +02:00
|
|
|
mem_free(buffer);
|
2012-01-21 21:57:41 +01:00
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
// Read the first sector on the device
|
|
|
|
if (!interface->readSectors(0, 1, §or.buffer)) {
|
|
|
|
errno = EIO;
|
|
|
|
mem_free(partitions);
|
2012-04-22 19:40:21 +02:00
|
|
|
mem_free(buffer);
|
2012-01-21 21:57:41 +01:00
|
|
|
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
|
2012-04-22 19:40:21 +02:00
|
|
|
if (interface->readSectors(part_lba, 4, buffer))
|
2012-01-21 21:57:41 +01:00
|
|
|
{
|
|
|
|
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
|
2012-04-22 19:40:21 +02:00
|
|
|
if (interface->readSectors(part_lba, 4, buffer))
|
2012-01-21 21:57:41 +01:00
|
|
|
{
|
|
|
|
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
|
2012-04-22 19:40:21 +02:00
|
|
|
if (interface->readSectors(part_lba, 4, buffer))
|
2012-01-21 21:57:41 +01:00
|
|
|
{
|
|
|
|
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++)
|
|
|
|
{
|
2012-04-22 19:40:21 +02:00
|
|
|
if (interface->readSectors(i, 4, buffer))
|
2012-01-21 21:57:41 +01:00
|
|
|
{
|
|
|
|
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);
|
2012-04-22 19:40:21 +02:00
|
|
|
if(buffer)
|
|
|
|
mem_free(buffer);
|
2012-01-21 21:57:41 +01:00
|
|
|
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|