Adding upstream version 6.03~pre2+dfsg.
[syslinux-debian/hramrach.git] / core / fs / ntfs / ntfs.c
blobf54df7e55c50920a44f60e9c1ee8061f40f72937
1 /*
2 * Copyright (C) 2011-2012 Paulo Alcantara <pcacjr@gmail.com>
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.
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.
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.
20 /* Note: No support for compressed files */
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>
34 #include "codepage.h"
35 #include "ntfs.h"
36 #include "runlist.h"
38 static struct ntfs_readdir_state *readdir_state;
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;
44 /*** Function definitions */
46 /* Check if there are specific zero fields in an NTFS boot sector */
47 static inline int ntfs_check_zero_fields(const struct ntfs_bpb *sb)
49 return !sb->res_sectors && (!sb->zero_0[0] && !sb->zero_0[1] &&
50 !sb->zero_0[2]) && !sb->zero_1 && !sb->zero_2 &&
51 !sb->zero_3;
54 static inline int ntfs_check_sb_fields(const struct ntfs_bpb *sb)
56 return ntfs_check_zero_fields(sb) &&
57 (!memcmp(sb->oem_name, "NTFS ", 8) ||
58 !memcmp(sb->oem_name, "MSWIN4.0", 8) ||
59 !memcmp(sb->oem_name, "MSWIN4.1", 8));
62 static inline struct inode *new_ntfs_inode(struct fs_info *fs)
64 struct inode *inode;
66 inode = alloc_inode(fs, 0, sizeof(struct ntfs_inode));
67 if (!inode)
68 malloc_error("inode structure");
70 return inode;
73 static void ntfs_fixups_writeback(struct fs_info *fs, struct ntfs_record *nrec)
75 uint16_t *usa;
76 uint16_t usa_no;
77 uint16_t usa_count;
78 uint16_t *blk;
80 dprintf("in %s()\n", __func__);
82 if (nrec->magic != NTFS_MAGIC_FILE && nrec->magic != NTFS_MAGIC_INDX)
83 return;
85 /* get the Update Sequence Array offset */
86 usa = (uint16_t *)((uint8_t *)nrec + nrec->usa_ofs);
87 /* get the Update Sequence Array Number and skip it */
88 usa_no = *usa++;
89 /* get the Update Sequene Array count */
90 usa_count = nrec->usa_count - 1; /* exclude the USA number */
91 /* make it to point to the last two bytes of the RECORD's first sector */
92 blk = (uint16_t *)((uint8_t *)nrec + SECTOR_SIZE(fs) - 2);
94 while (usa_count--) {
95 if (*blk != usa_no)
96 break;
98 *blk = *usa++;
99 blk = (uint16_t *)((uint8_t *)blk + SECTOR_SIZE(fs));
103 /* read content from cache */
104 static int ntfs_read(struct fs_info *fs, void *buf, size_t len, uint64_t count,
105 block_t *blk, uint64_t *blk_offset,
106 uint64_t *blk_next_offset, uint64_t *lcn)
108 uint8_t *data;
109 uint64_t offset = *blk_offset;
110 const uint32_t clust_byte_shift = NTFS_SB(fs)->clust_byte_shift;
111 const uint64_t blk_size = UINT64_C(1) << BLOCK_SHIFT(fs);
112 uint64_t bytes;
113 uint64_t lbytes;
114 uint64_t loffset;
115 uint64_t k;
117 dprintf("in %s()\n", __func__);
119 if (count > len)
120 goto out;
122 data = (uint8_t *)get_cache(fs->fs_dev, *blk);
123 if (!data)
124 goto out;
126 if (!offset)
127 offset = (*lcn << clust_byte_shift) % blk_size;
129 dprintf("LCN: 0x%X\n", *lcn);
130 dprintf("offset: 0x%X\n", offset);
132 bytes = count; /* bytes to copy */
133 lbytes = blk_size - offset; /* bytes left to copy */
134 if (lbytes >= bytes) {
135 /* so there's room enough, then copy the whole content */
136 memcpy(buf, data + offset, bytes);
137 loffset = offset;
138 offset += count;
139 } else {
140 dprintf("bytes: %u\n", bytes);
141 dprintf("bytes left: %u\n", lbytes);
142 /* otherwise, let's copy it partially... */
143 k = 0;
144 while (bytes) {
145 memcpy(buf + k, data + offset, lbytes);
146 bytes -= lbytes;
147 loffset = offset;
148 offset += lbytes;
149 k += lbytes;
150 if (offset >= blk_size) {
151 /* then fetch a new FS block */
152 data = (uint8_t *)get_cache(fs->fs_dev, ++*blk);
153 if (!data)
154 goto out;
156 lbytes = bytes;
157 loffset = offset;
158 offset = 0;
163 if (loffset >= blk_size)
164 loffset = 0; /* it must be aligned on a block boundary */
166 *blk_offset = loffset;
168 if (blk_next_offset)
169 *blk_next_offset = offset;
171 *lcn += blk_size / count; /* update LCN */
173 return 0;
175 out:
176 return -1;
179 static struct ntfs_mft_record *ntfs_mft_record_lookup_3_0(struct fs_info *fs,
180 uint32_t file, block_t *blk)
182 const uint64_t mft_record_size = NTFS_SB(fs)->mft_record_size;
183 uint8_t *buf;
184 const block_t mft_blk = NTFS_SB(fs)->mft_blk;
185 block_t cur_blk;
186 block_t right_blk;
187 uint64_t offset;
188 uint64_t next_offset;
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 lcn;
192 int err;
193 struct ntfs_mft_record *mrec;
195 dprintf("in %s()\n", __func__);
197 buf = (uint8_t *)malloc(mft_record_size);
198 if (!buf)
199 malloc_error("uint8_t *");
201 /* determine MFT record's LCN and block number */
202 lcn = NTFS_SB(fs)->mft_lcn + (file << mft_record_shift >> clust_byte_shift);
203 cur_blk = (lcn << clust_byte_shift >> BLOCK_SHIFT(fs)) - mft_blk;
204 offset = (file << mft_record_shift) % BLOCK_SIZE(fs);
205 for (;;) {
206 right_blk = cur_blk + mft_blk;
207 err = ntfs_read(fs, buf, mft_record_size, mft_record_size, &right_blk,
208 &offset, &next_offset, &lcn);
209 if (err) {
210 printf("Error while reading from cache.\n");
211 break;
214 ntfs_fixups_writeback(fs, (struct ntfs_record *)buf);
216 mrec = (struct ntfs_mft_record *)buf;
217 /* check if it has a valid magic number */
218 if (mrec->magic == NTFS_MAGIC_FILE) {
219 if (blk)
220 *blk = cur_blk; /* update record starting block */
222 return mrec; /* found MFT record */
225 if (next_offset >= BLOCK_SIZE(fs)) {
226 /* try the next FS block */
227 offset = 0;
228 cur_blk = right_blk - mft_blk + 1;
229 } else {
230 /* there's still content to fetch in the current block */
231 cur_blk = right_blk - mft_blk;
232 offset = next_offset; /* update FS block offset */
236 free(buf);
238 return NULL;
241 static struct ntfs_mft_record *ntfs_mft_record_lookup_3_1(struct fs_info *fs,
242 uint32_t file, block_t *blk)
244 const uint64_t mft_record_size = NTFS_SB(fs)->mft_record_size;
245 uint8_t *buf;
246 const block_t mft_blk = NTFS_SB(fs)->mft_blk;
247 block_t cur_blk;
248 block_t right_blk;
249 uint64_t offset;
250 uint64_t next_offset;
251 const uint32_t mft_record_shift = ilog2(mft_record_size);
252 const uint32_t clust_byte_shift = NTFS_SB(fs)->clust_byte_shift;
253 uint64_t lcn;
254 int err;
255 struct ntfs_mft_record *mrec;
257 dprintf("in %s()\n", __func__);
259 buf = (uint8_t *)malloc(mft_record_size);
260 if (!buf)
261 malloc_error("uint8_t *");
263 lcn = NTFS_SB(fs)->mft_lcn + (file << mft_record_shift >> clust_byte_shift);
264 cur_blk = (lcn << clust_byte_shift >> BLOCK_SHIFT(fs)) - mft_blk;
265 offset = (file << mft_record_shift) % BLOCK_SIZE(fs);
266 for (;;) {
267 right_blk = cur_blk + NTFS_SB(fs)->mft_blk;
268 err = ntfs_read(fs, buf, mft_record_size, mft_record_size, &right_blk,
269 &offset, &next_offset, &lcn);
270 if (err) {
271 printf("Error while reading from cache.\n");
272 break;
275 ntfs_fixups_writeback(fs, (struct ntfs_record *)buf);
277 mrec = (struct ntfs_mft_record *)buf;
278 /* Check if the NTFS 3.1 MFT record number matches */
279 if (mrec->magic == NTFS_MAGIC_FILE && mrec->mft_record_no == file) {
280 if (blk)
281 *blk = cur_blk; /* update record starting block */
283 return mrec; /* found MFT record */
286 if (next_offset >= BLOCK_SIZE(fs)) {
287 /* try the next FS block */
288 offset = 0;
289 cur_blk = right_blk - NTFS_SB(fs)->mft_blk + 1;
290 } else {
291 /* there's still content to fetch in the current block */
292 cur_blk = right_blk - NTFS_SB(fs)->mft_blk;
293 offset = next_offset; /* update FS block offset */
297 free(buf);
299 return NULL;
302 static bool ntfs_filename_cmp(const char *dname, struct ntfs_idx_entry *ie)
304 const uint16_t *entry_fn;
305 uint8_t entry_fn_len;
306 unsigned i;
308 dprintf("in %s()\n", __func__);
310 entry_fn = ie->key.file_name.file_name;
311 entry_fn_len = ie->key.file_name.file_name_len;
313 if (strlen(dname) != entry_fn_len)
314 return false;
316 /* Do case-sensitive compares for Posix file names */
317 if (ie->key.file_name.file_name_type == FILE_NAME_POSIX) {
318 for (i = 0; i < entry_fn_len; i++)
319 if (entry_fn[i] != dname[i])
320 return false;
321 } else {
322 for (i = 0; i < entry_fn_len; i++)
323 if (tolower(entry_fn[i]) != tolower(dname[i]))
324 return false;
327 return true;
330 static inline uint8_t *mapping_chunk_init(struct ntfs_attr_record *attr,
331 struct mapping_chunk *chunk,
332 uint32_t *offset)
334 memset(chunk, 0, sizeof *chunk);
335 *offset = 0U;
337 return (uint8_t *)attr + attr->data.non_resident.mapping_pairs_offset;
340 /* Parse data runs.
342 * return 0 on success or -1 on failure.
344 static int parse_data_run(const void *stream, uint32_t *offset,
345 uint8_t *attr_len, struct mapping_chunk *chunk)
347 uint8_t *buf; /* Pointer to the zero-terminated byte stream */
348 uint8_t count; /* The count byte */
349 uint8_t v, l; /* v is the number of changed low-order VCN bytes;
350 * l is the number of changed low-order LCN bytes
352 uint8_t *byte;
353 int byte_shift = 8;
354 int mask;
355 uint8_t val;
356 int64_t res;
358 (void)attr_len;
360 dprintf("in %s()\n", __func__);
362 chunk->flags &= ~MAP_MASK;
364 buf = (uint8_t *)stream + *offset;
365 if (buf > attr_len || !*buf) {
366 chunk->flags |= MAP_END; /* we're done */
367 return 0;
370 if (!*offset)
371 chunk->flags |= MAP_START; /* initial chunk */
373 count = *buf;
374 v = count & 0x0F;
375 l = count >> 4;
377 if (v > 8 || l > 8) /* more than 8 bytes ? */
378 goto out;
380 byte = (uint8_t *)buf + v;
381 count = v;
383 res = 0LL;
384 while (count--) {
385 val = *byte--;
386 mask = val >> (byte_shift - 1);
387 res = (res << byte_shift) | ((val + mask) ^ mask);
390 chunk->len = res; /* get length data */
392 byte = (uint8_t *)buf + v + l;
393 count = l;
395 mask = 0xFFFFFFFF;
396 res = 0LL;
397 if (*byte & 0x80)
398 res |= (int64_t)mask; /* sign-extend it */
400 while (count--)
401 res = (res << byte_shift) | *byte--;
403 chunk->lcn += res;
404 /* are VCNS from cur_vcn to next_vcn - 1 unallocated ? */
405 if (!chunk->lcn)
406 chunk->flags |= MAP_UNALLOCATED;
407 else
408 chunk->flags |= MAP_ALLOCATED;
410 *offset += v + l + 1;
412 return 0;
414 out:
415 return -1;
418 static struct ntfs_mft_record *
419 ntfs_attr_list_lookup(struct fs_info *fs, struct ntfs_attr_record *attr,
420 uint32_t type, struct ntfs_mft_record *mrec)
422 uint8_t *attr_len;
423 struct mapping_chunk chunk;
424 uint32_t offset;
425 uint8_t *stream;
426 int err;
427 const uint64_t blk_size = UINT64_C(1) << BLOCK_SHIFT(fs);
428 uint8_t buf[blk_size];
429 uint64_t blk_offset;
430 int64_t vcn;
431 int64_t lcn;
432 int64_t last_lcn;
433 block_t blk;
434 struct ntfs_attr_list_entry *attr_entry;
435 uint32_t len = 0;
436 struct ntfs_mft_record *retval;
437 uint64_t start_blk = 0;
439 dprintf("in %s()\n", __func__);
441 if (attr->non_resident)
442 goto handle_non_resident_attr;
444 attr_entry = (struct ntfs_attr_list_entry *)
445 ((uint8_t *)attr + attr->data.resident.value_offset);
446 len = attr->data.resident.value_len;
447 for (; (uint8_t *)attr_entry < (uint8_t *)attr + len;
448 attr_entry = (struct ntfs_attr_list_entry *)((uint8_t *)attr_entry +
449 attr_entry->length)) {
450 dprintf("<$ATTRIBUTE_LIST> Attribute type: 0x%X\n",
451 attr_entry->type);
452 if (attr_entry->type == type)
453 goto found; /* We got the attribute! :-) */
456 printf("No attribute found.\n");
457 goto out;
459 handle_non_resident_attr:
460 attr_len = (uint8_t *)attr + attr->len;
461 stream = mapping_chunk_init(attr, &chunk, &offset);
462 do {
463 err = parse_data_run(stream, &offset, attr_len, &chunk);
464 if (err) {
465 printf("parse_data_run()\n");
466 goto out;
469 if (chunk.flags & MAP_UNALLOCATED)
470 continue;
471 if (chunk.flags & MAP_END)
472 break;
473 if (chunk.flags & MAP_ALLOCATED) {
474 vcn = 0;
475 lcn = chunk.lcn;
476 while (vcn < chunk.len) {
477 blk = (lcn + vcn) << NTFS_SB(fs)->clust_byte_shift >>
478 BLOCK_SHIFT(fs);
479 blk_offset = 0;
480 last_lcn = lcn;
481 lcn += vcn;
482 err = ntfs_read(fs, buf, blk_size, blk_size, &blk,
483 &blk_offset, NULL, (uint64_t *)&lcn);
484 if (err) {
485 printf("Error while reading from cache.\n");
486 goto out;
489 attr_entry = (struct ntfs_attr_list_entry *)&buf;
490 len = attr->data.non_resident.data_size;
491 for (; (uint8_t *)attr_entry < (uint8_t *)&buf[0] + len;
492 attr_entry = (struct ntfs_attr_list_entry *)
493 ((uint8_t *)attr_entry + attr_entry->length)) {
494 dprintf("<$ATTRIBUTE_LIST> Attribute type: 0x%x\n",
495 attr_entry->type);
496 if (attr_entry->type == type)
497 goto found; /* We got the attribute! :-) */
500 lcn = last_lcn; /* restore original LCN */
501 /* go to the next VCN */
502 vcn += (blk_size / (1 << NTFS_SB(fs)->clust_byte_shift));
505 } while (!(chunk.flags & MAP_END));
507 printf("No attribute found.\n");
509 out:
510 return NULL;
512 found:
513 /* At this point we have the attribute we were looking for. Now we
514 * will look for the MFT record that stores information about this
515 * attribute.
518 /* Check if the attribute type we're looking for is in the same
519 * MFT record. If so, we do not need to look it up again - return it.
521 if (mrec->mft_record_no == attr_entry->mft_ref)
522 return mrec;
524 retval = NTFS_SB(fs)->mft_record_lookup(fs, attr_entry->mft_ref,
525 &start_blk);
526 if (!retval) {
527 printf("No MFT record found!\n");
528 goto out;
531 /* return the found MFT record */
532 return retval;
535 static struct ntfs_attr_record *
536 __ntfs_attr_lookup(struct fs_info *fs, uint32_t type,
537 struct ntfs_mft_record **mrec)
539 struct ntfs_mft_record *_mrec = *mrec;
540 struct ntfs_attr_record *attr;
541 struct ntfs_attr_record *attr_list_attr;
543 dprintf("in %s()\n", __func__);
545 if (!_mrec || type == NTFS_AT_END)
546 goto out;
548 again:
549 attr_list_attr = NULL;
551 attr = (struct ntfs_attr_record *)((uint8_t *)_mrec + _mrec->attrs_offset);
552 /* walk through the file attribute records */
553 for (;; attr = (struct ntfs_attr_record *)((uint8_t *)attr + attr->len)) {
554 if (attr->type == NTFS_AT_END)
555 break;
557 if (attr->type == NTFS_AT_ATTR_LIST) {
558 dprintf("MFT record #%lu has an $ATTRIBUTE_LIST attribute.\n",
559 _mrec->mft_record_no);
560 attr_list_attr = attr;
561 continue;
564 if (attr->type == type)
565 break;
568 /* if the record has an $ATTRIBUTE_LIST attribute associated
569 * with it, then we need to look for the wanted attribute in
570 * it as well.
572 if (attr->type == NTFS_AT_END && attr_list_attr) {
573 struct ntfs_mft_record *retval;
575 retval = ntfs_attr_list_lookup(fs, attr_list_attr, type, _mrec);
576 if (!retval)
577 goto out;
579 _mrec = retval;
580 goto again;
581 } else if (attr->type == NTFS_AT_END && !attr_list_attr) {
582 attr = NULL;
585 return attr;
587 out:
588 return NULL;
591 static inline struct ntfs_attr_record *
592 ntfs_attr_lookup(struct fs_info *fs, uint32_t type,
593 struct ntfs_mft_record **mmrec,
594 struct ntfs_mft_record *mrec)
596 struct ntfs_mft_record *_mrec = mrec;
597 struct ntfs_mft_record *other = *mmrec;
598 struct ntfs_attr_record *retval = NULL;
600 if (mrec == other)
601 return __ntfs_attr_lookup(fs, type, &other);
603 retval = __ntfs_attr_lookup(fs, type, &_mrec);
604 if (!retval) {
605 _mrec = other;
606 retval = __ntfs_attr_lookup(fs, type, &other);
607 if (!retval)
608 other = _mrec;
609 } else if (retval && (_mrec != mrec)) {
610 other = _mrec;
613 return retval;
616 static inline enum dirent_type get_inode_mode(struct ntfs_mft_record *mrec)
618 return mrec->flags & MFT_RECORD_IS_DIRECTORY ? DT_DIR : DT_REG;
621 static int index_inode_setup(struct fs_info *fs, unsigned long mft_no,
622 struct inode *inode)
624 uint64_t start_blk = 0;
625 struct ntfs_mft_record *mrec, *lmrec;
626 struct ntfs_attr_record *attr;
627 enum dirent_type d_type;
628 uint8_t *attr_len;
629 struct mapping_chunk chunk;
630 int err;
631 uint8_t *stream;
632 uint32_t offset;
634 dprintf("in %s()\n", __func__);
636 mrec = NTFS_SB(fs)->mft_record_lookup(fs, mft_no, &start_blk);
637 if (!mrec) {
638 printf("No MFT record found.\n");
639 goto out;
642 lmrec = mrec;
644 NTFS_PVT(inode)->mft_no = mft_no;
645 NTFS_PVT(inode)->seq_no = mrec->seq_no;
647 NTFS_PVT(inode)->start_cluster = start_blk >> NTFS_SB(fs)->clust_shift;
648 NTFS_PVT(inode)->here = start_blk;
650 d_type = get_inode_mode(mrec);
651 if (d_type == DT_DIR) { /* directory stuff */
652 dprintf("Got a directory.\n");
653 attr = ntfs_attr_lookup(fs, NTFS_AT_INDEX_ROOT, &mrec, lmrec);
654 if (!attr) {
655 printf("No attribute found.\n");
656 goto out;
659 /* check if we have a previous allocated state structure */
660 if (readdir_state) {
661 free(readdir_state);
662 readdir_state = NULL;
665 /* allocate our state structure */
666 readdir_state = malloc(sizeof *readdir_state);
667 if (!readdir_state)
668 malloc_error("ntfs_readdir_state structure");
670 readdir_state->mft_no = mft_no;
671 /* obviously, the ntfs_readdir() caller will start from INDEX root */
672 readdir_state->in_idx_root = true;
673 } else if (d_type == DT_REG) { /* file stuff */
674 dprintf("Got a file.\n");
675 attr = ntfs_attr_lookup(fs, NTFS_AT_DATA, &mrec, lmrec);
676 if (!attr) {
677 printf("No attribute found.\n");
678 goto out;
681 NTFS_PVT(inode)->non_resident = attr->non_resident;
682 NTFS_PVT(inode)->type = attr->type;
684 if (!attr->non_resident) {
685 NTFS_PVT(inode)->data.resident.offset =
686 (uint32_t)((uint8_t *)attr + attr->data.resident.value_offset);
687 inode->size = attr->data.resident.value_len;
688 } else {
689 attr_len = (uint8_t *)attr + attr->len;
691 stream = mapping_chunk_init(attr, &chunk, &offset);
692 NTFS_PVT(inode)->data.non_resident.rlist = NULL;
693 for (;;) {
694 err = parse_data_run(stream, &offset, attr_len, &chunk);
695 if (err) {
696 printf("parse_data_run()\n");
697 goto out;
700 if (chunk.flags & MAP_UNALLOCATED)
701 continue;
702 if (chunk.flags & MAP_END)
703 break;
704 if (chunk.flags & MAP_ALLOCATED) {
705 /* append new run to the runlist */
706 runlist_append(&NTFS_PVT(inode)->data.non_resident.rlist,
707 (struct runlist_element *)&chunk);
708 /* update for next VCN */
709 chunk.vcn += chunk.len;
713 if (runlist_is_empty(NTFS_PVT(inode)->data.non_resident.rlist)) {
714 printf("No mapping found\n");
715 goto out;
718 inode->size = attr->data.non_resident.initialized_size;
722 inode->mode = d_type;
724 free(mrec);
726 return 0;
728 out:
729 free(mrec);
731 return -1;
734 static struct inode *ntfs_index_lookup(const char *dname, struct inode *dir)
736 struct fs_info *fs = dir->fs;
737 struct ntfs_mft_record *mrec, *lmrec;
738 block_t blk;
739 uint64_t blk_offset;
740 struct ntfs_attr_record *attr;
741 struct ntfs_idx_root *ir;
742 struct ntfs_idx_entry *ie;
743 const uint64_t blk_size = UINT64_C(1) << BLOCK_SHIFT(fs);
744 uint8_t buf[blk_size];
745 struct ntfs_idx_allocation *iblk;
746 int err;
747 uint8_t *stream;
748 uint8_t *attr_len;
749 struct mapping_chunk chunk;
750 uint32_t offset;
751 int64_t vcn;
752 int64_t lcn;
753 int64_t last_lcn;
754 struct inode *inode;
756 dprintf("in %s()\n", __func__);
758 mrec = NTFS_SB(fs)->mft_record_lookup(fs, NTFS_PVT(dir)->mft_no, NULL);
759 if (!mrec) {
760 printf("No MFT record found.\n");
761 goto out;
764 lmrec = mrec;
765 attr = ntfs_attr_lookup(fs, NTFS_AT_INDEX_ROOT, &mrec, lmrec);
766 if (!attr) {
767 printf("No attribute found.\n");
768 goto out;
771 ir = (struct ntfs_idx_root *)((uint8_t *)attr +
772 attr->data.resident.value_offset);
773 ie = (struct ntfs_idx_entry *)((uint8_t *)&ir->index +
774 ir->index.entries_offset);
775 for (;; ie = (struct ntfs_idx_entry *)((uint8_t *)ie + ie->len)) {
776 /* bounds checks */
777 if ((uint8_t *)ie < (uint8_t *)mrec ||
778 (uint8_t *)ie + sizeof(struct ntfs_idx_entry_header) >
779 (uint8_t *)&ir->index + ir->index.index_len ||
780 (uint8_t *)ie + ie->len >
781 (uint8_t *)&ir->index + ir->index.index_len)
782 goto index_err;
784 /* last entry cannot contain a key. it can however contain
785 * a pointer to a child node in the B+ tree so we just break out
787 if (ie->flags & INDEX_ENTRY_END)
788 break;
790 if (ntfs_filename_cmp(dname, ie))
791 goto found;
794 /* check for the presence of a child node */
795 if (!(ie->flags & INDEX_ENTRY_NODE)) {
796 printf("No child node, aborting...\n");
797 goto out;
800 /* then descend into child node */
802 attr = ntfs_attr_lookup(fs, NTFS_AT_INDEX_ALLOCATION, &mrec, lmrec);
803 if (!attr) {
804 printf("No attribute found.\n");
805 goto out;
808 if (!attr->non_resident) {
809 printf("WTF ?! $INDEX_ALLOCATION isn't really resident.\n");
810 goto out;
813 attr_len = (uint8_t *)attr + attr->len;
814 stream = mapping_chunk_init(attr, &chunk, &offset);
815 do {
816 err = parse_data_run(stream, &offset, attr_len, &chunk);
817 if (err)
818 break;
820 if (chunk.flags & MAP_UNALLOCATED)
821 continue;
823 if (chunk.flags & MAP_ALLOCATED) {
824 dprintf("%d cluster(s) starting at 0x%08llX\n", chunk.len,
825 chunk.lcn);
827 vcn = 0;
828 lcn = chunk.lcn;
829 while (vcn < chunk.len) {
830 blk = (lcn + vcn) << NTFS_SB(fs)->clust_shift <<
831 SECTOR_SHIFT(fs) >> BLOCK_SHIFT(fs);
833 blk_offset = 0;
834 last_lcn = lcn;
835 lcn += vcn;
836 err = ntfs_read(fs, &buf, blk_size, blk_size, &blk,
837 &blk_offset, NULL, (uint64_t *)&lcn);
838 if (err) {
839 printf("Error while reading from cache.\n");
840 goto not_found;
843 ntfs_fixups_writeback(fs, (struct ntfs_record *)&buf);
845 iblk = (struct ntfs_idx_allocation *)&buf;
846 if (iblk->magic != NTFS_MAGIC_INDX) {
847 printf("Not a valid INDX record.\n");
848 goto not_found;
851 ie = (struct ntfs_idx_entry *)((uint8_t *)&iblk->index +
852 iblk->index.entries_offset);
853 for (;; ie = (struct ntfs_idx_entry *)((uint8_t *)ie +
854 ie->len)) {
855 /* bounds checks */
856 if ((uint8_t *)ie < (uint8_t *)iblk || (uint8_t *)ie +
857 sizeof(struct ntfs_idx_entry_header) >
858 (uint8_t *)&iblk->index + iblk->index.index_len ||
859 (uint8_t *)ie + ie->len >
860 (uint8_t *)&iblk->index + iblk->index.index_len)
861 goto index_err;
863 /* last entry cannot contain a key */
864 if (ie->flags & INDEX_ENTRY_END)
865 break;
867 if (ntfs_filename_cmp(dname, ie))
868 goto found;
871 lcn = last_lcn; /* restore the original LCN */
872 /* go to the next VCN */
873 vcn += (blk_size / (1 << NTFS_SB(fs)->clust_byte_shift));
876 } while (!(chunk.flags & MAP_END));
878 not_found:
879 dprintf("Index not found\n");
881 out:
882 free(mrec);
884 return NULL;
886 found:
887 dprintf("Index found\n");
888 inode = new_ntfs_inode(fs);
889 err = index_inode_setup(fs, ie->data.dir.indexed_file, inode);
890 if (err) {
891 printf("Error in index_inode_setup()\n");
892 free(inode);
893 goto out;
896 free(mrec);
898 return inode;
900 index_err:
901 printf("Corrupt index. Aborting lookup...\n");
902 goto out;
905 /* Convert an UTF-16LE LFN to OEM LFN */
906 static uint8_t ntfs_cvt_filename(char *filename,
907 const struct ntfs_idx_entry *ie)
909 const uint16_t *entry_fn;
910 uint8_t entry_fn_len;
911 unsigned i;
913 entry_fn = ie->key.file_name.file_name;
914 entry_fn_len = ie->key.file_name.file_name_len;
916 for (i = 0; i < entry_fn_len; i++)
917 filename[i] = (char)entry_fn[i];
919 filename[i] = '\0';
921 return entry_fn_len;
924 static int ntfs_next_extent(struct inode *inode, uint32_t lstart)
926 struct fs_info *fs = inode->fs;
927 struct ntfs_sb_info *sbi = NTFS_SB(fs);
928 sector_t pstart = 0;
929 struct runlist *rlist;
930 struct runlist *ret;
931 const uint32_t sec_size = SECTOR_SIZE(fs);
932 const uint32_t sec_shift = SECTOR_SHIFT(fs);
934 dprintf("in %s()\n", __func__);
936 if (!NTFS_PVT(inode)->non_resident) {
937 pstart = (sbi->mft_blk + NTFS_PVT(inode)->here) << BLOCK_SHIFT(fs) >>
938 sec_shift;
939 inode->next_extent.len = (inode->size + sec_size - 1) >> sec_shift;
940 } else {
941 rlist = NTFS_PVT(inode)->data.non_resident.rlist;
943 if (!lstart || lstart >= NTFS_PVT(inode)->here) {
944 if (runlist_is_empty(rlist))
945 goto out; /* nothing to do ;-) */
947 ret = runlist_remove(&rlist);
949 NTFS_PVT(inode)->here =
950 ((ret->run.len << sbi->clust_byte_shift) >> sec_shift);
952 pstart = ret->run.lcn << sbi->clust_shift;
953 inode->next_extent.len =
954 ((ret->run.len << sbi->clust_byte_shift) + sec_size - 1) >>
955 sec_shift;
957 NTFS_PVT(inode)->data.non_resident.rlist = rlist;
959 free(ret);
960 ret = NULL;
964 inode->next_extent.pstart = pstart;
966 return 0;
968 out:
969 return -1;
972 static uint32_t ntfs_getfssec(struct file *file, char *buf, int sectors,
973 bool *have_more)
975 uint8_t non_resident;
976 uint32_t ret;
977 struct fs_info *fs = file->fs;
978 struct inode *inode = file->inode;
979 struct ntfs_mft_record *mrec, *lmrec;
980 struct ntfs_attr_record *attr;
981 char *p;
983 dprintf("in %s()\n", __func__);
985 non_resident = NTFS_PVT(inode)->non_resident;
987 ret = generic_getfssec(file, buf, sectors, have_more);
988 if (!ret)
989 return ret;
991 if (!non_resident) {
992 mrec = NTFS_SB(fs)->mft_record_lookup(fs, NTFS_PVT(inode)->mft_no,
993 NULL);
994 if (!mrec) {
995 printf("No MFT record found.\n");
996 goto out;
999 lmrec = mrec;
1000 attr = ntfs_attr_lookup(fs, NTFS_AT_DATA, &mrec, lmrec);
1001 if (!attr) {
1002 printf("No attribute found.\n");
1003 goto out;
1006 p = (char *)((uint8_t *)attr + attr->data.resident.value_offset);
1008 /* p now points to the data offset, so let's copy it into buf */
1009 memcpy(buf, p, inode->size);
1011 ret = inode->size;
1013 free(mrec);
1016 return ret;
1018 out:
1019 free(mrec);
1021 return 0;
1024 static inline bool is_filename_printable(const char *s)
1026 return s && (*s != '.' && *s != '$');
1029 static int ntfs_readdir(struct file *file, struct dirent *dirent)
1031 struct fs_info *fs = file->fs;
1032 struct inode *inode = file->inode;
1033 struct ntfs_mft_record *mrec, *lmrec;
1034 block_t blk;
1035 uint64_t blk_offset;
1036 const uint64_t blk_size = UINT64_C(1) << BLOCK_SHIFT(fs);
1037 struct ntfs_attr_record *attr;
1038 struct ntfs_idx_root *ir;
1039 uint32_t count;
1040 int len;
1041 struct ntfs_idx_entry *ie = NULL;
1042 uint8_t buf[BLOCK_SIZE(fs)];
1043 struct ntfs_idx_allocation *iblk;
1044 int err;
1045 uint8_t *stream;
1046 uint8_t *attr_len;
1047 struct mapping_chunk chunk;
1048 uint32_t offset;
1049 int64_t vcn;
1050 int64_t lcn;
1051 char filename[NTFS_MAX_FILE_NAME_LEN + 1];
1053 dprintf("in %s()\n", __func__);
1055 mrec = NTFS_SB(fs)->mft_record_lookup(fs, NTFS_PVT(inode)->mft_no, NULL);
1056 if (!mrec) {
1057 printf("No MFT record found.\n");
1058 goto out;
1061 lmrec = mrec;
1062 attr = ntfs_attr_lookup(fs, NTFS_AT_INDEX_ROOT, &mrec, lmrec);
1063 if (!attr) {
1064 printf("No attribute found.\n");
1065 goto out;
1068 ir = (struct ntfs_idx_root *)((uint8_t *)attr +
1069 attr->data.resident.value_offset);
1071 if (!file->offset && readdir_state->in_idx_root) {
1072 file->offset = (uint32_t)((uint8_t *)&ir->index +
1073 ir->index.entries_offset);
1076 idx_root_next_entry:
1077 if (readdir_state->in_idx_root) {
1078 ie = (struct ntfs_idx_entry *)(uint8_t *)file->offset;
1079 if (ie->flags & INDEX_ENTRY_END) {
1080 file->offset = 0;
1081 readdir_state->in_idx_root = false;
1082 readdir_state->idx_blks_count = 1;
1083 readdir_state->entries_count = 0;
1084 readdir_state->last_vcn = 0;
1085 goto descend_into_child_node;
1088 file->offset = (uint32_t)((uint8_t *)ie + ie->len);
1089 len = ntfs_cvt_filename(filename, ie);
1090 if (!is_filename_printable(filename))
1091 goto idx_root_next_entry;
1093 goto done;
1096 descend_into_child_node:
1097 if (!(ie->flags & INDEX_ENTRY_NODE))
1098 goto out;
1100 attr = ntfs_attr_lookup(fs, NTFS_AT_INDEX_ALLOCATION, &mrec, lmrec);
1101 if (!attr)
1102 goto out;
1104 if (!attr->non_resident) {
1105 printf("WTF ?! $INDEX_ALLOCATION isn't really resident.\n");
1106 goto out;
1109 attr_len = (uint8_t *)attr + attr->len;
1111 next_run:
1112 stream = mapping_chunk_init(attr, &chunk, &offset);
1113 count = readdir_state->idx_blks_count;
1114 while (count--) {
1115 err = parse_data_run(stream, &offset, attr_len, &chunk);
1116 if (err) {
1117 printf("Error while parsing data runs.\n");
1118 goto out;
1121 if (chunk.flags & MAP_UNALLOCATED)
1122 break;
1123 if (chunk.flags & MAP_END)
1124 goto out;
1127 if (chunk.flags & MAP_UNALLOCATED) {
1128 readdir_state->idx_blks_count++;
1129 goto next_run;
1132 next_vcn:
1133 vcn = readdir_state->last_vcn;
1134 if (vcn >= chunk.len) {
1135 readdir_state->last_vcn = 0;
1136 readdir_state->idx_blks_count++;
1137 goto next_run;
1140 lcn = chunk.lcn;
1141 blk = (lcn + vcn) << NTFS_SB(fs)->clust_shift << SECTOR_SHIFT(fs) >>
1142 BLOCK_SHIFT(fs);
1144 blk_offset = 0;
1145 err = ntfs_read(fs, &buf, blk_size, blk_size, &blk, &blk_offset, NULL,
1146 (uint64_t *)&lcn);
1147 if (err) {
1148 printf("Error while reading from cache.\n");
1149 goto not_found;
1152 ntfs_fixups_writeback(fs, (struct ntfs_record *)&buf);
1154 iblk = (struct ntfs_idx_allocation *)&buf;
1155 if (iblk->magic != NTFS_MAGIC_INDX) {
1156 printf("Not a valid INDX record.\n");
1157 goto not_found;
1160 idx_block_next_entry:
1161 ie = (struct ntfs_idx_entry *)((uint8_t *)&iblk->index +
1162 iblk->index.entries_offset);
1163 count = readdir_state->entries_count;
1164 for ( ; count--; ie = (struct ntfs_idx_entry *)((uint8_t *)ie + ie->len)) {
1165 /* bounds checks */
1166 if ((uint8_t *)ie < (uint8_t *)iblk || (uint8_t *)ie +
1167 sizeof(struct ntfs_idx_entry_header) >
1168 (uint8_t *)&iblk->index + iblk->index.index_len ||
1169 (uint8_t *)ie + ie->len >
1170 (uint8_t *)&iblk->index + iblk->index.index_len)
1171 goto index_err;
1173 /* last entry cannot contain a key */
1174 if (ie->flags & INDEX_ENTRY_END) {
1175 /* go to the next VCN */
1176 readdir_state->last_vcn += (blk_size / (1 <<
1177 NTFS_SB(fs)->clust_byte_shift));
1178 readdir_state->entries_count = 0;
1179 goto next_vcn;
1183 readdir_state->entries_count++;
1185 /* Need to check if this entry has INDEX_ENTRY_END flag set. If
1186 * so, then it won't contain a indexed_file file, so continue the
1187 * lookup on the next VCN/LCN (if any).
1189 if (ie->flags & INDEX_ENTRY_END)
1190 goto next_vcn;
1192 len = ntfs_cvt_filename(filename, ie);
1193 if (!is_filename_printable(filename))
1194 goto idx_block_next_entry;
1196 goto done;
1198 out:
1199 readdir_state->in_idx_root = true;
1201 free(mrec);
1203 return -1;
1205 done:
1206 dirent->d_ino = ie->data.dir.indexed_file;
1207 dirent->d_off = file->offset;
1208 dirent->d_reclen = offsetof(struct dirent, d_name) + len + 1;
1210 free(mrec);
1212 mrec = NTFS_SB(fs)->mft_record_lookup(fs, ie->data.dir.indexed_file, NULL);
1213 if (!mrec) {
1214 printf("No MFT record found.\n");
1215 goto out;
1218 dirent->d_type = get_inode_mode(mrec);
1219 memcpy(dirent->d_name, filename, len + 1);
1221 free(mrec);
1223 return 0;
1225 not_found:
1226 printf("Index not found\n");
1227 goto out;
1229 index_err:
1230 printf("Corrupt index. Aborting lookup...\n");
1231 goto out;
1234 static inline struct inode *ntfs_iget(const char *dname, struct inode *parent)
1236 return ntfs_index_lookup(dname, parent);
1239 static struct inode *ntfs_iget_root(struct fs_info *fs)
1241 uint64_t start_blk;
1242 struct ntfs_mft_record *mrec, *lmrec;
1243 struct ntfs_attr_record *attr;
1244 struct ntfs_vol_info *vol_info;
1245 struct inode *inode;
1246 int err;
1248 dprintf("in %s()\n", __func__);
1250 /* Fetch the $Volume MFT record */
1251 start_blk = 0;
1252 mrec = NTFS_SB(fs)->mft_record_lookup(fs, FILE_Volume, &start_blk);
1253 if (!mrec) {
1254 printf("Could not fetch $Volume MFT record!\n");
1255 goto err_mrec;
1258 lmrec = mrec;
1260 /* Fetch the volume information attribute */
1261 attr = ntfs_attr_lookup(fs, NTFS_AT_VOL_INFO, &mrec, lmrec);
1262 if (!attr) {
1263 printf("Could not find volume info attribute!\n");
1264 goto err_attr;
1267 /* Note NTFS version and choose version-dependent functions */
1268 vol_info = (void *)((char *)attr + attr->data.resident.value_offset);
1269 NTFS_SB(fs)->major_ver = vol_info->major_ver;
1270 NTFS_SB(fs)->minor_ver = vol_info->minor_ver;
1271 if (vol_info->major_ver == 3 && vol_info->minor_ver == 0)
1272 NTFS_SB(fs)->mft_record_lookup = ntfs_mft_record_lookup_3_0;
1273 else if (vol_info->major_ver == 3 && vol_info->minor_ver == 1 &&
1274 mrec->mft_record_no == FILE_Volume)
1275 NTFS_SB(fs)->mft_record_lookup = ntfs_mft_record_lookup_3_1;
1277 /* Free MFT record */
1278 free(mrec);
1279 mrec = NULL;
1281 inode = new_ntfs_inode(fs);
1282 inode->fs = fs;
1284 err = index_inode_setup(fs, FILE_root, inode);
1285 if (err)
1286 goto err_setup;
1288 NTFS_PVT(inode)->start = NTFS_PVT(inode)->here;
1290 return inode;
1292 err_setup:
1294 free(inode);
1295 err_attr:
1297 free(mrec);
1298 err_mrec:
1300 return NULL;
1303 /* Initialize the filesystem metadata and return blk size in bits */
1304 static int ntfs_fs_init(struct fs_info *fs)
1306 int read_count;
1307 struct ntfs_bpb ntfs;
1308 struct ntfs_sb_info *sbi;
1309 struct disk *disk = fs->fs_dev->disk;
1310 uint8_t mft_record_shift;
1312 dprintf("in %s()\n", __func__);
1314 read_count = disk->rdwr_sectors(disk, &ntfs, 0, 1, 0);
1315 if (!read_count)
1316 return -1;
1318 if (!ntfs_check_sb_fields(&ntfs))
1319 return -1;
1321 SECTOR_SHIFT(fs) = disk->sector_shift;
1323 /* Note: ntfs.clust_per_mft_record can be a negative number.
1324 * If negative, it represents a shift count, else it represents
1325 * a multiplier for the cluster size.
1327 mft_record_shift = ntfs.clust_per_mft_record < 0 ?
1328 -ntfs.clust_per_mft_record :
1329 ilog2(ntfs.sec_per_clust) + SECTOR_SHIFT(fs) +
1330 ilog2(ntfs.clust_per_mft_record);
1332 SECTOR_SIZE(fs) = 1 << SECTOR_SHIFT(fs);
1334 sbi = malloc(sizeof *sbi);
1335 if (!sbi)
1336 malloc_error("ntfs_sb_info structure");
1338 fs->fs_info = sbi;
1340 sbi->clust_shift = ilog2(ntfs.sec_per_clust);
1341 sbi->clust_byte_shift = sbi->clust_shift + SECTOR_SHIFT(fs);
1342 sbi->clust_mask = ntfs.sec_per_clust - 1;
1343 sbi->clust_size = ntfs.sec_per_clust << SECTOR_SHIFT(fs);
1344 sbi->mft_record_size = 1 << mft_record_shift;
1345 sbi->clust_per_idx_record = ntfs.clust_per_idx_record;
1347 BLOCK_SHIFT(fs) = ilog2(ntfs.clust_per_idx_record) + sbi->clust_byte_shift;
1348 BLOCK_SIZE(fs) = 1 << BLOCK_SHIFT(fs);
1350 sbi->mft_lcn = ntfs.mft_lclust;
1351 sbi->mft_blk = ntfs.mft_lclust << sbi->clust_shift << SECTOR_SHIFT(fs) >>
1352 BLOCK_SHIFT(fs);
1353 /* 16 MFT entries reserved for metadata files (approximately 16 KiB) */
1354 sbi->mft_size = mft_record_shift << sbi->clust_shift << 4;
1356 sbi->clusters = ntfs.total_sectors << SECTOR_SHIFT(fs) >> sbi->clust_shift;
1357 if (sbi->clusters > 0xFFFFFFFFFFF4ULL)
1358 sbi->clusters = 0xFFFFFFFFFFF4ULL;
1361 * Assume NTFS version 3.0 to begin with. If we find that the
1362 * volume is a different version later on, we will adjust at
1363 * that time.
1365 sbi->major_ver = 3;
1366 sbi->minor_ver = 0;
1367 sbi->mft_record_lookup = ntfs_mft_record_lookup_3_0;
1369 /* Initialize the cache */
1370 cache_init(fs->fs_dev, BLOCK_SHIFT(fs));
1372 return BLOCK_SHIFT(fs);
1375 const struct fs_ops ntfs_fs_ops = {
1376 .fs_name = "ntfs",
1377 .fs_flags = FS_USEMEM | FS_THISIND,
1378 .fs_init = ntfs_fs_init,
1379 .searchdir = NULL,
1380 .getfssec = ntfs_getfssec,
1381 .close_file = generic_close_file,
1382 .mangle_name = generic_mangle_name,
1383 .open_config = generic_open_config,
1384 .readdir = ntfs_readdir,
1385 .iget_root = ntfs_iget_root,
1386 .iget = ntfs_iget,
1387 .next_extent = ntfs_next_extent,