blob: 54b9c7f872fd68b28d94bf8bb96d069ad8f3143e [file] [log] [blame] [raw]
/*
* NTFS file system driver for GRUB
*
* Copyright (C) 2007 Bean (bean123@126.com)
*
* 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., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
#ifdef FSYS_NTFS
#include "shared.h"
#include "filesys.h"
//#define NTFS_DEBUG 1
#ifdef FS_UTIL
#include "fsutil.h"
#endif
#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_OBJECT_ID 0x40
#define AT_SECURITY_DESCRIPTOR 0x50
#define AT_VOLUME_NAME 0x60
#define AT_VOLUME_INFORMATION 0x70
#define AT_DATA 0x80
#define AT_INDEX_ROOT 0x90
#define AT_INDEX_ALLOCATION 0xA0
#define AT_BITMAP 0xB0
#define AT_SYMLINK 0xC0
#define AT_EA_INFORMATION 0xD0
#define AT_EA 0xE0
#define ATTR_READ_ONLY 0x1
#define ATTR_HIDDEN 0x2
#define ATTR_SYSTEM 0x4
#define ATTR_ARCHIVE 0x20
#define ATTR_DEVICE 0x40
#define ATTR_NORMAL 0x80
#define ATTR_TEMPORARY 0x100
#define ATTR_SPARSE 0x200
#define ATTR_REPARSE 0x400
#define ATTR_COMPRESSED 0x800
#define ATTR_OFFLINE 0x1000
#define ATTR_NOT_INDEXED 0x2000
#define ATTR_ENCRYPTED 0x4000
#define ATTR_DIRECTORY 0x10000000
#define ATTR_INDEX_VIEW 0x20000000
#define FLAG_COMPRESSED 1
#define FLAG_ENCRYPTED 0x4000
#define FLAG_SPARSE 0x8000
#define BLK_SHR 9
#define MAX_MFT (1024 >> BLK_SHR)
#define MAX_IDX (16384 >> BLK_SHR)
#define MAX_SPC (4096 >> BLK_SHR)
#define valueat(buf,ofs,type) *((type*)(((char*)buf)+ofs))
static unsigned long mft_size,idx_size,spc,blocksize,mft_start;
static unsigned long mem_mask1,mem_mask2;
char* mmft_run;
typedef struct {
unsigned long target_vcn,curr_vcn,next_vcn,curr_lcn;
unsigned long cbuf_ofs,cbuf_lcn;
unsigned long cpb,blk_size,flags;
char *cbuf,*mft,*cur_run;
} read_ctx;
#define mmft ((char*)FSYS_BUF)
#define emft_ofs valueat(cur_mft,4,unsigned short)
#define ebuf_ofs valueat(cur_mft,6,unsigned short)
#define data_ofs valueat(cur_mft,8,unsigned short)
#define attr_cur valueat(cur_mft,0xA,unsigned short)
#define attr_als valueat(cur_mft,0xC,unsigned short)
#define attr_end valueat(cur_mft,0xE,unsigned short)
#define save_pos valueat(cur_mft,0x10,unsigned long)
#define VAR_LEN 16
#ifdef NTFS_DEBUG
#define dbg_printf printf
#else
#define dbg_printf if (0) printf
#endif
#ifdef NTFS_DEBUG
static void dump(char* buf,int len)
{
int ofs;
ofs=0;
while (len>0)
{
int cnt,i;
if (ofs<16)
printf("000%X: ",ofs);
else if (ofs<256)
printf("00%X: ",ofs);
else
printf("0%X: ",ofs);
cnt=16;
if (cnt>len)
cnt=len;
for (i=0;i<cnt;i++)
{
if ((unsigned char)buf[ofs+i]<16)
printf("0%X ",(unsigned char)buf[ofs+i]);
else
printf("%X ",(unsigned char)buf[ofs+i]);
if ((i!=15) && ((i & 3)==3))
printf(" ");
}
for (;i<16;i++)
{
printf(" ");
if ((i!=15) && ((i & 3)==3))
printf(" ");
}
printf("; ");
for (i=0;i<cnt;i++)
printf("%c",((buf[ofs+i]>=32) && (buf[ofs+i]<127))?buf[ofs+i]:'.');
printf("\n");
ofs+=16;
len-=cnt;
}
}
#endif
#define ofs2ptr(a) (mmft+(a))
#define ptr2ofs(a) ((unsigned short)((a)-mmft))
static unsigned short get_mem(int len)
{
unsigned long mask,i;
mask=(1<<len)-1;
for (i=0;i<32-len;i+=len,mask<<=len)
if ((mem_mask1 & mask)==0)
{
mem_mask1|= mask;
return (i << BLK_SHR);
}
mask=(1<<len)-1;
for (i=0;i<32-len;i+=len,mask<<=len)
if ((mem_mask2 & mask)==0)
{
mem_mask2|= mask;
return ((i+32) << BLK_SHR);
}
dbg_printf("No enough memory\n");
return 0;
}
static void free_mem(unsigned short mem,int len)
{
int blk;
blk=mem >> BLK_SHR;
if (blk<32)
mem_mask1 &= ~(((1<<len)-1)<<blk);
else
mem_mask2 &= ~(((1<<len)-1)<<(blk-32));
}
static int fixup(char* buf,int len,char* magic)
{
int ss;
char *pu;
unsigned us;
if (valueat(buf,0,unsigned long)!=valueat(magic,0,unsigned long))
{
dbg_printf("%s label not found\n",magic);
return 0;
}
ss=valueat(buf,6,unsigned short)-1;
if (ss*blocksize!=len*512)
{
dbg_printf("Size not match %d!=%d\n",ss*blocksize,len*512);
return 0;
}
pu=buf+valueat(buf,4,unsigned short);
us=valueat(pu,0,unsigned short);
buf-=2;
while (ss>0)
{
buf+=blocksize;
pu+=2;
if (valueat(buf,0,unsigned short)!=us)
{
dbg_printf("Fixup signature not match\n");
return 0;
}
valueat(buf,0,unsigned short)=valueat(pu,0,unsigned short);
ss--;
}
return 1;
}
static void init_attr(char* cur_mft)
{
attr_end=ptr2ofs(cur_mft+valueat(cur_mft,0x14,unsigned short));
attr_als=0;
if (emft_ofs)
{
free_mem(emft_ofs,mft_size);
emft_ofs=0;
}
if (ebuf_ofs)
{
free_mem(ebuf_ofs,spc);
ebuf_ofs=0;
}
if (data_ofs)
{
free_mem(data_ofs,8);
data_ofs=0;
}
}
static int read_mft(char* cur_mft,unsigned long mftno);
static char* find_attr_vcn(char* cur_mft,char* attr,unsigned long vcn);
static char* read_run_list(read_ctx* ctx,char* run);
static int read_attr(char* cur_mft,unsigned char attr,char* dest,unsigned long ofs,unsigned long len,int cached);
static char* find_attr(char* cur_mft,unsigned char attr)
{
char* save;
if (emft_ofs)
{
back:
while (attr_als<attr_end)
{
attr_cur=attr_als;
attr_als+=valueat(ofs2ptr(attr_als),4,unsigned short);
save=ofs2ptr(attr_cur);
if (((unsigned char)*save==attr) || (attr==0))
{
char *new_pos,*emft;
emft=ofs2ptr(emft_ofs);
if (cur_mft==mmft)
{
devread(valueat(save,0x10,unsigned long),0,mft_size << BLK_SHR,emft);
if (! fixup(emft,mft_size,"FILE"))
{
dbg_printf("Invalid MFT at 0x%X",valueat(save,0x10,unsigned long));
return NULL;
}
}
else
{
if (! read_mft(emft,valueat(save,0x10,unsigned long)))
return NULL;
}
new_pos=&emft[valueat(emft,0x14,unsigned short)];
while ((unsigned char)*new_pos!=0xFF)
{
if (((unsigned char)*new_pos==(unsigned char)*save) &&
(valueat(new_pos,0xE,unsigned short)==valueat(save,0x18,unsigned short)))
{
return new_pos;
}
new_pos+=valueat(new_pos,4,unsigned long);
}
dbg_printf("Can\'t find 0x%X in attribute list\n",(unsigned char)*save);
return NULL;
}
}
return NULL;
}
save=ofs2ptr(attr_end);
while ((unsigned char)*save!=0xFF)
{
attr_cur=attr_end;
attr_end+=valueat(save,4,unsigned long);
if ((unsigned char)*save==AT_ATTRIBUTE_LIST)
attr_als=attr_cur;
if (((unsigned char)*save==attr) || (attr==0))
return save;
save=ofs2ptr(attr_end);
}
if (attr_als)
{
unsigned short new_ofs;
if ((new_ofs=get_mem(mft_size))==0)
return NULL;
save=ofs2ptr(attr_als);
if (save[8])
{
if (valueat(save,0x30,unsigned long)>(spc << BLK_SHR))
{
dbg_printf("Non-resident attribute list too large\n");
return NULL;
}
if ((ebuf_ofs=get_mem(spc))==0)
return NULL;
attr_cur=attr_als;
if (! read_attr(cur_mft,AT_ATTRIBUTE_LIST,ofs2ptr(ebuf_ofs),0,(spc<<BLK_SHR),0))
{
dbg_printf("Fail to read non-resident attribute list\n");
return NULL;
}
attr_als=ebuf_ofs;
attr_end=ebuf_ofs+valueat(save,0x30,unsigned long);
}
else
{
attr_end=attr_als+valueat(save,4,unsigned long);
attr_als+=valueat(save,0x14,unsigned short);
}
emft_ofs=new_ofs;
if (cur_mft==mmft)
{
unsigned short save_als;
save_als=attr_als;
while (attr_als<attr_end)
{
attr_cur=attr_als;
save=ofs2ptr(attr_als);
attr_als+=valueat(ofs2ptr(attr_als),4,unsigned short);
if ((unsigned char)*save==AT_DATA)
{
unsigned short new_als,keep_als;
keep_als=attr_als;
valueat(save,0x10,unsigned long)=mft_start;
new_als=attr_als;
while (new_als<attr_end)
{
unsigned long vcn,mft;
read_ctx ctx;
char *pp,*aa;
pp=ofs2ptr(new_als);
new_als+=valueat(ofs2ptr(new_als),4,unsigned short);
if ((unsigned char)*pp!=AT_DATA)
break;
mft=valueat(pp,0x10,unsigned long);
vcn=(mft*mft_size) / spc;
attr_als=keep_als;
aa=find_attr_vcn(cur_mft,save,vcn);
if (aa==save)
aa=mmft_run;
vcn-=valueat(aa,0x10,unsigned long);
memset(&ctx,0,sizeof(ctx));
aa+=valueat(aa,0x20,unsigned short);
while ((aa=read_run_list(&ctx,aa))!=NULL)
{
if ((vcn<ctx.next_vcn) || (*aa==0))
break;
}
if (vcn<ctx.next_vcn)
valueat(pp,0x10,unsigned long)=(ctx.curr_lcn+vcn-ctx.curr_vcn)*spc+((mft*mft_size) % spc);
else
{
dbg_printf("Can\'t locate MFT\n");
return NULL;
}
}
break;
}
}
attr_als=save_als;
}
goto back;
}
return NULL;
}
static char* find_attr_vcn(char* cur_mft,char* attr,unsigned long vcn)
{
char *pp;
int mod;
if ((! emft_ofs) || (! vcn))
return attr;
mod=0;
pp=ofs2ptr(attr_als);
while ((attr_als<attr_end) &&
(*pp==*attr) &&
(vcn >= valueat(pp,0x8,unsigned long)))
{
attr_cur=attr_als;
attr_als+=valueat(pp,4,unsigned short);
mod=1;
pp=ofs2ptr(attr_als);
}
if (mod)
{
attr_als=attr_cur;
return find_attr(cur_mft,*attr);
}
else
return attr;
}
static int decompress_getch(read_ctx* ctx)
{
if (ctx->cbuf_ofs>=(spc << BLK_SHR))
{
devread((ctx->cbuf_lcn++)*spc,0,spc << BLK_SHR,ctx->cbuf);
ctx->cbuf_ofs=0;
}
return (unsigned char)ctx->cbuf[ctx->cbuf_ofs++];
}
// Decompress a block (4096 bytes)
static int decompress_block(read_ctx *ctx,char* dest)
{
unsigned short flg,cnt;
flg=decompress_getch(ctx);
flg+=decompress_getch(ctx)*256;
cnt=(flg & 0xFFF)+1;
if (dest)
{
if (flg & 0x8000)
{
unsigned long bits,copied,tag;
bits=copied=tag=0;
while (cnt > 0)
{
if (copied > 4096)
{
dbg_printf("B1\n");
return 0;
}
if (! bits)
{
tag = decompress_getch(ctx);
bits = 8;
cnt--;
if (cnt<=0)
break;
}
if (tag & 1)
{
unsigned long i, len, delta, code, lmask, dshift;
code=decompress_getch(ctx);
code+=decompress_getch(ctx)*256;
cnt-=2;
if (! copied)
{
dbg_printf("B2\n");
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++] = decompress_getch(ctx);
cnt--;
}
tag >>= 1;
bits--;
}
return 1;
}
else
{
if (cnt!=4096)
{
dbg_printf("B3\n");
return 0;
}
}
}
while (cnt>0)
{
int n;
n=(spc << BLK_SHR) - ctx->cbuf_ofs;
if (n>cnt)
n=cnt;
if ((dest) && (n))
{
memcpy(dest,&ctx->cbuf[ctx->cbuf_ofs],n);
dest+=n;
}
cnt-=n;
ctx->cbuf_ofs+=n;
if (cnt)
{
devread((ctx->cbuf_lcn++)*spc,0,spc << BLK_SHR,ctx->cbuf);
ctx->cbuf_ofs=0;
}
}
return 1;
}
static char* get_extra_run(char* cur_mft)
{
char* cur_pos;
cur_pos=find_attr(cur_mft,(unsigned char)*ofs2ptr(attr_cur));
if (cur_pos==NULL)
return NULL;
if (cur_pos[8]==0)
{
dbg_printf("$DATA should be non-resident\n");
return NULL;
}
return cur_pos;
}
static char* read_run_data(char* run,int nn,unsigned long* val,int sig)
{
unsigned long r, v;
r = 0;
v = 1;
while (nn--)
{
r += v * (*(unsigned char *)(run++));
v <<= 8;
}
if ((sig) && (r & (v>>1)))
r -=v;
*val=r;
return run;
}
#define RF_BLANK 1
#define RF_COMP_FILE 2
#define RF_COMP_BLOCK 4
#define set_flag(a,b) if (b) ctx->flags|=a; else ctx->flags&=~(a);
#define get_flag(a) (ctx->flags & a)
static char* read_run_list(read_ctx* ctx,char* run)
{
int c1,c2;
unsigned long val;
back:
c1=((unsigned char)(*run) & 0xF);
c2=((unsigned char)(*run) >> 4);
if (! c1)
{
char *cur_mft;
cur_mft=ctx->mft;
if ((cur_mft) && (emft_ofs))
{
void (*save_hook)(unsigned long, unsigned long, unsigned long);
save_hook=disk_read_func;
disk_read_func=NULL;
run=get_extra_run(cur_mft);
disk_read_func=save_hook;
if (run)
{
run+=valueat(run,0x20,unsigned short);
ctx->curr_lcn=0;
goto back;
}
}
dbg_printf("Run list overflow\n");
return NULL;
}
run=read_run_data(run+1,c1,&val,0); // length of current VCN
ctx->curr_vcn=ctx->next_vcn;
ctx->next_vcn+=val;
run=read_run_data(run,c2,&val,1); // offset to previous LCN
ctx->curr_lcn+=val;
set_flag(RF_BLANK,val==0);
return run;
}
static int read_block(read_ctx* ctx,char* buf,int num)
{
while (num)
{
if (get_flag(RF_COMP_BLOCK))
{
while ((num) && (ctx->target_vcn<(ctx->next_vcn & ~0xF) +16))
{
if (! decompress_block(ctx,buf))
return 0;
if (buf)
buf+=ctx->blk_size;
ctx->target_vcn+=ctx->cpb;
num--;
}
if (num==0)
break;
ctx->cur_run=read_run_list(ctx,ctx->cur_run);
if (ctx->cur_run==NULL)
return 0;
if ((! get_flag(RF_BLANK)) || (ctx->target_vcn<=ctx->curr_vcn) || (ctx->target_vcn>ctx->next_vcn))
{
dbg_printf("A1\n");
return 0;
}
if (ctx->target_vcn==ctx->next_vcn)
{
ctx->cur_run=read_run_list(ctx,ctx->cur_run);
if (ctx->cur_run==NULL)
return 0;
set_flag(RF_COMP_BLOCK,ctx->next_vcn - ctx->target_vcn<16);
}
else
{
if (ctx->next_vcn - ctx->target_vcn<16)
{
dbg_printf("A2\n");
return 0;
}
set_flag(RF_COMP_BLOCK,0);
}
}
else
{
int nn;
if (get_flag(RF_COMP_FILE))
nn=((ctx->next_vcn & ~0xF)-ctx->target_vcn) / ctx->cpb;
else
nn=(ctx->next_vcn - ctx->target_vcn) / ctx->cpb;
if (nn>num)
nn=num;
if (nn)
{
if (buf)
{
if (get_flag(RF_BLANK))
memset(buf,0,nn* ctx->blk_size);
else
devread((ctx->target_vcn - ctx->curr_vcn + ctx->curr_lcn)*spc,0,nn* ctx->blk_size,buf);
buf+=nn* ctx->blk_size;
}
ctx->target_vcn+=nn* ctx->cpb;
num-=nn;
if (num==0)
break;
}
if (ctx->target_vcn>=ctx->next_vcn)
{
ctx->cur_run=read_run_list(ctx,ctx->cur_run);
if (ctx->cur_run==NULL)
return 0;
}
set_flag(RF_COMP_BLOCK,(get_flag(RF_COMP_FILE)) && (ctx->next_vcn - ctx->target_vcn<16));
}
if (get_flag(RF_COMP_BLOCK))
{
ctx->cbuf_ofs=spc << BLK_SHR;
ctx->cbuf_lcn=ctx->curr_lcn+(ctx->target_vcn - ctx->curr_vcn);
}
}
return 1;
}
static int read_attr(char* cur_mft,unsigned char attr,char* dest,unsigned long ofs,unsigned long len,int cached)
{
unsigned long data_vcn;
unsigned short cbuf,tbuf,save_cur;
read_ctx cc,*ctx;
char *tb;
int ret;
if (len==0)
return 1;
save_cur=attr_cur;
if (emft_ofs)
attr_als=attr_cur;
else
attr_end=attr_cur;
ctx=&cc;
cbuf=tbuf=0;
ctx->cur_run=find_attr(cur_mft,attr);
if (ctx->cur_run==NULL)
{
dbg_printf("Can\'t find attribute 0x%X\n",attr);
return 0;
}
if (ctx->cur_run[8]==0)
{
if (ofs+len>valueat(ctx->cur_run,0x10,unsigned long))
{
dbg_printf("Read out of range\n");
return 0;
}
memcpy(dest,ctx->cur_run+valueat(ctx->cur_run,0x14,unsigned long)+ofs,len);
goto done;
}
ctx->flags=0;
ctx->mft=cur_mft;
set_flag(RF_COMP_FILE,valueat(ctx->cur_run,0xC,unsigned short) & FLAG_COMPRESSED);
ctx->blk_size=(get_flag(RF_COMP_FILE))?4096:(spc << BLK_SHR);
ctx->cpb=ctx->blk_size / (spc << BLK_SHR);
ctx->cur_run+=valueat(ctx->cur_run,0x20,unsigned short);
if ((get_flag(RF_COMP_FILE)) && (! cached))
{
dbg_printf("Attribute can\'t be compressed\n");
return 0;
}
if (cached)
{
if (data_ofs)
{
if ((ofs & (~(ctx->blk_size-1)))==save_pos)
{
int n;
n=ctx->blk_size - (ofs - save_pos);
if (n>len)
n=len;
memcpy(dest,ofs2ptr(data_ofs) + ofs - save_pos,n);
if (n==len)
goto done;
dest+=n;
len-=n;
ofs+=n;
}
}
else
{
if ((data_ofs=get_mem(8))==0)
return 0;
save_pos=1;
}
tb=ofs2ptr(data_ofs);
}
else
tb=NULL;
ctx->target_vcn=data_vcn=(ofs / ctx->blk_size) * ctx->cpb;
if (get_flag(RF_COMP_FILE))
ctx->target_vcn &= ~0xF;
if ((ctx->cur_run=find_attr_vcn(cur_mft,ctx->cur_run,ctx->target_vcn))==NULL)
return 0;
if (emft_ofs)
ctx->next_vcn=valueat(ofs2ptr(attr_cur),0x8,unsigned long);
else
ctx->next_vcn=0;
ctx->curr_lcn=0;
while (ctx->next_vcn<= ctx->target_vcn)
{
ctx->cur_run=read_run_list(ctx,ctx->cur_run);
if (ctx->cur_run==NULL)
return 0;
}
set_flag(RF_COMP_BLOCK,(get_flag(RF_COMP_FILE)) && (ctx->next_vcn - ctx->target_vcn<16));
if (get_flag(RF_COMP_BLOCK))
{
ctx->cbuf_ofs=spc << BLK_SHR;
ctx->cbuf_lcn=ctx->curr_lcn+(ctx->target_vcn - ctx->curr_vcn);
if ((cbuf=get_mem(8))==0)
return 0;
ctx->cbuf=ofs2ptr(cbuf);
}
ret=0;
if ((cached) && (! get_flag(RF_COMP_FILE)))
disk_read_func = disk_read_hook;
if ((data_vcn>ctx->target_vcn) &&
(! read_block(ctx,NULL,(data_vcn - ctx->target_vcn) / ctx->cpb)))
goto fail;
if (ofs % ctx->blk_size)
{
unsigned long t,n,o;
if (tb==NULL)
{
if ((tbuf=get_mem(ctx->blk_size >> BLK_SHR))==0)
goto fail;
tb=ofs2ptr(tbuf);
}
t=ctx->target_vcn*(spc << BLK_SHR);
if (! read_block(ctx,tb,1))
goto fail;
if (cached)
save_pos=t;
o=ofs % ctx->blk_size;
n=ctx->blk_size - o;
if (n>len)
n=len;
memcpy(dest,&tb[o],n);
if (n==len)
goto done;
dest+=n;
len-=n;
}
if (! read_block(ctx,dest,len / ctx->blk_size))
goto fail;
dest+=(len / ctx->blk_size) * ctx->blk_size;
len=len % ctx->blk_size;
if (len)
{
unsigned long t;
if (tb==NULL)
{
if ((tbuf=get_mem(ctx->blk_size >> BLK_SHR))==0)
goto fail;
tb=ofs2ptr(tbuf);
}
t=ctx->target_vcn * (spc << BLK_SHR);
if (! read_block(ctx,tb,1))
goto fail;
if (cached)
save_pos=t;
memcpy(dest,tb,len);
}
done:
ret=1;
attr_cur=save_cur;
if (emft_ofs)
attr_als=attr_cur;
else
attr_end=attr_cur;
fail:
if (cbuf)
free_mem(cbuf,8);
if (tbuf)
free_mem(tbuf,ctx->blk_size >> BLK_SHR);
disk_read_func = NULL;
return ret;
}
static int read_mft(char* buf,unsigned long mftno)
{
if (! read_attr(mmft,AT_DATA,buf,mftno*(mft_size << BLK_SHR),mft_size << BLK_SHR,0))
{
dbg_printf("Read MFT 0x%X fails\n",mftno);
return 0;
}
return fixup(buf,mft_size,"FILE");
}
static int init_mft(char* cur_mft)
{
/*
char *pp;
int len;
len=(mft_size << BLK_SHR)-VAR_LEN-4;
pp=cur_mft+valueat(cur_mft,0x14,unsigned short);
while ((pp<=cur_mft+len) && ((unsigned char)*pp!=0xFF))
pp+=valueat(pp,4,unsigned long);
if (pp>cur_mft+len)
{
dbg_printf("MFT data overflow\n");
return 0;
}
*/
emft_ofs=ebuf_ofs=data_ofs=0;
return 1;
}
static int init_file(char* cur_mft,unsigned long mftno)
{
unsigned short flag;
init_attr(cur_mft); // free memory
if ((! read_mft(cur_mft,mftno)) || (! init_mft(cur_mft)))
goto error;
init_attr(cur_mft);
flag=valueat(cur_mft,0x16,unsigned short);
if ((flag & 1)==0)
{
dbg_printf("MFT 0x%X is not in use\n",mftno);
goto error;
}
if (flag & 2)
filemax=0;
else
{
char *cur_pos;
cur_pos=find_attr(cur_mft,AT_DATA);
if (! cur_pos)
{
dbg_printf("No $DATA in MFT 0x%X\n",mftno);
goto error;
}
if ((! emft_ofs) && (attr_als))
{
cur_pos=find_attr(cur_mft,AT_DATA);
if ((cur_pos==NULL) || (! emft_ofs))
{
init_attr(cur_mft);
find_attr(cur_mft,AT_DATA);
attr_als=0;
}
}
if (! cur_pos[8])
filemax=valueat(cur_pos,0x10,unsigned long);
else
filemax=valueat(cur_pos,0x30,unsigned long);
}
if (! emft_ofs) // Don't jump to attribute list
attr_als=0;
filepos=0;
return 1;
error:
errnum=ERR_FSYS_CORRUPT;
return 0;
}
static int list_file(char* cur_mft,char *fn,char *pos)
{
char *np;
int i,ns,len;
len=strlen(fn);
while (1)
{
if (pos[0xC] & 2) // end signature
break;
np=pos+0x52;
ns=valueat(np,-2,unsigned char);
if (((print_possibilities) && (ns>=len)) ||
((! print_possibilities) && (ns==len)))
{
for (i=0;i<len;i++)
if (tolower(fn[i])!=tolower(np[i*2]))
break;
if (i>=len)
{
if (print_possibilities)
{
if ((i) || ((np[0]!='$') && ((np[0]!='.') || (ns!=1))))
{
#ifndef STAGE1_5
if (print_possibilities>0)
print_possibilities=-print_possibilities;
#endif
for (i=1;i<ns;i++)
np[i]=np[i*2];
np[ns]=0;
#ifdef FS_UTIL
print_completion_ex(np,valueat(pos,0,unsigned long),valueat(pos,0x40,unsigned long),(valueat(pos,0x48,unsigned long) & ATTR_DIRECTORY)?FS_ATTR_DIRECTORY:0);
#else
print_a_completion(np);
#endif
}
}
else
{
if (valueat(pos,4,unsigned short))
{
dbg_printf("64-bit MFT number\n");
return 0;
}
return init_file(cur_mft,valueat(pos,0,unsigned long));
}
}
}
pos+=valueat(pos,8,unsigned short);
}
return -1;
}
static int scan_dir(char* cur_mft,char *fn)
{
unsigned long bitmap_len;
unsigned char *bitmap;
char *cur_pos,*new_pos;
unsigned short cidx_ofs,run_pos;
int ret;
if ((valueat(cur_mft,0x16,unsigned short) & 2)==0)
{
errnum=ERR_FILE_NOT_FOUND;
return 0;
}
cidx_ofs=0;
init_attr(cur_mft);
new_pos=NULL;
while (1)
{
if ((cur_pos=find_attr(cur_mft,AT_INDEX_ROOT))==NULL)
{
dbg_printf("No $INDEX_ROOT\n");
goto error;
}
// Resident, Namelen=4, Offset=0x18, Flags=0x00
// Name="$I30"
if ((valueat(cur_pos,8,unsigned long)!=0x180400) ||
(valueat(cur_pos,0x18,unsigned long)!=0x490024) ||
(valueat(cur_pos,0x1C,unsigned long)!=0x300033))
continue;
new_pos=cur_pos+valueat(cur_pos,0x14,unsigned short);
if (*new_pos!=0x30) // Not filename index
continue;
new_pos+=0x10; // Skip index root
break;
}
ret=list_file(cur_mft,fn,new_pos+valueat(new_pos,0,unsigned short));
if (ret>=0)
goto done;
bitmap_len=0;
run_pos=0;
bitmap=NULL;
init_attr(cur_mft);
while ((cur_pos=find_attr(cur_mft,AT_BITMAP))!=NULL)
{
// Resident, Namelen=4, Offset=0x18, Flags=0x00
// Name="$I30"
if ((valueat(cur_pos,8,unsigned long)==0x180400) &&
(valueat(cur_pos,0x18,unsigned long)==0x490024) &&
(valueat(cur_pos,0x1C,unsigned long)==0x300033))
{
bitmap=(unsigned char*)(cur_pos+valueat(cur_pos,0x14,unsigned short));
bitmap_len=valueat(cur_pos,0x10,unsigned long);
break;
}
}
if ((bitmap) && (emft_ofs))
{
dbg_printf("$BITMAP should not in attribute list\n");
goto error;
}
init_attr(cur_mft);
while ((cur_pos=find_attr(cur_mft,AT_INDEX_ALLOCATION))!=NULL)
{
// Non-resident, Namelen=4, Offset=0x40, Flags=0
// Name="$I30"
if ((valueat(cur_pos,8,unsigned long)==0x400401) &&
(valueat(cur_pos,0x40,unsigned long)==0x490024) &&
(valueat(cur_pos,0x44,unsigned long)==0x300033))
{
run_pos=attr_cur;
break;
}
}
if ((! run_pos) && (bitmap))
{
dbg_printf("$BITMAP without $INDEX_ALLOCATION\n");
goto error;
}
if (bitmap)
{
unsigned long v,i;
char *cidx;
if ((cidx_ofs=get_mem(idx_size))==0)
goto error;
cidx=ofs2ptr(cidx_ofs);
v=1;
for (i=0;i<bitmap_len*8;i++)
{
if (*bitmap & v)
{
if ((! read_attr(cur_mft,AT_INDEX_ALLOCATION,cidx,i*(idx_size<<BLK_SHR),(idx_size<<BLK_SHR),0)) ||
(! fixup(cidx,idx_size,"INDX")))
goto error;
ret=list_file(cur_mft,fn,&cidx[0x18+valueat(cidx,0x18,unsigned short)]);
if (ret>=0)
goto done;
}
v<<=1;
if (v >= 0x100)
{
v=1;
bitmap++;
}
}
}
ret=(print_possibilities<0);
done:
if (cidx_ofs)
free_mem(cidx_ofs,idx_size);
if (! ret)
errnum = ERR_FILE_NOT_FOUND;
return ret;
error:
if (cidx_ofs)
free_mem(cidx_ofs,idx_size);
errnum = ERR_FSYS_CORRUPT;
return 0;
}
static char* cmft;
int ntfs_mount (void)
{
char *cur_pos,*cur_mft;
if (((current_drive & 0x80) || (current_slice != 0))
&& (current_slice != 7) && (current_slice != 0x17))
return 0;
mem_mask1=mem_mask2=0;
if (! devread (0, 0, 512, mmft))
return 0;
if (valueat(mmft,3,unsigned long)!=0x5346544E)
return 0;
blocksize=valueat(mmft,0xb,unsigned short);
spc=blocksize*valueat(mmft,0xd,unsigned char) >> BLK_SHR;
if (mmft[0x44]>0)
idx_size=spc*mmft[0x44];
else
idx_size=1<<(-mmft[0x44]-BLK_SHR);
if (mmft[0x40]>0)
mft_size=spc*mmft[0x40];
else
mft_size=1<<(-mmft[0x40]-BLK_SHR);
mft_start=spc*valueat(mmft,0x30,unsigned long);
if ((mft_size>MAX_MFT) ||(idx_size>MAX_IDX) ||(spc>MAX_SPC) || (spc>idx_size))
return 0;
get_mem(mft_size);
devread(mft_start,0,mft_size << BLK_SHR,mmft);
if ((! fixup(mmft,mft_size,"FILE")) || (! init_mft(mmft)))
return 0;
cur_mft=mmft;
init_attr(mmft);
cur_pos=find_attr(mmft,AT_DATA);
if ((cur_pos==NULL) || (emft_ofs))
{
dbg_printf("No $DATA in master MFT\n");
return 0;
}
mmft_run=cur_pos;
if (attr_als)
{
cur_pos=find_attr(mmft,AT_DATA);
if (cur_pos==NULL)
{
init_attr(mmft);
cur_pos=find_attr(mmft,AT_DATA);
}
if (! emft_ofs)
{
dbg_printf("Multiple $DATA not supported\n");
return 0;
}
}
cmft=ofs2ptr(get_mem(mft_size));
init_mft(cmft);
return 1;
}
int ntfs_dir (char *dirname)
{
int ret;
#ifndef STAGE1_5
int is_print=print_possibilities;
#endif
filepos=filemax=0;
if (*dirname=='/')
dirname++;
#ifndef STAGE1_5
if ((*dirname=='#') && (dirname[1]>='0') && (dirname[1]<='9'))
{
int mftno;
dirname++;
if (! safe_parse_maxint(&dirname,&mftno))
return 0;
return init_file(cmft,mftno);
}
#endif
if (! init_file(cmft,FILE_ROOT))
return 0;
ret=0;
while (1)
{
char *next,ch;
next=dirname;
ch=*next;
while ((ch!=0) && (ch!='/') && (! isspace(ch)))
ch=*(++next);
*next=0;
#ifndef STAGE1_5
print_possibilities=(ch=='/')?0:is_print;
#endif
ret=scan_dir(cmft,dirname);
*next=ch;
if (! ret)
break;
if (ch=='/')
dirname=next+1;
else
break;
}
#ifndef STAGE1_5
print_possibilities=is_print;
#endif
return ret;
}
unsigned long ntfs_read(char *buf, unsigned long len)
{
char *cur_mft;
cur_mft=cmft;
if (valueat(cur_mft,0x16,unsigned short) & 2)
goto error;
if (disk_read_hook)
save_pos=1;
if (! read_attr(cmft,AT_DATA,buf,filepos,len,1))
goto error;
filepos+=len;
return len;
error:
errnum=ERR_FSYS_CORRUPT;
return 0;
}
#ifdef FS_UTIL
void ntfs_info(int level)
{
dbg_printf("blocksize: %u\nspc: %u\nmft_size: %u\nidx_size: %u\nmft_start: 0x%X\n",
blocksize,spc,mft_size,idx_size,mft_start);
}
int ntfs_inode_read(char* buf)
{
if (buf)
memcpy(buf,cmft,mft_size<<BLK_SHR);
return mft_size<<BLK_SHR;
}
static char* attr2str(unsigned char attr)
{
switch (attr) {
case AT_STANDARD_INFORMATION:
return "$STANDARD_INFORMATION";
case AT_ATTRIBUTE_LIST:
return "$ATTRIBUTE_LIST";
case AT_FILENAME:
return "$FILENAME";
case AT_OBJECT_ID:
return "$OBJECT_ID";
case AT_SECURITY_DESCRIPTOR:
return "$SECURITY_DESCRIPTOR";
case AT_VOLUME_NAME:
return "$VOLUME_NAME";
case AT_VOLUME_INFORMATION:
return "$VOLUME_INFORMATION";
case AT_DATA:
return "$DATA";
case AT_INDEX_ROOT:
return "$INDEX_ROOT";
case AT_INDEX_ALLOCATION:
return "$INDEX_ALLOCATION";
case AT_BITMAP:
return "$BITMAP";
case AT_SYMLINK:
return "$SYMLINK";
case AT_EA_INFORMATION:
return "$EA_INFORMATION";
case AT_EA:
return "$EA";
}
return "$UNKNOWN";
}
static void print_name(char* s,int len)
{
int i;
for (i=0;i<len;i++)
putchar(s[i*2]);
}
void print_runlist(char *run)
{
read_ctx ctx;
int first;
memset(&ctx,0,sizeof(ctx));
first=1;
while (run=read_run_list(&ctx,run))
{
if (first)
first=0;
else
putchar(',');
printf("%d+%d",ctx.curr_lcn*spc,(ctx.next_vcn-ctx.curr_vcn)*spc);
if (*run==0)
break;
}
printf("\n");
}
void ntfs_inode_info(int level)
{
char *cur_mft,*pos;
int first;
cur_mft=cmft;
printf("Type: %s\n",(valueat(cur_mft,0x16,unsigned short) & 2)?"Directory":"File");
if (valueat(cur_mft,0x20,unsigned long))
printf("Base: 0x%X\n",valueat(cur_mft,0x20,unsigned long));
printf("Attr:\n");
first=1;
init_attr(cur_mft);
while ((pos=find_attr(cur_mft,0))!=NULL)
{
unsigned long fg;
if (emft_ofs)
{
if (first)
{
printf("Attr List:\n");
first=0;
}
}
printf(" %s (0x%X) ",attr2str(*pos),(unsigned char)*pos);
printf((pos[8])?"(nr":"(r");
fg=valueat(pos,0xC,unsigned short);
if (fg & FLAG_COMPRESSED)
printf(",c");
if (fg & FLAG_ENCRYPTED)
printf(",e");
if (fg & FLAG_SPARSE)
printf(",s");
if (emft_ofs)
{
printf(",mft=0x%X",valueat(ofs2ptr(attr_cur),0x10,unsigned long));
if (pos[8])
printf(",vcn=0x%X",valueat(ofs2ptr(attr_cur),0x8,unsigned long));
}
if (pos[9])
{
printf(",nm=");
print_name(pos+valueat(pos,0xA,unsigned short),pos[9]);
}
printf(",sz=%d",valueat(pos,((pos[8])?0x30:0x10),unsigned long));
printf(")\n");
if ((pos[8]) && (! emft_ofs))
{
printf(" ");
print_runlist(pos+valueat(pos,0x20,unsigned short));
}
switch ((unsigned char)pos[0]) {
case AT_FILENAME:
pos+=valueat(pos,0x14,unsigned short);
if (pos[0x40])
{
printf(" ");
print_name(pos+0x42,(unsigned char)pos[0x40]);
printf("\n");
}
break;
}
}
}
#endif
#endif /* FSYS_NTFS */