forked from len0rd/rockbox
Normally, we figure out the virual sector size from the filesystem info. However, if there's no filesystem, we fall back to the hardware's logical sector size. Some device firmware (eg ipod5g/6g) need their partition tables set up with larger-than-logical sector sizes; this way we can present the "correct" sector size to maintain interoperability with the stock firmware and make it so that the drive can still be properly partitioned from within rockbox. This patch adds support for DEFAULT_VIRT_SECTOR_SIZE. Nothing uses it yet. Change-Id: Iae746a50ffc37c51abb2c9b82d3c4596f1fa7559
654 lines
18 KiB
C
654 lines
18 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;
|
|
|
|
#if (CONFIG_STORAGE & STORAGE_ATA)
|
|
/* Query logical sector size */
|
|
struct storage_info *info = (struct storage_info*) sector;
|
|
storage_get_info(IF_MD_DRV(drive), info);
|
|
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 == 5 )
|
|
{
|
|
/* not handled yet */
|
|
}
|
|
|
|
if (pinfo[i].type == PARTITION_TYPE_GPT_GUARD) {
|
|
is_gpt = 1;
|
|
}
|
|
}
|
|
|
|
// XXX backup GPT header at final LBA of drive...
|
|
|
|
while (is_gpt) {
|
|
/* Re-start partition parsing using GPT */
|
|
uint64_t part_lba;
|
|
uint32_t part_entries;
|
|
uint32_t part_entry_size;
|
|
unsigned char* ptr = sector;
|
|
|
|
storage_read_sectors(IF_MD(drive,) 1, 1, sector);
|
|
|
|
part_lba = BYTES2INT64(ptr, 0);
|
|
if (part_lba != 0x5452415020494645ULL) {
|
|
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));
|
|
}
|
|
|
|
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 == 0 || pinfo[i].type == 5)
|
|
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 */
|
|
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 */
|
|
}
|
|
#endif /* MAX_VIRT_SECTOR_SIZE */
|
|
}
|
|
}
|
|
|
|
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 */
|