Changes by: antona
Update of /cvsroot/linux-ntfs/linux-ntfs/libntfs
In directory usw-pr-cvs1:/tmp/cvs-serv24237/libntfs
Modified Files:
Makefile.am Makefile.in attrib.c disk_io.c inode.c mft.c
volume.c
Log Message:
huge update!
Index: Makefile.am
===================================================================
RCS file: /cvsroot/linux-ntfs/linux-ntfs/libntfs/Makefile.am,v
retrieving revision 1.13
retrieving revision 1.14
diff -U2 -r1.13 -r1.14
--- Makefile.am 22 Apr 2002 10:34:31 -0000 1.13
+++ Makefile.am 1 Jun 2002 00:41:45 -0000 1.14
@@ -1,9 +1,28 @@
+#
# Before making a release, the LTVERSION string should be modified.
-# The string is of the form C:R:A.
-# - If interfaces have been changed or added, but binary compatibility has
-# been preserved, change to C+1:0:A+1
-# - If binary compatibility has been broken (eg removed or changed interfaces)
-# change to C+1:0:0
-# - If the interface is the same as the previous version, change to C:R+1:A
+# The string is of the form CURRENT:REVISION:AGE.
+#
+# CURRENT (C)
+# The most recent interface number that this library implements.
+#
+# REVISION (R)
+# The implementation number that this library implements.
+#
+# AGE (A)
+# The difference between the newest and oldest interfaces that this
+# library implements. In other works, the library implements all the
+# interface numbers in the range from number 'CURRENT - AGE' to
+# 'CURRENT'.
+#
+# This means that:
+#
+# - If interfaces have been changed or added, but binary compatibility has
+# been preserved, change to C+1:0:A+1
+#
+# - If binary compatibility has been broken (eg removed or changed
+# interfaces) change to C+1:0:0
+#
+# - If the interface is the same as the previous version, change to C:R+1:A
+#
LTVERSION = 4:0:0
Index: Makefile.in
===================================================================
RCS file: /cvsroot/linux-ntfs/linux-ntfs/libntfs/Makefile.in,v
retrieving revision 1.19
retrieving revision 1.20
diff -U2 -r1.19 -r1.20
--- Makefile.in 25 Apr 2002 07:50:34 -0000 1.19
+++ Makefile.in 1 Jun 2002 00:41:45 -0000 1.20
@@ -11,11 +11,30 @@
# PARTICULAR PURPOSE.
+#
# Before making a release, the LTVERSION string should be modified.
-# The string is of the form C:R:A.
-# - If interfaces have been changed or added, but binary compatibility has
-# been preserved, change to C+1:0:A+1
-# - If binary compatibility has been broken (eg removed or changed interfaces)
-# change to C+1:0:0
-# - If the interface is the same as the previous version, change to C:R+1:A
+# The string is of the form CURRENT:REVISION:AGE.
+#
+# CURRENT (C)
+# The most recent interface number that this library implements.
+#
+# REVISION (R)
+# The implementation number that this library implements.
+#
+# AGE (A)
+# The difference between the newest and oldest interfaces that this
+# library implements. In other works, the library implements all the
+# interface numbers in the range from number 'CURRENT - AGE' to
+# 'CURRENT'.
+#
+# This means that:
+#
+# - If interfaces have been changed or added, but binary compatibility has
+# been preserved, change to C+1:0:A+1
+#
+# - If binary compatibility has been broken (eg removed or changed
+# interfaces) change to C+1:0:0
+#
+# - If the interface is the same as the previous version, change to C:R+1:A
+#
Index: attrib.c
===================================================================
RCS file: /cvsroot/linux-ntfs/linux-ntfs/libntfs/attrib.c,v
retrieving revision 1.38
retrieving revision 1.39
diff -U2 -r1.38 -r1.39
--- attrib.c 29 Apr 2002 12:58:34 -0000 1.38
+++ attrib.c 1 Jun 2002 00:41:45 -0000 1.39
@@ -33,4 +33,8 @@
#include "debug.h"
#include "mst.h"
+#include "volume.h"
+#include "types.h"
+#include "layout.h"
+#include "inode.h"
/* FIXME: Need to write the new flags to disk. */
@@ -376,5 +380,5 @@
* Internal function:
*
- * ntfs_attr_init - initialize an ntfs attribute structure
+ * __ntfs_attr_init - primary initialization of an ntfs attribute structure
* @na: ntfs attribute to initialize
* @ni: ntfs inode with which to initialize the ntfs attribute
@@ -382,12 +386,10 @@
* @name: attribute name in little endian Unicode or NULL
* @name_len: length of attribute @name in Unicode characters (if @name given)
- * @state: initial ntfs attribute flags to set in the state of @na
*
- * Initialize the ntfs attribute @na with @ni, @type, @name, @name_len, and
- * @state.
+ * Initialize the ntfs attribute @na with @ni, @type, @name, and @name_len.
*/
-static __inline__ void ntfs_attr_init(ntfs_attr *na, ntfs_inode *ni,
+static __inline__ void __ntfs_attr_init(ntfs_attr *na, ntfs_inode *ni,
const ATTR_TYPES type, uchar_t *name,
- const __u32 name_len, const unsigned long state)
+ const __u32 name_len)
{
na->rl = NULL;
@@ -396,33 +398,127 @@
na->name = name;
na->name_len = name ? name_len : 0;
- na->state = state;
}
/**
- * ntfs_attr_get - allocate/initialize a new ntfs attribute structure
- * @ni: ntfs inode in which the ntfs attribute resides
+ * ntfs_attr_init - initialize an ntfs_attr with data sizes and status
+ * @na:
+ * @non_resident:
+ * @compressed:
+ * @ecnrypted:
+ * @sparse:
+ * @allocated_size:
+ * @data_size:
+ * @initialized_size:
+ * @compressed_size:
+ * @compression_unit:
+ *
+ * Final initialization for an ntfs attribute.
+ */
+void ntfs_attr_init(ntfs_attr *na, const BOOL non_resident,
+ const BOOL compressed, const BOOL encrypted, const BOOL sparse,
+ const __s64 allocated_size, const __s64 data_size,
+ const __s64 initialized_size, const __s64 compressed_size,
+ const __u8 compression_unit)
+{
+ if (!NAttrInitialized(na)) {
+ if (non_resident)
+ NAttrSetNonResident(na);
+ if (compressed)
+ NAttrSetCompressed(na);
+ if (encrypted)
+ NAttrSetEncrypted(na);
+ if (sparse)
+ NAttrSetSparse(na);
+ na->allocated_size = allocated_size;
+ na->data_size = data_size;
+ na->initialized_size = initialized_size;
+ if (compressed || sparse) {
+ ntfs_volume *vol = na->ni->vol;
+
+ na->compressed_size = compressed_size;
+ na->compression_block_clusters = 1 << compression_unit;
+ na->compression_block_size = 1 << (compression_unit +
+ vol->cluster_size_bits);
+ na->compression_block_size_bits = ffs(
+ na->compression_block_size) - 1;
+ }
+ NAttrSetInitialized(na);
+ }
+}
+
+/**
+ * ntfs_attr_open - open an ntfs attribute for access
+ * @ni: open ntfs inode in which the ntfs attribute resides
* @type: attribute type
* @name: attribute name in little endian Unicode or NULL
* @name_len: length of attribute @name in Unicode characters (if @name given)
- * @state: initial ntfs attribute flags to set in the state of @na
*
* Allocate a new ntfs attribute structure, initialize it with @ni, @type,
- * @name, @name_len, and @state, then return it. Return NULL on error with
- * errno set to ENOMEM.
+ * @name, and @name_len, then return it. Return NULL on error with
+ * errno set to the error code.
*
* If looking for an unnamed attribute set @name to NULL. @name_len is not used
* at all in that case.
*/
-ntfs_attr *ntfs_attr_get(ntfs_inode *ni, const ATTR_TYPES type,
- uchar_t *name, const __u32 name_len, const unsigned long state)
+ntfs_attr *ntfs_attr_open(ntfs_inode *ni, const ATTR_TYPES type,
+ uchar_t *name, const __u32 name_len)
{
- ntfs_attr *na = malloc(sizeof(ntfs_attr));
- if (na)
- ntfs_attr_init(na, ni, type, name, name_len, state);
+ ntfs_attr_search_ctx *ctx;
+ ntfs_attr *na;
+ ATTR_RECORD *a;
+ int err;
+
+ Dprintf(__FUNCTION__ "(): Entering for inode 0x%Lx, attr 0x%x.\n",
+ ni->mft_no, type);
+ if (!ni || !ni->vol || !ni->mrec) {
+ errno = EINVAL;
+ return NULL;
+ }
+ na = calloc(sizeof(ntfs_attr), 1);
+ if (!na)
+ return NULL;
+ __ntfs_attr_init(na, ni, type, name, name_len);
+
+ ctx = ntfs_get_attr_search_ctx(ni, NULL);
+ if (!ctx) {
+ err = errno;
+ goto err_out;
+ }
+
+ if (ntfs_lookup_attr(type, name, name_len, 0, 0, NULL, 0, ctx)) {
+ err = errno;
+ goto put_err_out;
+ }
+ a = ctx->attr;
+ if (a->non_resident) {
+ BOOL cs = a->flags & (ATTR_IS_COMPRESSED | ATTR_IS_SPARSE);
+ ntfs_attr_init(na, TRUE, a->flags & ATTR_IS_COMPRESSED,
+ a->flags & ATTR_IS_ENCRYPTED,
+ a->flags & ATTR_IS_SPARSE,
+ sle64_to_cpu(a->allocated_size),
+ sle64_to_cpu(a->data_size),
+ sle64_to_cpu(a->initialized_size),
+ cs ? sle64_to_cpu(a->compressed_size) : 0,
+ cs ? a->compression_unit : 0);
+ } else {
+ __s64 l = le32_to_cpu(a->value_length);
+ if (a->flags & (ATTR_COMPRESSION_MASK | ATTR_IS_ENCRYPTED |
+ ATTR_IS_SPARSE)) {
+ err = EIO;
+ goto put_err_out;
+ }
+ ntfs_attr_init(na, FALSE, FALSE, FALSE, FALSE, l, l, l, 0, 0);
+ }
+ ntfs_put_attr_search_ctx(ctx);
return na;
+put_err_out:
+ ntfs_put_attr_search_ctx(ctx);
+err_out:
+ errno = err;
+ return NULL;
}
/**
- * ntfs_attr_put - free an ntfs attribute structure
+ * ntfs_attr_close - free an ntfs attribute structure
* @na: ntfs attribute structure to free
*
@@ -430,5 +526,5 @@
* @na itself.
*/
-void ntfs_attr_put(ntfs_attr *na)
+void ntfs_attr_close(ntfs_attr *na)
{
if (NAttrNonResident(na) && na->rl)
@@ -1138,4 +1234,6 @@
__u8 b; /* Current byte offset in buf. */
+ Dprintf(__FUNCTION__ "(): Entering for attr 0x%x.\n",
+ le32_to_cpu(attr->type));
/* Make sure attr exists and is non-resident. */
if (!attr || !attr->non_resident ||
@@ -1348,5 +1446,6 @@
int err;
- Dprintf("Mapping run list part containing vcn 0x%Lx.\n",
+ Dprintf(__FUNCTION__ "(): Entering for inode 0x%Lx, attr 0x%x, "
+ "vcn 0x%Lx.\n", na->ni->mft_no, na->type,
(long long)vcn);
@@ -1573,9 +1672,12 @@
__s64 ntfs_attr_pread(ntfs_attr *na, const __s64 pos, __s64 count, void *dst)
{
- __s64 br, to_read, total;
+ __s64 br, to_read, ofs, total;
ntfs_volume *vol;
run_list_element *rl;
- int f, ofs;
+ int f;
+ Dprintf(__FUNCTION__ "(): Entering for inode 0x%Lx, attr 0x%x, pos "
+ "0x%Lx, count 0x%Lx.\n", na->ni->mft_no, na->type, pos,
+ count);
if (!na || !na->ni || !na->ni->vol || !dst || pos < 0 || count < 0) {
errno = EINVAL;
@@ -1590,4 +1692,21 @@
if (!count)
return 0;
+ /* Truncate reads beyond end of attribute. */
+ if (pos + count > na->data_size) {
+ if (pos > na->data_size)
+ return 0;
+ count = na->data_size - pos;
+ }
+ total = 0;
+ /* Zero out reads beyond initialized size. */
+ if (pos + count > na->initialized_size) {
+ if (pos > na->initialized_size) {
+ memset(dst, 0, count);
+ return count;
+ }
+ total = pos + count - na->initialized_size;
+ count -= total;
+ memset(dst + count, 0, total);
+ }
/* Find the run list element containing the vcn. */
rl = ntfs_attr_find_vcn(na, pos >> vol->cluster_size_bits);
@@ -1606,6 +1725,6 @@
* length.
*/
- ofs = pos & (vol->cluster_size - 1);
- for (total = 0; count; rl++, ofs = 0) {
+ ofs = pos - (rl->vcn << vol->cluster_size_bits);
+ for (; count; rl++, ofs = 0) {
/* If we have reached the end of the run list return. */
if (!rl->length)
@@ -1636,4 +1755,7 @@
ofs);
retry:
+ Dprintf(__FUNCTION__ "(): Reading 0x%Lx bytes from vcn "
+ "0x%Lx, lcn 0x%Lx, ofs 0x%Lx.\n", to_read,
+ rl->vcn, rl->lcn, ofs);
br = ntfs_pread(f, (rl->lcn << vol->cluster_size_bits) + ofs,
to_read, dst);
@@ -1693,4 +1815,6 @@
void *end;
+ Dprintf(__FUNCTION__ "(): Entering for inode 0x%Lx, attr type 0x%x, "
+ "pos 0x%Lx.\n", na->ni->mft_no, na->type, pos);
if (bk_cnt < 0 || bk_size % NTFS_SECTOR_SIZE) {
errno = EINVAL;
Index: disk_io.c
===================================================================
RCS file: /cvsroot/linux-ntfs/linux-ntfs/libntfs/disk_io.c,v
retrieving revision 1.23
retrieving revision 1.24
diff -U2 -r1.23 -r1.24
--- disk_io.c 29 Apr 2002 01:53:55 -0000 1.23
+++ disk_io.c 1 Jun 2002 00:41:45 -0000 1.24
@@ -55,5 +55,7 @@
{
__s64 br, total;
-
+
+ Dprintf(__FUNCTION__ "(): Entering for pos 0x%Lx, count 0x%Lx.\n",
+ pos, count);
if (!b || count < 0 || pos < 0) {
errno = EINVAL;
@@ -301,5 +303,5 @@
return -1;
}
- if (vol->number_of_clusters < lcn + count) {
+ if (vol->nr_clusters < lcn + count) {
errno = ESPIPE;
return -1;
@@ -343,5 +345,5 @@
return -1;
}
- if (vol->number_of_clusters < lcn + count) {
+ if (vol->nr_clusters < lcn + count) {
errno = ESPIPE;
return -1;
Index: inode.c
===================================================================
RCS file: /cvsroot/linux-ntfs/linux-ntfs/libntfs/inode.c,v
retrieving revision 1.8
retrieving revision 1.9
diff -U2 -r1.8 -r1.9
--- inode.c 22 Apr 2002 10:34:31 -0000 1.8
+++ inode.c 1 Jun 2002 00:41:45 -0000 1.9
@@ -42,4 +42,9 @@
}
+ntfs_inode *allocate_ntfs_inode(ntfs_volume *vol)
+{
+ return __allocate_ntfs_inode(vol);
+}
+
static __inline__ int __release_ntfs_inode(ntfs_inode *ni)
{
@@ -88,4 +93,5 @@
int err = 0;
+ Dprintf(__FUNCTION__ "(): Entering for inode 0x%Lx.\n", MREF(mref));
if (!vol) {
errno = EINVAL;
Index: mft.c
===================================================================
RCS file: /cvsroot/linux-ntfs/linux-ntfs/libntfs/mft.c,v
retrieving revision 1.25
retrieving revision 1.26
diff -U2 -r1.25 -r1.26
--- mft.c 27 Apr 2002 19:49:09 -0000 1.25
+++ mft.c 1 Jun 2002 00:41:45 -0000 1.26
@@ -49,44 +49,25 @@
*/
int ntfs_read_mft_records(const ntfs_volume *vol, const MFT_REF mref,
- const __s64 count, const MFT_RECORD *b)
+ const __s64 count, MFT_RECORD *b)
{
- __s64 br, ofs;
- LCN lcn;
+ __s64 br;
VCN m;
-
- if (!vol || !b || count < 0) {
+
+ Dprintf(__FUNCTION__ "(): Entering for inode 0x%Lx.\n", MREF(mref));
+ if (!vol || !vol->mft_na || !b || count < 0) {
errno = EINVAL;
return -1;
}
- if (!vol->fd) {
- errno = EBADF;
- return -1;
- }
m = MREF(mref);
- if (vol->number_of_mft_records < m + count) {
+ if (m + count > vol->nr_mft_records) {
errno = ESPIPE;
return -1;
}
- /* Starting cluster of the first mft record to read. */
- lcn = ntfs_rl_vcn_to_lcn(vol->mft_rl, m << vol->mft_record_size_bits
- >> vol->cluster_size_bits);
- /*
- * We always keep the COMPLETE run list for $MFT/$DATA in
- * vol->mft_rl.
- */
- if (lcn < 0) {
- Dprintf(__FUNCTION__ "(): BUG! ntfs_vcn_to_lcn() on $MFT/$DATA "
- "returned %Li.\n", lcn);
- errno = EIO;
- return -1;
- }
- /* Offset within the cluster of the first mft record to read. */
- ofs = (m << vol->mft_record_size_bits) & (vol->cluster_size - 1);
- br = ntfs_mst_pread(vol->fd, (lcn << vol->cluster_size_bits) + ofs,
+ br = ntfs_attr_mst_pread(vol->mft_na, m << vol->mft_record_size_bits,
count, vol->mft_record_size, b);
if (br != count) {
if (br != -1)
errno = EIO;
- if (!br)
+ if (br >= 0)
Dputs("Error: partition is smaller than it should be!");
else
@@ -110,7 +91,10 @@
* they are deprotected again, thus resulting in an increase in the update
* sequence number inside the buffer @b.
+ *
+ * FIXME: Take ->initialized_size & ->data_size into consideration... (AIA)
+ * TODO/FIXME: Should this extend $MFT? Probably... (AIA)
*/
int ntfs_write_mft_records(const ntfs_volume *vol, const MFT_REF mref,
- const __s64 count, const MFT_RECORD *b)
+ const __s64 count, MFT_RECORD *b)
{
__s64 bw, ofs;
@@ -127,11 +111,11 @@
}
m = MREF(mref);
- if (vol->number_of_mft_records < m + count) {
+ if (vol->nr_mft_records < m + count) {
errno = ESPIPE;
return -1;
}
/* Starting cluster of the first mft record to write. */
- lcn = ntfs_rl_vcn_to_lcn(vol->mft_rl, m << vol->mft_record_size_bits
- >> vol->cluster_size_bits);
+ lcn = ntfs_rl_vcn_to_lcn(vol->mft_na->rl, m <<
+ vol->mft_record_size_bits >> vol->cluster_size_bits);
/*
* We always keep the COMPLETE run list for $MFT/$DATA in
Index: volume.c
===================================================================
RCS file: /cvsroot/linux-ntfs/linux-ntfs/libntfs/volume.c,v
retrieving revision 1.40
retrieving revision 1.41
diff -U2 -r1.40 -r1.41
--- volume.c 29 Apr 2002 01:53:55 -0000 1.40
+++ volume.c 1 Jun 2002 00:41:45 -0000 1.41
@@ -36,4 +36,5 @@
#include "disk_io.h"
#include "debug.h"
+#include "inode.h"
/*
@@ -62,12 +63,18 @@
if (v->vol_name)
free(v->vol_name);
- if (v->lcn_bitmap)
- free(v->lcn_bitmap);
- if (v->mft_bitmap)
- free(v->mft_bitmap);
- if (v->mft_rl)
- free(v->mft_rl);
- if (v->mftmirr_rl)
- free(v->mftmirr_rl);
+ if (v->lcnbmp_na)
+ ntfs_attr_close(v->lcnbmp_na);
+ if (v->lcnbmp_ni)
+ ntfs_close_inode(v->lcnbmp_ni);
+ if (v->mftbmp_na)
+ ntfs_attr_close(v->mftbmp_na);
+ if (v->mft_na)
+ ntfs_attr_close(v->mft_na);
+ if (v->mft_ni)
+ ntfs_close_inode(v->mft_ni);
+ if (v->mftmirr_na)
+ ntfs_attr_close(v->mftmirr_na);
+ if (v->mftmirr_ni)
+ ntfs_close_inode(v->mftmirr_ni);
if (v->upcase)
free(v->upcase);
@@ -75,4 +82,6 @@
}
+extern ntfs_inode *allocate_ntfs_inode(ntfs_volume *);
+
/**
* ntfs_mount - open ntfs volume
@@ -105,20 +114,23 @@
ntfs_volume *ntfs_mount(const char *name, unsigned long rwflag)
{
+ VCN next_vcn, last_vcn, highest_vcn;
+ __s64 l;
const char *OK = "OK";
const char *FAILED = "FAILED";
ntfs_volume *vol = NULL;
NTFS_BOOT_SECTOR *bs = NULL;
- MFT_RECORD *mb = NULL;
+ MFT_RECORD *m = NULL, *m2 = NULL, *mb = NULL;
ntfs_attr_search_ctx *ctx = NULL;
+ run_list_element *rl;
+ ntfs_inode *ni;
+ ntfs_attr *na;
ATTR_RECORD *a;
VOLUME_INFORMATION *vinf;
uchar_t *vname;
- int j;
+ int i, j;
__u32 u;
- __s64 l;
ssize_t br;
__u8 sectors_per_cluster;
__s8 c;
- MFT_REF mref;
int eo, ro = rwflag & MS_RDONLY;
#ifdef DEBUG
@@ -182,5 +194,5 @@
goto error_exit;
}
- vol->number_of_clusters = sle64_to_cpu(bs->number_of_sectors) >>
+ vol->nr_clusters = sle64_to_cpu(bs->number_of_sectors) >>
(ffs(sectors_per_cluster) - 1);
@@ -189,6 +201,6 @@
Dprintf("MFT LCN = %Li\n", vol->mft_lcn);
Dprintf("MFTMirr LCN = 0x%Li\n", vol->mftmirr_lcn);
- if (vol->mft_lcn > vol->number_of_clusters ||
- vol->mftmirr_lcn > vol->number_of_clusters) {
+ if (vol->mft_lcn > vol->nr_clusters ||
+ vol->mftmirr_lcn > vol->nr_clusters) {
Dprintf("Error: %s is not a valid NTFS partition! ($Mft LCN "
"or\n$MftMirr LCN is greater than the number "
@@ -198,5 +210,5 @@
}
vol->cluster_size = sectors_per_cluster *
- le16_to_cpu(bs->bpb.bytes_per_sector);
+ le16_to_cpu(bs->bpb.bytes_per_sector);
if (vol->cluster_size & (vol->cluster_size - 1)) {
Dprintf("Error: %s is not a valid NTFS partition! "
@@ -246,13 +258,15 @@
else
vol->mftmirr_size = vol->cluster_size / vol->mft_record_size;
- vol->mftmirr_rl = malloc(2 * sizeof(run_list_element));
- vol->mftmirr_rl[0].vcn = vol->mftmirr_rl[1].length = 0;
- vol->mftmirr_rl[0].lcn = vol->mftmirr_lcn;
- vol->mftmirr_rl[1].vcn = vol->mftmirr_rl[0].length = (vol->mftmirr_size
- * vol->mft_record_size + vol->cluster_size - 1) /
- vol->cluster_size;
- vol->mftmirr_rl[1].lcn = LCN_ENOENT;
+
/* Start with $Mft. */
Dprintf("Loading $Mft... ");
+ /* Manually setup an ntfs_inode. */
+ vol->mft_ni = allocate_ntfs_inode(vol);
+ if (!vol->mft_ni) {
+ Dputs(FAILED);
+ Dperror("Error allocating memory for $Mft");
+ goto error_exit;
+ }
+ vol->mft_ni->mft_no = 0;
if (!(mb = (MFT_RECORD*)malloc(vol->mft_record_size))) {
Dputs(FAILED);
@@ -260,4 +274,5 @@
goto error_exit;
}
+ vol->mft_ni->mrec = mb;
/* Can't use any of the higher level functions yet! */
br = ntfs_mst_pread(vol->fd, vol->mft_lcn << vol->cluster_size_bits, 1,
@@ -285,6 +300,5 @@
goto error_exit;
}
- /* Find the bitmap attribute. */
- ctx = ntfs_get_attr_search_ctx(NULL, mb);
+ ctx = ntfs_get_attr_search_ctx(vol->mft_ni, mb);
if (!ctx) {
Dputs(FAILED);
@@ -296,152 +310,291 @@
Dputs(FAILED);
Dputs("Error: corrupt mft record for $Mft.");
- errno = EIO;
- goto error_exit;
+ goto io_error_exit;
}
- if (ntfs_lookup_attr(AT_BITMAP, NULL, 0, 0, 0, NULL, 0, ctx)) {
- Dputs(FAILED);
- Dputs("$bitmap attribute not found in $Mft?!?");
- goto error_exit;
+ /* Find the $ATTRIBUTE_LIST attribute in $MFT if present. */
+ if (ntfs_lookup_attr(AT_ATTRIBUTE_LIST, NULL, 0, 0, 0, NULL, 0, ctx)) {
+ if (errno != ENOENT) {
+ Dputs(FAILED);
+ Dputs("Error: corrupt attribute list in mft record "
+ "for $Mft.");
+ goto io_error_exit;
+ }
+ goto mft_has_no_attr_list;
}
- a = ctx->attr;
- /* Get attribute value size and allocate a big enough buffer. */
- l = get_attribute_value_length(a);
- if (!l) {
- Dputs(OK);
- Dputs("Error: $bitmap in $Mft has zero length?!?");
- errno = EIO;
- goto error_exit;
+ NInoSetAttrList(vol->mft_ni);
+ l = get_attribute_value_length(ctx->attr);
+ if (l <= 0) {
+ Dputs(FAILED);
+ Dputs("Error: attribute list in mft record for $Mft has zero "
+ "length.");
+ goto io_error_exit;
}
- vol->mft_bitmap = (__u8*)malloc(l);
- if (!vol->mft_bitmap) {
+ if (l > 0x40000) {
Dputs(FAILED);
- Dputs("Not enough memory to load bitmap attribute from $Mft.");
- goto error_exit;
+ Dputs("Error: attribute list in mft record for $Mft exceeds "
+ "maximum length.");
+ goto io_error_exit;
}
- /* Read in the bitmap attribute value into the buffer. */
- if (l != get_attribute_value(vol, mb, a, vol->mft_bitmap)) {
+ vol->mft_ni->attr_list_size = l;
+ vol->mft_ni->attr_list = malloc(l);
+ if (!vol->mft_ni->attr_list) {
Dputs(FAILED);
- Dputs("Amount of data read does not correspond to expected "
- "length!");
- errno = EIO;
+ Dputs("Error: failed to allocate buffer for attribute list.");
goto error_exit;
}
-#if 0
- // Output the mft bitmap. Can be useful when debugging write code.
- { int _i_;
- printf("$MFT $BITMAP =\n");
- for (_i_ = 0; _i_ < l; _i_++)
- printf("0x%s%x%s", vol->mft_bitmap[_i_] < 0x10 ? "0" : "",
- vol->mft_bitmap[_i_], _i_ % 8 ? ", " : "\n");
- printf("\n");
+ l = get_attribute_value(vol, vol->mft_ni->mrec, ctx->attr,
+ vol->mft_ni->attr_list);
+ if (!l) {
+ Dputs(FAILED);
+ Dputs("Error: failed to get attribute list.");
+ goto io_error_exit;
}
-#endif
- /* Find the $DATA attribute in $Mft. */
- ntfs_reinit_attr_search_ctx(ctx);
- if (ntfs_lookup_attr(AT_DATA, NULL, 0, 0, 0, NULL, 0, ctx)) {
+ if (l != vol->mft_ni->attr_list_size) {
Dputs(FAILED);
- Dputs("$DATA attribute not found in $Mft?!?");
- goto error_exit;
+ Dputs("Error: bytes gotten != expected when getting "
+ "attribute list.");
+ goto io_error_exit;
+ }
+ if (ctx->attr->non_resident) {
+ NInoSetAttrListNonResident(vol->mft_ni);
+ // FIXME: We are duplicating work here! (AIA)
+ vol->mft_ni->attr_list_rl = ntfs_decompress_mapping_pairs(vol,
+ ctx->attr, NULL);
+ if (!vol->mft_ni->attr_list_rl) {
+ Dputs(FAILED);
+ Dputs("Error: failed to get run list for attribute "
+ "list.");
+ goto error_exit;
+ }
}
- a = ctx->attr;
- /* Determine the number of mft records in $Mft. */
- vol->number_of_mft_records = a->data_size >> vol->mft_record_size_bits;
- /* The $DATA attribute of the $Mft has to be non-resident. */
- if (!a->non_resident) {
+mft_has_no_attr_list:
+ /* We now have a fully setup ntfs inode for $MFT in vol->mft_ni. */
+
+ /* Get an ntfs attribute for $MFT/$DATA and set it up, too. */
+ vol->mft_na = ntfs_attr_open(vol->mft_ni, AT_DATA, NULL, 0);
+ if (!vol->mft_na) {
Dputs(FAILED);
- Dputs("$Mft $DATA attribute is resident!?!");
- errno = EINVAL;
+ Dperror("Failed to open ntfs attribute");
goto error_exit;
}
- /* Get the run list. */
- vol->mft_rl = ntfs_decompress_mapping_pairs(vol, a, NULL);
- if (!vol->mft_rl) {
+ /* Set the number of mft records. */
+ vol->nr_mft_records = vol->mft_na->data_size >>
+ vol->mft_record_size_bits;
+ /* Read all extents from the $DATA attribute in $MFT. */
+ ntfs_reinit_attr_search_ctx(ctx);
+ last_vcn = vol->mft_na->allocated_size >> vol->cluster_size_bits;
+ highest_vcn = next_vcn = 0;
+ a = NULL;
+ while (!ntfs_lookup_attr(AT_DATA, NULL, 0, 0, next_vcn, NULL, 0, ctx)) {
+ run_list_element *nrl;
+
+ /* Cache the current attribute. */
+ a = ctx->attr;
+ /* $MFT must be non-resident. */
+ if (!a->non_resident) {
+ Dputs(FAILED);
+ Dputs("$MFT must be non-resident but a resident "
+ "extent was found. $MFT is corrupt. "
+ "Run chkdsk.");
+ goto io_error_exit;
+ }
+ /* $MFT must be uncompressed and unencrypted. */
+ if (a->flags & ATTR_COMPRESSION_MASK ||
+ a->flags & ATTR_IS_ENCRYPTED) {
+ Dputs(FAILED);
+ Dputs("$MFT must be uncompressed and unencrypted but "
+ "a compressed/encrypted extent was "
+ "found. $MFT is corrupt. Run chkdsk.");
+ goto io_error_exit;
+ }
+ /*
+ * Decompress the mapping pairs array of this extent and merge
+ * the result into the existing run list. No need for locking
+ * as we have exclusive access to the inode at this time and we
+ * are a mount in progress task, too.
+ */
+ nrl = ntfs_decompress_mapping_pairs(vol, a, vol->mft_na->rl);
+ if (!nrl) {
+ Dputs(FAILED);
+ Dperror("decompress_mapping_pairs() failed");
+ goto error_exit;
+ }
+ vol->mft_na->rl = nrl;
+
+ /* Get the lowest vcn for the next extent. */
+ highest_vcn = sle64_to_cpu(a->highest_vcn);
+ next_vcn = highest_vcn + 1;
+
+ /* Only one extent or error, which we catch below. */
+ if (next_vcn <= 0)
+ break;
+
+ /* Avoid endless loops due to corruption. */
+ if (next_vcn < sle64_to_cpu(a->lowest_vcn)) {
+ Dputs(FAILED);
+ Dputs("$MFT has corrupt attribute list attribute. "
+ "Run chkdsk.");
+ goto io_error_exit;
+ }
+ }
+ if (!a) {
Dputs(FAILED);
- Dputs("Error decompressing run list from $Mft $DATA "
- "attribute.");
- errno = EINVAL;
- goto error_exit;
+ Dputs("$MFT/$DATA attribute not found. $MFT is corrupt. "
+ "Run chkdsk.");
+ goto io_error_exit;
+ }
+ if (highest_vcn && highest_vcn != last_vcn - 1) {
+ Dputs(FAILED);
+ Dputs("Failed to load the complete run list for $MFT/$DATA. "
+ "Bug or corrupt $MFT. Run chkdsk.");
+ Dprintf("highest_vcn = 0x%Lx, last_vcn - 1 = 0x%Lx\n",
+ (long long)highest_vcn,
+ (long long)last_vcn - 1);
+ goto io_error_exit;
}
- Dputs(OK);
/* Done with the $Mft mft record. */
ntfs_put_attr_search_ctx(ctx);
ctx = NULL;
- free(mb);
mb = NULL;
- /* Now load the bitmap from $Bitmap. */
- Dprintf("Loading $Bitmap... ");
- mref = (MFT_REF)FILE_Bitmap;
- if (ntfs_read_file_record(vol, mref, &mb, NULL)) {
+ /*
+ * The volume is now setup so we can use all read access functions.
+ */
+ vol->mftbmp_na = ntfs_attr_open(vol->mft_ni, AT_BITMAP, NULL, 0);
+ if (!vol->mftbmp_na) {
Dputs(FAILED);
+ Dperror("Failed to open ntfs attribute");
goto error_exit;
}
- ctx = ntfs_get_attr_search_ctx(NULL, mb);
- if (!ctx) {
+ Dputs(OK);
+
+ /* Need to setup the mft mirror so we can use write functions, too. */
+ Dprintf("Loading $MFTMirr... ");
+ vol->mftmirr_ni = ntfs_open_inode(vol, FILE_MFTMirr);
+ if (!vol->mftmirr_ni) {
Dputs(FAILED);
- Dperror("Failed to allocate attribute search context");
+ Dperror("Failed to open inode");
goto error_exit;
}
- /* Find the bitmap (it is in the $DATA attribute). */
- if (ntfs_lookup_attr(AT_DATA, NULL, 0, 0, 0, NULL, 0, ctx)) {
+ /* Get an ntfs attribute for $MFTMirr/$DATA, too. */
+ vol->mftmirr_na = ntfs_attr_open(vol->mftmirr_ni, AT_DATA, NULL, 0);
+ if (!vol->mftmirr_na) {
Dputs(FAILED);
- Dputs("bitmap attribute not found in $Bitmap?!?");
+ Dperror("Failed to open ntfs attribute");
goto error_exit;
}
- a = ctx->attr;
- /* Get attribute value size and allocate a big enough buffer.*/
- l = get_attribute_value_length(a);
- if (!l) {
- Dputs(OK);
- Dputs("Error: bitmap in $Bitmap has zero length?!?");
- errno = EIO;
+ if (ntfs_attr_map_run_list(vol->mftmirr_na, 0) < 0) {
+ Dputs(FAILED);
+ Dperror("Failed to map run list");
goto error_exit;
}
- vol->lcn_bitmap = (__u8*)malloc(l);
- if (!vol->lcn_bitmap) {
- Dputs(FAILED);
- Dputs("Not enough memory to load $Bitmap.");
- errno = ENOMEM;
+ rl = (run_list_element*)malloc(sizeof(run_list_element) * 2);
+ if (!rl) {
+ Dperror("Failed to allocate run list for mft mirror");
goto error_exit;
}
- /* Read in the bitmap attribute value into the buffer. */
- if (l != get_attribute_value(vol, mb, a, vol->lcn_bitmap)) {
- Dputs(FAILED);
- Dputs("Amount of data read does not correspond to expected "
- "length!");
- errno = EIO;
+ /* Construct the mft mirror run list. */
+ rl[0].vcn = rl[1].length = 0;
+ rl[0].lcn = vol->mftmirr_lcn;
+ rl[1].vcn = rl[0].length = (vol->mftmirr_size * vol->mft_record_size +
+ vol->cluster_size - 1) / vol->cluster_size;
+ rl[1].lcn = LCN_ENOENT;
+ /* Compare the two run lists. They must be identical. */
+ i = 0;
+ do {
+ if (rl[i].vcn != vol->mftmirr_na->rl[i].vcn ||
+ rl[i].lcn != vol->mftmirr_na->rl[i].lcn ||
+ rl[i].length != vol->mftmirr_na->rl[i].length) {
+ Dputs(FAILED);
+ Dputs("$MFTMirr location mismatch! Run chkdsk.");
+ goto io_error_exit;
+ }
+ } while (rl[i++].length);
+ free(rl);
+ Dputs(OK);
+
+ /* Load $MFT and $MFTMirr and compare the contents. */
+ m = (MFT_RECORD*)malloc(vol->mftmirr_size << vol->mft_record_size_bits);
+ m2 = (MFT_RECORD*)malloc(vol->mftmirr_size <<
+ vol->mft_record_size_bits);
+ if (!m || !m2) {
+ Dperror("Failed to allocate memory");
goto error_exit;
}
- /* Done with the $BitMap mft record. */
+
+ l = ntfs_attr_mst_pread(vol->mft_na, 0, vol->mftmirr_size,
+ vol->mft_record_size, m);
+ if (l != vol->mftmirr_size) {
+ if (l == -1)
+ Dperror("Failed to read $MFT");
+ else {
+ Dputs("Length of data not equal expected length.");
+ errno = EIO;
+ }
+ goto error_exit;
+ }
+ l = ntfs_attr_mst_pread(vol->mftmirr_na, 0, vol->mftmirr_size,
+ vol->mft_record_size, m2);
+ if (l != vol->mftmirr_size) {
+ if (l == -1)
+ Dperror("Failed to read $MFTMirr");
+ else {
+ Dputs("Length of data not equal expected length.");
+ errno = EIO;
+ }
+ goto error_exit;
+ }
+
+ Dprintf("Comparing $MFTMirr to $MFT... ");
+ for (i = 0, j = 0; i < vol->mftmirr_size; ++i) {
+ if (memcmp((__u8*)m + i * vol->mft_record_size, (__u8*)m2 +
+ i * vol->mft_record_size,
+ ntfs_get_mft_record_data_size((MFT_RECORD*)(
+ (__u8*)m + i * vol->mft_record_size))))
+ j |= 1 << i;
+ }
+
+ free(m2);
+ free(m);
+ m = m2 = NULL;
+
+ if (j) {
+ Dputs(FAILED);
+ Dputs("The $MFT mirror does not match the $MFT. "
+ "You need to run chkdsk.");
+ goto io_error_exit;
+ }
Dputs(OK);
- ntfs_put_attr_search_ctx(ctx);
- ctx = NULL;
- free(mb);
- mb = NULL;
- /* Now load the upcase table from $UpCase. */
-...
[truncated message content] |