/*␊ |
* Copyright (c) 2004 Apple Computer, Inc. All rights reserved.␊ |
*␊ |
* @APPLE_LICENSE_HEADER_START@␊ |
* ␊ |
* Copyright (c) 1999-2004 Apple Computer, Inc. All Rights Reserved.␊ |
* ␊ |
* This file contains Original Code and/or Modifications of Original Code␊ |
* as defined in and that are subject to the Apple Public Source License␊ |
* Version 2.0 (the 'License'). You may not use this file except in␊ |
* compliance with the License. Please obtain a copy of the License at␊ |
* http://www.opensource.apple.com/apsl/ and read it before using this␊ |
* file.␊ |
* ␊ |
* The Original Code and all software distributed under the License are␊ |
* distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER␊ |
* EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,␊ |
* INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,␊ |
* FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.␊ |
* Please see the License for the specific language governing rights and␊ |
* limitations under the License.␊ |
* ␊ |
* @APPLE_LICENSE_HEADER_END@␊ |
*/␊ |
/*␊ |
* dmazar, 14/7/2011␊ |
* support for EXFAT volume label reading␊ |
* EXFAT info from: http://www.ntfs.com/exfat-overview.htm␊ |
*␊ |
* EXFAT shares partition type with NTFS (0x7) and easiest way of␊ |
* adding it was through ntfs.c module. All functions here are called␊ |
* from similar ntfs.c funcs as fallback (if not NTFS, maybe it's EXFAT).␊ |
*/␊ |
␊ |
#include "libsaio.h"␊ |
#include "sl.h"␊ |
␊ |
#ifndef DEBUG_EXFAT␊ |
#define DEBUG_EXFAT 0␊ |
#endif␊ |
␊ |
#if DEBUG_EXFAT␊ |
#define DBG(x...)␉␉printf(x)␊ |
#define PAUSE()␉␉␉getchar()␊ |
#else␊ |
#define DBG(x...)␊ |
#define PAUSE()␊ |
#endif␊ |
␊ |
#define␉EXFAT_BBID␉"EXFAT "␊ |
#define␉EXFAT_BBIDLEN␉8␊ |
␊ |
#define MAX_BLOCK_SIZE␉␉4096␊ |
#define MAX_CLUSTER_SIZE␉32 * 1024 * 1024␊ |
␊ |
#define ERROR -1␊ |
␊ |
␊ |
/*␊ |
* boot sector of the partition␊ |
* http://www.ntfs.com/exfat-boot-sector.htm␊ |
*/␊ |
struct exfatbootfile {␊ |
␉u_int8_t reserved1[3];␉/* JumpBoot: 0xEB7690 */␊ |
␉u_int8_t bf_sysid[8];␉/* FileSystemName: 'EXFAT ' */␊ |
␉u_int8_t reserved2[53];␉/* MustBeZero */␊ |
␉u_int64_t bf_prtoff;␉␉/* PartitionOffset: In sectors; if 0, shall be ignored */␊ |
␉u_int64_t bf_vollen;␉␉/* VolumeLength: Size of exFAT volume in sectors */␊ |
␉u_int32_t bf_fatoff;␉␉/* FatOffset: In sectors */␊ |
␉u_int32_t bf_fatlen;␉␉/* FatLength: In sectors. May exceed the required space in order to align the second FAT */␊ |
␉u_int32_t bf_cloff;␉␉/* ClusterHeapOffset: In sectors. */␊ |
␉u_int32_t bf_clcnt;␉␉/* ClusterCount: 2^32-11 is the maximum number of clusters could be described. */␊ |
␉u_int32_t bf_rdircl;␉␉/* RootDirectoryCluster. */␊ |
␉u_int32_t bf_volsn;␉␉/* VolumeSerialNumber. */␊ |
␉u_int16_t bf_fsrev;␉␉/* FileSystemRevision: as MAJOR.minor, major revision is high byte, minor is low byte; currently 01.00. */␊ |
␉u_int16_t bf_volflags;␉/* VolumeFlags. */␊ |
␉u_int8_t bf_bpss;␉␉/* BytesPerSectorShift: Power of 2. Minimum 9 (512 bytes per sector), maximum 12 (4096 bytes per sector) */␊ |
|
␉u_int8_t bf_nfats;␉␉/* NumberOfFats: 2 is for TexFAT only */␊ |
␉u_int8_t bf_drvs;␉␉/* DriveSelect: Extended INT 13h drive number; typically 0x80 */␊ |
|
␉u_int8_t reserved3[7];␉/* Reserved */␊ |
␉u_int8_t bootcode[390];␉/* BootCode */␊ |
␉u_int16_t bf_bsig;␉␉/* BootSignature: 0xAA55 */␊ |
};␊ |
␊ |
struct direntry_label {␊ |
␉u_int8_t type;␉␉␉/* EntryType: 0x83 (or 0x03 if label is empty) */␊ |
␉u_int8_t llen;␉␉␉/* CharacterCount: Length in Unicode characters (max 11) */␊ |
␉u_int16_t label[11];␉␉/* VolumeLabel: Unicode characters (max 11) */␊ |
␉u_int8_t reserved1[8];␉/* Reserved */␊ |
};␊ |
␊ |
␊ |
/**␊ |
* Reads volume label into str.␊ |
* Reads boot sector, performs dome checking, loads root dir␊ |
* and parses volume label.␊ |
*/␊ |
void␊ |
EXFATGetDescription(CICell ih, char *str, long strMaxLen)␊ |
{␊ |
struct exfatbootfile *boot;␊ |
u_int32_t bytesPerSector = 0;␊ |
u_int32_t sectorsPerCluster = 0;␊ |
long long rdirOffset = 0;␊ |
char *buf = NULL;␊ |
struct direntry_label *dire = NULL;␊ |
int loopControl = 0;␊ |
␊ |
DBG("EXFAT: start %x:%x\n", ih->biosdev, ih->part_no);␊ |
␉␊ |
buf = (char *)malloc(MAX_BLOCK_SIZE);␊ |
if (buf == 0)␊ |
{␊ |
goto error;␊ |
}␊ |
␊ |
/*␊ |
* Read the boot sector, check signatures, and do some minimal␊ |
* sanity checking. NOTE: the size of the read below is intended␊ |
* to be a multiple of all supported block sizes, so we don't␊ |
* have to determine or change the device's block size.␊ |
*/␊ |
Seek(ih, 0);␊ |
Read(ih, (long)buf, MAX_BLOCK_SIZE);␊ |
␊ |
// take our boot structure␊ |
boot = (struct exfatbootfile *) buf;␊ |
␊ |
/*␊ |
* The first three bytes are an Intel x86 jump instruction. I assume it␊ |
* can be the same forms as DOS FAT:␊ |
* 0xE9 0x?? 0x??␊ |
* 0xEC 0x?? 0x90␊ |
* where 0x?? means any byte value is OK.␊ |
*/␊ |
if (boot->reserved1[0] != 0xE9␊ |
&& (boot->reserved1[0] != 0xEB || boot->reserved1[2] != 0x90))␊ |
{␊ |
goto error;␊ |
}␊ |
␊ |
// Check the "EXFAT " signature.␊ |
if (memcmp((const char *)boot->bf_sysid, EXFAT_BBID, EXFAT_BBIDLEN) != 0)␊ |
{␊ |
goto error;␊ |
}␊ |
␊ |
/*␊ |
* Make sure the bytes per sector and sectors per cluster are␊ |
* powers of two, and within reasonable ranges.␊ |
*/␊ |
bytesPerSector = 1 << boot->bf_bpss;␉/* Just one byte; no swapping needed */␊ |
DBG("EXFAT: bpss=%d, bytesPerSector=%d\n", boot->bf_bpss, bytesPerSector);␊ |
if (boot->bf_bpss < 9 || boot->bf_bpss > 12)␊ |
{␊ |
DBG("EXFAT: invalid bytes per sector shift(%d)\n", boot->bf_bpss);␊ |
goto error;␊ |
}␊ |
␊ |
sectorsPerCluster = 1 << boot->bf_spcs;␉/* Just one byte; no swapping needed */␊ |
DBG("EXFAT: spcs=%d, sectorsPerCluster=%d\n", boot->bf_spcs, sectorsPerCluster);␊ |
if (boot->bf_spcs > (25 - boot->bf_bpss))␊ |
{␊ |
DBG("EXFAT: invalid sectors per cluster shift (%d)\n", boot->bf_spcs);␊ |
goto error;␊ |
}␊ |
␊ |
// calculate root dir cluster offset␊ |
rdirOffset = boot->bf_cloff + (boot->bf_rdircl - 2) * sectorsPerCluster;␊ |
DBG("EXFAT: rdirOffset=%d\n", rdirOffset);␊ |
␊ |
// load MAX_BLOCK_SIZE bytes of root dir␊ |
Seek(ih, rdirOffset * bytesPerSector);␊ |
Read(ih, (long)buf, MAX_BLOCK_SIZE);␊ |
DBG("buf 0 1 2 = %x %x %x\n", 0x00ff & buf[0], 0x00ff & buf[1], 0x00ff & buf[2]);␊ |
␊ |
str[0] = '\0';␊ |
␊ |
/*␊ |
* Search for volume label dir entry (type 0x83), convert from unicode and put to str.␊ |
* Set loopControl var to avoid searching outside of buf.␊ |
*/␊ |
loopControl = MAX_BLOCK_SIZE / sizeof(struct direntry_label);␊ |
dire = (struct direntry_label *)buf;␊ |
while (loopControl && dire->type && dire->type != 0x83)␊ |
{␊ |
dire++;␊ |
loopControl--;␊ |
}␊ |
if (dire->type == 0x83 && dire->llen > 0 && dire->llen <= 11)␊ |
{␊ |
utf_encodestr( dire->label, (int)dire->llen, (u_int8_t *)str, strMaxLen, OSLittleEndian );␊ |
}␊ |
DBG("EXFAT: label=%s\n", str);␊ |
␊ |
free(buf);␊ |
PAUSE();␊ |
return;␊ |
␊ |
error:␊ |
if (buf) free(buf);␊ |
DBG("EXFAT: error\n");␊ |
PAUSE();␊ |
return;␊ |
}␊ |
␊ |
/**␊ |
* Sets UUID to uuidStr.␊ |
* Reads the boot sector, does some checking, generates UUID␊ |
* (like the one you get on Windows???)␊ |
*/␊ |
long EXFATGetUUID(CICell ih, char *uuidStr)␊ |
{␊ |
struct exfatbootfile *boot;␊ |
void *buf = malloc(MAX_BLOCK_SIZE);␊ |
if ( !buf )␊ |
return -1;␊ |
␊ |
/*␊ |
* Read the boot sector, check signatures, and do some minimal␊ |
* sanity checking.␉ NOTE: the size of the read below is intended␊ |
* to be a multiple of all supported block sizes, so we don't␊ |
* have to determine or change the device's block size.␊ |
*/␊ |
Seek(ih, 0);␊ |
Read(ih, (long)buf, MAX_BLOCK_SIZE);␊ |
␊ |
boot = (struct exfatbootfile *) buf;␊ |
␊ |
/*␊ |
* Check the "EXFAT " signature.␊ |
*/␊ |
if (memcmp((const char *)boot->bf_sysid, EXFAT_BBID, EXFAT_BBIDLEN) != 0)␊ |
return -1;␊ |
␊ |
// Check for non-null volume serial number␊ |
if( !boot->bf_volsn )␊ |
return -1;␊ |
␊ |
// Use UUID like the one you get on Windows␊ |
sprintf(uuidStr, "%04X-%04X", (unsigned short)(boot->bf_volsn >> 16) & 0xFFFF,␊ |
(unsigned short)boot->bf_volsn & 0xFFFF);␊ |
␊ |
DBG("EXFATGetUUID: %x:%x = %s\n", ih->biosdev, ih->part_no, uuidStr);␊ |
return 0;␊ |
} ␊ |
␊ |
/**␊ |
* Returns true if given buffer is the boot rec of the EXFAT volume.␊ |
*/␊ |
bool EXFATProbe(const void * buffer)␊ |
{␊ |
bool result = false;␊ |
␊ |
// boot sector structure␊ |
const struct exfatbootfile␉* boot = buffer;␊ |
␊ |
// Looking for EXFAT signature.␊ |
if (memcmp((const char *)boot->bf_sysid, EXFAT_BBID, EXFAT_BBIDLEN) == 0)␊ |
result = true;␊ |
␉␊ |
DBG("EXFATProbe: %d\n", result ? 1 : 0);␊ |
return result;␊ |
}␊ |