]> git.saurik.com Git - apple/boot.git/blob - i386/libsaio/ntfs.c
boot-132.tar.gz
[apple/boot.git] / 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
26 #include "libsaio.h"
27 #include "sl.h"
28
29 #define BYTE_ORDER_MARK 0xFEFF
30
31 #include "ntfs_private.h"
32
33 #define FS_TYPE "ntfs"
34 #define FS_NAME_FILE "NTFS"
35
36 #define MAX_BLOCK_SIZE 2048
37 #define MAX_CLUSTER_SIZE 32768
38
39 #define LABEL_LENGTH 1024
40 #define UNKNOWN_LABEL "Untitled NTFS"
41
42 #define FSUR_IO_FAIL -1
43 #define FSUR_UNRECOGNIZED -1
44 #define FSUR_RECOGNIZED 0
45
46 #define ERROR -1
47
48 /*
49 * Process per-sector "fixups" that NTFS uses to detect corruption of
50 * multi-sector data structures, like MFT records.
51 */
52 static int
53 ntfs_fixup(
54 char *buf,
55 size_t len,
56 u_int32_t magic,
57 u_int32_t bytesPerSector)
58 {
59 struct fixuphdr *fhp = (struct fixuphdr *) buf;
60 int i;
61 u_int16_t fixup;
62 u_int16_t *fxp;
63 u_int16_t *cfxp;
64 u_int32_t fixup_magic;
65 u_int16_t fixup_count;
66 u_int16_t fixup_offset;
67
68 fixup_magic = OSReadLittleInt32(&fhp->fh_magic,0);
69 if (fixup_magic != magic) {
70 error("ntfs_fixup: magic doesn't match: %08x != %08x\n",
71 fixup_magic, magic);
72 return (ERROR);
73 }
74 fixup_count = OSReadLittleInt16(&fhp->fh_fnum,0);
75 if ((fixup_count - 1) * bytesPerSector != len) {
76 error("ntfs_fixup: " \
77 "bad fixups number: %d for %ld bytes block\n",
78 fixup_count, (long)len); /* XXX printf kludge */
79 return (ERROR);
80 }
81 fixup_offset = OSReadLittleInt16(&fhp->fh_foff,0);
82 if (fixup_offset >= len) {
83 error("ntfs_fixup: invalid offset: %x", fixup_offset);
84 return (ERROR);
85 }
86 fxp = (u_int16_t *) (buf + fixup_offset);
87 cfxp = (u_int16_t *) (buf + bytesPerSector - 2);
88 fixup = *fxp++;
89 for (i = 1; i < fixup_count; i++, fxp++) {
90 if (*cfxp != fixup) {
91 error("ntfs_fixup: fixup %d doesn't match\n", i);
92 return (ERROR);
93 }
94 *cfxp = *fxp;
95 cfxp = (u_int16_t *)(((caddr_t)cfxp) + bytesPerSector);
96 }
97 return (0);
98 }
99
100 /*
101 * Find a resident attribute of a given type. Returns a pointer to the
102 * attribute data, and its size in bytes.
103 */
104 static int
105 ntfs_find_attr(
106 char *buf,
107 u_int32_t attrType,
108 void **attrData,
109 size_t *attrSize)
110 {
111 struct filerec *filerec;
112 struct attr *attr;
113 u_int16_t offset;
114
115 filerec = (struct filerec *) buf;
116 offset = OSReadLittleInt16(&filerec->fr_attroff,0);
117 attr = (struct attr *) (buf + offset);
118
119 /*¥¥ Should we also check offset < buffer size? */
120 while (attr->a_hdr.a_type != 0xFFFFFFFF) /* same for big/little endian */
121 {
122 if (OSReadLittleInt32(&attr->a_hdr.a_type,0) == attrType)
123 {
124 if (attr->a_hdr.a_flag != 0)
125 {
126 //verbose("NTFS: attriubte 0x%X is non-resident\n", attrType);
127 return 1;
128 }
129
130 *attrSize = OSReadLittleInt16(&attr->a_r.a_datalen,0);
131 *attrData = buf + offset + OSReadLittleInt16(&attr->a_r.a_dataoff,0);
132 return 0; /* found it! */
133 }
134
135 /* Skip to the next attribute */
136 offset += OSReadLittleInt32(&attr->a_hdr.reclen,0);
137 attr = (struct attr *) (buf + offset);
138 }
139
140 return 1; /* No matching attrType found */
141 }
142
143 static int
144 memcmp(const char *p1, const char *p2, int len)
145 {
146 while (len--) {
147 if (*p1++ != *p2++)
148 return -1;
149 }
150 return 0;
151 }
152
153 /*
154 * Examine a volume to see if we recognize it as a mountable.
155 */
156 void
157 NTFSGetDescription(CICell ih, char *str, long strMaxLen)
158 {
159 struct bootfile *boot;
160 unsigned bytesPerSector;
161 unsigned sectorsPerCluster;
162 int mftRecordSize;
163 u_int64_t totalClusters;
164 u_int64_t cluster, mftCluster;
165 size_t mftOffset;
166 void *nameAttr;
167 size_t nameSize;
168 char *buf;
169
170 buf = (char *)malloc(MAX_CLUSTER_SIZE);
171 if (buf == 0) {
172 goto error;
173 }
174
175 /*
176 * Read the boot sector, check signatures, and do some minimal
177 * sanity checking. NOTE: the size of the read below is intended
178 * to be a multiple of all supported block sizes, so we don't
179 * have to determine or change the device's block size.
180 */
181 Seek(ih, 0);
182 Read(ih, (long)buf, MAX_BLOCK_SIZE);
183
184 boot = (struct bootfile *) buf;
185
186 /*
187 * The first three bytes are an Intel x86 jump instruction. I assume it
188 * can be the same forms as DOS FAT:
189 * 0xE9 0x?? 0x??
190 * 0xEC 0x?? 0x90
191 * where 0x?? means any byte value is OK.
192 */
193 if (boot->reserved1[0] != 0xE9
194 && (boot->reserved1[0] != 0xEB || boot->reserved1[2] != 0x90))
195 {
196 goto error;
197 }
198
199 /*
200 * Check the "NTFS " signature.
201 */
202 if (memcmp((const char *)boot->bf_sysid, "NTFS ", 8) != 0)
203 {
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 {
275 //verbose("NTFS: error reading MFT $Volume record: %s\n",
276 strerror(errno));
277 goto error;
278 }
279 #endif
280
281 if (ntfs_fixup(buf, mftRecordSize, NTFS_FILEMAGIC, bytesPerSector) != 0)
282 {
283 //verbose("NTFS: block fixup failed\n");
284 goto error;
285 }
286
287 /*
288 * Loop over the attributes, looking for $VOLUME_NAME (0x60).
289 */
290 if(ntfs_find_attr(buf, NTFS_A_VOLUMENAME, &nameAttr, &nameSize) != 0)
291 {
292 //verbose("NTFS: $VOLUME_NAME attribute not found\n");
293 goto error;
294 }
295
296 str[0] = '\0';
297
298 utf_encodestr( nameAttr, nameSize / 2, (u_int8_t *)str, strMaxLen, OSLittleEndian );
299
300 free(buf);
301 return;
302
303 error:
304 if (buf) free(buf);
305 return;
306 }
307