3 * ntfs file system driver code.
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
)
376 if(attribute_ondisk(ptr
, len
) == 0) {
378 attribute_get_embeded(ptr
, len
, &ptr
, &len
);
381 *optrp
= AllocatePool(len
);
382 fsw_memcpy(*optrp
, ptr
, len
);
387 olen
= attribute_size(ptr
, len
);
392 fsw_u8
*buf
= AllocatePool(olen
);
393 fsw_memzero(buf
, olen
);
396 attribute_get_rle(ptr
, len
, &ptr
, &len
);
399 int clustersize
= 1<<vol
->clbits
;
402 while(len
> 0 && get_extent(&ptr
, &len
, &lcn
, &cnt
, &pos
)==FSW_SUCCESS
) {
404 for(; cnt
>0; lcn
++, cnt
--) {
406 if (fsw_block_get(&vol
->g
, lcn
, 0, (void **)&block
) != FSW_SUCCESS
)
411 return FSW_VOLUME_CORRUPTED
;
413 fsw_memcpy(buf
, block
, clustersize
> olen
? olen
: clustersize
);
414 fsw_block_release(&vol
->g
, lcn
, block
);
420 buf
+= cnt
<< vol
->clbits
;
421 olen
-= cnt
<< vol
->clbits
;
427 static void init_mft(struct fsw_ntfs_volume
*vol
, struct ntfs_mft
*mft
, fsw_u64 mftno
)
430 mft
->buf
= AllocatePool(1<<vol
->mftbits
);
435 static void free_mft(struct ntfs_mft
*mft
)
437 if(mft
->buf
) FreePool(mft
->buf
);
438 if(mft
->atlst
) FreePool(mft
->atlst
);
441 static fsw_status_t
load_atlist(struct fsw_ntfs_volume
*vol
, struct ntfs_mft
*mft
)
445 fsw_status_t err
= find_attribute_direct(mft
->buf
, 1<<vol
->mftbits
, AT_ATTRIBUTE_LIST
, &ptr
, &len
);
446 if(err
!= FSW_SUCCESS
)
448 return read_attribute_direct(vol
, ptr
, len
, &mft
->atlst
, &mft
->atlen
);
451 static fsw_status_t
read_mft(struct fsw_ntfs_volume
*vol
, fsw_u8
*mft
, fsw_u64 mftno
)
454 int r
= vol
->extmap
.used
- 1;
456 fsw_u64 vcn
= (mftno
<< vol
->mftbits
) >> vol
->clbits
;
457 struct extent_slot
*e
= vol
->extmap
.extent
;
463 } else if(vcn
>= e
[m
].vcn
+ e
[m
].cnt
) {
465 } else if(vol
->mftbits
<= vol
->clbits
) {
466 fsw_u64 lcn
= e
[m
].lcn
+ (vcn
- e
[m
].vcn
);
467 int offset
= (mftno
<< vol
->mftbits
) & ((1<<vol
->clbits
)-1);
471 if(e
[m
].lcn
+ 1 == 0)
472 return FSW_VOLUME_CORRUPTED
;
474 if ((err
= fsw_block_get(&vol
->g
, lcn
, 0, (void **)&buffer
)) != FSW_SUCCESS
)
475 return FSW_VOLUME_CORRUPTED
;
477 fsw_memcpy(mft
, buffer
+offset
, 1<<vol
->mftbits
);
478 fsw_block_release(&vol
->g
, lcn
, buffer
);
479 return fixup(mft
, "FILE", 1<<vol
->sctbits
, 1<<vol
->mftbits
);
482 fsw_u64 lcn
= e
[m
].lcn
+ (vcn
- e
[m
].vcn
);
483 fsw_u64 ecnt
= e
[m
].cnt
- (vcn
- e
[m
].vcn
);
484 int count
= 1 << (vol
->mftbits
- vol
->clbits
);
487 if(e
[m
].lcn
+ 1 == 0)
488 return FSW_VOLUME_CORRUPTED
;
491 if ((err
= fsw_block_get(&vol
->g
, lcn
, 0, (void **)&buffer
)) != FSW_SUCCESS
)
492 return FSW_VOLUME_CORRUPTED
;
493 fsw_memcpy(p
, buffer
, 1<<vol
->clbits
);
494 fsw_block_release(&vol
->g
, lcn
, buffer
);
502 } else if(++m
>= vol
->extmap
.used
|| e
[m
].vcn
!= vcn
) {
503 return FSW_VOLUME_CORRUPTED
;
509 return fixup(mft
, "FILE", 1<<vol
->sctbits
, 1<<vol
->mftbits
);
512 return FSW_NOT_FOUND
;
515 static void init_attr(struct fsw_ntfs_volume
*vol
, struct ntfs_attr
*attr
, int type
)
517 fsw_memzero(attr
, sizeof(*attr
));
519 attr
->emftno
= BADMFT
;
522 static void free_attr(struct ntfs_attr
*attr
)
524 if(attr
->emft
) FreePool(attr
->emft
);
527 static fsw_status_t
find_attribute(struct fsw_ntfs_volume
*vol
, struct ntfs_mft
*mft
, struct ntfs_attr
*attr
, fsw_u64 vcn
)
529 fsw_u8
*buf
= mft
->buf
;
530 if(mft
->atlst
&& mft
->atlen
) {
535 err
= find_attrlist_direct(mft
->atlst
, mft
->atlen
, attr
->type
, vcn
, &mftno
, &pos
);
536 if(err
!= FSW_SUCCESS
)
539 if(mftno
== mft
->mftno
) {
541 } else if(mftno
== attr
->emftno
&& attr
->emft
) {
544 attr
->emftno
= BADMFT
;
545 err
= read_mft(vol
, attr
->emft
, mftno
);
546 if(err
!= FSW_SUCCESS
)
548 attr
->emftno
= mftno
;
552 return find_attribute_direct(buf
, 1<<vol
->mftbits
, attr
->type
, &attr
->ptr
, &attr
->len
);
555 static fsw_status_t
read_small_attribute(struct fsw_ntfs_volume
*vol
, struct ntfs_mft
*mft
, int type
, fsw_u8
**optrp
, int *olenp
)
558 struct ntfs_attr attr
;
560 init_attr(vol
, &attr
, type
);
561 err
= find_attribute(vol
, mft
, &attr
, 0);
562 if(err
== FSW_SUCCESS
)
563 err
= read_attribute_direct(vol
, attr
.ptr
, attr
.len
, optrp
, olenp
);
568 static void add_single_mft_map(struct fsw_ntfs_volume
*vol
, fsw_u8
*mft
)
573 if(find_attribute_direct(mft
, 1<<vol
->mftbits
, AT_DATA
, &ptr
, &len
)!=FSW_SUCCESS
)
576 if(attribute_ondisk(ptr
, len
) == 0)
579 fsw_u64 vcn
= GETU64(ptr
, 0x10);
580 int off
= GETU16(ptr
, 0x20);
587 while(len
> 0 && get_extent(&ptr
, &len
, &lcn
, &cnt
, &pos
)==FSW_SUCCESS
) {
589 int u
= vol
->extmap
.used
;
590 if(u
>= vol
->extmap
.total
) {
591 vol
->extmap
.total
= vol
->extmap
.extent
? u
*2 : 16;
592 struct extent_slot
*e
= AllocatePool(vol
->extmap
.total
* sizeof(struct extent_slot
));
593 if(vol
->extmap
.extent
) {
594 fsw_memcpy(e
, vol
->extmap
.extent
, u
*sizeof(struct extent_slot
));
595 FreePool(vol
->extmap
.extent
);
597 vol
->extmap
.extent
= e
;
599 vol
->extmap
.extent
[u
].vcn
= vcn
;
600 vol
->extmap
.extent
[u
].lcn
= lcn
;
601 vol
->extmap
.extent
[u
].cnt
= cnt
;
608 static void add_mft_map(struct fsw_ntfs_volume
*vol
, struct ntfs_mft
*mft
)
610 load_atlist(vol
, mft
);
611 add_single_mft_map(vol
, mft
->buf
);
612 if(mft
->atlst
== NULL
) return;
617 fsw_u8
*emft
= AllocatePool(1<<vol
->mftbits
);
618 while(find_attrlist_direct(mft
->atlst
, mft
->atlen
, AT_DATA
, 0, &mftno
, &pos
) == FSW_SUCCESS
) {
619 if(mftno
== 0) continue;
620 if(read_mft(vol
, emft
, mftno
)==FSW_SUCCESS
)
621 add_single_mft_map(vol
, emft
);
626 static int tobits(fsw_u32 val
)
628 return 31 - __builtin_clz(val
);
631 static fsw_status_t
fsw_ntfs_volume_mount(struct fsw_volume
*volg
)
633 struct fsw_ntfs_volume
*vol
= (struct fsw_ntfs_volume
*)volg
;
639 fsw_u64 mft_start
[2];
640 struct ntfs_mft mft0
;
642 fsw_memzero((char *)vol
+sizeof(*volg
), sizeof(*vol
)-sizeof(*volg
));
643 fsw_set_blocksize(volg
, 512, 512);
644 if ((err
= fsw_block_get(volg
, 0, 0, (void **)&buffer
)) != FSW_SUCCESS
)
645 return FSW_UNSUPPORTED
;
647 if (!fsw_memeq(buffer
+3, "NTFS ", 8))
648 return FSW_UNSUPPORTED
;
650 sector_size
= GETU16(buffer
, 0xB);
651 if(sector_size
==0 || (sector_size
& (sector_size
-1)) || sector_size
< 0x100 || sector_size
> 0x1000)
652 return FSW_UNSUPPORTED
;
654 vol
->sctbits
= tobits(sector_size
);
655 vol
->totalbytes
= GETU64(buffer
, 0x28) << vol
->sctbits
;
656 Print(L
"NTFS size=%ld M\n", vol
->totalbytes
>>20);
658 cluster_size
= GETU8(buffer
, 0xD) * sector_size
;
659 if(cluster_size
==0 || (cluster_size
& (cluster_size
-1)) || cluster_size
> 0x10000)
660 return FSW_UNSUPPORTED
;
662 vol
->clbits
= tobits(cluster_size
);
664 tmp
= GETU8(buffer
, 0x40);
666 vol
->mftbits
= vol
->clbits
+ tobits(tmp
);
670 if(vol
->mftbits
< vol
->sctbits
|| vol
->mftbits
> 16)
671 return FSW_UNSUPPORTED
;
673 tmp
= GETU8(buffer
, 0x44);
675 vol
->idxbits
= vol
->clbits
+ tobits(tmp
);
679 if(vol
->idxbits
< vol
->sctbits
|| vol
->idxbits
> 16)
680 return FSW_UNSUPPORTED
;
682 mft_start
[0] = GETU64(buffer
, 0x30);
683 mft_start
[1] = GETU64(buffer
, 0x38);
685 fsw_block_release(volg
, 0, (void *)buffer
);
686 fsw_set_blocksize(volg
, cluster_size
, cluster_size
);
688 init_mft(vol
, &mft0
, MFTNO_MFT
);
689 for(tmp
=0; tmp
<2; tmp
++) {
690 fsw_u8
*ptr
= mft0
.buf
;
691 int len
= 1 << vol
->mftbits
;
692 fsw_u64 lcn
= mft_start
[tmp
];
694 if ((err
= fsw_block_get(volg
, lcn
, 0, (void **)&buffer
)) != FSW_SUCCESS
)
697 return FSW_UNSUPPORTED
;
699 int n
= vol
->mftbits
< vol
->clbits
? (1<<vol
->mftbits
) : cluster_size
;
700 fsw_memcpy(ptr
, buffer
, n
);
701 fsw_block_release(volg
, lcn
, (void *)buffer
);
706 err
= fixup(mft0
.buf
, "FILE", sector_size
, 1<<vol
->mftbits
);
707 if(err
!= FSW_SUCCESS
)
711 add_mft_map(vol
, &mft0
);
715 for(i
=0; i
<vol
->extmap
.used
; i
++)
716 Print(L
"extent %d: vcn=%lx lcn=%lx len=%lx\n",
718 vol
->extmap
.extent
[i
].vcn
,
719 vol
->extmap
.extent
[i
].lcn
,
720 vol
->extmap
.extent
[i
].cnt
);
725 /* load $Volume name */
726 init_mft(vol
, &mft0
, MFTNO_VOLUME
);
729 if(read_mft(vol
, mft0
.buf
, MFTNO_VOLUME
)==FSW_SUCCESS
&&
730 find_attribute_direct(mft0
.buf
, 1<<vol
->mftbits
, AT_VOLUME_NAME
, &ptr
, &len
)==FSW_SUCCESS
&&
734 s
.type
= FSW_STRING_TYPE_UTF16_LE
;
735 s
.size
= GETU16(ptr
, 0x10);
737 s
.data
= ptr
+ GETU16(ptr
, 0x14);
738 Print(L
"Volume name [%.*ls]\n", s
.len
, s
.data
);
739 err
= fsw_strdup_coerce(&volg
->label
, volg
->host_string_type
, &s
);
743 err
= fsw_dnode_create_root(volg
, MFTNO_ROOT
, &volg
->root
);
750 static void fsw_ntfs_volume_free(struct fsw_volume
*volg
)
752 struct fsw_ntfs_volume
*vol
= (struct fsw_ntfs_volume
*)volg
;
753 if(vol
->extmap
.extent
)
754 FreePool(vol
->extmap
.extent
);
755 if(vol
->upcase
&& vol
->upcase
!= upcase
)
756 FreePool((void *)vol
->upcase
);
759 static fsw_status_t
fsw_ntfs_volume_stat(struct fsw_volume
*volg
, struct fsw_volume_stat
*sb
)
761 struct fsw_ntfs_volume
*vol
= (struct fsw_ntfs_volume
*)volg
;
762 sb
->total_bytes
= vol
->totalbytes
;
763 /* reading through cluster bitmap is too costly */
768 static fsw_status_t
fsw_ntfs_dnode_fill(struct fsw_volume
*volg
, struct fsw_dnode
*dnog
)
770 struct fsw_ntfs_volume
*vol
= (struct fsw_ntfs_volume
*)volg
;
771 struct fsw_ntfs_dnode
*dno
= (struct fsw_ntfs_dnode
*)dnog
;
775 fsw_memzero((char *)dno
+sizeof(*dnog
), sizeof(*dno
)-sizeof(*dnog
));
776 init_mft(vol
, &dno
->mft
, dno
->g
.dnode_id
);
777 err
= read_mft(vol
, dno
->mft
.buf
, dno
->g
.dnode_id
);
778 if(err
!= FSW_SUCCESS
)
781 len
= GETU8(dno
->mft
.buf
, 22);
783 return FSW_NOT_FOUND
;
785 load_atlist(vol
, &dno
->mft
);
787 if(read_small_attribute(vol
, &dno
->mft
, AT_REPARSE_POINT
, &dno
->cbuf
, &len
)==FSW_SUCCESS
) {
788 switch(GETU32(dno
->cbuf
, 0)) {
792 dno
->g
.type
= FSW_DNODE_TYPE_SYMLINK
;
803 dno
->g
.type
= FSW_DNODE_TYPE_DIR
;
804 /* $INDEX_ROOT:$I30 must present */
805 err
= read_small_attribute(vol
, &dno
->mft
, AT_INDEX_ROOT
|AT_I30
, &dno
->idxroot
, &dno
->rootsz
);
806 if(err
!= FSW_SUCCESS
)
808 Print(L
"dno_fill INDEX_ROOT:$I30 error %d\n", err
);
812 dno
->idxsz
= GETU32(dno
->idxroot
, 8);
814 dno
->idxsz
= 1<<vol
->idxbits
;
816 /* $Bitmap:$I30 is optional */
817 err
= read_small_attribute(vol
, &dno
->mft
, AT_BITMAP
|AT_I30
, &dno
->idxbmp
, &dno
->bmpsz
);
818 if(err
!= FSW_SUCCESS
&& err
!= FSW_NOT_FOUND
)
820 Print(L
"dno_fill $Bitmap:$I30 error %d\n", err
);
824 /* $INDEX_ALLOCATION:$I30 is optional */
825 init_attr(vol
, &dno
->attr
, AT_INDEX_ALLOCATION
|AT_I30
);
826 err
= find_attribute(vol
, &dno
->mft
, &dno
->attr
, 0);
827 if(err
== FSW_SUCCESS
) {
828 dno
->has_idxtree
= 1;
829 dno
->fsize
= attribute_size(dno
->attr
.ptr
, dno
->attr
.len
);
830 dno
->finited
= dno
->fsize
;
831 } else if(err
!= FSW_NOT_FOUND
) {
832 Print(L
"dno_fill $INDEX_ALLOCATION:$I30 error %d\n", err
);
837 dno
->g
.type
= FSW_DNODE_TYPE_FILE
;
838 init_attr(vol
, &dno
->attr
, AT_DATA
);
839 err
= find_attribute(vol
, &dno
->mft
, &dno
->attr
, 0);
840 if(err
!= FSW_SUCCESS
)
842 Print(L
"dno_fill AT_DATA error %d\n", err
);
845 dno
->embeded
= !attribute_ondisk(dno
->attr
.ptr
, dno
->attr
.len
);
846 dno
->fsize
= attribute_size(dno
->attr
.ptr
, dno
->attr
.len
);
847 dno
->finited
= attribute_inited_size(dno
->attr
.ptr
, dno
->attr
.len
);
848 if(attribute_encrypted(dno
->attr
.ptr
, dno
->attr
.len
))
850 else if(attribute_compressed_future(dno
->attr
.ptr
, dno
->attr
.len
))
852 else if(attribute_compressed(dno
->attr
.ptr
, dno
->attr
.len
))
855 dno
->g
.size
= dno
->fsize
;
860 static void fsw_ntfs_dnode_free(struct fsw_volume
*vol
, struct fsw_dnode
*dnog
)
862 struct fsw_ntfs_dnode
*dno
= (struct fsw_ntfs_dnode
*)dnog
;
864 free_attr(&dno
->attr
);
866 FreePool(dno
->idxroot
);
868 FreePool(dno
->idxbmp
);
873 static fsw_u32
get_ntfs_time(fsw_u8
*buf
, int pos
)
875 fsw_u64 t
= GETU64(buf
, pos
);
876 t
= FSW_U64_DIV(t
, 10000000);
877 t
-= 134774ULL * 86400;
881 static fsw_status_t
fsw_ntfs_dnode_stat(struct fsw_volume
*volg
, struct fsw_dnode
*dnog
, struct fsw_dnode_stat
*sb
)
883 struct fsw_ntfs_volume
*vol
= (struct fsw_ntfs_volume
*)volg
;
884 struct fsw_ntfs_dnode
*dno
= (struct fsw_ntfs_dnode
*)dnog
;
890 err
= find_attribute_direct(dno
->mft
.buf
, 1<<vol
->mftbits
, AT_STANDARD_INFORMATION
, &ptr
, &len
);
892 if(err
!= FSW_SUCCESS
|| GETU8(ptr
, 8))
895 ptr
+= GETU16(ptr
, 0x14);
896 attr
= GETU8(ptr
, 0x20); /* only lower 8 of 32 bit is used */
897 attr
&= EFI_FILE_READ_ONLY
| EFI_FILE_HIDDEN
| EFI_FILE_SYSTEM
| EFI_FILE_ARCHIVE
;
898 /* add DIR again if symlink */
899 if(GETU8(dno
->mft
.buf
, 22) & 2)
900 attr
|= EFI_FILE_DIRECTORY
;
902 fsw_store_attr_efi(sb
, attr
);
903 sb
->used_bytes
= dno
->fsize
;
904 fsw_store_time_posix(sb
, FSW_DNODE_STAT_ATIME
, get_ntfs_time(ptr
, 24));
905 fsw_store_time_posix(sb
, FSW_DNODE_STAT_CTIME
, get_ntfs_time(ptr
, 8));
906 fsw_store_time_posix(sb
, FSW_DNODE_STAT_MTIME
, get_ntfs_time(ptr
, 16));
911 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
)
914 if(vcn
>= dno
->cext
.vcn
&& vcn
< dno
->cext
.vcn
+dno
->cext
.cnt
) {
915 *lcnp
= dno
->cext
.lcn
+ vcn
- dno
->cext
.vcn
;
918 if(!attribute_has_vcn(dno
->attr
.ptr
, dno
->attr
.len
, vcn
)) {
919 err
= find_attribute(vol
, &dno
->mft
, &dno
->attr
, vcn
);
920 if( err
!= FSW_SUCCESS
)
922 if(!attribute_has_vcn(dno
->attr
.ptr
, dno
->attr
.len
, vcn
))
923 return FSW_VOLUME_CORRUPTED
;
925 fsw_u8
*ptr
= dno
->attr
.ptr
;
926 int len
= dno
->attr
.len
;
929 fsw_u64 svcn
= attribute_first_vcn(ptr
, len
);
930 fsw_u64 evcn
= attribute_last_vcn(ptr
, len
) + 1;
931 int off
= GETU16(ptr
, 0x20);
934 while(len
> 0 && get_extent(&ptr
, &len
, &lcn
, &cnt
, &pos
)==FSW_SUCCESS
) {
935 if(vcn
>= svcn
&& vcn
< svcn
+cnt
) {
936 dno
->cext
.vcn
= svcn
;
940 return FSW_NOT_FOUND
;
941 *lcnp
= lcn
+ vcn
- svcn
;
948 return FSW_NOT_FOUND
;
951 static int fsw_ntfs_read_buffer(struct fsw_ntfs_volume
*vol
, struct fsw_ntfs_dnode
*dno
, fsw_u8
*buf
, fsw_u64 offset
, int size
)
956 attribute_get_embeded(dno
->attr
.ptr
, dno
->attr
.len
, &ptr
, &len
);
959 fsw_memcpy(buf
, ptr
, size
);
963 if(!dno
->attr
.ptr
|| !dno
->attr
.len
) {
964 Print(L
"BAD--------: attr.ptr %p attr.len %x cleared\n", dno
->attr
.ptr
, dno
->attr
.len
);
965 if(find_attribute(vol
, &dno
->mft
, &dno
->attr
, 0) != FSW_SUCCESS
)
969 if(offset
>= dno
->fsize
)
971 if(offset
+ size
>= dno
->fsize
)
972 size
= dno
->fsize
- offset
;
974 if(offset
>= dno
->finited
) {
975 fsw_memzero(buf
, size
);
981 if(offset
+ size
>= dno
->finited
) {
982 zsize
= offset
+ size
- dno
->finited
;
983 size
= dno
->finited
- offset
;
986 fsw_u64 vcn
= offset
>> vol
->clbits
;
987 int boff
= offset
& ((1<<vol
->clbits
)-1);
995 err
= fsw_ntfs_dnode_get_lcn(vol
, dno
, vcn
, &lcn
);
996 if (err
!= FSW_SUCCESS
) break;
998 err
= fsw_block_get(&vol
->g
, lcn
, 0, (void **)&block
);
999 if (err
!= FSW_SUCCESS
) break;
1001 bsz
= (1<<vol
->clbits
) - boff
;
1005 fsw_memcpy(buf
, block
+boff
, bsz
);
1006 fsw_block_release(&vol
->g
, lcn
, block
);
1013 if(size
==0 && zsize
> 0) {
1014 fsw_memzero(buf
, zsize
);
1020 static fsw_status_t
fsw_ntfs_get_extent_embeded(struct fsw_ntfs_volume
*vol
, struct fsw_ntfs_dnode
*dno
, struct fsw_extent
*extent
)
1022 if(extent
->log_start
> 0)
1023 return FSW_NOT_FOUND
;
1024 extent
->log_count
= 1;
1025 extent
->buffer
= AllocatePool(1<<vol
->clbits
);
1028 attribute_get_embeded(dno
->attr
.ptr
, dno
->attr
.len
, &ptr
, &len
);
1029 if(len
> (1<<vol
->clbits
))
1030 len
= 1<<vol
->clbits
;
1031 fsw_memcpy(extent
->buffer
, ptr
, len
);
1032 extent
->type
= FSW_EXTENT_TYPE_BUFFER
;
1036 static int ntfs_decomp_1page(fsw_u8
*src
, int slen
, fsw_u8
*dst
) {
1039 while(soff
< slen
) {
1041 int tag
= src
[soff
++];
1042 for(j
= 0; j
< 8 && soff
< slen
; j
++) {
1048 if(!doff
|| soff
+ 2 > slen
)
1050 len
= GETU16(src
, soff
); soff
+= 2;
1051 bits
= __builtin_clz((doff
-1)>>3)-19;
1052 back
= (len
>> bits
) + 1;
1053 len
= (len
& ((1<<bits
)-1)) + 3;
1054 if(doff
< back
|| doff
+ len
> 0x1000)
1057 dst
[doff
] = dst
[doff
-back
];
1063 dst
[doff
++] = src
[soff
++];
1070 static int ntfs_decomp(fsw_u8
*src
, int slen
, fsw_u8
*dst
, int npage
) {
1071 fsw_u8
*se
= src
+ slen
;
1072 fsw_u8
*de
= dst
+ (npage
<<12);
1074 for(i
=0; i
<npage
; i
++) {
1075 fsw_u16 slen
= GETU16(src
, 0);
1076 int comp
= slen
& 0x8000;
1077 slen
= (slen
&0xfff)+1;
1080 if(src
+ slen
> se
|| dst
+ 0x1000 > de
)
1084 fsw_memcpy(dst
, src
, slen
);
1086 fsw_memzero(dst
+slen
, 0x1000-slen
);
1087 } else if(slen
== 1) {
1088 fsw_memzero(dst
, 0x1000);
1090 int dlen
= ntfs_decomp_1page(src
, slen
, dst
);
1094 fsw_memzero(dst
+dlen
, 0x1000-dlen
);
1102 static fsw_status_t
fsw_ntfs_get_extent_compressed(struct fsw_ntfs_volume
*vol
, struct fsw_ntfs_dnode
*dno
, struct fsw_extent
*extent
)
1104 if(vol
->clbits
> 16)
1105 return FSW_VOLUME_CORRUPTED
;
1107 if((extent
->log_start
<< vol
->clbits
) > dno
->fsize
)
1108 return FSW_NOT_FOUND
;
1111 fsw_u64 vcn
= extent
->log_start
& ~15;
1113 if(vcn
== dno
->cvcn
)
1120 for(i
=0; i
<16; i
++) {
1122 err
= fsw_ntfs_dnode_get_lcn(vol
, dno
, vcn
+i
, &dno
->clcn
[i
]);
1123 if(err
== FSW_NOT_FOUND
) {
1125 } else if(err
!= FSW_SUCCESS
) {
1126 Print(L
"BAD LCN\n");
1128 return FSW_VOLUME_CORRUPTED
;
1136 if(dno
->cbuf
== NULL
)
1137 dno
->cbuf
= AllocatePool(16<<vol
->clbits
);
1138 fsw_u8
*src
= AllocatePool(i
<< vol
->clbits
);
1140 for(b
=0; b
<i
; b
++) {
1142 if (fsw_block_get(&vol
->g
, dno
->clcn
[b
], 0, (void **)&block
) != FSW_SUCCESS
) {
1144 Print(L
"Read ERROR at block %d\n", i
);
1147 fsw_memcpy(src
+(b
<<vol
->clbits
), block
, 1<<vol
->clbits
);
1148 fsw_block_release(&vol
->g
, dno
->clcn
[b
], block
);
1151 if(dno
->fsize
>= ((vcn
+16)<<vol
->clbits
))
1152 b
= 16<<vol
->clbits
>>12;
1154 b
= (dno
->fsize
- (vcn
<< vol
->clbits
) + 0xfff)>>12;
1155 if(!dno
->cperror
&& ntfs_decomp(src
, i
<<vol
->clbits
, dno
->cbuf
, b
) < 0)
1161 return FSW_VOLUME_CORRUPTED
;
1162 i
= extent
->log_start
- vcn
;
1164 fsw_u64 lcn
= dno
->clcn
[i
];
1165 extent
->phys_start
= lcn
;
1166 extent
->log_count
= 1;
1167 extent
->type
= FSW_EXTENT_TYPE_PHYSBLOCK
;
1168 for(i
++, lcn
++; i
<16 && lcn
==dno
->clcn
[i
]; i
++, lcn
++)
1169 extent
->log_count
++;
1170 } else if(dno
->cpzero
) {
1171 extent
->log_count
= 16 - i
;
1172 extent
->buffer
= NULL
;
1173 extent
->type
= FSW_EXTENT_TYPE_SPARSE
;
1175 extent
->log_count
= 1;
1176 extent
->buffer
= AllocatePool(1<<vol
->clbits
);
1177 fsw_memcpy(extent
->buffer
, dno
->cbuf
+ (i
<<vol
->clbits
), 1<<vol
->clbits
);
1178 extent
->type
= FSW_EXTENT_TYPE_BUFFER
;
1183 static fsw_status_t
fsw_ntfs_get_extent_sparse(struct fsw_ntfs_volume
*vol
, struct fsw_ntfs_dnode
*dno
, struct fsw_extent
*extent
)
1187 if((extent
->log_start
<< vol
->clbits
) > dno
->fsize
)
1188 return FSW_NOT_FOUND
;
1189 if((extent
->log_start
<< vol
->clbits
) > dno
->finited
)
1191 extent
->log_count
= 1;
1192 extent
->buffer
= NULL
;
1193 extent
->type
= FSW_EXTENT_TYPE_SPARSE
;
1197 err
= fsw_ntfs_dnode_get_lcn(vol
, dno
, extent
->log_start
, &lcn
);
1198 if(err
== FSW_NOT_FOUND
) {
1199 extent
->log_count
= 1;
1200 extent
->buffer
= NULL
;
1201 extent
->type
= FSW_EXTENT_TYPE_SPARSE
;
1204 if(err
!= FSW_SUCCESS
)
1206 extent
->phys_start
= lcn
;
1207 extent
->log_count
= 1;
1208 if(extent
->log_start
>= dno
->cext
.vcn
&& extent
->log_start
< dno
->cext
.vcn
+dno
->cext
.cnt
)
1209 extent
->log_count
= dno
->cext
.cnt
+ extent
->log_start
- dno
->cext
.vcn
;
1210 extent
->type
= FSW_EXTENT_TYPE_PHYSBLOCK
;
1214 static fsw_status_t
fsw_ntfs_get_extent(struct fsw_volume
*volg
, struct fsw_dnode
*dnog
, struct fsw_extent
*extent
)
1216 struct fsw_ntfs_volume
*vol
= (struct fsw_ntfs_volume
*)volg
;
1217 struct fsw_ntfs_dnode
*dno
= (struct fsw_ntfs_dnode
*)dnog
;
1220 return FSW_UNSUPPORTED
;
1222 return fsw_ntfs_get_extent_embeded(vol
, dno
, extent
);
1224 return fsw_ntfs_get_extent_compressed(vol
, dno
, extent
);
1225 return fsw_ntfs_get_extent_sparse(vol
, dno
, extent
);
1228 static fsw_status_t
load_upcase(struct fsw_ntfs_volume
*vol
)
1231 struct ntfs_mft mft
;
1232 init_mft(vol
, &mft
, MFTNO_UPCASE
);
1233 err
= read_mft(vol
, mft
.buf
, MFTNO_UPCASE
);
1234 if(err
== FSW_SUCCESS
) {
1235 if((err
= read_small_attribute(vol
, &mft
, AT_DATA
, (fsw_u8
**)&vol
->upcase
, &vol
->upcount
))==FSW_SUCCESS
) {
1237 #ifndef FSW_LITTLE_ENDIAN
1239 for( i
=0; i
<vol
->upcount
; i
++)
1240 vol
->upcase
[i
] = fsw_u16_le_swap(vol
->upcase
[i
]);
1248 static int ntfs_filename_cmp(struct fsw_ntfs_volume
*vol
, fsw_u8
*p1
, int s1
, fsw_u8
*p2
, int s2
)
1250 while(s1
> 0 && s2
> 0) {
1251 fsw_u16 c1
= GETU16(p1
,0);
1252 fsw_u16 c2
= GETU16(p2
,0);
1253 if(c1
< 0x80 || c2
< 0x80) {
1254 if(c1
< 0x80) c1
= upcase
[c1
];
1255 if(c2
< 0x80) c2
= upcase
[c2
];
1258 * Only load upcase table if both char is international.
1259 * We assume international char never upcased to ASCII.
1264 /* use raw value & prevent load again */
1265 vol
->upcase
= upcase
;
1269 if(c1
< vol
->upcount
) c1
= vol
->upcase
[c1
];
1270 if(c2
< vol
->upcount
) c2
= vol
->upcase
[c2
];
1288 static fsw_status_t
fsw_ntfs_create_subnode(struct fsw_ntfs_dnode
*dno
, fsw_u8
*buf
, struct fsw_dnode
**child_dno
)
1290 fsw_u64 mftno
= GETU64(buf
, 0) & MFTMASK
;
1291 if(mftno
< MFTNO_META
)
1292 return FSW_NOT_FOUND
;
1294 int type
= GETU32(buf
, 0x48) & 0x10000000 ? FSW_DNODE_TYPE_DIR
: FSW_DNODE_TYPE_FILE
;
1295 struct fsw_string s
;
1296 s
.type
= FSW_STRING_TYPE_UTF16_LE
;
1297 s
.len
= GETU8(buf
, 0x50);
1299 s
.data
= buf
+ 0x52;
1301 return fsw_dnode_create(&dno
->g
, mftno
, type
, &s
, child_dno
);
1304 static fsw_u8
*fsw_ntfs_read_index_block(struct fsw_ntfs_volume
*vol
, struct fsw_ntfs_dnode
*dno
, fsw_u64 block
)
1307 dno
->cbuf
= AllocatePool(dno
->idxsz
);
1308 else if(block
== dno
->cvcn
)
1312 if(fsw_ntfs_read_buffer(vol
, dno
, dno
->cbuf
, (block
-1)*dno
->idxsz
, dno
->idxsz
) != dno
->idxsz
)
1314 if(fixup(dno
->cbuf
, "INDX", 1<<vol
->sctbits
, dno
->idxsz
) != FSW_SUCCESS
)
1321 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
)
1323 struct fsw_ntfs_volume
*vol
= (struct fsw_ntfs_volume
*)volg
;
1324 struct fsw_ntfs_dnode
*dno
= (struct fsw_ntfs_dnode
*)dnog
;
1326 struct fsw_string s
;
1334 err
= fsw_strdup_coerce(&s
, FSW_STRING_TYPE_UTF16_LE
, lookup_name
);
1338 /* start from AT_INDEX_ROOT */
1339 buf
= dno
->idxroot
+ 16;
1340 len
= dno
->rootsz
- 16;
1342 return FSW_NOT_FOUND
;
1345 /* real index size */
1346 if(GETU32(buf
, 4) < len
)
1347 len
= GETU32(buf
, 4);
1349 /* skip index header */
1350 off
= GETU32(buf
, 0);
1352 return FSW_NOT_FOUND
;
1355 while(off
+ 0x18 <= len
) {
1356 int flag
= GETU8(buf
, off
+12);
1357 int next
= off
+ GETU16(buf
, off
+8);
1361 /* the end of index entry */
1363 Print(L
"depth %d len %x off %x flag %x next %x cmp %d\n", depth
, len
, off
, flag
, next
, cmp
);
1365 int nlen
= GETU8(buf
, off
+0x50);
1366 fsw_u8
*name
= buf
+off
+0x52;
1367 cmp
= ntfs_filename_cmp(vol
, s
.data
, s
.len
, name
, nlen
);
1368 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
);
1372 return fsw_ntfs_create_subnode(dno
, buf
+off
, child_dno
);
1373 } else if(cmp
< 0) {
1374 if(!(flag
& 1) || !dno
->has_idxtree
)
1375 return FSW_NOT_FOUND
;
1376 block
= GETU64(buf
, next
-8) + 1;
1378 } else { /* cmp > 0 */
1385 if(!(buf
= fsw_ntfs_read_index_block(vol
, dno
, block
)))
1388 len
= dno
->idxsz
- 24;
1392 return FSW_NOT_FOUND
;
1395 static inline void set_shand_pos( struct fsw_shandle
*shand
, int block
, int off
)
1397 shand
->pos
= (((fsw_u64
)block
) << 32) + off
;
1400 static inline int test_idxbmp(struct fsw_ntfs_dnode
*dno
, int block
)
1403 if(dno
->idxbmp
==NULL
)
1406 mask
= 1 << (block
& 7);
1408 if(block
> dno
->bmpsz
)
1410 return dno
->idxbmp
[block
] & mask
;
1413 #define shand_pos(x,y) (((fsw_u64)(x)<<32)|(y))
1414 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
)
1416 struct fsw_ntfs_volume
*vol
= (struct fsw_ntfs_volume
*)volg
;
1417 struct fsw_ntfs_dnode
*dno
= (struct fsw_ntfs_dnode
*)dnog
;
1419 * high 32 bit: index block#
1422 * low 32 bit: index offset
1424 int off
= shand
->pos
& 0xFFFFFFFF;
1425 int block
= shand
->pos
>> 32;
1428 mblocks
= FSW_U64_DIV(dno
->fsize
, dno
->idxsz
);
1430 while(block
<= mblocks
) {
1435 buf
= dno
->idxroot
+ 16;
1436 len
= dno
->rootsz
- 16;
1439 } else if(!test_idxbmp(dno
, block
) || !(buf
= fsw_ntfs_read_index_block(vol
, dno
, block
)))
1441 /* unused or bad index block */
1444 /* AT_INDEX_ALLOCATION block */
1446 len
= dno
->idxsz
- 24;
1448 if(GETU32(buf
, 4) < len
)
1449 len
= GETU32(buf
, 4);
1451 off
= GETU32(buf
, 0);
1452 Print(L
"block %d len %x off %x\n", block
, len
, off
);
1453 while(off
+ 0x18 <= len
) {
1454 int flag
= GETU8(buf
, off
+12);
1456 int next
= off
+ GETU16(buf
, off
+8);
1457 Print(L
"flag %x next %x nt %x [%.*ls]\n", flag
, next
, GETU8(buf
, off
+0x51), GETU8(buf
, off
+0x50), buf
+off
+0x52);
1458 if((GETU8(buf
, off
+0x51) != 2)) {
1459 /* LONG FILE NAME */
1460 fsw_status_t err
= fsw_ntfs_create_subnode(dno
, buf
+off
, child_dno
);
1461 if(err
!= FSW_NOT_FOUND
) {
1462 set_shand_pos(shand
, block
, next
);
1465 // skip internal MFT record
1470 if(!dno
->has_idxtree
)
1475 set_shand_pos(shand
, mblocks
+1, 0);
1476 return FSW_NOT_FOUND
;
1479 static fsw_status_t
fsw_ntfs_readlink(struct fsw_volume
*volg
, struct fsw_dnode
*dnog
, struct fsw_string
*link
)
1481 struct fsw_ntfs_dnode
*dno
= (struct fsw_ntfs_dnode
*)dnog
;
1487 return FSW_UNSUPPORTED
;
1489 name
= dno
->cbuf
+ 0x10 + GETU16(dno
->cbuf
, 8);
1490 len
= GETU16(dno
->cbuf
, 10);
1491 if(GETU32(dno
->cbuf
, 0) == 0xa000000c)
1494 for(i
=0; i
<len
; i
+=2)
1495 if(GETU16(name
, i
)=='\\')
1496 *(fsw_u16
*)(name
+i
) = fsw_u16_le_swap('/');
1498 if(len
> 6 && GETU16(name
,0)=='/' && GETU16(name
,2)=='?' &&
1499 GETU16(name
,4)=='?' && GETU16(name
,6)=='/' &&
1500 GETU16(name
,10)==':' && GETU16(name
,12)=='/' &&
1501 (GETU16(name
,8)|0x20)>='a' && (GETU16(name
,8)|0x20)<='z')
1506 struct fsw_string s
;
1507 s
.type
= FSW_STRING_TYPE_UTF16_LE
;
1511 return fsw_strdup_coerce(link
, volg
->host_string_type
, &s
);
1518 struct fsw_fstype_table
FSW_FSTYPE_TABLE_NAME(ntfs
) = {
1519 { FSW_STRING_TYPE_UTF8
, 4, 4, "ntfs" },
1520 sizeof(struct fsw_ntfs_volume
),
1521 sizeof(struct fsw_ntfs_dnode
),
1523 fsw_ntfs_volume_mount
,
1524 fsw_ntfs_volume_free
,
1525 fsw_ntfs_volume_stat
,
1526 fsw_ntfs_dnode_fill
,
1527 fsw_ntfs_dnode_free
,
1528 fsw_ntfs_dnode_stat
,
1529 fsw_ntfs_get_extent
,
1530 fsw_ntfs_dir_lookup
,