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 | #include "exfat.h"␊ |
39 | ␊ |
40 | #ifndef DEBUG_EXFAT␊ |
41 | #define DEBUG_EXFAT 0␊ |
42 | #endif␊ |
43 | ␊ |
44 | #if DEBUG_EXFAT␊ |
45 | #define DBG(x...)␉␉printf(x)␊ |
46 | #define PAUSE()␉␉␉getc()␊ |
47 | #else␊ |
48 | #define DBG(x...)␊ |
49 | #define PAUSE()␊ |
50 | #endif␊ |
51 | ␊ |
52 | #define␉EXFAT_BBID␉"EXFAT "␊ |
53 | #define␉EXFAT_BBIDLEN␉8␊ |
54 | ␊ |
55 | #define MAX_BLOCK_SIZE␉␉4096␊ |
56 | #define MAX_CLUSTER_SIZE␉32 * 1024 * 1024␊ |
57 | ␊ |
58 | #define ERROR -1␊ |
59 | ␊ |
60 | ␊ |
61 | /*␊ |
62 | * boot sector of the partition␊ |
63 | * http://www.ntfs.com/exfat-boot-sector.htm␊ |
64 | */␊ |
65 | struct exfatbootfile {␊ |
66 | ␉u_int8_t reserved1[3];␉/* JumpBoot: 0xEB7690 */␊ |
67 | ␉u_int8_t bf_sysid[8];␉/* FileSystemName: 'EXFAT ' */␊ |
68 | ␉u_int8_t reserved2[53];␉/* MustBeZero */␊ |
69 | ␉u_int64_t bf_prtoff;␉␉/* PartitionOffset: In sectors; if 0, shall be ignored */␊ |
70 | ␉u_int64_t bf_vollen;␉␉/* VolumeLength: Size of exFAT volume in sectors */␊ |
71 | ␉u_int32_t bf_fatoff;␉␉/* FatOffset: In sectors */␊ |
72 | ␉u_int32_t bf_fatlen;␉␉/* FatLength: In sectors. May exceed the required space in order to align the second FAT */␊ |
73 | ␉u_int32_t bf_cloff;␉␉/* ClusterHeapOffset: In sectors. */␊ |
74 | ␉u_int32_t bf_clcnt;␉␉/* ClusterCount: 2^32-11 is the maximum number of clusters could be described. */␊ |
75 | ␉u_int32_t bf_rdircl;␉␉/* RootDirectoryCluster. */␊ |
76 | ␉u_int32_t bf_volsn;␉␉/* VolumeSerialNumber. */␊ |
77 | ␉u_int16_t bf_fsrev;␉␉/* FileSystemRevision: as MAJOR.minor, major revision is high byte, minor is low byte; currently 01.00. */␊ |
78 | ␉u_int16_t bf_volflags;␉/* VolumeFlags. */␊ |
79 | ␉u_int8_t bf_bpss;␉␉/* BytesPerSectorShift: Power of 2. Minimum 9 (512 bytes per sector), maximum 12 (4096 bytes per sector) */␊ |
80 | ␉u_int8_t bf_spcs;␉␉/* SectorsPerClusterShift: Power of 2. Minimum 0 (1 sector per cluster), maximum 25 – BytesPerSectorShift, so max cluster size is 32 MB */␊ |
81 | ␉u_int8_t bf_nfats;␉␉/* NumberOfFats: 2 is for TexFAT only */␊ |
82 | ␉u_int8_t bf_drvs;␉␉/* DriveSelect: Extended INT 13h drive number; typically 0x80 */␊ |
83 | ␉u_int8_t bf_pused;␉␉/* PercentInUse: 0..100 – percentage of allocated clusters rounded down to the integer 0xFF – percentage is not available */␊ |
84 | ␉u_int8_t reserved3[7];␉/* Reserved */␊ |
85 | ␉u_int8_t bootcode[390];␉/* BootCode */␊ |
86 | ␉u_int16_t bf_bsig;␉␉/* BootSignature: 0xAA55 */␊ |
87 | };␊ |
88 | ␊ |
89 | struct direntry_label {␊ |
90 | ␉u_int8_t type;␉␉␉/* EntryType: 0x83 (or 0x03 if label is empty) */␊ |
91 | ␉u_int8_t llen;␉␉␉/* CharacterCount: Length in Unicode characters (max 11) */␊ |
92 | ␉u_int16_t label[11];␉␉/* VolumeLabel: Unicode characters (max 11) */␊ |
93 | ␉u_int8_t reserved1[8];␉/* Reserved */␊ |
94 | };␊ |
95 | ␊ |
96 | ␊ |
97 | /**␊ |
98 | * Reads volume label into str.␊ |
99 | * Reads boot sector, performs dome checking, loads root dir␊ |
100 | * and parses volume label.␊ |
101 | */␊ |
102 | void␊ |
103 | EXFATGetDescription(CICell ih, char *str, long strMaxLen)␊ |
104 | {␊ |
105 | struct exfatbootfile *boot;␊ |
106 | u_int32_t bytesPerSector = 0;␊ |
107 | u_int32_t sectorsPerCluster = 0;␊ |
108 | long long rdirOffset = 0;␊ |
109 | char *buf = NULL;␊ |
110 | struct direntry_label *dire = NULL;␊ |
111 | int loopControl = 0;␊ |
112 | ␉␊ |
113 | DBG("EXFAT: start %x:%x\n", ih->biosdev, ih->part_no);␊ |
114 | ␉␊ |
115 | buf = (char *)malloc(MAX_BLOCK_SIZE);␊ |
116 | if (buf == 0)␊ |
117 | {␊ |
118 | goto error;␊ |
119 | }␊ |
120 | ␉bzero(buf,MAX_BLOCK_SIZE );␊ |
121 | ␉␊ |
122 | /*␊ |
123 | * Read the boot sector, check signatures, and do some minimal␊ |
124 | * sanity checking. NOTE: the size of the read below is intended␊ |
125 | * to be a multiple of all supported block sizes, so we don't␊ |
126 | * have to determine or change the device's block size.␊ |
127 | */␊ |
128 | Seek(ih, 0);␊ |
129 | Read(ih, (long)buf, MAX_BLOCK_SIZE);␊ |
130 | ␉␊ |
131 | // take our boot structure␊ |
132 | boot = (struct exfatbootfile *) buf;␊ |
133 | ␊ |
134 | /*␊ |
135 | * The first three bytes are an Intel x86 jump instruction. I assume it␊ |
136 | * can be the same forms as DOS FAT:␊ |
137 | * 0xE9 0x?? 0x??␊ |
138 | * 0xEC 0x?? 0x90␊ |
139 | * where 0x?? means any byte value is OK.␊ |
140 | */␊ |
141 | if (boot->reserved1[0] != 0xE9␊ |
142 | && (boot->reserved1[0] != 0xEB || boot->reserved1[2] != 0x90))␊ |
143 | {␊ |
144 | goto error;␊ |
145 | }␊ |
146 | ␉␊ |
147 | ␉// Check the "EXFAT " signature.␊ |
148 | if (memcmp((const char *)boot->bf_sysid, EXFAT_BBID, EXFAT_BBIDLEN) != 0)␊ |
149 | {␊ |
150 | goto error;␊ |
151 | }␊ |
152 | ␉␊ |
153 | /*␊ |
154 | * Make sure the bytes per sector and sectors per cluster are␊ |
155 | * powers of two, and within reasonable ranges.␊ |
156 | */␊ |
157 | bytesPerSector = 1 << boot->bf_bpss;␉/* Just one byte; no swapping needed */␊ |
158 | DBG("EXFAT: bpss=%d, bytesPerSector=%d\n", boot->bf_bpss, bytesPerSector);␊ |
159 | if (boot->bf_bpss < 9 || boot->bf_bpss > 12)␊ |
160 | {␊ |
161 | DBG("EXFAT: invalid bytes per sector shift(%d)\n", boot->bf_bpss);␊ |
162 | goto error;␊ |
163 | }␊ |
164 | ␉␊ |
165 | sectorsPerCluster = 1 << boot->bf_spcs;␉/* Just one byte; no swapping needed */␊ |
166 | DBG("EXFAT: spcs=%d, sectorsPerCluster=%d\n", boot->bf_spcs, sectorsPerCluster);␊ |
167 | if (boot->bf_spcs > (25 - boot->bf_bpss))␊ |
168 | {␊ |
169 | DBG("EXFAT: invalid sectors per cluster shift (%d)\n", boot->bf_spcs);␊ |
170 | goto error;␊ |
171 | }␊ |
172 | ␊ |
173 | ␉// calculate root dir cluster offset␊ |
174 | rdirOffset = boot->bf_cloff + (boot->bf_rdircl - 2) * sectorsPerCluster;␊ |
175 | DBG("EXFAT: rdirOffset=%d\n", rdirOffset);␊ |
176 | ␉␊ |
177 | // load MAX_BLOCK_SIZE bytes of root dir␊ |
178 | Seek(ih, rdirOffset * bytesPerSector);␊ |
179 | Read(ih, (long)buf, MAX_BLOCK_SIZE);␊ |
180 | DBG("buf 0 1 2 = %x %x %x\n", 0x00ff & buf[0], 0x00ff & buf[1], 0x00ff & buf[2]);␊ |
181 | ␉␊ |
182 | str[0] = '\0';␊ |
183 | ␉␊ |
184 | /*␊ |
185 | * Search for volume label dir entry (type 0x83), convert from unicode and put to str.␊ |
186 | * Set loopControl var to avoid searching outside of buf.␊ |
187 | */␊ |
188 | loopControl = MAX_BLOCK_SIZE / sizeof(struct direntry_label);␊ |
189 | dire = (struct direntry_label *)buf;␊ |
190 | while (loopControl && dire->type && dire->type != 0x83)␊ |
191 | {␊ |
192 | dire++;␊ |
193 | loopControl--;␊ |
194 | }␊ |
195 | if (dire->type == 0x83 && dire->llen > 0 && dire->llen <= 11)␊ |
196 | {␊ |
197 | utf_encodestr( dire->label, (int)dire->llen, (u_int8_t *)str, strMaxLen, OSLittleEndian );␊ |
198 | }␊ |
199 | DBG("EXFAT: label=%s\n", str);␊ |
200 | ␉␊ |
201 | free(buf);␊ |
202 | PAUSE();␊ |
203 | return;␊ |
204 | ␉␊ |
205 | error:␊ |
206 | if (buf) free(buf);␊ |
207 | DBG("EXFAT: error\n");␊ |
208 | PAUSE();␊ |
209 | return;␊ |
210 | }␊ |
211 | ␊ |
212 | /**␊ |
213 | * Sets UUID to uuidStr.␊ |
214 | * Reads the boot sector, does some checking, generates UUID␊ |
215 | * (like the one you get on Windows???)␊ |
216 | */␊ |
217 | long EXFATGetUUID(CICell ih, char *uuidStr, long strMaxLen)␊ |
218 | {␊ |
219 | struct exfatbootfile *boot;␊ |
220 | void *buf = malloc(MAX_BLOCK_SIZE);␊ |
221 | if ( !buf )␊ |
222 | return -1;␊ |
223 | ␉␊ |
224 | ␉bzero(buf,MAX_BLOCK_SIZE );␊ |
225 | ␉␊ |
226 | /*␊ |
227 | * Read the boot sector, check signatures, and do some minimal␊ |
228 | * sanity checking.␉ NOTE: the size of the read below is intended␊ |
229 | * to be a multiple of all supported block sizes, so we don't␊ |
230 | * have to determine or change the device's block size.␊ |
231 | */␊ |
232 | Seek(ih, 0);␊ |
233 | Read(ih, (long)buf, MAX_BLOCK_SIZE);␊ |
234 | ␉␊ |
235 | boot = (struct exfatbootfile *) buf;␊ |
236 | ␉␊ |
237 | /*␊ |
238 | * Check the "EXFAT " signature.␊ |
239 | */␊ |
240 | if (memcmp((const char *)boot->bf_sysid, EXFAT_BBID, EXFAT_BBIDLEN) != 0)␊ |
241 | return -1;␊ |
242 | ␉␊ |
243 | // Check for non-null volume serial number␊ |
244 | if( !boot->bf_volsn )␊ |
245 | return -1;␊ |
246 | ␉␊ |
247 | // Use UUID like the one you get on Windows␊ |
248 | snprintf(uuidStr, strMaxLen,"%04X-%04X", (unsigned short)(boot->bf_volsn >> 16) & 0xFFFF,␊ |
249 | ␉␉␉ (unsigned short)boot->bf_volsn & 0xFFFF);␊ |
250 | ␉␊ |
251 | DBG("EXFATGetUUID: %x:%x = %s\n", ih->biosdev, ih->part_no, uuidStr);␊ |
252 | return 0;␊ |
253 | } ␊ |
254 | ␊ |
255 | /**␊ |
256 | * Returns true if given buffer is the boot rec of the EXFAT volume.␊ |
257 | */␊ |
258 | bool EXFATProbe(const void * buffer)␊ |
259 | {␊ |
260 | bool result = false;␊ |
261 | ␉␊ |
262 | // boot sector structure␊ |
263 | const struct exfatbootfile␉* boot = buffer;␊ |
264 | ␉␊ |
265 | // Looking for EXFAT signature.␊ |
266 | if (memcmp((const char *)boot->bf_sysid, EXFAT_BBID, EXFAT_BBIDLEN) == 0)␊ |
267 | result = true;␊ |
268 | ␉␊ |
269 | DBG("EXFATProbe: %d\n", result ? 1 : 0);␊ |
270 | return result;␊ |
271 | }␊ |
272 | |