diff options
Diffstat (limited to 'qemu/roms/openbios/fs/grubfs/fsys_ntfs.c')
-rw-r--r-- | qemu/roms/openbios/fs/grubfs/fsys_ntfs.c | 1255 |
1 files changed, 1255 insertions, 0 deletions
diff --git a/qemu/roms/openbios/fs/grubfs/fsys_ntfs.c b/qemu/roms/openbios/fs/grubfs/fsys_ntfs.c new file mode 100644 index 000000000..a244f5c6b --- /dev/null +++ b/qemu/roms/openbios/fs/grubfs/fsys_ntfs.c @@ -0,0 +1,1255 @@ +/* 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 */ |