Chameleon

Chameleon Svn Source Tree

Root/branches/ErmaC/Trunk/i386/libsaio/ntfs.c

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

Archive Download this file

Revision: 2045