usbloadergx/source/libntfs/ntfs.c
giantpune 9e79c9d99b * remove little unused code
* code cleanup
2010-09-18 23:16:05 +00:00

809 lines
23 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;
}