mirror of
https://github.com/wiidev/usbloadergx.git
synced 2024-11-18 09:19:17 +01:00
9e79c9d99b
* code cleanup
809 lines
23 KiB
C
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, §or.buffer ) )
|
|
{
|
|
errno = EIO;
|
|
return -1;
|
|
}
|
|
|
|
// If this is the devices master boot record
|
|
if ( sector.mbr.signature == MBR_SIGNATURE )
|
|
{
|
|
memcpy( &mbr, §or, 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, §or ) )
|
|
{
|
|
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, §or ) )
|
|
{
|
|
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, §or ) )
|
|
{
|
|
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, §or ) )
|
|
{
|
|
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, §or ) )
|
|
{
|
|
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;
|
|
}
|