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

Archive Download this file

Revision: 1804