Chameleon

Chameleon Svn Source Tree

Root/branches/cparm/i386/libsaio/ntfs.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#include "libsaio.h"
26#include "sl.h"
27#include "ntfs.h"
28
29/*
30 * dmazar, 14/7/2011 - support for EXFAT volume label reading
31 */
32
33#include "exfat.h"
34
35//#define BYTE_ORDER_MARK0xFEFF
36
37#include "ntfs_private.h"
38
39//#define FS_TYPE"ntfs"
40//#define FS_NAME_FILE"NTFS"
41
42#define MAX_BLOCK_SIZE2048
43#define MAX_CLUSTER_SIZE32768
44
45//#define LABEL_LENGTH1024
46//#define UNKNOWN_LABEL"Untitled NTFS"
47
48//#define FSUR_IO_FAIL -1
49//#define FSUR_UNRECOGNIZED -1
50//#define FSUR_RECOGNIZED 0
51
52#define ERROR -1
53static int
54ntfs_fixup(
55 char *buf,
56 size_t len,
57 u_int32_t magic,
58 u_int32_t bytesPerSector);
59static int
60ntfs_find_attr(
61 char *buf,
62 u_int32_t attrType,
63 void **attrData,
64 size_t *attrSize);
65/*
66 * Process per-sector "fixups" that NTFS uses to detect corruption of
67 * multi-sector data structures, like MFT records.
68 */
69static int
70ntfs_fixup(
71 char *buf,
72 size_t len,
73 u_int32_t magic,
74 u_int32_t bytesPerSector)
75{
76struct fixuphdr *fhp = (struct fixuphdr *) buf;
77int i;
78u_int16_t fixup;
79u_int16_t *fxp;
80u_int16_t *cfxp;
81 u_int32_tfixup_magic;
82 u_int16_tfixup_count;
83 u_int16_tfixup_offset;
84
85 fixup_magic = OSReadLittleInt32(&fhp->fh_magic,0);
86if (fixup_magic != magic) {
87error("ntfs_fixup: magic doesn't match: %08x != %08x\n",
88 fixup_magic, magic);
89return (ERROR);
90}
91 fixup_count = OSReadLittleInt16(&fhp->fh_fnum,0);
92if ((fixup_count - 1) * bytesPerSector != len) {
93error("ntfs_fixup: " \
94 "bad fixups number: %d for %ld bytes block\n",
95 fixup_count, (long)len);/* XXX printf kludge */
96return (ERROR);
97}
98 fixup_offset = OSReadLittleInt16(&fhp->fh_foff,0);
99if (fixup_offset >= len) {
100error("ntfs_fixup: invalid offset: %x", fixup_offset);
101return (ERROR);
102}
103fxp = (u_int16_t *) (buf + fixup_offset);
104cfxp = (u_int16_t *) (buf + bytesPerSector - 2);
105fixup = *fxp++;
106for (i = 1; i < fixup_count; i++, fxp++) {
107if (*cfxp != fixup) {
108error("ntfs_fixup: fixup %d doesn't match\n", i);
109return (ERROR);
110}
111*cfxp = *fxp;
112 cfxp = (u_int16_t *)(((caddr_t)cfxp) + bytesPerSector);
113}
114return (0);
115}
116
117/*
118 * Find a resident attribute of a given type. Returns a pointer to the
119 * attribute data, and its size in bytes.
120 */
121static int
122ntfs_find_attr(
123 char *buf,
124 u_int32_t attrType,
125 void **attrData,
126 size_t *attrSize)
127{
128 struct filerec *filerec;
129 struct attr *attr;
130 u_int16_t offset;
131
132 filerec = (struct filerec *) buf;
133 offset = OSReadLittleInt16(&filerec->fr_attroff,0);
134 attr = (struct attr *) (buf + offset);
135
136
137 while (attr->a_hdr.a_type != 0xFFFFFFFF)/* same for big/little endian */
138 {
139 if (OSReadLittleInt32(&attr->a_hdr.a_type,0) == attrType)
140 {
141 if (attr->a_hdr.a_flag != 0)
142 {
143 //verbose("NTFS: attriubte 0x%X is non-resident\n", attrType);
144 return 1;
145 }
146
147 *attrSize = OSReadLittleInt16(&attr->a_r.a_datalen,0);
148 *attrData = buf + offset + OSReadLittleInt16(&attr->a_r.a_dataoff,0);
149 return 0;/* found it! */
150 }
151
152 /* Skip to the next attribute */
153 offset += OSReadLittleInt32(&attr->a_hdr.reclen,0);
154 attr = (struct attr *) (buf + offset);
155 }
156
157 return 1;/* No matching attrType found */
158}
159
160/*
161 * Examine a volume to see if we recognize it as a mountable.
162 */
163void
164NTFSGetDescription(CICell ih, char *str, long strMaxLen)
165{
166 struct bootfile *boot;
167 unsigned bytesPerSector;
168 unsigned sectorsPerCluster;
169 int mftRecordSize;
170 u_int64_t totalClusters;
171 u_int64_t cluster, mftCluster;
172 size_t mftOffset;
173 void *nameAttr;
174 size_t nameSize;
175 char *buf;
176
177 buf = (char *)malloc(MAX_CLUSTER_SIZE);
178 if (buf == 0) {
179 goto error;
180 }
181
182 /*
183 * Read the boot sector, check signatures, and do some minimal
184 * sanity checking. NOTE: the size of the read below is intended
185 * to be a multiple of all supported block sizes, so we don't
186 * have to determine or change the device's block size.
187 */
188 Seek(ih, 0);
189 Read(ih, (long)buf, MAX_BLOCK_SIZE);
190
191 boot = (struct bootfile *) buf;
192
193 /*
194 * The first three bytes are an Intel x86 jump instruction. I assume it
195 * can be the same forms as DOS FAT:
196 * 0xE9 0x?? 0x??
197 * 0xEC 0x?? 0x90
198 * where 0x?? means any byte value is OK.
199 */
200 if (boot->reserved1[0] != 0xE9
201 && (boot->reserved1[0] != 0xEB || boot->reserved1[2] != 0x90))
202 {
203 goto error;
204 }
205
206 /*
207 * Check the "NTFS " signature.
208 */
209 if (memcmp((const char *)boot->bf_sysid, "NTFS ", 8) != 0)
210 {
211 /*
212 * Check for EXFAT. Finish by jumping to error to free buf,
213 * although if it is EXFAT then it's no an error.
214 */
215
216EXFATGetDescription(ih, str, strMaxLen);
217
218goto error;
219 }
220
221 /*
222 * Make sure the bytes per sector and sectors per cluster are
223 * powers of two, and within reasonable ranges.
224 */
225 bytesPerSector = OSReadLittleInt16(&boot->bf_bps,0);
226 if ((bytesPerSector & (bytesPerSector-1)) || bytesPerSector < 512 || bytesPerSector > 32768)
227 {
228 //verbose("NTFS: invalid bytes per sector (%d)\n", bytesPerSector);
229 goto error;
230 }
231
232 sectorsPerCluster = boot->bf_spc;/* Just one byte; no swapping needed */
233 if ((sectorsPerCluster & (sectorsPerCluster-1)) || sectorsPerCluster > 128)
234 {
235 //verbose("NTFS: invalid sectors per cluster (%d)\n", bytesPerSector);
236 goto error;
237 }
238
239 /*
240 * Calculate the number of clusters from the number of sectors.
241 * Then bounds check the $MFT and $MFTMirr clusters.
242 */
243 totalClusters = OSReadLittleInt64(&boot->bf_spv,0) / sectorsPerCluster;
244 mftCluster = OSReadLittleInt64(&boot->bf_mftcn,0);
245 if (mftCluster > totalClusters)
246 {
247 ////verbose("NTFS: invalid $MFT cluster (%lld)\n", mftCluster);
248 goto error;
249 }
250 cluster = OSReadLittleInt64(&boot->bf_mftmirrcn,0);
251 if (cluster > totalClusters)
252 {
253 //verbose("NTFS: invalid $MFTMirr cluster (%lld)\n", cluster);
254 goto error;
255 }
256
257 /*
258 * Determine the size of an MFT record.
259 */
260 mftRecordSize = (int8_t) boot->bf_mftrecsz;
261 if (mftRecordSize < 0)
262 mftRecordSize = 1 << -mftRecordSize;
263 else
264 mftRecordSize *= bytesPerSector * sectorsPerCluster;
265 //verbose("NTFS: MFT record size = %d\n", mftRecordSize);
266
267 /*
268 * Read the MFT record for $Volume. This assumes the first four
269 * file records in the MFT are contiguous; if they aren't, we
270 * would have to map the $MFT itself.
271 *
272 * This will fail if the device sector size is larger than the
273 * MFT record size, since the $Volume record won't be aligned
274 * on a sector boundary.
275 */
276 mftOffset = mftCluster * sectorsPerCluster * bytesPerSector;
277 mftOffset += mftRecordSize * NTFS_VOLUMEINO;
278
279 Seek(ih, mftOffset);
280 Read(ih, (long)buf, mftRecordSize);
281#if UNUSED
282 if (lseek(fd, mftOffset, SEEK_SET) == -1)
283 {
284 //verbose("NTFS: lseek to $Volume failed: %s\n", strerror(errno));
285 goto error;
286 }
287 if (read(fd, buf, mftRecordSize) != mftRecordSize)
288 {
289 //verbose("NTFS: error reading MFT $Volume record: %s\n",
290 strerror(errno));
291 goto error;
292 }
293#endif
294
295 if (ntfs_fixup(buf, mftRecordSize, NTFS_FILEMAGIC, bytesPerSector) != 0)
296 {
297 //verbose("NTFS: block fixup failed\n");
298 goto error;
299 }
300
301 /*
302 * Loop over the attributes, looking for $VOLUME_NAME (0x60).
303 */
304 if(ntfs_find_attr(buf, NTFS_A_VOLUMENAME, &nameAttr, &nameSize) != 0)
305 {
306 //verbose("NTFS: $VOLUME_NAME attribute not found\n");
307 goto error;
308 }
309
310 str[0] = '\0';
311
312 utf_encodestr( nameAttr, nameSize / 2, (u_int8_t *)str, strMaxLen, OSLittleEndian );
313
314 free(buf);
315 return;
316
317 error:
318 if (buf) free(buf);
319 return;
320}
321
322long NTFSGetUUID(CICell ih, char *uuidStr, long strMaxLen)
323{
324bool NTFSProbe(const void*);
325
326struct bootfile *boot;
327void *buf = malloc(MAX_BLOCK_SIZE);
328if ( !buf )
329return -1;
330
331/*
332 * Read the boot sector, check signatures, and do some minimal
333 * sanity checking. NOTE: the size of the read below is intended
334 * to be a multiple of all supported block sizes, so we don't
335 * have to determine or change the device's block size.
336 */
337Seek(ih, 0);
338Read(ih, (long)buf, MAX_BLOCK_SIZE);
339
340boot = (struct bootfile *) buf;
341
342// Check for NTFS signature
343if ( memcmp((void*)boot->bf_sysid, NTFS_BBID, NTFS_BBIDLEN) != 0 ) {
344// If not NTFS, maybe it is EXFAT
345return EXFATGetUUID(ih, uuidStr, strMaxLen);
346}
347
348// Check for non-null volume serial number
349if( !boot->bf_volsn )
350return -1;
351
352// Use UUID like the one you get on Windows
353snprintf(uuidStr,strMaxLen, "%04X-%04X",(unsigned short)(boot->bf_volsn >> 16) & 0xFFFF,
354(unsigned short)boot->bf_volsn & 0xFFFF);
355
356return 0;
357}
358
359bool NTFSProbe(const void * buffer)
360{
361bool result = false;
362
363const struct bootfile* part_bootfile = buffer;// NTFS boot sector structure
364
365// Looking for NTFS signature.
366if (strncmp((const char *)part_bootfile->bf_sysid, NTFS_BBID, NTFS_BBIDLEN) == 0)
367result = true;
368
369// If not NTFS, maybe it is EXFAT
370if (!result == false)
371result = EXFATProbe(buffer);
372
373return result;
374}
375

Archive Download this file

Revision: 2117