1 /*
2 * Copyright (C) 2011-2012 Paulo Alcantara <pcacjr@gmail.com>
3 *
4 * This program is free software; you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License as published by
6 * the Free Software Foundation; either version 2 of the License, or
7 * (at your option) any later version.
8 *
9 * This program is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 * GNU General Public License for more details.
13 *
14 * You should have received a copy of the GNU General Public License
15 * along with this program; if not, write to the
16 * Free Software Foundation, Inc.,
17 * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
18 */
19
20 /* Note: No support for compressed files */
21
22 #include <dprintf.h>
23 #include <stdio.h>
24 #include <string.h>
25 #include <sys/dirent.h>
26 #include <cache.h>
27 #include <core.h>
28 #include <disk.h>
29 #include <fs.h>
30 #include <ilog2.h>
31 #include <klibc/compiler.h>
32 #include <ctype.h>
33
34 #include "codepage.h"
35 #include "ntfs.h"
36 #include "runlist.h"
37
38 static struct ntfs_readdir_state *readdir_state;
39
40 /*** Function declarations */
41 static f_mft_record_lookup ntfs_mft_record_lookup_3_0;
42 static f_mft_record_lookup ntfs_mft_record_lookup_3_1;
43 static inline enum dirent_type get_inode_mode(struct ntfs_mft_record *mrec);
44 static inline struct ntfs_attr_record * ntfs_attr_lookup(struct fs_info *fs, uint32_t type, struct ntfs_mft_record **mmrec, struct ntfs_mft_record *mrec);
45 static inline uint8_t *mapping_chunk_init(struct ntfs_attr_record *attr,struct mapping_chunk *chunk,uint32_t *offset);
46 static int parse_data_run(const void *stream, uint32_t *offset, uint8_t *attr_len, struct mapping_chunk *chunk);
47
48 /*** Function definitions */
49
50 /* Check if there are specific zero fields in an NTFS boot sector */
ntfs_check_zero_fields(const struct ntfs_bpb * sb)51 static inline int ntfs_check_zero_fields(const struct ntfs_bpb *sb)
52 {
53 return !sb->res_sectors && (!sb->zero_0[0] && !sb->zero_0[1] &&
54 !sb->zero_0[2]) && !sb->zero_1 && !sb->zero_2 &&
55 !sb->zero_3;
56 }
57
ntfs_check_sb_fields(const struct ntfs_bpb * sb)58 static inline int ntfs_check_sb_fields(const struct ntfs_bpb *sb)
59 {
60 return ntfs_check_zero_fields(sb) &&
61 (!memcmp(sb->oem_name, "NTFS ", 8) ||
62 !memcmp(sb->oem_name, "MSWIN4.0", 8) ||
63 !memcmp(sb->oem_name, "MSWIN4.1", 8));
64 }
65
new_ntfs_inode(struct fs_info * fs)66 static inline struct inode *new_ntfs_inode(struct fs_info *fs)
67 {
68 struct inode *inode;
69
70 inode = alloc_inode(fs, 0, sizeof(struct ntfs_inode));
71 if (!inode)
72 malloc_error("inode structure");
73
74 return inode;
75 }
76
ntfs_fixups_writeback(struct fs_info * fs,struct ntfs_record * nrec)77 static void ntfs_fixups_writeback(struct fs_info *fs, struct ntfs_record *nrec)
78 {
79 uint16_t *usa;
80 uint16_t usa_no;
81 uint16_t usa_count;
82 uint16_t *blk;
83
84 dprintf("in %s()\n", __func__);
85
86 if (nrec->magic != NTFS_MAGIC_FILE && nrec->magic != NTFS_MAGIC_INDX)
87 return;
88
89 /* get the Update Sequence Array offset */
90 usa = (uint16_t *)((uint8_t *)nrec + nrec->usa_ofs);
91 /* get the Update Sequence Array Number and skip it */
92 usa_no = *usa++;
93 /* get the Update Sequene Array count */
94 usa_count = nrec->usa_count - 1; /* exclude the USA number */
95 /* make it to point to the last two bytes of the RECORD's first sector */
96 blk = (uint16_t *)((uint8_t *)nrec + SECTOR_SIZE(fs) - 2);
97
98 while (usa_count--) {
99 if (*blk != usa_no)
100 break;
101
102 *blk = *usa++;
103 blk = (uint16_t *)((uint8_t *)blk + SECTOR_SIZE(fs));
104 }
105 }
106
107 /* read content from cache */
ntfs_read(struct fs_info * fs,void * buf,size_t len,uint64_t count,block_t * blk,uint64_t * blk_offset,uint64_t * blk_next_offset,uint64_t * lcn)108 static int ntfs_read(struct fs_info *fs, void *buf, size_t len, uint64_t count,
109 block_t *blk, uint64_t *blk_offset,
110 uint64_t *blk_next_offset, uint64_t *lcn)
111 {
112 uint8_t *data;
113 uint64_t offset = *blk_offset;
114 const uint32_t clust_byte_shift = NTFS_SB(fs)->clust_byte_shift;
115 const uint64_t blk_size = UINT64_C(1) << BLOCK_SHIFT(fs);
116 uint64_t bytes;
117 uint64_t lbytes;
118 uint64_t loffset;
119 uint64_t k;
120
121 dprintf("in %s()\n", __func__);
122
123 if (count > len)
124 goto out;
125
126 data = (uint8_t *)get_cache(fs->fs_dev, *blk);
127 if (!data)
128 goto out;
129
130 if (!offset)
131 offset = (*lcn << clust_byte_shift) % blk_size;
132
133 dprintf("LCN: 0x%X\n", *lcn);
134 dprintf("offset: 0x%X\n", offset);
135
136 bytes = count; /* bytes to copy */
137 lbytes = blk_size - offset; /* bytes left to copy */
138 if (lbytes >= bytes) {
139 /* so there's room enough, then copy the whole content */
140 memcpy(buf, data + offset, bytes);
141 loffset = offset;
142 offset += count;
143 } else {
144 dprintf("bytes: %u\n", bytes);
145 dprintf("bytes left: %u\n", lbytes);
146 /* otherwise, let's copy it partially... */
147 k = 0;
148 while (bytes) {
149 memcpy(buf + k, data + offset, lbytes);
150 bytes -= lbytes;
151 loffset = offset;
152 offset += lbytes;
153 k += lbytes;
154 if (offset >= blk_size) {
155 /* then fetch a new FS block */
156 data = (uint8_t *)get_cache(fs->fs_dev, ++*blk);
157 if (!data)
158 goto out;
159
160 lbytes = bytes;
161 loffset = offset;
162 offset = 0;
163 }
164 }
165 }
166
167 if (loffset >= blk_size)
168 loffset = 0; /* it must be aligned on a block boundary */
169
170 *blk_offset = loffset;
171
172 if (blk_next_offset)
173 *blk_next_offset = offset;
174
175 *lcn += blk_size / count; /* update LCN */
176
177 return 0;
178
179 out:
180 return -1;
181 }
182
183 /* AndyAlex: read and validate single MFT record. Keep in mind that MFT itself can be fragmented */
ntfs_mft_record_lookup_any(struct fs_info * fs,uint32_t file,block_t * out_blk,bool is_v31)184 static struct ntfs_mft_record *ntfs_mft_record_lookup_any(struct fs_info *fs,
185 uint32_t file, block_t *out_blk, bool is_v31)
186 {
187 const uint64_t mft_record_size = NTFS_SB(fs)->mft_record_size;
188 uint8_t *buf = NULL;
189 const uint32_t mft_record_shift = ilog2(mft_record_size);
190 const uint32_t clust_byte_shift = NTFS_SB(fs)->clust_byte_shift;
191 uint64_t next_offset = 0;
192 uint64_t lcn = 0;
193 block_t blk = 0;
194 uint64_t offset = 0;
195
196 struct ntfs_mft_record *mrec = NULL, *lmrec = NULL;
197 uint64_t start_blk = 0;
198 struct ntfs_attr_record *attr = NULL;
199 uint8_t *stream = NULL;
200 uint32_t attr_offset = 0;
201 uint8_t *attr_len = NULL;
202 struct mapping_chunk chunk;
203
204 int err = 0;
205
206 /* determine MFT record's LCN */
207 uint64_t vcn = (file << mft_record_shift >> clust_byte_shift);
208 dprintf("in %s(%s)\n", __func__,(is_v31?"v3.1":"v3.0"));
209 if (0==vcn) {
210 lcn = NTFS_SB(fs)->mft_lcn;
211 } else do {
212 dprintf("%s: looking for VCN %u for MFT record %u\n", __func__,(unsigned)vcn,(unsigned)file);
213 mrec = NTFS_SB(fs)->mft_record_lookup(fs, 0, &start_blk);
214 if (!mrec) {dprintf("%s: read MFT(0) failed\n", __func__); break;}
215 lmrec = mrec;
216 if (get_inode_mode(mrec) != DT_REG) {dprintf("%s: $MFT is not a file\n", __func__); break;}
217 attr = ntfs_attr_lookup(fs, NTFS_AT_DATA, &mrec, lmrec);
218 if (!attr) {dprintf("%s: $MFT have no data attr\n", __func__); break;}
219 if (!attr->non_resident) {dprintf("%s: $MFT data attr is resident\n", __func__); break;}
220 attr_len = (uint8_t *)attr + attr->len;
221 stream = mapping_chunk_init(attr, &chunk, &attr_offset);
222 while (true) {
223 err = parse_data_run(stream, &attr_offset, attr_len, &chunk);
224 if (err) {dprintf("%s: $MFT data run parse failed with error %d\n", __func__,err); break;}
225 if (chunk.flags & MAP_UNALLOCATED) continue;
226 if (chunk.flags & MAP_END) break;
227 if (chunk.flags & MAP_ALLOCATED) {
228 dprintf("%s: Chunk: VCN=%u, LCN=%u, len=%u\n", __func__,(unsigned)chunk.vcn,(unsigned)chunk.lcn,(unsigned)chunk.len);
229 if ((vcn>=chunk.vcn)&&(vcn<chunk.vcn+chunk.len)) {
230 lcn=vcn-chunk.vcn+chunk.lcn;
231 dprintf("%s: VCN %u for MFT record %u maps to lcn %u\n", __func__,(unsigned)vcn,(unsigned)file,(unsigned)lcn);
232 break;
233 }
234 chunk.vcn += chunk.len;
235 }
236 }
237 } while(false);
238 if (mrec!=NULL) free(mrec);
239 mrec = NULL;
240 if (0==lcn) {
241 dprintf("%s: unable to map VCN %u for MFT record %u\n", __func__,(unsigned)vcn,(unsigned)file);
242 return NULL;
243 }
244
245 /* determine MFT record's block number */
246 blk = (lcn << clust_byte_shift >> BLOCK_SHIFT(fs));
247 offset = (file << mft_record_shift) % BLOCK_SIZE(fs);
248
249 /* Allocate buffer */
250 buf = (uint8_t *)malloc(mft_record_size);
251 if (!buf) {malloc_error("uint8_t *");return 0;}
252
253 /* Read block */
254 err = ntfs_read(fs, buf, mft_record_size, mft_record_size, &blk,
255 &offset, &next_offset, &lcn);
256 if (err) {
257 dprintf("%s: error read block %u from cache\n", __func__, blk);
258 printf("Error while reading from cache.\n");
259 free(buf);
260 return NULL;
261 }
262
263 /* Process fixups and make structure pointer */
264 ntfs_fixups_writeback(fs, (struct ntfs_record *)buf);
265 mrec = (struct ntfs_mft_record *)buf;
266
267 /* check if it has a valid magic number and record number */
268 if (mrec->magic != NTFS_MAGIC_FILE) mrec = NULL;
269 if (mrec && is_v31) if (mrec->mft_record_no != file) mrec = NULL;
270 if (mrec!=NULL) {
271 if (out_blk) {
272 *out_blk = (file << mft_record_shift >> BLOCK_SHIFT(fs)); /* update record starting block */
273 }
274 return mrec; /* found MFT record */
275 }
276
277 /* Invalid record */
278 dprintf("%s: MFT record %u is invalid\n", __func__, (unsigned)file);
279 free(buf);
280 return NULL;
281 }
282
ntfs_mft_record_lookup_3_0(struct fs_info * fs,uint32_t file,block_t * blk)283 static struct ntfs_mft_record *ntfs_mft_record_lookup_3_0(struct fs_info *fs,
284 uint32_t file, block_t *blk)
285 {
286 return ntfs_mft_record_lookup_any(fs,file,blk,false);
287 }
288
ntfs_mft_record_lookup_3_1(struct fs_info * fs,uint32_t file,block_t * blk)289 static struct ntfs_mft_record *ntfs_mft_record_lookup_3_1(struct fs_info *fs,
290 uint32_t file, block_t *blk)
291 {
292 return ntfs_mft_record_lookup_any(fs,file,blk,true);
293 }
294
ntfs_filename_cmp(const char * dname,struct ntfs_idx_entry * ie)295 static bool ntfs_filename_cmp(const char *dname, struct ntfs_idx_entry *ie)
296 {
297 const uint16_t *entry_fn;
298 uint8_t entry_fn_len;
299 unsigned i;
300
301 dprintf("in %s()\n", __func__);
302
303 entry_fn = ie->key.file_name.file_name;
304 entry_fn_len = ie->key.file_name.file_name_len;
305
306 if (strlen(dname) != entry_fn_len)
307 return false;
308
309 /* Do case-sensitive compares for Posix file names */
310 if (ie->key.file_name.file_name_type == FILE_NAME_POSIX) {
311 for (i = 0; i < entry_fn_len; i++)
312 if (entry_fn[i] != dname[i])
313 return false;
314 } else {
315 for (i = 0; i < entry_fn_len; i++)
316 if (tolower(entry_fn[i]) != tolower(dname[i]))
317 return false;
318 }
319
320 return true;
321 }
322
mapping_chunk_init(struct ntfs_attr_record * attr,struct mapping_chunk * chunk,uint32_t * offset)323 static inline uint8_t *mapping_chunk_init(struct ntfs_attr_record *attr,
324 struct mapping_chunk *chunk,
325 uint32_t *offset)
326 {
327 memset(chunk, 0, sizeof *chunk);
328 *offset = 0U;
329
330 return (uint8_t *)attr + attr->data.non_resident.mapping_pairs_offset;
331 }
332
333 /* Parse data runs.
334 *
335 * return 0 on success or -1 on failure.
336 */
parse_data_run(const void * stream,uint32_t * offset,uint8_t * attr_len,struct mapping_chunk * chunk)337 static int parse_data_run(const void *stream, uint32_t *offset,
338 uint8_t *attr_len, struct mapping_chunk *chunk)
339 {
340 uint8_t *buf; /* Pointer to the zero-terminated byte stream */
341 uint8_t count; /* The count byte */
342 uint8_t v, l; /* v is the number of changed low-order VCN bytes;
343 * l is the number of changed low-order LCN bytes
344 */
345 uint8_t *byte;
346 const int byte_shift = 8;
347 int mask;
348 int64_t res;
349
350 (void)attr_len;
351
352 dprintf("in %s()\n", __func__);
353
354 chunk->flags &= ~MAP_MASK;
355
356 buf = (uint8_t *)stream + *offset;
357 if (buf > attr_len || !*buf) {
358 chunk->flags |= MAP_END; /* we're done */
359 return 0;
360 }
361
362 if (!*offset)
363 chunk->flags |= MAP_START; /* initial chunk */
364
365 count = *buf;
366 v = count & 0x0F;
367 l = count >> 4;
368
369 if (v > 8 || l > 8) /* more than 8 bytes ? */
370 goto out;
371
372 byte = (uint8_t *)buf + v;
373 count = v;
374
375 res = 0LL;
376 while (count--)
377 res = (res << byte_shift) | *byte--;
378
379 chunk->len = res; /* get length data */
380
381 byte = (uint8_t *)buf + v + l;
382 count = l;
383
384 mask = 0xFFFFFFFF;
385 res = 0LL;
386 if (*byte & 0x80)
387 res |= (int64_t)mask; /* sign-extend it */
388
389 while (count--)
390 res = (res << byte_shift) | *byte--;
391
392 chunk->lcn += res;
393 /* are VCNS from cur_vcn to next_vcn - 1 unallocated ? */
394 if (!chunk->lcn)
395 chunk->flags |= MAP_UNALLOCATED;
396 else
397 chunk->flags |= MAP_ALLOCATED;
398
399 *offset += v + l + 1;
400
401 return 0;
402
403 out:
404 return -1;
405 }
406
407 static struct ntfs_mft_record *
ntfs_attr_list_lookup(struct fs_info * fs,struct ntfs_attr_record * attr,uint32_t type,struct ntfs_mft_record * mrec)408 ntfs_attr_list_lookup(struct fs_info *fs, struct ntfs_attr_record *attr,
409 uint32_t type, struct ntfs_mft_record *mrec)
410 {
411 uint8_t *attr_len;
412 struct mapping_chunk chunk;
413 uint32_t offset;
414 uint8_t *stream;
415 int err;
416 const uint64_t blk_size = UINT64_C(1) << BLOCK_SHIFT(fs);
417 uint8_t buf[blk_size];
418 uint64_t blk_offset;
419 int64_t vcn;
420 int64_t lcn;
421 int64_t last_lcn;
422 block_t blk;
423 struct ntfs_attr_list_entry *attr_entry;
424 uint32_t len = 0;
425 struct ntfs_mft_record *retval;
426 uint64_t start_blk = 0;
427
428 dprintf("in %s()\n", __func__);
429
430 if (attr->non_resident)
431 goto handle_non_resident_attr;
432
433 attr_entry = (struct ntfs_attr_list_entry *)
434 ((uint8_t *)attr + attr->data.resident.value_offset);
435 len = attr->data.resident.value_len;
436 for (; (uint8_t *)attr_entry < (uint8_t *)attr + len;
437 attr_entry = (struct ntfs_attr_list_entry *)((uint8_t *)attr_entry +
438 attr_entry->length)) {
439 dprintf("<$ATTRIBUTE_LIST> Attribute type: 0x%X\n",
440 attr_entry->type);
441 if (attr_entry->type == type)
442 goto found; /* We got the attribute! :-) */
443 }
444
445 printf("No attribute found.\n");
446 goto out;
447
448 handle_non_resident_attr:
449 attr_len = (uint8_t *)attr + attr->len;
450 stream = mapping_chunk_init(attr, &chunk, &offset);
451 do {
452 err = parse_data_run(stream, &offset, attr_len, &chunk);
453 if (err) {
454 printf("parse_data_run()\n");
455 goto out;
456 }
457
458 if (chunk.flags & MAP_UNALLOCATED)
459 continue;
460 if (chunk.flags & MAP_END)
461 break;
462 if (chunk.flags & MAP_ALLOCATED) {
463 vcn = 0;
464 lcn = chunk.lcn;
465 while (vcn < chunk.len) {
466 blk = (lcn + vcn) << NTFS_SB(fs)->clust_byte_shift >>
467 BLOCK_SHIFT(fs);
468 blk_offset = 0;
469 last_lcn = lcn;
470 lcn += vcn;
471 err = ntfs_read(fs, buf, blk_size, blk_size, &blk,
472 &blk_offset, NULL, (uint64_t *)&lcn);
473 if (err) {
474 printf("Error while reading from cache.\n");
475 goto out;
476 }
477
478 attr_entry = (struct ntfs_attr_list_entry *)&buf;
479 len = attr->data.non_resident.data_size;
480 for (; (uint8_t *)attr_entry < (uint8_t *)&buf[0] + len;
481 attr_entry = (struct ntfs_attr_list_entry *)
482 ((uint8_t *)attr_entry + attr_entry->length)) {
483 dprintf("<$ATTRIBUTE_LIST> Attribute type: 0x%x\n",
484 attr_entry->type);
485 if (attr_entry->type == type)
486 goto found; /* We got the attribute! :-) */
487 }
488
489 lcn = last_lcn; /* restore original LCN */
490 /* go to the next VCN */
491 vcn += (blk_size / (1 << NTFS_SB(fs)->clust_byte_shift));
492 }
493 }
494 } while (!(chunk.flags & MAP_END));
495
496 printf("No attribute found.\n");
497
498 out:
499 return NULL;
500
501 found:
502 /* At this point we have the attribute we were looking for. Now we
503 * will look for the MFT record that stores information about this
504 * attribute.
505 */
506
507 /* Check if the attribute type we're looking for is in the same
508 * MFT record. If so, we do not need to look it up again - return it.
509 */
510 if (mrec->mft_record_no == attr_entry->mft_ref)
511 return mrec;
512
513 retval = NTFS_SB(fs)->mft_record_lookup(fs, attr_entry->mft_ref,
514 &start_blk);
515 if (!retval) {
516 printf("No MFT record found!\n");
517 goto out;
518 }
519
520 /* return the found MFT record */
521 return retval;
522 }
523
524 static struct ntfs_attr_record *
__ntfs_attr_lookup(struct fs_info * fs,uint32_t type,struct ntfs_mft_record ** mrec)525 __ntfs_attr_lookup(struct fs_info *fs, uint32_t type,
526 struct ntfs_mft_record **mrec)
527 {
528 struct ntfs_mft_record *_mrec = *mrec;
529 struct ntfs_attr_record *attr;
530 struct ntfs_attr_record *attr_list_attr;
531
532 dprintf("in %s()\n", __func__);
533
534 if (!_mrec || type == NTFS_AT_END)
535 goto out;
536
537 again:
538 attr_list_attr = NULL;
539
540 attr = (struct ntfs_attr_record *)((uint8_t *)_mrec + _mrec->attrs_offset);
541 /* walk through the file attribute records */
542 for (;; attr = (struct ntfs_attr_record *)((uint8_t *)attr + attr->len)) {
543 if (attr->type == NTFS_AT_END)
544 break;
545
546 if (attr->type == NTFS_AT_ATTR_LIST) {
547 dprintf("MFT record #%lu has an $ATTRIBUTE_LIST attribute.\n",
548 _mrec->mft_record_no);
549 attr_list_attr = attr;
550 continue;
551 }
552
553 if (attr->type == type)
554 break;
555 }
556
557 /* if the record has an $ATTRIBUTE_LIST attribute associated
558 * with it, then we need to look for the wanted attribute in
559 * it as well.
560 */
561 if (attr->type == NTFS_AT_END && attr_list_attr) {
562 struct ntfs_mft_record *retval;
563
564 retval = ntfs_attr_list_lookup(fs, attr_list_attr, type, _mrec);
565 if (!retval)
566 goto out;
567
568 _mrec = retval;
569 goto again;
570 } else if (attr->type == NTFS_AT_END && !attr_list_attr) {
571 attr = NULL;
572 }
573
574 return attr;
575
576 out:
577 return NULL;
578 }
579
580 static inline struct ntfs_attr_record *
ntfs_attr_lookup(struct fs_info * fs,uint32_t type,struct ntfs_mft_record ** mmrec,struct ntfs_mft_record * mrec)581 ntfs_attr_lookup(struct fs_info *fs, uint32_t type,
582 struct ntfs_mft_record **mmrec,
583 struct ntfs_mft_record *mrec)
584 {
585 struct ntfs_mft_record *_mrec = mrec;
586 struct ntfs_mft_record *other = *mmrec;
587 struct ntfs_attr_record *retval = NULL;
588
589 if (mrec == other)
590 return __ntfs_attr_lookup(fs, type, &other);
591
592 retval = __ntfs_attr_lookup(fs, type, &_mrec);
593 if (!retval) {
594 _mrec = other;
595 retval = __ntfs_attr_lookup(fs, type, &other);
596 if (!retval)
597 other = _mrec;
598 } else if (retval && (_mrec != mrec)) {
599 other = _mrec;
600 }
601
602 return retval;
603 }
604
get_inode_mode(struct ntfs_mft_record * mrec)605 static inline enum dirent_type get_inode_mode(struct ntfs_mft_record *mrec)
606 {
607 return mrec->flags & MFT_RECORD_IS_DIRECTORY ? DT_DIR : DT_REG;
608 }
609
index_inode_setup(struct fs_info * fs,unsigned long mft_no,struct inode * inode)610 static int index_inode_setup(struct fs_info *fs, unsigned long mft_no,
611 struct inode *inode)
612 {
613 uint64_t start_blk = 0;
614 struct ntfs_mft_record *mrec, *lmrec;
615 struct ntfs_attr_record *attr;
616 enum dirent_type d_type;
617 uint8_t *attr_len;
618 struct mapping_chunk chunk;
619 int err;
620 uint8_t *stream;
621 uint32_t offset;
622
623 dprintf("in %s()\n", __func__);
624
625 mrec = NTFS_SB(fs)->mft_record_lookup(fs, mft_no, &start_blk);
626 if (!mrec) {
627 printf("No MFT record found.\n");
628 goto out;
629 }
630
631 lmrec = mrec;
632
633 NTFS_PVT(inode)->mft_no = mft_no;
634 NTFS_PVT(inode)->seq_no = mrec->seq_no;
635
636 NTFS_PVT(inode)->start_cluster = start_blk >> NTFS_SB(fs)->clust_shift;
637 NTFS_PVT(inode)->here = start_blk;
638
639 d_type = get_inode_mode(mrec);
640 if (d_type == DT_DIR) { /* directory stuff */
641 dprintf("Got a directory.\n");
642 attr = ntfs_attr_lookup(fs, NTFS_AT_INDEX_ROOT, &mrec, lmrec);
643 if (!attr) {
644 printf("No attribute found.\n");
645 goto out;
646 }
647
648 /* check if we have a previous allocated state structure */
649 if (readdir_state) {
650 free(readdir_state);
651 readdir_state = NULL;
652 }
653
654 /* allocate our state structure */
655 readdir_state = malloc(sizeof *readdir_state);
656 if (!readdir_state)
657 malloc_error("ntfs_readdir_state structure");
658
659 readdir_state->mft_no = mft_no;
660 /* obviously, the ntfs_readdir() caller will start from INDEX root */
661 readdir_state->in_idx_root = true;
662 } else if (d_type == DT_REG) { /* file stuff */
663 dprintf("Got a file.\n");
664 attr = ntfs_attr_lookup(fs, NTFS_AT_DATA, &mrec, lmrec);
665 if (!attr) {
666 printf("No attribute found.\n");
667 goto out;
668 }
669
670 NTFS_PVT(inode)->non_resident = attr->non_resident;
671 NTFS_PVT(inode)->type = attr->type;
672
673 if (!attr->non_resident) {
674 NTFS_PVT(inode)->data.resident.offset =
675 (uint32_t)((uint8_t *)attr + attr->data.resident.value_offset);
676 inode->size = attr->data.resident.value_len;
677 } else {
678 attr_len = (uint8_t *)attr + attr->len;
679
680 stream = mapping_chunk_init(attr, &chunk, &offset);
681 NTFS_PVT(inode)->data.non_resident.rlist = NULL;
682 for (;;) {
683 err = parse_data_run(stream, &offset, attr_len, &chunk);
684 if (err) {
685 printf("parse_data_run()\n");
686 goto out;
687 }
688
689 if (chunk.flags & MAP_UNALLOCATED)
690 continue;
691 if (chunk.flags & MAP_END)
692 break;
693 if (chunk.flags & MAP_ALLOCATED) {
694 /* append new run to the runlist */
695 runlist_append(&NTFS_PVT(inode)->data.non_resident.rlist,
696 (struct runlist_element *)&chunk);
697 /* update for next VCN */
698 chunk.vcn += chunk.len;
699 }
700 }
701
702 if (runlist_is_empty(NTFS_PVT(inode)->data.non_resident.rlist)) {
703 printf("No mapping found\n");
704 goto out;
705 }
706
707 inode->size = attr->data.non_resident.initialized_size;
708 }
709 }
710
711 inode->mode = d_type;
712
713 free(mrec);
714
715 return 0;
716
717 out:
718 free(mrec);
719
720 return -1;
721 }
722
ntfs_index_lookup(const char * dname,struct inode * dir)723 static struct inode *ntfs_index_lookup(const char *dname, struct inode *dir)
724 {
725 struct fs_info *fs = dir->fs;
726 struct ntfs_mft_record *mrec, *lmrec;
727 block_t blk;
728 uint64_t blk_offset;
729 struct ntfs_attr_record *attr;
730 struct ntfs_idx_root *ir;
731 struct ntfs_idx_entry *ie;
732 const uint64_t blk_size = UINT64_C(1) << BLOCK_SHIFT(fs);
733 uint8_t buf[blk_size];
734 struct ntfs_idx_allocation *iblk;
735 int err;
736 uint8_t *stream;
737 uint8_t *attr_len;
738 struct mapping_chunk chunk;
739 uint32_t offset;
740 int64_t vcn;
741 int64_t lcn;
742 int64_t last_lcn;
743 struct inode *inode;
744
745 dprintf("in %s()\n", __func__);
746
747 mrec = NTFS_SB(fs)->mft_record_lookup(fs, NTFS_PVT(dir)->mft_no, NULL);
748 if (!mrec) {
749 printf("No MFT record found.\n");
750 goto out;
751 }
752
753 lmrec = mrec;
754 attr = ntfs_attr_lookup(fs, NTFS_AT_INDEX_ROOT, &mrec, lmrec);
755 if (!attr) {
756 printf("No attribute found.\n");
757 goto out;
758 }
759
760 ir = (struct ntfs_idx_root *)((uint8_t *)attr +
761 attr->data.resident.value_offset);
762 ie = (struct ntfs_idx_entry *)((uint8_t *)&ir->index +
763 ir->index.entries_offset);
764 for (;; ie = (struct ntfs_idx_entry *)((uint8_t *)ie + ie->len)) {
765 /* bounds checks */
766 if ((uint8_t *)ie < (uint8_t *)mrec ||
767 (uint8_t *)ie + sizeof(struct ntfs_idx_entry_header) >
768 (uint8_t *)&ir->index + ir->index.index_len ||
769 (uint8_t *)ie + ie->len >
770 (uint8_t *)&ir->index + ir->index.index_len)
771 goto index_err;
772
773 /* last entry cannot contain a key. it can however contain
774 * a pointer to a child node in the B+ tree so we just break out
775 */
776 if (ie->flags & INDEX_ENTRY_END)
777 break;
778
779 if (ntfs_filename_cmp(dname, ie))
780 goto found;
781 }
782
783 /* check for the presence of a child node */
784 if (!(ie->flags & INDEX_ENTRY_NODE)) {
785 printf("No child node, aborting...\n");
786 goto out;
787 }
788
789 /* then descend into child node */
790
791 attr = ntfs_attr_lookup(fs, NTFS_AT_INDEX_ALLOCATION, &mrec, lmrec);
792 if (!attr) {
793 printf("No attribute found.\n");
794 goto out;
795 }
796
797 if (!attr->non_resident) {
798 printf("WTF ?! $INDEX_ALLOCATION isn't really resident.\n");
799 goto out;
800 }
801
802 attr_len = (uint8_t *)attr + attr->len;
803 stream = mapping_chunk_init(attr, &chunk, &offset);
804 do {
805 err = parse_data_run(stream, &offset, attr_len, &chunk);
806 if (err)
807 break;
808
809 if (chunk.flags & MAP_UNALLOCATED)
810 continue;
811
812 if (chunk.flags & MAP_ALLOCATED) {
813 dprintf("%d cluster(s) starting at 0x%08llX\n", chunk.len,
814 chunk.lcn);
815
816 vcn = 0;
817 lcn = chunk.lcn;
818 while (vcn < chunk.len) {
819 blk = (lcn + vcn) << NTFS_SB(fs)->clust_shift <<
820 SECTOR_SHIFT(fs) >> BLOCK_SHIFT(fs);
821
822 blk_offset = 0;
823 last_lcn = lcn;
824 lcn += vcn;
825 err = ntfs_read(fs, &buf, blk_size, blk_size, &blk,
826 &blk_offset, NULL, (uint64_t *)&lcn);
827 if (err) {
828 printf("Error while reading from cache.\n");
829 goto not_found;
830 }
831
832 ntfs_fixups_writeback(fs, (struct ntfs_record *)&buf);
833
834 iblk = (struct ntfs_idx_allocation *)&buf;
835 if (iblk->magic != NTFS_MAGIC_INDX) {
836 printf("Not a valid INDX record.\n");
837 goto not_found;
838 }
839
840 ie = (struct ntfs_idx_entry *)((uint8_t *)&iblk->index +
841 iblk->index.entries_offset);
842 for (;; ie = (struct ntfs_idx_entry *)((uint8_t *)ie +
843 ie->len)) {
844 /* bounds checks */
845 if ((uint8_t *)ie < (uint8_t *)iblk || (uint8_t *)ie +
846 sizeof(struct ntfs_idx_entry_header) >
847 (uint8_t *)&iblk->index + iblk->index.index_len ||
848 (uint8_t *)ie + ie->len >
849 (uint8_t *)&iblk->index + iblk->index.index_len)
850 goto index_err;
851
852 /* last entry cannot contain a key */
853 if (ie->flags & INDEX_ENTRY_END)
854 break;
855
856 if (ntfs_filename_cmp(dname, ie))
857 goto found;
858 }
859
860 lcn = last_lcn; /* restore the original LCN */
861 /* go to the next VCN */
862 vcn += (blk_size / (1 << NTFS_SB(fs)->clust_byte_shift));
863 }
864 }
865 } while (!(chunk.flags & MAP_END));
866
867 not_found:
868 dprintf("Index not found\n");
869
870 out:
871 free(mrec);
872
873 return NULL;
874
875 found:
876 dprintf("Index found\n");
877 inode = new_ntfs_inode(fs);
878 err = index_inode_setup(fs, ie->data.dir.indexed_file, inode);
879 if (err) {
880 printf("Error in index_inode_setup()\n");
881 free(inode);
882 goto out;
883 }
884
885 free(mrec);
886
887 return inode;
888
889 index_err:
890 printf("Corrupt index. Aborting lookup...\n");
891 goto out;
892 }
893
894 /* Convert an UTF-16LE LFN to OEM LFN */
ntfs_cvt_filename(char * filename,const struct ntfs_idx_entry * ie)895 static uint8_t ntfs_cvt_filename(char *filename,
896 const struct ntfs_idx_entry *ie)
897 {
898 const uint16_t *entry_fn;
899 uint8_t entry_fn_len;
900 unsigned i;
901
902 entry_fn = ie->key.file_name.file_name;
903 entry_fn_len = ie->key.file_name.file_name_len;
904
905 for (i = 0; i < entry_fn_len; i++)
906 filename[i] = (char)entry_fn[i];
907
908 filename[i] = '\0';
909
910 return entry_fn_len;
911 }
912
ntfs_next_extent(struct inode * inode,uint32_t lstart)913 static int ntfs_next_extent(struct inode *inode, uint32_t lstart)
914 {
915 struct fs_info *fs = inode->fs;
916 struct ntfs_sb_info *sbi = NTFS_SB(fs);
917 sector_t pstart = 0;
918 struct runlist *rlist;
919 struct runlist *ret;
920 const uint32_t sec_size = SECTOR_SIZE(fs);
921 const uint32_t sec_shift = SECTOR_SHIFT(fs);
922
923 dprintf("in %s()\n", __func__);
924
925 if (!NTFS_PVT(inode)->non_resident) {
926 pstart = (sbi->mft_blk + NTFS_PVT(inode)->here) << BLOCK_SHIFT(fs) >>
927 sec_shift;
928 inode->next_extent.len = (inode->size + sec_size - 1) >> sec_shift;
929 } else {
930 rlist = NTFS_PVT(inode)->data.non_resident.rlist;
931
932 if (!lstart || lstart >= NTFS_PVT(inode)->here) {
933 if (runlist_is_empty(rlist))
934 goto out; /* nothing to do ;-) */
935
936 ret = runlist_remove(&rlist);
937
938 NTFS_PVT(inode)->here =
939 ((ret->run.len << sbi->clust_byte_shift) >> sec_shift);
940
941 pstart = ret->run.lcn << sbi->clust_shift;
942 inode->next_extent.len =
943 ((ret->run.len << sbi->clust_byte_shift) + sec_size - 1) >>
944 sec_shift;
945
946 NTFS_PVT(inode)->data.non_resident.rlist = rlist;
947
948 free(ret);
949 ret = NULL;
950 }
951 }
952
953 inode->next_extent.pstart = pstart;
954
955 return 0;
956
957 out:
958 return -1;
959 }
960
ntfs_getfssec(struct file * file,char * buf,int sectors,bool * have_more)961 static uint32_t ntfs_getfssec(struct file *file, char *buf, int sectors,
962 bool *have_more)
963 {
964 uint8_t non_resident;
965 uint32_t ret;
966 struct fs_info *fs = file->fs;
967 struct inode *inode = file->inode;
968 struct ntfs_mft_record *mrec, *lmrec;
969 struct ntfs_attr_record *attr;
970 char *p;
971
972 dprintf("in %s()\n", __func__);
973
974 non_resident = NTFS_PVT(inode)->non_resident;
975
976 ret = generic_getfssec(file, buf, sectors, have_more);
977 if (!ret)
978 return ret;
979
980 if (!non_resident) {
981 mrec = NTFS_SB(fs)->mft_record_lookup(fs, NTFS_PVT(inode)->mft_no,
982 NULL);
983 if (!mrec) {
984 printf("No MFT record found.\n");
985 goto out;
986 }
987
988 lmrec = mrec;
989 attr = ntfs_attr_lookup(fs, NTFS_AT_DATA, &mrec, lmrec);
990 if (!attr) {
991 printf("No attribute found.\n");
992 goto out;
993 }
994
995 p = (char *)((uint8_t *)attr + attr->data.resident.value_offset);
996
997 /* p now points to the data offset, so let's copy it into buf */
998 memcpy(buf, p, inode->size);
999
1000 ret = inode->size;
1001
1002 free(mrec);
1003 }
1004
1005 return ret;
1006
1007 out:
1008 free(mrec);
1009
1010 return 0;
1011 }
1012
is_filename_printable(const char * s)1013 static inline bool is_filename_printable(const char *s)
1014 {
1015 return s && (*s != '.' && *s != '$');
1016 }
1017
ntfs_readdir(struct file * file,struct dirent * dirent)1018 static int ntfs_readdir(struct file *file, struct dirent *dirent)
1019 {
1020 struct fs_info *fs = file->fs;
1021 struct inode *inode = file->inode;
1022 struct ntfs_mft_record *mrec, *lmrec;
1023 block_t blk;
1024 uint64_t blk_offset;
1025 const uint64_t blk_size = UINT64_C(1) << BLOCK_SHIFT(fs);
1026 struct ntfs_attr_record *attr;
1027 struct ntfs_idx_root *ir;
1028 uint32_t count;
1029 int len;
1030 struct ntfs_idx_entry *ie = NULL;
1031 uint8_t buf[BLOCK_SIZE(fs)];
1032 struct ntfs_idx_allocation *iblk;
1033 int err;
1034 uint8_t *stream;
1035 uint8_t *attr_len;
1036 struct mapping_chunk chunk;
1037 uint32_t offset;
1038 int64_t vcn;
1039 int64_t lcn;
1040 char filename[NTFS_MAX_FILE_NAME_LEN + 1];
1041
1042 dprintf("in %s()\n", __func__);
1043
1044 mrec = NTFS_SB(fs)->mft_record_lookup(fs, NTFS_PVT(inode)->mft_no, NULL);
1045 if (!mrec) {
1046 printf("No MFT record found.\n");
1047 goto out;
1048 }
1049
1050 lmrec = mrec;
1051 attr = ntfs_attr_lookup(fs, NTFS_AT_INDEX_ROOT, &mrec, lmrec);
1052 if (!attr) {
1053 printf("No attribute found.\n");
1054 goto out;
1055 }
1056
1057 ir = (struct ntfs_idx_root *)((uint8_t *)attr +
1058 attr->data.resident.value_offset);
1059
1060 if (!file->offset && readdir_state->in_idx_root)
1061 file->offset = ir->index.entries_offset;
1062
1063 idx_root_next_entry:
1064 if (readdir_state->in_idx_root) {
1065 ie = (struct ntfs_idx_entry *)((uint8_t *)&ir->index + file->offset);
1066 if (ie->flags & INDEX_ENTRY_END) {
1067 file->offset = 0;
1068 readdir_state->in_idx_root = false;
1069 readdir_state->idx_blks_count = 1;
1070 readdir_state->entries_count = 0;
1071 readdir_state->last_vcn = 0;
1072 goto descend_into_child_node;
1073 }
1074
1075 file->offset += ie->len;
1076 len = ntfs_cvt_filename(filename, ie);
1077 if (!is_filename_printable(filename))
1078 goto idx_root_next_entry;
1079
1080 goto done;
1081 }
1082
1083 descend_into_child_node:
1084 if (!(ie->flags & INDEX_ENTRY_NODE))
1085 goto out;
1086
1087 attr = ntfs_attr_lookup(fs, NTFS_AT_INDEX_ALLOCATION, &mrec, lmrec);
1088 if (!attr)
1089 goto out;
1090
1091 if (!attr->non_resident) {
1092 printf("WTF ?! $INDEX_ALLOCATION isn't really resident.\n");
1093 goto out;
1094 }
1095
1096 attr_len = (uint8_t *)attr + attr->len;
1097
1098 next_run:
1099 stream = mapping_chunk_init(attr, &chunk, &offset);
1100 count = readdir_state->idx_blks_count;
1101 while (count--) {
1102 err = parse_data_run(stream, &offset, attr_len, &chunk);
1103 if (err) {
1104 printf("Error while parsing data runs.\n");
1105 goto out;
1106 }
1107
1108 if (chunk.flags & MAP_UNALLOCATED)
1109 break;
1110 if (chunk.flags & MAP_END)
1111 goto out;
1112 }
1113
1114 if (chunk.flags & MAP_UNALLOCATED) {
1115 readdir_state->idx_blks_count++;
1116 goto next_run;
1117 }
1118
1119 next_vcn:
1120 vcn = readdir_state->last_vcn;
1121 if (vcn >= chunk.len) {
1122 readdir_state->last_vcn = 0;
1123 readdir_state->idx_blks_count++;
1124 goto next_run;
1125 }
1126
1127 lcn = chunk.lcn;
1128 blk = (lcn + vcn) << NTFS_SB(fs)->clust_shift << SECTOR_SHIFT(fs) >>
1129 BLOCK_SHIFT(fs);
1130
1131 blk_offset = 0;
1132 err = ntfs_read(fs, &buf, blk_size, blk_size, &blk, &blk_offset, NULL,
1133 (uint64_t *)&lcn);
1134 if (err) {
1135 printf("Error while reading from cache.\n");
1136 goto not_found;
1137 }
1138
1139 ntfs_fixups_writeback(fs, (struct ntfs_record *)&buf);
1140
1141 iblk = (struct ntfs_idx_allocation *)&buf;
1142 if (iblk->magic != NTFS_MAGIC_INDX) {
1143 printf("Not a valid INDX record.\n");
1144 goto not_found;
1145 }
1146
1147 idx_block_next_entry:
1148 ie = (struct ntfs_idx_entry *)((uint8_t *)&iblk->index +
1149 iblk->index.entries_offset);
1150 count = readdir_state->entries_count;
1151 for ( ; count--; ie = (struct ntfs_idx_entry *)((uint8_t *)ie + ie->len)) {
1152 /* bounds checks */
1153 if ((uint8_t *)ie < (uint8_t *)iblk || (uint8_t *)ie +
1154 sizeof(struct ntfs_idx_entry_header) >
1155 (uint8_t *)&iblk->index + iblk->index.index_len ||
1156 (uint8_t *)ie + ie->len >
1157 (uint8_t *)&iblk->index + iblk->index.index_len)
1158 goto index_err;
1159
1160 /* last entry cannot contain a key */
1161 if (ie->flags & INDEX_ENTRY_END) {
1162 /* go to the next VCN */
1163 readdir_state->last_vcn += (blk_size / (1 <<
1164 NTFS_SB(fs)->clust_byte_shift));
1165 readdir_state->entries_count = 0;
1166 goto next_vcn;
1167 }
1168 }
1169
1170 readdir_state->entries_count++;
1171
1172 /* Need to check if this entry has INDEX_ENTRY_END flag set. If
1173 * so, then it won't contain a indexed_file file, so continue the
1174 * lookup on the next VCN/LCN (if any).
1175 */
1176 if (ie->flags & INDEX_ENTRY_END)
1177 goto next_vcn;
1178
1179 len = ntfs_cvt_filename(filename, ie);
1180 if (!is_filename_printable(filename))
1181 goto idx_block_next_entry;
1182
1183 goto done;
1184
1185 out:
1186 readdir_state->in_idx_root = true;
1187
1188 free(mrec);
1189
1190 return -1;
1191
1192 done:
1193 dirent->d_ino = ie->data.dir.indexed_file;
1194 dirent->d_off = file->offset;
1195 dirent->d_reclen = offsetof(struct dirent, d_name) + len + 1;
1196
1197 free(mrec);
1198
1199 mrec = NTFS_SB(fs)->mft_record_lookup(fs, ie->data.dir.indexed_file, NULL);
1200 if (!mrec) {
1201 printf("No MFT record found.\n");
1202 goto out;
1203 }
1204
1205 dirent->d_type = get_inode_mode(mrec);
1206 memcpy(dirent->d_name, filename, len + 1);
1207
1208 free(mrec);
1209
1210 return 0;
1211
1212 not_found:
1213 printf("Index not found\n");
1214 goto out;
1215
1216 index_err:
1217 printf("Corrupt index. Aborting lookup...\n");
1218 goto out;
1219 }
1220
ntfs_iget(const char * dname,struct inode * parent)1221 static inline struct inode *ntfs_iget(const char *dname, struct inode *parent)
1222 {
1223 return ntfs_index_lookup(dname, parent);
1224 }
1225
ntfs_iget_root(struct fs_info * fs)1226 static struct inode *ntfs_iget_root(struct fs_info *fs)
1227 {
1228 uint64_t start_blk;
1229 struct ntfs_mft_record *mrec, *lmrec;
1230 struct ntfs_attr_record *attr;
1231 struct ntfs_vol_info *vol_info;
1232 struct inode *inode;
1233 int err;
1234
1235 dprintf("in %s()\n", __func__);
1236
1237 /* Fetch the $Volume MFT record */
1238 start_blk = 0;
1239 mrec = NTFS_SB(fs)->mft_record_lookup(fs, FILE_Volume, &start_blk);
1240 if (!mrec) {
1241 printf("Could not fetch $Volume MFT record!\n");
1242 goto err_mrec;
1243 }
1244
1245 lmrec = mrec;
1246
1247 /* Fetch the volume information attribute */
1248 attr = ntfs_attr_lookup(fs, NTFS_AT_VOL_INFO, &mrec, lmrec);
1249 if (!attr) {
1250 printf("Could not find volume info attribute!\n");
1251 goto err_attr;
1252 }
1253
1254 /* Note NTFS version and choose version-dependent functions */
1255 vol_info = (void *)((char *)attr + attr->data.resident.value_offset);
1256 NTFS_SB(fs)->major_ver = vol_info->major_ver;
1257 NTFS_SB(fs)->minor_ver = vol_info->minor_ver;
1258 if (vol_info->major_ver == 3 && vol_info->minor_ver == 0)
1259 NTFS_SB(fs)->mft_record_lookup = ntfs_mft_record_lookup_3_0;
1260 else if (vol_info->major_ver == 3 && vol_info->minor_ver == 1 &&
1261 mrec->mft_record_no == FILE_Volume)
1262 NTFS_SB(fs)->mft_record_lookup = ntfs_mft_record_lookup_3_1;
1263
1264 /* Free MFT record */
1265 free(mrec);
1266 mrec = NULL;
1267
1268 inode = new_ntfs_inode(fs);
1269 inode->fs = fs;
1270
1271 err = index_inode_setup(fs, FILE_root, inode);
1272 if (err)
1273 goto err_setup;
1274
1275 NTFS_PVT(inode)->start = NTFS_PVT(inode)->here;
1276
1277 return inode;
1278
1279 err_setup:
1280
1281 free(inode);
1282 err_attr:
1283
1284 free(mrec);
1285 err_mrec:
1286
1287 return NULL;
1288 }
1289
1290 /* Initialize the filesystem metadata and return blk size in bits */
ntfs_fs_init(struct fs_info * fs)1291 static int ntfs_fs_init(struct fs_info *fs)
1292 {
1293 int read_count;
1294 struct ntfs_bpb ntfs;
1295 struct ntfs_sb_info *sbi;
1296 struct disk *disk = fs->fs_dev->disk;
1297 uint8_t mft_record_shift;
1298
1299 dprintf("in %s()\n", __func__);
1300
1301 read_count = disk->rdwr_sectors(disk, &ntfs, 0, 1, 0);
1302 if (!read_count)
1303 return -1;
1304
1305 if (!ntfs_check_sb_fields(&ntfs))
1306 return -1;
1307
1308 SECTOR_SHIFT(fs) = disk->sector_shift;
1309
1310 /* Note: ntfs.clust_per_mft_record can be a negative number.
1311 * If negative, it represents a shift count, else it represents
1312 * a multiplier for the cluster size.
1313 */
1314 mft_record_shift = ntfs.clust_per_mft_record < 0 ?
1315 -ntfs.clust_per_mft_record :
1316 ilog2(ntfs.sec_per_clust) + SECTOR_SHIFT(fs) +
1317 ilog2(ntfs.clust_per_mft_record);
1318
1319 SECTOR_SIZE(fs) = 1 << SECTOR_SHIFT(fs);
1320
1321 sbi = malloc(sizeof *sbi);
1322 if (!sbi)
1323 malloc_error("ntfs_sb_info structure");
1324
1325 fs->fs_info = sbi;
1326
1327 sbi->clust_shift = ilog2(ntfs.sec_per_clust);
1328 sbi->clust_byte_shift = sbi->clust_shift + SECTOR_SHIFT(fs);
1329 sbi->clust_mask = ntfs.sec_per_clust - 1;
1330 sbi->clust_size = ntfs.sec_per_clust << SECTOR_SHIFT(fs);
1331 sbi->mft_record_size = 1 << mft_record_shift;
1332 sbi->clust_per_idx_record = ntfs.clust_per_idx_record;
1333
1334 BLOCK_SHIFT(fs) = ilog2(ntfs.clust_per_idx_record) + sbi->clust_byte_shift;
1335 BLOCK_SIZE(fs) = 1 << BLOCK_SHIFT(fs);
1336
1337 sbi->mft_lcn = ntfs.mft_lclust;
1338 sbi->mft_blk = ntfs.mft_lclust << sbi->clust_shift << SECTOR_SHIFT(fs) >>
1339 BLOCK_SHIFT(fs);
1340 /* 16 MFT entries reserved for metadata files (approximately 16 KiB) */
1341 sbi->mft_size = mft_record_shift << sbi->clust_shift << 4;
1342
1343 sbi->clusters = ntfs.total_sectors << SECTOR_SHIFT(fs) >> sbi->clust_shift;
1344 if (sbi->clusters > 0xFFFFFFFFFFF4ULL)
1345 sbi->clusters = 0xFFFFFFFFFFF4ULL;
1346
1347 /*
1348 * Assume NTFS version 3.0 to begin with. If we find that the
1349 * volume is a different version later on, we will adjust at
1350 * that time.
1351 */
1352 sbi->major_ver = 3;
1353 sbi->minor_ver = 0;
1354 sbi->mft_record_lookup = ntfs_mft_record_lookup_3_0;
1355
1356 /* Initialize the cache */
1357 cache_init(fs->fs_dev, BLOCK_SHIFT(fs));
1358
1359 return BLOCK_SHIFT(fs);
1360 }
1361
1362 const struct fs_ops ntfs_fs_ops = {
1363 .fs_name = "ntfs",
1364 .fs_flags = FS_USEMEM | FS_THISIND,
1365 .fs_init = ntfs_fs_init,
1366 .searchdir = NULL,
1367 .getfssec = ntfs_getfssec,
1368 .close_file = generic_close_file,
1369 .mangle_name = generic_mangle_name,
1370 .open_config = generic_open_config,
1371 .readdir = ntfs_readdir,
1372 .iget_root = ntfs_iget_root,
1373 .iget = ntfs_iget,
1374 .next_extent = ntfs_next_extent,
1375 .fs_uuid = NULL,
1376 };
1377