mirror of
https://github.com/wiidev/usbloadergx.git
synced 2024-11-29 06:34:17 +01:00
64f8406b07
*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.
724 lines
21 KiB
C
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, §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;
|
|
}
|