| /* |
| * 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 */ |