mirror of
https://github.com/Rockbox/rockbox.git
synced 2025-10-14 02:27:39 -04:00
We've received multiple reports from users of 6th gen iPods where we fail to find any mountable partition. A user was able to supply an MBR dump, which showed that the "type" listed for parition 0 was set to 0x00 (ie "unused"), leading us to (correctly) completely ignore that entry. However, it looks like the stock firmware ignores the type and unconditionally uses the first entry even if it's nominally "invalid" So, to deal with this, always try to mount partition 0 if it is not one of the two "extended partition table" types. If that speculative mount succeeds, we now treat it as type 0x0c (Fat32 w/LBA) internally. Note that this will not allow the partition to be mountable over USB, as the MBR is still incorrect, leading the host OS to ignore the partition. Further complicating things, the stock Apple firmware always constructs a fake MBR to hand to the host! To prevent user confusion with these devices, we may consider faking the MBR too; alternatively we could correct the MBR and write it back to disk, perhaps via a debug menu option. Change-Id: I1e9392d20401eb94ecc6d70263fb0e45392a9bd4
708 lines
20 KiB
C
708 lines
20 KiB
C
/***************************************************************************
|
|
* __________ __ ___.
|
|
* Open \______ \ ____ ____ | | _\_ |__ _______ ___
|
|
* Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
|
|
* Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
|
|
* Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
|
|
* \/ \/ \/ \/ \/
|
|
* $Id$
|
|
*
|
|
* Copyright (C) 2002 by Björn Stenberg
|
|
*
|
|
* This program 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 software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
|
|
* KIND, either express or implied.
|
|
*
|
|
****************************************************************************/
|
|
#include <stdio.h>
|
|
#include <string.h>
|
|
#include "config.h"
|
|
#include "kernel.h"
|
|
#include "storage.h"
|
|
#include "debug.h"
|
|
#include "disk_cache.h"
|
|
#include "fileobj_mgr.h"
|
|
#include "dir.h"
|
|
#include "rb_namespace.h"
|
|
#include "disk.h"
|
|
#include "panic.h"
|
|
|
|
#if defined(HAVE_BOOTDATA) && !defined(SIMULATOR) && !defined(BOOTLOADER)
|
|
#include "bootdata.h"
|
|
#include "crc32.h"
|
|
#endif
|
|
|
|
#ifndef CONFIG_DEFAULT_PARTNUM
|
|
#define CONFIG_DEFAULT_PARTNUM 0
|
|
#endif
|
|
|
|
#define disk_reader_lock() file_internal_lock_READER()
|
|
#define disk_reader_unlock() file_internal_unlock_READER()
|
|
#define disk_writer_lock() file_internal_lock_WRITER()
|
|
#define disk_writer_unlock() file_internal_unlock_WRITER()
|
|
|
|
/* "MBR" Partition table entry layout:
|
|
-----------------------
|
|
0: 0x80 - active
|
|
1: starting head
|
|
2: starting sector
|
|
3: starting cylinder
|
|
4: partition type
|
|
5: end head
|
|
6: end sector
|
|
7: end cylinder
|
|
8-11: starting sector (LBA)
|
|
12-15: nr of sectors in partition
|
|
*/
|
|
|
|
#define BYTES2INT64(array, pos) \
|
|
(((uint64_t)array[pos+0] << 0) | \
|
|
((uint64_t)array[pos+1] << 8) | \
|
|
((uint64_t)array[pos+2] << 16) | \
|
|
((uint64_t)array[pos+3] << 24) | \
|
|
((uint64_t)array[pos+4] << 32) | \
|
|
((uint64_t)array[pos+5] << 40) | \
|
|
((uint64_t)array[pos+6] << 48) | \
|
|
((uint64_t)array[pos+7] << 56) )
|
|
|
|
#define BYTES2INT32(array, pos) \
|
|
(((uint32_t)array[pos+0] << 0) | \
|
|
((uint32_t)array[pos+1] << 8) | \
|
|
((uint32_t)array[pos+2] << 16) | \
|
|
((uint32_t)array[pos+3] << 24))
|
|
|
|
#define BYTES2INT16(array, pos) \
|
|
(((uint16_t)array[pos+0] << 0) | \
|
|
((uint16_t)array[pos+1] << 8))
|
|
|
|
static struct partinfo part[NUM_DRIVES*MAX_PARTITIONS_PER_DRIVE];
|
|
static struct volumeinfo volumes[NUM_VOLUMES];
|
|
|
|
/* check if the entry points to a free volume */
|
|
static bool is_free_volume(const struct volumeinfo *vi)
|
|
{
|
|
return vi->drive < 0;
|
|
}
|
|
|
|
/* mark a volume entry as free */
|
|
static void mark_free_volume(struct volumeinfo *vi)
|
|
{
|
|
vi->drive = -1;
|
|
vi->partition = -1;
|
|
}
|
|
|
|
static int get_free_volume(void)
|
|
{
|
|
for (int i = 0; i < NUM_VOLUMES; i++)
|
|
if (is_free_volume(&volumes[i]))
|
|
return i;
|
|
|
|
return -1; /* none found */
|
|
}
|
|
|
|
static void init_volume(struct volumeinfo *vi, int drive, int part)
|
|
{
|
|
vi->drive = drive;
|
|
vi->partition = part;
|
|
}
|
|
|
|
#ifdef MAX_VIRT_SECTOR_SIZE
|
|
static uint16_t disk_sector_multiplier[NUM_DRIVES] =
|
|
{ [0 ... NUM_DRIVES-1] = 1 };
|
|
|
|
int disk_get_sector_multiplier(IF_MD_NONVOID(int drive))
|
|
{
|
|
if (!CHECK_DRV(drive))
|
|
return 0;
|
|
|
|
disk_reader_lock();
|
|
int multiplier = disk_sector_multiplier[IF_MD_DRV(drive)];
|
|
disk_reader_unlock();
|
|
return multiplier;
|
|
}
|
|
|
|
#ifdef DEFAULT_VIRT_SECTOR_SIZE
|
|
void disk_set_sector_multiplier(IF_MD(int drive,) uint16_t mult)
|
|
{
|
|
if (!CHECK_DRV(drive))
|
|
return;
|
|
|
|
disk_writer_lock();
|
|
disk_sector_multiplier[IF_MD_DRV(drive)] = mult;
|
|
disk_writer_unlock();
|
|
}
|
|
#endif /* DEFAULT_VIRT_SECTOR_SIZE */
|
|
#endif /* MAX_VIRT_SECTOR_SIZE */
|
|
|
|
#ifdef MAX_VARIABLE_LOG_SECTOR
|
|
static uint16_t disk_log_sector_size[NUM_DRIVES] =
|
|
{ [0 ... NUM_DRIVES-1] = SECTOR_SIZE }; /* Updated from STORAGE_INFO */
|
|
int disk_get_log_sector_size(IF_MD_NONVOID(int drive))
|
|
{
|
|
if (!CHECK_DRV(drive))
|
|
return 0;
|
|
|
|
disk_reader_lock();
|
|
int size = disk_log_sector_size[IF_MD_DRV(drive)];
|
|
disk_reader_unlock();
|
|
return size;
|
|
}
|
|
#define LOG_SECTOR_SIZE(__drive) disk_log_sector_size[IF_MD_DRV(__drive)]
|
|
#else /* !MAX_VARIABLE_LOG_SECTOR */
|
|
#define LOG_SECTOR_SIZE(__drive) SECTOR_SIZE
|
|
#endif /* !MAX_VARIABLE_LOG_SECTOR */
|
|
|
|
bool disk_init(IF_MD_NONVOID(int drive))
|
|
{
|
|
if (!CHECK_DRV(drive))
|
|
return false; /* out of space in table */
|
|
|
|
unsigned char *sector = dc_get_buffer();
|
|
if (!sector)
|
|
return false;
|
|
|
|
/* Query logical sector size */
|
|
struct storage_info *info = (struct storage_info*) sector;
|
|
storage_get_info(IF_MD_DRV(drive), info);
|
|
sector_t num_sectors = info->num_sectors;
|
|
#ifdef DEFAULT_VIRT_SECTOR_SIZE
|
|
unsigned int sector_size = info->sector_size;
|
|
#endif
|
|
|
|
#if (CONFIG_STORAGE & STORAGE_ATA)
|
|
disk_writer_lock();
|
|
#ifdef MAX_VARIABLE_LOG_SECTOR
|
|
disk_log_sector_size[IF_MD_DRV(drive)] = info->sector_size;
|
|
#endif
|
|
disk_writer_unlock();
|
|
|
|
#ifdef MAX_VARIABLE_LOG_SECTOR
|
|
if (info->sector_size > MAX_VARIABLE_LOG_SECTOR || info->sector_size > DC_CACHE_BUFSIZE) {
|
|
panicf("Unsupported logical sector size: %d",
|
|
info->sector_size);
|
|
}
|
|
#else /* !MAX_VARIABLE_LOG_SECTOR */
|
|
if (info->sector_size != SECTOR_SIZE) {
|
|
panicf("Unsupported logical sector size: %d",
|
|
info->sector_size);
|
|
}
|
|
#endif /* !MAX_VARIABLE_LOG_SECTOR */
|
|
#endif /* STORAGE_ATA */
|
|
|
|
memset(sector, 0, DC_CACHE_BUFSIZE);
|
|
storage_read_sectors(IF_MD(drive,) 0, 1, sector);
|
|
|
|
bool init = false;
|
|
|
|
/* check that the boot sector is initialized */
|
|
if (BYTES2INT16(sector, 510) == 0xaa55)
|
|
{
|
|
/* For each drive, start at a different position, in order not to
|
|
destroy the first entry of drive 0. That one is needed to calculate
|
|
config sector position. */
|
|
struct partinfo *pinfo = &part[IF_MD_DRV(drive)*MAX_PARTITIONS_PER_DRIVE];
|
|
uint8_t is_gpt = 0;
|
|
|
|
disk_writer_lock();
|
|
|
|
/* parse partitions */
|
|
for (int i = 0; i < MAX_PARTITIONS_PER_DRIVE && i < 4; i++)
|
|
{
|
|
unsigned char* ptr = sector + 0x1be + 16*i;
|
|
pinfo[i].type = ptr[4];
|
|
pinfo[i].start = BYTES2INT32(ptr, 8);
|
|
pinfo[i].size = BYTES2INT32(ptr, 12);
|
|
|
|
DEBUGF("Part%d: Type %02x, start: %08lx size: %08lx\n",
|
|
i,pinfo[i].type,pinfo[i].start,pinfo[i].size);
|
|
|
|
/* extended? */
|
|
if ( pinfo[i].type == 0x05 || pinfo[i].type == 0x0f ) {
|
|
/* not handled yet */
|
|
}
|
|
|
|
if (pinfo[i].type == PARTITION_TYPE_GPT_GUARD) {
|
|
is_gpt = 1;
|
|
}
|
|
|
|
#if 0 // Currently done in disk_mount() upon successful mount only
|
|
/* Auto-correct partition entries */
|
|
if (i == 0 && pinfo[i].type == 0 &&
|
|
pinfo[i].start != 0 && pinfo[i].size != 0) {
|
|
pinfo[i].type = PARTITION_TYPE_FAT32_LBA;
|
|
// XXX consider correcting MBR and writing sector back?
|
|
}
|
|
#endif
|
|
}
|
|
|
|
while (is_gpt) {
|
|
/* Re-start partition parsing using GPT */
|
|
uint64_t part_lba;
|
|
uint32_t part_entry_size;
|
|
uint32_t part_entries = 0;
|
|
unsigned char* ptr = sector;
|
|
|
|
#ifdef DEFAULT_VIRT_SECTOR_SIZE
|
|
sector_t try_gpt[4] = { 1, num_sectors - 1,
|
|
DEFAULT_VIRT_SECTOR_SIZE / sector_size,
|
|
(num_sectors / (DEFAULT_VIRT_SECTOR_SIZE / sector_size)) - 1
|
|
};
|
|
|
|
#else
|
|
sector_t try_gpt[2] = { 1, num_sectors - 1 };
|
|
#endif
|
|
|
|
for (unsigned int i = 0 ; i < (sizeof(try_gpt) / sizeof(try_gpt[0])) ; i++) {
|
|
storage_read_sectors(IF_MD(drive,) try_gpt[i], 1, sector);
|
|
part_lba = BYTES2INT64(ptr, 0);
|
|
if (part_lba == 0x5452415020494645ULL) {
|
|
part_entries = 1;
|
|
#ifdef MAX_VIRT_SECTOR_SIZE
|
|
if (i >= 2)
|
|
disk_sector_multiplier[IF_MD_DRV(drive)] = try_gpt[2]; // XXX use this later?
|
|
#endif
|
|
break;
|
|
}
|
|
}
|
|
if (!part_entries) {
|
|
DEBUGF("GPT: Invalid signature\n");
|
|
break;
|
|
}
|
|
|
|
part_entry_size = BYTES2INT32(ptr, 8);
|
|
if (part_entry_size != 0x00010000) {
|
|
DEBUGF("GPT: Invalid version\n");
|
|
break;
|
|
}
|
|
part_entry_size = BYTES2INT32(ptr, 12);
|
|
if (part_entry_size != 0x5c) {
|
|
DEBUGF("GPT: Invalid header size\n");
|
|
break;
|
|
}
|
|
// XXX checksum header -- u32 @ offset 16
|
|
part_entry_size = BYTES2INT32(ptr, 24);
|
|
if (part_entry_size != 1) {
|
|
DEBUGF("GPT: Invalid header LBA\n");
|
|
break;
|
|
}
|
|
|
|
part_lba = BYTES2INT64(ptr, 72);
|
|
part_entries = BYTES2INT32(ptr, 80);
|
|
part_entry_size = BYTES2INT32(ptr, 84);
|
|
|
|
int part = 0;
|
|
reload:
|
|
storage_read_sectors(IF_MD(drive,) part_lba, 1, sector);
|
|
uint8_t *pptr = ptr;
|
|
while (part < MAX_PARTITIONS_PER_DRIVE && part_entries) {
|
|
if (pptr - ptr >= LOG_SECTOR_SIZE(drive)) {
|
|
part_lba++;
|
|
goto reload;
|
|
}
|
|
|
|
/* Parse GPT entry. We only care about the "General Data" type, ie:
|
|
EBD0A0A2-B9E5-4433-87C0-68B6B72699C7
|
|
LE32 LE16 LE16 BE16 BE16
|
|
*/
|
|
uint64_t tmp;
|
|
tmp = BYTES2INT32(pptr, 0);
|
|
if (tmp != 0xEBD0A0A2)
|
|
goto skip;
|
|
tmp = BYTES2INT16(pptr, 4);
|
|
if (tmp != 0xB9E5)
|
|
goto skip;
|
|
tmp = BYTES2INT16(pptr, 6);
|
|
if (tmp != 0x4433)
|
|
goto skip;
|
|
if (pptr[8] != 0x87 || pptr[9] != 0xc0)
|
|
goto skip;
|
|
if (pptr[10] != 0x68 || pptr[11] != 0xb6 || pptr[12] != 0xb7 ||
|
|
pptr[13] != 0x26 || pptr[14] != 0x99 || pptr[15] != 0xc7)
|
|
goto skip;
|
|
|
|
tmp = BYTES2INT64(pptr, 48); /* Flags */
|
|
if (tmp) {
|
|
DEBUGF("GPT: Skip parition with flags\n");
|
|
goto skip; /* Any flag makes us ignore this */
|
|
}
|
|
tmp = BYTES2INT64(pptr, 32); /* FIRST LBA */
|
|
#ifndef STORAGE_64BIT_SECTOR
|
|
if (tmp > UINT32_MAX) {
|
|
DEBUGF("GPT: partition starts after 2TiB mark\n");
|
|
goto skip;
|
|
}
|
|
#endif
|
|
if (tmp < 34) {
|
|
DEBUGF("GPT: Invalid start LBA\n");
|
|
goto skip;
|
|
}
|
|
pinfo[part].start = tmp;
|
|
tmp = BYTES2INT64(pptr, 40); /* LAST LBA */
|
|
#ifndef STORAGE_64BIT_SECTOR
|
|
if (tmp > UINT32_MAX) {
|
|
DEBUGF("GPT: partition ends after 2TiB mark\n");
|
|
goto skip;
|
|
}
|
|
#endif
|
|
if (tmp <= pinfo[part].start) {
|
|
DEBUGF("GPT: Invalid end LBA\n");
|
|
goto skip;
|
|
}
|
|
pinfo[part].size = tmp - pinfo[part].start + 1;
|
|
pinfo[part].type = PARTITION_TYPE_FAT32_LBA;
|
|
|
|
DEBUGF("GPart%d: start: %016lx size: %016lx\n",
|
|
part,pinfo[part].start,pinfo[part].size);
|
|
part++;
|
|
|
|
skip:
|
|
pptr += part_entry_size;
|
|
part_entries--;
|
|
}
|
|
|
|
is_gpt = 0; /* To break out of the loop */
|
|
}
|
|
disk_writer_unlock();
|
|
|
|
init = true;
|
|
}
|
|
else
|
|
{
|
|
DEBUGF("Bad boot sector signature\n");
|
|
}
|
|
|
|
dc_release_buffer(sector);
|
|
return init;
|
|
}
|
|
|
|
bool disk_partinfo(int partition, struct partinfo *info)
|
|
{
|
|
if (partition < 0 || partition >= (int)ARRAYLEN(part) || !info)
|
|
return false;
|
|
|
|
disk_reader_lock();
|
|
*info = part[partition];
|
|
disk_reader_unlock();
|
|
return true;
|
|
}
|
|
|
|
int disk_mount(int drive)
|
|
{
|
|
int mounted = 0; /* reset partition-on-drive flag */
|
|
|
|
disk_writer_lock();
|
|
|
|
int volume = get_free_volume();
|
|
|
|
if (volume < 0)
|
|
{
|
|
DEBUGF("No Free Volumes\n");
|
|
disk_writer_unlock();
|
|
return 0;
|
|
}
|
|
|
|
if (!disk_init(IF_MD(drive)))
|
|
{
|
|
disk_writer_unlock();
|
|
return 0;
|
|
}
|
|
|
|
struct partinfo *pinfo = &part[IF_MD_DRV(drive)*4];
|
|
#ifdef MAX_VIRT_SECTOR_SIZE
|
|
disk_sector_multiplier[IF_MD_DRV(drive)] = 1;
|
|
#endif
|
|
|
|
/* try "superfloppy" mode */
|
|
DEBUGF("Trying to mount sector 0.\n");
|
|
|
|
if (!fat_mount(IF_MV(volume,) IF_MD(drive,) 0))
|
|
{
|
|
#ifdef MAX_VIRT_SECTOR_SIZE
|
|
disk_sector_multiplier[drive] = fat_get_bytes_per_sector(IF_MV(volume)) / LOG_SECTOR_SIZE(drive);
|
|
#endif
|
|
mounted = 1;
|
|
init_volume(&volumes[volume], drive, 0);
|
|
volume_onmount_internal(IF_MV(volume));
|
|
|
|
struct storage_info info;
|
|
storage_get_info(drive, &info);
|
|
|
|
pinfo[0].type = PARTITION_TYPE_FAT32_LBA;
|
|
pinfo[0].start = 0;
|
|
pinfo[0].size = info.num_sectors;
|
|
}
|
|
|
|
if (mounted == 0 && volume != -1) /* not a "superfloppy"? */
|
|
{
|
|
for (int i = CONFIG_DEFAULT_PARTNUM;
|
|
volume != -1 && i < MAX_PARTITIONS_PER_DRIVE && mounted < NUM_VOLUMES_PER_DRIVE;
|
|
i++)
|
|
{
|
|
if (pinfo[i].type == 0x05 ||
|
|
pinfo[i].type == 0x0f ||
|
|
(i != 0 && pinfo[i].type == 0))
|
|
continue; /* skip free/extended partitions */
|
|
|
|
DEBUGF("Trying to mount partition %d.\n", i);
|
|
|
|
#ifdef MAX_VIRT_SECTOR_SIZE
|
|
for (int j = 1; j <= (MAX_VIRT_SECTOR_SIZE/LOG_SECTOR_SIZE(drive)); j <<= 1)
|
|
{
|
|
if (!fat_mount(IF_MV(volume,) IF_MD(drive,) pinfo[i].start * j))
|
|
{
|
|
pinfo[i].start *= j;
|
|
pinfo[i].size *= j;
|
|
mounted++;
|
|
init_volume(&volumes[volume], drive, i);
|
|
disk_sector_multiplier[drive] = j;
|
|
volume_onmount_internal(IF_MV(volume));
|
|
volume = get_free_volume(); /* prepare next entry */
|
|
if (pinfo[i].type == 0) {
|
|
pinfo[i].type = PARTITION_TYPE_FAT32_LBA;
|
|
// XXX write the sector back.?
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
#else /* ndef MAX_VIRT_SECTOR_SIZE */
|
|
if (!fat_mount(IF_MV(volume,) IF_MD(drive,) pinfo[i].start))
|
|
{
|
|
mounted++;
|
|
init_volume(&volumes[volume], drive, i);
|
|
volume_onmount_internal(IF_MV(volume));
|
|
volume = get_free_volume(); /* prepare next entry */
|
|
if (pinfo[i].type == 0) {
|
|
pinfo[i].type = PARTITION_TYPE_FAT32_LBA;
|
|
// XXX write the sector back.?
|
|
}
|
|
|
|
}
|
|
#endif /* MAX_VIRT_SECTOR_SIZE */
|
|
}
|
|
|
|
#if defined(MAX_VIRT_SECTOR_SIZE) && defined(MAX_PHYS_SECTOR_SIZE)
|
|
if (mounted)
|
|
ata_set_phys_sector_mult(disk_sector_multiplier[drive]);
|
|
#endif
|
|
}
|
|
|
|
disk_writer_unlock();
|
|
return mounted;
|
|
}
|
|
|
|
int disk_mount_all(void)
|
|
{
|
|
int mounted = 0;
|
|
|
|
disk_writer_lock();
|
|
|
|
/* reset all mounted partitions */
|
|
volume_onunmount_internal(IF_MV(-1));
|
|
fat_init();
|
|
|
|
/* mark all volumes as free */
|
|
for (int i = 0; i < NUM_VOLUMES; i++)
|
|
mark_free_volume(&volumes[i]);
|
|
|
|
for (int i = 0; i < NUM_DRIVES; i++)
|
|
{
|
|
#ifdef HAVE_HOTSWAP
|
|
if (storage_present(i))
|
|
#endif
|
|
mounted += disk_mount(i);
|
|
}
|
|
|
|
disk_writer_unlock();
|
|
return mounted;
|
|
}
|
|
|
|
int disk_unmount(int drive)
|
|
{
|
|
if (!CHECK_DRV(drive))
|
|
return 0;
|
|
|
|
int unmounted = 0;
|
|
|
|
disk_writer_lock();
|
|
|
|
for (int i = 0; i < NUM_VOLUMES; i++)
|
|
{
|
|
struct volumeinfo *vi = &volumes[i];
|
|
/* unmount any volumes on the drive */
|
|
if (vi->drive == drive)
|
|
{
|
|
mark_free_volume(vi); /* FIXME: should do this after unmount? */
|
|
volume_onunmount_internal(IF_MV(i));
|
|
fat_unmount(IF_MV(i));
|
|
unmounted++;
|
|
}
|
|
}
|
|
|
|
disk_writer_unlock();
|
|
return unmounted;
|
|
}
|
|
|
|
int disk_unmount_all(void)
|
|
{
|
|
int unmounted = 0;
|
|
|
|
disk_writer_lock();
|
|
|
|
volume_onunmount_internal(IF_MV(-1));
|
|
|
|
for (int i = 0; i < NUM_DRIVES; i++)
|
|
{
|
|
#ifdef HAVE_HOTSWAP
|
|
if (storage_present(i))
|
|
#endif
|
|
unmounted += disk_unmount(i);
|
|
}
|
|
|
|
disk_writer_unlock();
|
|
return unmounted;
|
|
}
|
|
|
|
bool disk_present(IF_MD_NONVOID(int drive))
|
|
{
|
|
int rc = -1;
|
|
|
|
if (CHECK_DRV(drive))
|
|
{
|
|
void *sector = dc_get_buffer();
|
|
if (sector)
|
|
{
|
|
rc = storage_read_sectors(IF_MD(drive,) 0, 1, sector);
|
|
dc_release_buffer(sector);
|
|
}
|
|
}
|
|
|
|
return rc == 0;
|
|
}
|
|
|
|
|
|
/** Volume-centric functions **/
|
|
|
|
void volume_recalc_free(IF_MV_NONVOID(int volume))
|
|
{
|
|
if (!CHECK_VOL(volume))
|
|
return;
|
|
|
|
/* FIXME: this is crummy but the only way to ensure a correct freecount
|
|
if other threads are writing and changing the fsinfo; it is possible
|
|
to get multiple threads calling here and also writing and get correct
|
|
freespace counts, however a bit complicated to do; if thou desireth I
|
|
shall implement the concurrent version -- jethead71 */
|
|
disk_writer_lock();
|
|
fat_recalc_free(IF_MV(volume));
|
|
disk_writer_unlock();
|
|
}
|
|
|
|
unsigned int volume_get_cluster_size(IF_MV_NONVOID(int volume))
|
|
{
|
|
if (!CHECK_VOL(volume))
|
|
return 0;
|
|
|
|
disk_reader_lock();
|
|
unsigned int clustersize = fat_get_cluster_size(IF_MV(volume));
|
|
disk_reader_unlock();
|
|
return clustersize;
|
|
}
|
|
|
|
void volume_size(IF_MV(int volume,) sector_t *sizep, sector_t *freep)
|
|
{
|
|
disk_reader_lock();
|
|
|
|
if (!CHECK_VOL(volume) || !fat_size(IF_MV(volume,) sizep, freep))
|
|
{
|
|
if (sizep) *sizep = 0;
|
|
if (freep) *freep = 0;
|
|
}
|
|
|
|
disk_reader_unlock();
|
|
}
|
|
|
|
#if defined (HAVE_HOTSWAP) || defined (HAVE_MULTIDRIVE) \
|
|
|| defined (HAVE_DIRCACHE) || defined(HAVE_BOOTDATA)
|
|
enum volume_info_type
|
|
{
|
|
#ifdef HAVE_HOTSWAP
|
|
VP_REMOVABLE,
|
|
VP_PRESENT,
|
|
#endif
|
|
#if defined (HAVE_MULTIDRIVE) || defined (HAVE_DIRCACHE)
|
|
VP_DRIVE,
|
|
#endif
|
|
VP_PARTITION,
|
|
};
|
|
|
|
static int volume_properties(int volume, enum volume_info_type infotype)
|
|
{
|
|
int res = -1;
|
|
|
|
disk_reader_lock();
|
|
|
|
if (CHECK_VOL(volume))
|
|
{
|
|
struct volumeinfo *vi = &volumes[volume];
|
|
switch (infotype)
|
|
{
|
|
#ifdef HAVE_HOTSWAP
|
|
case VP_REMOVABLE:
|
|
res = storage_removable(vi->drive) ? 1 : 0;
|
|
break;
|
|
case VP_PRESENT:
|
|
res = storage_present(vi->drive) ? 1 : 0;
|
|
break;
|
|
#endif
|
|
#if defined(HAVE_MULTIDRIVE) || defined(HAVE_DIRCACHE)
|
|
case VP_DRIVE:
|
|
res = vi->drive;
|
|
break;
|
|
#endif
|
|
case VP_PARTITION:
|
|
res = vi->partition;
|
|
break;
|
|
}
|
|
}
|
|
|
|
disk_reader_unlock();
|
|
return res;
|
|
}
|
|
|
|
#ifdef HAVE_HOTSWAP
|
|
bool volume_removable(int volume)
|
|
{
|
|
return volume_properties(volume, VP_REMOVABLE) > 0;
|
|
}
|
|
|
|
bool volume_present(int volume)
|
|
{
|
|
return volume_properties(volume, VP_PRESENT) > 0;
|
|
}
|
|
#endif /* HAVE_HOTSWAP */
|
|
|
|
#ifdef HAVE_MULTIDRIVE
|
|
int volume_drive(int volume)
|
|
{
|
|
return volume_properties(volume, VP_DRIVE);
|
|
}
|
|
#endif /* HAVE_MULTIDRIVE */
|
|
|
|
int volume_partition(int volume)
|
|
{
|
|
return volume_properties(volume, VP_PARTITION);
|
|
}
|
|
|
|
#ifdef HAVE_DIRCACHE
|
|
bool volume_ismounted(IF_MV_NONVOID(int volume))
|
|
{
|
|
return volume_properties(IF_MV_VOL(volume), VP_DRIVE) >= 0;
|
|
}
|
|
#endif /* HAVE_DIRCACHE */
|
|
|
|
|
|
#endif /* HAVE_HOTSWAP || HAVE_MULTIDRIVE || HAVE_DIRCACHE || HAVE_BOOTDATA */
|