1255 lines
30 KiB
C
1255 lines
30 KiB
C
/* vim: set sw=4 :*/
|
|
/*
|
|
* GRUB -- GRand Unified Bootloader
|
|
* Copyright (C) 1999 Free Software Foundation, Inc.
|
|
*
|
|
* This program is free software; you can redistribute it and/or modify
|
|
* it under the terms of the GNU General Public License as published by
|
|
* the Free Software Foundation; either version 2 of the License, or
|
|
* (at your option) any later version.
|
|
*
|
|
* This program is distributed in the hope that it will be useful,
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
* GNU General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU General Public License
|
|
* along with this program; if not, write to the Free Software
|
|
* Foundation, Inc., 51 Franklin Street - Fifth Floor, Boston,
|
|
* MA 02110-1301, USA.
|
|
*/
|
|
|
|
/*
|
|
* Samuel Leo <samuel@_.remove.me._szonline.net>
|
|
* Limitations:
|
|
* 1. Only 32 bit size support
|
|
* 2. don't support >1k MFT record size, >16k INDEX record size
|
|
* 3. don't support recursive at_attribute_list
|
|
* 4. don't support compressed attribute other than Datastream
|
|
* 5. all MFT's at_attribute_list must resident at first run list
|
|
* 6. don't support journaling
|
|
* 7. don't support EFS encryption
|
|
* 8. don't support mount point and junction
|
|
*/
|
|
#ifdef FSYS_NTFS
|
|
|
|
//#define DEBUG_NTFS 1
|
|
|
|
/*
|
|
#define NO_ATTRIBUTE_LIST 1
|
|
totally disable at_attribute_list support,
|
|
if no compressed/fragment file and MFT,
|
|
not recommended
|
|
#define NO_NON_RESIDENT_ATTRIBUTE_LIST 1
|
|
disable non-resident at_attribute_list support,
|
|
if no huge compressed/fragment file and MFT
|
|
#define NO_NTFS_DECOMPRESSION 1
|
|
disable ntfs compressed file support
|
|
#define NO_ALTERNATE_DATASTREAM 1
|
|
disable ntfs alternate datastream support
|
|
*/
|
|
|
|
#include "shared.h"
|
|
#include "filesys.h"
|
|
|
|
#ifdef STAGE1_5
|
|
/* safe turn off non-resident attribute list if MFT fragments < 4000 */
|
|
//#define NO_NON_RESIDENT_ATTRIBUTE_LIST 1
|
|
#define NO_NTFS_DECOMPRESSION 1
|
|
#endif
|
|
|
|
#define MAX_MFT_RECORD_SIZE 1024
|
|
#define MAX_INDEX_RECORD_SIZE 16384
|
|
#define MAX_INDEX_BITMAP_SIZE 4096
|
|
#define DECOMP_DEST_BUFFER_SIZE 16384
|
|
#define DECOMP_SOURCE_BUFFER_SIZE (8192+2)
|
|
#define MAX_DIR_DEPTH 64
|
|
|
|
/* sizes are always in bytes, BLOCK values are always in DEV_BSIZE (sectors) */
|
|
#define DEV_BSIZE 512
|
|
|
|
/* include/linux/fs.h */
|
|
#define BLOCK_SIZE 512
|
|
|
|
#define WHICH_SUPER 1
|
|
#define SBLOCK (WHICH_SUPER * BLOCK_SIZE / DEV_BSIZE) /* = 2 */
|
|
|
|
/* include/asm-i386/type.h */
|
|
typedef __signed__ char __s8;
|
|
typedef unsigned char __u8;
|
|
typedef __signed__ short __s16;
|
|
typedef unsigned short __u16;
|
|
typedef __signed__ int __s32;
|
|
typedef unsigned int __u32;
|
|
typedef __signed__ long long __s64;
|
|
typedef unsigned long long __u64;
|
|
|
|
#define FILE_MFT 0
|
|
#define FILE_MFTMIRR 1
|
|
#define FILE_LOGFILE 2
|
|
#define FILE_VOLUME 3
|
|
#define FILE_ATTRDEF 4
|
|
#define FILE_ROOT 5
|
|
#define FILE_BITMAP 6
|
|
#define FILE_BOOT 7
|
|
#define FILE_BADCLUS 8
|
|
#define FILE_QUOTA 9
|
|
#define FILE_UPCASE 10
|
|
|
|
#define at_standard_information 0x10
|
|
#define at_attribute_list 0x20
|
|
#define at_filename 0x30
|
|
#define at_security_descriptor 0x50
|
|
#define at_data 0x80
|
|
#define at_index_root 0x90
|
|
#define at_index_allocation 0xa0
|
|
#define at_bitmap 0xb0
|
|
#define at_symlink 0xc0
|
|
|
|
#define NONAME ""
|
|
#define ATTR_NORMAL 0
|
|
#define ATTR_COMPRESSED 1
|
|
#define ATTR_RESIDENT 2
|
|
#define ATTR_ENCRYPTED 16384
|
|
#define ATTR_SPARSE 32768
|
|
|
|
typedef struct run_list {
|
|
char *start;
|
|
char *ptr;
|
|
int svcn;
|
|
int evcn;
|
|
int vcn;
|
|
int cnum0;
|
|
int cnum;
|
|
int clen;
|
|
} RUNL;
|
|
|
|
typedef struct ntfs_mft_record {
|
|
char mft[MAX_MFT_RECORD_SIZE];
|
|
char mft2[MAX_MFT_RECORD_SIZE];
|
|
int attr_type;
|
|
char *attr_name;
|
|
int attr_flag;
|
|
int attr_size;
|
|
char *attr;
|
|
int attr_len;
|
|
RUNL runl;
|
|
char *attr_list;
|
|
int attr_list_len;
|
|
int attr_list_size;
|
|
int attr_list_off;
|
|
int attr_inited;
|
|
char attr_list_buf[2*BLOCK_SIZE];
|
|
RUNL attr_list_runl;
|
|
} MFTR;
|
|
|
|
|
|
#define index_data ((char *)FSYS_BUF)
|
|
#define bitmap_data ((__u8 *)(FSYS_BUF+MAX_INDEX_RECORD_SIZE))
|
|
#define dcdbuf ((__u8 *)index_data)
|
|
#define dcsbuf (bitmap_data)
|
|
#define dcend (dcsbuf+DECOMP_SOURCE_BUFFER_SIZE)
|
|
#define fnbuf ((char *)(bitmap_data+MAX_INDEX_BITMAP_SIZE))
|
|
#define mmft ((MFTR *)dcend)
|
|
#define cmft ((MFTR *)(dcend+sizeof(MFTR)))
|
|
#define mft_run ((RUNL *)(dcend+2*sizeof(MFTR)))
|
|
#define path_ino ((int *)(dcend+2*sizeof(MFTR)+sizeof(RUNL)))
|
|
#define cluster16 (path_ino+MAX_DIR_DEPTH)
|
|
#define index16 cluster16[16]
|
|
#define blocksize cluster16[17]
|
|
#define clustersize cluster16[18]
|
|
#define mft_record_size cluster16[19]
|
|
#define index_record_size cluster16[20]
|
|
#define dcvcn cluster16[21]
|
|
#define dcoff cluster16[22]
|
|
#define dclen cluster16[23]
|
|
#define dcrem cluster16[24]
|
|
#define dcslen cluster16[25]
|
|
#define dcsptr ((__u8 *)cluster16[26])
|
|
#define is_ads_completion cluster16[27]
|
|
|
|
static int read_mft_record(int mftno, char *mft, int self);
|
|
static int read_attribute(MFTR *mftr, int offset, char *buf, int len, RUNL *from_rl);
|
|
static int get_next_run(RUNL *runl);
|
|
|
|
static inline int
|
|
nsubstring (char *s1, char *s2)
|
|
{
|
|
while (tolower(*s1) == tolower(*s2))
|
|
{
|
|
/* The strings match exactly. */
|
|
if (! *(s1++))
|
|
return 0;
|
|
s2 ++;
|
|
}
|
|
|
|
/* S1 is a substring of S2. */
|
|
if (*s1 == 0)
|
|
return -1;
|
|
|
|
/* S1 isn't a substring. */
|
|
return 1;
|
|
}
|
|
|
|
static int fixup_record(char *record, char *magic, int size)
|
|
{
|
|
int start, count, offset;
|
|
__u16 fixup;
|
|
|
|
if(*(int *)record != *(int *)magic)
|
|
return 0;
|
|
start=*(__u16 *)(record+4);
|
|
count=*(__u16 *)(record+6);
|
|
count--;
|
|
if(size && blocksize*count != size)
|
|
return 0;
|
|
fixup = *(__u16 *)(record+start);
|
|
start+=2;
|
|
offset=blocksize-2;
|
|
while(count--){
|
|
if(*(__u16 *)(record+offset)!=fixup)
|
|
return 0;
|
|
*(__u16 *)(record+offset) = *(__u16 *)(record+start);
|
|
start+=2;
|
|
offset+=blocksize;
|
|
}
|
|
return 1;
|
|
}
|
|
|
|
static void rewind_run_list( RUNL *runl) {
|
|
runl->vcn = runl->svcn;
|
|
runl->ptr = runl->start;
|
|
runl->cnum0 = 0;
|
|
runl->cnum = 0;
|
|
runl->clen = 0;
|
|
}
|
|
|
|
static int get_next_run(RUNL *runl){
|
|
int t, n, v;
|
|
|
|
#ifdef DEBUG_NTFS
|
|
printf("get_next_run: s=%d e=%d c=%d start=%x ptr=%x\n",
|
|
runl->svcn, runl->evcn, runl->vcn, runl->start, runl->ptr);
|
|
#endif
|
|
|
|
runl->vcn += runl->clen;
|
|
if(runl->vcn > runl->evcn) {
|
|
return 0;
|
|
}
|
|
|
|
t = *(runl->ptr)++;
|
|
n = t&0xf;
|
|
runl->clen = 0; v = 1;
|
|
while(n--) {
|
|
runl->clen += v * *((__u8 *)runl->ptr)++;
|
|
v <<= 8;
|
|
}
|
|
n = (t>>4)&0xf;
|
|
if(n==0)
|
|
runl->cnum = 0;
|
|
else {
|
|
int c = 0;
|
|
v = 1;
|
|
while(n--) {
|
|
c += v * *((__u8 *)runl->ptr)++;
|
|
v <<= 8;
|
|
}
|
|
if(c & (v>>1)) c -= v;
|
|
runl->cnum0 += c;
|
|
runl->cnum = runl->cnum0;
|
|
}
|
|
#ifdef DEBUG_NTFS
|
|
printf("got_next_run: t=%x cluster %x len %x vcn=%x ecn=%x\n",
|
|
t, runl->cnum, runl->clen, runl->vcn, runl->evcn);
|
|
#endif
|
|
return 1;
|
|
}
|
|
|
|
#ifndef NO_ATTRIBUTE_LIST
|
|
static void init_run_list(char *attr, int len, RUNL *runl, __u32 *initp) {
|
|
int allocated;
|
|
|
|
runl->svcn = *(__u32 *)(attr+0x10); /* only support 32 bit */
|
|
runl->evcn = *(__u32 *)(attr+0x18); /* only support 32 bit */
|
|
runl->start = attr + *(__u16 *)(attr+0x20);
|
|
allocated = *(__u32 *)(attr+0x28);
|
|
if(initp) *initp = *(__u32 *)(attr+0x38);
|
|
if(!runl->evcn) runl->evcn = (allocated - 1) / clustersize;
|
|
#ifdef DEBUG_NTFS
|
|
printf("size %d allocated=%d inited=%d cegin=%x csize=%d vcn=%d-%d\n",
|
|
/*attr_size*/ *(__u32 *)(attr+0x30),
|
|
/*allocated*/ *(__u32 *)(attr+0x28),
|
|
/*attr_inited*/ *(__u32 *)(attr+0x38),
|
|
/*cengin*/ *(__u16 *)(attr+0x22),
|
|
/*csize*/ *(__u16 *)(attr+0x40),
|
|
runl->svcn, runl->evcn);
|
|
#endif
|
|
rewind_run_list(runl);
|
|
}
|
|
#endif
|
|
|
|
|
|
static int find_attribute(char *mft, int type, char *name, char **attr, int *size, int *len, int *flag) {
|
|
int t, l, r, n, i, namelen;
|
|
unsigned short *attr_name;
|
|
|
|
n = strlen(name);
|
|
r = mft_record_size - *(__u16 *)(mft+0x14);
|
|
mft += *(__u16 *)(mft+0x14);
|
|
while( (t = *(__s32 *)mft) != -1 ) {
|
|
l = *(__u32 *)(mft+4);
|
|
if(l>r) break;
|
|
#ifdef DEBUG_NTFS
|
|
printf("type = %x len = %d namelen=%d resident=%d compresed=%d attrno=%d\n",
|
|
t, l,
|
|
/*namelen*/ *(mft+9),
|
|
//name = (__u16 *)(mft + *(__u16 *)(mft+10)),
|
|
/*resident */ (*(mft+8) == 0),
|
|
/*compressed*/ *(__u16 *)(mft+12),
|
|
/*attrno*/ *(__u16 *)(mft+14));
|
|
#endif
|
|
namelen = *(mft+9);
|
|
if(t == type) {
|
|
#ifndef STAGE1_5
|
|
#ifndef NO_ALTERNATE_DATASTREAM
|
|
if(is_ads_completion && type == at_data) {
|
|
if(namelen && namelen >= n &&
|
|
(!*(mft+8)/*resident*/ || !*(__u32 *)(attr+0x10)/*svcn==0*/))
|
|
{
|
|
for(i=0, attr_name=(__u16 *)(mft + *(__u16 *)(mft+10)); i < n; i++)
|
|
if(tolower(name[i]) != tolower(attr_name[i]))
|
|
break;
|
|
if(i >= n) {
|
|
for(; i < namelen; i++)
|
|
name[i] = attr_name[i];
|
|
name[i] = '\0';
|
|
if(print_possibilities > 0)
|
|
print_possibilities = -print_possibilities;
|
|
print_a_completion(fnbuf);
|
|
name[n] = '\0';
|
|
}
|
|
}
|
|
} else
|
|
#endif
|
|
#endif
|
|
if(namelen == n) {
|
|
|
|
for(i=0, attr_name=(__u16 *)(mft + *(__u16 *)(mft+10)); i<n; i++)
|
|
if(tolower(name[i]) != tolower(attr_name[i]))
|
|
break;
|
|
if(i>=n) {
|
|
if(flag) *flag = *(__u16 *)(mft+12);
|
|
if(*(mft+8) == 0) {
|
|
if(flag) *flag |= ATTR_RESIDENT;
|
|
#ifdef DEBUG_NTFS
|
|
printf("resident data at %x size %x indexed=%d\n",
|
|
/*data*/ *(__u16 *)(mft+0x14),
|
|
/*attr_size*/ *(__u16 *)(mft+0x10),
|
|
/*indexed*/ *(__u16 *)(mft+0x16));
|
|
#endif
|
|
if(attr) *attr = mft + *(__u16 *)(mft+0x14);
|
|
if(size) *size = *(__u16 *)(mft+0x10);
|
|
if(len) *len = *(__u16 *)(mft+0x10);
|
|
} else {
|
|
if(attr) *attr = mft;
|
|
if(size) *size = *(__u32 *)(mft+0x30);
|
|
if(len) *len = l;
|
|
}
|
|
return 1;
|
|
}
|
|
}
|
|
}
|
|
mft += l;
|
|
r -= l;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
#ifndef NO_ATTRIBUTE_LIST
|
|
static __u32 get_next_attribute_list(MFTR *mftr, int *size) {
|
|
int l, t, mftno;
|
|
#ifdef DEBUG_NTFS
|
|
printf("get_next_attribute_list: type=%x\n",mftr->attr_type);
|
|
#endif
|
|
again:
|
|
while(mftr->attr_list_len>0x14) {
|
|
t = *(__u32 *)(mftr->attr_list + 0);
|
|
l = *(__u16 *)(mftr->attr_list + 4);
|
|
#ifdef DEBUG_NTFS
|
|
printf("attr_list type=%x len=%x remain=%x\n", t, l, mftr->attr_list_len);
|
|
#endif
|
|
if(l==0 || l>mftr->attr_list_len) return 0;
|
|
mftno = *(__u32 *)(mftr->attr_list + 0x10);
|
|
mftr->attr_list_len -= l;
|
|
mftr->attr_list += l;
|
|
if(t==mftr->attr_type)
|
|
{
|
|
#ifdef DEBUG_NTFS
|
|
printf("attr_list mftno=%x\n", mftno);
|
|
#endif
|
|
if(read_mft_record(mftno, mftr->mft2, (mftr==mmft))==0)
|
|
break;
|
|
if(find_attribute(mftr->mft2, mftr->attr_type, mftr->attr_name,
|
|
&mftr->attr, size, &mftr->attr_len, &mftr->attr_flag))
|
|
return 1;
|
|
}
|
|
}
|
|
#ifndef NO_NON_RESIDENT_ATTRIBUTE_LIST
|
|
if(mftr->attr_list_off < mftr->attr_list_size) {
|
|
int len = mftr->attr_list_size - mftr->attr_list_off;
|
|
if(len > BLOCK_SIZE) len = BLOCK_SIZE;
|
|
|
|
if(mftr->attr_list_len)
|
|
memmove(mftr->attr_list_buf, mftr->attr_list, mftr->attr_list_len);
|
|
mftr->attr_list = mftr->attr_list_buf;
|
|
|
|
if(read_attribute( NULL, mftr->attr_list_off,
|
|
mftr->attr_list_buf + mftr->attr_list_len,
|
|
len, &mftr->attr_list_runl) != len)
|
|
{
|
|
#ifdef DEBUG_NTFS
|
|
printf("CORRUPT NON-RESIDENT ATTRIBUTE_LIST\n");
|
|
#endif
|
|
/* corrupt */
|
|
errnum = ERR_FSYS_CORRUPT;
|
|
mftr->attr_list_size = 0;
|
|
mftr->attr_len = 0;
|
|
mftr->attr_list = NULL;
|
|
return 0;
|
|
}
|
|
|
|
mftr->attr_list_len += len;
|
|
mftr->attr_list_off += len;
|
|
goto again;
|
|
}
|
|
#endif
|
|
mftr->attr_list = NULL;
|
|
return 0;
|
|
}
|
|
#endif
|
|
|
|
static int search_attribute( MFTR *mftr, int type, char *name)
|
|
{
|
|
#ifdef DEBUG_NTFS
|
|
printf("searching attribute %x <%s>\n", type, name);
|
|
#endif
|
|
|
|
mftr->attr_type = type;
|
|
mftr->attr_name = name;
|
|
mftr->attr_list = NULL;
|
|
mftr->attr_list_len = 0;
|
|
mftr->attr_list_size = 0;
|
|
mftr->attr_list_off = 0;
|
|
dcrem = dclen = 0;
|
|
|
|
#ifndef NO_ATTRIBUTE_LIST
|
|
if(find_attribute(mftr->mft, at_attribute_list, NONAME,
|
|
&mftr->attr_list, &mftr->attr_list_size,
|
|
&mftr->attr_list_len, &mftr->attr_list_off)) {
|
|
if(mftr->attr_list_off&ATTR_RESIDENT) {
|
|
/* resident at_attribute_list */
|
|
mftr->attr_list_size = 0;
|
|
#ifdef DEBUG_NTFS
|
|
printf("resident attribute_list len=%x\n", mftr->attr_list_len);
|
|
#endif
|
|
} else {
|
|
#ifdef DEBUG_NTFS
|
|
printf("non-resident attribute_list len=%x size=%x\n",
|
|
mftr->attr_list_len, mftr->attr_list_size);
|
|
#endif
|
|
#ifndef NO_NON_RESIDENT_ATTRIBUTE_LIST
|
|
init_run_list(mftr->attr_list, mftr->attr_list_len, &mftr->attr_list_runl, NULL);
|
|
if(get_next_run(&mftr->attr_list_runl)==0 ||
|
|
mftr->attr_list_runl.cnum==0)
|
|
mftr->attr_list_size = 0;
|
|
#endif
|
|
mftr->attr_list = NULL;
|
|
mftr->attr_list_len = 0;
|
|
}
|
|
}
|
|
#endif
|
|
|
|
if(find_attribute(mftr->mft, type, name,
|
|
&mftr->attr, &mftr->attr_size, &mftr->attr_len,
|
|
&mftr->attr_flag)
|
|
#ifndef NO_ATTRIBUTE_LIST
|
|
|| get_next_attribute_list(mftr, &mftr->attr_size)
|
|
#endif
|
|
)
|
|
{
|
|
#ifndef NO_ATTRIBUTE_LIST
|
|
if(!(mftr->attr_flag&ATTR_RESIDENT)){
|
|
init_run_list(mftr->attr, mftr->attr_len, &mftr->runl, &mftr->attr_inited);
|
|
if(mftr->attr_inited > mftr->attr_size)
|
|
mftr->attr_inited = mftr->attr_size;
|
|
if(get_next_run(&mftr->runl)==0) {
|
|
mftr->attr_flag |= ATTR_RESIDENT;
|
|
mftr->attr_len = 0;
|
|
}
|
|
} else
|
|
mftr->attr_inited = mftr->attr_size;
|
|
#endif
|
|
|
|
return 1;
|
|
}
|
|
|
|
mftr->attr_type = 0;
|
|
return 0;
|
|
}
|
|
|
|
static int get_run( RUNL *rl, int vcn, int *clp, int *lenp) {
|
|
if(rl->evcn < vcn)
|
|
return 0;
|
|
|
|
if(rl->vcn > vcn) {
|
|
rewind_run_list(rl);
|
|
get_next_run(rl);
|
|
}
|
|
|
|
while(rl->vcn+rl->clen <= vcn)
|
|
{
|
|
if(get_next_run(rl)==0)
|
|
return 0;
|
|
}
|
|
|
|
if(clp) *clp = rl->cnum == 0 ? 0 : rl->cnum + vcn - rl->vcn;
|
|
if(lenp) *lenp = rl->clen - vcn + rl->vcn;
|
|
return 1;
|
|
}
|
|
|
|
static int search_run(MFTR *mftr, int vcn) {
|
|
|
|
if( mftr->attr==NULL && !search_attribute(mftr, mftr->attr_type, mftr->attr_name))
|
|
return 0;
|
|
|
|
if(mftr->runl.svcn > vcn)
|
|
search_attribute(mftr, mftr->attr_type, mftr->attr_name);
|
|
|
|
#ifdef NO_ATTRIBUTE_LIST
|
|
if(mftr->runl.evcn < vcn)
|
|
return 0;
|
|
#else
|
|
while(mftr->runl.evcn < vcn) {
|
|
if(get_next_attribute_list(mftr, NULL)==0) {
|
|
mftr->attr = NULL;
|
|
return 0;
|
|
}
|
|
init_run_list(mftr->attr, mftr->attr_len, &mftr->runl, NULL);
|
|
if(get_next_run(&mftr->runl)==0) {
|
|
mftr->attr = NULL;
|
|
return 0;
|
|
}
|
|
}
|
|
#endif
|
|
|
|
return 1;
|
|
}
|
|
|
|
static int read_attribute(MFTR *mftr, int offset, char *buf, int len, RUNL *from_rl) {
|
|
int vcn;
|
|
int cnum, clen;
|
|
int done = 0;
|
|
int n;
|
|
RUNL *rl;
|
|
|
|
if(!from_rl && (mftr->attr_flag & ATTR_RESIDENT)) {
|
|
/* resident attribute */
|
|
if(offset > mftr->attr_len)
|
|
return 0;
|
|
if(offset+len > mftr->attr_len)
|
|
len = mftr->attr_len - offset;
|
|
memmove( buf, mftr->attr + offset, len);
|
|
return len;
|
|
}
|
|
|
|
vcn = offset / clustersize;
|
|
offset %= clustersize;
|
|
|
|
while(len>0) {
|
|
if(from_rl)
|
|
rl = from_rl;
|
|
else if(search_run(mftr, vcn) == 0)
|
|
break;
|
|
else
|
|
rl = &mftr->runl;
|
|
if(get_run(rl, vcn, &cnum, &clen) == 0)
|
|
break;
|
|
if(cnum==0 && from_rl)
|
|
break;
|
|
n = clen * clustersize - offset;
|
|
if(n > len) n = len;
|
|
if(cnum==0) {
|
|
memset( buf, 0, n);
|
|
} else if(!devread(cnum*(clustersize>>9)+(offset>>9), offset&0x1ff, n, buf))
|
|
break;
|
|
|
|
buf += n;
|
|
vcn += (offset+n)/clustersize;
|
|
done += n;
|
|
offset = 0;
|
|
len -= n;
|
|
}
|
|
return done;
|
|
}
|
|
|
|
static int read_mft_record(int mftno, char *mft, int self){
|
|
#ifdef DEBUG_NTFS
|
|
printf("Reading MFT record: mftno=%d\n", mftno);
|
|
#endif
|
|
if( read_attribute( mmft, mftno * mft_record_size,
|
|
mft, mft_record_size, self?mft_run:NULL) != mft_record_size)
|
|
return 0;
|
|
if(!fixup_record( mft, "FILE", mft_record_size))
|
|
return 0;
|
|
return 1;
|
|
}
|
|
|
|
#ifndef NO_NTFS_DECOMPRESSION
|
|
static int get_16_cluster(MFTR *mftr, int vcn) {
|
|
int n = 0, cnum, clen;
|
|
while(n < 16 && search_run(mftr, vcn) && get_run(&mftr->runl, vcn, &cnum, &clen) && cnum) {
|
|
if(clen > 16 - n)
|
|
clen = 16 - n;
|
|
vcn += clen;
|
|
while(clen--)
|
|
cluster16[n++] = cnum++;
|
|
}
|
|
cluster16[n] = 0;
|
|
return n;
|
|
}
|
|
|
|
static inline int compressed_block_size( unsigned char *src ) {
|
|
return 3 + (*(__u16 *)src & 0xfff);
|
|
}
|
|
|
|
static int decompress_block(unsigned char *dest, unsigned char *src) {
|
|
int head;
|
|
int copied=0;
|
|
unsigned char *last;
|
|
int bits;
|
|
int tag=0;
|
|
|
|
/* high bit indicates that compression was performed */
|
|
if(!(*(__u16 *)src & 0x8000)) {
|
|
memmove(dest,src+2,0x1000);
|
|
return 0x1000;
|
|
}
|
|
|
|
if((head = *(__u16 *)src & 0xFFF)==0)
|
|
/* block is not used */
|
|
return 0;
|
|
|
|
src += 2;
|
|
last = src+head;
|
|
bits = 0;
|
|
|
|
while(src<=last)
|
|
{
|
|
if(copied>4096)
|
|
{
|
|
#ifdef DEBUG_NTFS
|
|
printf("decompress error 1\n");
|
|
#endif
|
|
errnum = ERR_FSYS_CORRUPT;
|
|
return 0;
|
|
}
|
|
if(!bits){
|
|
tag=*(__u8 *)src;
|
|
bits=8;
|
|
src++;
|
|
if(src>last)
|
|
break;
|
|
}
|
|
if(tag & 1){
|
|
int i,len,delta,code,lmask,dshift;
|
|
code = *(__u16 *)src;
|
|
src+=2;
|
|
if(!copied)
|
|
{
|
|
#ifdef DEBUG_NTFS
|
|
printf("decompress error 2\n");
|
|
#endif
|
|
errnum = ERR_FSYS_CORRUPT;
|
|
return 0;
|
|
}
|
|
for(i=copied-1,lmask=0xFFF,dshift=12;i>=0x10;i>>=1)
|
|
{
|
|
lmask >>= 1;
|
|
dshift--;
|
|
}
|
|
delta = code >> dshift;
|
|
len = (code & lmask) + 3;
|
|
for(i=0; i<len; i++)
|
|
{
|
|
dest[copied]=dest[copied-delta-1];
|
|
copied++;
|
|
}
|
|
} else
|
|
dest[copied++]=*(__u8 *)src++;
|
|
tag>>=1;
|
|
bits--;
|
|
}
|
|
|
|
return copied;
|
|
}
|
|
#endif
|
|
|
|
int ntfs_read(char *buf, int len){
|
|
int ret;
|
|
#ifdef STAGE1_5
|
|
/* stage2 can't be resident/compressed/encrypted files,
|
|
* but does sparse flag, cause stage2 never sparsed
|
|
*/
|
|
if((cmft->attr_flag&~ATTR_SPARSE) != ATTR_NORMAL)
|
|
return 0;
|
|
disk_read_func = disk_read_hook;
|
|
ret = read_attribute(cmft, filepos, buf, len, 0);
|
|
disk_read_func = NULL;
|
|
filepos += ret;
|
|
#else
|
|
|
|
#ifndef NO_NTFS_DECOMPRESSION
|
|
int off;
|
|
int vcn;
|
|
int size;
|
|
int len0;
|
|
#endif
|
|
|
|
if(len<=0 || filepos >= cmft->attr_size || (cmft->attr_flag&ATTR_ENCRYPTED))
|
|
return 0;
|
|
|
|
if(filepos+len > cmft->attr_size)
|
|
len = cmft->attr_size - filepos;
|
|
if(filepos >= cmft->attr_inited) {
|
|
#ifdef DEBUG_NTFS
|
|
printf("reading uninitialized data 1\n");
|
|
#endif
|
|
memset(buf, 0, len);
|
|
return len;
|
|
} else if(filepos+len > cmft->attr_inited) {
|
|
len0 = len;
|
|
len = cmft->attr_inited - filepos;
|
|
len0 -= len;
|
|
} else
|
|
len0 = 0;
|
|
#ifdef DEBUG_NTFS
|
|
printf("read filepos=%x filemax=%x inited=%x len=%x len0=%x\n",filepos,filemax,cmft->attr_inited,len,len0);
|
|
#endif
|
|
|
|
if((cmft->attr_flag&(ATTR_COMPRESSED|ATTR_RESIDENT)) != ATTR_COMPRESSED) {
|
|
if(cmft->attr_flag==ATTR_NORMAL)
|
|
disk_read_func = disk_read_hook;
|
|
ret = read_attribute(cmft, filepos, buf, len, 0);
|
|
if(cmft->attr_flag==ATTR_NORMAL)
|
|
disk_read_func = NULL;
|
|
filepos += ret;
|
|
if(ret==len && len0) {
|
|
memset(buf+len, 0, len0);
|
|
filepos += len0;
|
|
ret += len0;
|
|
}
|
|
return ret;
|
|
}
|
|
|
|
ret = 0;
|
|
|
|
#ifndef NO_NTFS_DECOMPRESSION
|
|
/* NTFS don't support compression if cluster size > 4k */
|
|
if(clustersize > 4096) {
|
|
errnum = ERR_FSYS_CORRUPT;
|
|
return 0;
|
|
}
|
|
|
|
while(len > 0){
|
|
#ifdef DEBUG_NTFS
|
|
printf("Reading filepos=%x len=%x\n", filepos, len);
|
|
#endif
|
|
if(filepos >= dcoff && filepos < (dcoff+dclen)) {
|
|
#ifdef DEBUG_NTFS
|
|
printf("decompress cache %x+%x\n", dcoff, dclen);
|
|
#endif
|
|
size = dcoff + dclen - filepos;
|
|
if(size > len) size = len;
|
|
memmove( buf, dcdbuf + filepos - dcoff, size);
|
|
filepos += size;
|
|
len -= size;
|
|
ret += size;
|
|
buf += size;
|
|
if(len==0) {
|
|
if(len0) {
|
|
#ifdef DEBUG_NTFS
|
|
printf("reading uninitialized data 2\n");
|
|
#endif
|
|
memset(buf, 0, len0);
|
|
filepos += len0;
|
|
ret += len0;
|
|
}
|
|
return ret;
|
|
}
|
|
}
|
|
|
|
vcn = filepos / clustersize / 16;
|
|
vcn *= 16;
|
|
off = filepos % (16 * clustersize);
|
|
if( dcvcn != vcn || filepos < dcoff)
|
|
dcrem = 0;
|
|
|
|
#ifdef DEBUG_NTFS
|
|
printf("vcn %x off %x dcrem %x\n", vcn, off, dcrem);
|
|
#endif
|
|
if(dcrem) {
|
|
int head;
|
|
|
|
/* reading source */
|
|
if(dcslen < 2 || compressed_block_size(dcsptr) > dcslen) {
|
|
if(cluster16[index16]==0) {
|
|
errnum = ERR_FSYS_CORRUPT;
|
|
return ret;
|
|
}
|
|
if(dcslen)
|
|
memmove(dcsbuf, dcsptr, dcslen);
|
|
dcsptr = dcsbuf;
|
|
while((dcslen+clustersize) < DECOMP_SOURCE_BUFFER_SIZE) {
|
|
if(cluster16[index16]==0)
|
|
break;
|
|
#ifdef DEBUG_NTFS
|
|
printf("reading dcslen=%x cluster %x\n", dcslen, cluster16[index16]);
|
|
#endif
|
|
if(!devread(cluster16[index16]*(clustersize>>9), 0, clustersize, dcsbuf+dcslen))
|
|
return ret;
|
|
dcslen += clustersize;
|
|
index16++;
|
|
}
|
|
}
|
|
/* flush destination */
|
|
dcoff += dclen;
|
|
dclen = 0;
|
|
|
|
while(dcrem && dclen < DECOMP_DEST_BUFFER_SIZE &&
|
|
dcslen >= 2 && (head=compressed_block_size(dcsptr)) <= dcslen) {
|
|
size = decompress_block(dcdbuf+dclen, dcsptr);
|
|
if(dcrem>=0x1000 && size!=0x1000) {
|
|
errnum = ERR_FSYS_CORRUPT;
|
|
return ret;
|
|
}
|
|
dcrem -= size;
|
|
dclen += size;
|
|
dcsptr += head;
|
|
dcslen -= head;
|
|
}
|
|
continue;
|
|
}
|
|
dclen = dcrem = 0;
|
|
#ifdef DEBUG_NTFS
|
|
printf("get next 16 clusters\n");
|
|
#endif
|
|
switch(get_16_cluster(cmft, vcn)) {
|
|
case 0:
|
|
#ifdef DEBUG_NTFS
|
|
printf("sparse\n");
|
|
#endif
|
|
/* sparse */
|
|
size = 16 * clustersize - off;
|
|
if( len < size )
|
|
size = len;
|
|
#ifndef STAGE1_5
|
|
memset( buf, 0, size);
|
|
#endif
|
|
filepos += size;
|
|
len -= size;
|
|
ret += size;
|
|
buf += size;
|
|
break;
|
|
|
|
case 16:
|
|
#ifdef DEBUG_NTFS
|
|
printf("uncompressed\n");
|
|
#endif
|
|
/* uncompressed */
|
|
index16 = off / clustersize;
|
|
off %= clustersize;
|
|
while(index16 < 16) {
|
|
size = clustersize - off;
|
|
if( len < size )
|
|
size = len;
|
|
if(!devread(cluster16[index16]*(clustersize>>9)+(off>>9), off&0x1ff, size, buf))
|
|
return ret;
|
|
filepos += size;
|
|
len -= size;
|
|
ret += size;
|
|
if(len==0)
|
|
return ret;
|
|
off = 0;
|
|
buf += size;
|
|
index16++;
|
|
}
|
|
break;
|
|
|
|
default:
|
|
#ifdef DEBUG_NTFS
|
|
printf("compressed\n");
|
|
#endif
|
|
index16 = 0;
|
|
dcvcn = vcn;
|
|
dcoff = vcn * clustersize;
|
|
dcrem = cmft->attr_inited - dcoff;
|
|
if(dcrem > 16 * clustersize)
|
|
dcrem = 16 * clustersize;
|
|
dcsptr = dcsbuf;
|
|
dcslen = 0;
|
|
}
|
|
}
|
|
if(len0) {
|
|
#ifdef DEBUG_NTFS
|
|
printf("reading uninitialized data 3\n");
|
|
#endif
|
|
memset(buf, 0, len0);
|
|
filepos += len0;
|
|
ret += len0;
|
|
}
|
|
#else
|
|
errnum = FSYS_CORRUPT;
|
|
#endif /*NO_NTFS_DECOMPRESSION*/
|
|
#endif /*STAGE1_5*/
|
|
return ret;
|
|
}
|
|
|
|
int ntfs_mount (void)
|
|
{
|
|
char *sb = (char *)FSYS_BUF;
|
|
int mft_record;
|
|
int spc;
|
|
|
|
if (((current_drive & 0x80) || (current_slice != 0))
|
|
&& (current_slice != /*PC_SLICE_TYPE_NTFS*/7)
|
|
&& (current_slice != /*PC_SLICE_TYPE_NTFS*/0x17))
|
|
return 0;
|
|
|
|
if (!devread (0, 0, 512, (char *) FSYS_BUF))
|
|
return 0; /* Cannot read superblock */
|
|
|
|
if(sb[3]!='N' || sb[4]!='T' || sb[5]!='F' || sb[6]!='S')
|
|
return 0;
|
|
blocksize = *(__u16 *)(sb+0xb);
|
|
spc = *(unsigned char *)(sb+0xd);
|
|
clustersize = spc * blocksize;
|
|
mft_record_size = *(char *)(sb+0x40);
|
|
index_record_size = *(char *)(sb+0x44);
|
|
if(mft_record_size>0)
|
|
mft_record_size *= clustersize;
|
|
else
|
|
mft_record_size = 1 << (-mft_record_size);
|
|
|
|
index_record_size *= clustersize;
|
|
mft_record = *(__u32 *)(sb+0x30); /* only support 32 bit */
|
|
spc = clustersize / 512;
|
|
|
|
if(mft_record_size > MAX_MFT_RECORD_SIZE || index_record_size > MAX_INDEX_RECORD_SIZE) {
|
|
/* only support 1k MFT record, 4k INDEX record */
|
|
return 0;
|
|
}
|
|
|
|
#ifdef DEBUG_NTFS
|
|
printf("spc=%x mft_record=%x:%x\n", spc, *(__s64 *)(sb+0x30));
|
|
#endif
|
|
|
|
if (!devread (mft_record*spc, 0, mft_record_size, mmft->mft))
|
|
return 0; /* Cannot read superblock */
|
|
|
|
if(!fixup_record( mmft->mft, "FILE", mft_record_size))
|
|
return 0;
|
|
|
|
#ifndef NO_ALTERNATE_DATASTREAM
|
|
is_ads_completion = 0;
|
|
#endif
|
|
if(!search_attribute(mmft, at_data, NONAME)) return 0;
|
|
|
|
*mft_run = mmft->runl;
|
|
|
|
*path_ino = FILE_ROOT;
|
|
|
|
return 1;
|
|
}
|
|
|
|
int
|
|
ntfs_dir (char *dirname)
|
|
{
|
|
char *rest, ch;
|
|
int namelen;
|
|
int depth = 0;
|
|
int chk_sfn = 1;
|
|
int flag = 0;
|
|
int record_offset;
|
|
int my_index_record_size;
|
|
unsigned char *index_entry = 0, *entry, *index_end;
|
|
int i;
|
|
|
|
/* main loop to find desired directory entry */
|
|
loop:
|
|
|
|
#ifdef DEBUG_NTFS
|
|
printf("dirname=%s\n", dirname);
|
|
#endif
|
|
if(!read_mft_record(path_ino[depth], cmft->mft, 0))
|
|
{
|
|
#ifdef DEBUG_NTFS
|
|
printf("MFT error 1\n");
|
|
#endif
|
|
errnum = ERR_FSYS_CORRUPT;
|
|
return 0;
|
|
}
|
|
|
|
/* if we have a real file (and we're not just printing possibilities),
|
|
then this is where we want to exit */
|
|
|
|
if (!*dirname || isspace (*dirname) || *dirname==':')
|
|
{
|
|
#ifndef STAGE1_5
|
|
#ifndef NO_ALTERNATE_DATASTREAM
|
|
if (*dirname==':' && print_possibilities) {
|
|
char *tmp;
|
|
|
|
/* preparing ADS name completion */
|
|
for(tmp = dirname; *tmp != '/'; tmp--);
|
|
for(tmp++, rest=fnbuf; *tmp && !isspace(*tmp); *rest++ = *tmp++)
|
|
if(*tmp==':') dirname = rest;
|
|
*rest++ = '\0';
|
|
|
|
is_ads_completion = 1;
|
|
search_attribute(cmft, at_data, dirname+1);
|
|
is_ads_completion = 0;
|
|
|
|
if(errnum==0) {
|
|
if(print_possibilities < 0)
|
|
return 1;
|
|
errnum = ERR_FILE_NOT_FOUND;
|
|
}
|
|
return 0;
|
|
}
|
|
#endif
|
|
#endif
|
|
|
|
if (*dirname==':') dirname++;
|
|
for (rest = dirname; (ch = *rest) && !isspace (ch); rest++);
|
|
*rest = 0;
|
|
|
|
#ifdef DEBUG_NTFS
|
|
printf("got file: search at_data\n");
|
|
#endif
|
|
|
|
if (!search_attribute(cmft, at_data, dirname)) {
|
|
errnum = *(dirname-1)==':'?ERR_FILE_NOT_FOUND:ERR_BAD_FILETYPE;
|
|
*rest = ch;
|
|
return 0;
|
|
}
|
|
*rest = ch;
|
|
|
|
filemax = cmft->attr_size;
|
|
#ifdef DEBUG_NTFS
|
|
printf("filemax=%x\n", filemax);
|
|
#endif
|
|
return 1;
|
|
}
|
|
|
|
if(depth >= (MAX_DIR_DEPTH-1)) {
|
|
errnum = ERR_FSYS_CORRUPT;
|
|
return 0;
|
|
}
|
|
|
|
/* continue with the file/directory name interpretation */
|
|
|
|
while (*dirname == '/')
|
|
dirname++;
|
|
|
|
for (rest = dirname; (ch = *rest) && !isspace (ch) && ch != '/' && ch != ':'; rest++);
|
|
|
|
*rest = 0;
|
|
|
|
if (!search_attribute(cmft, at_index_root, "$I30"))
|
|
{
|
|
errnum = ERR_BAD_FILETYPE;
|
|
return 0;
|
|
}
|
|
|
|
read_attribute(cmft, 0, fnbuf, 16, 0);
|
|
my_index_record_size = *(__u32 *)(fnbuf+8);
|
|
|
|
if(my_index_record_size > MAX_INDEX_RECORD_SIZE) {
|
|
errnum = ERR_FSYS_CORRUPT;
|
|
return 0;
|
|
}
|
|
|
|
#ifdef DEBUG_NTFS
|
|
printf("index_record_size=%x\n", my_index_record_size);
|
|
#endif
|
|
|
|
if(cmft->attr_size > MAX_INDEX_RECORD_SIZE) {
|
|
errnum = ERR_FSYS_CORRUPT;
|
|
return 0;
|
|
}
|
|
read_attribute(cmft, 0, index_data, cmft->attr_size, 0);
|
|
index_end = index_data + cmft->attr_size;
|
|
index_entry = index_data + 0x20;
|
|
record_offset = -1;
|
|
|
|
#ifndef STAGE1_5
|
|
if (print_possibilities && ch != '/' && ch != ':' && !*dirname)
|
|
{
|
|
print_possibilities = -print_possibilities;
|
|
/* fake '.' for empty directory */
|
|
print_a_completion (".");
|
|
}
|
|
#endif
|
|
|
|
if (search_attribute(cmft, at_bitmap, "$I30")) {
|
|
if(cmft->attr_size > MAX_INDEX_BITMAP_SIZE) {
|
|
errnum = ERR_FSYS_CORRUPT;
|
|
return 0;
|
|
}
|
|
|
|
read_attribute(cmft, 0, bitmap_data, cmft->attr_size, 0);
|
|
|
|
if (search_attribute(cmft, at_index_allocation, "$I30")==0) {
|
|
errnum = ERR_FSYS_CORRUPT;
|
|
return 0;
|
|
}
|
|
|
|
for(record_offset = 0; record_offset*my_index_record_size<cmft->attr_size; record_offset++){
|
|
int bit = 1 << (record_offset&3);
|
|
int byte = record_offset>>3;
|
|
#ifdef DEBUG_NTFS
|
|
printf("record_offset=%x\n", record_offset);
|
|
#endif
|
|
if((bitmap_data[byte]&bit))
|
|
break;
|
|
}
|
|
|
|
if(record_offset*my_index_record_size>=cmft->attr_size) record_offset = -1;
|
|
}
|
|
|
|
do
|
|
{
|
|
entry = index_entry; index_entry += *(__u16 *)(entry+8);
|
|
if(entry+0x50>=index_entry||entry>=index_end||
|
|
index_entry>=index_end||(entry[0x12]&2)){
|
|
if(record_offset < 0 ||
|
|
!read_attribute(cmft, record_offset*my_index_record_size, index_data, my_index_record_size, 0)){
|
|
if (!errnum)
|
|
{
|
|
if (print_possibilities < 0)
|
|
{
|
|
#if 0
|
|
putchar ('\n');
|
|
#endif
|
|
return 1;
|
|
}
|
|
|
|
errnum = ERR_FILE_NOT_FOUND;
|
|
*rest = ch;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
if(!fixup_record( index_data, "INDX", my_index_record_size))
|
|
{
|
|
#ifdef DEBUG_NTFS
|
|
printf("index error\n");
|
|
#endif
|
|
errnum = ERR_FSYS_CORRUPT;
|
|
return 0;
|
|
}
|
|
entry = index_data + 0x18 + *(__u16 *)(index_data+0x18);
|
|
index_entry = entry + *(__u16 *)(entry+8);
|
|
index_end = index_data + my_index_record_size - 0x52;
|
|
for(record_offset++; record_offset*my_index_record_size<cmft->attr_size; record_offset++){
|
|
int bit = 1 << (record_offset&3);
|
|
int byte = record_offset>>3;
|
|
if((bitmap_data[byte]&bit)) break;
|
|
}
|
|
if(record_offset*my_index_record_size>=cmft->attr_size) record_offset = -1;
|
|
#ifdef DEBUG_NTFS
|
|
printf("record_offset=%x\n", record_offset);
|
|
#endif
|
|
}
|
|
flag = entry[0x51];
|
|
path_ino[depth+1] = *(__u32 *)entry;
|
|
if(path_ino[depth+1] < 16)
|
|
continue;
|
|
namelen = entry[0x50];
|
|
//if(index_data[0x48]&2) printf("hidden file\n");
|
|
#ifndef STAGE1_5
|
|
/* skip short file name */
|
|
if( flag == 2 && print_possibilities && ch != '/' && ch != ':' )
|
|
continue;
|
|
#endif
|
|
|
|
for( i = 0, entry+=0x52; i < namelen; i++, entry+=2 )
|
|
{
|
|
int c = *(__u16 *)entry;
|
|
if(c==' '||c>=0x100)
|
|
fnbuf[i] = '_';
|
|
else
|
|
fnbuf[i] = c;
|
|
}
|
|
fnbuf[namelen] = 0;
|
|
#ifdef DEBUG_NTFS
|
|
printf("FLAG: %d NAME: %s inum=%d\n", flag,fnbuf,path_ino[depth+1]);
|
|
#endif
|
|
|
|
//uncntrl(fnbuf);
|
|
|
|
chk_sfn = nsubstring(dirname,fnbuf);
|
|
#ifndef STAGE1_5
|
|
if (print_possibilities && ch != '/' && ch != ':'
|
|
&& (!*dirname || chk_sfn <= 0))
|
|
{
|
|
if (print_possibilities > 0)
|
|
print_possibilities = -print_possibilities;
|
|
print_a_completion (fnbuf);
|
|
}
|
|
#endif /* STAGE1_5 */
|
|
}
|
|
while (chk_sfn != 0 ||
|
|
(print_possibilities && ch != '/' && ch != ':'));
|
|
|
|
*(dirname = rest) = ch;
|
|
|
|
depth++;
|
|
|
|
/* go back to main loop at top of function */
|
|
goto loop;
|
|
}
|
|
|
|
#ifdef DEBUG_NTFS
|
|
int dump_block(char *msg, char *buf, int size){
|
|
int l = (size+15)/16;
|
|
int off;
|
|
int i, j;
|
|
int c;
|
|
printf("----- %s -----\n", msg);
|
|
for( i = 0, off = 0; i < l; i++, off+=16)
|
|
{
|
|
if(off<16)
|
|
printf("000%x:", off);
|
|
else if(off<256)
|
|
printf("00%x:", off);
|
|
else
|
|
printf("0%x:", off);
|
|
for(j=0;j<16;j++)
|
|
{
|
|
c = buf[off+j]&0xff;
|
|
if( c >= 16 )
|
|
printf("%c%x",j==8?'-':' ',c);
|
|
else
|
|
printf("%c0%x",j==8?'-':' ',c);
|
|
}
|
|
printf(" ");
|
|
for(j=0;j<16;j++) {
|
|
char c = buf[off+j];
|
|
printf("%c",c<' '||c>='\x7f'?'.':c);
|
|
}
|
|
printf("\n");
|
|
}
|
|
}
|
|
#endif
|
|
#endif /* FSYS_NTFS */
|