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