usbloadergx/source/libntfs/ntfs.c
dimok321 64f8406b07 *Mem2 fix
*updated libntfs (write fix)
*updated libfat
*lots of changes in the startup code, removed almost everything. This might cause problems for some drives at loading the gamelist and needs to be adjusted later but better this time. more cleanup is needed in main.cpp and will come.
*using libogc sd/usb for config loading and reload to cIOS afterwards
*added missing boothomebrew stuff pune forgot

NOTE: From now on we will be doing a lot of revs which we won't be compiling and releasing. This revs are officially not available for public so don't making issues regarding those revs. Those will be closed right away. We need first to cleanup a lot of crap and update loader to new standards before releasing stuff again.
2010-09-16 19:59:41 +00:00

724 lines
21 KiB
C

/**
* ntfs.c - Simple functionality for startup, mounting and unmounting of NTFS-based devices.
*
* Copyright (c) 2009 Rhys "Shareese" Koedijk
* Copyright (c) 2006 Michael "Chishm" Chisholm
*
* 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
*/
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif
#ifdef HAVE_STDLIB_H
#include <stdlib.h>
#endif
#ifdef HAVE_ERRNO_H
#include <errno.h>
#endif
#ifdef HAVE_STRING_H
#include <string.h>
#endif
#include "ntfs.h"
#include "ntfsinternal.h"
#include "ntfsfile.h"
#include "ntfsdir.h"
#include "gekko_io.h"
#include "cache.h"
// NTFS device driver devoptab
static const devoptab_t devops_ntfs = {
NULL, /* Device name */
sizeof (ntfs_file_state),
ntfs_open_r,
ntfs_close_r,
ntfs_write_r,
ntfs_read_r,
ntfs_seek_r,
ntfs_fstat_r,
ntfs_stat_r,
ntfs_link_r,
ntfs_unlink_r,
ntfs_chdir_r,
ntfs_rename_r,
ntfs_mkdir_r,
sizeof (ntfs_dir_state),
ntfs_diropen_r,
ntfs_dirreset_r,
ntfs_dirnext_r,
ntfs_dirclose_r,
ntfs_statvfs_r,
ntfs_ftruncate_r,
ntfs_fsync_r,
NULL /* Device data */
};
void ntfsInit (void)
{
static bool isInit = false;
// Initialise ntfs-3g (if not already done so)
if (!isInit) {
isInit = true;
// Set the log handler
#ifdef NTFS_ENABLE_LOG
ntfs_log_set_handler(ntfs_log_handler_stderr);
#else
ntfs_log_set_handler(ntfs_log_handler_null);
#endif
// Set our current local
ntfs_set_locale();
}
return;
}
int ntfsFindPartitions (const DISC_INTERFACE *interface, sec_t **partitions)
{
MASTER_BOOT_RECORD mbr;
PARTITION_RECORD *partition = NULL;
sec_t partition_starts[NTFS_MAX_PARTITIONS] = {0};
int partition_count = 0;
sec_t part_lba = 0;
int i;
union {
u8 buffer[512];
MASTER_BOOT_RECORD mbr;
EXTENDED_BOOT_RECORD ebr;
NTFS_BOOT_SECTOR boot;
} sector;
// Sanity check
if (!interface) {
errno = EINVAL;
return -1;
}
if (!partitions)
return 0;
// Initialise ntfs-3g
ntfsInit();
// Start the device and check that it is inserted
if (!interface->startup()) {
errno = EIO;
return -1;
}
if (!interface->isInserted()) {
return 0;
}
// Read the first sector on the device
if (!interface->readSectors(0, 1, &sector.buffer)) {
errno = EIO;
return -1;
}
// If this is the devices master boot record
if (sector.mbr.signature == MBR_SIGNATURE) {
memcpy(&mbr, &sector, sizeof(MASTER_BOOT_RECORD));
ntfs_log_debug("Valid Master Boot Record found\n");
// Search the partition table for all NTFS partitions (max. 4 primary partitions)
for (i = 0; i < 4; i++) {
partition = &mbr.partitions[i];
part_lba = le32_to_cpu(mbr.partitions[i].lba_start);
ntfs_log_debug("Partition %i: %s, sector %d, type 0x%x\n", i + 1,
partition->status == PARTITION_STATUS_BOOTABLE ? "bootable (active)" : "non-bootable",
part_lba, partition->type);
// Figure out what type of partition this is
switch (partition->type) {
// Ignore empty partitions
case PARTITION_TYPE_EMPTY:
continue;
// NTFS partition
case PARTITION_TYPE_NTFS: {
ntfs_log_debug("Partition %i: Claims to be NTFS\n", i + 1);
// Read and validate the NTFS partition
if (interface->readSectors(part_lba, 1, &sector)) {
if (sector.boot.oem_id == NTFS_OEM_ID) {
ntfs_log_debug("Partition %i: Valid NTFS boot sector found\n", i + 1);
if (partition_count < NTFS_MAX_PARTITIONS) {
partition_starts[partition_count] = part_lba;
partition_count++;
}
} else {
ntfs_log_debug("Partition %i: Invalid NTFS boot sector, not actually NTFS\n", i + 1);
}
}
break;
}
// DOS 3.3+ or Windows 95 extended partition
case PARTITION_TYPE_DOS33_EXTENDED:
case PARTITION_TYPE_WIN95_EXTENDED: {
ntfs_log_debug("Partition %i: Claims to be Extended\n", i + 1);
// Walk the extended partition chain, finding all NTFS 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, &sector)) {
if (sector.ebr.signature == EBR_SIGNATURE) {
ntfs_log_debug("Logical Partition @ %d: 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 + le32_to_cpu(sector.ebr.partition.lba_start);
next_erb_lba = le32_to_cpu(sector.ebr.next_ebr.lba_start);
// Check if this partition has a valid NTFS boot record
if (interface->readSectors(part_lba, 1, &sector)) {
if (sector.boot.oem_id == NTFS_OEM_ID) {
ntfs_log_debug("Logical Partition @ %d: Valid NTFS boot sector found\n", part_lba);
if(sector.ebr.partition.type != PARTITION_TYPE_NTFS) {
ntfs_log_warning("Logical Partition @ %d: Is NTFS but type is 0x%x; 0x%x was expected\n", part_lba, sector.ebr.partition.type, PARTITION_TYPE_NTFS);
}
if (partition_count < NTFS_MAX_PARTITIONS) {
partition_starts[partition_count] = part_lba;
partition_count++;
}
}
}
} else {
next_erb_lba = 0;
}
}
} while (next_erb_lba);
break;
}
// Unknown or unsupported partition type
default: {
// Check if this partition has a valid NTFS boot record anyway,
// it might be misrepresented due to a lazy partition editor
if (interface->readSectors(part_lba, 1, &sector)) {
if (sector.boot.oem_id == NTFS_OEM_ID) {
ntfs_log_debug("Partition %i: Valid NTFS boot sector found\n", i + 1);
if(partition->type != PARTITION_TYPE_NTFS) {
ntfs_log_warning("Partition %i: Is NTFS but type is 0x%x; 0x%x was expected\n", i + 1, partition->type, PARTITION_TYPE_NTFS);
}
if (partition_count < NTFS_MAX_PARTITIONS) {
partition_starts[partition_count] = part_lba;
partition_count++;
}
}
}
break;
}
}
}
// Else it is assumed this device has no master boot record
} else {
ntfs_log_debug("No Master Boot Record was found!\n");
// As a last-ditched effort, search the first 64 sectors of the device for stray NTFS partitions
for (i = 0; i < 64; i++) {
if (interface->readSectors(i, 1, &sector)) {
if (sector.boot.oem_id == NTFS_OEM_ID) {
ntfs_log_debug("Valid NTFS boot sector found at sector %d!\n", i);
if (partition_count < NTFS_MAX_PARTITIONS) {
partition_starts[partition_count] = i;
partition_count++;
}
}
}
}
}
// Shutdown the device
/*interface->shutdown();*/
// Return the found partitions (if any)
if (partition_count > 0) {
*partitions = (sec_t*)ntfs_alloc(sizeof(sec_t) * partition_count);
if (*partitions) {
memcpy(*partitions, &partition_starts, sizeof(sec_t) * partition_count);
return partition_count;
}
}
return 0;
}
int ntfsMountAll (ntfs_md **mounts, u32 flags)
{
const INTERFACE_ID *discs = ntfsGetDiscInterfaces();
const INTERFACE_ID *disc = NULL;
ntfs_md mount_points[NTFS_MAX_MOUNTS];
sec_t *partitions = NULL;
int mount_count = 0;
int partition_count = 0;
char name[128];
int i, j, k;
// Initialise ntfs-3g
ntfsInit();
// Find and mount all NTFS partitions on all known devices
for (i = 0; discs[i].name != NULL && discs[i].interface != NULL; i++) {
disc = &discs[i];
partition_count = ntfsFindPartitions(disc->interface, &partitions);
if (partition_count > 0 && partitions) {
for (j = 0, k = 0; j < partition_count; j++) {
// Find the next unused mount name
do {
sprintf(name, "%s%i", NTFS_MOUNT_PREFIX, k++);
if (k >= NTFS_MAX_MOUNTS) {
ntfs_free(partitions);
errno = EADDRNOTAVAIL;
return -1;
}
} while (ntfsGetDevice(name, false));
// Mount the partition
if (mount_count < NTFS_MAX_MOUNTS) {
if (ntfsMount(name, disc->interface, partitions[j], CACHE_DEFAULT_PAGE_SIZE, CACHE_DEFAULT_PAGE_COUNT, flags)) {
strcpy(mount_points[mount_count].name, name);
mount_points[mount_count].interface = disc->interface;
mount_points[mount_count].startSector = partitions[j];
mount_count++;
}
}
}
ntfs_free(partitions);
}
}
// Return the mounts (if any)
if (mount_count > 0 && mounts) {
*mounts = (ntfs_md*)ntfs_alloc(sizeof(ntfs_md) * mount_count);
if (*mounts) {
memcpy(*mounts, &mount_points, sizeof(ntfs_md) * mount_count);
return mount_count;
}
}
return 0;
}
int ntfsMountDevice (const DISC_INTERFACE *interface, ntfs_md **mounts, u32 flags)
{
const INTERFACE_ID *discs = ntfsGetDiscInterfaces();
const INTERFACE_ID *disc = NULL;
ntfs_md mount_points[NTFS_MAX_MOUNTS];
sec_t *partitions = NULL;
int mount_count = 0;
int partition_count = 0;
char name[128];
int i, j, k;
// Sanity check
if (!interface) {
errno = EINVAL;
return -1;
}
// Initialise ntfs-3g
ntfsInit();
// Find the specified device then find and mount all NTFS partitions on it
for (i = 0; discs[i].name != NULL && discs[i].interface != NULL; i++) {
if (discs[i].interface == interface) {
disc = &discs[i];
partition_count = ntfsFindPartitions(disc->interface, &partitions);
if (partition_count > 0 && partitions) {
for (j = 0, k = 0; j < partition_count; j++) {
// Find the next unused mount name
do {
sprintf(name, "%s%i", NTFS_MOUNT_PREFIX, k++);
if (k >= NTFS_MAX_MOUNTS) {
ntfs_free(partitions);
errno = EADDRNOTAVAIL;
return -1;
}
} while (ntfsGetDevice(name, false));
// Mount the partition
if (mount_count < NTFS_MAX_MOUNTS) {
if (ntfsMount(name, disc->interface, partitions[j], CACHE_DEFAULT_PAGE_SIZE, CACHE_DEFAULT_PAGE_COUNT, flags)) {
strcpy(mount_points[mount_count].name, name);
mount_points[mount_count].interface = disc->interface;
mount_points[mount_count].startSector = partitions[j];
mount_count++;
}
}
}
ntfs_free(partitions);
}
break;
}
}
// If we couldn't find the device then return with error status
if (!disc) {
errno = ENODEV;
return -1;
}
// Return the mounts (if any)
if (mount_count > 0 && mounts) {
*mounts = (ntfs_md*)ntfs_alloc(sizeof(ntfs_md) * mount_count);
if (*mounts) {
memcpy(*mounts, &mount_points, sizeof(ntfs_md) * mount_count);
return mount_count;
}
}
return 0;
}
bool ntfsMount (const char *name, const DISC_INTERFACE *interface, sec_t startSector, u32 cachePageCount, u32 cachePageSize, u32 flags)
{
ntfs_vd *vd = NULL;
gekko_fd *fd = NULL;
// Sanity check
if (!name || !interface) {
errno = EINVAL;
return false;
}
// Initialise ntfs-3g
ntfsInit();
// Check that the requested mount name is free
if (ntfsGetDevice(name, false)) {
errno = EADDRINUSE;
return false;
}
// Check that we can at least read from this device
if (!(interface->features & FEATURE_MEDIUM_CANREAD)) {
errno = EPERM;
return false;
}
// Allocate the volume descriptor
vd = (ntfs_vd*)ntfs_alloc(sizeof(ntfs_vd));
if (!vd) {
errno = ENOMEM;
return false;
}
// Setup the volume descriptor
vd->id = interface->ioType;
vd->flags = 0;
vd->uid = 0;
vd->gid = 0;
vd->fmask = 0;
vd->dmask = 0;
vd->atime = ((flags & NTFS_UPDATE_ACCESS_TIMES) ? ATIME_ENABLED : ATIME_DISABLED);
vd->showHiddenFiles = (flags & NTFS_SHOW_HIDDEN_FILES);
vd->showSystemFiles = (flags & NTFS_SHOW_SYSTEM_FILES);
// Allocate the device driver descriptor
fd = (gekko_fd*)ntfs_alloc(sizeof(gekko_fd));
if (!fd) {
ntfs_free(vd);
errno = ENOMEM;
return false;
}
// Setup the device driver descriptor
fd->interface = interface;
fd->startSector = startSector;
fd->sectorSize = 0;
fd->sectorCount = 0;
fd->cachePageCount = cachePageCount;
fd->cachePageSize = cachePageSize;
// Allocate the device driver
vd->dev = ntfs_device_alloc(name, 0, &ntfs_device_gekko_io_ops, fd);
if (!vd->dev) {
ntfs_free(fd);
ntfs_free(vd);
return false;
}
// Build the mount flags
if (flags & NTFS_READ_ONLY)
vd->flags |= MS_RDONLY;
else
{
if (!(interface->features & FEATURE_MEDIUM_CANWRITE))
vd->flags |= MS_RDONLY;
if ((interface->features & FEATURE_MEDIUM_CANREAD) && (interface->features & FEATURE_MEDIUM_CANWRITE))
vd->flags |= MS_EXCLUSIVE;
}
if (flags & NTFS_RECOVER)
vd->flags |= MS_RECOVER;
if (flags & NTFS_IGNORE_HIBERFILE)
vd->flags |= MS_IGNORE_HIBERFILE;
if (vd->flags & MS_RDONLY)
ntfs_log_debug("Mounting \"%s\" as read-only\n", name);
// Mount the device
vd->vol = ntfs_device_mount(vd->dev, vd->flags);
if (!vd->vol) {
switch(ntfs_volume_error(errno)) {
case NTFS_VOLUME_NOT_NTFS: errno = EINVALPART; break;
case NTFS_VOLUME_CORRUPT: errno = EINVALPART; break;
case NTFS_VOLUME_HIBERNATED: errno = EHIBERNATED; break;
case NTFS_VOLUME_UNCLEAN_UNMOUNT: errno = EDIRTY; break;
default: errno = EINVAL; break;
}
ntfs_device_free(vd->dev);
ntfs_free(vd);
return false;
}
// Initialise the volume descriptor
if (ntfsInitVolume(vd)) {
ntfs_umount(vd->vol, true);
ntfs_free(vd);
return false;
}
// Add the device to the devoptab table
if (ntfsAddDevice(name, vd)) {
ntfsDeinitVolume(vd);
ntfs_umount(vd->vol, true);
ntfs_free(vd);
return false;
}
return true;
}
void ntfsUnmount (const char *name, bool force)
{
ntfs_vd *vd = NULL;
// Get the devices volume descriptor
vd = ntfsGetVolume(name);
if (!vd)
return;
// Remove the device from the devoptab table
ntfsRemoveDevice(name);
// Deinitialise the volume descriptor
ntfsDeinitVolume(vd);
// Unmount the volume
ntfs_umount(vd->vol, force);
// Free the volume descriptor
ntfs_free(vd);
return;
}
const char *ntfsGetVolumeName (const char *name)
{
ntfs_vd *vd = NULL;
//ntfs_attr *na = NULL;
//ntfschar *ulabel = NULL;
//char *volumeName = NULL;
// Sanity check
if (!name) {
errno = EINVAL;
return NULL;
}
// Get the devices volume descriptor
vd = ntfsGetVolume(name);
if (!vd) {
errno = ENODEV;
return NULL;
}
return vd->vol->vol_name;
/*
// If the volume name has already been cached then just use that
if (vd->name[0])
return vd->name;
// Lock
ntfsLock(vd);
// Check if the volume name attribute exists
na = ntfs_attr_open(vd->vol->vol_ni, AT_VOLUME_NAME, NULL, 0);
if (!na) {
ntfsUnlock(vd);
errno = ENOENT;
return false;
}
// Allocate a buffer to store the raw volume name
ulabel = ntfs_alloc(na->data_size * sizeof(ntfschar));
if (!ulabel) {
ntfsUnlock(vd);
errno = ENOMEM;
return false;
}
// Read the volume name
if (ntfs_attr_pread(na, 0, na->data_size, ulabel) != na->data_size) {
ntfs_free(ulabel);
ntfsUnlock(vd);
errno = EIO;
return false;
}
// Convert the volume name to the current local
if (ntfsUnicodeToLocal(ulabel, na->data_size, &volumeName, 0) < 0) {
errno = EINVAL;
ntfs_free(ulabel);
ntfsUnlock(vd);
return false;
}
// If the volume name was read then cache it (for future fetches)
if (volumeName)
strcpy(vd->name, volumeName);
// Close the volume name attribute
if (na)
ntfs_attr_close(na);
// Clean up
ntfs_free(volumeName);
ntfs_free(ulabel);
// Unlock
ntfsUnlock(vd);
return vd->name;
*/
}
bool ntfsSetVolumeName (const char *name, const char *volumeName)
{
ntfs_vd *vd = NULL;
ntfs_attr *na = NULL;
ntfschar *ulabel = NULL;
int ulabel_len;
// Sanity check
if (!name) {
errno = EINVAL;
return false;
}
// Get the devices volume descriptor
vd = ntfsGetVolume(name);
if (!vd) {
errno = ENODEV;
return false;
}
// Lock
ntfsLock(vd);
// Convert the new volume name to unicode
ulabel_len = ntfsLocalToUnicode(volumeName, &ulabel) * sizeof(ntfschar);
if (ulabel_len < 0) {
ntfsUnlock(vd);
errno = EINVAL;
return false;
}
// Check if the volume name attribute exists
na = ntfs_attr_open(vd->vol->vol_ni, AT_VOLUME_NAME, NULL, 0);
if (na) {
// It does, resize it to match the length of the new volume name
if (ntfs_attr_truncate(na, ulabel_len)) {
ntfs_free(ulabel);
ntfsUnlock(vd);
return false;
}
// Write the new volume name
if (ntfs_attr_pwrite(na, 0, ulabel_len, ulabel) != ulabel_len) {
ntfs_free(ulabel);
ntfsUnlock(vd);
return false;
}
} else {
// It doesn't, create it now
if (ntfs_attr_add(vd->vol->vol_ni, AT_VOLUME_NAME, NULL, 0, (u8*)ulabel, ulabel_len)) {
ntfs_free(ulabel);
ntfsUnlock(vd);
return false;
}
}
// Reset the volumes name cache (as it has now been changed)
vd->name[0] = '\0';
// Close the volume name attribute
if (na)
ntfs_attr_close(na);
// Sync the volume node
if (ntfs_inode_sync(vd->vol->vol_ni)) {
ntfs_free(ulabel);
ntfsUnlock(vd);
return false;
}
// Clean up
ntfs_free(ulabel);
// Unlock
ntfsUnlock(vd);
return true;
}
const devoptab_t *ntfsGetDevOpTab (void)
{
return &devops_ntfs;
}