"Fossies" - the Fresh Open Source Software Archive  

Source code changes of the file "ntfsprogs/ntfsrecover.c" between
ntfs-3g_ntfsprogs-2016.2.22.tgz and ntfs-3g_ntfsprogs-2017.3.23.tgz

About: NTFS-3G is a read-write NTFS driver for Linux and other operating systems. It provides safe handling of the Windows XP, Windows Server 2003, Windows 2000, Windows Vista, Windows Server 2008, Windows 7 and Windows 8 NTFS file systems.

ntfsrecover.c  (ntfs-3g_ntfsprogs-2016.2.22.tgz):ntfsrecover.c  (ntfs-3g_ntfsprogs-2017.3.23.tgz)
/* /*
* Process log data from an NTFS partition * Process log data from an NTFS partition
* *
* Copyright (c) 2012-2015 Jean-Pierre Andre * Copyright (c) 2012-2016 Jean-Pierre Andre
* *
* This program examines the Windows log file of an ntfs partition * This program examines the Windows log file of an ntfs partition
* and plays the committed transactions in order to restore the * and plays the committed transactions in order to restore the
* integrity of metadata. * integrity of metadata.
* *
* It can also display the contents of the log file in human-readable * It can also display the contents of the log file in human-readable
* text, either from a full partition or from the log file itself. * text, either from a full partition or from the log file itself.
* *
* *
* History * History
skipping to change at line 104 skipping to change at line 104
#include "logging.h" #include "logging.h"
#include "runlist.h" #include "runlist.h"
#include "mft.h" #include "mft.h"
#include "inode.h" #include "inode.h"
#include "attrib.h" #include "attrib.h"
#include "bitmap.h" #include "bitmap.h"
#include "index.h" #include "index.h"
#include "volume.h" #include "volume.h"
#include "unistr.h" #include "unistr.h"
#include "mst.h" #include "mst.h"
#include "logfile.h"
#include "ntfsrecover.h" #include "ntfsrecover.h"
#include "utils.h" #include "utils.h"
#include "misc.h" #include "misc.h"
typedef struct { typedef struct {
ntfs_volume *vol; ntfs_volume *vol;
FILE *file; FILE *file;
struct ACTION_RECORD *firstaction; struct ACTION_RECORD *firstaction;
struct ACTION_RECORD *lastaction; struct ACTION_RECORD *lastaction;
} CONTEXT; } CONTEXT;
typedef enum { T_OK, T_ERR, T_DONE } TRISTATE; typedef enum { T_OK, T_ERR, T_DONE } TRISTATE;
struct RESTART_PAGE_HEADER log_header; RESTART_PAGE_HEADER log_header;
struct RESTART_AREA restart; RESTART_AREA restart;
struct RESTART_CLIENT client; LOG_CLIENT_RECORD client;
u32 clustersz = 0; u32 clustersz = 0;
int clusterbits; int clusterbits;
u32 blocksz; u32 blocksz;
int blockbits; int blockbits;
u16 bytespersect; u16 bytespersect;
u64 mftlcn; u64 mftlcn;
u32 mftrecsz; u32 mftrecsz;
int mftrecbits; int mftrecbits;
u32 mftcnt; /* number of entries */ u32 mftcnt; /* number of entries */
ntfs_inode *log_ni; ntfs_inode *log_ni;
skipping to change at line 148 skipping to change at line 149
unsigned long firstblk; /* first block to dump (option -r) */ unsigned long firstblk; /* first block to dump (option -r) */
unsigned long lastblk; /* last block to dump (option -r) */ unsigned long lastblk; /* last block to dump (option -r) */
u64 firstlcn; /* first block to dump (option -c) */ u64 firstlcn; /* first block to dump (option -c) */
u64 lastlcn; /* last block to dump (option -c) */ u64 lastlcn; /* last block to dump (option -c) */
BOOL optb; /* show the log backward */ BOOL optb; /* show the log backward */
BOOL optc; /* restrict to cluster range */ BOOL optc; /* restrict to cluster range */
BOOL optd; /* device argument present*/ BOOL optd; /* device argument present*/
BOOL opth; /* show help */ BOOL opth; /* show help */
BOOL opti; /* show invalid (stale) records */ BOOL opti; /* show invalid (stale) records */
BOOL optf; /* show full log */ BOOL optf; /* show full log */
BOOL optk; /* kill fast restart */
BOOL optn; /* do not apply modifications */ BOOL optn; /* do not apply modifications */
BOOL optp; /* count of transaction sets to play */ BOOL optp; /* count of transaction sets to play */
BOOL optr; /* show a range of blocks */ BOOL optr; /* show a range of blocks */
int opts; /* sync the file system */ int opts; /* sync the file system */
BOOL optt; /* show transactions */ BOOL optt; /* show transactions */
BOOL optu; /* count of transaction sets to undo */ BOOL optu; /* count of transaction sets to undo */
int optv; /* verbose */ int optv; /* verbose */
int optV; /* version */ int optV; /* version */
int optx[MAXEXCEPTION + 1]; int optx[MAXEXCEPTION + 1];
struct ATTR **attrtable; struct ATTR **attrtable;
skipping to change at line 209 skipping to change at line 211
/* /*
* Deprotect a block * Deprotect a block
* Only to be used for log buffers * Only to be used for log buffers
* *
* Returns 0 if block was found correct * Returns 0 if block was found correct
*/ */
static int replaceusa(struct BUFFER *buffer, unsigned int lth) static int replaceusa(struct BUFFER *buffer, unsigned int lth)
{ {
char *buf; char *buf;
struct RECORD_PAGE_HEADER *record; RECORD_PAGE_HEADER *record;
unsigned int j; unsigned int j;
BOOL err; BOOL err;
unsigned int used; unsigned int used;
unsigned int xusa, nusa; unsigned int xusa, nusa;
err = FALSE; err = FALSE;
/* Restart blocks have no protection */ /* Restart blocks have no protection */
if (buffer->num >= RSTBLKS) { if (buffer->num >= RSTBLKS) {
/* Do not check beyond used sectors */ /* Do not check beyond used sectors */
record = &buffer->block.record; record = &buffer->block.record;
used = blocksz; used = blocksz;
xusa = le16_to_cpu(record->head.usa_ofs); xusa = le16_to_cpu(record->usa_ofs);
nusa = le16_to_cpu(record->head.usa_count); nusa = le16_to_cpu(record->usa_count);
if (xusa && nusa if (xusa && nusa
&& ((xusa + 1) < lth) && ((xusa + 1) < lth)
&& ((nusa - 1)*NTFSBLKLTH == lth)) { && ((nusa - 1)*NTFSBLKLTH == lth)) {
buf = buffer->block.data; buf = buffer->block.data;
for (j=1; (j<nusa) && ((j-1)*NTFSBLKLTH<used); j++) for (j=1; (j<nusa) && ((j-1)*NTFSBLKLTH<used); j++)
if ((buf[xusa] == buf[j*NTFSBLKLTH - 2]) if ((buf[xusa] == buf[j*NTFSBLKLTH - 2])
&& (buf[xusa+1] == buf[j*NTFSBLKLTH - 1])) { && (buf[xusa+1] == buf[j*NTFSBLKLTH - 1])) {
buf[j*NTFSBLKLTH - 2] = buf[xusa + 2*j]; buf[j*NTFSBLKLTH - 2] = buf[xusa + 2*j];
buf[j*NTFSBLKLTH - 1] = buf[xusa + 2*j + 1]; buf[j*NTFSBLKLTH - 1] = buf[xusa + 2*j + 1];
} else { } else {
skipping to change at line 361 skipping to change at line 363
buffer->num = num; buffer->num = num;
if (ctx->vol) if (ctx->vol)
got = (ntfs_attr_pread(log_na,(u64)num << blockbits, got = (ntfs_attr_pread(log_na,(u64)num << blockbits,
blocksz, buffer->block.data) == blocksz); blocksz, buffer->block.data) == blocksz);
else else
got = !fseek(ctx->file, loclogblk(ctx, num), 0) got = !fseek(ctx->file, loclogblk(ctx, num), 0)
&& (fread(buffer->block.data, blocksz, && (fread(buffer->block.data, blocksz,
1, ctx->file) == 1); 1, ctx->file) == 1);
if (got) { if (got) {
char *data = buffer->block.data; char *data = buffer->block.data;
buffer->headsz = sizeof(struct RECORD_PAGE_HEADER) buffer->headsz = sizeof(RECORD_PAGE_HEADER)
+ ((2*getle16(data,6) - 1) | 7) + 1; + ((2*getle16(data,6) - 1) | 7) + 1;
buffer->safe = !replaceusa(buffer, blocksz); buffer->safe = !replaceusa(buffer, blocksz);
} else { } else {
buffer->safe = FALSE; buffer->safe = FALSE;
fprintf(stderr,"** Could not read block %d\n", num); fprintf(stderr,"** Could not read block %d\n", num);
} }
} }
return (buffer && buffer->safe ? buffer : (const struct BUFFER*)NULL); return (buffer && buffer->safe ? buffer : (const struct BUFFER*)NULL);
} }
skipping to change at line 797 skipping to change at line 799
case Win10Action37 : case Win10Action37 :
onmft = TRUE; onmft = TRUE;
break; break;
default : default :
onmft = FALSE; onmft = FALSE;
break; break;
} }
return (onmft); return (onmft);
} }
u32 get_undo_offset(const struct LOG_RECORD *logr) u32 get_undo_offset(const LOG_RECORD *logr)
{ {
u32 offset; u32 offset;
if (logr->lcns_to_follow) if (logr->lcns_to_follow)
offset = 0x30 + le16_to_cpu(logr->undo_offset); offset = 0x30 + le16_to_cpu(logr->undo_offset);
else else
offset = 0x28 + le16_to_cpu(logr->undo_offset); offset = 0x28 + le16_to_cpu(logr->undo_offset);
return (offset); return (offset);
} }
u32 get_redo_offset(const struct LOG_RECORD *logr) u32 get_redo_offset(const LOG_RECORD *logr)
{ {
u32 offset; u32 offset;
if (logr->lcns_to_follow) if (logr->lcns_to_follow)
offset = 0x30 + le16_to_cpu(logr->redo_offset); offset = 0x30 + le16_to_cpu(logr->redo_offset);
else else
offset = 0x28 + le16_to_cpu(logr->redo_offset); offset = 0x28 + le16_to_cpu(logr->redo_offset);
return (offset); return (offset);
} }
u32 get_extra_offset(const struct LOG_RECORD *logr) u32 get_extra_offset(const LOG_RECORD *logr)
{ {
u32 uoffset; u32 uoffset;
u32 roffset; u32 roffset;
roffset = get_redo_offset(logr) roffset = get_redo_offset(logr)
+ le16_to_cpu(logr->redo_length); + le16_to_cpu(logr->redo_length);
uoffset = get_undo_offset(logr) uoffset = get_undo_offset(logr)
+ le16_to_cpu(logr->undo_length); + le16_to_cpu(logr->undo_length);
return ((((uoffset > roffset ? uoffset : roffset) - 1) | 7) + 1); return ((((uoffset > roffset ? uoffset : roffset) - 1) | 7) + 1);
} }
static BOOL likelyop(const struct LOG_RECORD *logr) static BOOL likelyop(const LOG_RECORD *logr)
{ {
BOOL likely; BOOL likely;
switch (le32_to_cpu(logr->record_type)) { switch (logr->record_type) {
case LOG_STANDARD : /* standard record */ case LOG_STANDARD : /* standard record */
/* Operations in range 0..LastAction-1, can be both null */ /* Operations in range 0..LastAction-1, can be both null */
likely = ((unsigned int)le16_to_cpu(logr->redo_operation) likely = ((unsigned int)le16_to_cpu(logr->redo_operation)
< LastAction) < LastAction)
&& ((unsigned int)le16_to_cpu(logr->undo_operation) && ((unsigned int)le16_to_cpu(logr->undo_operation)
< LastAction) < LastAction)
/* Offsets aligned to 8 bytes */ /* Offsets aligned to 8 bytes */
&& !(le16_to_cpu(logr->redo_offset) & 7) && !(le16_to_cpu(logr->redo_offset) & 7)
&& !(le16_to_cpu(logr->undo_offset) & 7) && !(le16_to_cpu(logr->undo_offset) & 7)
/* transaction id must not be null */ /* transaction id must not be null */
skipping to change at line 917 skipping to change at line 919
/* /*
* Search for a likely record in a block * Search for a likely record in a block
* *
* Must not be used when syncing. * Must not be used when syncing.
* *
* Returns 0 when not found * Returns 0 when not found
*/ */
static u16 searchlikely(const struct BUFFER *buf) static u16 searchlikely(const struct BUFFER *buf)
{ {
const struct LOG_RECORD *logr; const LOG_RECORD *logr;
const char *data; const char *data;
u16 k; u16 k;
if (opts) if (opts)
printf("** Error : searchlikely() used for syncing\n"); printf("** Error : searchlikely() used for syncing\n");
data = buf->block.data; data = buf->block.data;
k = buf->headsz; k = buf->headsz;
logr = (const struct LOG_RECORD*)&data[k]; logr = (const LOG_RECORD*)&data[k];
if (!likelyop(logr)) { if (!likelyop(logr)) {
do { do {
k += 8; k += 8;
logr = (const struct LOG_RECORD*)&data[k]; logr = (const LOG_RECORD*)&data[k];
} while ((k <= (blocksz - LOG_RECORD_HEAD_SZ)) } while ((k <= (blocksz - LOG_RECORD_HEAD_SZ))
&& !likelyop(logr)); && !likelyop(logr));
if (k > (blocksz - LOG_RECORD_HEAD_SZ)) if (k > (blocksz - LOG_RECORD_HEAD_SZ))
k = 0; k = 0;
} }
return (k); return (k);
} }
/* /*
* From a previous block, determine the location of first record * From a previous block, determine the location of first record
skipping to change at line 957 skipping to change at line 959
* the current block when it ends near the end of the last skipped block. * the current block when it ends near the end of the last skipped block.
* *
* Returns 0 if some bad condition is found * Returns 0 if some bad condition is found
* Returns near blocksz when there is no beginning of record in * Returns near blocksz when there is no beginning of record in
* the current block * the current block
*/ */
static u16 firstrecord(int skipped, const struct BUFFER *buf, static u16 firstrecord(int skipped, const struct BUFFER *buf,
const struct BUFFER *prevbuf) const struct BUFFER *prevbuf)
{ {
const struct RECORD_PAGE_HEADER *rph; const RECORD_PAGE_HEADER *rph;
const struct RECORD_PAGE_HEADER *prevrph; const RECORD_PAGE_HEADER *prevrph;
const struct LOG_RECORD *logr; const LOG_RECORD *logr;
const char *data; const char *data;
const char *prevdata; const char *prevdata;
u16 k; u16 k;
u16 blkheadsz; u16 blkheadsz;
s32 size; s32 size;
rph = &buf->block.record; rph = &buf->block.record;
data = buf->block.data; data = buf->block.data;
if (prevbuf) { if (prevbuf) {
prevrph = &prevbuf->block.record; prevrph = &prevbuf->block.record;
skipping to change at line 981 skipping to change at line 983
blkheadsz = prevbuf->headsz; blkheadsz = prevbuf->headsz;
/* From previous page, determine where the current one starts */ /* From previous page, determine where the current one starts */
k = le16_to_cpu(prevrph->next_record_offset); k = le16_to_cpu(prevrph->next_record_offset);
/* a null value means there is no full record in next block */ /* a null value means there is no full record in next block */
if (!k) if (!k)
k = blkheadsz; k = blkheadsz;
} else } else
k = 0; k = 0;
/* Minimal size is apparently 48 : offset of redo_operation */ /* Minimal size is apparently 48 : offset of redo_operation */
if (k && ((blocksz - k) >= LOG_RECORD_HEAD_SZ)) { if (k && ((blocksz - k) >= LOG_RECORD_HEAD_SZ)) {
logr = (const struct LOG_RECORD*)&prevdata[k]; logr = (const LOG_RECORD*)&prevdata[k];
if (!logr->client_data_length) { if (!logr->client_data_length) {
/* /*
* Sometimes the end of record is free space. * Sometimes the end of record is free space.
* This apparently means reaching the end of * This apparently means reaching the end of
* a previous session, and must be considered * a previous session, and must be considered
* as an error. * as an error.
* We however tolerate this, unless syncing * We however tolerate this, unless syncing
* is requested. * is requested.
*/ */
printf("* Reaching free space at end of block %d\n", printf("* Reaching free space at end of block %d\n",
skipping to change at line 1054 skipping to change at line 1056
(int)buf->num, (int)k); (int)buf->num, (int)k);
} }
} }
/* /*
* In a wraparound situation, there is frequently no * In a wraparound situation, there is frequently no
* match... because there were no wraparound. * match... because there were no wraparound.
* Return an error if syncing is requested, otherwise * Return an error if syncing is requested, otherwise
* try to find a starting record. * try to find a starting record.
*/ */
if (k && prevbuf && (prevbuf->num > buf->num)) { if (k && prevbuf && (prevbuf->num > buf->num)) {
logr = (const struct LOG_RECORD*)&data[k]; logr = (const LOG_RECORD*)&data[k];
/* Accept reaching the end with no record beginning */ /* Accept reaching the end with no record beginning */
if ((k != le16_to_cpu(rph->next_record_offset)) if ((k != le16_to_cpu(rph->next_record_offset))
&& !likelyop(logr)) { && !likelyop(logr)) {
if (opts) { if (opts) {
k = 0; k = 0;
printf("** Could not wraparound\n"); printf("** Could not wraparound\n");
} else { } else {
k = searchlikely(buf); k = searchlikely(buf);
printf("* Skipping over bad wraparound\n"); printf("* Skipping over bad wraparound\n");
} }
skipping to change at line 1084 skipping to change at line 1086
* on current one, or it ends in such as there is no space for an * on current one, or it ends in such as there is no space for an
* overlapping one. * overlapping one.
* *
* Returns 0 if the previous block cannot be determined. * Returns 0 if the previous block cannot be determined.
*/ */
static const struct BUFFER *findprevious(CONTEXT *ctx, const struct BUFFER *buf) static const struct BUFFER *findprevious(CONTEXT *ctx, const struct BUFFER *buf)
{ {
const struct BUFFER *prevbuf; const struct BUFFER *prevbuf;
const struct BUFFER *savebuf; const struct BUFFER *savebuf;
const struct RECORD_PAGE_HEADER *rph; const RECORD_PAGE_HEADER *rph;
int skipped; int skipped;
int prevblk; int prevblk;
BOOL prevmiddle; BOOL prevmiddle;
BOOL error; BOOL error;
u16 endoff; u16 endoff;
error = FALSE; error = FALSE;
prevblk = buf->num; prevblk = buf->num;
savebuf = (struct BUFFER*)NULL; savebuf = (struct BUFFER*)NULL;
skipped = 0; skipped = 0;
do { do {
prevmiddle = FALSE; prevmiddle = FALSE;
if (prevblk > BASEBLKS) if (prevblk > BASEBLKS)
prevblk--; prevblk--;
else else
if (prevblk == BASEBLKS) if (prevblk == BASEBLKS)
prevblk = (logfilesz >> blockbits) - 1; prevblk = (logfilesz >> blockbits) - 1;
else { else {
rph = &buf->block.record; rph = &buf->block.record;
prevblk = (le32_to_cpu(rph->copy.file_offset) prevblk = (sle64_to_cpu(rph->copy.file_offset)
>> blockbits) - 1; >> blockbits) - 1;
/* /*
* If an initial block leads to block 4, it * If an initial block leads to block 4, it
* can mean the last block or no previous * can mean the last block or no previous
* block at all. Using the last block is safer, * block at all. Using the last block is safer,
* its lsn will indicate whether it is stale. * its lsn will indicate whether it is stale.
*/ */
if (prevblk < BASEBLKS) if (prevblk < BASEBLKS)
prevblk = (logfilesz >> blockbits) - 1; prevblk = (logfilesz >> blockbits) - 1;
} }
skipping to change at line 1151 skipping to change at line 1153
endoff = le16_to_cpu(rph->next_record_offset); endoff = le16_to_cpu(rph->next_record_offset);
if (endoff > (blocksz - LOG_RECORD_HEAD_SZ)) { if (endoff > (blocksz - LOG_RECORD_HEAD_SZ)) {
prevbuf = savebuf; prevbuf = savebuf;
} }
} }
return (error ? (struct BUFFER*)NULL : prevbuf); return (error ? (struct BUFFER*)NULL : prevbuf);
} }
void copy_attribute(struct ATTR *pa, const char *buf, int length) void copy_attribute(struct ATTR *pa, const char *buf, int length)
{ {
const struct ATTR_NEW *panew; const ATTR_NEW *panew;
struct ATTR_OLD old_aligned; ATTR_OLD old_aligned;
if (pa) { if (pa) {
switch (length) { switch (length) {
case sizeof(struct ATTR_NEW) : case sizeof(ATTR_NEW) :
panew = (const struct ATTR_NEW*)buf; panew = (const ATTR_NEW*)buf;
pa->type = panew->type; pa->type = panew->type;
pa->lsn = sle64_to_cpu(panew->lsn); pa->lsn = sle64_to_cpu(panew->lsn);
pa->inode = MREF(le64_to_cpu(panew->inode)); pa->inode = MREF(le64_to_cpu(panew->inode));
break; break;
case sizeof(struct ATTR_OLD) : case sizeof(ATTR_OLD) :
/* Badly aligned, first realign */ /* Badly aligned, first realign */
memcpy(&old_aligned,buf,sizeof(old_aligned)); memcpy(&old_aligned,buf,sizeof(old_aligned));
pa->type = old_aligned.type; pa->type = old_aligned.type;
pa->lsn = sle64_to_cpu(old_aligned.lsn); pa->lsn = sle64_to_cpu(old_aligned.lsn);
pa->inode = MREF(le64_to_cpu(old_aligned.inode)); pa->inode = MREF(le64_to_cpu(old_aligned.inode));
break; break;
default : default :
printf("** Unexpected attribute format, length %d\n", printf("** Unexpected attribute format, length %d\n",
length); length);
} }
} }
} }
static int refresh_attributes(const struct ACTION_RECORD *firstaction) static int refresh_attributes(const struct ACTION_RECORD *firstaction)
{ {
const struct ACTION_RECORD *action; const struct ACTION_RECORD *action;
const struct LOG_RECORD *logr; const LOG_RECORD *logr;
struct ATTR *pa; struct ATTR *pa;
const char *buf; const char *buf;
u32 extra; u32 extra;
u32 length; u32 length;
u32 len; u32 len;
u32 key; u32 key;
u32 x; u32 x;
u32 i; u32 i;
u32 step; u32 step;
u32 used; u32 used;
skipping to change at line 1270 skipping to change at line 1272
break; break;
} }
} }
return (0); return (0);
} }
/* /*
* Display a fixup * Display a fixup
*/ */
static void fixup(CONTEXT *ctx, const struct LOG_RECORD *logr, const char *buf, static void fixup(CONTEXT *ctx, const LOG_RECORD *logr, const char *buf,
BOOL redo) BOOL redo)
{ {
struct ATTR *pa; struct ATTR *pa;
int action; int action;
int attr; int attr;
int offs; int offs;
s32 length; s32 length;
int extra; int extra;
s32 i; s32 i;
int p; int p;
skipping to change at line 1323 skipping to change at line 1325
*/ */
if (!ctx->vol && !mftrecsz && (length > 8)) { if (!ctx->vol && !mftrecsz && (length > 8)) {
/* mftrecsz can be determined from usa_count */ /* mftrecsz can be determined from usa_count */
mftrecsz = (getle16(buf,6) - 1)*512; mftrecsz = (getle16(buf,6) - 1)*512;
mftrecbits = 1; mftrecbits = 1;
while ((u32)(1 << mftrecbits) < mftrecsz) while ((u32)(1 << mftrecbits) < mftrecsz)
mftrecbits++; mftrecbits++;
} }
printf(" new base MFT record, attr 0x%x (%s)\n",attr,attrname(a ttr)); printf(" new base MFT record, attr 0x%x (%s)\n",attr,attrname(a ttr));
printf(" inode %lld\n", printf(" inode %lld\n",
(((long long)le32_to_cpu(logr->target_vcn) (((long long)sle64_to_cpu(logr->target_vcn)
<< clusterbits) << clusterbits)
+ (le16_to_cpu(logr->cluster_index) << 9)) + (le16_to_cpu(logr->cluster_index) << 9))
>> mftrecbits); >> mftrecbits);
if (length >= 18) if (length >= 18)
printf(" seq number 0x%04x\n",(int)getle16(buf, 16)); printf(" seq number 0x%04x\n",(int)getle16(buf, 16));
if (length >= 20) if (length >= 20)
printf(" link count %d\n",(int)getle16(buf, 18)); printf(" link count %d\n",(int)getle16(buf, 18));
if (length >= 24) { if (length >= 24) {
u16 flags; u16 flags;
skipping to change at line 1372 skipping to change at line 1374
if ((lth <= 0) || (lth & 7)) if ((lth <= 0) || (lth & 7))
base = length; base = length;
else else
base += lth; base += lth;
} }
break; break;
case DeallocateFileRecordSegment : /* 3 */ case DeallocateFileRecordSegment : /* 3 */
printf(" free base MFT record, attr 0x%x (%s)\n", printf(" free base MFT record, attr 0x%x (%s)\n",
attr,attrname(attr)); attr,attrname(attr));
printf(" inode %lld\n", printf(" inode %lld\n",
(((long long)le32_to_cpu(logr->target_vcn) << clusterbits) (((long long)sle64_to_cpu(logr->target_vcn) << clusterbits)
+ (le16_to_cpu(logr->cluster_index) << 9)) >> mftrecbits); + (le16_to_cpu(logr->cluster_index) << 9)) >> mftrecbits);
break; break;
case CreateAttribute : /* 5 */ case CreateAttribute : /* 5 */
pa = getattrentry(attr,0); pa = getattrentry(attr,0);
base = 24; base = 24;
/* Assume the beginning of the attribute is always present */ /* Assume the beginning of the attribute is always present */
switch (getle32(buf,0)) { switch (getle32(buf,0)) {
case 0x30 : case 0x30 :
printf(" create file name, attr 0x%x\n",attr); printf(" create file name, attr 0x%x\n",attr);
if (pa) if (pa)
skipping to change at line 1761 skipping to change at line 1763
int more; int more;
int step; int step;
int used; int used;
step = getle16(buf, 8); step = getle16(buf, 8);
used = getle16(buf, 12); used = getle16(buf, 12);
/* /*
* Changed from Win10, formerly we got step = 44. * Changed from Win10, formerly we got step = 44.
* The record layout has also changed * The record layout has also changed
*/ */
if ((step != sizeof(struct ATTR_OLD)) if ((step != sizeof(ATTR_OLD))
&& (step != sizeof(struct ATTR_NEW))) { && (step != sizeof(ATTR_NEW))) {
printf(" ** Unexpected step %d\n",step); printf(" ** Unexpected step %d\n",step);
} }
more = 0; more = 0;
for (x=0; (x<used) && (i<length); i+=step, x++) { for (x=0; (x<used) && (i<length); i+=step, x++) {
pa = getattrentry(i,0); pa = getattrentry(i,0);
if (pa) { if (pa) {
copy_attribute(pa, &buf[i], step); copy_attribute(pa, &buf[i], step);
if (x <= SHOWATTRS) { if (x <= SHOWATTRS) {
printf(" attr 0x%x inode %lld" printf(" attr 0x%x inode %lld"
" type %s", " type %s",
skipping to change at line 1835 skipping to change at line 1837
} while (key && (i < length)); } while (key && (i < length));
if (more) if (more)
printf(" (%d more attrs not shown)\n",more); printf(" (%d more attrs not shown)\n",more);
} }
break; break;
default : default :
break; break;
} }
} }
static void detaillogr(CONTEXT *ctx, const struct LOG_RECORD *logr) static void detaillogr(CONTEXT *ctx, const LOG_RECORD *logr)
{ {
u64 lcn; u64 lcn;
u64 baselcn; u64 baselcn;
unsigned int i; unsigned int i;
unsigned int off; unsigned int off;
unsigned int undo; unsigned int undo;
unsigned int redo; unsigned int redo;
unsigned int extra; unsigned int extra;
unsigned int end; unsigned int end;
unsigned int listsize; unsigned int listsize;
BOOL onmft; BOOL onmft;
switch (le32_to_cpu(logr->record_type)) { switch (logr->record_type) {
case 1 : case LOG_STANDARD :
onmft = logr->cluster_index onmft = logr->cluster_index
|| acts_on_mft(le16_to_cpu(logr->redo_operation)) || acts_on_mft(le16_to_cpu(logr->redo_operation))
|| acts_on_mft(le16_to_cpu(logr->undo_operation)); || acts_on_mft(le16_to_cpu(logr->undo_operation));
printf("redo_operation %04x %s\n", printf("redo_operation %04x %s\n",
(int)le16_to_cpu(logr->redo_operation), (int)le16_to_cpu(logr->redo_operation),
actionname(le16_to_cpu(logr->redo_operation))); actionname(le16_to_cpu(logr->redo_operation)));
printf("undo_operation %04x %s\n", printf("undo_operation %04x %s\n",
(int)le16_to_cpu(logr->undo_operation), (int)le16_to_cpu(logr->undo_operation),
actionname(le16_to_cpu(logr->undo_operation))); actionname(le16_to_cpu(logr->undo_operation)));
printf("redo_offset %04x\n", printf("redo_offset %04x\n",
skipping to change at line 1880 skipping to change at line 1882
(int)le16_to_cpu(logr->lcns_to_follow)); (int)le16_to_cpu(logr->lcns_to_follow));
printf("record_offset %04x\n", printf("record_offset %04x\n",
(int)le16_to_cpu(logr->record_offset)); (int)le16_to_cpu(logr->record_offset));
printf("attribute_offset %04x\n", printf("attribute_offset %04x\n",
(int)le16_to_cpu(logr->attribute_offset)); (int)le16_to_cpu(logr->attribute_offset));
printf("cluster_index %04x\n", printf("cluster_index %04x\n",
(int)le16_to_cpu(logr->cluster_index)); (int)le16_to_cpu(logr->cluster_index));
printf("attribute_flags %04x\n", printf("attribute_flags %04x\n",
(int)le16_to_cpu(logr->attribute_flags)); (int)le16_to_cpu(logr->attribute_flags));
if (mftrecbits && onmft) if (mftrecbits && onmft)
printf("target_vcn %08lx (inode %lld)\n", printf("target_vcn %016llx (inode %lld)\n",
(long)le32_to_cpu(logr->target_vcn), (long long)sle64_to_cpu(logr->target_vcn),
(((long long)le32_to_cpu(logr->target_vcn) (((long long)sle64_to_cpu(logr->target_vcn)
<< clusterbits) << clusterbits)
+ (le16_to_cpu(logr->cluster_index) << 9)) + (le16_to_cpu(logr->cluster_index) << 9))
>> mftrecbits); >> mftrecbits);
else else
printf("target_vcn %08lx\n", printf("target_vcn %016llx\n",
(long)le32_to_cpu(logr->target_vcn)); (long long)sle64_to_cpu(logr->target_vcn));
printf("reserved3 %08lx\n",
(long)le32_to_cpu(logr->reserved3));
/* Compute a base for the current run of mft */ /* Compute a base for the current run of mft */
baselcn = le64_to_cpu(logr->lcn_list[0]) baselcn = sle64_to_cpu(logr->lcn_list[0])
- le32_to_cpu(logr->target_vcn); - sle64_to_cpu(logr->target_vcn);
for (i=0; i<le16_to_cpu(logr->lcns_to_follow) for (i=0; i<le16_to_cpu(logr->lcns_to_follow)
&& (i<SHOWLISTS); i++) { && (i<SHOWLISTS); i++) {
lcn = le64_to_cpu(logr->lcn_list[i]); lcn = sle64_to_cpu(logr->lcn_list[i]);
printf(" (%d offs 0x%x) lcn %016llx",i, printf(" (%d offs 0x%x) lcn %016llx",i,
(int)(8*i + sizeof(LOG_RECORD) - 8), (int)(8*i + sizeof(LOG_RECORD) - 8),
(long long)lcn); (long long)lcn);
lcn &= 0xffffffffffffULL; lcn &= 0xffffffffffffULL;
if (mftrecsz && onmft) { if (mftrecsz && onmft) {
if (clustersz > mftrecsz) if (clustersz > mftrecsz)
printf(" (MFT records for inodes" printf(" (MFT records for inodes"
" %lld-%lld)\n", " %lld-%lld)\n",
(long long)((lcn - baselcn) (long long)((lcn - baselcn)
*clustersz/mftrecsz), *clustersz/mftrecsz),
skipping to change at line 1995 skipping to change at line 1995
if (extra <= end) if (extra <= end)
{ {
/* show redo data */ /* show redo data */
if (logr->redo_length) if (logr->redo_length)
{ {
if (logr->lcns_to_follow) if (logr->lcns_to_follow)
{ {
off = le16_to_cpu(logr->record_offset) off = le16_to_cpu(logr->record_offset)
+ le16_to_cpu(logr->attribute_offset); + le16_to_cpu(logr->attribute_offset);
printf("redo data (new data) cluster 0x%llx pos 0x%x :\n", printf("redo data (new data) cluster 0x%llx pos 0x%x :\n",
(long long)le64_to_cpu(logr->lcn_list[off (long long)sle64_to_cpu(logr->lcn_list[off
>> clusterbits]), >> clusterbits]),
(int)(off & (clustersz - 1))); (int)(off & (clustersz - 1)));
} }
else else
printf("redo data (new data) at offs 0x%x :\n",redo); printf("redo data (new data) at offs 0x%x :\n",redo);
if ((u32)(redo + le16_to_cpu(logr->redo_length)) if ((u32)(redo + le16_to_cpu(logr->redo_length))
<= end) <= end)
{ {
hexdump((const char*)logr hexdump((const char*)logr
+ redo,le16_to_cpu(logr->redo_length)); + redo,le16_to_cpu(logr->redo_length));
skipping to change at line 2024 skipping to change at line 2024
} }
/* show undo data */ /* show undo data */
if (logr->undo_length) if (logr->undo_length)
{ {
if (logr->lcns_to_follow) if (logr->lcns_to_follow)
{ {
off = le16_to_cpu(logr->record_offset) off = le16_to_cpu(logr->record_offset)
+ le16_to_cpu(logr->attribute_offset); + le16_to_cpu(logr->attribute_offset);
printf("undo data (old data) cluster 0x%llx pos 0x%x :\n", printf("undo data (old data) cluster 0x%llx pos 0x%x :\n",
(long long)le64_to_cpu(logr->lcn_list[off (long long)sle64_to_cpu(logr->lcn_list[off
>> clusterbits]), >> clusterbits]),
(int)(off & (clustersz - 1))); (int)(off & (clustersz - 1)));
} }
else printf("undo data (old data) at offs 0x%x :\n",undo); else printf("undo data (old data) at offs 0x%x :\n",undo);
if ((u32)(undo + le16_to_cpu(logr->undo_length)) <= end) if ((u32)(undo + le16_to_cpu(logr->undo_length)) <= end)
{ {
if ((undo + le16_to_cpu(logr->undo_length)) < 2*blocksz) if ((undo + le16_to_cpu(logr->undo_length)) < 2*blocksz)
{ {
hexdump((const char*)logr hexdump((const char*)logr
+ undo,le16_to_cpu(logr->undo_length)); + undo,le16_to_cpu(logr->undo_length));
skipping to change at line 2071 skipping to change at line 2071
{ {
/* sometimes the designated data overflows */ /* sometimes the designated data overflows */
if (logr->redo_length if (logr->redo_length
&& ((u32)(redo + le16_to_cpu(logr->redo_length)) > end)) && ((u32)(redo + le16_to_cpu(logr->redo_length)) > end))
printf("* redo data overflows from record\n"); printf("* redo data overflows from record\n");
if (logr->undo_length if (logr->undo_length
&& ((u32)(undo + le16_to_cpu(logr->undo_length)) > end)) && ((u32)(undo + le16_to_cpu(logr->undo_length)) > end))
printf("* undo data overflows from record\n"); printf("* undo data overflows from record\n");
} }
break; break;
case 2 : case LOG_CHECKPOINT :
printf("---> checkpoint record\n"); printf("---> checkpoint record\n");
printf("redo_operation %04x %s\n", printf("redo_operation %04x %s\n",
(int)le16_to_cpu(logr->redo_operation), (int)le16_to_cpu(logr->redo_operation),
actionname(le16_to_cpu(logr->redo_operation))); actionname(le16_to_cpu(logr->redo_operation)));
printf("undo_operation %04x %s\n", printf("undo_operation %04x %s\n",
(int)le16_to_cpu(logr->undo_operation), (int)le16_to_cpu(logr->undo_operation),
actionname(le16_to_cpu(logr->undo_operation))); actionname(le16_to_cpu(logr->undo_operation)));
printf("redo_offset %04x\n", printf("redo_offset %04x\n",
(int)le16_to_cpu(logr->redo_offset)); (int)le16_to_cpu(logr->redo_offset));
printf("redo_length %04x\n", printf("redo_length %04x\n",
skipping to change at line 2093 skipping to change at line 2093
printf("transaction_lsn %016llx\n", printf("transaction_lsn %016llx\n",
(long long)sle64_to_cpu(logr->transaction_lsn)); (long long)sle64_to_cpu(logr->transaction_lsn));
printf("attributes_lsn %016llx\n", printf("attributes_lsn %016llx\n",
(long long)sle64_to_cpu(logr->attributes_lsn)); (long long)sle64_to_cpu(logr->attributes_lsn));
printf("names_lsn %016llx\n", printf("names_lsn %016llx\n",
(long long)sle64_to_cpu(logr->names_lsn)); (long long)sle64_to_cpu(logr->names_lsn));
printf("dirty_pages_lsn %016llx\n", printf("dirty_pages_lsn %016llx\n",
(long long)sle64_to_cpu(logr->dirty_pages_lsn)); (long long)sle64_to_cpu(logr->dirty_pages_lsn));
listsize = le32_to_cpu(logr->client_data_length) listsize = le32_to_cpu(logr->client_data_length)
+ LOG_RECORD_HEAD_SZ + LOG_RECORD_HEAD_SZ
- offsetof(struct LOG_RECORD, unknown_list); - offsetof(LOG_RECORD, unknown_list);
if (listsize > 8*SHOWLISTS) if (listsize > 8*SHOWLISTS)
listsize = 8*SHOWLISTS; listsize = 8*SHOWLISTS;
for (i=0; 8*i<listsize; i++) for (i=0; 8*i<listsize; i++)
printf("unknown-%u %016llx\n",i, printf("unknown-%u %016llx\n",i,
(long long)le64_to_cpu(logr->unknown_list[i])); (long long)le64_to_cpu(logr->unknown_list[i]));
break; break;
default : default :
printf("** Unknown action type\n"); printf("** Unknown action type\n");
if (le32_to_cpu(logr->client_data_length) < blocksz) { if (le32_to_cpu(logr->client_data_length) < blocksz) {
printf("client_data for record type %ld\n", printf("client_data for record type %ld\n",
(long)le32_to_cpu(logr->record_type)); (long)le32_to_cpu(logr->record_type));
hexdump((const char*)&logr->redo_operation, hexdump((const char*)&logr->redo_operation,
le32_to_cpu(logr->client_data_length)); le32_to_cpu(logr->client_data_length));
} else } else
printf("** Bad client data\n"); printf("** Bad client data\n");
break; break;
} }
} }
BOOL within_lcn_range(const struct LOG_RECORD *logr) BOOL within_lcn_range(const LOG_RECORD *logr)
{ {
u64 lcn; u64 lcn;
unsigned int i; unsigned int i;
BOOL within; BOOL within;
within = FALSE; within = FALSE;
switch (le32_to_cpu(logr->record_type)) { switch (logr->record_type) {
case 1 : case LOG_STANDARD :
for (i=0; i<le16_to_cpu(logr->lcns_to_follow); i++) { for (i=0; i<le16_to_cpu(logr->lcns_to_follow); i++) {
lcn = MREF(le64_to_cpu(logr->lcn_list[i])); lcn = MREF(sle64_to_cpu(logr->lcn_list[i]));
if ((lcn >= firstlcn) && (lcn <= lastlcn)) if ((lcn >= firstlcn) && (lcn <= lastlcn))
within = TRUE; within = TRUE;
} }
break; break;
default : default :
break; break;
} }
return (within); return (within);
} }
static void showlogr(CONTEXT *ctx, int k, const struct LOG_RECORD *logr) static void showlogr(CONTEXT *ctx, int k, const LOG_RECORD *logr)
{ {
s32 diff; s32 diff;
if (optv && (!optc || within_lcn_range(logr))) { if (optv && (!optc || within_lcn_range(logr))) {
diff = sle64_to_cpu(logr->this_lsn) - synced_lsn; diff = sle64_to_cpu(logr->this_lsn) - synced_lsn;
printf("this_lsn %016llx (synced%s%ld) %s\n", printf("this_lsn %016llx (synced%s%ld) %s\n",
(long long)sle64_to_cpu(logr->this_lsn), (long long)sle64_to_cpu(logr->this_lsn),
(diff < 0 ? "" : "+"),(long)diff, (diff < 0 ? "" : "+"),(long)diff,
commitment(diff + synced_lsn)); commitment(diff + synced_lsn));
printf("client_previous_lsn %016llx\n", printf("client_previous_lsn %016llx\n",
skipping to change at line 2161 skipping to change at line 2161
(int)le16_to_cpu(logr->client_id.seq_number)); (int)le16_to_cpu(logr->client_id.seq_number));
printf("client_index %d\n", printf("client_index %d\n",
(int)le16_to_cpu(logr->client_id.client_index)); (int)le16_to_cpu(logr->client_id.client_index));
printf("record_type %08lx\n", printf("record_type %08lx\n",
(long)le32_to_cpu(logr->record_type)); (long)le32_to_cpu(logr->record_type));
printf("transaction_id %08lx\n", printf("transaction_id %08lx\n",
(long)le32_to_cpu(logr->transaction_id)); (long)le32_to_cpu(logr->transaction_id));
printf("log_record_flags %04x\n", printf("log_record_flags %04x\n",
(int)le16_to_cpu(logr->log_record_flags)); (int)le16_to_cpu(logr->log_record_flags));
printf("reserved1 %04x %04x %04x\n", printf("reserved1 %04x %04x %04x\n",
(int)le16_to_cpu(logr->reserved1[0]), (int)le16_to_cpu(logr->reserved_or_alignment[0]),
(int)le16_to_cpu(logr->reserved1[1]), (int)le16_to_cpu(logr->reserved_or_alignment[1]),
(int)le16_to_cpu(logr->reserved1[2])); (int)le16_to_cpu(logr->reserved_or_alignment[2]));
detaillogr(ctx, logr); detaillogr(ctx, logr);
} }
if (optt) { if (optt) {
const char *state; const char *state;
if (logr->record_type == const_cpu_to_le32(2)) if (logr->record_type == LOG_CHECKPOINT)
state = "--checkpoint--"; state = "--checkpoint--";
else else
state = commitment(sle64_to_cpu(logr->this_lsn)); state = commitment(sle64_to_cpu(logr->this_lsn));
printf(" at %04x %016llx %s (%ld) %s\n",k, printf(" at %04x %016llx %s (%ld) %s\n",k,
(long long)sle64_to_cpu(logr->this_lsn), (long long)sle64_to_cpu(logr->this_lsn),
state, state,
(long)(sle64_to_cpu(logr->this_lsn) - synced_lsn), (long)(sle64_to_cpu(logr->this_lsn) - synced_lsn),
actionname(le16_to_cpu(logr->redo_operation))); actionname(le16_to_cpu(logr->redo_operation)));
if (logr->client_previous_lsn || logr->client_undo_next_lsn) { if (logr->client_previous_lsn || logr->client_undo_next_lsn) {
if (logr->client_previous_lsn if (logr->client_previous_lsn
skipping to change at line 2209 skipping to change at line 2209
} }
} }
/* /*
* Mark transactions which should be redone * Mark transactions which should be redone
*/ */
static void mark_transactions(struct ACTION_RECORD *lastaction) static void mark_transactions(struct ACTION_RECORD *lastaction)
{ {
struct ACTION_RECORD *action; struct ACTION_RECORD *action;
const struct LOG_RECORD *logr; const LOG_RECORD *logr;
le32 id; le32 id;
int actives; int actives;
BOOL more; BOOL more;
BOOL committed; BOOL committed;
actives = 0; actives = 0;
do { do {
more = FALSE; more = FALSE;
id = const_cpu_to_le32(0); id = const_cpu_to_le32(0);
for (action=lastaction; action; action=action->prev) { for (action=lastaction; action; action=action->prev) {
skipping to change at line 2266 skipping to change at line 2266
} }
} }
if (optv && (actives > 1)) if (optv && (actives > 1))
printf("%d active transactions in set\n",actives); printf("%d active transactions in set\n",actives);
} }
/* /*
* Enqueue an action and play the queued actions on end of set * Enqueue an action and play the queued actions on end of set
*/ */
static TRISTATE enqueue_action(CONTEXT *ctx, const struct LOG_RECORD *logr, static TRISTATE enqueue_action(CONTEXT *ctx, const LOG_RECORD *logr,
int size, int num) int size, int num)
{ {
struct ACTION_RECORD *action; struct ACTION_RECORD *action;
TRISTATE state; TRISTATE state;
int err; int err;
err = 1; err = 1;
state = T_ERR; state = T_ERR;
/* enqueue record */ /* enqueue record */
action = (struct ACTION_RECORD*) action = (struct ACTION_RECORD*)
skipping to change at line 2293 skipping to change at line 2293
action->prev = (struct ACTION_RECORD*)NULL; action->prev = (struct ACTION_RECORD*)NULL;
action->next = ctx->firstaction; action->next = ctx->firstaction;
if (ctx->firstaction) if (ctx->firstaction)
ctx->firstaction->prev = action; ctx->firstaction->prev = action;
else else
ctx->lastaction = action; ctx->lastaction = action;
ctx->firstaction = action; ctx->firstaction = action;
err = 0; err = 0;
state = T_OK; state = T_OK;
if ((optp || optu) if ((optp || optu)
&& (logr->record_type == const_cpu_to_le32(2))) { && (logr->record_type == LOG_CHECKPOINT)) {
/* if chkp process queue, and increment count */ /* if chkp process queue, and increment count */
playedactions++; playedactions++;
if (playedactions <= playcount) { if (playedactions <= playcount) {
if (optv) if (optv)
printf("* Refreshing attributes\n"); printf("* Refreshing attributes\n");
err = refresh_attributes(ctx->firstaction); err = refresh_attributes(ctx->firstaction);
if (optv) if (optv)
printf("* Undoing transaction set %d" printf("* Undoing transaction set %d"
" (actions %d->%d)\n", " (actions %d->%d)\n",
(int)playedactions, (int)playedactions,
skipping to change at line 2361 skipping to change at line 2361
if (err) { if (err) {
printf("* Syncing actions failed\n"); printf("* Syncing actions failed\n");
state = T_ERR; state = T_ERR;
} else } else
state = T_DONE; state = T_DONE;
} }
} }
return (state); return (state);
} }
static void showheadrcrd(u32 blk, const struct RECORD_PAGE_HEADER *rph) static void showheadrcrd(u32 blk, const RECORD_PAGE_HEADER *rph)
{ {
s32 diff; s32 diff;
if (optv) { if (optv) {
printf("magic %08lx\n", printf("magic %08lx\n",
(long)le32_to_cpu(rph->head.magic)); (long)le32_to_cpu(rph->magic));
printf("usa_ofs %04x\n", printf("usa_ofs %04x\n",
(int)le16_to_cpu(rph->head.usa_ofs)); (int)le16_to_cpu(rph->usa_ofs));
printf("usa_count %04x\n", printf("usa_count %04x\n",
(int)le16_to_cpu(rph->head.usa_count)); (int)le16_to_cpu(rph->usa_count));
if (blk < 4) if (blk < 4)
printf("file_offset %08lx\n", printf("file_offset %016llx\n",
(long)le32_to_cpu(rph->copy.file_offset)); (long long)sle64_to_cpu(rph->copy.file_offset));
else { else {
diff = sle64_to_cpu(rph->copy.last_lsn) - synced_lsn; diff = sle64_to_cpu(rph->copy.last_lsn) - synced_lsn;
printf("last_lsn %016llx" printf("last_lsn %016llx"
" (synced%s%ld)\n", " (synced%s%ld)\n",
(long long)sle64_to_cpu(rph->copy.last_lsn), (long long)sle64_to_cpu(rph->copy.last_lsn),
(diff < 0 ? "" : "+"),(long)diff); (diff < 0 ? "" : "+"),(long)diff);
} }
printf("flags %08lx\n", printf("flags %08lx\n",
(long)le32_to_cpu(rph->flags)); (long)le32_to_cpu(rph->flags));
printf("page_count %d\n", printf("page_count %d\n",
(int)le16_to_cpu(rph->page_count)); (int)le16_to_cpu(rph->page_count));
printf("page_position %d\n", printf("page_position %d\n",
(int)le16_to_cpu(rph->page_position)); (int)le16_to_cpu(rph->page_position));
printf("next_record_offset %04x\n", printf("next_record_offset %04x\n",
(int)le16_to_cpu(rph->next_record_offset)); (int)le16_to_cpu(rph->next_record_offset));
printf("reserved4 %04x %04x %04x\n", printf("reserved4 %04x %04x %04x\n",
(int)le16_to_cpu(rph->reserved4[0]), (int)le16_to_cpu(rph->reserved[0]),
(int)le16_to_cpu(rph->reserved4[1]), (int)le16_to_cpu(rph->reserved[1]),
(int)le16_to_cpu(rph->reserved4[2])); (int)le16_to_cpu(rph->reserved[2]));
diff = sle64_to_cpu(rph->last_end_lsn) - synced_lsn; diff = sle64_to_cpu(rph->last_end_lsn) - synced_lsn;
printf("last_end_lsn %016llx (synced%s%ld)\n", printf("last_end_lsn %016llx (synced%s%ld)\n",
(long long)sle64_to_cpu(rph->last_end_lsn), (long long)sle64_to_cpu(rph->last_end_lsn),
(diff < 0 ? "" : "+"),(long)diff); (diff < 0 ? "" : "+"),(long)diff);
printf("usn %04x\n", printf("usn %04x\n",
(int)getle16(rph,le16_to_cpu(rph->head.usa_ofs))); (int)getle16(rph,le16_to_cpu(rph->usa_ofs)));
printf("\n"); printf("\n");
} else { } else {
if (optt) { if (optt) {
const char *state; const char *state;
state = commitment(sle64_to_cpu(rph->copy.last_lsn)); state = commitment(sle64_to_cpu(rph->copy.last_lsn));
diff = sle64_to_cpu(rph->copy.last_lsn) - synced_lsn; diff = sle64_to_cpu(rph->copy.last_lsn) - synced_lsn;
printf(" last %016llx (synced%s%ld) %s\n", printf(" last %016llx (synced%s%ld) %s\n",
(long long)sle64_to_cpu(rph->copy.last_lsn), (long long)sle64_to_cpu(rph->copy.last_lsn),
(diff < 0 ? "" : "+"),(long)diff, state); (diff < 0 ? "" : "+"),(long)diff, state);
skipping to change at line 2432 skipping to change at line 2432
* Returns the position of first action in next block. If this is * Returns the position of first action in next block. If this is
* greater than a block size (for actions overlapping more than * greater than a block size (for actions overlapping more than
* two blocks), then some blocks have to be skipped. * two blocks), then some blocks have to be skipped.
* *
* Returns 0 in case of error * Returns 0 in case of error
*/ */
static u16 overlapshow(CONTEXT *ctx, u16 k, u32 blk, const struct BUFFER *buf, static u16 overlapshow(CONTEXT *ctx, u16 k, u32 blk, const struct BUFFER *buf,
const struct BUFFER *nextbuf) const struct BUFFER *nextbuf)
{ {
const struct LOG_RECORD *logr; const LOG_RECORD *logr;
const char *data; const char *data;
const char *nextdata; const char *nextdata;
char *fullrec; char *fullrec;
u32 size; u32 size;
u32 nextspace; u32 nextspace;
u32 space; u32 space;
BOOL likely; BOOL likely;
u16 blkheadsz; u16 blkheadsz;
data = buf->block.data; data = buf->block.data;
logr = (const struct LOG_RECORD*)&data[k]; logr = (const LOG_RECORD*)&data[k];
size = le32_to_cpu(logr->client_data_length) + LOG_RECORD_HEAD_SZ; size = le32_to_cpu(logr->client_data_length) + LOG_RECORD_HEAD_SZ;
blkheadsz = buf->headsz; blkheadsz = buf->headsz;
if (nextbuf && (blk >= BASEBLKS)) { if (nextbuf && (blk >= BASEBLKS)) {
nextdata = nextbuf->block.data; nextdata = nextbuf->block.data;
space = blocksz - k; space = blocksz - k;
nextspace = blocksz - blkheadsz; nextspace = blocksz - blkheadsz;
if ((space >= LOG_RECORD_HEAD_SZ) if ((space >= LOG_RECORD_HEAD_SZ)
&& (size > space)) { && (size > space)) {
fullrec = (char*)malloc(size); fullrec = (char*)malloc(size);
if (size <= (space + nextspace)) { if (size <= (space + nextspace)) {
/* Overlap on two blocks */ /* Overlap on two blocks */
memcpy(fullrec,&data[k],space); memcpy(fullrec,&data[k],space);
memcpy(&fullrec[space], memcpy(&fullrec[space],
nextdata + blkheadsz, nextdata + blkheadsz,
size - space); size - space);
likely = likelyop((struct LOG_RECORD*)fullrec); likely = likelyop((LOG_RECORD*)fullrec);
actionnum++; actionnum++;
if (optv) { if (optv) {
printf("\nOverlapping record %u at 0x%x" printf("\nOverlapping record %u at 0x%x"
" size %d (next at 0x%x)\n", " size %d (next at 0x%x)\n",
(int)actionnum,(int)k, (int)actionnum,(int)k,
(int)size, (int)(k + size)); (int)size, (int)(k + size));
printf("Overlap marked for block %ld" printf("Overlap marked for block %ld"
" space %d likely %d\n", " space %d likely %d\n",
(long)blk,(int)space,likely); (long)blk,(int)space,likely);
} }
if (likely) if (likely)
showlogr(ctx, k, showlogr(ctx, k,
(struct LOG_RECORD*)fullrec); (LOG_RECORD*)fullrec);
else else
printf("** Skipping unlikely" printf("** Skipping unlikely"
" overlapping record\n"); " overlapping record\n");
k += size - blocksz + blkheadsz; k += size - blocksz + blkheadsz;
} else { } else {
const struct BUFFER *midbuf; const struct BUFFER *midbuf;
int skip; int skip;
u32 next; u32 next;
u32 pos; u32 pos;
int i; int i;
skipping to change at line 2525 skipping to change at line 2525
(int)pos,(int)size); (int)pos,(int)size);
likely = FALSE; likely = FALSE;
} }
midbuf = read_buffer(ctx, blk + skip + 1); midbuf = read_buffer(ctx, blk + skip + 1);
if (midbuf) if (midbuf)
memcpy(&fullrec[pos], memcpy(&fullrec[pos],
&midbuf->block.data[blkheadsz], &midbuf->block.data[blkheadsz],
size - pos); size - pos);
else else
likely = FALSE; likely = FALSE;
if (!likelyop((struct LOG_RECORD*)fullrec)) if (!likelyop((LOG_RECORD*)fullrec))
likely = FALSE; likely = FALSE;
actionnum++; actionnum++;
if (optv) { if (optv) {
printf("\nBig overlapping record %u at " printf("\nBig overlapping record %u at "
"0x%x size %u (next at 0x%x)\n", "0x%x size %u (next at 0x%x)\n",
(int)actionnum,(int)k,(int)size, (int)actionnum,(int)k,(int)size,
(int)(k + size)); (int)(k + size));
printf("Overlap marked for block %ld" printf("Overlap marked for block %ld"
" space %d likely %d\n", " space %d likely %d\n",
(long)blk,(int)space,likely); (long)blk,(int)space,likely);
} }
if (likely) if (likely)
showlogr(ctx, k, showlogr(ctx, k,
(struct LOG_RECORD*)fullrec); (LOG_RECORD*)fullrec);
else else
printf("** Skipping unlikely" printf("** Skipping unlikely"
" overlapping record\n"); " overlapping record\n");
/* next and skip are only for displaying */ /* next and skip are only for displaying */
next = (size - space) % nextspace next = (size - space) % nextspace
+ blkheadsz; + blkheadsz;
if ((blocksz - next) < LOG_RECORD_HEAD_SZ) if ((blocksz - next) < LOG_RECORD_HEAD_SZ)
next = blkheadsz; next = blkheadsz;
if (next == blkheadsz) if (next == blkheadsz)
skip++; skip++;
skipping to change at line 2591 skipping to change at line 2591
* *
* Returns the position of first action in next block. If this is * Returns the position of first action in next block. If this is
* greater than a block size, then some blocks have to be skipped. * greater than a block size, then some blocks have to be skipped.
* *
* Returns 0 in case of error * Returns 0 in case of error
*/ */
static u16 forward_rcrd(CONTEXT *ctx, u32 blk, u16 pos, static u16 forward_rcrd(CONTEXT *ctx, u32 blk, u16 pos,
const struct BUFFER *buf, const struct BUFFER *nextbuf) const struct BUFFER *buf, const struct BUFFER *nextbuf)
{ {
const struct RECORD_PAGE_HEADER *rph; const RECORD_PAGE_HEADER *rph;
const struct LOG_RECORD *logr; const LOG_RECORD *logr;
const char *data; const char *data;
u16 k; u16 k;
u16 endoff; u16 endoff;
BOOL stop; BOOL stop;
rph = &buf->block.record; rph = &buf->block.record;
if (rph && (rph->head.magic == magic_RCRD)) { if (rph && (rph->magic == magic_RCRD)) {
data = buf->block.data; data = buf->block.data;
showheadrcrd(blk, rph); showheadrcrd(blk, rph);
k = buf->headsz; k = buf->headsz;
if ((k < pos) && (pos < blocksz)) { if ((k < pos) && (pos < blocksz)) {
k = ((pos - 1) | 7) + 1; k = ((pos - 1) | 7) + 1;
} }
// TODO check bad start > blocksz - 48 // TODO check bad start > blocksz - 48
logr = (const struct LOG_RECORD*)&data[k]; logr = (const LOG_RECORD*)&data[k];
stop = FALSE; stop = FALSE;
if (!likelyop(logr)) { if (!likelyop(logr)) {
if (optv) if (optv)
printf("* Bad start 0x%x for block %d\n", printf("* Bad start 0x%x for block %d\n",
(int)pos,(int)blk); (int)pos,(int)blk);
k = searchlikely(buf); k = searchlikely(buf);
if ((k + sizeof(struct LOG_RECORD)) > blocksz) { if ((k + sizeof(LOG_RECORD)) > blocksz) {
printf("No likely full record in block %lu\n", printf("No likely full record in block %lu\n",
(unsigned long)blk); (unsigned long)blk);
/* there can be a partial one */ /* there can be a partial one */
k = le16_to_cpu(rph->next_record_offset); k = le16_to_cpu(rph->next_record_offset);
if ((k < (u16)sizeof(struct RECORD_PAGE_HEADER)) if ((k < (u16)sizeof(RECORD_PAGE_HEADER))
|| ((blocksz - k) < LOG_RECORD_HEAD_SZ)) || ((blocksz - k) < LOG_RECORD_HEAD_SZ))
stop = TRUE; stop = TRUE;
} else { } else {
if (optv) if (optv)
printf("First record computed at" printf("First record computed at"
" offset 0x%x\n", (int)k); " offset 0x%x\n", (int)k);
} }
} }
while (!stop) { while (!stop) {
s32 size; s32 size;
logr = (const struct LOG_RECORD*)&data[k]; logr = (const LOG_RECORD*)&data[k];
size = le32_to_cpu(logr->client_data_length) size = le32_to_cpu(logr->client_data_length)
+ LOG_RECORD_HEAD_SZ; + LOG_RECORD_HEAD_SZ;
if ((size < MINRECSIZE) if ((size < MINRECSIZE)
|| (size > MAXRECSIZE) || (size > MAXRECSIZE)
|| (size & 7)) { || (size & 7)) {
printf("** Bad record size %ld in block %ld" printf("** Bad record size %ld in block %ld"
" offset 0x%x\n", " offset 0x%x\n",
(long)size, (long)buf->num, (int)k); (long)size, (long)buf->num, (int)k);
showlogr(ctx, k, logr); showlogr(ctx, k, logr);
k = 0; k = 0;
skipping to change at line 2676 skipping to change at line 2676
} }
} else { } else {
k = overlapshow(ctx, k, blk, k = overlapshow(ctx, k, blk,
buf, nextbuf); buf, nextbuf);
stop = TRUE; stop = TRUE;
} }
} }
} }
} else { } else {
printf("** Not a RCRD record, MAGIC 0x%08lx\n", printf("** Not a RCRD record, MAGIC 0x%08lx\n",
(long)le32_to_cpu(rph->head.magic)); (long)le32_to_cpu(rph->magic));
k = 0; k = 0;
} }
return (k); return (k);
} }
/* /*
* Display a restart page * Display a restart page
*/ */
static void showrest(const struct RESTART_PAGE_HEADER *rest) static void showrest(const RESTART_PAGE_HEADER *rest)
{ {
const struct RESTART_AREA *resa; const RESTART_AREA *resa;
const struct RESTART_CLIENT *rcli; const LOG_CLIENT_RECORD *rcli;
const char *data; const char *data;
data = (const char*)rest; data = (const char*)rest;
if ((rest->head.magic == magic_RSTR) if ((rest->magic == magic_RSTR)
|| (rest->head.magic == magic_CHKD)) { || (rest->magic == magic_CHKD)) {
if (optv) { if (optv) {
printf("magic %08lx\n", printf("magic %08lx\n",
(long)le32_to_cpu(rest->head.magic)); (long)le32_to_cpu(rest->magic));
printf("usa_ofs %04x\n", printf("usa_ofs %04x\n",
(int)le16_to_cpu(rest->head.usa_ofs)); (int)le16_to_cpu(rest->usa_ofs));
printf("usa_count %04x\n", printf("usa_count %04x\n",
(int)le16_to_cpu(rest->head.usa_count)); (int)le16_to_cpu(rest->usa_count));
printf("chkdsk_lsn %016llx\n", printf("chkdsk_lsn %016llx\n",
(long long)sle64_to_cpu(rest->chkdsk_lsn)); (long long)sle64_to_cpu(rest->chkdsk_lsn));
printf("system_page_size %08lx\n", printf("system_page_size %08lx\n",
(long)le32_to_cpu(rest->system_page_size)); (long)le32_to_cpu(rest->system_page_size));
printf("log_page_size %08lx\n", printf("log_page_size %08lx\n",
(long)le32_to_cpu(rest->log_page_size)); (long)le32_to_cpu(rest->log_page_size));
printf("restart_offset %04x\n", printf("restart_area_offset %04x\n",
(int)le16_to_cpu(rest->restart_offset)); (int)le16_to_cpu(rest->restart_area_offset));
printf("minor_vers %d\n", printf("minor_vers %d\n",
(int)le16_to_cpu(rest->minor_ver)); (int)sle16_to_cpu(rest->minor_ver));
printf("major_vers %d\n", printf("major_vers %d\n",
(int)le16_to_cpu(rest->major_ver)); (int)sle16_to_cpu(rest->major_ver));
printf("usn %04x\n", printf("usn %04x\n",
(int)le16_to_cpu(rest->usn)); (int)le16_to_cpu(rest->usn));
printf("\n"); printf("\n");
} else { } else {
if (optt) if (optt)
printf(" chkdsk %016llx\n", printf(" chkdsk %016llx\n",
(long long)sle64_to_cpu(rest->chkdsk_lsn)); (long long)sle64_to_cpu(rest->chkdsk_lsn));
} }
resa = (const struct RESTART_AREA*) resa = (const RESTART_AREA*)
&data[le16_to_cpu(rest->restart_offset)]; &data[le16_to_cpu(rest->restart_area_offset)];
if (optv) { if (optv) {
printf("current_lsn %016llx\n", printf("current_lsn %016llx\n",
(long long)sle64_to_cpu(resa->current_lsn)); (long long)sle64_to_cpu(resa->current_lsn));
printf("log_clients %04x\n", printf("log_clients %04x\n",
(int)le16_to_cpu(resa->log_clients)); (int)le16_to_cpu(resa->log_clients));
printf("client_free_list %04x\n", printf("client_free_list %04x\n",
(int)le16_to_cpu(resa->client_free_list)); (int)le16_to_cpu(resa->client_free_list));
printf("client_in_use_list %04x\n", printf("client_in_use_list %04x\n",
(int)le16_to_cpu(resa->client_in_use_list)); (int)le16_to_cpu(resa->client_in_use_list));
printf("flags %04x\n", printf("flags %04x\n",
(int)le16_to_cpu(resa->flags)); (int)le16_to_cpu(resa->flags));
printf("seq_number_bits %08lx\n", printf("seq_number_bits %08lx\n",
(long)le32_to_cpu(resa->seq_number_bits)); (long)le32_to_cpu(resa->seq_number_bits));
printf("restart_area_length %04x\n", printf("restart_area_length %04x\n",
(int)le16_to_cpu(resa->restart_area_length)); (int)le16_to_cpu(resa->restart_area_length));
printf("client_array_offset %04x\n", printf("client_array_offset %04x\n",
(int)le16_to_cpu(resa->client_array_offset)); (int)le16_to_cpu(resa->client_array_offset));
printf("file_size %016llx\n", printf("file_size %016llx\n",
(long long)le64_to_cpu(resa->file_size)); (long long)sle64_to_cpu(resa->file_size));
printf("last_lsn_data_len %08lx\n", printf("last_lsn_data_len %08lx\n",
(long)le32_to_cpu(resa->last_lsn_data_length)); (long)le32_to_cpu(resa->last_lsn_data_length));
printf("record_length %04x\n", printf("record_length %04x\n",
(int)le16_to_cpu(resa->record_length)); (int)le16_to_cpu(resa->log_record_header_length)) ;
printf("log_page_data_offs %04x\n", printf("log_page_data_offs %04x\n",
(int)le16_to_cpu(resa->log_page_data_offset)); (int)le16_to_cpu(resa->log_page_data_offset));
printf("restart_log_open_count %08lx\n", printf("restart_log_open_count %08lx\n",
(long)le32_to_cpu(resa->restart_log_open_count)); (long)le32_to_cpu(resa->restart_log_open_count));
printf("\n"); printf("\n");
} else { } else {
if (optt) if (optt)
printf(" latest %016llx\n", printf(" latest %016llx\n",
(long long)sle64_to_cpu(resa->current_lsn)); (long long)sle64_to_cpu(resa->current_lsn));
} }
rcli = (const struct RESTART_CLIENT*) rcli = (const LOG_CLIENT_RECORD*)
&data[le16_to_cpu(rest->restart_offset) &data[le16_to_cpu(rest->restart_area_offset)
+ le16_to_cpu(resa->client_array_offset)]; + le16_to_cpu(resa->client_array_offset)];
if (optv) { if (optv) {
printf("oldest_lsn %016llx\n", printf("oldest_lsn %016llx\n",
(long long)sle64_to_cpu(rcli->oldest_lsn)); (long long)sle64_to_cpu(rcli->oldest_lsn));
printf("client_restart_lsn %016llx\n", printf("client_restart_lsn %016llx\n",
(long long)sle64_to_cpu(rcli->client_restart_lsn) ); (long long)sle64_to_cpu(rcli->client_restart_lsn) );
printf("prev_client %04x\n", printf("prev_client %04x\n",
(int)le16_to_cpu(rcli->prev_client)); (int)le16_to_cpu(rcli->prev_client));
printf("next_client %04x\n", printf("next_client %04x\n",
(int)le16_to_cpu(rcli->next_client)); (int)le16_to_cpu(rcli->next_client));
skipping to change at line 2789 skipping to change at line 2789
printf(" synced %016llx\n", printf(" synced %016llx\n",
(long long)sle64_to_cpu( (long long)sle64_to_cpu(
rcli->oldest_lsn)); rcli->oldest_lsn));
printf(" committed %016llx\n", printf(" committed %016llx\n",
(long long)sle64_to_cpu( (long long)sle64_to_cpu(
rcli->client_restart_lsn)); rcli->client_restart_lsn));
} }
} }
} else } else
printf("Not a RSTR or CHKD record, MAGIC 0x%08lx\n", printf("Not a RSTR or CHKD record, MAGIC 0x%08lx\n",
(long)le32_to_cpu(rest->head.magic)); (long)le32_to_cpu(rest->magic));
} }
static BOOL dorest(CONTEXT *ctx, unsigned long blk, static BOOL dorest(CONTEXT *ctx, unsigned long blk,
const struct RESTART_PAGE_HEADER *rph, BOOL initial) const RESTART_PAGE_HEADER *rph, BOOL initial)
{ {
const struct RESTART_AREA *resa; const RESTART_AREA *resa;
const struct RESTART_CLIENT *rcli; const LOG_CLIENT_RECORD *rcli;
const char *data; const char *data;
s64 diff; s64 diff;
int offs; int offs;
int size; int size;
BOOL change; BOOL change;
BOOL dirty; BOOL dirty;
data = (const char*)rph; data = (const char*)rph;
offs = le16_to_cpu(rph->restart_offset); offs = le16_to_cpu(rph->restart_area_offset);
resa = (const struct RESTART_AREA*)&data[offs]; resa = (const RESTART_AREA*)&data[offs];
rcli = (const struct RESTART_CLIENT*)&data[offs rcli = (const LOG_CLIENT_RECORD*)&data[offs
+ le16_to_cpu(resa->client_array_offset)]; + le16_to_cpu(resa->client_array_offset)];
if (initial) { if (initial) {
/* Information from block initially found best */ /* Information from block initially found best */
latest_lsn = sle64_to_cpu(resa->current_lsn); latest_lsn = sle64_to_cpu(resa->current_lsn);
committed_lsn = sle64_to_cpu(rcli->client_restart_lsn); committed_lsn = sle64_to_cpu(rcli->client_restart_lsn);
synced_lsn = sle64_to_cpu(rcli->oldest_lsn); synced_lsn = sle64_to_cpu(rcli->oldest_lsn);
memcpy(&log_header, rph, memcpy(&log_header, rph,
sizeof(struct RESTART_PAGE_HEADER)); sizeof(RESTART_PAGE_HEADER));
offs = le16_to_cpu(log_header.restart_offset); offs = le16_to_cpu(log_header.restart_area_offset);
memcpy(&restart, &data[offs], memcpy(&restart, &data[offs],
sizeof(struct RESTART_AREA)); sizeof(RESTART_AREA));
offs += le16_to_cpu(restart.client_array_offset); offs += le16_to_cpu(restart.client_array_offset);
memcpy(&client, &data[offs], memcpy(&client, &data[offs],
sizeof(struct RESTART_CLIENT)); sizeof(LOG_CLIENT_RECORD));
dirty = !(resa->flags & RESTART_VOLUME_IS_CLEAN); dirty = !(resa->flags & RESTART_VOLUME_IS_CLEAN);
if (optv || optt) if (optv || optt)
printf("* Using initial restart page," printf("* Using initial restart page,"
" syncing from 0x%llx, %s\n", " syncing from 0x%llx, %s\n",
(long long)synced_lsn, (long long)synced_lsn,
(dirty ? "dirty" : "clean")); (dirty ? "dirty" : "clean"));
/* Get the block page size */ /* Get the block page size */
blocksz = le32_to_cpu(rph->log_page_size); blocksz = le32_to_cpu(rph->log_page_size);
if (optv) if (optv)
printf("* Block size %ld bytes\n", (long)blocksz); printf("* Block size %ld bytes\n", (long)blocksz);
skipping to change at line 2861 skipping to change at line 2861
if (ctx->vol) { if (ctx->vol) {
change = (opts > 1) && (diff < 0); change = (opts > 1) && (diff < 0);
} else { } else {
change = (opts > 1 ? diff < 0 : diff > 0); change = (opts > 1 ? diff < 0 : diff > 0);
} }
if (change) { if (change) {
committed_lsn = sle64_to_cpu(rcli->client_restart_lsn); committed_lsn = sle64_to_cpu(rcli->client_restart_lsn);
synced_lsn = sle64_to_cpu(rcli->oldest_lsn); synced_lsn = sle64_to_cpu(rcli->oldest_lsn);
latest_lsn = sle64_to_cpu(resa->current_lsn); latest_lsn = sle64_to_cpu(resa->current_lsn);
memcpy(&log_header, rph, memcpy(&log_header, rph,
sizeof(struct RESTART_PAGE_HEADER)); sizeof(RESTART_PAGE_HEADER));
offs = le16_to_cpu(log_header.restart_offset); offs = le16_to_cpu(log_header.restart_area_offset);
memcpy(&restart, &data[offs], memcpy(&restart, &data[offs],
sizeof(struct RESTART_AREA)); sizeof(RESTART_AREA));
offs += le16_to_cpu(restart.client_array_offset); offs += le16_to_cpu(restart.client_array_offset);
memcpy(&client, &data[offs], memcpy(&client, &data[offs],
sizeof(struct RESTART_CLIENT)); sizeof(LOG_CLIENT_RECORD));
dirty = !(resa->flags & RESTART_VOLUME_IS_CLEAN); dirty = !(resa->flags & RESTART_VOLUME_IS_CLEAN);
if (optv || optt) if (optv || optt)
printf("* Using %s restart page," printf("* Using %s restart page,"
" syncing from 0x%llx, %s\n", " syncing from 0x%llx, %s\n",
(diff < 0 ? "older" : "newer"), (diff < 0 ? "older" : "newer"),
(long long)synced_lsn, (long long)synced_lsn,
(dirty ? "dirty" : "clean")); (dirty ? "dirty" : "clean"));
} }
} }
restart_lsn = synced_lsn; restart_lsn = synced_lsn;
skipping to change at line 2896 skipping to change at line 2896
* sync parameters. * sync parameters.
* *
* Returns the first restart buffer * Returns the first restart buffer
* or NULL if the restart block is not valid * or NULL if the restart block is not valid
*/ */
static const struct BUFFER *read_restart(CONTEXT *ctx) static const struct BUFFER *read_restart(CONTEXT *ctx)
{ {
const struct BUFFER *buf; const struct BUFFER *buf;
BOOL bad; BOOL bad;
int major, minor;
bad = FALSE; bad = FALSE;
if (ctx->vol) { if (ctx->vol) {
struct RESTART_PAGE_HEADER *rph; RESTART_PAGE_HEADER *rph;
rph = (struct RESTART_PAGE_HEADER*)NULL; rph = (RESTART_PAGE_HEADER*)NULL;
/* Full mode : use the restart page selected by the library */ /* Full mode : use the restart page selected by the library */
if (ntfs_check_logfile(log_na, &rph)) { if (ntfs_check_logfile(log_na, &rph)) {
/* rph is left unchanged for a wiped out log file */ /* rph is left unchanged for a wiped out log file */
if (rph) { if (rph) {
dorest(ctx, 0, rph, TRUE); dorest(ctx, 0, rph, TRUE);
free(rph); free(rph);
buf = read_buffer(ctx,0); buf = read_buffer(ctx,0);
} else { } else {
buf = (const struct BUFFER*)NULL; buf = (const struct BUFFER*)NULL;
printf("** The log file has been wiped out\n"); printf("** The log file has been wiped out\n");
skipping to change at line 2926 skipping to change at line 2927
} }
} else { } else {
/* Reduced mode : rely on first restart page */ /* Reduced mode : rely on first restart page */
blockbits = BLOCKBITS; /* Until the correct value is read */ blockbits = BLOCKBITS; /* Until the correct value is read */
blocksz = 1L << blockbits; blocksz = 1L << blockbits;
buf = read_buffer(ctx,0); buf = read_buffer(ctx,0);
} }
if (buf) { if (buf) {
NTFS_RECORD_TYPES magic; NTFS_RECORD_TYPES magic;
magic = buf->block.restart.head.magic; magic = buf->block.restart.magic;
switch (magic) { switch (magic) {
case magic_RSTR : case magic_RSTR :
break; break;
case magic_CHKD : case magic_CHKD :
printf("** The log file has been obsoleted by chkdsk\n"); printf("** The log file has been obsoleted by chkdsk\n");
bad = TRUE; bad = TRUE;
break; break;
case magic_empty : case magic_empty :
printf("** The log file has been wiped out\n"); printf("** The log file has been wiped out\n");
bad = TRUE; bad = TRUE;
break; break;
default : default :
printf("** Invalid restart block\n"); printf("** Invalid restart block\n");
bad = TRUE; bad = TRUE;
break; break;
} }
if (!bad && !ctx->vol) if (!bad && !ctx->vol)
dorest(ctx, 0, &buf->block.restart, TRUE); dorest(ctx, 0, &buf->block.restart, TRUE);
if ((buf->block.restart.major_ver != const_cpu_to_le16(1)) major = sle16_to_cpu(buf->block.restart.major_ver);
|| (buf->block.restart.minor_ver != const_cpu_to_le16(1))) { minor = sle16_to_cpu(buf->block.restart.minor_ver);
printf("** Unsupported $LogFile version %d.%d\n", if ((major == 2) && (minor == 0)) {
le16_to_cpu(buf->block.restart.major_ver), if (!optk) {
le16_to_cpu(buf->block.restart.minor_ver)); printf("** Fast restart mode detected,"
bad = TRUE; " data could be lost\n");
} printf(" Use option --kill-fast-restart"
" to bypass\n");
bad = TRUE;
}
} else
if ((major != 1) || (minor != 1)) {
printf("** Unsupported $LogFile version %d.%d\n",
major, minor);
bad = TRUE;
}
if (bad) { if (bad) {
buf = (const struct BUFFER*)NULL; buf = (const struct BUFFER*)NULL;
} }
} }
return (buf); return (buf);
} }
/* /*
* Mark the logfile as synced * Mark the logfile as synced
*/ */
skipping to change at line 2976 skipping to change at line 2986
int off; int off;
int err; int err;
err = 1; err = 1;
buffer = (char*)malloc(blocksz); buffer = (char*)malloc(blocksz);
if (buffer) { if (buffer) {
memset(buffer, 0, blocksz); memset(buffer, 0, blocksz);
restart.client_in_use_list = LOGFILE_NO_CLIENT; restart.client_in_use_list = LOGFILE_NO_CLIENT;
restart.flags |= RESTART_VOLUME_IS_CLEAN; restart.flags |= RESTART_VOLUME_IS_CLEAN;
client.oldest_lsn = cpu_to_sle64(restart_lsn); client.oldest_lsn = cpu_to_sle64(restart_lsn);
/* Set $LogFile version to 1.1 so that volume can be mounted */
log_header.major_ver = const_cpu_to_sle16(1);
log_header.minor_ver = const_cpu_to_sle16(1);
memcpy(buffer, &log_header, memcpy(buffer, &log_header,
sizeof(struct RESTART_PAGE_HEADER)); sizeof(RESTART_PAGE_HEADER));
off = le16_to_cpu(log_header.restart_offset); off = le16_to_cpu(log_header.restart_area_offset);
memcpy(&buffer[off], &restart, memcpy(&buffer[off], &restart,
sizeof(struct RESTART_AREA)); sizeof(RESTART_AREA));
off += le16_to_cpu(restart.client_array_offset); off += le16_to_cpu(restart.client_array_offset);
memcpy(&buffer[off], &client, memcpy(&buffer[off], &client,
sizeof(struct RESTART_CLIENT)); sizeof(LOG_CLIENT_RECORD));
if (!ntfs_mst_pre_write_fixup((NTFS_RECORD*)buffer, blocksz) if (!ntfs_mst_pre_write_fixup((NTFS_RECORD*)buffer, blocksz)
&& (ntfs_attr_pwrite(log_na, 0, && (ntfs_attr_pwrite(log_na, 0,
blocksz, buffer) == blocksz) blocksz, buffer) == blocksz)
&& (ntfs_attr_pwrite(log_na, (u64)1 << blockbits, && (ntfs_attr_pwrite(log_na, (u64)1 << blockbits,
blocksz, buffer) == blocksz)) blocksz, buffer) == blocksz))
err = 0; err = 0;
free(buffer); free(buffer);
} }
return (err); return (err);
} }
/* /*
* Determine the most recent valid record block * Determine the most recent valid record block
*/ */
static const struct BUFFER *best_start(const struct BUFFER *buf, static const struct BUFFER *best_start(const struct BUFFER *buf,
const struct BUFFER *altbuf) const struct BUFFER *altbuf)
{ {
const struct BUFFER *best; const struct BUFFER *best;
const struct RECORD_PAGE_HEADER *head; const RECORD_PAGE_HEADER *head;
const struct RECORD_PAGE_HEADER *althead; const RECORD_PAGE_HEADER *althead;
s64 diff; s64 diff;
if (!buf || !altbuf) if (!buf || !altbuf)
best = (buf ? buf : altbuf); best = (buf ? buf : altbuf);
else { else {
head = &buf->block.record; head = &buf->block.record;
althead = &altbuf->block.record; althead = &altbuf->block.record;
/* determine most recent, caring for wraparounds */ /* determine most recent, caring for wraparounds */
diff = sle64_to_cpu(althead->last_end_lsn) diff = sle64_to_cpu(althead->last_end_lsn)
- sle64_to_cpu(head->last_end_lsn); - sle64_to_cpu(head->last_end_lsn);
if (diff > 0) if (diff > 0)
best = altbuf; best = altbuf;
else else
best = buf; best = buf;
} }
if (best && (best->block.record.head.magic != magic_RCRD)) if (best && (best->block.record.magic != magic_RCRD))
best = (const struct BUFFER*)NULL; best = (const struct BUFFER*)NULL;
return (best); return (best);
} }
/* /*
* Interpret the boot data * Interpret the boot data
* *
* Probably not needed any more, use ctx->vol * Probably not needed any more, use ctx->vol
*/ */
skipping to change at line 3110 skipping to change at line 3123
/* /*
* Analyze a $LogFile copy * Analyze a $LogFile copy
* *
* A $LogFile cannot be played. It can be however be analyzed in * A $LogFile cannot be played. It can be however be analyzed in
* stand-alone mode. * stand-alone mode.
* The location of the $MFT will have to be determined elsewhere. * The location of the $MFT will have to be determined elsewhere.
*/ */
static BOOL getlogfiledata(CONTEXT *ctx, const char *boot) static BOOL getlogfiledata(CONTEXT *ctx, const char *boot)
{ {
const struct RESTART_PAGE_HEADER *rph; const RESTART_PAGE_HEADER *rph;
const struct RESTART_AREA *rest; const RESTART_AREA *rest;
BOOL ok; BOOL ok;
u32 off; u32 off;
s64 size; s64 size;
u32 system_page_size;
u32 log_page_size;
ok = FALSE; ok = FALSE;
fseek(ctx->file,0L,2); fseek(ctx->file,0L,2);
size = ftell(ctx->file); size = ftell(ctx->file);
rph = (const struct RESTART_PAGE_HEADER*)boot; rph = (const RESTART_PAGE_HEADER*)boot;
off = le16_to_cpu(rph->restart_offset); off = le16_to_cpu(rph->restart_area_offset);
rest = (const struct RESTART_AREA*)&boot[off]; /*
* If the system or log page sizes are smaller than the ntfs block size
* or either is not a power of 2 we cannot handle this log file.
*/
system_page_size = le32_to_cpu(rph->system_page_size);
log_page_size = le32_to_cpu(rph->log_page_size);
if (system_page_size < NTFS_BLOCK_SIZE ||
log_page_size < NTFS_BLOCK_SIZE ||
system_page_size & (system_page_size - 1) ||
log_page_size & (log_page_size - 1)) {
printf("** Unsupported page size.\n");
goto out;
}
if (off & 7 || off > system_page_size) {
printf("** Inconsistent restart area offset.\n");
goto out;
}
rest = (const RESTART_AREA*)&boot[off];
/* estimate cluster size from log file size (unreliable) */ /* estimate cluster size from log file size (unreliable) */
switch (le32_to_cpu(rest->seq_number_bits)) { switch (le32_to_cpu(rest->seq_number_bits)) {
case 45 : clustersz = 512; break; case 45 : clustersz = 512; break;
case 43 : clustersz = 1024; break; /* can be 1024 or 2048 */ case 43 : clustersz = 1024; break; /* can be 1024 or 2048 */
case 40 : case 40 :
default : clustersz = 4096; break; default : clustersz = 4096; break;
} }
clusterbits = 1; clusterbits = 1;
skipping to change at line 3144 skipping to change at line 3176
printf("* Assuming cluster size %ld\n",(long)clustersz); printf("* Assuming cluster size %ld\n",(long)clustersz);
logfilelcn = 0; logfilelcn = 0;
logfilesz = size; logfilesz = size;
if (optv) if (optv)
printf("Log file size %lld bytes, cluster size %ld\n", printf("Log file size %lld bytes, cluster size %ld\n",
(long long)size, (long)clustersz); (long long)size, (long)clustersz);
/* Have to wait an InitializeFileRecordSegment to get these values */ /* Have to wait an InitializeFileRecordSegment to get these values */
mftrecsz = 0; mftrecsz = 0;
mftrecbits = 0; mftrecbits = 0;
ok = TRUE; ok = TRUE;
out:
return (ok); return (ok);
} }
/* /*
* Get basic volume data * Get basic volume data
* *
* Locate the MFT and Logfile * Locate the MFT and Logfile
* Not supposed to read the first log block... * Not supposed to read the first log block...
*/ */
static BOOL getvolumedata(CONTEXT *ctx, char *boot) static BOOL getvolumedata(CONTEXT *ctx, char *boot)
{ {
const struct RESTART_AREA *rest; const RESTART_AREA *rest;
BOOL ok; BOOL ok;
ok = FALSE; ok = FALSE;
rest = (const struct RESTART_AREA*)NULL; rest = (const RESTART_AREA*)NULL;
if (ctx->vol) { if (ctx->vol) {
getboot(boot); getboot(boot);
mftlcn = ctx->vol->mft_lcn; mftlcn = ctx->vol->mft_lcn;
mftcnt = ctx->vol->mft_na->data_size/mftrecsz; mftcnt = ctx->vol->mft_na->data_size/mftrecsz;
if (!locatelogfile(ctx)) if (!locatelogfile(ctx))
ok = TRUE; ok = TRUE;
else { else {
fprintf(stderr,"** Could not read the log file\n"); fprintf(stderr,"** Could not read the log file\n");
} }
} else { } else {
if (ctx->file if (ctx->file
&& (!memcmp(boot,"RSTR",4) || !memcmp(boot,"CHKD",4))) { && (!memcmp(boot,"RSTR",4) || !memcmp(boot,"CHKD",4))) {
printf("* Assuming a log file copy\n"); printf("* Assuming a log file copy\n");
getlogfiledata(ctx, boot); ok = getlogfiledata(ctx, boot);
ok = TRUE; if (!ok)
goto out;
} else } else
fprintf(stderr,"** Not an NTFS image or log file\n"); fprintf(stderr,"** Not an NTFS image or log file\n");
} }
// TODO get rest ?, meaningful ? // TODO get rest ?, meaningful ?
if (ok && rest) { if (ok && rest) {
if (rest->client_in_use_list if (rest->client_in_use_list
|| !(rest->flags & const_cpu_to_le16(2))) || !(rest->flags & const_cpu_to_le16(2)))
printf("Volume was not unmounted safely\n"); printf("Volume was not unmounted safely\n");
else else
printf("Volume was unmounted safely\n"); printf("Volume was unmounted safely\n");
if (le16_to_cpu(rest->client_in_use_list) > 1) if (le16_to_cpu(rest->client_in_use_list) > 1)
printf("** multiple clients not implemented\n"); printf("** multiple clients not implemented\n");
} }
out:
return (ok); return (ok);
} }
/* /*
* Open the volume (or the log file) and gets its parameters * Open the volume (or the log file) and gets its parameters
* *
* Returns TRUE if successful * Returns TRUE if successful
*/ */
static BOOL open_volume(CONTEXT *ctx, const char *device_name) static BOOL open_volume(CONTEXT *ctx, const char *device_name)
skipping to change at line 3225 skipping to change at line 3260
ctx->vol = (ntfs_volume*)NULL; ctx->vol = (ntfs_volume*)NULL;
ctx->file = fopen(device_name, "rb"); ctx->file = fopen(device_name, "rb");
if (ctx->file) { if (ctx->file) {
got = fread(boot.buf,1,1024,ctx->file); got = fread(boot.buf,1,1024,ctx->file);
if ((got == 1024) if ((got == 1024)
&& (!memcmp(boot.buf, "RSTR", 4) && (!memcmp(boot.buf, "RSTR", 4)
|| !memcmp(boot.buf, "CHKD", 4))) { || !memcmp(boot.buf, "CHKD", 4))) {
/* This appears to be a log file */ /* This appears to be a log file */
ctx->vol = (ntfs_volume*)NULL; ctx->vol = (ntfs_volume*)NULL;
ok = getvolumedata(ctx, boot.buf); ok = getvolumedata(ctx, boot.buf);
} if (!ok) {
if (!ok) fclose(ctx->file);
goto out;
}
} else {
fclose(ctx->file); fclose(ctx->file);
}
} }
if (!ok) { if (!ok) {
/* Not a log file, assume an ntfs device, mount it */ /* Not a log file, assume an ntfs device, mount it */
ctx->file = (FILE*)NULL; ctx->file = (FILE*)NULL;
ctx->vol = ntfs_mount(device_name, ctx->vol = ntfs_mount(device_name,
((optp || optu || opts) && !optn ((optk || optp || optu || opts) && !optn
? NTFS_MNT_FORENSIC : NTFS_MNT_RDONLY)); ? NTFS_MNT_FORENSIC : NTFS_MNT_RDONLY));
if (ctx->vol) { if (ctx->vol) {
ok = getvolumedata(ctx, boot.buf); ok = getvolumedata(ctx, boot.buf);
if (!ok) if (!ok)
ntfs_umount(ctx->vol, TRUE); ntfs_umount(ctx->vol, TRUE);
} }
} }
out:
return (ok); return (ok);
} }
static u16 dorcrd(CONTEXT *ctx, u32 blk, u16 pos, const struct BUFFER *buf, static u16 dorcrd(CONTEXT *ctx, u32 blk, u16 pos, const struct BUFFER *buf,
const struct BUFFER *nextbuf) const struct BUFFER *nextbuf)
{ {
if (optv) { if (optv) {
if (optv >= 2) if (optv >= 2)
hexdump(buf->block.data,blocksz); hexdump(buf->block.data,blocksz);
printf("* RCRD in block %ld 0x%lx (addr 0x%llx)" printf("* RCRD in block %ld 0x%lx (addr 0x%llx)"
skipping to change at line 3268 skipping to change at line 3308
return (forward_rcrd(ctx, blk, pos, buf, nextbuf)); return (forward_rcrd(ctx, blk, pos, buf, nextbuf));
} }
/* /*
* Concatenate and process a record overlapping on several blocks * Concatenate and process a record overlapping on several blocks
*/ */
static TRISTATE backoverlap(CONTEXT *ctx, int blk, static TRISTATE backoverlap(CONTEXT *ctx, int blk,
const char *data, const char *nextdata, int k) const char *data, const char *nextdata, int k)
{ {
const struct LOG_RECORD *logr; const LOG_RECORD *logr;
char *fullrec; char *fullrec;
s32 size; s32 size;
int space; int space;
int nextspace; int nextspace;
TRISTATE state; TRISTATE state;
u16 blkheadsz; u16 blkheadsz;
logr = (const struct LOG_RECORD*)&data[k]; logr = (const LOG_RECORD*)&data[k];
state = T_ERR; state = T_ERR;
size = le32_to_cpu(logr->client_data_length) + LOG_RECORD_HEAD_SZ; size = le32_to_cpu(logr->client_data_length) + LOG_RECORD_HEAD_SZ;
space = blocksz - k; space = blocksz - k;
blkheadsz = sizeof(struct RECORD_PAGE_HEADER) blkheadsz = sizeof(RECORD_PAGE_HEADER)
+ ((2*getle16(data,6) - 1) | 7) + 1; + ((2*getle16(data,6) - 1) | 7) + 1;
nextspace = blocksz - blkheadsz; nextspace = blocksz - blkheadsz;
if ((space >= LOG_RECORD_HEAD_SZ) if ((space >= LOG_RECORD_HEAD_SZ)
&& (size > space) && (size > space)
&& (size < MAXRECSIZE)) { && (size < MAXRECSIZE)) {
fullrec = (char*)malloc(size); fullrec = (char*)malloc(size);
memcpy(fullrec,&data[k],space); memcpy(fullrec,&data[k],space);
if (size <= (space + nextspace)) if (size <= (space + nextspace))
memcpy(&fullrec[space], nextdata + blkheadsz, memcpy(&fullrec[space], nextdata + blkheadsz,
size - space); size - space);
skipping to change at line 3319 skipping to change at line 3359
if (morebuf) { if (morebuf) {
moredata = morebuf->block.data; moredata = morebuf->block.data;
memcpy(&fullrec[total], memcpy(&fullrec[total],
moredata + blkheadsz, more); moredata + blkheadsz, more);
} }
total += more; total += more;
mblk++; mblk++;
} }
} }
state = (likelyop((struct LOG_RECORD*)fullrec) ? T_OK : T_ERR); state = (likelyop((LOG_RECORD*)fullrec) ? T_OK : T_ERR);
actionnum++; actionnum++;
if (optv) { if (optv) {
printf("\nOverlapping backward action %d at 0x%x" printf("\nOverlapping backward action %d at 0x%x"
" size %d (next at 0x%x)\n", " size %d (next at 0x%x)\n",
(int)actionnum,(int)k, (int)actionnum,(int)k,
(int)size,(int)(k + size)); (int)size,(int)(k + size));
printf("Overlap marked for block %ld space %d" printf("Overlap marked for block %ld space %d"
" likely %d\n", " likely %d\n",
(long)blk,(int)space,(state == T_OK)); (long)blk,(int)space,(state == T_OK));
} }
if (state == T_OK) { if (state == T_OK) {
showlogr(ctx, k, (struct LOG_RECORD*)fullrec); showlogr(ctx, k, (LOG_RECORD*)fullrec);
if (optp || optu || opts) if (optp || optu || opts)
state = enqueue_action(ctx, state = enqueue_action(ctx,
(struct LOG_RECORD*)fullrec, (LOG_RECORD*)fullrec,
size, actionnum); size, actionnum);
} else { } else {
/* Try to go on unless playing actions */ /* Try to go on unless playing actions */
if (optb && (state == T_ERR)) if (optb && (state == T_ERR))
state = T_OK; state = T_OK;
} }
free(fullrec); free(fullrec);
} else { } else {
/* Error conditions */ /* Error conditions */
if ((size < MINRECSIZE) || (size > MAXRECSIZE)) { if ((size < MINRECSIZE) || (size > MAXRECSIZE)) {
skipping to change at line 3363 skipping to change at line 3403
/* Do not abort, unless playing actions */ /* Do not abort, unless playing actions */
state = (optb ? T_OK : T_ERR); state = (optb ? T_OK : T_ERR);
} }
return (state); return (state);
} }
static TRISTATE backward_rcrd(CONTEXT *ctx, u32 blk, int skipped, static TRISTATE backward_rcrd(CONTEXT *ctx, u32 blk, int skipped,
const struct BUFFER *buf, const struct BUFFER *prevbuf, const struct BUFFER *buf, const struct BUFFER *prevbuf,
const struct BUFFER *nextbuf) const struct BUFFER *nextbuf)
{ {
u16 poslist[75]; /* 4096/sizeof(struct LOG_RECORD) */ u16 poslist[75]; /* 4096/sizeof(LOG_RECORD) */
const struct RECORD_PAGE_HEADER *rph; const RECORD_PAGE_HEADER *rph;
const struct RECORD_PAGE_HEADER *prevrph; const RECORD_PAGE_HEADER *prevrph;
const struct LOG_RECORD *logr; const LOG_RECORD *logr;
const char *data; const char *data;
const char *nextdata; const char *nextdata;
BOOL stop; BOOL stop;
TRISTATE state; TRISTATE state;
s32 size; s32 size;
int cnt; int cnt;
u16 k; u16 k;
u16 endoff; u16 endoff;
int j; int j;
state = T_ERR; state = T_ERR;
rph = &buf->block.record; rph = &buf->block.record;
prevrph = (struct RECORD_PAGE_HEADER*)NULL; prevrph = (RECORD_PAGE_HEADER*)NULL;
if (prevbuf) if (prevbuf)
prevrph = &prevbuf->block.record; prevrph = &prevbuf->block.record;
data = buf->block.data; data = buf->block.data;
if (rph && (rph->head.magic == magic_RCRD) if (rph && (rph->magic == magic_RCRD)
&& (!prevrph || (prevrph->head.magic == magic_RCRD))) { && (!prevrph || (prevrph->magic == magic_RCRD))) {
if (optv) { if (optv) {
if (optv >= 2) if (optv >= 2)
hexdump(data,blocksz); hexdump(data,blocksz);
printf("* RCRD in block %ld 0x%lx (addr 0x%llx)\n", printf("* RCRD in block %ld 0x%lx (addr 0x%llx)\n",
(long)blk,(long)blk, (long)blk,(long)blk,
(long long)loclogblk(ctx, blk)); (long long)loclogblk(ctx, blk));
} else { } else {
if (optt) if (optt)
printf("block %ld\n",(long)blk); printf("block %ld\n",(long)blk);
} }
showheadrcrd(blk, rph); showheadrcrd(blk, rph);
if (!prevbuf) if (!prevbuf)
k = buf->headsz; k = buf->headsz;
else else
k = firstrecord(skipped, buf, prevbuf); k = firstrecord(skipped, buf, prevbuf);
logr = (const struct LOG_RECORD*)&data[k]; logr = (const LOG_RECORD*)&data[k];
cnt = 0; cnt = 0;
/* check whether there is at least one beginning of record */ /* check whether there is at least one beginning of record */
endoff = le16_to_cpu(rph->next_record_offset); endoff = le16_to_cpu(rph->next_record_offset);
if (k && ((k < endoff) || !endoff)) { if (k && ((k < endoff) || !endoff)) {
logr = (const struct LOG_RECORD*)&data[k]; logr = (const LOG_RECORD*)&data[k];
if (likelyop(logr)) { if (likelyop(logr)) {
stop = FALSE; stop = FALSE;
state = T_OK; state = T_OK;
if (optv) if (optv)
printf("First record checked" printf("First record checked"
" at offset 0x%x\n", (int)k); " at offset 0x%x\n", (int)k);
} else { } else {
printf("** Bad first record at offset 0x%x\n", printf("** Bad first record at offset 0x%x\n",
(int)k); (int)k);
if (optv) if (optv)
skipping to change at line 3431 skipping to change at line 3471
" stopping at block %d\n", " stopping at block %d\n",
(int)blk); (int)blk);
state = T_ERR; state = T_ERR;
} else { } else {
/* Try to go on, unless running */ /* Try to go on, unless running */
if (optb) if (optb)
state = T_OK; state = T_OK;
} }
} }
while (!stop) { while (!stop) {
logr = (const struct LOG_RECORD*)&data[k]; logr = (const LOG_RECORD*)&data[k];
size = le32_to_cpu(logr->client_data_length) size = le32_to_cpu(logr->client_data_length)
+ LOG_RECORD_HEAD_SZ; + LOG_RECORD_HEAD_SZ;
if ((size < MINRECSIZE) if ((size < MINRECSIZE)
|| (size > MAXRECSIZE) || (size > MAXRECSIZE)
|| (size & 7)) { || (size & 7)) {
printf("** Bad size %ld in block %ld" printf("** Bad size %ld in block %ld"
" offset 0x%x, stopping\n", " offset 0x%x, stopping\n",
(long)size,(long)blk,(int)k); (long)size,(long)blk,(int)k);
stop = TRUE; stop = TRUE;
} else { } else {
skipping to change at line 3473 skipping to change at line 3513
&& ((k == endoff) || !endoff) && ((k == endoff) || !endoff)
&& ((u32)(k + LOG_RECORD_HEAD_SZ) <= blocksz)) { && ((u32)(k + LOG_RECORD_HEAD_SZ) <= blocksz)) {
if (nextbuf && (blk >= BASEBLKS)) { if (nextbuf && (blk >= BASEBLKS)) {
nextdata = nextbuf->block.data; nextdata = nextbuf->block.data;
state = backoverlap(ctx, blk, state = backoverlap(ctx, blk,
data, nextdata, k); data, nextdata, k);
} }
} }
for (j=cnt-1; (j>=0) && (state==T_OK); j--) { for (j=cnt-1; (j>=0) && (state==T_OK); j--) {
k = poslist[j]; k = poslist[j];
logr = (const struct LOG_RECORD*)&data[k]; logr = (const LOG_RECORD*)&data[k];
size = le32_to_cpu(logr->client_data_length) size = le32_to_cpu(logr->client_data_length)
+ LOG_RECORD_HEAD_SZ; + LOG_RECORD_HEAD_SZ;
actionnum++; actionnum++;
if (optv && (!optc || within_lcn_range(logr))) { if (optv && (!optc || within_lcn_range(logr))) {
printf("\n* log backward action %u at 0x%x" printf("\n* log backward action %u at 0x%x"
" size %d (next at 0x%x)\n", " size %d (next at 0x%x)\n",
actionnum, k, size, k + size); actionnum, k, size, k + size);
} }
if ((optv | optt) if ((optv | optt)
&& (!nextbuf && (j == (cnt - 1)))) { && (!nextbuf && (j == (cnt - 1)))) {
skipping to change at line 3538 skipping to change at line 3578
blk = prevblk; blk = prevblk;
skipped = 0; skipped = 0;
prevbuf = findprevious(ctx, buf); prevbuf = findprevious(ctx, buf);
if (prevbuf) { if (prevbuf) {
prevblk = prevbuf->num; prevblk = prevbuf->num;
if (prevblk < blk) if (prevblk < blk)
skipped = blk - prevblk - 1; skipped = blk - prevblk - 1;
else else
skipped = blk - prevblk - 1 skipped = blk - prevblk - 1
+ (logfilesz >> blockbits) - BASEBLKS; + (logfilesz >> blockbits) - BASEBLKS;
magic = prevbuf->block.record.head.magic; magic = prevbuf->block.record.magic;
switch (magic) { switch (magic) {
case magic_RCRD : case magic_RCRD :
break; break;
case magic_CHKD : case magic_CHKD :
printf("** Unexpected block type CHKD\n"); printf("** Unexpected block type CHKD\n");
break; break;
case magic_RSTR : case magic_RSTR :
printf("** Unexpected block type RSTR\n"); printf("** Unexpected block type RSTR\n");
break; break;
default : default :
skipping to change at line 3600 skipping to change at line 3640
return (state == T_ERR ? 1 : 0); return (state == T_ERR ? 1 : 0);
} }
static int walk(CONTEXT *ctx) static int walk(CONTEXT *ctx)
{ {
const struct BUFFER *buf; const struct BUFFER *buf;
const struct BUFFER *nextbuf; const struct BUFFER *nextbuf;
const struct BUFFER *prevbuf; const struct BUFFER *prevbuf;
const struct BUFFER *startbuf; const struct BUFFER *startbuf;
const NTFS_RECORD *record; const NTFS_RECORD *record;
const struct RECORD_PAGE_HEADER *rph; const RECORD_PAGE_HEADER *rph;
NTFS_RECORD_TYPES magic; NTFS_RECORD_TYPES magic;
u32 blk; u32 blk;
u32 nextblk; u32 nextblk;
u32 prevblk; u32 prevblk;
int err; int err;
u16 blkheadsz; u16 blkheadsz;
u16 pos; u16 pos;
BOOL dirty; BOOL dirty;
BOOL done; BOOL done;
skipping to change at line 3653 skipping to change at line 3693
switch (magic) { switch (magic) {
case magic_CHKD : case magic_CHKD :
case magic_RSTR : case magic_RSTR :
case magic_RCRD : case magic_RCRD :
break; break;
default : default :
printf("** Invalid block\n"); printf("** Invalid block\n");
err = 1; err = 1;
break; break;
} }
magic = buf->block.record.head.magic; magic = buf->block.record.magic;
switch (magic) { switch (magic) {
case magic_CHKD : case magic_CHKD :
case magic_RSTR : case magic_RSTR :
dirty = dorest(ctx, blk, &buf->block.restart, dirty = dorest(ctx, blk, &buf->block.restart,
FALSE); FALSE);
break; break;
case magic_RCRD : case magic_RCRD :
if (blk < BASEBLKS) if (blk < BASEBLKS)
pos = buf->headsz; pos = buf->headsz;
pos = dorcrd(ctx, blk, pos, buf, nextbuf); pos = dorcrd(ctx, blk, pos, buf, nextbuf);
skipping to change at line 3791 skipping to change at line 3831
if ((s64)(sle64_to_cpu(rph->last_end_lsn) if ((s64)(sle64_to_cpu(rph->last_end_lsn)
- committed_lsn) > 0) { - committed_lsn) > 0) {
committed_lsn = sle64_to_cpu(rph->last_end_lsn); committed_lsn = sle64_to_cpu(rph->last_end_lsn);
if (optv) if (optv)
printf("* Restart page was obsolete\n"); printf("* Restart page was obsolete\n");
} }
nextbuf = (const struct BUFFER*)NULL; nextbuf = (const struct BUFFER*)NULL;
prevbuf = findprevious(ctx, buf); prevbuf = findprevious(ctx, buf);
if (prevbuf) { if (prevbuf) {
prevblk = prevbuf->num; prevblk = prevbuf->num;
magic = prevbuf->block.record.head.magic; magic = prevbuf->block.record.magic;
switch (magic) { switch (magic) {
case magic_RCRD : case magic_RCRD :
break; break;
case magic_CHKD : case magic_CHKD :
printf("** Unexpected block type CHKD\n") ; printf("** Unexpected block type CHKD\n") ;
err = 1; err = 1;
break; break;
case magic_RSTR : case magic_RSTR :
err = 1; err = 1;
printf("** Unexpected block type RSTR\n") ; printf("** Unexpected block type RSTR\n") ;
skipping to change at line 3835 skipping to change at line 3875
i = 0; i = 0;
while ((i < 10) && optx[i] && (optx[i] != num)) while ((i < 10) && optx[i] && (optx[i] != num))
i++; i++;
return (optx[i] == num); return (optx[i] == num);
} }
static void version(void) static void version(void)
{ {
printf("\n%s v%s (libntfs-3g) - Recover updates committed by Windows" printf("\n%s v%s (libntfs-3g) - Recover updates committed by Windows"
" on an NTFS Volume.\n\n", "ntfsrecover", VERSION); " on an NTFS Volume.\n\n", "ntfsrecover", VERSION);
printf("Copyright (c) 2012-2015 Jean-Pierre Andre\n"); printf("Copyright (c) 2012-2016 Jean-Pierre Andre\n");
printf("\n%s\n%s%s\n", ntfs_gpl, ntfs_bugs, ntfs_home); printf("\n%s\n%s%s\n", ntfs_gpl, ntfs_bugs, ntfs_home);
} }
static void usage(void) static void usage(void)
{ {
fprintf(stderr,"Usage : for recovering the updates committed by Windows : \n"); fprintf(stderr,"Usage : for recovering the updates committed by Windows : \n");
fprintf(stderr," ntfsrecover partition\n"); fprintf(stderr," ntfsrecover partition\n");
fprintf(stderr," (e.g. ntfsrecover /dev/sda1)\n"); fprintf(stderr," (e.g. ntfsrecover /dev/sda1)\n");
fprintf(stderr,"Advanced : ntfsrecover [-b] [-c first-last] [-i] [-f] [-n ] [-p count]\n"); fprintf(stderr,"Advanced : ntfsrecover [-b] [-c first-last] [-i] [-f] [-n ] [-p count]\n");
fprintf(stderr," [-r first-last] [-t] [-u count] [-v] partition\n"); fprintf(stderr," [-r first-last] [-t] [-u count] [-v] partition\n");
fprintf(stderr," -b : show the full log backward\n"); fprintf(stderr," -b : show the full log backward\n");
fprintf(stderr," -c : restrict to the actions related to cluste r range\n"); fprintf(stderr," -c : restrict to the actions related to cluste r range\n");
fprintf(stderr," -i : show invalid (stale) records\n"); fprintf(stderr," -i : show invalid (stale) records\n");
fprintf(stderr," -f : show the full log forward\n"); fprintf(stderr," -f : show the full log forward\n");
fprintf(stderr," -h : show this help information\n"); fprintf(stderr," -h : show this help information\n");
fprintf(stderr," -k : kill fast restart data\n");
fprintf(stderr," -n : do not apply any modification\n"); fprintf(stderr," -n : do not apply any modification\n");
fprintf(stderr," -p : undo the latest count transaction sets an d play one\n"); fprintf(stderr," -p : undo the latest count transaction sets an d play one\n");
fprintf(stderr," -r : show a range of log blocks forward\n"); fprintf(stderr," -r : show a range of log blocks forward\n");
fprintf(stderr," -s : sync the committed changes (default)\n"); fprintf(stderr," -s : sync the committed changes (default)\n");
fprintf(stderr," -t : show transactions\n"); fprintf(stderr," -t : show transactions\n");
fprintf(stderr," -u : undo the latest count transaction sets\n" ); fprintf(stderr," -u : undo the latest count transaction sets\n" );
fprintf(stderr," -v : show more information (-vv yet more)\n"); fprintf(stderr," -v : show more information (-vv yet more)\n");
fprintf(stderr," -V : show version and exit\n"); fprintf(stderr," -V : show version and exit\n");
fprintf(stderr," Copyright (c) 2012-2015 Jean-Pierre Andre\n");
} }
/* /*
* Process command options * Process command options
*/ */
static BOOL getoptions(int argc, char *argv[]) static BOOL getoptions(int argc, char *argv[])
{ {
int c; int c;
int xcount; int xcount;
u32 xval; u32 xval;
char *endptr; char *endptr;
BOOL err; BOOL err;
static const char *sopt = "-bc:hifnp:r:stu:vVx:"; static const char *sopt = "-bc:hifknp:r:stu:vVx:";
static const struct option lopt[] = { static const struct option lopt[] = {
{ "backward", no_argument, NULL, 'b' }, { "backward", no_argument, NULL, 'b' },
{ "clusters", required_argument, NULL, 'c' }, { "clusters", required_argument, NULL, 'c' },
{ "forward", no_argument, NULL, 'f' }, { "forward", no_argument, NULL, 'f' },
{ "help", no_argument, NULL, 'h' }, { "help", no_argument, NULL, 'h' },
{ "kill-fast-restart", no_argument, NULL, 'k' },
{ "no-action", no_argument, NULL, 'n' }, { "no-action", no_argument, NULL, 'n' },
{ "play", required_argument, NULL, 'p' }, { "play", required_argument, NULL, 'p' },
{ "range", required_argument, NULL, 'r' }, { "range", required_argument, NULL, 'r' },
{ "sync", no_argument, NULL, 's' }, { "sync", no_argument, NULL, 's' },
{ "transactions", no_argument, NULL, 't' }, { "transactions", no_argument, NULL, 't' },
{ "undo", required_argument, NULL, 'u' }, { "undo", required_argument, NULL, 'u' },
{ "verbose", no_argument, NULL, 'v' }, { "verbose", no_argument, NULL, 'v' },
{ "version", no_argument, NULL, 'V' }, { "version", no_argument, NULL, 'V' },
{ "exceptions", required_argument, NULL, 'x' }, { "exceptions", required_argument, NULL, 'x' },
{ NULL, 0, NULL, 0 } { NULL, 0, NULL, 0 }
}; };
err = FALSE; err = FALSE;
optb = FALSE; optb = FALSE;
optc = FALSE; optc = FALSE;
optd = FALSE; optd = FALSE;
optf = FALSE; optf = FALSE;
opth = FALSE; opth = FALSE;
opti = FALSE; opti = FALSE;
optk = FALSE;
optn = FALSE; optn = FALSE;
optp = FALSE; optp = FALSE;
optr = FALSE; optr = FALSE;
opts = 0; opts = 0;
optt = FALSE; optt = FALSE;
optu = FALSE; optu = FALSE;
optv = 0; optv = 0;
optV = FALSE; optV = FALSE;
optx[0] = 0; optx[0] = 0;
skipping to change at line 3940 skipping to change at line 3982
} else } else
optc = TRUE; optc = TRUE;
break; break;
case 'f': case 'f':
optf = TRUE; optf = TRUE;
break; break;
case '?': case '?':
case 'h': case 'h':
opth = TRUE; opth = TRUE;
break; break;
case 'k':
optk = TRUE;
break;
case 'n': case 'n':
optn = TRUE; optn = TRUE;
break; break;
case 'p': case 'p':
playcount = strtoull(optarg, &endptr, 0); playcount = strtoull(optarg, &endptr, 0);
if (*endptr) { if (*endptr) {
fprintf(stderr,"Bad play count\n"); fprintf(stderr,"Bad play count\n");
err = TRUE; err = TRUE;
} else } else
optp = TRUE; optp = TRUE;
skipping to change at line 4053 skipping to change at line 4098
/* /*
* Quick checks on the layout of needed structs * Quick checks on the layout of needed structs
*/ */
static BOOL checkstructs(void) static BOOL checkstructs(void)
{ {
BOOL ok; BOOL ok;
ok = TRUE; ok = TRUE;
if (sizeof(struct RECORD_PAGE_HEADER) != 40) { if (sizeof(RECORD_PAGE_HEADER) != 40) {
fprintf(stderr, fprintf(stderr,
"* error : bad sizeof(struct RECORD_PAGE_HEADER) %d\n", "* error : bad sizeof(RECORD_PAGE_HEADER) %d\n",
(int)sizeof(struct RECORD_PAGE_HEADER)); (int)sizeof(RECORD_PAGE_HEADER));
ok = FALSE; ok = FALSE;
} }
if (sizeof(struct LOG_RECORD) != 88) { if (sizeof(LOG_RECORD) != 88) {
fprintf(stderr, fprintf(stderr,
"* error : bad sizeof(struct LOG_RECORD) %d\n", "* error : bad sizeof(LOG_RECORD) %d\n",
(int)sizeof(struct LOG_RECORD)); (int)sizeof(LOG_RECORD));
ok = FALSE; ok = FALSE;
} }
if (sizeof(struct RESTART_PAGE_HEADER) != 32) { if (sizeof(RESTART_PAGE_HEADER) != 32) {
fprintf(stderr, fprintf(stderr,
"* error : bad sizeof(struct RESTART_PAGE_HEADER) %d\n", "* error : bad sizeof(RESTART_PAGE_HEADER) %d\n",
(int)sizeof(struct RESTART_PAGE_HEADER)); (int)sizeof(RESTART_PAGE_HEADER));
ok = FALSE; ok = FALSE;
} }
if (sizeof(struct RESTART_AREA) != 44) { if (sizeof(RESTART_AREA) != 48) {
fprintf(stderr, fprintf(stderr,
"* error : bad sizeof(struct RESTART_AREA) %d\n", "* error : bad sizeof(RESTART_AREA) %d\n",
(int)sizeof(struct RESTART_AREA)); (int)sizeof(RESTART_AREA));
ok = FALSE; ok = FALSE;
} }
if (sizeof(struct ATTR_OLD) != 44) { if (sizeof(ATTR_OLD) != 44) {
fprintf(stderr, fprintf(stderr,
"* error : bad sizeof(struct ATTR_OLD) %d\n", "* error : bad sizeof(ATTR_OLD) %d\n",
(int)sizeof(struct ATTR_OLD)); (int)sizeof(ATTR_OLD));
ok = FALSE; ok = FALSE;
} }
if (sizeof(struct ATTR_NEW) != 40) { if (sizeof(ATTR_NEW) != 40) {
fprintf(stderr, fprintf(stderr,
"* error : bad sizeof(struct ATTR_NEW) %d\n", "* error : bad sizeof(ATTR_NEW) %d\n",
(int)sizeof(struct ATTR_NEW)); (int)sizeof(ATTR_NEW));
ok = FALSE; ok = FALSE;
} }
if (LastAction != 38) { if (LastAction != 38) {
fprintf(stderr, fprintf(stderr,
"* error : bad action list, %d actions\n", "* error : bad action list, %d actions\n",
(int)LastAction); (int)LastAction);
ok = FALSE; ok = FALSE;
} }
return (ok); return (ok);
} }
 End of changes. 149 change blocks. 
191 lines changed or deleted 236 lines changed or added

Home  |  About  |  All  |  Newest  |  Fossies Dox  |  Screenshots  |  Comments  |  Imprint  |  Privacy  |  HTTPS