Chameleon

Chameleon Svn Source Tree

Root/branches/slice/trunkM/i386/libsaio/exfat.c

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

Archive Download this file

Revision: 1184