Hi,

Attached is an update to the patches, after having checked
all my tests run successfully.

Jean-Pierre

Jean-Pierre André wrote:
Hi,

Your tests, leading to the file system getting filled up,
produced situations not seen before.

Could you please check the attached patches.

They care for two different issues :
- replaying some redo actions which were not foreseen,
- locating log blocks more recent than temporary ones.

So far, I have only checked one of your proposed cases
(metadata7_5.gz), so there still might be issues with
the other ones.

Jean-Pierre

Maayan Hanin wrote:
Hello,

On some occasions, I’ve got an NTFS journal containing ops which
ntfsrecover ignores since the Redo/Undo pair doesn’t match what
ntfsrecover expects in the distribute_redosfunction.

Example #1:

Redo op: WriteEndofFileRecordSegment

Actual undo op: Noop

Expected undo op: WriteEndofFileRecordSegment

Example #2:

Redo op: InitializeFileRecordSegment

Actual undo op: DeallocateFileRecordSegment

Expected undo op: Noop

Example #3:

Getting CompensationlogRecord as the undo op, for many different redoops
(DeleteAttribute, CreateAttribute, DeleteIndexEntryAllocation,
WriteEndOfIndexBuffer etc.).

This seems to happen when the partition is full or nearly full.

What is the reason ntfsrecover ignores such cases, instead of just
playing the operations present in the journal?

Worth noting that all examples occurred on a regular Windows 7 machine,
performing trivial operations against the file system.

I have raw partition files available for inspection.

Thanks,

Maayan


--- ntfsprogs/ntfsrecover.c.ref 2017-09-30 10:39:48.603684400 +0200
+++ ntfsprogs/ntfsrecover.c     2017-10-14 08:38:49.781763100 +0200
@@ -3810,6 +3810,60 @@
 }
 
 /*
+ *             Find the latest log block
+ *
+ *     Usually, the latest block is either block 2 or 3 which act as
+ *     temporary block before being copied to target location.
+ *     However under some unknown condition the block are written
+ *     immediately to target location, and we have to scan for the
+ *     latest one.
+ *     Currently this is not checked for logfile version 2.x which
+ *     use a different layout of temporary blocks.
+ */
+
+const struct BUFFER *find_latest_block(CONTEXT *ctx, u32 baseblk,
+                       const struct BUFFER *basebuf)
+{
+       le64 offset;
+       leLSN prevlsn;
+       leLSN curlsn;
+       u32 curblk;
+       u32 prevblk;
+       const struct BUFFER *prevbuf;
+       const struct BUFFER *curbuf;
+
+       offset = basebuf->block.record.copy.file_offset;
+       curblk = baseblk;
+       do {
+               if (curblk < BASEBLKS) {
+                       prevbuf = basebuf;
+                       prevlsn = basebuf->block.record.last_end_lsn;
+                       prevblk = baseblk;
+                       curblk = le64_to_cpu(offset) >> blockbits;
+               } else {
+                       if (optv)
+                               printf("block %d is more recent than block 
%d\n",
+                                       (int)curblk, (int)prevblk);
+                       prevbuf = curbuf;
+                       prevlsn = curlsn;
+                       prevblk = curblk;
+                       curblk++;
+                       if (curblk >= (logfilesz >> blockbits))
+                               curblk = (log_major < 2 ? BASEBLKS : BASEBLKS2);
+               }
+               curbuf = read_buffer(ctx, curblk);
+               if (curbuf && (curbuf->block.record.magic == magic_RCRD)) {
+                       curlsn = curbuf->block.record.copy.last_lsn;
+               }
+       } while (curbuf
+               && (curbuf->block.record.magic == magic_RCRD)
+               && (le64_to_cpu(curlsn) > le64_to_cpu(prevlsn)));
+       if (optv)
+               printf("Block %d is the latest one\n",(int)prevblk);
+       return (prevbuf);
+}
+
+/*
  *             Determine the sequencing of blocks (when version >= 2.0)
  *
  *     Blocks 2..17 and 18..33 are temporary blocks being filled until
@@ -4131,6 +4185,12 @@
                                                        &nextbuf->block.record);
                                }
                        }
+                       if (startbuf && opts) {
+                               buf = startbuf = find_latest_block(ctx,
+                                               blk, startbuf);
+                               latest_lsn = le64_to_cpu(
+                                       buf->block.record.last_end_lsn);
+                       }
                } else {
                        buf = startbuf = read_buffer(ctx, blk);
                        nextbuf = (const struct BUFFER*)NULL;
--- ntfsprogs/playlog.c.ref     2017-09-30 10:39:48.672072800 +0200
+++ ntfsprogs/playlog.c 2017-10-14 08:41:01.635953700 +0200
@@ -2165,7 +2165,7 @@
        record = (MFT_RECORD*)buffer;
        if ((target + length) <= mftrecsz) {
                /* write a void mft entry (needed ?) */
-               changed = memcmp(buffer + target, data, length)
+               changed = (length && memcmp(buffer + target, data, length))
                        || (record->flags & MFT_RECORD_IN_USE);
                err = 0;
                if (changed) {
@@ -2205,7 +2205,6 @@
        data = ((const char*)&action->record)
                        + get_undo_offset(&action->record);
        length = le16_to_cpu(action->record.undo_length);
-// TODO merge with undo_add_index ?
        target = le16_to_cpu(action->record.record_offset)
                + le16_to_cpu(action->record.attribute_offset);
        if (optv > 1) {
@@ -2224,7 +2223,9 @@
            && !(length & 7)
            && ((target + length) <= xsize)) {
                /* This has to be an idempotent action */
-               found = !memcmp(buffer + target, data, length);
+               found = (action->record.undo_operation
+                               == const_cpu_to_le16(CompensationlogRecord))
+                   || !memcmp(buffer + target, data, length);
                err = 0;
                if (found) {
                        /* Remove the entry */
@@ -2239,7 +2240,7 @@
                }
                if (optv > 1) {
                        printf("-> INDX record %s\n",
-                               (found ? "unchanged" : "removed"));
+                               (found ? "removed" : "unchanged"));
                }
        }
        return (err);
@@ -2286,7 +2287,9 @@
            && !(length & 7)
            && ((target + length) <= mftrecsz)) {
                /* This has to be an idempotent action */
-               found = !memcmp(buffer + target, data, length);
+               found = (action->record.undo_operation
+                               == const_cpu_to_le16(CompensationlogRecord))
+                       || !memcmp(buffer + target, data, length);
                err = 0;
                /* Only delete if present */
                if (found) {
@@ -2626,7 +2629,9 @@
                        dump(&buffer[target], length);
                }
                if ((target + length) <= mftrecsz) {
-                       changed = memcmp(buffer + target, data, length);
+                       changed = (action->record.undo_operation
+                                   == const_cpu_to_le16(CompensationlogRecord))
+                               || memcmp(buffer + target, data, length);
                        err = 0;
                        if (changed) {
                                memcpy(buffer + target, data, length);
@@ -4139,8 +4144,10 @@
                        err = redo_add_root_index(vol, action, buffer);
                break;
        case ClearBitsInNonResidentBitMap :
-               if (action->record.undo_operation
-                   == const_cpu_to_le16(SetBitsInNonResidentBitMap))
+               if ((action->record.undo_operation
+                       == const_cpu_to_le16(SetBitsInNonResidentBitMap))
+                   || (action->record.undo_operation
+                       == const_cpu_to_le16(CompensationlogRecord)))
                        err = redo_force_bits(vol, action, buffer);
                break;
        case CompensationlogRecord :
@@ -4149,28 +4156,38 @@
                        err = redo_compensate(vol, action, buffer);
                break;
        case CreateAttribute :
-               if (action->record.undo_operation
-                   == const_cpu_to_le16(DeleteAttribute))
+               if ((action->record.undo_operation
+                       == const_cpu_to_le16(DeleteAttribute))
+                   || (action->record.undo_operation
+                       == const_cpu_to_le16(CompensationlogRecord)))
                        err = redo_create_attribute(vol, action, buffer);
                break;
        case DeallocateFileRecordSegment :
-               if (action->record.undo_operation
+               if ((action->record.undo_operation
                        == const_cpu_to_le16(InitializeFileRecordSegment))
+                   || (action->record.undo_operation
+                       == const_cpu_to_le16(CompensationlogRecord)))
                        err = redo_delete_file(vol, action, buffer);
                break;
        case DeleteAttribute :
-               if (action->record.undo_operation
-                   == const_cpu_to_le16(CreateAttribute))
+               if ((action->record.undo_operation
+                       == const_cpu_to_le16(CreateAttribute))
+                   || (action->record.undo_operation
+                       == const_cpu_to_le16(CompensationlogRecord)))
                        err = redo_delete_attribute(vol, action, buffer);
                break;
        case DeleteIndexEntryAllocation :
-               if (action->record.undo_operation
-                   == const_cpu_to_le16(AddIndexEntryAllocation))
+               if ((action->record.undo_operation
+                       == const_cpu_to_le16(AddIndexEntryAllocation))
+                   || (action->record.undo_operation
+                       == const_cpu_to_le16(CompensationlogRecord)))
                        err = redo_delete_index(vol, action, buffer);
                break;
        case DeleteIndexEntryRoot :
-               if (action->record.undo_operation
+               if ((action->record.undo_operation
                        == const_cpu_to_le16(AddIndexEntryRoot))
+                   || (action->record.undo_operation
+                       == const_cpu_to_le16(CompensationlogRecord)))
                        err = redo_delete_root_index(vol, action, buffer);
                break;
        case InitializeFileRecordSegment :
@@ -4232,8 +4249,10 @@
                }
                break;
        case UpdateResidentValue :
-               if (action->record.undo_operation
-                   == const_cpu_to_le16(UpdateResidentValue))
+               if ((action->record.undo_operation
+                       == const_cpu_to_le16(UpdateResidentValue))
+                   || (action->record.undo_operation
+                       == const_cpu_to_le16(CompensationlogRecord)))
                        err = redo_update_resident(vol, action, buffer);
                break;
        case Win10Action37 :
------------------------------------------------------------------------------
Check out the vibrant tech community on one of the world's most
engaging tech sites, Slashdot.org! http://sdm.link/slashdot
_______________________________________________
ntfs-3g-devel mailing list
ntfs-3g-devel@lists.sourceforge.net
https://lists.sourceforge.net/lists/listinfo/ntfs-3g-devel

Reply via email to