/* 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 * 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) { 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>=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_sizeattr_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_sizeattr_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 */