From 631e27718ed09c4ae18c03567b9b31fc62266ed3 Mon Sep 17 00:00:00 2001 From: srs5694 Date: Fri, 30 Jan 2015 20:50:56 -0500 Subject: [PATCH] New documentation screen shot and new file for NTFS driver. --- docs/refind/startup-disk.png | Bin 0 -> 34708 bytes filesystems/fsw_ntfs.c | 1535 ++++++++++++++++++++++++++++++++++ 2 files changed, 1535 insertions(+) create mode 100644 docs/refind/startup-disk.png create mode 100644 filesystems/fsw_ntfs.c diff --git a/docs/refind/startup-disk.png b/docs/refind/startup-disk.png new file mode 100644 index 0000000000000000000000000000000000000000..19ac11f6ae0e6a0e29c661886e038313a6c68007 GIT binary patch literal 34708 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 + type &= ATTRMASK; + + for(n = 0; mftsize >= 8; mft += n, mftsize -= n) { + int t = GETU32(mft, 0); + n = GETU32(mft, 4); + if( t==0 || (t+1)==0 || t==0xffff || n<24 || mftsize>ATTRBITS; + type &= ATTRMASK; + + while( *pos + 0x18 <= atlen) { + int off = *pos; + fsw_u32 t = GETU32(atlst, off); + fsw_u32 n = GETU16(atlst, off+4); + + *pos = off + n; + if(t==0 || (t+1)==0 || t==0xffff || n < 0x18 || *pos > atlen) + break; + + fsw_u8 ns = GETU8(atlst, off+6); + fsw_u8 *nm = atlst + off + GETU8(atlst, off+7); + if( type == t && namelen==ns && (ns==0 || fsw_memeq(NAME_I30, nm, ns*2))) { + fsw_u64 avcn = GETU64(atlst, off+8); + if(vcn < avcn) { + if(mftno == BADMFT) + return FSW_NOT_FOUND; + *out = mftno; + return FSW_SUCCESS; + } + if(vcn == avcn) { + *out = GETU64(atlst, off+0x10) & MFTMASK; + return FSW_SUCCESS; + } + mftno = GETU64(atlst, off+0x10) & MFTMASK; + } + } + return FSW_NOT_FOUND; +} + +static fsw_status_t get_extent(fsw_u8 **rlep, int *rlenp, fsw_u64 *lcnp, fsw_u64 *lenp, fsw_u64 *pos) +{ + fsw_u8 *rle = *rlep; + fsw_u8 *rle_end = rle + *rlenp; + int f = *rle++; + fsw_u64 m = 1; + fsw_u64 c = 0; + fsw_u64 v = 0; + + int n = f & 0xf; + if(n==0) return FSW_NOT_FOUND; + + if(rle + n > rle_end) return FSW_VOLUME_CORRUPTED; + + while(--n >= 0) { + c += m * (*rle++); + m <<= 8; + } + + n = f >> 4; + if(n==0) { + /* LCN 0 as sparse, due to we don't need $Boot */ + *lcnp = 0; + *lenp = c; + } else { + if(rle + n > rle_end) return FSW_VOLUME_CORRUPTED; + + m = 1; + while(--n >= 0) { + v += m * (*rle++); + m <<= 8; + } + + *pos += v; + if(v & (m>>1)) + *pos -= m; + + *lcnp = *pos; + *lenp = c; + } + *rlenp -= rle - *rlep; + *rlep = rle; + return FSW_SUCCESS; +} + +static inline int attribute_ondisk(fsw_u8 *ptr, int len) +{ + return GETU8(ptr, 8); +} + +static inline int attribute_compressed(fsw_u8 *ptr, int len) +{ + return (GETU8(ptr, 12) & 0xFF) == 1; +} + +static inline int attribute_compressed_future(fsw_u8 *ptr, int len) +{ + return (GETU8(ptr, 12) & 0xFF) > 1; +} + +static inline int attribute_sparse(fsw_u8 *ptr, int len) +{ + return GETU8(ptr, 12) & 0x8000; +} + +static inline int attribute_encrypted(fsw_u8 *ptr, int len) +{ + return GETU8(ptr, 12) & 0x4000; +} + +static void attribute_get_embeded(fsw_u8 *ptr, int len, fsw_u8 **outp, int *outlenp) +{ + int off = GETU16(ptr, 0x14); + int olen = GETU16(ptr, 0x10); + if(olen + off > len) + olen = len - off; + *outp = ptr + off; + *outlenp = olen; +} + +static void attribute_get_rle(fsw_u8 *ptr, int len, fsw_u8 **outp, int *outlenp) +{ + int off = GETU16(ptr, 0x20); + int olen = len - off; + *outp = ptr + off; + *outlenp = olen; +} + +static inline int attribute_rle_offset(fsw_u8 *ptr, int len) +{ + return GETU16(ptr, 0x20); +} + +static inline fsw_u64 attribute_size(fsw_u8 *ptr, int len) +{ + return GETU8(ptr, 8) ? GETU64(ptr, 0x30) : GETU16(ptr, 0x10); +} + +static inline fsw_u64 attribute_inited_size(fsw_u8 *ptr, int len) +{ + return GETU8(ptr, 8) ? GETU64(ptr, 0x38) : GETU16(ptr, 0x10); +} + +static inline int attribute_has_vcn(fsw_u8 *ptr, int len, fsw_u64 vcn) { + if(GETU8(ptr, 8)==0) + return 1; + return vcn >= GETU64(ptr, 0x10) && vcn <= GETU64(ptr, 0x18); +} + +static inline fsw_u64 attribute_first_vcn(fsw_u8 *ptr, int len) +{ + return GETU8(ptr, 8) ? GETU64(ptr, 0x10) : 0; +} + +static inline fsw_u64 attribute_last_vcn(fsw_u8 *ptr, int len) +{ + return GETU8(ptr, 8) ? GETU64(ptr, 0x18) : 0; +} + +static fsw_status_t read_attribute_direct(struct fsw_ntfs_volume *vol, fsw_u8 *ptr, int len, fsw_u8 **optrp, int *olenp) +{ + int olen; + + if(attribute_ondisk(ptr, len) == 0) { + /* EMBEDED DATA */ + attribute_get_embeded(ptr, len, &ptr, &len); + *olenp = len; + if(optrp) { + *optrp = AllocatePool(len); + fsw_memcpy(*optrp, ptr, len); + } + return FSW_SUCCESS; + } + + olen = attribute_size(ptr, len); + *olenp = olen; + if(!optrp) + return FSW_SUCCESS; + + fsw_u8 *buf = AllocatePool(olen); + fsw_memzero(buf, olen); + *optrp = buf; + + attribute_get_rle(ptr, len, &ptr, &len); + + fsw_u64 pos = 0; + int clustersize = 1<clbits; + fsw_u64 lcn, cnt; + + while(len > 0 && get_extent(&ptr, &len, &lcn, &cnt, &pos)==FSW_SUCCESS) { + if(lcn) { + for(; cnt>0; lcn++, cnt--) { + fsw_u8 *block; + if (fsw_block_get(&vol->g, lcn, 0, (void **)&block) != FSW_SUCCESS) + { + FreePool(*optrp); + *optrp = NULL; + *olenp = 0; + return FSW_VOLUME_CORRUPTED; + } + fsw_memcpy(buf, block, clustersize > olen ? olen : clustersize); + fsw_block_release(&vol->g, lcn, block); + + buf += clustersize; + olen -= clustersize; + } + } else { + buf += cnt << vol->clbits; + olen -= cnt << vol->clbits; + } + } + return FSW_SUCCESS; +} + +static void init_mft(struct fsw_ntfs_volume *vol, struct ntfs_mft *mft, fsw_u64 mftno) +{ + mft->mftno = mftno; + mft->buf = AllocatePool(1<mftbits); + mft->atlst = NULL; + mft->atlen = 0; +} + +static void free_mft(struct ntfs_mft *mft) +{ + if(mft->buf) FreePool(mft->buf); + if(mft->atlst) FreePool(mft->atlst); +} + +static fsw_status_t load_atlist(struct fsw_ntfs_volume *vol, struct ntfs_mft *mft) +{ + fsw_u8 *ptr; + int len; + fsw_status_t err = find_attribute_direct(mft->buf, 1<mftbits, AT_ATTRIBUTE_LIST, &ptr, &len); + if(err != FSW_SUCCESS) + return err; + return read_attribute_direct(vol, ptr, len, &mft->atlst, &mft->atlen); +} + +static fsw_status_t read_mft(struct fsw_ntfs_volume *vol, fsw_u8 *mft, fsw_u64 mftno) +{ + int l = 0; + int r = vol->extmap.used - 1; + int m; + fsw_u64 vcn = (mftno << vol->mftbits) >> vol->clbits; + struct extent_slot *e = vol->extmap.extent; + + while(l <= r) { + m = (l+r)/2; + if(vcn < e[m].vcn) { + r = m - 1; + } else if(vcn >= e[m].vcn + e[m].cnt) { + l = m + 1; + } else if(vol->mftbits <= vol->clbits) { + fsw_u64 lcn = e[m].lcn + (vcn - e[m].vcn); + int offset = (mftno << vol->mftbits) & ((1<clbits)-1); + fsw_u8 *buffer; + fsw_status_t err; + + if(e[m].lcn + 1 == 0) + return FSW_VOLUME_CORRUPTED; + + if ((err = fsw_block_get(&vol->g, lcn, 0, (void **)&buffer)) != FSW_SUCCESS) + return FSW_VOLUME_CORRUPTED; + + fsw_memcpy(mft, buffer+offset, 1<mftbits); + fsw_block_release(&vol->g, lcn, buffer); + return fixup(mft, "FILE", 1<sctbits, 1<mftbits); + } else { + fsw_u8 *p = mft; + fsw_u64 lcn = e[m].lcn + (vcn - e[m].vcn); + fsw_u64 ecnt = e[m].cnt - (vcn - e[m].vcn); + int count = 1 << (vol->mftbits - vol->clbits); + fsw_status_t err; + + if(e[m].lcn + 1 == 0) + return FSW_VOLUME_CORRUPTED; + while(count-- > 0) { + fsw_u8 *buffer; + if ((err = fsw_block_get(&vol->g, lcn, 0, (void **)&buffer)) != FSW_SUCCESS) + return FSW_VOLUME_CORRUPTED; + fsw_memcpy(p, buffer, 1<clbits); + fsw_block_release(&vol->g, lcn, buffer); + + p += 1<clbits; + ecnt--; + vcn++; + if(count==0) break; + if(ecnt > 0) { + lcn++; + } else if(++m >= vol->extmap.used || e[m].vcn != vcn) { + return FSW_VOLUME_CORRUPTED; + } else { + lcn = e[m].lcn; + ecnt = e[m].cnt; + } + } + return fixup(mft, "FILE", 1<sctbits, 1<mftbits); + } + } + return FSW_NOT_FOUND; +} + +static void init_attr(struct fsw_ntfs_volume *vol, struct ntfs_attr *attr, int type) +{ + fsw_memzero(attr, sizeof(*attr)); + attr->type = type; + attr->emftno = BADMFT; +} + +static void free_attr(struct ntfs_attr *attr) +{ + if(attr->emft) FreePool(attr->emft); +} + +static fsw_status_t find_attribute(struct fsw_ntfs_volume *vol, struct ntfs_mft *mft, struct ntfs_attr *attr, fsw_u64 vcn) +{ + fsw_u8 *buf = mft->buf; + if(mft->atlst && mft->atlen) { + fsw_status_t err; + fsw_u64 mftno; + int pos = 0; + + err = find_attrlist_direct(mft->atlst, mft->atlen, attr->type, vcn, &mftno, &pos); + if(err != FSW_SUCCESS) + return err; + + if(mftno == mft->mftno) { + buf = mft->buf; + } else if(mftno == attr->emftno && attr->emft) { + buf = attr->emft; + } else { + attr->emftno = BADMFT; + err = read_mft(vol, attr->emft, mftno); + if(err != FSW_SUCCESS) + return err; + attr->emftno = mftno; + buf = attr->emft; + } + } + return find_attribute_direct(buf, 1<mftbits, attr->type, &attr->ptr, &attr->len); +} + +static fsw_status_t read_small_attribute(struct fsw_ntfs_volume *vol, struct ntfs_mft *mft, int type, fsw_u8 **optrp, int *olenp) +{ + fsw_status_t err; + struct ntfs_attr attr; + + init_attr(vol, &attr, type); + err = find_attribute(vol, mft, &attr, 0); + if(err == FSW_SUCCESS) + err = read_attribute_direct(vol, attr.ptr, attr.len, optrp, olenp); + free_attr(&attr); + return err; +} + +static void add_single_mft_map(struct fsw_ntfs_volume *vol, fsw_u8 *mft) +{ + fsw_u8 *ptr; + int len; + + if(find_attribute_direct(mft, 1<mftbits, AT_DATA, &ptr, &len)!=FSW_SUCCESS) + return; + + if(attribute_ondisk(ptr, len) == 0) + return; + + fsw_u64 vcn = GETU64(ptr, 0x10); + int off = GETU16(ptr, 0x20); + ptr += off; + len -= off; + + fsw_u64 pos = 0; + fsw_u64 lcn, cnt; + + while(len > 0 && get_extent(&ptr, &len, &lcn, &cnt, &pos)==FSW_SUCCESS) { + if(lcn) { + int u = vol->extmap.used; + if(u >= vol->extmap.total) { + vol->extmap.total = vol->extmap.extent ? u*2 : 16; + struct extent_slot *e = AllocatePool(vol->extmap.total * sizeof(struct extent_slot)); + if(vol->extmap.extent) { + fsw_memcpy(e, vol->extmap.extent, u*sizeof(struct extent_slot)); + FreePool(vol->extmap.extent); + } + vol->extmap.extent = e; + } + vol->extmap.extent[u].vcn = vcn; + vol->extmap.extent[u].lcn = lcn; + vol->extmap.extent[u].cnt = cnt; + vol->extmap.used++; + } + vcn += cnt; + } +} + +static void add_mft_map(struct fsw_ntfs_volume *vol, struct ntfs_mft *mft) +{ + load_atlist(vol, mft); + add_single_mft_map(vol, mft->buf); + if(mft->atlst == NULL) return; + + fsw_u64 mftno; + int pos = 0; + + fsw_u8 *emft = AllocatePool(1<mftbits); + while(find_attrlist_direct(mft->atlst, mft->atlen, AT_DATA, 0, &mftno, &pos) == FSW_SUCCESS) { + if(mftno == 0) continue; + if(read_mft(vol, emft, mftno)==FSW_SUCCESS) + add_single_mft_map(vol, emft); + } + FreePool(emft); +} + +static int tobits(fsw_u32 val) +{ + return 31 - __builtin_clz(val); +} + +static fsw_status_t fsw_ntfs_volume_mount(struct fsw_volume *volg) +{ + struct fsw_ntfs_volume *vol = (struct fsw_ntfs_volume *)volg; + fsw_status_t err; + fsw_u8 *buffer; + int sector_size; + int cluster_size; + signed char tmp; + fsw_u64 mft_start[2]; + struct ntfs_mft mft0; + + fsw_memzero((char *)vol+sizeof(*volg), sizeof(*vol)-sizeof(*volg)); + fsw_set_blocksize(volg, 512, 512); + if ((err = fsw_block_get(volg, 0, 0, (void **)&buffer)) != FSW_SUCCESS) + return FSW_UNSUPPORTED; + + if (!fsw_memeq(buffer+3, "NTFS ", 8)) + return FSW_UNSUPPORTED; + + sector_size = GETU16(buffer, 0xB); + if(sector_size==0 || (sector_size & (sector_size-1)) || sector_size < 0x100 || sector_size > 0x1000) + return FSW_UNSUPPORTED; + + vol->sctbits = tobits(sector_size); + vol->totalbytes = GETU64(buffer, 0x28) << vol->sctbits; + Print(L"NTFS size=%ld M\n", vol->totalbytes>>20); + + cluster_size = GETU8(buffer, 0xD) * sector_size; + if(cluster_size==0 || (cluster_size & (cluster_size-1)) || cluster_size > 0x10000) + return FSW_UNSUPPORTED; + + vol->clbits = tobits(cluster_size); + + tmp = GETU8(buffer, 0x40); + if(tmp > 0) + vol->mftbits = vol->clbits + tobits(tmp); + else + vol->mftbits = -tmp; + + if(vol->mftbits < vol->sctbits || vol->mftbits > 16) + return FSW_UNSUPPORTED; + + tmp = GETU8(buffer, 0x44); + if(tmp > 0) + vol->idxbits = vol->clbits + tobits(tmp); + else + vol->idxbits = -tmp; + + if(vol->idxbits < vol->sctbits || vol->idxbits > 16) + return FSW_UNSUPPORTED; + + mft_start[0] = GETU64(buffer, 0x30); + mft_start[1] = GETU64(buffer, 0x38); + + fsw_block_release(volg, 0, (void *)buffer); + fsw_set_blocksize(volg, cluster_size, cluster_size); + + init_mft(vol, &mft0, MFTNO_MFT); + for(tmp=0; tmp<2; tmp++) { + fsw_u8 *ptr = mft0.buf; + int len = 1 << vol->mftbits; + fsw_u64 lcn = mft_start[tmp]; + while(len > 0) { + if ((err = fsw_block_get(volg, lcn, 0, (void **)&buffer)) != FSW_SUCCESS) + { + free_mft(&mft0); + return FSW_UNSUPPORTED; + } + int n = vol->mftbits < vol->clbits ? (1<mftbits) : cluster_size; + fsw_memcpy(ptr, buffer, n); + fsw_block_release(volg, lcn, (void *)buffer); + ptr += n; + len -= n; + lcn++; + } + err = fixup(mft0.buf, "FILE", sector_size, 1<mftbits); + if(err != FSW_SUCCESS) + return err; + } + + add_mft_map(vol, &mft0); + + { + int i; + for(i=0; iextmap.used; i++) + Print(L"extent %d: vcn=%lx lcn=%lx len=%lx\n", + i, + vol->extmap.extent[i].vcn, + vol->extmap.extent[i].lcn, + vol->extmap.extent[i].cnt); + } + + free_mft(&mft0); + + /* load $Volume name */ + init_mft(vol, &mft0, MFTNO_VOLUME); + fsw_u8 *ptr; + int len; + if(read_mft(vol, mft0.buf, MFTNO_VOLUME)==FSW_SUCCESS && + find_attribute_direct(mft0.buf, 1<mftbits, AT_VOLUME_NAME, &ptr, &len)==FSW_SUCCESS && + GETU8(ptr, 8)==0) + { + struct fsw_string s; + s.type = FSW_STRING_TYPE_UTF16_LE; + s.size = GETU16(ptr, 0x10); + s.len = s.size / 2; + s.data = ptr + GETU16(ptr, 0x14); + Print(L"Volume name [%.*ls]\n", s.len, s.data); + err = fsw_strdup_coerce(&volg->label, volg->host_string_type, &s); + } + free_mft(&mft0); + + err = fsw_dnode_create_root(volg, MFTNO_ROOT, &volg->root); + if (err) + return err; + + return FSW_SUCCESS; +} + +static void fsw_ntfs_volume_free(struct fsw_volume *volg) +{ + struct fsw_ntfs_volume *vol = (struct fsw_ntfs_volume *)volg; + if(vol->extmap.extent) + FreePool(vol->extmap.extent); + if(vol->upcase && vol->upcase != upcase) + FreePool((void *)vol->upcase); +} + +static fsw_status_t fsw_ntfs_volume_stat(struct fsw_volume *volg, struct fsw_volume_stat *sb) +{ + struct fsw_ntfs_volume *vol = (struct fsw_ntfs_volume *)volg; + sb->total_bytes = vol->totalbytes; + /* reading through cluster bitmap is too costly */ + sb->free_bytes = 0; + return FSW_SUCCESS; +} + +static fsw_status_t fsw_ntfs_dnode_fill(struct fsw_volume *volg, struct fsw_dnode *dnog) +{ + struct fsw_ntfs_volume *vol = (struct fsw_ntfs_volume *)volg; + struct fsw_ntfs_dnode *dno = (struct fsw_ntfs_dnode *)dnog; + fsw_status_t err; + int len; + + fsw_memzero((char *)dno+sizeof(*dnog), sizeof(*dno)-sizeof(*dnog)); + init_mft(vol, &dno->mft, dno->g.dnode_id); + err = read_mft(vol, dno->mft.buf, dno->g.dnode_id); + if(err != FSW_SUCCESS) + return err; + + len = GETU8(dno->mft.buf, 22); + if( (len & 1) == 0 ) + return FSW_NOT_FOUND; + + load_atlist(vol, &dno->mft); + + if(read_small_attribute(vol, &dno->mft, AT_REPARSE_POINT, &dno->cbuf, &len)==FSW_SUCCESS) { + switch(GETU32(dno->cbuf, 0)) { + case 0xa0000003: + case 0xa000000c: + dno->g.size = len; + dno->g.type = FSW_DNODE_TYPE_SYMLINK; + dno->islink = 1; + dno->fsize = len; + return FSW_SUCCESS; + default: + FreePool(dno->cbuf); + dno->cbuf = NULL; + }; + } + + if( (len & 2) ) { + dno->g.type = FSW_DNODE_TYPE_DIR; + /* $INDEX_ROOT:$I30 must present */ + err = read_small_attribute(vol, &dno->mft, AT_INDEX_ROOT|AT_I30, &dno->idxroot, &dno->rootsz); + if(err != FSW_SUCCESS) + { + Print(L"dno_fill INDEX_ROOT:$I30 error %d\n", err); + return err; + } + + dno->idxsz = GETU32(dno->idxroot, 8); + if(dno->idxsz == 0) + dno->idxsz = 1<idxbits; + + /* $Bitmap:$I30 is optional */ + err = read_small_attribute(vol, &dno->mft, AT_BITMAP|AT_I30, &dno->idxbmp, &dno->bmpsz); + if(err != FSW_SUCCESS && err != FSW_NOT_FOUND) + { + Print(L"dno_fill $Bitmap:$I30 error %d\n", err); + return err; + } + + /* $INDEX_ALLOCATION:$I30 is optional */ + init_attr(vol, &dno->attr, AT_INDEX_ALLOCATION|AT_I30); + err = find_attribute(vol, &dno->mft, &dno->attr, 0); + if(err == FSW_SUCCESS) { + dno->has_idxtree = 1; + dno->fsize = attribute_size(dno->attr.ptr, dno->attr.len); + dno->finited = dno->fsize; + } else if(err != FSW_NOT_FOUND) { + Print(L"dno_fill $INDEX_ALLOCATION:$I30 error %d\n", err); + return err; + } + + } else { + dno->g.type = FSW_DNODE_TYPE_FILE; + init_attr(vol, &dno->attr, AT_DATA); + err = find_attribute(vol, &dno->mft, &dno->attr, 0); + if(err != FSW_SUCCESS) + { + Print(L"dno_fill AT_DATA error %d\n", err); + return err; + } + dno->embeded = !attribute_ondisk(dno->attr.ptr, dno->attr.len); + dno->fsize = attribute_size(dno->attr.ptr, dno->attr.len); + dno->finited = attribute_inited_size(dno->attr.ptr, dno->attr.len); + if(attribute_encrypted(dno->attr.ptr, dno->attr.len)) + dno->unreadable = 1; + else if(attribute_compressed_future(dno->attr.ptr, dno->attr.len)) + dno->unreadable = 1; + else if(attribute_compressed(dno->attr.ptr, dno->attr.len)) + dno->compressed = 1; + dno->cvcn = BADVCN; + dno->g.size = dno->fsize; + } + return FSW_SUCCESS; +} + +static void fsw_ntfs_dnode_free(struct fsw_volume *vol, struct fsw_dnode *dnog) +{ + struct fsw_ntfs_dnode *dno = (struct fsw_ntfs_dnode *)dnog; + free_mft(&dno->mft); + free_attr(&dno->attr); + if(dno->idxroot) + FreePool(dno->idxroot); + if(dno->idxbmp) + FreePool(dno->idxbmp); + if(dno->cbuf) + FreePool(dno->cbuf); +} + +static fsw_u32 get_ntfs_time(fsw_u8 *buf, int pos) +{ + fsw_u64 t = GETU64(buf, pos); + t = FSW_U64_DIV(t, 10000000); + t -= 134774ULL * 86400; + return t; +} + +static fsw_status_t fsw_ntfs_dnode_stat(struct fsw_volume *volg, struct fsw_dnode *dnog, struct fsw_dnode_stat *sb) +{ + struct fsw_ntfs_volume *vol = (struct fsw_ntfs_volume *)volg; + struct fsw_ntfs_dnode *dno = (struct fsw_ntfs_dnode *)dnog; + fsw_status_t err; + fsw_u16 attr; + fsw_u8 *ptr; + int len; + + err = find_attribute_direct(dno->mft.buf, 1<mftbits, AT_STANDARD_INFORMATION, &ptr, &len); + + if(err != FSW_SUCCESS || GETU8(ptr, 8)) + return err; + + ptr += GETU16(ptr, 0x14); + attr = GETU8(ptr, 0x20); /* only lower 8 of 32 bit is used */ + attr &= EFI_FILE_READ_ONLY | EFI_FILE_HIDDEN | EFI_FILE_SYSTEM | EFI_FILE_ARCHIVE; + /* add DIR again if symlink */ + if(GETU8(dno->mft.buf, 22) & 2) + attr |= EFI_FILE_DIRECTORY; + + fsw_store_attr_efi(sb, attr); + sb->used_bytes = dno->fsize; + fsw_store_time_posix(sb, FSW_DNODE_STAT_ATIME, get_ntfs_time(ptr, 24)); + fsw_store_time_posix(sb, FSW_DNODE_STAT_CTIME, get_ntfs_time(ptr, 8)); + fsw_store_time_posix(sb, FSW_DNODE_STAT_MTIME, get_ntfs_time(ptr, 16)); + + return FSW_SUCCESS; +} + +static fsw_status_t fsw_ntfs_dnode_get_lcn(struct fsw_ntfs_volume *vol, struct fsw_ntfs_dnode *dno, fsw_u64 vcn, fsw_u64 *lcnp) +{ + fsw_status_t err; + if(vcn >= dno->cext.vcn && vcn < dno->cext.vcn+dno->cext.cnt) { + *lcnp = dno->cext.lcn + vcn - dno->cext.vcn; + return FSW_SUCCESS; + } + if(!attribute_has_vcn(dno->attr.ptr, dno->attr.len, vcn)) { + err = find_attribute(vol, &dno->mft, &dno->attr, vcn); + if( err != FSW_SUCCESS ) + return err; + if(!attribute_has_vcn(dno->attr.ptr, dno->attr.len, vcn)) + return FSW_VOLUME_CORRUPTED; + } + fsw_u8 *ptr = dno->attr.ptr; + int len = dno->attr.len; + fsw_u64 pos = 0; + fsw_u64 lcn, cnt; + fsw_u64 svcn = attribute_first_vcn(ptr, len); + fsw_u64 evcn = attribute_last_vcn(ptr, len) + 1; + int off = GETU16(ptr, 0x20); + ptr += off; + len -= off; + while(len > 0 && get_extent(&ptr, &len, &lcn, &cnt, &pos)==FSW_SUCCESS) { + if(vcn >= svcn && vcn < svcn+cnt) { + dno->cext.vcn = svcn; + dno->cext.lcn = lcn; + dno->cext.cnt = cnt; + if(lcn == 0) + return FSW_NOT_FOUND; + *lcnp = lcn + vcn - svcn; + return FSW_SUCCESS; + } + svcn += cnt; + if(svcn >= evcn) + break; + } + return FSW_NOT_FOUND; +} + +static int fsw_ntfs_read_buffer(struct fsw_ntfs_volume *vol, struct fsw_ntfs_dnode *dno, fsw_u8 *buf, fsw_u64 offset, int size) +{ + if(dno->embeded) { + fsw_u8 *ptr; + int len; + attribute_get_embeded(dno->attr.ptr, dno->attr.len, &ptr, &len); + if(size > len) + size = len; + fsw_memcpy(buf, ptr, size); + return size; + } + + if(!dno->attr.ptr || !dno->attr.len) { + Print(L"BAD--------: attr.ptr %p attr.len %x cleared\n", dno->attr.ptr, dno->attr.len); + if(find_attribute(vol, &dno->mft, &dno->attr, 0) != FSW_SUCCESS) + return 0; + } + + if(offset >= dno->fsize) + return 0; + if(offset + size >= dno->fsize) + size = dno->fsize - offset; + + if(offset >= dno->finited) { + fsw_memzero(buf, size); + return size; + } + + int ret = 0; + int zsize = 0; + if(offset + size >= dno->finited) { + zsize = offset + size - dno->finited; + size = dno->finited - offset; + } + + fsw_u64 vcn = offset >> vol->clbits; + int boff = offset & ((1<clbits)-1); + fsw_u64 lcn = 0; + + while(size > 0) { + fsw_u8 *block; + fsw_status_t err; + int bsz; + + err = fsw_ntfs_dnode_get_lcn(vol, dno, vcn, &lcn); + if (err != FSW_SUCCESS) break; + + err = fsw_block_get(&vol->g, lcn, 0, (void **)&block); + if (err != FSW_SUCCESS) break; + + bsz = (1<clbits) - boff; + if(bsz > size) + bsz = size; + + fsw_memcpy(buf, block+boff, bsz); + fsw_block_release(&vol->g, lcn, block); + + ret += bsz; + buf += bsz; + size -= bsz; + boff = 0; + } + if(size==0 && zsize > 0) { + fsw_memzero(buf, zsize); + ret += zsize; + } + return ret; +} + +static fsw_status_t fsw_ntfs_get_extent_embeded(struct fsw_ntfs_volume *vol, struct fsw_ntfs_dnode *dno, struct fsw_extent *extent) +{ + if(extent->log_start > 0) + return FSW_NOT_FOUND; + extent->log_count = 1; + extent->buffer = AllocatePool(1<clbits); + fsw_u8 *ptr; + int len; + attribute_get_embeded(dno->attr.ptr, dno->attr.len, &ptr, &len); + if(len > (1<clbits)) + len = 1<clbits; + fsw_memcpy(extent->buffer, ptr, len); + extent->type = FSW_EXTENT_TYPE_BUFFER; + return FSW_SUCCESS; +} + +static int ntfs_decomp_1page(fsw_u8 *src, int slen, fsw_u8 *dst) { + int soff = 0; + int doff = 0; + while(soff < slen) { + int j; + int tag = src[soff++]; + for(j = 0; j < 8 && soff < slen; j++) { + if(tag & (1< slen) + return -1; + len = GETU16(src, soff); soff += 2; + bits = __builtin_clz((doff-1)>>3)-19; + back = (len >> bits) + 1; + len = (len & ((1< 0x1000) + return -1; + while(len-- > 0) { + dst[doff] = dst[doff-back]; + doff++; + } + } else { + if(doff >= 0x1000) + return -1; + dst[doff++] = src[soff++]; + } + } + } + return doff; +} + +static int ntfs_decomp(fsw_u8 *src, int slen, fsw_u8 *dst, int npage) { + fsw_u8 *se = src + slen; + fsw_u8 *de = dst + (npage<<12); + int i; + for(i=0; i se || dst + 0x1000 > de) + return -1; + + if(!comp) { + fsw_memcpy(dst, src, slen); + if(slen < 0x1000) + fsw_memzero(dst+slen, 0x1000-slen); + } else if(slen == 1) { + fsw_memzero(dst, 0x1000); + } else { + int dlen = ntfs_decomp_1page(src, slen, dst); + if(dlen < 0) + return -1; + if(dlen < 0x1000) + fsw_memzero(dst+dlen, 0x1000-dlen); + } + src += slen; + dst += 0x1000; + } + return 0; +} + +static fsw_status_t fsw_ntfs_get_extent_compressed(struct fsw_ntfs_volume *vol, struct fsw_ntfs_dnode *dno, struct fsw_extent *extent) +{ + if(vol->clbits > 16) + return FSW_VOLUME_CORRUPTED; + + if((extent->log_start << vol->clbits) > dno->fsize) + return FSW_NOT_FOUND; + + int i; + fsw_u64 vcn = extent->log_start & ~15; + + if(vcn == dno->cvcn) + goto hit; + dno->cvcn = vcn; + dno->cperror = 0; + dno->cpfull = 0; + dno->cpzero = 0; + + for(i=0; i<16; i++) { + fsw_status_t err; + err = fsw_ntfs_dnode_get_lcn(vol, dno, vcn+i, &dno->clcn[i]); + if(err == FSW_NOT_FOUND) { + break; + } else if(err != FSW_SUCCESS) { + Print(L"BAD LCN\n"); + dno->cperror = 1; + return FSW_VOLUME_CORRUPTED; + } + } + if(i == 0) + dno->cpzero = 1; + else if(i==16) + dno->cpfull = 1; + else { + if(dno->cbuf == NULL) + dno->cbuf = AllocatePool(16<clbits); + fsw_u8 *src = AllocatePool(i << vol->clbits); + int b; + for(b=0; bg, dno->clcn[b], 0, (void **)&block) != FSW_SUCCESS) { + dno->cperror = 1; + Print(L"Read ERROR at block %d\n", i); + break; + } + fsw_memcpy(src+(b<clbits), block, 1<clbits); + fsw_block_release(&vol->g, dno->clcn[b], block); + } + + if(dno->fsize >= ((vcn+16)<clbits)) + b = 16<clbits>>12; + else + b = (dno->fsize - (vcn << vol->clbits) + 0xfff)>>12; + if(!dno->cperror && ntfs_decomp(src, i<clbits, dno->cbuf, b) < 0) + dno->cperror = 1; + FreePool(src); + } +hit: + if(dno->cperror) + return FSW_VOLUME_CORRUPTED; + i = extent->log_start - vcn; + if(dno->cpfull) { + fsw_u64 lcn = dno->clcn[i]; + extent->phys_start = lcn; + extent->log_count = 1; + extent->type = FSW_EXTENT_TYPE_PHYSBLOCK; + for(i++, lcn++; i<16 && lcn==dno->clcn[i]; i++, lcn++) + extent->log_count++; + } else if(dno->cpzero) { + extent->log_count = 16 - i; + extent->buffer = NULL; + extent->type = FSW_EXTENT_TYPE_SPARSE; + } else { + extent->log_count = 1; + extent->buffer = AllocatePool(1<clbits); + fsw_memcpy(extent->buffer, dno->cbuf + (i<clbits), 1<clbits); + extent->type = FSW_EXTENT_TYPE_BUFFER; + } + return FSW_SUCCESS; +} + +static fsw_status_t fsw_ntfs_get_extent_sparse(struct fsw_ntfs_volume *vol, struct fsw_ntfs_dnode *dno, struct fsw_extent *extent) +{ + fsw_status_t err; + + if((extent->log_start << vol->clbits) > dno->fsize) + return FSW_NOT_FOUND; + if((extent->log_start << vol->clbits) > dno->finited) + { + extent->log_count = 1; + extent->buffer = NULL; + extent->type = FSW_EXTENT_TYPE_SPARSE; + return FSW_SUCCESS; + } + fsw_u64 lcn; + err = fsw_ntfs_dnode_get_lcn(vol, dno, extent->log_start, &lcn); + if(err == FSW_NOT_FOUND) { + extent->log_count = 1; + extent->buffer = NULL; + extent->type = FSW_EXTENT_TYPE_SPARSE; + return FSW_SUCCESS; + } + if(err != FSW_SUCCESS) + return err; + extent->phys_start = lcn; + extent->log_count = 1; + if(extent->log_start >= dno->cext.vcn && extent->log_start < dno->cext.vcn+dno->cext.cnt) + extent->log_count = dno->cext.cnt + extent->log_start - dno->cext.vcn; + extent->type = FSW_EXTENT_TYPE_PHYSBLOCK; + return FSW_SUCCESS; +} + +static fsw_status_t fsw_ntfs_get_extent(struct fsw_volume *volg, struct fsw_dnode *dnog, struct fsw_extent *extent) +{ + struct fsw_ntfs_volume *vol = (struct fsw_ntfs_volume *)volg; + struct fsw_ntfs_dnode *dno = (struct fsw_ntfs_dnode *)dnog; + + if(dno->unreadable) + return FSW_UNSUPPORTED; + if(dno->embeded) + return fsw_ntfs_get_extent_embeded(vol, dno, extent); + if(dno->compressed) + return fsw_ntfs_get_extent_compressed(vol, dno, extent); + return fsw_ntfs_get_extent_sparse(vol, dno, extent); +} + +static fsw_status_t load_upcase(struct fsw_ntfs_volume *vol) +{ + fsw_status_t err; + struct ntfs_mft mft; + init_mft(vol, &mft, MFTNO_UPCASE); + err = read_mft(vol, mft.buf, MFTNO_UPCASE); + if(err == FSW_SUCCESS) { + if((err = read_small_attribute(vol, &mft, AT_DATA, (fsw_u8 **)&vol->upcase, &vol->upcount))==FSW_SUCCESS) { + vol->upcount /= 2; +#ifndef FSW_LITTLE_ENDIAN + int i; + for( i=0; iupcount; i++) + vol->upcase[i] = fsw_u16_le_swap(vol->upcase[i]); +#endif + } + } + free_mft(&mft); + return err; +} + +static int ntfs_filename_cmp(struct fsw_ntfs_volume *vol, fsw_u8 *p1, int s1, fsw_u8 *p2, int s2) +{ + while(s1 > 0 && s2 > 0) { + fsw_u16 c1 = GETU16(p1,0); + fsw_u16 c2 = GETU16(p2,0); + if(c1 < 0x80 || c2 < 0x80) { + if(c1 < 0x80) c1 = upcase[c1]; + if(c2 < 0x80) c2 = upcase[c2]; + } else { + /* + * Only load upcase table if both char is international. + * We assume international char never upcased to ASCII. + */ + if(!vol->upcase) { + load_upcase(vol); + if(!vol->upcase) { + /* use raw value & prevent load again */ + vol->upcase = upcase; + vol->upcount = 0; + } + } + if(c1 < vol->upcount) c1 = vol->upcase[c1]; + if(c2 < vol->upcount) c2 = vol->upcase[c2]; + } + if(c1 < c2) + return -1; + if(c1 > c2) + return 1; + p1+=2; + p2+=2; + s1--; + s2--; + } + if(s1 < s2) + return -1; + if(s1 > s2) + return 1; + return 0; +} + +static fsw_status_t fsw_ntfs_create_subnode(struct fsw_ntfs_dnode *dno, fsw_u8 *buf, struct fsw_dnode **child_dno) +{ + fsw_u64 mftno = GETU64(buf, 0) & MFTMASK; + if(mftno < MFTNO_META) + return FSW_NOT_FOUND; + + int type = GETU32(buf, 0x48) & 0x10000000 ? FSW_DNODE_TYPE_DIR: FSW_DNODE_TYPE_FILE; + struct fsw_string s; + s.type = FSW_STRING_TYPE_UTF16_LE; + s.len = GETU8(buf, 0x50); + s.size = s.len * 2; + s.data = buf + 0x52; + + return fsw_dnode_create(&dno->g, mftno, type, &s, child_dno); +} + +static fsw_u8 *fsw_ntfs_read_index_block(struct fsw_ntfs_volume *vol, struct fsw_ntfs_dnode *dno, fsw_u64 block) +{ + if(dno->cbuf==NULL) + dno->cbuf = AllocatePool(dno->idxsz); + else if(block == dno->cvcn) + return dno->cbuf; + + dno->cvcn = 0; + if(fsw_ntfs_read_buffer(vol, dno, dno->cbuf, (block-1)*dno->idxsz, dno->idxsz) != dno->idxsz) + return NULL; + if(fixup(dno->cbuf, "INDX", 1<sctbits, dno->idxsz) != FSW_SUCCESS) + return NULL; + + dno->cvcn = block; + return dno->cbuf; +} + +static fsw_status_t fsw_ntfs_dir_lookup(struct fsw_volume *volg, struct fsw_dnode *dnog, struct fsw_string *lookup_name, struct fsw_dnode **child_dno) +{ + struct fsw_ntfs_volume *vol = (struct fsw_ntfs_volume *)volg; + struct fsw_ntfs_dnode *dno = (struct fsw_ntfs_dnode *)dnog; + int depth = 0; + struct fsw_string s; + fsw_u8 *buf; + int len; + int off; + fsw_status_t err; + fsw_u64 block; + + *child_dno = NULL; + err = fsw_strdup_coerce(&s, FSW_STRING_TYPE_UTF16_LE, lookup_name); + if(err) + return err; + + /* start from AT_INDEX_ROOT */ + buf = dno->idxroot + 16; + len = dno->rootsz - 16; + if(len < 0x18) + return FSW_NOT_FOUND; + + while(depth < 10) { + /* real index size */ + if(GETU32(buf, 4) < len) + len = GETU32(buf, 4); + + /* skip index header */ + off = GETU32(buf, 0); + if(off >= len) + return FSW_NOT_FOUND; + + block = 0; + while(off + 0x18 <= len) { + int flag = GETU8(buf, off+12); + int next = off + GETU16(buf, off+8); + int cmp; + + if(flag & 2) { + /* the end of index entry */ + cmp = -1; + Print(L"depth %d len %x off %x flag %x next %x cmp %d\n", depth, len, off, flag, next, cmp); + } else { + int nlen = GETU8(buf, off+0x50); + fsw_u8 *name = buf+off+0x52; + cmp = ntfs_filename_cmp(vol, s.data, s.len, name, nlen); + Print(L"depth %d len %x off %x flag %x next %x cmp %d name %d[%.*ls]\n", depth, len, off, flag, next, cmp, nlen, nlen, name); + } + + if(cmp == 0) { + return fsw_ntfs_create_subnode(dno, buf+off, child_dno); + } else if(cmp < 0) { + if(!(flag & 1) || !dno->has_idxtree) + return FSW_NOT_FOUND; + block = GETU64(buf, next-8) + 1; + break; + } else { /* cmp > 0 */ + off = next; + } + } + if(!block) + break; + + if(!(buf = fsw_ntfs_read_index_block(vol, dno, block))) + break; + buf += 24; + len = dno->idxsz - 24; + depth++; + } + + return FSW_NOT_FOUND; +} + +static inline void set_shand_pos( struct fsw_shandle *shand, int block, int off) +{ + shand->pos = (((fsw_u64)block) << 32) + off; +} + +static inline int test_idxbmp(struct fsw_ntfs_dnode *dno, int block) +{ + int mask; + if(dno->idxbmp==NULL) + return 1; + block--; + mask = 1 << (block & 7); + block >>= 3; + if(block > dno->bmpsz) + return 0; + return dno->idxbmp[block] & mask; +} + +#define shand_pos(x,y) (((fsw_u64)(x)<<32)|(y)) +static fsw_status_t fsw_ntfs_dir_read(struct fsw_volume *volg, struct fsw_dnode *dnog, struct fsw_shandle *shand, struct fsw_dnode **child_dno) +{ + struct fsw_ntfs_volume *vol = (struct fsw_ntfs_volume *)volg; + struct fsw_ntfs_dnode *dno = (struct fsw_ntfs_dnode *)dnog; + /* + * high 32 bit: index block# + * 0 --> index root + * >0 --> vcn+1 + * low 32 bit: index offset + */ + int off = shand->pos & 0xFFFFFFFF; + int block = shand->pos >> 32; + int mblocks; + + mblocks = FSW_U64_DIV(dno->fsize, dno->idxsz); + + while(block <= mblocks) { + fsw_u8 *buf; + int len; + if(block == 0) { + /* AT_INDEX_ROOT */ + buf = dno->idxroot + 16; + len = dno->rootsz - 16; + if(len < 0x18) + goto miss; + } else if(!test_idxbmp(dno, block) || !(buf = fsw_ntfs_read_index_block(vol, dno, block))) + { + /* unused or bad index block */ + goto miss; + } else { + /* AT_INDEX_ALLOCATION block */ + buf += 24; + len = dno->idxsz - 24; + } + if(GETU32(buf, 4) < len) + len = GETU32(buf, 4); + if(off == 0) + off = GETU32(buf, 0); + Print(L"block %d len %x off %x\n", block, len, off); + while(off + 0x18 <= len) { + int flag = GETU8(buf, off+12); + if(flag & 2) break; + int next = off + GETU16(buf, off+8); + Print(L"flag %x next %x nt %x [%.*ls]\n", flag, next, GETU8(buf, off+0x51), GETU8(buf, off+0x50), buf+off+0x52); + if((GETU8(buf, off+0x51) != 2)) { + /* LONG FILE NAME */ + fsw_status_t err = fsw_ntfs_create_subnode(dno, buf+off, child_dno); + if(err != FSW_NOT_FOUND) { + set_shand_pos(shand, block, next); + return err; + } + // skip internal MFT record + } + off = next; + } +miss: + if(!dno->has_idxtree) + break; + block++; + off = 0; + } + set_shand_pos(shand, mblocks+1, 0); + return FSW_NOT_FOUND; +} + +static fsw_status_t fsw_ntfs_readlink(struct fsw_volume *volg, struct fsw_dnode *dnog, struct fsw_string *link) +{ + struct fsw_ntfs_dnode *dno = (struct fsw_ntfs_dnode *)dnog; + fsw_u8 *name; + int i; + int len; + + if(!dno->islink) + return FSW_UNSUPPORTED; + + name = dno->cbuf + 0x10 + GETU16(dno->cbuf, 8); + len = GETU16(dno->cbuf, 10); + if(GETU32(dno->cbuf, 0) == 0xa000000c) + name += 4; + + for(i=0; i 6 && GETU16(name,0)=='/' && GETU16(name,2)=='?' && + GETU16(name,4)=='?' && GETU16(name,6)=='/' && + GETU16(name,10)==':' && GETU16(name,12)=='/' && + (GETU16(name,8)|0x20)>='a' && (GETU16(name,8)|0x20)<='z') + { + len -= 12; + name += 12; + } + struct fsw_string s; + s.type = FSW_STRING_TYPE_UTF16_LE; + s.size = len; + s.len = len/2; + s.data = name; + return fsw_strdup_coerce(link, volg->host_string_type, &s); +} + +// +// Dispatch Table +// + +struct fsw_fstype_table FSW_FSTYPE_TABLE_NAME(ntfs) = { + { FSW_STRING_TYPE_UTF8, 4, 4, "ntfs" }, + sizeof(struct fsw_ntfs_volume), + sizeof(struct fsw_ntfs_dnode), + + fsw_ntfs_volume_mount, + fsw_ntfs_volume_free, + fsw_ntfs_volume_stat, + fsw_ntfs_dnode_fill, + fsw_ntfs_dnode_free, + fsw_ntfs_dnode_stat, + fsw_ntfs_get_extent, + fsw_ntfs_dir_lookup, + fsw_ntfs_dir_read, + fsw_ntfs_readlink, +}; + +// EOF -- 2.39.2