| /* 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., 675 Mass Ave, Cambridge, MA 02139, 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 |
| */ |
| /*#ifdef STAGE1_5*/ |
| |
| #include <shared.h> |
| #include <filesys.h> |
| #include "ntfs.h" |
| |
| /* 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 |
| |
| |
| |
| #define index_data ((char *)FSYS_BUF) |
| #define bitmap_data ((unsigned char *)(FSYS_BUF+MAX_INDEX_RECORD_SIZE)) |
| #define dcdbuf ((unsigned char *)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 ((unsigned long *)(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 ((unsigned char *)cluster16[26]) |
| #define is_ads_completion cluster16[27] |
| |
| typedef struct run_list { |
| char *start; |
| char *ptr; |
| unsigned long svcn; |
| unsigned long evcn; |
| unsigned long vcn; |
| unsigned long cnum0; |
| unsigned long cnum; |
| unsigned long clen; |
| } RUNL; |
| |
| typedef struct ntfs_mft_record { |
| char mft[MAX_MFT_RECORD_SIZE]; |
| char mft2[MAX_MFT_RECORD_SIZE]; |
| unsigned long attr_type; |
| char *attr_name; |
| unsigned long attr_flag; |
| unsigned long attr_size; |
| char *attr; |
| unsigned long attr_len; |
| RUNL runl; |
| char *attr_list; |
| unsigned long attr_list_len; |
| unsigned long attr_list_size; |
| unsigned long attr_list_off; |
| unsigned long attr_inited; |
| char attr_list_buf[2*BLOCK_SIZE]; |
| RUNL attr_list_runl; |
| } MFTR; |
| |
| static int read_mft_record(unsigned long mftno, char *mft, unsigned long self); |
| static unsigned long read_attribute(MFTR *mftr, unsigned long offset, char *buf, unsigned long len, RUNL *from_rl); |
| static int get_next_run(RUNL *runl); |
| |
| |
| |
| #if 0 |
| 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; |
| } |
| #endif |
| |
| static int |
| fixup_record (char *record, char *magic, unsigned long size) |
| { |
| unsigned long start, count, offset; |
| unsigned short fixup; |
| |
| if (*(int *)record != *(int *)magic) |
| return 0; |
| |
| start = *(unsigned short *)(record + 4); |
| count = *(unsigned short *)(record + 6); |
| count--; |
| |
| if (size && blocksize * count != size) |
| return 0; |
| |
| fixup = *(unsigned short *)(record + start); |
| start += 2; |
| offset = blocksize - 2; |
| while (count--) |
| { |
| if(*(unsigned short *)(record + offset) != fixup) |
| return 0; |
| |
| *(unsigned short *)(record + offset) = *(unsigned short *)(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) |
| { |
| unsigned long 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 * (*(unsigned char *)((runl->ptr)++)); |
| v <<= 8; |
| } |
| |
| n = (t >> 4) & 0xf; |
| |
| if (n == 0) |
| runl->cnum = 0; |
| else |
| { |
| unsigned long c = 0; |
| v = 1; |
| while(n--) |
| { |
| c += v * (*(unsigned char *)((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, unsigned long len, RUNL *runl, unsigned long *initp) |
| { |
| unsigned long allocated; |
| /* int inited; */ |
| |
| runl->svcn = *(unsigned long *)(attr + 0x10); /* only support 32 bit */ |
| runl->evcn = *(unsigned long *)(attr + 0x18); /* only support 32 bit */ |
| runl->start = attr + (*(unsigned short *)(attr + 0x20)); |
| allocated = *(unsigned long *)(attr + 0x28); |
| |
| if (initp) |
| *initp = *(unsigned long *)(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*/ *(unsigned long *)(attr+0x30), |
| /*allocated*/ *(unsigned long *)(attr+0x28), |
| /*attr_inited*/ *(unsigned long *)(attr+0x38), |
| /*cengin*/ *(unsigned short *)(attr+0x22), |
| /*csize*/ *(unsigned short *)(attr+0x40), |
| runl->svcn, runl->evcn); |
| #endif |
| rewind_run_list(runl); |
| } |
| #endif |
| |
| |
| static int |
| find_attribute (char *mft, unsigned long type, char *name, char **attr, unsigned long *size, unsigned long *len, unsigned long *flag) |
| { |
| unsigned long t, l, r, n, i, namelen; |
| unsigned short *attr_name; |
| |
| n = strlen (name); |
| r = mft_record_size - *(unsigned short *)(mft + 0x14); |
| mft += *(unsigned short *)(mft + 0x14); |
| while ((t = *(unsigned long *)mft) != -1) |
| { |
| l = *(unsigned long *)(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 = (unsigned short *)(mft + *(unsigned short *)(mft+10)), |
| /*resident */ (*(mft+8) == 0), |
| /*compressed*/ *(unsigned short *)(mft+12), |
| /*attrno*/ *(unsigned short *)(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*/ || ! *(unsigned long *)(attr + 0x10)/*svcn==0*/)) |
| { |
| for (i = 0, attr_name = (unsigned short *)(mft + *(unsigned short *)(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 = (unsigned short *)(mft + *(unsigned short *)(mft + 10)); i < n; i++) |
| if(tolower(name[i]) != tolower(attr_name[i])) |
| break; |
| |
| if (i >= n) |
| { |
| if (flag) |
| *flag = *(unsigned short *)(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*/ *(unsigned short *)(mft+0x14), |
| /*attr_size*/ *(unsigned short *)(mft+0x10), |
| /*indexed*/ *(unsigned short *)(mft+0x16)); |
| #endif |
| if (attr) |
| *attr = mft + (*(unsigned short *)(mft + 0x14)); |
| |
| if (size) |
| *size = (*(unsigned short *)(mft + 0x10)); |
| |
| if (len) |
| *len = (*(unsigned short *)(mft + 0x10)); |
| |
| } else { |
| |
| if (attr) |
| *attr = mft; |
| |
| if (size) |
| *size = *(unsigned long *)(mft+0x30); |
| |
| if (len) |
| *len = l; |
| } |
| |
| return 1; |
| } |
| } |
| } |
| |
| mft += l; |
| r -= l; |
| } |
| return 0; |
| } |
| |
| #ifndef NO_ATTRIBUTE_LIST |
| static unsigned long |
| get_next_attribute_list (MFTR *mftr, unsigned long *size) |
| { |
| unsigned long 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 = *(unsigned long *)(mftr->attr_list + 0); |
| l = *(unsigned short *)(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 = *(unsigned long *)(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) |
| { |
| unsigned long 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, unsigned long 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, (unsigned long *)&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, unsigned long vcn, unsigned long *clp, unsigned long *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, unsigned long 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 unsigned long |
| read_attribute (MFTR *mftr, unsigned long offset, char *buf, unsigned long len, RUNL *from_rl) |
| { |
| unsigned long vcn; |
| unsigned long cnum, clen; |
| unsigned long done = 0; |
| unsigned long 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; /* len always >= 0 */ |
| } |
| |
| return done; |
| } |
| |
| static int read_mft_record (unsigned long mftno, char *mft, unsigned long 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 unsigned long get_16_cluster (MFTR *mftr, unsigned long vcn) |
| { |
| unsigned long 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 unsigned long compressed_block_size (unsigned char *src) |
| { |
| return 3 + (*(unsigned short *)src & 0xfff); |
| } |
| |
| static unsigned long decompress_block (unsigned char *dest, unsigned char *src) |
| { |
| unsigned long head; |
| unsigned long copied=0; |
| unsigned char *last; |
| unsigned long bits; |
| unsigned long tag=0; |
| |
| /* high bit indicates that compression was performed */ |
| if (! (*(unsigned short *)src & 0x8000)) |
| { |
| memmove (dest, src + 2, 0x1000); |
| return 0x1000; |
| } |
| |
| if ((head = *(unsigned short *)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 = *(unsigned char *)src; |
| bits = 8; |
| src++; |
| if (src > last) |
| break; |
| } |
| if (tag & 1) |
| { |
| unsigned long i, len, delta, code, lmask, dshift; |
| code = *(unsigned short *)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++] = *(unsigned char *)src++; |
| tag >>= 1; |
| bits--; |
| } |
| |
| return copied; |
| } |
| #endif |
| |
| unsigned long |
| ntfs_read(char *buf, unsigned long len) |
| { |
| unsigned long 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; |
| unsigned long len0 = 0; |
| /*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 0 |
| 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; |
| #endif |
| #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) |
| //if((cmft->attr_flag | ATTR_RESIDENT) == ATTR_RESIDENT) |
| disk_read_func = disk_read_hook; |
| |
| ret = read_attribute (cmft, filepos, buf, len, 0); |
| |
| if (cmft->attr_flag == ATTR_NORMAL) |
| //if((cmft->attr_flag | ATTR_RESIDENT) == ATTR_RESIDENT) |
| 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; /* len always >= 0 */ |
| 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) |
| { |
| unsigned long 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 (size > len) |
| size = len; |
| #ifndef STAGE1_5 |
| memset (buf, 0, size); |
| #endif |
| filepos += size; |
| len -= size; /* len always >= 0 */ |
| 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 (size > len) |
| size = len; |
| |
| if (! devread (cluster16[index16] * (clustersize >> 9) + (off >> 9), off & 0x1ff, size, buf)) |
| return ret; |
| filepos += size; |
| len -= size; /* len always >= 0 */ |
| 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 = ERR_FSYS_CORRUPT; |
| #endif /*NO_NTFS_DECOMPRESSION*/ |
| #endif /*STAGE1_5*/ |
| return ret; |
| } |
| |
| int ntfs_mount (void) |
| { |
| #define sb ((char *)FSYS_BUF) |
| unsigned long mft_record; |
| unsigned long spc; /* sectors per cluster */ |
| |
| 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') |
| if (*(long *)(sb + 3) != 0x5346544E) /* "NTFS" */ |
| return 0; |
| blocksize = *(unsigned short *)(sb + 0xb); |
| spc = *(unsigned char *)(sb + 0xd); |
| clustersize = spc * blocksize; |
| mft_record_size = (long)(*(char *)(sb + 0x40)); /* XXX: signed value */ |
| index_record_size = *(char *)(sb + 0x44); /* XXX */ |
| if (((long)mft_record_size) > 0) |
| mft_record_size *= clustersize; |
| else if (((long)mft_record_size) < 0) |
| mft_record_size = (1 << (-mft_record_size)); |
| else |
| return 0; |
| |
| index_record_size *= clustersize; |
| mft_record = *(unsigned long *)(sb + 0x30); /* only support 32 bit */ |
| spc = clustersize >> 9; /* 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, *(long long *)(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; |
| #undef sb |
| } |
| |
| int |
| ntfs_dir (char *dirname) |
| { |
| char *rest, ch; |
| unsigned long namelen; |
| unsigned long depth = 0; |
| unsigned long flag = 0; |
| unsigned long record_offset; |
| unsigned long my_index_record_size; |
| unsigned char *index_entry = 0, *entry, *index_end; |
| unsigned long i; |
| int chk_sfn = 1; |
| |
| /* 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 = *(unsigned long *)(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 = (unsigned char *)(index_data + cmft->attr_size); |
| index_entry = (unsigned char *)(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, (char *)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++) |
| { |
| unsigned long bit = (1 << (record_offset & 3)); |
| unsigned long 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 += *(unsigned short *)(entry + 8); |
| |
| if (entry + 0x50 >= index_entry || entry >= index_end || index_entry >= index_end || (entry[0x12] & 2)) |
| { |
| if ((long)record_offset == -1 || |
| ! read_attribute (cmft, record_offset * my_index_record_size, index_data, my_index_record_size, 0)) |
| { |
| if (errnum == 0) |
| { |
| #ifndef STAGE1_5 |
| if (print_possibilities < 0) |
| { |
| #if 0 |
| putchar ('\n'); |
| #endif |
| return 1; |
| } |
| #endif |
| |
| 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 = (unsigned char *)(index_data + 0x18 + (*(unsigned short *)(index_data + 0x18))); |
| index_entry = entry + (*(unsigned short *)(entry + 8)); |
| index_end = (unsigned char *)(index_data + my_index_record_size - 0x52); |
| |
| for (record_offset++; record_offset * my_index_record_size < cmft->attr_size; record_offset++) |
| { |
| unsigned long bit = (1 << (record_offset & 3)); |
| unsigned long 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]; |
| if ((long)(path_ino[depth + 1] = (*(unsigned long *)entry)) < 16) |
| // 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) |
| { |
| unsigned long c = *(unsigned short *)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 = substring (dirname, fnbuf, 1); |
| #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, unsigned long size){ |
| unsigned long l = (size+15)/16; |
| unsigned long off; |
| unsigned long 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 */ |