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
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 */
371 chunk
->flags
|= MAP_START
; /* initial chunk */
377 if (v
> 8 || l
> 8) /* more than 8 bytes ? */
380 byte
= (uint8_t *)buf
+ v
;
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
;
398 res
|= (int64_t)mask
; /* sign-extend it */
401 res
= (res
<< byte_shift
) | *byte
--;
404 /* are VCNS from cur_vcn to next_vcn - 1 unallocated ? */
406 chunk
->flags
|= MAP_UNALLOCATED
;
408 chunk
->flags
|= MAP_ALLOCATED
;
410 *offset
+= v
+ l
+ 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
)
423 struct mapping_chunk chunk
;
427 const uint64_t blk_size
= UINT64_C(1) << BLOCK_SHIFT(fs
);
428 uint8_t buf
[blk_size
];
434 struct ntfs_attr_list_entry
*attr_entry
;
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",
452 if (attr_entry
->type
== type
)
453 goto found
; /* We got the attribute! :-) */
456 printf("No attribute found.\n");
459 handle_non_resident_attr
:
460 attr_len
= (uint8_t *)attr
+ attr
->len
;
461 stream
= mapping_chunk_init(attr
, &chunk
, &offset
);
463 err
= parse_data_run(stream
, &offset
, attr_len
, &chunk
);
465 printf("parse_data_run()\n");
469 if (chunk
.flags
& MAP_UNALLOCATED
)
471 if (chunk
.flags
& MAP_END
)
473 if (chunk
.flags
& MAP_ALLOCATED
) {
476 while (vcn
< chunk
.len
) {
477 blk
= (lcn
+ vcn
) << NTFS_SB(fs
)->clust_byte_shift
>>
482 err
= ntfs_read(fs
, buf
, blk_size
, blk_size
, &blk
,
483 &blk_offset
, NULL
, (uint64_t *)&lcn
);
485 printf("Error while reading from cache.\n");
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",
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");
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
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
)
524 retval
= NTFS_SB(fs
)->mft_record_lookup(fs
, attr_entry
->mft_ref
,
527 printf("No MFT record found!\n");
531 /* return the found MFT record */
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
)
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
)
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
;
564 if (attr
->type
== type
)
568 /* if the record has an $ATTRIBUTE_LIST attribute associated
569 * with it, then we need to look for the wanted attribute in
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
);
581 } else if (attr
->type
== NTFS_AT_END
&& !attr_list_attr
) {
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
;
601 return __ntfs_attr_lookup(fs
, type
, &other
);
603 retval
= __ntfs_attr_lookup(fs
, type
, &_mrec
);
606 retval
= __ntfs_attr_lookup(fs
, type
, &other
);
609 } else if (retval
&& (_mrec
!= mrec
)) {
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
,
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
;
629 struct mapping_chunk chunk
;
634 dprintf("in %s()\n", __func__
);
636 mrec
= NTFS_SB(fs
)->mft_record_lookup(fs
, mft_no
, &start_blk
);
638 printf("No MFT record found.\n");
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
);
655 printf("No attribute found.\n");
659 /* check if we have a previous allocated state structure */
662 readdir_state
= NULL
;
665 /* allocate our state structure */
666 readdir_state
= malloc(sizeof *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
);
677 printf("No attribute found.\n");
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
;
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
;
694 err
= parse_data_run(stream
, &offset
, attr_len
, &chunk
);
696 printf("parse_data_run()\n");
700 if (chunk
.flags
& MAP_UNALLOCATED
)
702 if (chunk
.flags
& MAP_END
)
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");
718 inode
->size
= attr
->data
.non_resident
.initialized_size
;
722 inode
->mode
= d_type
;
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
;
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
;
749 struct mapping_chunk chunk
;
756 dprintf("in %s()\n", __func__
);
758 mrec
= NTFS_SB(fs
)->mft_record_lookup(fs
, NTFS_PVT(dir
)->mft_no
, NULL
);
760 printf("No MFT record found.\n");
765 attr
= ntfs_attr_lookup(fs
, NTFS_AT_INDEX_ROOT
, &mrec
, lmrec
);
767 printf("No attribute found.\n");
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
)) {
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
)
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
)
790 if (ntfs_filename_cmp(dname
, ie
))
794 /* check for the presence of a child node */
795 if (!(ie
->flags
& INDEX_ENTRY_NODE
)) {
796 printf("No child node, aborting...\n");
800 /* then descend into child node */
802 attr
= ntfs_attr_lookup(fs
, NTFS_AT_INDEX_ALLOCATION
, &mrec
, lmrec
);
804 printf("No attribute found.\n");
808 if (!attr
->non_resident
) {
809 printf("WTF ?! $INDEX_ALLOCATION isn't really resident.\n");
813 attr_len
= (uint8_t *)attr
+ attr
->len
;
814 stream
= mapping_chunk_init(attr
, &chunk
, &offset
);
816 err
= parse_data_run(stream
, &offset
, attr_len
, &chunk
);
820 if (chunk
.flags
& MAP_UNALLOCATED
)
823 if (chunk
.flags
& MAP_ALLOCATED
) {
824 dprintf("%d cluster(s) starting at 0x%08llX\n", chunk
.len
,
829 while (vcn
< chunk
.len
) {
830 blk
= (lcn
+ vcn
) << NTFS_SB(fs
)->clust_shift
<<
831 SECTOR_SHIFT(fs
) >> BLOCK_SHIFT(fs
);
836 err
= ntfs_read(fs
, &buf
, blk_size
, blk_size
, &blk
,
837 &blk_offset
, NULL
, (uint64_t *)&lcn
);
839 printf("Error while reading from cache.\n");
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");
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
+
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
)
863 /* last entry cannot contain a key */
864 if (ie
->flags
& INDEX_ENTRY_END
)
867 if (ntfs_filename_cmp(dname
, ie
))
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
));
879 dprintf("Index not found\n");
887 dprintf("Index found\n");
888 inode
= new_ntfs_inode(fs
);
889 err
= index_inode_setup(fs
, ie
->data
.dir
.indexed_file
, inode
);
891 printf("Error in index_inode_setup()\n");
901 printf("Corrupt index. Aborting lookup...\n");
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
;
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
];
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
);
929 struct runlist
*rlist
;
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
) >>
939 inode
->next_extent
.len
= (inode
->size
+ sec_size
- 1) >> sec_shift
;
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) >>
957 NTFS_PVT(inode
)->data
.non_resident
.rlist
= rlist
;
964 inode
->next_extent
.pstart
= pstart
;
972 static uint32_t ntfs_getfssec(struct file
*file
, char *buf
, int sectors
,
975 uint8_t non_resident
;
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
;
983 dprintf("in %s()\n", __func__
);
985 non_resident
= NTFS_PVT(inode
)->non_resident
;
987 ret
= generic_getfssec(file
, buf
, sectors
, have_more
);
992 mrec
= NTFS_SB(fs
)->mft_record_lookup(fs
, NTFS_PVT(inode
)->mft_no
,
995 printf("No MFT record found.\n");
1000 attr
= ntfs_attr_lookup(fs
, NTFS_AT_DATA
, &mrec
, lmrec
);
1002 printf("No attribute found.\n");
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
);
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
;
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
;
1041 struct ntfs_idx_entry
*ie
= NULL
;
1042 uint8_t buf
[BLOCK_SIZE(fs
)];
1043 struct ntfs_idx_allocation
*iblk
;
1047 struct mapping_chunk chunk
;
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
);
1057 printf("No MFT record found.\n");
1062 attr
= ntfs_attr_lookup(fs
, NTFS_AT_INDEX_ROOT
, &mrec
, lmrec
);
1064 printf("No attribute found.\n");
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
) {
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
;
1096 descend_into_child_node
:
1097 if (!(ie
->flags
& INDEX_ENTRY_NODE
))
1100 attr
= ntfs_attr_lookup(fs
, NTFS_AT_INDEX_ALLOCATION
, &mrec
, lmrec
);
1104 if (!attr
->non_resident
) {
1105 printf("WTF ?! $INDEX_ALLOCATION isn't really resident.\n");
1109 attr_len
= (uint8_t *)attr
+ attr
->len
;
1112 stream
= mapping_chunk_init(attr
, &chunk
, &offset
);
1113 count
= readdir_state
->idx_blks_count
;
1115 err
= parse_data_run(stream
, &offset
, attr_len
, &chunk
);
1117 printf("Error while parsing data runs.\n");
1121 if (chunk
.flags
& MAP_UNALLOCATED
)
1123 if (chunk
.flags
& MAP_END
)
1127 if (chunk
.flags
& MAP_UNALLOCATED
) {
1128 readdir_state
->idx_blks_count
++;
1133 vcn
= readdir_state
->last_vcn
;
1134 if (vcn
>= chunk
.len
) {
1135 readdir_state
->last_vcn
= 0;
1136 readdir_state
->idx_blks_count
++;
1141 blk
= (lcn
+ vcn
) << NTFS_SB(fs
)->clust_shift
<< SECTOR_SHIFT(fs
) >>
1145 err
= ntfs_read(fs
, &buf
, blk_size
, blk_size
, &blk
, &blk_offset
, NULL
,
1148 printf("Error while reading from cache.\n");
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");
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
)) {
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
)
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;
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
)
1192 len
= ntfs_cvt_filename(filename
, ie
);
1193 if (!is_filename_printable(filename
))
1194 goto idx_block_next_entry
;
1199 readdir_state
->in_idx_root
= true;
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;
1212 mrec
= NTFS_SB(fs
)->mft_record_lookup(fs
, ie
->data
.dir
.indexed_file
, NULL
);
1214 printf("No MFT record found.\n");
1218 dirent
->d_type
= get_inode_mode(mrec
);
1219 memcpy(dirent
->d_name
, filename
, len
+ 1);
1226 printf("Index not found\n");
1230 printf("Corrupt index. Aborting lookup...\n");
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
)
1242 struct ntfs_mft_record
*mrec
, *lmrec
;
1243 struct ntfs_attr_record
*attr
;
1244 struct ntfs_vol_info
*vol_info
;
1245 struct inode
*inode
;
1248 dprintf("in %s()\n", __func__
);
1250 /* Fetch the $Volume MFT record */
1252 mrec
= NTFS_SB(fs
)->mft_record_lookup(fs
, FILE_Volume
, &start_blk
);
1254 printf("Could not fetch $Volume MFT record!\n");
1260 /* Fetch the volume information attribute */
1261 attr
= ntfs_attr_lookup(fs
, NTFS_AT_VOL_INFO
, &mrec
, lmrec
);
1263 printf("Could not find volume info attribute!\n");
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 */
1281 inode
= new_ntfs_inode(fs
);
1284 err
= index_inode_setup(fs
, FILE_root
, inode
);
1288 NTFS_PVT(inode
)->start
= NTFS_PVT(inode
)->here
;
1303 /* Initialize the filesystem metadata and return blk size in bits */
1304 static int ntfs_fs_init(struct fs_info
*fs
)
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);
1318 if (!ntfs_check_sb_fields(&ntfs
))
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
);
1336 malloc_error("ntfs_sb_info structure");
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
) >>
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
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
= {
1377 .fs_flags
= FS_USEMEM
| FS_THISIND
,
1378 .fs_init
= ntfs_fs_init
,
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
,
1387 .next_extent
= ntfs_next_extent
,