3 * ntfs file system driver code.
4 * Copyright (C) 2015 by Samuel Liao
8 * This program is free software; you can redistribute it and/or
9 * modify it under the terms of the GNU General Public License
10 * as published by the Free Software Foundation; either version 2
11 * of the License, or (at your option) any later version.
13 * This program is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 * GNU General Public License for more details.
18 * You should have received a copy of the GNU General Public License
19 * along with this program; if not, write to the Free Software
20 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
25 #define Print(x...) /* */
27 static inline fsw_u8
GETU8(fsw_u8
*buf
, int pos
)
32 static inline fsw_u16
GETU16(fsw_u8
*buf
, int pos
)
34 return fsw_u16_le_swap(*(fsw_u16
*)(buf
+pos
));
37 static inline fsw_u32
GETU32(fsw_u8
*buf
, int pos
)
39 return fsw_u32_le_swap(*(fsw_u32
*)(buf
+pos
));
42 static inline fsw_u64
GETU64(fsw_u8
*buf
, int pos
)
44 return fsw_u64_le_swap(*(fsw_u64
*)(buf
+pos
));
47 #define MFTMASK ((1ULL<<48) - 1)
48 #define BADMFT (~0ULL)
50 #define MFTNO_VOLUME 3
52 #define MFTNO_UPCASE 10
54 #define BADVCN (~0ULL)
56 #define AT_STANDARD_INFORMATION 0x10
57 #define AT_ATTRIBUTE_LIST 0x20
58 #define AT_FILENAME 0x30 /* UNUSED */
59 #define AT_VOLUME_NAME 0x60
60 #define AT_VOLUME_INFORMATION 0x70 /* UNUSED */
62 #define AT_INDEX_ROOT 0x90
63 #define AT_INDEX_ALLOCATION 0xa0
64 #define AT_BITMAP 0xb0
65 #define AT_REPARSE_POINT 0xc0
67 #define ATTRMASK 0xFFFF
69 /* $I30 is LE, we can't use L"$I30" */
70 #define NAME_I30 "$\0I\0003\0000\0"
71 #define AT_I30 0x40000
73 static const fsw_u16 upcase
[0x80] =
75 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f,
76 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f,
77 0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, 0x28, 0x29, 0x2a, 0x2b, 0x2c, 0x2d, 0x2e, 0x2f,
78 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39, 0x3a, 0x3b, 0x3c, 0x3d, 0x3e, 0x3f,
79 0x40, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47, 0x48, 0x49, 0x4a, 0x4b, 0x4c, 0x4d, 0x4e, 0x4f,
80 0x50, 0x51, 0x52, 0x53, 0x54, 0x55, 0x56, 0x57, 0x58, 0x59, 0x5a, 0x5b, 0x5c, 0x5d, 0x5e, 0x5f,
81 0x60, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47, 0x48, 0x49, 0x4a, 0x4b, 0x4c, 0x4d, 0x4e, 0x4f,
82 0x50, 0x51, 0x52, 0x53, 0x54, 0x55, 0x56, 0x57, 0x58, 0x59, 0x5a, 0x7b, 0x7c, 0x7d, 0x7e, 0x7f,
95 * we build mft extent table instead use generic code, to prevent
96 * read_mft recursive or dead loop.
97 * While mft has too many fragments, it need AT_ATTRIBUTE_LIST for extra
98 * data, the AT_ATTRIBUTE_LIST parsing code need call read_mft again.
100 struct extent_slot
*extent
;
107 fsw_u64 mftno
; /* current MFT no */
108 fsw_u8
*buf
; /* current MFT record data */
109 fsw_u8
*atlst
; /* AT_ATTRIBUTE_LIST data */
110 int atlen
; /* AT_ATTRIBUTE_LIST size */
115 fsw_u64 emftno
; /* MFT no of emft */
116 fsw_u8
*emft
; /* cached extend MFT record */
117 fsw_u8
*ptr
; /* current attribute data */
118 int len
; /* current attribute size */
119 int type
; /* current attribute type */
122 struct fsw_ntfs_volume
125 struct extent_map extmap
; /* MFT extent map */
126 fsw_u64 totalbytes
; /* volume size */
127 const fsw_u16
*upcase
; /* upcase map for non-ascii */
128 int upcount
; /* upcase map size */
130 fsw_u8 sctbits
; /* sector size */
131 fsw_u8 clbits
; /* cluster size */
132 fsw_u8 mftbits
; /* MFT record size */
133 fsw_u8 idxbits
; /* unused index size, use AT_INDEX_ROOT instead */
136 struct fsw_ntfs_dnode
140 struct ntfs_attr attr
; /* AT_INDEX_ALLOCATION:$I30/AT_DATA */
141 fsw_u8
*idxroot
; /* AT_INDEX_ROOT:$I30 */
142 fsw_u8
*idxbmp
; /* AT_BITMAP:$I30 */
143 unsigned int embeded
:1; /* embeded AT_DATA */
144 unsigned int has_idxtree
:1; /* valid AT_INDEX_ALLOCATION:$I30 */
145 unsigned int compressed
:1; /* compressed AT_DATA */
146 unsigned int unreadable
:1; /* unreadable/encrypted AT_DATA */
147 unsigned int cpfull
:1; /* in-compressable chunk */
148 unsigned int cpzero
:1; /* empty chunk */
149 unsigned int cperror
:1; /* decompress error */
150 unsigned int islink
:1; /* is symlink: AT_REPARSE_POINT */
151 int idxsz
; /* size of index block */
152 int rootsz
; /* size of idxroot: AT_INDEX_ROOT:$I30 */
153 int bmpsz
; /* size of idxbmp: AT_BITMAP:$I30 */
154 struct extent_slot cext
; /* cached extent */
155 fsw_u64 fsize
; /* logical file size */
156 fsw_u64 finited
; /* initialized file size */
157 fsw_u64 cvcn
; /* vcn of compress chunk: cbuf */
158 fsw_u64 clcn
[16]; /* cluster map of compress chunk */
159 fsw_u8
*cbuf
; /* compress chunk/index block/symlink target */
162 static fsw_status_t
fixup(fsw_u8
*record
, char *magic
, int sectorsize
, int size
)
167 if(*(int *)record
!= *(int *)magic
)
168 return FSW_VOLUME_CORRUPTED
;
170 off
= GETU16(record
, 4);
171 cnt
= GETU16(record
, 6);
172 if(size
&& sectorsize
*(cnt
-1) != size
)
173 return FSW_VOLUME_CORRUPTED
;
174 val
= GETU16(record
, off
);
175 for(i
=1; i
<cnt
; i
++) {
176 if(GETU16(record
, i
*sectorsize
-2)!=val
)
177 return FSW_VOLUME_CORRUPTED
;
179 *(fsw_u16
*)(record
+i
*sectorsize
-2) = *(fsw_u16
*)(record
+off
+i
*2);
184 /* only supported attribute name is $I30 */
185 static fsw_status_t
find_attribute_direct(fsw_u8
*mft
, int mftsize
, int type
, fsw_u8
**outptr
, int *outlen
)
190 if(GETU32(mft
, 0x18) < mftsize
)
191 mftsize
= GETU32(mft
, 0x18);
192 mftsize
-= GETU16(mft
, 0x14);
193 mft
+= GETU16(mft
, 0x14);
195 namelen
= type
>>ATTRBITS
;
198 for(n
= 0; mftsize
>= 8; mft
+= n
, mftsize
-= n
) {
199 int t
= GETU32(mft
, 0);
201 if( t
==0 || (t
+1)==0 || t
==0xffff || n
<24 || mftsize
<n
)
204 fsw_u8 ns
= GETU8(mft
, 9);
205 fsw_u8
*nm
= mft
+ GETU8(mft
, 10);
206 if(type
==t
&& namelen
==ns
&& (ns
==0 || fsw_memeq(NAME_I30
, nm
, ns
*2))) {
207 if(outptr
) *outptr
= mft
;
208 if(outlen
) *outlen
= n
;
212 return FSW_NOT_FOUND
;
215 /* only supported attribute name is $I30 */
216 static fsw_status_t
find_attrlist_direct(fsw_u8
*atlst
, int atlen
, int type
, fsw_u64 vcn
, fsw_u64
*out
, int *pos
)
218 fsw_u64 mftno
= BADMFT
;
221 namelen
= type
>>ATTRBITS
;
224 while( *pos
+ 0x18 <= atlen
) {
226 fsw_u32 t
= GETU32(atlst
, off
);
227 fsw_u32 n
= GETU16(atlst
, off
+4);
230 if(t
==0 || (t
+1)==0 || t
==0xffff || n
< 0x18 || *pos
> atlen
)
233 fsw_u8 ns
= GETU8(atlst
, off
+6);
234 fsw_u8
*nm
= atlst
+ off
+ GETU8(atlst
, off
+7);
235 if( type
== t
&& namelen
==ns
&& (ns
==0 || fsw_memeq(NAME_I30
, nm
, ns
*2))) {
236 fsw_u64 avcn
= GETU64(atlst
, off
+8);
239 return FSW_NOT_FOUND
;
244 *out
= GETU64(atlst
, off
+0x10) & MFTMASK
;
247 mftno
= GETU64(atlst
, off
+0x10) & MFTMASK
;
250 return FSW_NOT_FOUND
;
253 static fsw_status_t
get_extent(fsw_u8
**rlep
, int *rlenp
, fsw_u64
*lcnp
, fsw_u64
*lenp
, fsw_u64
*pos
)
256 fsw_u8
*rle_end
= rle
+ *rlenp
;
263 if(n
==0) return FSW_NOT_FOUND
;
265 if(rle
+ n
> rle_end
) return FSW_VOLUME_CORRUPTED
;
274 /* LCN 0 as sparse, due to we don't need $Boot */
278 if(rle
+ n
> rle_end
) return FSW_VOLUME_CORRUPTED
;
293 *rlenp
-= rle
- *rlep
;
298 static inline int attribute_ondisk(fsw_u8
*ptr
, int len
)
300 return GETU8(ptr
, 8);
303 static inline int attribute_compressed(fsw_u8
*ptr
, int len
)
305 return (GETU8(ptr
, 12) & 0xFF) == 1;
308 static inline int attribute_compressed_future(fsw_u8
*ptr
, int len
)
310 return (GETU8(ptr
, 12) & 0xFF) > 1;
313 static inline int attribute_sparse(fsw_u8
*ptr
, int len
)
315 return GETU8(ptr
, 12) & 0x8000;
318 static inline int attribute_encrypted(fsw_u8
*ptr
, int len
)
320 return GETU8(ptr
, 12) & 0x4000;
323 static void attribute_get_embeded(fsw_u8
*ptr
, int len
, fsw_u8
**outp
, int *outlenp
)
325 int off
= GETU16(ptr
, 0x14);
326 int olen
= GETU16(ptr
, 0x10);
333 static void attribute_get_rle(fsw_u8
*ptr
, int len
, fsw_u8
**outp
, int *outlenp
)
335 int off
= GETU16(ptr
, 0x20);
336 int olen
= len
- off
;
341 static inline int attribute_rle_offset(fsw_u8
*ptr
, int len
)
343 return GETU16(ptr
, 0x20);
346 static inline fsw_u64
attribute_size(fsw_u8
*ptr
, int len
)
348 return GETU8(ptr
, 8) ? GETU64(ptr
, 0x30) : GETU16(ptr
, 0x10);
351 static inline fsw_u64
attribute_inited_size(fsw_u8
*ptr
, int len
)
353 return GETU8(ptr
, 8) ? GETU64(ptr
, 0x38) : GETU16(ptr
, 0x10);
356 static inline int attribute_has_vcn(fsw_u8
*ptr
, int len
, fsw_u64 vcn
) {
359 return vcn
>= GETU64(ptr
, 0x10) && vcn
<= GETU64(ptr
, 0x18);
362 static inline fsw_u64
attribute_first_vcn(fsw_u8
*ptr
, int len
)
364 return GETU8(ptr
, 8) ? GETU64(ptr
, 0x10) : 0;
367 static inline fsw_u64
attribute_last_vcn(fsw_u8
*ptr
, int len
)
369 return GETU8(ptr
, 8) ? GETU64(ptr
, 0x18) : 0;
372 static fsw_status_t
read_attribute_direct(struct fsw_ntfs_volume
*vol
, fsw_u8
*ptr
, int len
, fsw_u8
**optrp
, int *olenp
)
377 if(attribute_ondisk(ptr
, len
) == 0) {
379 attribute_get_embeded(ptr
, len
, &ptr
, &len
);
382 if((err
= fsw_alloc(len
, (void **)optrp
)) != FSW_SUCCESS
)
384 fsw_memcpy(*optrp
, ptr
, len
);
389 olen
= attribute_size(ptr
, len
);
394 if((err
= fsw_alloc_zero(olen
, (void **)optrp
)) != FSW_SUCCESS
)
396 fsw_u8
*buf
= *optrp
;
398 attribute_get_rle(ptr
, len
, &ptr
, &len
);
401 int clustersize
= 1<<vol
->clbits
;
404 while(len
> 0 && get_extent(&ptr
, &len
, &lcn
, &cnt
, &pos
)==FSW_SUCCESS
) {
406 for(; cnt
>0; lcn
++, cnt
--) {
408 if (fsw_block_get(&vol
->g
, lcn
, 0, (void **)&block
) != FSW_SUCCESS
)
413 return FSW_VOLUME_CORRUPTED
;
415 fsw_memcpy(buf
, block
, clustersize
> olen
? olen
: clustersize
);
416 fsw_block_release(&vol
->g
, lcn
, block
);
422 buf
+= cnt
<< vol
->clbits
;
423 olen
-= cnt
<< vol
->clbits
;
429 static void init_mft(struct fsw_ntfs_volume
*vol
, struct ntfs_mft
*mft
, fsw_u64 mftno
)
433 mft
->atlen
= fsw_alloc(1<<vol
->mftbits
, &mft
->buf
);
437 static void free_mft(struct ntfs_mft
*mft
)
439 if(mft
->buf
) fsw_free(mft
->buf
);
440 if(mft
->atlst
) fsw_free(mft
->atlst
);
443 static fsw_status_t
load_atlist(struct fsw_ntfs_volume
*vol
, struct ntfs_mft
*mft
)
447 fsw_status_t err
= find_attribute_direct(mft
->buf
, 1<<vol
->mftbits
, AT_ATTRIBUTE_LIST
, &ptr
, &len
);
448 if(err
!= FSW_SUCCESS
)
450 return read_attribute_direct(vol
, ptr
, len
, &mft
->atlst
, &mft
->atlen
);
453 static fsw_status_t
read_mft(struct fsw_ntfs_volume
*vol
, fsw_u8
*mft
, fsw_u64 mftno
)
456 int r
= vol
->extmap
.used
- 1;
458 fsw_u64 vcn
= (mftno
<< vol
->mftbits
) >> vol
->clbits
;
459 struct extent_slot
*e
= vol
->extmap
.extent
;
465 } else if(vcn
>= e
[m
].vcn
+ e
[m
].cnt
) {
467 } else if(vol
->mftbits
<= vol
->clbits
) {
468 fsw_u64 lcn
= e
[m
].lcn
+ (vcn
- e
[m
].vcn
);
469 int offset
= (mftno
<< vol
->mftbits
) & ((1<<vol
->clbits
)-1);
473 if(e
[m
].lcn
+ 1 == 0)
474 return FSW_VOLUME_CORRUPTED
;
476 if ((err
= fsw_block_get(&vol
->g
, lcn
, 0, (void **)&buffer
)) != FSW_SUCCESS
)
477 return FSW_VOLUME_CORRUPTED
;
479 fsw_memcpy(mft
, buffer
+offset
, 1<<vol
->mftbits
);
480 fsw_block_release(&vol
->g
, lcn
, buffer
);
481 return fixup(mft
, "FILE", 1<<vol
->sctbits
, 1<<vol
->mftbits
);
484 fsw_u64 lcn
= e
[m
].lcn
+ (vcn
- e
[m
].vcn
);
485 fsw_u64 ecnt
= e
[m
].cnt
- (vcn
- e
[m
].vcn
);
486 int count
= 1 << (vol
->mftbits
- vol
->clbits
);
489 if(e
[m
].lcn
+ 1 == 0)
490 return FSW_VOLUME_CORRUPTED
;
493 if ((err
= fsw_block_get(&vol
->g
, lcn
, 0, (void **)&buffer
)) != FSW_SUCCESS
)
494 return FSW_VOLUME_CORRUPTED
;
495 fsw_memcpy(p
, buffer
, 1<<vol
->clbits
);
496 fsw_block_release(&vol
->g
, lcn
, buffer
);
504 } else if(++m
>= vol
->extmap
.used
|| e
[m
].vcn
!= vcn
) {
505 return FSW_VOLUME_CORRUPTED
;
511 return fixup(mft
, "FILE", 1<<vol
->sctbits
, 1<<vol
->mftbits
);
514 return FSW_NOT_FOUND
;
517 static void init_attr(struct fsw_ntfs_volume
*vol
, struct ntfs_attr
*attr
, int type
)
519 fsw_memzero(attr
, sizeof(*attr
));
521 attr
->emftno
= BADMFT
;
524 static void free_attr(struct ntfs_attr
*attr
)
526 if(attr
->emft
) fsw_free(attr
->emft
);
529 static fsw_status_t
find_attribute(struct fsw_ntfs_volume
*vol
, struct ntfs_mft
*mft
, struct ntfs_attr
*attr
, fsw_u64 vcn
)
531 fsw_u8
*buf
= mft
->buf
;
532 if(mft
->atlst
&& mft
->atlen
) {
537 err
= find_attrlist_direct(mft
->atlst
, mft
->atlen
, attr
->type
, vcn
, &mftno
, &pos
);
538 if(err
!= FSW_SUCCESS
)
541 if(mftno
== mft
->mftno
) {
543 } else if(mftno
== attr
->emftno
&& attr
->emft
) {
546 attr
->emftno
= BADMFT
;
547 err
= fsw_alloc(1<<vol
->mftbits
, &attr
->emft
);
548 if(err
!= FSW_SUCCESS
)
550 err
= read_mft(vol
, attr
->emft
, mftno
);
551 if(err
!= FSW_SUCCESS
)
553 attr
->emftno
= mftno
;
557 return find_attribute_direct(buf
, 1<<vol
->mftbits
, attr
->type
, &attr
->ptr
, &attr
->len
);
560 static fsw_status_t
read_small_attribute(struct fsw_ntfs_volume
*vol
, struct ntfs_mft
*mft
, int type
, fsw_u8
**optrp
, int *olenp
)
563 struct ntfs_attr attr
;
565 init_attr(vol
, &attr
, type
);
566 err
= find_attribute(vol
, mft
, &attr
, 0);
567 if(err
== FSW_SUCCESS
)
568 err
= read_attribute_direct(vol
, attr
.ptr
, attr
.len
, optrp
, olenp
);
573 static void add_single_mft_map(struct fsw_ntfs_volume
*vol
, fsw_u8
*mft
)
578 if(find_attribute_direct(mft
, 1<<vol
->mftbits
, AT_DATA
, &ptr
, &len
)!=FSW_SUCCESS
)
581 if(attribute_ondisk(ptr
, len
) == 0)
584 fsw_u64 vcn
= GETU64(ptr
, 0x10);
585 int off
= GETU16(ptr
, 0x20);
592 while(len
> 0 && get_extent(&ptr
, &len
, &lcn
, &cnt
, &pos
)==FSW_SUCCESS
) {
594 int u
= vol
->extmap
.used
;
595 if(u
>= vol
->extmap
.total
) {
596 vol
->extmap
.total
= vol
->extmap
.extent
? u
*2 : 16;
597 struct extent_slot
*e
;
598 if(fsw_alloc(vol
->extmap
.total
* sizeof(struct extent_slot
), &e
)!=FSW_SUCCESS
)
600 if(vol
->extmap
.extent
) {
601 fsw_memcpy(e
, vol
->extmap
.extent
, u
*sizeof(struct extent_slot
));
602 fsw_free(vol
->extmap
.extent
);
604 vol
->extmap
.extent
= e
;
606 vol
->extmap
.extent
[u
].vcn
= vcn
;
607 vol
->extmap
.extent
[u
].lcn
= lcn
;
608 vol
->extmap
.extent
[u
].cnt
= cnt
;
615 static void add_mft_map(struct fsw_ntfs_volume
*vol
, struct ntfs_mft
*mft
)
617 load_atlist(vol
, mft
);
618 add_single_mft_map(vol
, mft
->buf
);
619 if(mft
->atlst
== NULL
) return;
625 if(fsw_alloc(1<<vol
->mftbits
, &emft
) != FSW_SUCCESS
) return;
626 while(find_attrlist_direct(mft
->atlst
, mft
->atlen
, AT_DATA
, 0, &mftno
, &pos
) == FSW_SUCCESS
) {
627 if(mftno
== 0) continue;
628 if(read_mft(vol
, emft
, mftno
)==FSW_SUCCESS
)
629 add_single_mft_map(vol
, emft
);
634 static int tobits(fsw_u32 val
)
636 return 31 - __builtin_clz(val
);
639 static fsw_status_t
fsw_ntfs_volume_mount(struct fsw_volume
*volg
)
641 struct fsw_ntfs_volume
*vol
= (struct fsw_ntfs_volume
*)volg
;
647 fsw_u64 mft_start
[2];
648 struct ntfs_mft mft0
;
650 fsw_set_blocksize(volg
, 512, 512);
651 if ((err
= fsw_block_get(volg
, 0, 0, (void **)&buffer
)) != FSW_SUCCESS
)
652 return FSW_UNSUPPORTED
;
654 if (!fsw_memeq(buffer
+3, "NTFS ", 8))
655 return FSW_UNSUPPORTED
;
657 sector_size
= GETU16(buffer
, 0xB);
658 if(sector_size
==0 || (sector_size
& (sector_size
-1)) || sector_size
< 0x100 || sector_size
> 0x1000)
659 return FSW_UNSUPPORTED
;
661 vol
->sctbits
= tobits(sector_size
);
662 vol
->totalbytes
= GETU64(buffer
, 0x28) << vol
->sctbits
;
663 Print(L
"NTFS size=%ld M\n", vol
->totalbytes
>>20);
665 cluster_size
= GETU8(buffer
, 0xD) * sector_size
;
666 if(cluster_size
==0 || (cluster_size
& (cluster_size
-1)) || cluster_size
> 0x10000)
667 return FSW_UNSUPPORTED
;
669 vol
->clbits
= tobits(cluster_size
);
671 tmp
= GETU8(buffer
, 0x40);
673 vol
->mftbits
= vol
->clbits
+ tobits(tmp
);
677 if(vol
->mftbits
< vol
->sctbits
|| vol
->mftbits
> 16)
678 return FSW_UNSUPPORTED
;
680 tmp
= GETU8(buffer
, 0x44);
682 vol
->idxbits
= vol
->clbits
+ tobits(tmp
);
686 if(vol
->idxbits
< vol
->sctbits
|| vol
->idxbits
> 16)
687 return FSW_UNSUPPORTED
;
689 mft_start
[0] = GETU64(buffer
, 0x30);
690 mft_start
[1] = GETU64(buffer
, 0x38);
692 fsw_block_release(volg
, 0, (void *)buffer
);
693 fsw_set_blocksize(volg
, cluster_size
, cluster_size
);
695 init_mft(vol
, &mft0
, MFTNO_MFT
);
696 for(tmp
=0; tmp
<2; tmp
++) {
697 fsw_u8
*ptr
= mft0
.buf
;
698 int len
= 1 << vol
->mftbits
;
699 fsw_u64 lcn
= mft_start
[tmp
];
701 if ((err
= fsw_block_get(volg
, lcn
, 0, (void **)&buffer
)) != FSW_SUCCESS
)
704 return FSW_UNSUPPORTED
;
706 int n
= vol
->mftbits
< vol
->clbits
? (1<<vol
->mftbits
) : cluster_size
;
707 fsw_memcpy(ptr
, buffer
, n
);
708 fsw_block_release(volg
, lcn
, (void *)buffer
);
713 err
= fixup(mft0
.buf
, "FILE", sector_size
, 1<<vol
->mftbits
);
714 if(err
!= FSW_SUCCESS
)
718 add_mft_map(vol
, &mft0
);
722 for(i
=0; i
<vol
->extmap
.used
; i
++)
723 Print(L
"extent %d: vcn=%lx lcn=%lx len=%lx\n",
725 vol
->extmap
.extent
[i
].vcn
,
726 vol
->extmap
.extent
[i
].lcn
,
727 vol
->extmap
.extent
[i
].cnt
);
732 /* load $Volume name */
733 init_mft(vol
, &mft0
, MFTNO_VOLUME
);
736 if(read_mft(vol
, mft0
.buf
, MFTNO_VOLUME
)==FSW_SUCCESS
&&
737 find_attribute_direct(mft0
.buf
, 1<<vol
->mftbits
, AT_VOLUME_NAME
, &ptr
, &len
)==FSW_SUCCESS
&&
741 s
.type
= FSW_STRING_TYPE_UTF16_LE
;
742 s
.size
= GETU16(ptr
, 0x10);
744 s
.data
= ptr
+ GETU16(ptr
, 0x14);
745 Print(L
"Volume name [%.*ls]\n", s
.len
, s
.data
);
746 err
= fsw_strdup_coerce(&volg
->label
, volg
->host_string_type
, &s
);
750 err
= fsw_dnode_create_root(volg
, MFTNO_ROOT
, &volg
->root
);
757 static void fsw_ntfs_volume_free(struct fsw_volume
*volg
)
759 struct fsw_ntfs_volume
*vol
= (struct fsw_ntfs_volume
*)volg
;
760 if(vol
->extmap
.extent
)
761 fsw_free(vol
->extmap
.extent
);
762 if(vol
->upcase
&& vol
->upcase
!= upcase
)
763 fsw_free((void *)vol
->upcase
);
766 static fsw_status_t
fsw_ntfs_volume_stat(struct fsw_volume
*volg
, struct fsw_volume_stat
*sb
)
768 struct fsw_ntfs_volume
*vol
= (struct fsw_ntfs_volume
*)volg
;
769 sb
->total_bytes
= vol
->totalbytes
;
770 /* reading through cluster bitmap is too costly */
775 static void fsw_ntfs_dnode_free(struct fsw_volume
*vol
, struct fsw_dnode
*dnog
)
777 struct fsw_ntfs_dnode
*dno
= (struct fsw_ntfs_dnode
*)dnog
;
779 free_attr(&dno
->attr
);
781 fsw_free(dno
->idxroot
);
783 fsw_free(dno
->idxbmp
);
788 static fsw_status_t
fsw_ntfs_dnode_fill(struct fsw_volume
*volg
, struct fsw_dnode
*dnog
)
790 struct fsw_ntfs_volume
*vol
= (struct fsw_ntfs_volume
*)volg
;
791 struct fsw_ntfs_dnode
*dno
= (struct fsw_ntfs_dnode
*)dnog
;
795 if(dno
->mft
.buf
!= NULL
)
798 init_mft(vol
, &dno
->mft
, dno
->g
.dnode_id
);
799 err
= read_mft(vol
, dno
->mft
.buf
, dno
->g
.dnode_id
);
800 if(err
!= FSW_SUCCESS
)
803 len
= GETU8(dno
->mft
.buf
, 22);
804 if( (len
& 1) == 0 ) {
809 load_atlist(vol
, &dno
->mft
);
811 if(read_small_attribute(vol
, &dno
->mft
, AT_REPARSE_POINT
, &dno
->cbuf
, &len
)==FSW_SUCCESS
) {
812 switch(GETU32(dno
->cbuf
, 0)) {
816 dno
->g
.type
= FSW_DNODE_TYPE_SYMLINK
;
827 dno
->g
.type
= FSW_DNODE_TYPE_DIR
;
828 /* $INDEX_ROOT:$I30 must present */
829 err
= read_small_attribute(vol
, &dno
->mft
, AT_INDEX_ROOT
|AT_I30
, &dno
->idxroot
, &dno
->rootsz
);
830 if(err
!= FSW_SUCCESS
)
832 Print(L
"dno_fill INDEX_ROOT:$I30 error %d\n", err
);
836 dno
->idxsz
= GETU32(dno
->idxroot
, 8);
838 dno
->idxsz
= 1<<vol
->idxbits
;
840 /* $Bitmap:$I30 is optional */
841 err
= read_small_attribute(vol
, &dno
->mft
, AT_BITMAP
|AT_I30
, &dno
->idxbmp
, &dno
->bmpsz
);
842 if(err
!= FSW_SUCCESS
&& err
!= FSW_NOT_FOUND
)
844 Print(L
"dno_fill $Bitmap:$I30 error %d\n", err
);
848 /* $INDEX_ALLOCATION:$I30 is optional */
849 init_attr(vol
, &dno
->attr
, AT_INDEX_ALLOCATION
|AT_I30
);
850 err
= find_attribute(vol
, &dno
->mft
, &dno
->attr
, 0);
851 if(err
== FSW_SUCCESS
) {
852 dno
->has_idxtree
= 1;
853 dno
->fsize
= attribute_size(dno
->attr
.ptr
, dno
->attr
.len
);
854 dno
->finited
= dno
->fsize
;
855 } else if(err
!= FSW_NOT_FOUND
) {
856 Print(L
"dno_fill $INDEX_ALLOCATION:$I30 error %d\n", err
);
861 dno
->g
.type
= FSW_DNODE_TYPE_FILE
;
862 init_attr(vol
, &dno
->attr
, AT_DATA
);
863 err
= find_attribute(vol
, &dno
->mft
, &dno
->attr
, 0);
864 if(err
!= FSW_SUCCESS
)
866 Print(L
"dno_fill AT_DATA error %d\n", err
);
869 dno
->embeded
= !attribute_ondisk(dno
->attr
.ptr
, dno
->attr
.len
);
870 dno
->fsize
= attribute_size(dno
->attr
.ptr
, dno
->attr
.len
);
871 dno
->finited
= attribute_inited_size(dno
->attr
.ptr
, dno
->attr
.len
);
872 if(attribute_encrypted(dno
->attr
.ptr
, dno
->attr
.len
))
874 else if(attribute_compressed_future(dno
->attr
.ptr
, dno
->attr
.len
))
876 else if(attribute_compressed(dno
->attr
.ptr
, dno
->attr
.len
))
879 dno
->g
.size
= dno
->fsize
;
883 fsw_ntfs_dnode_free(volg
, dnog
);
884 // clear tag for good dnode
889 static fsw_u32
get_ntfs_time(fsw_u8
*buf
, int pos
)
891 fsw_u64 t
= GETU64(buf
, pos
);
892 t
= FSW_U64_DIV(t
, 10000000);
893 t
-= 134774ULL * 86400;
897 static fsw_status_t
fsw_ntfs_dnode_stat(struct fsw_volume
*volg
, struct fsw_dnode
*dnog
, struct fsw_dnode_stat
*sb
)
899 struct fsw_ntfs_volume
*vol
= (struct fsw_ntfs_volume
*)volg
;
900 struct fsw_ntfs_dnode
*dno
= (struct fsw_ntfs_dnode
*)dnog
;
906 err
= find_attribute_direct(dno
->mft
.buf
, 1<<vol
->mftbits
, AT_STANDARD_INFORMATION
, &ptr
, &len
);
908 if(err
!= FSW_SUCCESS
|| GETU8(ptr
, 8))
911 ptr
+= GETU16(ptr
, 0x14);
912 attr
= GETU8(ptr
, 0x20); /* only lower 8 of 32 bit is used */
913 #ifndef EFI_FILE_READ_ONLY
914 #define EFI_FILE_READ_ONLY 1
915 #define EFI_FILE_HIDDEN 2
916 #define EFI_FILE_SYSTEM 4
917 #define EFI_FILE_DIRECTORY 0x10
918 #define EFI_FILE_ARCHIVE 0x20
920 attr
&= EFI_FILE_READ_ONLY
| EFI_FILE_HIDDEN
| EFI_FILE_SYSTEM
| EFI_FILE_ARCHIVE
;
921 /* add DIR again if symlink */
922 if(GETU8(dno
->mft
.buf
, 22) & 2)
923 attr
|= EFI_FILE_DIRECTORY
;
925 fsw_store_attr_efi(sb
, attr
);
926 sb
->used_bytes
= dno
->fsize
;
927 fsw_store_time_posix(sb
, FSW_DNODE_STAT_ATIME
, get_ntfs_time(ptr
, 24));
928 fsw_store_time_posix(sb
, FSW_DNODE_STAT_CTIME
, get_ntfs_time(ptr
, 8));
929 fsw_store_time_posix(sb
, FSW_DNODE_STAT_MTIME
, get_ntfs_time(ptr
, 16));
934 static fsw_status_t
fsw_ntfs_dnode_get_lcn(struct fsw_ntfs_volume
*vol
, struct fsw_ntfs_dnode
*dno
, fsw_u64 vcn
, fsw_u64
*lcnp
)
937 if(vcn
>= dno
->cext
.vcn
&& vcn
< dno
->cext
.vcn
+dno
->cext
.cnt
) {
938 *lcnp
= dno
->cext
.lcn
+ vcn
- dno
->cext
.vcn
;
941 if(!attribute_has_vcn(dno
->attr
.ptr
, dno
->attr
.len
, vcn
)) {
942 err
= find_attribute(vol
, &dno
->mft
, &dno
->attr
, vcn
);
943 if( err
!= FSW_SUCCESS
)
945 if(!attribute_has_vcn(dno
->attr
.ptr
, dno
->attr
.len
, vcn
))
946 return FSW_VOLUME_CORRUPTED
;
948 fsw_u8
*ptr
= dno
->attr
.ptr
;
949 int len
= dno
->attr
.len
;
952 fsw_u64 svcn
= attribute_first_vcn(ptr
, len
);
953 fsw_u64 evcn
= attribute_last_vcn(ptr
, len
) + 1;
954 int off
= GETU16(ptr
, 0x20);
957 while(len
> 0 && get_extent(&ptr
, &len
, &lcn
, &cnt
, &pos
)==FSW_SUCCESS
) {
958 if(vcn
>= svcn
&& vcn
< svcn
+cnt
) {
959 dno
->cext
.vcn
= svcn
;
963 return FSW_NOT_FOUND
;
964 *lcnp
= lcn
+ vcn
- svcn
;
971 return FSW_NOT_FOUND
;
974 static int fsw_ntfs_read_buffer(struct fsw_ntfs_volume
*vol
, struct fsw_ntfs_dnode
*dno
, fsw_u8
*buf
, fsw_u64 offset
, int size
)
979 attribute_get_embeded(dno
->attr
.ptr
, dno
->attr
.len
, &ptr
, &len
);
986 fsw_memcpy(buf
, ptr
, size
);
990 if(!dno
->attr
.ptr
|| !dno
->attr
.len
) {
991 Print(L
"BAD--------: attr.ptr %p attr.len %x cleared\n", dno
->attr
.ptr
, dno
->attr
.len
);
992 if(find_attribute(vol
, &dno
->mft
, &dno
->attr
, 0) != FSW_SUCCESS
)
996 if(offset
>= dno
->fsize
)
998 if(offset
+ size
>= dno
->fsize
)
999 size
= dno
->fsize
- offset
;
1001 if(offset
>= dno
->finited
) {
1002 fsw_memzero(buf
, size
);
1008 if(offset
+ size
>= dno
->finited
) {
1009 zsize
= offset
+ size
- dno
->finited
;
1010 size
= dno
->finited
- offset
;
1013 fsw_u64 vcn
= offset
>> vol
->clbits
;
1014 int boff
= offset
& ((1<<vol
->clbits
)-1);
1022 err
= fsw_ntfs_dnode_get_lcn(vol
, dno
, vcn
, &lcn
);
1023 if (err
!= FSW_SUCCESS
) break;
1025 err
= fsw_block_get(&vol
->g
, lcn
, 0, (void **)&block
);
1026 if (err
!= FSW_SUCCESS
) break;
1028 bsz
= (1<<vol
->clbits
) - boff
;
1032 fsw_memcpy(buf
, block
+boff
, bsz
);
1033 fsw_block_release(&vol
->g
, lcn
, block
);
1041 if(size
==0 && zsize
> 0) {
1042 fsw_memzero(buf
, zsize
);
1048 static fsw_status_t
fsw_ntfs_get_extent_embeded(struct fsw_ntfs_volume
*vol
, struct fsw_ntfs_dnode
*dno
, struct fsw_extent
*extent
)
1051 if(extent
->log_start
> 0)
1052 return FSW_NOT_FOUND
;
1053 extent
->log_count
= 1;
1054 err
= fsw_alloc(1<<vol
->clbits
, &extent
->buffer
);
1055 if(err
!= FSW_SUCCESS
) return err
;
1058 attribute_get_embeded(dno
->attr
.ptr
, dno
->attr
.len
, &ptr
, &len
);
1059 if(len
> (1<<vol
->clbits
))
1060 len
= 1<<vol
->clbits
;
1061 fsw_memcpy(extent
->buffer
, ptr
, len
);
1062 extent
->type
= FSW_EXTENT_TYPE_BUFFER
;
1066 static int ntfs_decomp_1page(fsw_u8
*src
, int slen
, fsw_u8
*dst
) {
1069 while(soff
< slen
) {
1071 int tag
= src
[soff
++];
1072 for(j
= 0; j
< 8 && soff
< slen
; j
++) {
1078 if(!doff
|| soff
+ 2 > slen
)
1080 len
= GETU16(src
, soff
); soff
+= 2;
1081 bits
= __builtin_clz((doff
-1)>>3)-19;
1082 back
= (len
>> bits
) + 1;
1083 len
= (len
& ((1<<bits
)-1)) + 3;
1084 if(doff
< back
|| doff
+ len
> 0x1000)
1087 dst
[doff
] = dst
[doff
-back
];
1093 dst
[doff
++] = src
[soff
++];
1100 static int ntfs_decomp(fsw_u8
*src
, int slen
, fsw_u8
*dst
, int npage
) {
1101 fsw_u8
*se
= src
+ slen
;
1102 fsw_u8
*de
= dst
+ (npage
<<12);
1104 for(i
=0; i
<npage
; i
++) {
1105 fsw_u16 slen
= GETU16(src
, 0);
1106 int comp
= slen
& 0x8000;
1107 slen
= (slen
&0xfff)+1;
1110 if(src
+ slen
> se
|| dst
+ 0x1000 > de
)
1114 fsw_memcpy(dst
, src
, slen
);
1116 fsw_memzero(dst
+slen
, 0x1000-slen
);
1117 } else if(slen
== 1) {
1118 fsw_memzero(dst
, 0x1000);
1120 int dlen
= ntfs_decomp_1page(src
, slen
, dst
);
1124 fsw_memzero(dst
+dlen
, 0x1000-dlen
);
1132 static fsw_status_t
fsw_ntfs_get_extent_compressed(struct fsw_ntfs_volume
*vol
, struct fsw_ntfs_dnode
*dno
, struct fsw_extent
*extent
)
1134 if(vol
->clbits
> 16)
1135 return FSW_VOLUME_CORRUPTED
;
1137 if((extent
->log_start
<< vol
->clbits
) > dno
->fsize
)
1138 return FSW_NOT_FOUND
;
1141 fsw_u64 vcn
= extent
->log_start
& ~15;
1143 if(vcn
== dno
->cvcn
)
1150 for(i
=0; i
<16; i
++) {
1152 err
= fsw_ntfs_dnode_get_lcn(vol
, dno
, vcn
+i
, &dno
->clcn
[i
]);
1153 if(err
== FSW_NOT_FOUND
) {
1155 } else if(err
!= FSW_SUCCESS
) {
1156 Print(L
"BAD LCN\n");
1158 return FSW_VOLUME_CORRUPTED
;
1167 if(dno
->cbuf
== NULL
) {
1168 err
= fsw_alloc(16<<vol
->clbits
, &dno
->cbuf
);
1169 if(err
!= FSW_SUCCESS
) {
1175 err
= fsw_alloc(i
<< vol
->clbits
, &src
);
1176 if(err
!= FSW_SUCCESS
) {
1181 for(b
=0; b
<i
; b
++) {
1183 if (fsw_block_get(&vol
->g
, dno
->clcn
[b
], 0, (void **)&block
) != FSW_SUCCESS
) {
1185 Print(L
"Read ERROR at block %d\n", i
);
1188 fsw_memcpy(src
+(b
<<vol
->clbits
), block
, 1<<vol
->clbits
);
1189 fsw_block_release(&vol
->g
, dno
->clcn
[b
], block
);
1192 if(dno
->fsize
>= ((vcn
+16)<<vol
->clbits
))
1193 b
= 16<<vol
->clbits
>>12;
1195 b
= (dno
->fsize
- (vcn
<< vol
->clbits
) + 0xfff)>>12;
1196 if(!dno
->cperror
&& ntfs_decomp(src
, i
<<vol
->clbits
, dno
->cbuf
, b
) < 0)
1202 return FSW_VOLUME_CORRUPTED
;
1203 i
= extent
->log_start
- vcn
;
1205 fsw_u64 lcn
= dno
->clcn
[i
];
1206 extent
->phys_start
= lcn
;
1207 extent
->log_count
= 1;
1208 extent
->type
= FSW_EXTENT_TYPE_PHYSBLOCK
;
1209 for(i
++, lcn
++; i
<16 && lcn
==dno
->clcn
[i
]; i
++, lcn
++)
1210 extent
->log_count
++;
1211 } else if(dno
->cpzero
) {
1212 extent
->log_count
= 16 - i
;
1213 extent
->buffer
= NULL
;
1214 extent
->type
= FSW_EXTENT_TYPE_SPARSE
;
1216 extent
->log_count
= 1;
1217 fsw_status_t err
= fsw_alloc(1<<vol
->clbits
, &extent
->buffer
);
1218 if(err
!= FSW_SUCCESS
) return err
;
1219 fsw_memcpy(extent
->buffer
, dno
->cbuf
+ (i
<<vol
->clbits
), 1<<vol
->clbits
);
1220 extent
->type
= FSW_EXTENT_TYPE_BUFFER
;
1225 static fsw_status_t
fsw_ntfs_get_extent_sparse(struct fsw_ntfs_volume
*vol
, struct fsw_ntfs_dnode
*dno
, struct fsw_extent
*extent
)
1229 if((extent
->log_start
<< vol
->clbits
) > dno
->fsize
)
1230 return FSW_NOT_FOUND
;
1231 if((extent
->log_start
<< vol
->clbits
) > dno
->finited
)
1233 extent
->log_count
= 1;
1234 extent
->buffer
= NULL
;
1235 extent
->type
= FSW_EXTENT_TYPE_SPARSE
;
1239 err
= fsw_ntfs_dnode_get_lcn(vol
, dno
, extent
->log_start
, &lcn
);
1240 if(err
== FSW_NOT_FOUND
) {
1241 extent
->log_count
= 1;
1242 extent
->buffer
= NULL
;
1243 extent
->type
= FSW_EXTENT_TYPE_SPARSE
;
1246 if(err
!= FSW_SUCCESS
)
1248 extent
->phys_start
= lcn
;
1249 extent
->log_count
= 1;
1250 if(extent
->log_start
>= dno
->cext
.vcn
&& extent
->log_start
< dno
->cext
.vcn
+dno
->cext
.cnt
)
1251 extent
->log_count
= dno
->cext
.cnt
+ extent
->log_start
- dno
->cext
.vcn
;
1252 extent
->type
= FSW_EXTENT_TYPE_PHYSBLOCK
;
1256 static fsw_status_t
fsw_ntfs_get_extent(struct fsw_volume
*volg
, struct fsw_dnode
*dnog
, struct fsw_extent
*extent
)
1258 struct fsw_ntfs_volume
*vol
= (struct fsw_ntfs_volume
*)volg
;
1259 struct fsw_ntfs_dnode
*dno
= (struct fsw_ntfs_dnode
*)dnog
;
1262 return FSW_UNSUPPORTED
;
1264 return fsw_ntfs_get_extent_embeded(vol
, dno
, extent
);
1266 return fsw_ntfs_get_extent_compressed(vol
, dno
, extent
);
1267 return fsw_ntfs_get_extent_sparse(vol
, dno
, extent
);
1270 static fsw_status_t
load_upcase(struct fsw_ntfs_volume
*vol
)
1273 struct ntfs_mft mft
;
1274 init_mft(vol
, &mft
, MFTNO_UPCASE
);
1275 err
= read_mft(vol
, mft
.buf
, MFTNO_UPCASE
);
1276 if(err
== FSW_SUCCESS
) {
1277 if((err
= read_small_attribute(vol
, &mft
, AT_DATA
, (fsw_u8
**)&vol
->upcase
, &vol
->upcount
))==FSW_SUCCESS
) {
1279 #ifndef FSW_LITTLE_ENDIAN
1281 for( i
=0; i
<vol
->upcount
; i
++)
1282 vol
->upcase
[i
] = fsw_u16_le_swap(vol
->upcase
[i
]);
1290 static int ntfs_filename_cmp(struct fsw_ntfs_volume
*vol
, fsw_u8
*p1
, int s1
, fsw_u8
*p2
, int s2
)
1292 while(s1
> 0 && s2
> 0) {
1293 fsw_u16 c1
= GETU16(p1
,0);
1294 fsw_u16 c2
= GETU16(p2
,0);
1295 if(c1
< 0x80 || c2
< 0x80) {
1296 if(c1
< 0x80) c1
= upcase
[c1
];
1297 if(c2
< 0x80) c2
= upcase
[c2
];
1300 * Only load upcase table if both char is international.
1301 * We assume international char never upcased to ASCII.
1306 /* use raw value & prevent load again */
1307 vol
->upcase
= upcase
;
1311 if(c1
< vol
->upcount
) c1
= vol
->upcase
[c1
];
1312 if(c2
< vol
->upcount
) c2
= vol
->upcase
[c2
];
1330 static fsw_status_t
fsw_ntfs_create_subnode(struct fsw_ntfs_dnode
*dno
, fsw_u8
*buf
, struct fsw_dnode
**child_dno
)
1332 fsw_u64 mftno
= GETU64(buf
, 0) & MFTMASK
;
1333 if(mftno
< MFTNO_META
)
1334 return FSW_NOT_FOUND
;
1336 int type
= GETU32(buf
, 0x48) & 0x10000000 ? FSW_DNODE_TYPE_DIR
: FSW_DNODE_TYPE_FILE
;
1337 struct fsw_string s
;
1338 s
.type
= FSW_STRING_TYPE_UTF16_LE
;
1339 s
.len
= GETU8(buf
, 0x50);
1341 s
.data
= buf
+ 0x52;
1343 return fsw_dnode_create(&dno
->g
, mftno
, type
, &s
, child_dno
);
1346 static fsw_u8
*fsw_ntfs_read_index_block(struct fsw_ntfs_volume
*vol
, struct fsw_ntfs_dnode
*dno
, fsw_u64 block
)
1348 if(dno
->cbuf
==NULL
) {
1349 if(fsw_alloc(dno
->idxsz
, &dno
->cbuf
) != FSW_SUCCESS
)
1351 } else if(block
== dno
->cvcn
)
1355 if(fsw_ntfs_read_buffer(vol
, dno
, dno
->cbuf
, (block
-1)*dno
->idxsz
, dno
->idxsz
) != dno
->idxsz
)
1357 if(fixup(dno
->cbuf
, "INDX", 1<<vol
->sctbits
, dno
->idxsz
) != FSW_SUCCESS
)
1364 static fsw_status_t
fsw_ntfs_dir_lookup(struct fsw_volume
*volg
, struct fsw_dnode
*dnog
, struct fsw_string
*lookup_name
, struct fsw_dnode
**child_dno
)
1366 struct fsw_ntfs_volume
*vol
= (struct fsw_ntfs_volume
*)volg
;
1367 struct fsw_ntfs_dnode
*dno
= (struct fsw_ntfs_dnode
*)dnog
;
1369 struct fsw_string s
;
1377 err
= fsw_strdup_coerce(&s
, FSW_STRING_TYPE_UTF16_LE
, lookup_name
);
1381 /* start from AT_INDEX_ROOT */
1382 buf
= dno
->idxroot
+ 16;
1383 len
= dno
->rootsz
- 16;
1388 /* real index size */
1389 if(GETU32(buf
, 4) < len
)
1390 len
= GETU32(buf
, 4);
1392 /* skip index header */
1393 off
= GETU32(buf
, 0);
1398 while(off
+ 0x18 <= len
) {
1399 int flag
= GETU8(buf
, off
+12);
1400 int next
= off
+ GETU16(buf
, off
+8);
1404 /* the end of index entry */
1406 Print(L
"depth %d len %x off %x flag %x next %x cmp %d\n", depth
, len
, off
, flag
, next
, cmp
);
1408 int nlen
= GETU8(buf
, off
+0x50);
1409 fsw_u8
*name
= buf
+off
+0x52;
1410 cmp
= ntfs_filename_cmp(vol
, s
.data
, s
.len
, name
, nlen
);
1411 Print(L
"depth %d len %x off %x flag %x next %x cmp %d name %d[%.*ls]\n", depth
, len
, off
, flag
, next
, cmp
, nlen
, nlen
, name
);
1416 return fsw_ntfs_create_subnode(dno
, buf
+off
, child_dno
);
1417 } else if(cmp
< 0) {
1418 if(!(flag
& 1) || !dno
->has_idxtree
)
1420 block
= GETU64(buf
, next
-8) + 1;
1422 } else { /* cmp > 0 */
1429 if(!(buf
= fsw_ntfs_read_index_block(vol
, dno
, block
)))
1432 len
= dno
->idxsz
- 24;
1438 return FSW_NOT_FOUND
;
1441 static inline void set_shand_pos( struct fsw_shandle
*shand
, int block
, int off
)
1443 shand
->pos
= (((fsw_u64
)block
) << 32) + off
;
1446 static inline int test_idxbmp(struct fsw_ntfs_dnode
*dno
, int block
)
1449 if(dno
->idxbmp
==NULL
)
1452 mask
= 1 << (block
& 7);
1454 if(block
> dno
->bmpsz
)
1456 return dno
->idxbmp
[block
] & mask
;
1459 #define shand_pos(x,y) (((fsw_u64)(x)<<32)|(y))
1460 static fsw_status_t
fsw_ntfs_dir_read(struct fsw_volume
*volg
, struct fsw_dnode
*dnog
, struct fsw_shandle
*shand
, struct fsw_dnode
**child_dno
)
1462 struct fsw_ntfs_volume
*vol
= (struct fsw_ntfs_volume
*)volg
;
1463 struct fsw_ntfs_dnode
*dno
= (struct fsw_ntfs_dnode
*)dnog
;
1465 * high 32 bit: index block#
1468 * low 32 bit: index offset
1470 int off
= shand
->pos
& 0xFFFFFFFF;
1471 int block
= shand
->pos
>> 32;
1474 mblocks
= FSW_U64_DIV(dno
->fsize
, dno
->idxsz
);
1476 while(block
<= mblocks
) {
1481 buf
= dno
->idxroot
+ 16;
1482 len
= dno
->rootsz
- 16;
1485 } else if(!test_idxbmp(dno
, block
) || !(buf
= fsw_ntfs_read_index_block(vol
, dno
, block
)))
1487 /* unused or bad index block */
1490 /* AT_INDEX_ALLOCATION block */
1492 len
= dno
->idxsz
- 24;
1494 if(GETU32(buf
, 4) < len
)
1495 len
= GETU32(buf
, 4);
1497 off
= GETU32(buf
, 0);
1498 Print(L
"block %d len %x off %x\n", block
, len
, off
);
1499 while(off
+ 0x18 <= len
) {
1500 int flag
= GETU8(buf
, off
+12);
1502 int next
= off
+ GETU16(buf
, off
+8);
1503 Print(L
"flag %x next %x nt %x [%.*ls]\n", flag
, next
, GETU8(buf
, off
+0x51), GETU8(buf
, off
+0x50), buf
+off
+0x52);
1504 if((GETU8(buf
, off
+0x51) != 2)) {
1505 /* LONG FILE NAME */
1506 fsw_status_t err
= fsw_ntfs_create_subnode(dno
, buf
+off
, child_dno
);
1507 if(err
!= FSW_NOT_FOUND
) {
1508 set_shand_pos(shand
, block
, next
);
1511 // skip internal MFT record
1516 if(!dno
->has_idxtree
)
1521 set_shand_pos(shand
, mblocks
+1, 0);
1522 return FSW_NOT_FOUND
;
1525 static fsw_status_t
fsw_ntfs_readlink(struct fsw_volume
*volg
, struct fsw_dnode
*dnog
, struct fsw_string
*link
)
1527 struct fsw_ntfs_dnode
*dno
= (struct fsw_ntfs_dnode
*)dnog
;
1533 return FSW_UNSUPPORTED
;
1535 name
= dno
->cbuf
+ 0x10 + GETU16(dno
->cbuf
, 8);
1536 len
= GETU16(dno
->cbuf
, 10);
1537 if(GETU32(dno
->cbuf
, 0) == 0xa000000c)
1540 for(i
=0; i
<len
; i
+=2)
1541 if(GETU16(name
, i
)=='\\')
1542 *(fsw_u16
*)(name
+i
) = fsw_u16_le_swap('/');
1544 if(len
> 6 && GETU16(name
,0)=='/' && GETU16(name
,2)=='?' &&
1545 GETU16(name
,4)=='?' && GETU16(name
,6)=='/' &&
1546 GETU16(name
,10)==':' && GETU16(name
,12)=='/' &&
1547 (GETU16(name
,8)|0x20)>='a' && (GETU16(name
,8)|0x20)<='z')
1552 struct fsw_string s
;
1553 s
.type
= FSW_STRING_TYPE_UTF16_LE
;
1557 return fsw_strdup_coerce(link
, volg
->host_string_type
, &s
);
1564 struct fsw_fstype_table
FSW_FSTYPE_TABLE_NAME(ntfs
) = {
1565 { FSW_STRING_TYPE_UTF8
, 4, 4, "ntfs" },
1566 sizeof(struct fsw_ntfs_volume
),
1567 sizeof(struct fsw_ntfs_dnode
),
1569 fsw_ntfs_volume_mount
,
1570 fsw_ntfs_volume_free
,
1571 fsw_ntfs_volume_stat
,
1572 fsw_ntfs_dnode_fill
,
1573 fsw_ntfs_dnode_free
,
1574 fsw_ntfs_dnode_stat
,
1575 fsw_ntfs_get_extent
,
1576 fsw_ntfs_dir_lookup
,