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#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#defineEXFAT_BBID"EXFAT "
53#defineEXFAT_BBIDLEN8
54
55#define MAX_BLOCK_SIZE4096
56#define MAX_CLUSTER_SIZE32 * 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 */
65struct exfatbootfile {
66u_int8_t reserved1[3];/* JumpBoot: 0xEB7690 */
67u_int8_t bf_sysid[8];/* FileSystemName: 'EXFAT ' */
68u_int8_t reserved2[53];/* MustBeZero */
69u_int64_t bf_prtoff;/* PartitionOffset: In sectors; if 0, shall be ignored */
70u_int64_t bf_vollen;/* VolumeLength: Size of exFAT volume in sectors */
71u_int32_t bf_fatoff;/* FatOffset: In sectors */
72u_int32_t bf_fatlen;/* FatLength: In sectors. May exceed the required space in order to align the second FAT */
73u_int32_t bf_cloff;/* ClusterHeapOffset: In sectors. */
74u_int32_t bf_clcnt;/* ClusterCount: 2^32-11 is the maximum number of clusters could be described. */
75u_int32_t bf_rdircl;/* RootDirectoryCluster. */
76u_int32_t bf_volsn;/* VolumeSerialNumber. */
77u_int16_t bf_fsrev;/* FileSystemRevision: as MAJOR.minor, major revision is high byte, minor is low byte; currently 01.00. */
78u_int16_t bf_volflags;/* VolumeFlags. */
79u_int8_t bf_bpss;/* BytesPerSectorShift: Power of 2. Minimum 9 (512 bytes per sector), maximum 12 (4096 bytes per sector) */
80u_int8_t bf_spcs;/* SectorsPerClusterShift: Power of 2. Minimum 0 (1 sector per cluster), maximum 25 – BytesPerSectorShift, so max cluster size is 32 MB */
81u_int8_t bf_nfats;/* NumberOfFats: 2 is for TexFAT only */
82u_int8_t bf_drvs;/* DriveSelect: Extended INT 13h drive number; typically 0x80 */
83u_int8_t bf_pused;/* PercentInUse: 0..100 – percentage of allocated clusters rounded down to the integer 0xFF – percentage is not available */
84u_int8_t reserved3[7];/* Reserved */
85u_int8_t bootcode[390];/* BootCode */
86u_int16_t bf_bsig;/* BootSignature: 0xAA55 */
87};
88
89struct direntry_label {
90u_int8_t type;/* EntryType: 0x83 (or 0x03 if label is empty) */
91u_int8_t llen;/* CharacterCount: Length in Unicode characters (max 11) */
92u_int16_t label[11];/* VolumeLabel: Unicode characters (max 11) */
93u_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 */
102void
103EXFATGetDescription(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 }
120bzero(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
205error:
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 */
217long 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
224bzero(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 */
258bool 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

Archive Download this file

Revision: 2182