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 }
120
121 /*
122 * Read the boot sector, check signatures, and do some minimal
123 * sanity checking. NOTE: the size of the read below is intended
124 * to be a multiple of all supported block sizes, so we don't
125 * have to determine or change the device's block size.
126 */
127 Seek(ih, 0);
128 Read(ih, (long)buf, MAX_BLOCK_SIZE);
129
130 // take our boot structure
131 boot = (struct exfatbootfile *) buf;
132
133 /*
134 * The first three bytes are an Intel x86 jump instruction. I assume it
135 * can be the same forms as DOS FAT:
136 * 0xE9 0x?? 0x??
137 * 0xEC 0x?? 0x90
138 * where 0x?? means any byte value is OK.
139 */
140 if (boot->reserved1[0] != 0xE9
141 && (boot->reserved1[0] != 0xEB || boot->reserved1[2] != 0x90))
142 {
143 goto error;
144 }
145
146 // Check the "EXFAT " signature.
147 if (memcmp((const char *)boot->bf_sysid, EXFAT_BBID, EXFAT_BBIDLEN) != 0)
148 {
149 goto error;
150 }
151
152 /*
153 * Make sure the bytes per sector and sectors per cluster are
154 * powers of two, and within reasonable ranges.
155 */
156 bytesPerSector = 1 << boot->bf_bpss;/* Just one byte; no swapping needed */
157 DBG("EXFAT: bpss=%d, bytesPerSector=%d\n", boot->bf_bpss, bytesPerSector);
158 if (boot->bf_bpss < 9 || boot->bf_bpss > 12)
159 {
160 DBG("EXFAT: invalid bytes per sector shift(%d)\n", boot->bf_bpss);
161 goto error;
162 }
163
164 sectorsPerCluster = 1 << boot->bf_spcs;/* Just one byte; no swapping needed */
165 DBG("EXFAT: spcs=%d, sectorsPerCluster=%d\n", boot->bf_spcs, sectorsPerCluster);
166 if (boot->bf_spcs > (25 - boot->bf_bpss))
167 {
168 DBG("EXFAT: invalid sectors per cluster shift (%d)\n", boot->bf_spcs);
169 goto error;
170 }
171
172 // calculate root dir cluster offset
173 rdirOffset = boot->bf_cloff + (boot->bf_rdircl - 2) * sectorsPerCluster;
174 DBG("EXFAT: rdirOffset=%d\n", rdirOffset);
175
176 // load MAX_BLOCK_SIZE bytes of root dir
177 Seek(ih, rdirOffset * bytesPerSector);
178 Read(ih, (long)buf, MAX_BLOCK_SIZE);
179 DBG("buf 0 1 2 = %x %x %x\n", 0x00ff & buf[0], 0x00ff & buf[1], 0x00ff & buf[2]);
180
181 str[0] = '\0';
182
183 /*
184 * Search for volume label dir entry (type 0x83), convert from unicode and put to str.
185 * Set loopControl var to avoid searching outside of buf.
186 */
187 loopControl = MAX_BLOCK_SIZE / sizeof(struct direntry_label);
188 dire = (struct direntry_label *)buf;
189 while (loopControl && dire->type && dire->type != 0x83)
190 {
191 dire++;
192 loopControl--;
193 }
194 if (dire->type == 0x83 && dire->llen > 0 && dire->llen <= 11)
195 {
196 utf_encodestr( dire->label, (int)dire->llen, (u_int8_t *)str, strMaxLen, OSLittleEndian );
197 }
198 DBG("EXFAT: label=%s\n", str);
199
200 free(buf);
201 PAUSE();
202 return;
203
204 error:
205 if (buf) free(buf);
206 DBG("EXFAT: error\n");
207 PAUSE();
208 return;
209}
210
211/**
212 * Sets UUID to uuidStr.
213 * Reads the boot sector, does some checking, generates UUID
214 * (like the one you get on Windows???)
215 */
216long EXFATGetUUID(CICell ih, char *uuidStr, long strMaxLen)
217{
218 struct exfatbootfile *boot;
219 void *buf = malloc(MAX_BLOCK_SIZE);
220 if ( !buf )
221 return -1;
222
223 /*
224 * Read the boot sector, check signatures, and do some minimal
225 * sanity checking. NOTE: the size of the read below is intended
226 * to be a multiple of all supported block sizes, so we don't
227 * have to determine or change the device's block size.
228 */
229 Seek(ih, 0);
230 Read(ih, (long)buf, MAX_BLOCK_SIZE);
231
232 boot = (struct exfatbootfile *) buf;
233
234 /*
235 * Check the "EXFAT " signature.
236 */
237 if (memcmp((const char *)boot->bf_sysid, EXFAT_BBID, EXFAT_BBIDLEN) != 0)
238 return -1;
239
240 // Check for non-null volume serial number
241 if( !boot->bf_volsn )
242 return -1;
243
244 // Use UUID like the one you get on Windows
245 snprintf(uuidStr, strMaxLen,"%04X-%04X", (unsigned short)(boot->bf_volsn >> 16) & 0xFFFF,
246 (unsigned short)boot->bf_volsn & 0xFFFF);
247
248 DBG("EXFATGetUUID: %x:%x = %s\n", ih->biosdev, ih->part_no, uuidStr);
249 return 0;
250}
251
252/**
253 * Returns true if given buffer is the boot rec of the EXFAT volume.
254 */
255bool EXFATProbe(const void * buffer)
256{
257 bool result = false;
258
259 // boot sector structure
260 const struct exfatbootfile* boot = buffer;
261
262 // Looking for EXFAT signature.
263 if (memcmp((const char *)boot->bf_sysid, EXFAT_BBID, EXFAT_BBIDLEN) == 0)
264 result = true;
265
266 DBG("EXFATProbe: %d\n", result ? 1 : 0);
267 return result;
268}
269

Archive Download this file

Revision: 2117