Chameleon

Chameleon Svn Source Tree

Root/branches/cparm/i386/libsaio/exfat.c

1
2/*
3 * Copyright (c) 2004 Apple Computer, Inc. All rights reserved.
4 *
5 * @APPLE_LICENSE_HEADER_START@
6 *
7 * Copyright (c) 1999-2004 Apple Computer, Inc. All Rights Reserved.
8 *
9 * This file contains Original Code and/or Modifications of Original Code
10 * as defined in and that are subject to the Apple Public Source License
11 * Version 2.0 (the 'License'). You may not use this file except in
12 * compliance with the License. Please obtain a copy of the License at
13 * http://www.opensource.apple.com/apsl/ and read it before using this
14 * file.
15 *
16 * The Original Code and all software distributed under the License are
17 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
18 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
19 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
20 * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
21 * Please see the License for the specific language governing rights and
22 * limitations under the License.
23 *
24 * @APPLE_LICENSE_HEADER_END@
25 */
26/*
27 * dmazar, 14/7/2011
28 * support for EXFAT volume label reading
29 * EXFAT info from: http://www.ntfs.com/exfat-overview.htm
30 *
31 * EXFAT shares partition type with NTFS (0x7) and easiest way of
32 * adding it was through ntfs.c module. All functions here are called
33 * from similar ntfs.c funcs as fallback (if not NTFS, maybe it's EXFAT).
34 */
35
36#include "libsaio.h"
37#include "sl.h"
38
39#ifndef DEBUG_EXFAT
40#define DEBUG_EXFAT 0
41#endif
42
43#if DEBUG_EXFAT
44#define DBG(x...)printf(x)
45#define PAUSE()getc()
46#else
47#define DBG(x...)
48#define PAUSE()
49#endif
50
51#defineEXFAT_BBID"EXFAT "
52#defineEXFAT_BBIDLEN8
53
54#define MAX_BLOCK_SIZE4096
55#define MAX_CLUSTER_SIZE32 * 1024 * 1024
56
57#define ERROR -1
58
59
60/*
61 * boot sector of the partition
62 * http://www.ntfs.com/exfat-boot-sector.htm
63 */
64struct exfatbootfile {
65u_int8_t reserved1[3];/* JumpBoot: 0xEB7690 */
66u_int8_t bf_sysid[8];/* FileSystemName: 'EXFAT ' */
67u_int8_t reserved2[53];/* MustBeZero */
68u_int64_t bf_prtoff;/* PartitionOffset: In sectors; if 0, shall be ignored */
69u_int64_t bf_vollen;/* VolumeLength: Size of exFAT volume in sectors */
70u_int32_t bf_fatoff;/* FatOffset: In sectors */
71u_int32_t bf_fatlen;/* FatLength: In sectors. May exceed the required space in order to align the second FAT */
72u_int32_t bf_cloff;/* ClusterHeapOffset: In sectors. */
73u_int32_t bf_clcnt;/* ClusterCount: 2^32-11 is the maximum number of clusters could be described. */
74u_int32_t bf_rdircl;/* RootDirectoryCluster. */
75u_int32_t bf_volsn;/* VolumeSerialNumber. */
76u_int16_t bf_fsrev;/* FileSystemRevision: as MAJOR.minor, major revision is high byte, minor is low byte; currently 01.00. */
77u_int16_t bf_volflags;/* VolumeFlags. */
78u_int8_t bf_bpss;/* BytesPerSectorShift: Power of 2. Minimum 9 (512 bytes per sector), maximum 12 (4096 bytes per sector) */
79u_int8_t bf_spcs;/* SectorsPerClusterShift: Power of 2. Minimum 0 (1 sector per cluster), maximum 25 – BytesPerSectorShift, so max cluster size is 32 MB */
80u_int8_t bf_nfats;/* NumberOfFats: 2 is for TexFAT only */
81u_int8_t bf_drvs;/* DriveSelect: Extended INT 13h drive number; typically 0x80 */
82u_int8_t bf_pused;/* PercentInUse: 0..100 – percentage of allocated clusters rounded down to the integer 0xFF – percentage is not available */
83u_int8_t reserved3[7];/* Reserved */
84u_int8_t bootcode[390];/* BootCode */
85u_int16_t bf_bsig;/* BootSignature: 0xAA55 */
86};
87
88struct direntry_label {
89u_int8_t type;/* EntryType: 0x83 (or 0x03 if label is empty) */
90u_int8_t llen;/* CharacterCount: Length in Unicode characters (max 11) */
91u_int16_t label[11];/* VolumeLabel: Unicode characters (max 11) */
92u_int8_t reserved1[8];/* Reserved */
93};
94
95
96/**
97 * Reads volume label into str.
98 * Reads boot sector, performs dome checking, loads root dir
99 * and parses volume label.
100 */
101void
102EXFATGetDescription(CICell ih, char *str, long strMaxLen)
103{
104 struct exfatbootfile *boot;
105 u_int32_t bytesPerSector = 0;
106 u_int32_t sectorsPerCluster = 0;
107 long long rdirOffset = 0;
108 char *buf = NULL;
109 struct direntry_label *dire = NULL;
110 int loopControl = 0;
111
112 DBG("EXFAT: start %x:%x\n", ih->biosdev, ih->part_no);
113
114 buf = (char *)malloc(MAX_BLOCK_SIZE);
115 if (buf == 0)
116 {
117 goto error;
118 }
119
120 /*
121 * Read the boot sector, check signatures, and do some minimal
122 * sanity checking. NOTE: the size of the read below is intended
123 * to be a multiple of all supported block sizes, so we don't
124 * have to determine or change the device's block size.
125 */
126 Seek(ih, 0);
127 Read(ih, (long)buf, MAX_BLOCK_SIZE);
128
129 // take our boot structure
130 boot = (struct exfatbootfile *) buf;
131
132 /*
133 * The first three bytes are an Intel x86 jump instruction. I assume it
134 * can be the same forms as DOS FAT:
135 * 0xE9 0x?? 0x??
136 * 0xEC 0x?? 0x90
137 * where 0x?? means any byte value is OK.
138 */
139 if (boot->reserved1[0] != 0xE9
140 && (boot->reserved1[0] != 0xEB || boot->reserved1[2] != 0x90))
141 {
142 goto error;
143 }
144
145 // Check the "EXFAT " signature.
146 if (memcmp((const char *)boot->bf_sysid, EXFAT_BBID, EXFAT_BBIDLEN) != 0)
147 {
148 goto error;
149 }
150
151 /*
152 * Make sure the bytes per sector and sectors per cluster are
153 * powers of two, and within reasonable ranges.
154 */
155 bytesPerSector = 1 << boot->bf_bpss;/* Just one byte; no swapping needed */
156 DBG("EXFAT: bpss=%d, bytesPerSector=%d\n", boot->bf_bpss, bytesPerSector);
157 if (boot->bf_bpss < 9 || boot->bf_bpss > 12)
158 {
159 DBG("EXFAT: invalid bytes per sector shift(%d)\n", boot->bf_bpss);
160 goto error;
161 }
162
163 sectorsPerCluster = 1 << boot->bf_spcs;/* Just one byte; no swapping needed */
164 DBG("EXFAT: spcs=%d, sectorsPerCluster=%d\n", boot->bf_spcs, sectorsPerCluster);
165 if (boot->bf_spcs > (25 - boot->bf_bpss))
166 {
167 DBG("EXFAT: invalid sectors per cluster shift (%d)\n", boot->bf_spcs);
168 goto error;
169 }
170
171 // calculate root dir cluster offset
172 rdirOffset = boot->bf_cloff + (boot->bf_rdircl - 2) * sectorsPerCluster;
173 DBG("EXFAT: rdirOffset=%d\n", rdirOffset);
174
175 // load MAX_BLOCK_SIZE bytes of root dir
176 Seek(ih, rdirOffset * bytesPerSector);
177 Read(ih, (long)buf, MAX_BLOCK_SIZE);
178 DBG("buf 0 1 2 = %x %x %x\n", 0x00ff & buf[0], 0x00ff & buf[1], 0x00ff & buf[2]);
179
180 str[0] = '\0';
181
182 /*
183 * Search for volume label dir entry (type 0x83), convert from unicode and put to str.
184 * Set loopControl var to avoid searching outside of buf.
185 */
186 loopControl = MAX_BLOCK_SIZE / sizeof(struct direntry_label);
187 dire = (struct direntry_label *)buf;
188 while (loopControl && dire->type && dire->type != 0x83)
189 {
190 dire++;
191 loopControl--;
192 }
193 if (dire->type == 0x83 && dire->llen > 0 && dire->llen <= 11)
194 {
195 utf_encodestr( dire->label, (int)dire->llen, (u_int8_t *)str, strMaxLen, OSLittleEndian );
196 }
197 DBG("EXFAT: label=%s\n", str);
198
199 free(buf);
200 PAUSE();
201 return;
202
203 error:
204 if (buf) free(buf);
205 DBG("EXFAT: error\n");
206 PAUSE();
207 return;
208}
209
210/**
211 * Sets UUID to uuidStr.
212 * Reads the boot sector, does some checking, generates UUID
213 * (like the one you get on Windows???)
214 */
215long EXFATGetUUID(CICell ih, char *uuidStr)
216{
217 struct exfatbootfile *boot;
218 void *buf = malloc(MAX_BLOCK_SIZE);
219 if ( !buf )
220 return -1;
221
222 /*
223 * Read the boot sector, check signatures, and do some minimal
224 * sanity checking. NOTE: the size of the read below is intended
225 * to be a multiple of all supported block sizes, so we don't
226 * have to determine or change the device's block size.
227 */
228 Seek(ih, 0);
229 Read(ih, (long)buf, MAX_BLOCK_SIZE);
230
231 boot = (struct exfatbootfile *) buf;
232
233 /*
234 * Check the "EXFAT " signature.
235 */
236 if (memcmp((const char *)boot->bf_sysid, EXFAT_BBID, EXFAT_BBIDLEN) != 0)
237 return -1;
238
239 // Check for non-null volume serial number
240 if( !boot->bf_volsn )
241 return -1;
242
243 // Use UUID like the one you get on Windows
244 sprintf(uuidStr, "%04X-%04X", (unsigned short)(boot->bf_volsn >> 16) & 0xFFFF,
245 (unsigned short)boot->bf_volsn & 0xFFFF);
246
247 DBG("EXFATGetUUID: %x:%x = %s\n", ih->biosdev, ih->part_no, uuidStr);
248 return 0;
249}
250
251/**
252 * Returns true if given buffer is the boot rec of the EXFAT volume.
253 */
254bool EXFATProbe(const void * buffer)
255{
256 bool result = false;
257
258 // boot sector structure
259 const struct exfatbootfile* boot = buffer;
260
261 // Looking for EXFAT signature.
262 if (memcmp((const char *)boot->bf_sysid, EXFAT_BBID, EXFAT_BBIDLEN) == 0)
263 result = true;
264
265 DBG("EXFATProbe: %d\n", result ? 1 : 0);
266 return result;
267}
268

Archive Download this file

Revision: 2006