2 * Copyright (C) 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 */
25 #include <sys/dirent.h>
31 #include <klibc/compiler.h>
38 /* Check if there are specific zero fields in an NTFS boot sector */
39 static inline int ntfs_check_zero_fields(const struct ntfs_bpb *sb)
41 return !sb->res_sectors && (!sb->zero_0[0] && !sb->zero_0[1] &&
42 !sb->zero_0[2]) && !sb->zero_1 && !sb->zero_2 &&
46 static inline int ntfs_check_sb_fields(const struct ntfs_bpb *sb)
48 return ntfs_check_zero_fields(sb) &&
49 (!memcmp(sb->oem_name, "NTFS ", 8) ||
50 !memcmp(sb->oem_name, "MSWIN4.0", 8) ||
51 !memcmp(sb->oem_name, "MSWIN4.1", 8));
54 static inline struct inode *new_ntfs_inode(struct fs_info *fs)
58 inode = alloc_inode(fs, 0, sizeof(struct ntfs_inode));
60 malloc_error("inode structure");
65 static void ntfs_fixups_writeback(struct fs_info *fs, NTFS_RECORD *nrec)
72 if (nrec->magic != NTFS_MAGIC_FILE && nrec->magic != NTFS_MAGIC_INDX)
75 /* get the Update Sequence Array offset */
76 usa = (uint16_t *)((uint8_t *)nrec + nrec->usa_ofs);
77 /* get the Update Sequence Array Number and skip it */
79 /* get the Update Sequene Array count */
80 usa_count = nrec->usa_count - 1; /* exclude the USA number */
81 /* make it to point to the last two bytes of the RECORD's first sector */
82 blk = (uint16_t *)((uint8_t *)nrec + SECTOR_SIZE(fs) - 2);
89 blk = (uint16_t *)((uint8_t *)blk + SECTOR_SIZE(fs));
93 /* read content from cache */
94 static int ntfs_read(struct fs_info *fs, void *buf, size_t len, uint64_t count,
95 block_t *blk, uint64_t *blk_offset,
96 uint64_t *blk_next_offset, uint64_t *lcn)
99 uint64_t offset = *blk_offset;
100 const uint32_t clust_byte_shift = NTFS_SB(fs)->clust_byte_shift;
101 const uint64_t blk_size = UINT64_C(1) << BLOCK_SHIFT(fs);
110 data = (uint8_t *)get_cache(fs->fs_dev, *blk);
115 offset = (*lcn << clust_byte_shift) % blk_size;
117 dprintf("LCN: 0x%X\n", *lcn);
118 dprintf("offset: 0x%X\n", offset);
120 bytes = count; /* bytes to copy */
121 lbytes = blk_size - offset; /* bytes left to copy */
122 if (lbytes >= bytes) {
123 /* so there's room enough, then copy the whole content */
124 memcpy(buf, data + offset, bytes);
128 dprintf("bytes: %u\n", bytes);
129 dprintf("bytes left: %u\n", lbytes);
130 /* otherwise, let's copy it partially... */
133 memcpy(buf + k, data + offset, lbytes);
138 if (offset >= blk_size) {
139 /* then fetch a new FS block */
140 data = (uint8_t *)get_cache(fs->fs_dev, ++*blk);
151 if (loffset >= blk_size)
152 loffset = 0; /* it must be aligned on a block boundary */
154 *blk_offset = loffset;
157 *blk_next_offset = offset;
159 *lcn += blk_size / count; /* update LCN */
167 static MFT_RECORD *ntfs_mft_record_lookup(struct fs_info *fs, uint32_t file, block_t *blk)
169 const uint64_t mft_record_size = NTFS_SB(fs)->mft_record_size;
171 const block_t mft_blk = NTFS_SB(fs)->mft_blk;
175 uint64_t next_offset;
176 const unsigned mft_record_shift = ilog2(mft_record_size);
177 const unsigned clust_byte_shift = NTFS_SB(fs)->clust_byte_shift;
178 uint64_t lcn = NTFS_SB(fs)->mft_lcn;
182 buf = (uint8_t *)malloc(mft_record_size);
184 malloc_error("uint8_t *");
186 /* determine MFT record's LCN and block number */
187 lcn = NTFS_SB(fs)->mft_lcn + (file << mft_record_shift >> clust_byte_shift);
188 cur_blk = (lcn << clust_byte_shift >> BLOCK_SHIFT(fs)) - mft_blk - 1;
190 right_blk = cur_blk + mft_blk;
191 err = ntfs_read(fs, buf, mft_record_size, mft_record_size, &right_blk,
192 &offset, &next_offset, &lcn);
194 printf("Error on reading from cache.\n");
198 ntfs_fixups_writeback(fs, (NTFS_RECORD *)buf);
200 mrec = (MFT_RECORD *)buf;
201 if (mrec->mft_record_no == file) {
203 *blk = cur_blk; /* update record starting block */
205 return mrec; /* found MFT record */
208 if (next_offset >= BLOCK_SIZE(fs)) {
209 /* try the next FS block */
211 cur_blk = right_blk - mft_blk + 1;
213 /* there's still content to fetch in the current block */
214 cur_blk = right_blk - mft_blk;
215 offset = next_offset; /* update FS block offset */
224 static ATTR_RECORD *ntfs_attr_lookup(uint32_t type, const MFT_RECORD *mrec)
229 if (!mrec || type == NTFS_AT_END)
232 attr = (ATTR_RECORD *)((uint8_t *)mrec + mrec->attrs_offset);
233 /* walk through the file attribute records */
234 for (;; attr = (ATTR_RECORD *)((uint8_t *)attr + attr->len)) {
235 if (attr->type == NTFS_AT_END)
238 if (attr->type == type)
245 static bool ntfs_filename_cmp(const char *dname, INDEX_ENTRY *ie)
247 const uint16_t *entry_fn;
248 uint8_t entry_fn_len;
251 entry_fn = ie->key.file_name.file_name;
252 entry_fn_len = ie->key.file_name.file_name_len;
254 if (strlen(dname) != entry_fn_len)
257 /* Do case-sensitive compares for Posix file names */
258 if (ie->key.file_name.file_name_type == FILE_NAME_POSIX) {
259 for (i = 0; i < entry_fn_len; i++)
260 if (entry_fn[i] != dname[i])
263 for (i = 0; i < entry_fn_len; i++)
264 if (tolower(entry_fn[i]) != tolower(dname[i]))
271 static inline uint8_t *mapping_chunk_init(ATTR_RECORD *attr,
272 struct mapping_chunk *chunk,
275 memset(chunk, 0, sizeof *chunk);
278 return (uint8_t *)attr + attr->data.non_resident.mapping_pairs_offset;
283 * return 0 on success or -1 on failure.
285 static int parse_data_run(const void *stream, uint32_t *offset,
286 uint8_t *attr_len, struct mapping_chunk *chunk)
288 uint8_t *buf; /* Pointer to the zero-terminated byte stream */
289 uint8_t count; /* The count byte */
290 uint8_t v, l; /* v is the number of changed low-order VCN bytes;
291 * l is the number of changed low-order LCN bytes
301 chunk->flags &= ~MAP_MASK;
303 buf = (uint8_t *)stream + *offset;
304 if (buf > attr_len || !*buf) {
305 chunk->flags |= MAP_END; /* we're done */
310 chunk->flags |= MAP_START; /* initial chunk */
316 if (v > 8 || l > 8) /* more than 8 bytes ? */
319 byte = (uint8_t *)buf + v;
325 mask = val >> (byte_shift - 1);
326 res = (res << byte_shift) | ((val + mask) ^ mask);
329 chunk->len = res; /* get length data */
331 byte = (uint8_t *)buf + v + l;
337 res |= (int64_t)mask; /* sign-extend it */
340 res = (res << byte_shift) | *byte--;
343 /* are VCNS from cur_vcn to next_vcn - 1 unallocated ? */
345 chunk->flags |= MAP_UNALLOCATED;
347 chunk->flags |= MAP_ALLOCATED;
349 *offset += v + l + 1;
357 static inline enum dirent_type get_inode_mode(MFT_RECORD *mrec)
359 return mrec->flags & MFT_RECORD_IS_DIRECTORY ? DT_DIR : DT_REG;
362 static int index_inode_setup(struct fs_info *fs, unsigned long mft_no,
365 uint64_t start_blk = 0;
368 enum dirent_type d_type;
373 struct mapping_chunk chunk;
378 mrec = ntfs_mft_record_lookup(fs, mft_no, &start_blk);
380 printf("No MFT record found.\n");
384 NTFS_PVT(inode)->mft_no = mft_no;
385 NTFS_PVT(inode)->seq_no = mrec->seq_no;
387 NTFS_PVT(inode)->start_cluster = start_blk >> NTFS_SB(fs)->clust_shift;
388 NTFS_PVT(inode)->here = start_blk;
390 d_type = get_inode_mode(mrec);
391 if (d_type == DT_DIR) { /* directory stuff */
392 dprintf("Got a directory.\n");
393 attr = ntfs_attr_lookup(NTFS_AT_INDEX_ROOT, mrec);
395 printf("No attribute found.\n");
399 /* note: INDEX_ROOT is always resident */
400 ir = (INDEX_ROOT *)((uint8_t *)attr +
401 attr->data.resident.value_offset);
402 len = attr->data.resident.value_len;
403 if ((uint8_t *)ir + len > (uint8_t *)mrec +
404 NTFS_SB(fs)->mft_record_size) {
405 dprintf("Corrupt index\n");
409 NTFS_PVT(inode)->itype.index.collation_rule = ir->collation_rule;
410 NTFS_PVT(inode)->itype.index.blk_size = ir->index_block_size;
411 NTFS_PVT(inode)->itype.index.blk_size_shift =
412 ilog2(NTFS_PVT(inode)->itype.index.blk_size);
414 /* determine the size of a vcn in the index */
415 clust_size = NTFS_PVT(inode)->itype.index.blk_size;
416 if (NTFS_SB(fs)->clust_size <= clust_size) {
417 NTFS_PVT(inode)->itype.index.vcn_size = NTFS_SB(fs)->clust_size;
418 NTFS_PVT(inode)->itype.index.vcn_size_shift =
419 NTFS_SB(fs)->clust_shift;
421 NTFS_PVT(inode)->itype.index.vcn_size = BLOCK_SIZE(fs);
422 NTFS_PVT(inode)->itype.index.vcn_size_shift = BLOCK_SHIFT(fs);
425 NTFS_PVT(inode)->in_idx_root = true;
426 } else if (d_type == DT_REG) { /* file stuff */
427 dprintf("Got a file.\n");
428 attr = ntfs_attr_lookup(NTFS_AT_DATA, mrec);
430 printf("No attribute found.\n");
434 NTFS_PVT(inode)->non_resident = attr->non_resident;
435 NTFS_PVT(inode)->type = attr->type;
437 if (!attr->non_resident) {
438 NTFS_PVT(inode)->data.resident.offset =
439 (uint32_t)((uint8_t *)attr + attr->data.resident.value_offset);
440 inode->size = attr->data.resident.value_len;
442 attr_len = (uint8_t *)attr + attr->len;
444 stream = mapping_chunk_init(attr, &chunk, &offset);
445 NTFS_PVT(inode)->data.non_resident.rlist = NULL;
447 err = parse_data_run(stream, &offset, attr_len, &chunk);
449 printf("parse_data_run()\n");
453 if (chunk.flags & MAP_UNALLOCATED)
455 if (chunk.flags & MAP_END)
457 if (chunk.flags & MAP_ALLOCATED) {
458 /* append new run to the runlist */
459 runlist_append(&NTFS_PVT(inode)->data.non_resident.rlist,
460 (struct runlist_element *)&chunk);
461 /* update for next VCN */
462 chunk.vcn += chunk.len;
466 if (runlist_is_empty(NTFS_PVT(inode)->data.non_resident.rlist)) {
467 printf("No mapping found\n");
471 inode->size = attr->data.non_resident.initialized_size;
475 inode->mode = d_type;
487 static struct inode *ntfs_index_lookup(const char *dname, struct inode *dir)
489 struct fs_info *fs = dir->fs;
497 const uint64_t blk_size = UINT64_C(1) << BLOCK_SHIFT(fs);
498 uint8_t buf[blk_size];
503 struct mapping_chunk chunk;
510 dprintf("ntfs_index_lookup() - mft record number: %d\n",
511 NTFS_PVT(dir)->mft_no);
512 mrec = ntfs_mft_record_lookup(fs, NTFS_PVT(dir)->mft_no, NULL);
514 printf("No MFT record found.\n");
518 attr = ntfs_attr_lookup(NTFS_AT_INDEX_ROOT, mrec);
520 printf("No attribute found.\n");
524 ir = (INDEX_ROOT *)((uint8_t *)attr +
525 attr->data.resident.value_offset);
526 len = attr->data.resident.value_len;
528 if ((uint8_t *)ir + len > (uint8_t *)mrec + NTFS_SB(fs)->mft_record_size)
531 ie = (INDEX_ENTRY *)((uint8_t *)&ir->index +
532 ir->index.entries_offset);
533 for (;; ie = (INDEX_ENTRY *)((uint8_t *)ie + ie->len)) {
535 if ((uint8_t *)ie < (uint8_t *)mrec ||
536 (uint8_t *)ie + sizeof(INDEX_ENTRY_HEADER) >
537 (uint8_t *)&ir->index + ir->index.index_len ||
538 (uint8_t *)ie + ie->len >
539 (uint8_t *)&ir->index + ir->index.index_len)
542 /* last entry cannot contain a key. it can however contain
543 * a pointer to a child node in the B+ tree so we just break out
545 dprintf("(0) ie->flags: 0x%X\n", ie->flags);
546 if (ie->flags & INDEX_ENTRY_END)
549 if (ntfs_filename_cmp(dname, ie))
553 /* check for the presence of a child node */
554 if (!(ie->flags & INDEX_ENTRY_NODE)) {
555 printf("No child node, aborting...\n");
559 /* then descend into child node */
561 attr = ntfs_attr_lookup(NTFS_AT_INDEX_ALLOCATION, mrec);
563 printf("No attribute found.\n");
567 if (!attr->non_resident) {
568 printf("WTF ?! $INDEX_ALLOCATION isn't really resident.\n");
572 attr_len = (uint8_t *)attr + attr->len;
573 stream = mapping_chunk_init(attr, &chunk, &offset);
575 err = parse_data_run(stream, &offset, attr_len, &chunk);
579 if (chunk.flags & MAP_UNALLOCATED)
582 if (chunk.flags & MAP_ALLOCATED) {
583 dprintf("%d cluster(s) starting at 0x%08llX\n", chunk.len,
588 while (vcn < chunk.len) {
589 blk = (lcn + vcn) << NTFS_SB(fs)->clust_shift <<
590 SECTOR_SHIFT(fs) >> BLOCK_SHIFT(fs);
595 err = ntfs_read(fs, &buf, blk_size, blk_size, &blk,
596 &blk_offset, NULL, (uint64_t *)&lcn);
598 printf("Error on reading from cache.\n");
602 ntfs_fixups_writeback(fs, (NTFS_RECORD *)&buf);
604 iblk = (INDEX_BLOCK *)&buf;
605 if (iblk->magic != NTFS_MAGIC_INDX) {
606 printf("Not a valid INDX record.\n");
610 ie = (INDEX_ENTRY *)((uint8_t *)&iblk->index +
611 iblk->index.entries_offset);
612 for (;; ie = (INDEX_ENTRY *)((uint8_t *)ie + ie->len)) {
614 if ((uint8_t *)ie < (uint8_t *)iblk || (uint8_t *)ie +
615 sizeof(INDEX_ENTRY_HEADER) >
616 (uint8_t *)&iblk->index + iblk->index.index_len ||
617 (uint8_t *)ie + ie->len >
618 (uint8_t *)&iblk->index + iblk->index.index_len)
621 /* last entry cannot contain a key */
622 if (ie->flags & INDEX_ENTRY_END)
625 /* Do case-sensitive compares for Posix file names */
626 if (ie->key.file_name.file_name_type == FILE_NAME_POSIX) {
627 if (ie->key.file_name.file_name[0] > *dname)
630 if (tolower(ie->key.file_name.file_name[0]) >
635 if (ntfs_filename_cmp(dname, ie))
639 lcn = last_lcn; /* restore the original LCN */
640 /* go to the next VCN */
641 vcn += (blk_size / (1 << NTFS_SB(fs)->clust_byte_shift));
644 } while (!(chunk.flags & MAP_END));
647 dprintf("Index not found\n");
650 dprintf("%s not found!\n", dname);
657 dprintf("Index found\n");
658 inode = new_ntfs_inode(fs);
659 err = index_inode_setup(fs, ie->data.dir.indexed_file, inode);
661 printf("Error in index_inode_setup()\n");
666 dprintf("%s found!\n", dname);
673 printf("Corrupt index. Aborting lookup...\n");
677 /* Convert an UTF-16LE LFN to OEM LFN */
678 static uint8_t ntfs_cvt_filename(char *filename, INDEX_ENTRY *ie)
680 const uint16_t *entry_fn;
681 uint8_t entry_fn_len;
684 entry_fn = ie->key.file_name.file_name;
685 entry_fn_len = ie->key.file_name.file_name_len;
687 for (i = 0; i < entry_fn_len; i++)
688 filename[i] = (char)entry_fn[i];
695 static int ntfs_next_extent(struct inode *inode, uint32_t lstart)
697 struct fs_info *fs = inode->fs;
698 struct ntfs_sb_info *sbi = NTFS_SB(fs);
700 struct runlist *rlist;
702 const uint32_t sec_size = SECTOR_SIZE(fs);
703 const uint32_t sec_shift = SECTOR_SHIFT(fs);
705 if (!NTFS_PVT(inode)->non_resident) {
706 pstart = (sbi->mft_blk + NTFS_PVT(inode)->here) << BLOCK_SHIFT(fs) >>
708 inode->next_extent.len = (inode->size + sec_size - 1) >> sec_shift;
710 rlist = NTFS_PVT(inode)->data.non_resident.rlist;
712 if (!lstart || lstart >= NTFS_PVT(inode)->here) {
713 if (runlist_is_empty(rlist))
714 goto out; /* nothing to do ;-) */
716 ret = runlist_remove(&rlist);
718 NTFS_PVT(inode)->here =
719 ((ret->run.len << sbi->clust_byte_shift) >> sec_shift);
721 pstart = ret->run.lcn << sbi->clust_shift;
722 inode->next_extent.len =
723 ((ret->run.len << sbi->clust_byte_shift) + sec_size - 1) >>
726 NTFS_PVT(inode)->data.non_resident.rlist = rlist;
733 inode->next_extent.pstart = pstart;
741 static uint32_t ntfs_getfssec(struct file *file, char *buf, int sectors,
744 uint8_t non_resident;
746 struct fs_info *fs = file->fs;
747 struct inode *inode = file->inode;
752 non_resident = NTFS_PVT(inode)->non_resident;
754 ret = generic_getfssec(file, buf, sectors, have_more);
759 mrec = ntfs_mft_record_lookup(fs, NTFS_PVT(inode)->mft_no, NULL);
761 printf("No MFT record found.\n");
765 attr = ntfs_attr_lookup(NTFS_AT_DATA, mrec);
767 printf("No attribute found.\n");
771 p = (char *)((uint8_t *)attr + attr->data.resident.value_offset);
773 /* p now points to the data offset, so let's copy it into buf */
774 memcpy(buf, p, inode->size);
789 static inline bool is_filename_printable(const char *s)
791 return s && (*s != '.' && *s != '$');
794 static int ntfs_readdir(struct file *file, struct dirent *dirent)
796 struct fs_info *fs = file->fs;
797 struct inode *inode = file->inode;
801 const uint64_t blk_size = UINT64_C(1) << BLOCK_SHIFT(fs);
806 INDEX_ENTRY *ie = NULL;
807 uint8_t buf[BLOCK_SIZE(fs)];
812 struct mapping_chunk chunk;
816 char filename[NTFS_MAX_FILE_NAME_LEN + 1];
818 mrec = ntfs_mft_record_lookup(fs, NTFS_PVT(inode)->mft_no, NULL);
820 printf("No MFT record found.\n");
824 attr = ntfs_attr_lookup(NTFS_AT_INDEX_ROOT, mrec);
826 printf("No attribute found.\n");
830 ir = (INDEX_ROOT *)((uint8_t *)attr +
831 attr->data.resident.value_offset);
832 len = attr->data.resident.value_len;
834 if ((uint8_t *)ir + len > (uint8_t *)mrec + NTFS_SB(fs)->mft_record_size)
837 if (!file->offset && NTFS_PVT(inode)->in_idx_root) {
838 file->offset = (uint32_t)((uint8_t *)&ir->index +
839 ir->index.entries_offset);
843 if (NTFS_PVT(inode)->in_idx_root) {
844 ie = (INDEX_ENTRY *)(uint8_t *)file->offset;
845 if (ie->flags & INDEX_ENTRY_END) {
847 NTFS_PVT(inode)->in_idx_root = false;
848 NTFS_PVT(inode)->idx_blks_count = 1;
849 NTFS_PVT(inode)->entries_count = 0;
850 NTFS_PVT(inode)->last_vcn = 0;
851 goto descend_into_child_node;
854 file->offset = (uint32_t)((uint8_t *)ie + ie->len);
855 len = ntfs_cvt_filename(filename, ie);
856 if (!is_filename_printable(filename))
857 goto idx_root_next_entry;
862 descend_into_child_node:
863 if (!(ie->flags & INDEX_ENTRY_NODE))
866 attr = ntfs_attr_lookup(NTFS_AT_INDEX_ALLOCATION, mrec);
870 if (!attr->non_resident) {
871 printf("WTF ?! $INDEX_ALLOCATION isn't really resident.\n");
875 attr_len = (uint8_t *)attr + attr->len;
878 stream = mapping_chunk_init(attr, &chunk, &offset);
879 count = NTFS_PVT(inode)->idx_blks_count;
881 err = parse_data_run(stream, &offset, attr_len, &chunk);
883 printf("Error on parsing data runs.\n");
887 if (chunk.flags & MAP_UNALLOCATED)
889 if (chunk.flags & MAP_END)
893 if (chunk.flags & MAP_UNALLOCATED) {
894 NTFS_PVT(inode)->idx_blks_count++;
899 vcn = NTFS_PVT(inode)->last_vcn;
900 if (vcn >= chunk.len) {
901 NTFS_PVT(inode)->last_vcn = 0;
902 NTFS_PVT(inode)->idx_blks_count++;
907 blk = (lcn + vcn) << NTFS_SB(fs)->clust_shift << SECTOR_SHIFT(fs) >>
911 err = ntfs_read(fs, &buf, blk_size, blk_size, &blk, &blk_offset, NULL,
914 printf("Error on reading from cache.\n");
918 ntfs_fixups_writeback(fs, (NTFS_RECORD *)&buf);
920 iblk = (INDEX_BLOCK *)&buf;
921 if (iblk->magic != NTFS_MAGIC_INDX) {
922 printf("Not a valid INDX record.\n");
926 idx_block_next_entry:
927 ie = (INDEX_ENTRY *)((uint8_t *)&iblk->index +
928 iblk->index.entries_offset);
929 count = NTFS_PVT(inode)->entries_count;
930 for ( ; count--; ie = (INDEX_ENTRY *)((uint8_t *)ie + ie->len)) {
932 if ((uint8_t *)ie < (uint8_t *)iblk || (uint8_t *)ie +
933 sizeof(INDEX_ENTRY_HEADER) >
934 (uint8_t *)&iblk->index + iblk->index.index_len ||
935 (uint8_t *)ie + ie->len >
936 (uint8_t *)&iblk->index + iblk->index.index_len)
939 /* last entry cannot contain a key */
940 if (ie->flags & INDEX_ENTRY_END) {
941 /* go to the next VCN */
942 NTFS_PVT(inode)->last_vcn += (blk_size / (1 <<
943 NTFS_SB(fs)->clust_byte_shift));
944 NTFS_PVT(inode)->entries_count = 0;
949 NTFS_PVT(inode)->entries_count++;
950 len = ntfs_cvt_filename(filename, ie);
951 if (!is_filename_printable(filename))
952 goto idx_block_next_entry;
957 NTFS_PVT(inode)->in_idx_root = true;
964 dirent->d_ino = ie->data.dir.indexed_file;
965 dirent->d_off = file->offset;
966 dirent->d_reclen = offsetof(struct dirent, d_name) + len + 1;
970 mrec = ntfs_mft_record_lookup(fs, ie->data.dir.indexed_file, NULL);
972 printf("No MFT record found.\n");
976 dirent->d_type = get_inode_mode(mrec);
977 memcpy(dirent->d_name, filename, len + 1);
984 printf("Index not found\n");
988 printf("Corrupt index. Aborting lookup...\n");
992 static struct inode *ntfs_iget(const char *dname, struct inode *parent)
994 return ntfs_index_lookup(dname, parent);
997 static struct inode *ntfs_iget_root(struct fs_info *fs)
999 struct inode *inode = new_ntfs_inode(fs);
1004 err = index_inode_setup(fs, FILE_root, inode);
1008 NTFS_PVT(inode)->start = NTFS_PVT(inode)->here;
1018 /* Initialize the filesystem metadata and return blk size in bits */
1019 static int ntfs_fs_init(struct fs_info *fs)
1021 struct ntfs_bpb ntfs;
1022 struct ntfs_sb_info *sbi;
1023 struct disk *disk = fs->fs_dev->disk;
1024 uint8_t mft_record_shift;
1026 disk->rdwr_sectors(disk, &ntfs, 0, 1, 0);
1029 if (!ntfs_check_sb_fields(&ntfs))
1032 SECTOR_SHIFT(fs) = disk->sector_shift;
1034 /* Note: ntfs.clust_per_mft_record can be a negative number.
1035 * If negative, it represents a shift count, else it represents
1036 * a multiplier for the cluster size.
1038 mft_record_shift = ntfs.clust_per_mft_record < 0 ?
1039 -ntfs.clust_per_mft_record :
1040 ilog2(ntfs.sec_per_clust) + SECTOR_SHIFT(fs) +
1041 ilog2(ntfs.clust_per_mft_record);
1043 SECTOR_SIZE(fs) = 1 << SECTOR_SHIFT(fs);
1045 sbi = malloc(sizeof *sbi);
1047 malloc_error("ntfs_sb_info structure");
1051 sbi->clust_shift = ilog2(ntfs.sec_per_clust);
1052 sbi->clust_byte_shift = sbi->clust_shift + SECTOR_SHIFT(fs);
1053 sbi->clust_mask = ntfs.sec_per_clust - 1;
1054 sbi->clust_size = ntfs.sec_per_clust << SECTOR_SHIFT(fs);
1055 sbi->mft_record_size = 1 << mft_record_shift;
1056 sbi->clust_per_idx_record = ntfs.clust_per_idx_record;
1058 BLOCK_SHIFT(fs) = ilog2(ntfs.clust_per_idx_record) + sbi->clust_byte_shift;
1059 BLOCK_SIZE(fs) = 1 << BLOCK_SHIFT(fs);
1061 sbi->mft_lcn = ntfs.mft_lclust;
1062 sbi->mft_blk = ntfs.mft_lclust << sbi->clust_shift <<
1063 SECTOR_SHIFT(fs) >> BLOCK_SHIFT(fs);
1064 /* 16 MFT entries reserved for metadata files (approximately 16 KiB) */
1065 sbi->mft_size = mft_record_shift << sbi->clust_shift << 4;
1067 sbi->clusters = ntfs.total_sectors << SECTOR_SHIFT(fs) >> sbi->clust_shift;
1068 if (sbi->clusters > 0xFFFFFFFFFFF4ULL)
1069 sbi->clusters = 0xFFFFFFFFFFF4ULL;
1071 /* Initialize the cache */
1072 cache_init(fs->fs_dev, BLOCK_SHIFT(fs));
1074 return BLOCK_SHIFT(fs);
1077 const struct fs_ops ntfs_fs_ops = {
1079 .fs_flags = FS_USEMEM | FS_THISIND,
1080 .fs_init = ntfs_fs_init,
1082 .getfssec = ntfs_getfssec,
1083 .close_file = generic_close_file,
1084 .mangle_name = generic_mangle_name,
1085 .load_config = generic_load_config,
1086 .readdir = ntfs_readdir,
1087 .iget_root = ntfs_iget_root,
1089 .next_extent = ntfs_next_extent,