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 */
25 #include <sys/dirent.h>
31 #include <klibc/compiler.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
&&
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
)
66 inode
= alloc_inode(fs
, 0, sizeof(struct ntfs_inode
));
68 malloc_error("inode structure");
73 static void ntfs_fixups_writeback(struct fs_info
*fs
, struct ntfs_record
*nrec
)
80 dprintf("in %s()\n", __func__
);
82 if (nrec
->magic
!= NTFS_MAGIC_FILE
&& nrec
->magic
!= NTFS_MAGIC_INDX
)
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 */
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);
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
)
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
);
117 dprintf("in %s()\n", __func__
);
122 data
= (uint8_t *)get_cache(fs
->fs_dev
, *blk
);
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
);
140 dprintf("bytes: %u\n", bytes
);
141 dprintf("bytes left: %u\n", lbytes
);
142 /* otherwise, let's copy it partially... */
145 memcpy(buf
+ k
, data
+ offset
, lbytes
);
150 if (offset
>= blk_size
) {
151 /* then fetch a new FS block */
152 data
= (uint8_t *)get_cache(fs
->fs_dev
, ++*blk
);
163 if (loffset
>= blk_size
)
164 loffset
= 0; /* it must be aligned on a block boundary */
166 *blk_offset
= loffset
;
169 *blk_next_offset
= offset
;
171 *lcn
+= blk_size
/ count
; /* update LCN */
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
;
184 const block_t mft_blk
= NTFS_SB(fs
)->mft_blk
;
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
;
193 struct ntfs_mft_record
*mrec
;
195 dprintf("in %s()\n", __func__
);
197 buf
= (uint8_t *)malloc(mft_record_size
);
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
);
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
);
210 printf("Error while reading from cache.\n");
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
) {
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 */
228 cur_blk
= right_blk
- mft_blk
+ 1;
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 */
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
;
246 const block_t mft_blk
= NTFS_SB(fs
)->mft_blk
;
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
;
255 struct ntfs_mft_record
*mrec
;
257 dprintf("in %s()\n", __func__
);
259 buf
= (uint8_t *)malloc(mft_record_size
);
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
);
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
);
271 printf("Error while reading from cache.\n");
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
) {
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 */
289 cur_blk
= right_blk
- NTFS_SB(fs
)->mft_blk
+ 1;
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 */
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
;
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
)
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
])
322 for (i
= 0; i
< entry_fn_len
; i
++)
323 if (tolower(entry_fn
[i
]) != tolower(dname
[i
]))
330 static inline uint8_t *mapping_chunk_init(struct ntfs_attr_record
*attr
,
331 struct mapping_chunk
*chunk
,
334 memset(chunk
, 0, sizeof *chunk
);
337 return (uint8_t *)attr
+ attr
->data
.non_resident
.mapping_pairs_offset
;
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
353 const int byte_shift
= 8;
359 dprintf("in %s()\n", __func__
);
361 chunk
->flags
&= ~MAP_MASK
;
363 buf
= (uint8_t *)stream
+ *offset
;
364 if (buf
> attr_len
|| !*buf
) {
365 chunk
->flags
|= MAP_END
; /* we're done */
370 chunk
->flags
|= MAP_START
; /* initial chunk */
376 if (v
> 8 || l
> 8) /* more than 8 bytes ? */
379 byte
= (uint8_t *)buf
+ v
;
384 res
= (res
<< byte_shift
) | *byte
--;
386 chunk
->len
= res
; /* get length data */
388 byte
= (uint8_t *)buf
+ v
+ l
;
394 res
|= (int64_t)mask
; /* sign-extend it */
397 res
= (res
<< byte_shift
) | *byte
--;
400 /* are VCNS from cur_vcn to next_vcn - 1 unallocated ? */
402 chunk
->flags
|= MAP_UNALLOCATED
;
404 chunk
->flags
|= MAP_ALLOCATED
;
406 *offset
+= v
+ l
+ 1;
414 static struct ntfs_mft_record
*
415 ntfs_attr_list_lookup(struct fs_info
*fs
, struct ntfs_attr_record
*attr
,
416 uint32_t type
, struct ntfs_mft_record
*mrec
)
419 struct mapping_chunk chunk
;
423 const uint64_t blk_size
= UINT64_C(1) << BLOCK_SHIFT(fs
);
424 uint8_t buf
[blk_size
];
430 struct ntfs_attr_list_entry
*attr_entry
;
432 struct ntfs_mft_record
*retval
;
433 uint64_t start_blk
= 0;
435 dprintf("in %s()\n", __func__
);
437 if (attr
->non_resident
)
438 goto handle_non_resident_attr
;
440 attr_entry
= (struct ntfs_attr_list_entry
*)
441 ((uint8_t *)attr
+ attr
->data
.resident
.value_offset
);
442 len
= attr
->data
.resident
.value_len
;
443 for (; (uint8_t *)attr_entry
< (uint8_t *)attr
+ len
;
444 attr_entry
= (struct ntfs_attr_list_entry
*)((uint8_t *)attr_entry
+
445 attr_entry
->length
)) {
446 dprintf("<$ATTRIBUTE_LIST> Attribute type: 0x%X\n",
448 if (attr_entry
->type
== type
)
449 goto found
; /* We got the attribute! :-) */
452 printf("No attribute found.\n");
455 handle_non_resident_attr
:
456 attr_len
= (uint8_t *)attr
+ attr
->len
;
457 stream
= mapping_chunk_init(attr
, &chunk
, &offset
);
459 err
= parse_data_run(stream
, &offset
, attr_len
, &chunk
);
461 printf("parse_data_run()\n");
465 if (chunk
.flags
& MAP_UNALLOCATED
)
467 if (chunk
.flags
& MAP_END
)
469 if (chunk
.flags
& MAP_ALLOCATED
) {
472 while (vcn
< chunk
.len
) {
473 blk
= (lcn
+ vcn
) << NTFS_SB(fs
)->clust_byte_shift
>>
478 err
= ntfs_read(fs
, buf
, blk_size
, blk_size
, &blk
,
479 &blk_offset
, NULL
, (uint64_t *)&lcn
);
481 printf("Error while reading from cache.\n");
485 attr_entry
= (struct ntfs_attr_list_entry
*)&buf
;
486 len
= attr
->data
.non_resident
.data_size
;
487 for (; (uint8_t *)attr_entry
< (uint8_t *)&buf
[0] + len
;
488 attr_entry
= (struct ntfs_attr_list_entry
*)
489 ((uint8_t *)attr_entry
+ attr_entry
->length
)) {
490 dprintf("<$ATTRIBUTE_LIST> Attribute type: 0x%x\n",
492 if (attr_entry
->type
== type
)
493 goto found
; /* We got the attribute! :-) */
496 lcn
= last_lcn
; /* restore original LCN */
497 /* go to the next VCN */
498 vcn
+= (blk_size
/ (1 << NTFS_SB(fs
)->clust_byte_shift
));
501 } while (!(chunk
.flags
& MAP_END
));
503 printf("No attribute found.\n");
509 /* At this point we have the attribute we were looking for. Now we
510 * will look for the MFT record that stores information about this
514 /* Check if the attribute type we're looking for is in the same
515 * MFT record. If so, we do not need to look it up again - return it.
517 if (mrec
->mft_record_no
== attr_entry
->mft_ref
)
520 retval
= NTFS_SB(fs
)->mft_record_lookup(fs
, attr_entry
->mft_ref
,
523 printf("No MFT record found!\n");
527 /* return the found MFT record */
531 static struct ntfs_attr_record
*
532 __ntfs_attr_lookup(struct fs_info
*fs
, uint32_t type
,
533 struct ntfs_mft_record
**mrec
)
535 struct ntfs_mft_record
*_mrec
= *mrec
;
536 struct ntfs_attr_record
*attr
;
537 struct ntfs_attr_record
*attr_list_attr
;
539 dprintf("in %s()\n", __func__
);
541 if (!_mrec
|| type
== NTFS_AT_END
)
545 attr_list_attr
= NULL
;
547 attr
= (struct ntfs_attr_record
*)((uint8_t *)_mrec
+ _mrec
->attrs_offset
);
548 /* walk through the file attribute records */
549 for (;; attr
= (struct ntfs_attr_record
*)((uint8_t *)attr
+ attr
->len
)) {
550 if (attr
->type
== NTFS_AT_END
)
553 if (attr
->type
== NTFS_AT_ATTR_LIST
) {
554 dprintf("MFT record #%lu has an $ATTRIBUTE_LIST attribute.\n",
555 _mrec
->mft_record_no
);
556 attr_list_attr
= attr
;
560 if (attr
->type
== type
)
564 /* if the record has an $ATTRIBUTE_LIST attribute associated
565 * with it, then we need to look for the wanted attribute in
568 if (attr
->type
== NTFS_AT_END
&& attr_list_attr
) {
569 struct ntfs_mft_record
*retval
;
571 retval
= ntfs_attr_list_lookup(fs
, attr_list_attr
, type
, _mrec
);
577 } else if (attr
->type
== NTFS_AT_END
&& !attr_list_attr
) {
587 static inline struct ntfs_attr_record
*
588 ntfs_attr_lookup(struct fs_info
*fs
, uint32_t type
,
589 struct ntfs_mft_record
**mmrec
,
590 struct ntfs_mft_record
*mrec
)
592 struct ntfs_mft_record
*_mrec
= mrec
;
593 struct ntfs_mft_record
*other
= *mmrec
;
594 struct ntfs_attr_record
*retval
= NULL
;
597 return __ntfs_attr_lookup(fs
, type
, &other
);
599 retval
= __ntfs_attr_lookup(fs
, type
, &_mrec
);
602 retval
= __ntfs_attr_lookup(fs
, type
, &other
);
605 } else if (retval
&& (_mrec
!= mrec
)) {
612 static inline enum dirent_type
get_inode_mode(struct ntfs_mft_record
*mrec
)
614 return mrec
->flags
& MFT_RECORD_IS_DIRECTORY
? DT_DIR
: DT_REG
;
617 static int index_inode_setup(struct fs_info
*fs
, unsigned long mft_no
,
620 uint64_t start_blk
= 0;
621 struct ntfs_mft_record
*mrec
, *lmrec
;
622 struct ntfs_attr_record
*attr
;
623 enum dirent_type d_type
;
625 struct mapping_chunk chunk
;
630 dprintf("in %s()\n", __func__
);
632 mrec
= NTFS_SB(fs
)->mft_record_lookup(fs
, mft_no
, &start_blk
);
634 printf("No MFT record found.\n");
640 NTFS_PVT(inode
)->mft_no
= mft_no
;
641 NTFS_PVT(inode
)->seq_no
= mrec
->seq_no
;
643 NTFS_PVT(inode
)->start_cluster
= start_blk
>> NTFS_SB(fs
)->clust_shift
;
644 NTFS_PVT(inode
)->here
= start_blk
;
646 d_type
= get_inode_mode(mrec
);
647 if (d_type
== DT_DIR
) { /* directory stuff */
648 dprintf("Got a directory.\n");
649 attr
= ntfs_attr_lookup(fs
, NTFS_AT_INDEX_ROOT
, &mrec
, lmrec
);
651 printf("No attribute found.\n");
655 /* check if we have a previous allocated state structure */
658 readdir_state
= NULL
;
661 /* allocate our state structure */
662 readdir_state
= malloc(sizeof *readdir_state
);
664 malloc_error("ntfs_readdir_state structure");
666 readdir_state
->mft_no
= mft_no
;
667 /* obviously, the ntfs_readdir() caller will start from INDEX root */
668 readdir_state
->in_idx_root
= true;
669 } else if (d_type
== DT_REG
) { /* file stuff */
670 dprintf("Got a file.\n");
671 attr
= ntfs_attr_lookup(fs
, NTFS_AT_DATA
, &mrec
, lmrec
);
673 printf("No attribute found.\n");
677 NTFS_PVT(inode
)->non_resident
= attr
->non_resident
;
678 NTFS_PVT(inode
)->type
= attr
->type
;
680 if (!attr
->non_resident
) {
681 NTFS_PVT(inode
)->data
.resident
.offset
=
682 (uint32_t)((uint8_t *)attr
+ attr
->data
.resident
.value_offset
);
683 inode
->size
= attr
->data
.resident
.value_len
;
685 attr_len
= (uint8_t *)attr
+ attr
->len
;
687 stream
= mapping_chunk_init(attr
, &chunk
, &offset
);
688 NTFS_PVT(inode
)->data
.non_resident
.rlist
= NULL
;
690 err
= parse_data_run(stream
, &offset
, attr_len
, &chunk
);
692 printf("parse_data_run()\n");
696 if (chunk
.flags
& MAP_UNALLOCATED
)
698 if (chunk
.flags
& MAP_END
)
700 if (chunk
.flags
& MAP_ALLOCATED
) {
701 /* append new run to the runlist */
702 runlist_append(&NTFS_PVT(inode
)->data
.non_resident
.rlist
,
703 (struct runlist_element
*)&chunk
);
704 /* update for next VCN */
705 chunk
.vcn
+= chunk
.len
;
709 if (runlist_is_empty(NTFS_PVT(inode
)->data
.non_resident
.rlist
)) {
710 printf("No mapping found\n");
714 inode
->size
= attr
->data
.non_resident
.initialized_size
;
718 inode
->mode
= d_type
;
730 static struct inode
*ntfs_index_lookup(const char *dname
, struct inode
*dir
)
732 struct fs_info
*fs
= dir
->fs
;
733 struct ntfs_mft_record
*mrec
, *lmrec
;
736 struct ntfs_attr_record
*attr
;
737 struct ntfs_idx_root
*ir
;
738 struct ntfs_idx_entry
*ie
;
739 const uint64_t blk_size
= UINT64_C(1) << BLOCK_SHIFT(fs
);
740 uint8_t buf
[blk_size
];
741 struct ntfs_idx_allocation
*iblk
;
745 struct mapping_chunk chunk
;
752 dprintf("in %s()\n", __func__
);
754 mrec
= NTFS_SB(fs
)->mft_record_lookup(fs
, NTFS_PVT(dir
)->mft_no
, NULL
);
756 printf("No MFT record found.\n");
761 attr
= ntfs_attr_lookup(fs
, NTFS_AT_INDEX_ROOT
, &mrec
, lmrec
);
763 printf("No attribute found.\n");
767 ir
= (struct ntfs_idx_root
*)((uint8_t *)attr
+
768 attr
->data
.resident
.value_offset
);
769 ie
= (struct ntfs_idx_entry
*)((uint8_t *)&ir
->index
+
770 ir
->index
.entries_offset
);
771 for (;; ie
= (struct ntfs_idx_entry
*)((uint8_t *)ie
+ ie
->len
)) {
773 if ((uint8_t *)ie
< (uint8_t *)mrec
||
774 (uint8_t *)ie
+ sizeof(struct ntfs_idx_entry_header
) >
775 (uint8_t *)&ir
->index
+ ir
->index
.index_len
||
776 (uint8_t *)ie
+ ie
->len
>
777 (uint8_t *)&ir
->index
+ ir
->index
.index_len
)
780 /* last entry cannot contain a key. it can however contain
781 * a pointer to a child node in the B+ tree so we just break out
783 if (ie
->flags
& INDEX_ENTRY_END
)
786 if (ntfs_filename_cmp(dname
, ie
))
790 /* check for the presence of a child node */
791 if (!(ie
->flags
& INDEX_ENTRY_NODE
)) {
792 printf("No child node, aborting...\n");
796 /* then descend into child node */
798 attr
= ntfs_attr_lookup(fs
, NTFS_AT_INDEX_ALLOCATION
, &mrec
, lmrec
);
800 printf("No attribute found.\n");
804 if (!attr
->non_resident
) {
805 printf("WTF ?! $INDEX_ALLOCATION isn't really resident.\n");
809 attr_len
= (uint8_t *)attr
+ attr
->len
;
810 stream
= mapping_chunk_init(attr
, &chunk
, &offset
);
812 err
= parse_data_run(stream
, &offset
, attr_len
, &chunk
);
816 if (chunk
.flags
& MAP_UNALLOCATED
)
819 if (chunk
.flags
& MAP_ALLOCATED
) {
820 dprintf("%d cluster(s) starting at 0x%08llX\n", chunk
.len
,
825 while (vcn
< chunk
.len
) {
826 blk
= (lcn
+ vcn
) << NTFS_SB(fs
)->clust_shift
<<
827 SECTOR_SHIFT(fs
) >> BLOCK_SHIFT(fs
);
832 err
= ntfs_read(fs
, &buf
, blk_size
, blk_size
, &blk
,
833 &blk_offset
, NULL
, (uint64_t *)&lcn
);
835 printf("Error while reading from cache.\n");
839 ntfs_fixups_writeback(fs
, (struct ntfs_record
*)&buf
);
841 iblk
= (struct ntfs_idx_allocation
*)&buf
;
842 if (iblk
->magic
!= NTFS_MAGIC_INDX
) {
843 printf("Not a valid INDX record.\n");
847 ie
= (struct ntfs_idx_entry
*)((uint8_t *)&iblk
->index
+
848 iblk
->index
.entries_offset
);
849 for (;; ie
= (struct ntfs_idx_entry
*)((uint8_t *)ie
+
852 if ((uint8_t *)ie
< (uint8_t *)iblk
|| (uint8_t *)ie
+
853 sizeof(struct ntfs_idx_entry_header
) >
854 (uint8_t *)&iblk
->index
+ iblk
->index
.index_len
||
855 (uint8_t *)ie
+ ie
->len
>
856 (uint8_t *)&iblk
->index
+ iblk
->index
.index_len
)
859 /* last entry cannot contain a key */
860 if (ie
->flags
& INDEX_ENTRY_END
)
863 if (ntfs_filename_cmp(dname
, ie
))
867 lcn
= last_lcn
; /* restore the original LCN */
868 /* go to the next VCN */
869 vcn
+= (blk_size
/ (1 << NTFS_SB(fs
)->clust_byte_shift
));
872 } while (!(chunk
.flags
& MAP_END
));
875 dprintf("Index not found\n");
883 dprintf("Index found\n");
884 inode
= new_ntfs_inode(fs
);
885 err
= index_inode_setup(fs
, ie
->data
.dir
.indexed_file
, inode
);
887 printf("Error in index_inode_setup()\n");
897 printf("Corrupt index. Aborting lookup...\n");
901 /* Convert an UTF-16LE LFN to OEM LFN */
902 static uint8_t ntfs_cvt_filename(char *filename
,
903 const struct ntfs_idx_entry
*ie
)
905 const uint16_t *entry_fn
;
906 uint8_t entry_fn_len
;
909 entry_fn
= ie
->key
.file_name
.file_name
;
910 entry_fn_len
= ie
->key
.file_name
.file_name_len
;
912 for (i
= 0; i
< entry_fn_len
; i
++)
913 filename
[i
] = (char)entry_fn
[i
];
920 static int ntfs_next_extent(struct inode
*inode
, uint32_t lstart
)
922 struct fs_info
*fs
= inode
->fs
;
923 struct ntfs_sb_info
*sbi
= NTFS_SB(fs
);
925 struct runlist
*rlist
;
927 const uint32_t sec_size
= SECTOR_SIZE(fs
);
928 const uint32_t sec_shift
= SECTOR_SHIFT(fs
);
930 dprintf("in %s()\n", __func__
);
932 if (!NTFS_PVT(inode
)->non_resident
) {
933 pstart
= (sbi
->mft_blk
+ NTFS_PVT(inode
)->here
) << BLOCK_SHIFT(fs
) >>
935 inode
->next_extent
.len
= (inode
->size
+ sec_size
- 1) >> sec_shift
;
937 rlist
= NTFS_PVT(inode
)->data
.non_resident
.rlist
;
939 if (!lstart
|| lstart
>= NTFS_PVT(inode
)->here
) {
940 if (runlist_is_empty(rlist
))
941 goto out
; /* nothing to do ;-) */
943 ret
= runlist_remove(&rlist
);
945 NTFS_PVT(inode
)->here
=
946 ((ret
->run
.len
<< sbi
->clust_byte_shift
) >> sec_shift
);
948 pstart
= ret
->run
.lcn
<< sbi
->clust_shift
;
949 inode
->next_extent
.len
=
950 ((ret
->run
.len
<< sbi
->clust_byte_shift
) + sec_size
- 1) >>
953 NTFS_PVT(inode
)->data
.non_resident
.rlist
= rlist
;
960 inode
->next_extent
.pstart
= pstart
;
968 static uint32_t ntfs_getfssec(struct file
*file
, char *buf
, int sectors
,
971 uint8_t non_resident
;
973 struct fs_info
*fs
= file
->fs
;
974 struct inode
*inode
= file
->inode
;
975 struct ntfs_mft_record
*mrec
, *lmrec
;
976 struct ntfs_attr_record
*attr
;
979 dprintf("in %s()\n", __func__
);
981 non_resident
= NTFS_PVT(inode
)->non_resident
;
983 ret
= generic_getfssec(file
, buf
, sectors
, have_more
);
988 mrec
= NTFS_SB(fs
)->mft_record_lookup(fs
, NTFS_PVT(inode
)->mft_no
,
991 printf("No MFT record found.\n");
996 attr
= ntfs_attr_lookup(fs
, NTFS_AT_DATA
, &mrec
, lmrec
);
998 printf("No attribute found.\n");
1002 p
= (char *)((uint8_t *)attr
+ attr
->data
.resident
.value_offset
);
1004 /* p now points to the data offset, so let's copy it into buf */
1005 memcpy(buf
, p
, inode
->size
);
1020 static inline bool is_filename_printable(const char *s
)
1022 return s
&& (*s
!= '.' && *s
!= '$');
1025 static int ntfs_readdir(struct file
*file
, struct dirent
*dirent
)
1027 struct fs_info
*fs
= file
->fs
;
1028 struct inode
*inode
= file
->inode
;
1029 struct ntfs_mft_record
*mrec
, *lmrec
;
1031 uint64_t blk_offset
;
1032 const uint64_t blk_size
= UINT64_C(1) << BLOCK_SHIFT(fs
);
1033 struct ntfs_attr_record
*attr
;
1034 struct ntfs_idx_root
*ir
;
1037 struct ntfs_idx_entry
*ie
= NULL
;
1038 uint8_t buf
[BLOCK_SIZE(fs
)];
1039 struct ntfs_idx_allocation
*iblk
;
1043 struct mapping_chunk chunk
;
1047 char filename
[NTFS_MAX_FILE_NAME_LEN
+ 1];
1049 dprintf("in %s()\n", __func__
);
1051 mrec
= NTFS_SB(fs
)->mft_record_lookup(fs
, NTFS_PVT(inode
)->mft_no
, NULL
);
1053 printf("No MFT record found.\n");
1058 attr
= ntfs_attr_lookup(fs
, NTFS_AT_INDEX_ROOT
, &mrec
, lmrec
);
1060 printf("No attribute found.\n");
1064 ir
= (struct ntfs_idx_root
*)((uint8_t *)attr
+
1065 attr
->data
.resident
.value_offset
);
1067 if (!file
->offset
&& readdir_state
->in_idx_root
) {
1068 file
->offset
= (uint32_t)((uint8_t *)&ir
->index
+
1069 ir
->index
.entries_offset
);
1072 idx_root_next_entry
:
1073 if (readdir_state
->in_idx_root
) {
1074 ie
= (struct ntfs_idx_entry
*)(uint8_t *)file
->offset
;
1075 if (ie
->flags
& INDEX_ENTRY_END
) {
1077 readdir_state
->in_idx_root
= false;
1078 readdir_state
->idx_blks_count
= 1;
1079 readdir_state
->entries_count
= 0;
1080 readdir_state
->last_vcn
= 0;
1081 goto descend_into_child_node
;
1084 file
->offset
= (uint32_t)((uint8_t *)ie
+ ie
->len
);
1085 len
= ntfs_cvt_filename(filename
, ie
);
1086 if (!is_filename_printable(filename
))
1087 goto idx_root_next_entry
;
1092 descend_into_child_node
:
1093 if (!(ie
->flags
& INDEX_ENTRY_NODE
))
1096 attr
= ntfs_attr_lookup(fs
, NTFS_AT_INDEX_ALLOCATION
, &mrec
, lmrec
);
1100 if (!attr
->non_resident
) {
1101 printf("WTF ?! $INDEX_ALLOCATION isn't really resident.\n");
1105 attr_len
= (uint8_t *)attr
+ attr
->len
;
1108 stream
= mapping_chunk_init(attr
, &chunk
, &offset
);
1109 count
= readdir_state
->idx_blks_count
;
1111 err
= parse_data_run(stream
, &offset
, attr_len
, &chunk
);
1113 printf("Error while parsing data runs.\n");
1117 if (chunk
.flags
& MAP_UNALLOCATED
)
1119 if (chunk
.flags
& MAP_END
)
1123 if (chunk
.flags
& MAP_UNALLOCATED
) {
1124 readdir_state
->idx_blks_count
++;
1129 vcn
= readdir_state
->last_vcn
;
1130 if (vcn
>= chunk
.len
) {
1131 readdir_state
->last_vcn
= 0;
1132 readdir_state
->idx_blks_count
++;
1137 blk
= (lcn
+ vcn
) << NTFS_SB(fs
)->clust_shift
<< SECTOR_SHIFT(fs
) >>
1141 err
= ntfs_read(fs
, &buf
, blk_size
, blk_size
, &blk
, &blk_offset
, NULL
,
1144 printf("Error while reading from cache.\n");
1148 ntfs_fixups_writeback(fs
, (struct ntfs_record
*)&buf
);
1150 iblk
= (struct ntfs_idx_allocation
*)&buf
;
1151 if (iblk
->magic
!= NTFS_MAGIC_INDX
) {
1152 printf("Not a valid INDX record.\n");
1156 idx_block_next_entry
:
1157 ie
= (struct ntfs_idx_entry
*)((uint8_t *)&iblk
->index
+
1158 iblk
->index
.entries_offset
);
1159 count
= readdir_state
->entries_count
;
1160 for ( ; count
--; ie
= (struct ntfs_idx_entry
*)((uint8_t *)ie
+ ie
->len
)) {
1162 if ((uint8_t *)ie
< (uint8_t *)iblk
|| (uint8_t *)ie
+
1163 sizeof(struct ntfs_idx_entry_header
) >
1164 (uint8_t *)&iblk
->index
+ iblk
->index
.index_len
||
1165 (uint8_t *)ie
+ ie
->len
>
1166 (uint8_t *)&iblk
->index
+ iblk
->index
.index_len
)
1169 /* last entry cannot contain a key */
1170 if (ie
->flags
& INDEX_ENTRY_END
) {
1171 /* go to the next VCN */
1172 readdir_state
->last_vcn
+= (blk_size
/ (1 <<
1173 NTFS_SB(fs
)->clust_byte_shift
));
1174 readdir_state
->entries_count
= 0;
1179 readdir_state
->entries_count
++;
1181 /* Need to check if this entry has INDEX_ENTRY_END flag set. If
1182 * so, then it won't contain a indexed_file file, so continue the
1183 * lookup on the next VCN/LCN (if any).
1185 if (ie
->flags
& INDEX_ENTRY_END
)
1188 len
= ntfs_cvt_filename(filename
, ie
);
1189 if (!is_filename_printable(filename
))
1190 goto idx_block_next_entry
;
1195 readdir_state
->in_idx_root
= true;
1202 dirent
->d_ino
= ie
->data
.dir
.indexed_file
;
1203 dirent
->d_off
= file
->offset
;
1204 dirent
->d_reclen
= offsetof(struct dirent
, d_name
) + len
+ 1;
1208 mrec
= NTFS_SB(fs
)->mft_record_lookup(fs
, ie
->data
.dir
.indexed_file
, NULL
);
1210 printf("No MFT record found.\n");
1214 dirent
->d_type
= get_inode_mode(mrec
);
1215 memcpy(dirent
->d_name
, filename
, len
+ 1);
1222 printf("Index not found\n");
1226 printf("Corrupt index. Aborting lookup...\n");
1230 static inline struct inode
*ntfs_iget(const char *dname
, struct inode
*parent
)
1232 return ntfs_index_lookup(dname
, parent
);
1235 static struct inode
*ntfs_iget_root(struct fs_info
*fs
)
1238 struct ntfs_mft_record
*mrec
, *lmrec
;
1239 struct ntfs_attr_record
*attr
;
1240 struct ntfs_vol_info
*vol_info
;
1241 struct inode
*inode
;
1244 dprintf("in %s()\n", __func__
);
1246 /* Fetch the $Volume MFT record */
1248 mrec
= NTFS_SB(fs
)->mft_record_lookup(fs
, FILE_Volume
, &start_blk
);
1250 printf("Could not fetch $Volume MFT record!\n");
1256 /* Fetch the volume information attribute */
1257 attr
= ntfs_attr_lookup(fs
, NTFS_AT_VOL_INFO
, &mrec
, lmrec
);
1259 printf("Could not find volume info attribute!\n");
1263 /* Note NTFS version and choose version-dependent functions */
1264 vol_info
= (void *)((char *)attr
+ attr
->data
.resident
.value_offset
);
1265 NTFS_SB(fs
)->major_ver
= vol_info
->major_ver
;
1266 NTFS_SB(fs
)->minor_ver
= vol_info
->minor_ver
;
1267 if (vol_info
->major_ver
== 3 && vol_info
->minor_ver
== 0)
1268 NTFS_SB(fs
)->mft_record_lookup
= ntfs_mft_record_lookup_3_0
;
1269 else if (vol_info
->major_ver
== 3 && vol_info
->minor_ver
== 1 &&
1270 mrec
->mft_record_no
== FILE_Volume
)
1271 NTFS_SB(fs
)->mft_record_lookup
= ntfs_mft_record_lookup_3_1
;
1273 /* Free MFT record */
1277 inode
= new_ntfs_inode(fs
);
1280 err
= index_inode_setup(fs
, FILE_root
, inode
);
1284 NTFS_PVT(inode
)->start
= NTFS_PVT(inode
)->here
;
1299 /* Initialize the filesystem metadata and return blk size in bits */
1300 static int ntfs_fs_init(struct fs_info
*fs
)
1303 struct ntfs_bpb ntfs
;
1304 struct ntfs_sb_info
*sbi
;
1305 struct disk
*disk
= fs
->fs_dev
->disk
;
1306 uint8_t mft_record_shift
;
1308 dprintf("in %s()\n", __func__
);
1310 read_count
= disk
->rdwr_sectors(disk
, &ntfs
, 0, 1, 0);
1314 if (!ntfs_check_sb_fields(&ntfs
))
1317 SECTOR_SHIFT(fs
) = disk
->sector_shift
;
1319 /* Note: ntfs.clust_per_mft_record can be a negative number.
1320 * If negative, it represents a shift count, else it represents
1321 * a multiplier for the cluster size.
1323 mft_record_shift
= ntfs
.clust_per_mft_record
< 0 ?
1324 -ntfs
.clust_per_mft_record
:
1325 ilog2(ntfs
.sec_per_clust
) + SECTOR_SHIFT(fs
) +
1326 ilog2(ntfs
.clust_per_mft_record
);
1328 SECTOR_SIZE(fs
) = 1 << SECTOR_SHIFT(fs
);
1330 sbi
= malloc(sizeof *sbi
);
1332 malloc_error("ntfs_sb_info structure");
1336 sbi
->clust_shift
= ilog2(ntfs
.sec_per_clust
);
1337 sbi
->clust_byte_shift
= sbi
->clust_shift
+ SECTOR_SHIFT(fs
);
1338 sbi
->clust_mask
= ntfs
.sec_per_clust
- 1;
1339 sbi
->clust_size
= ntfs
.sec_per_clust
<< SECTOR_SHIFT(fs
);
1340 sbi
->mft_record_size
= 1 << mft_record_shift
;
1341 sbi
->clust_per_idx_record
= ntfs
.clust_per_idx_record
;
1343 BLOCK_SHIFT(fs
) = ilog2(ntfs
.clust_per_idx_record
) + sbi
->clust_byte_shift
;
1344 BLOCK_SIZE(fs
) = 1 << BLOCK_SHIFT(fs
);
1346 sbi
->mft_lcn
= ntfs
.mft_lclust
;
1347 sbi
->mft_blk
= ntfs
.mft_lclust
<< sbi
->clust_shift
<< SECTOR_SHIFT(fs
) >>
1349 /* 16 MFT entries reserved for metadata files (approximately 16 KiB) */
1350 sbi
->mft_size
= mft_record_shift
<< sbi
->clust_shift
<< 4;
1352 sbi
->clusters
= ntfs
.total_sectors
<< SECTOR_SHIFT(fs
) >> sbi
->clust_shift
;
1353 if (sbi
->clusters
> 0xFFFFFFFFFFF4ULL
)
1354 sbi
->clusters
= 0xFFFFFFFFFFF4ULL
;
1357 * Assume NTFS version 3.0 to begin with. If we find that the
1358 * volume is a different version later on, we will adjust at
1363 sbi
->mft_record_lookup
= ntfs_mft_record_lookup_3_0
;
1365 /* Initialize the cache */
1366 cache_init(fs
->fs_dev
, BLOCK_SHIFT(fs
));
1368 return BLOCK_SHIFT(fs
);
1371 const struct fs_ops ntfs_fs_ops
= {
1373 .fs_flags
= FS_USEMEM
| FS_THISIND
,
1374 .fs_init
= ntfs_fs_init
,
1376 .getfssec
= ntfs_getfssec
,
1377 .close_file
= generic_close_file
,
1378 .mangle_name
= generic_mangle_name
,
1379 .open_config
= generic_open_config
,
1380 .readdir
= ntfs_readdir
,
1381 .iget_root
= ntfs_iget_root
,
1383 .next_extent
= ntfs_next_extent
,