1987da915Sopenharmony_ci/*
2987da915Sopenharmony_ci *		Process log data from an NTFS partition
3987da915Sopenharmony_ci *
4987da915Sopenharmony_ci * Copyright (c) 2012-2017 Jean-Pierre Andre
5987da915Sopenharmony_ci *
6987da915Sopenharmony_ci *	This program examines the Windows log file of an ntfs partition
7987da915Sopenharmony_ci *	and plays the committed transactions in order to restore the
8987da915Sopenharmony_ci *	integrity of metadata.
9987da915Sopenharmony_ci *
10987da915Sopenharmony_ci *	It can also display the contents of the log file in human-readable
11987da915Sopenharmony_ci *	text, either from a full partition or from the log file itself.
12987da915Sopenharmony_ci *
13987da915Sopenharmony_ci *
14987da915Sopenharmony_ci *            History
15987da915Sopenharmony_ci *
16987da915Sopenharmony_ci *  Sep 2012
17987da915Sopenharmony_ci *     - displayed textual logfile contents forward
18987da915Sopenharmony_ci *
19987da915Sopenharmony_ci *  Nov 2014
20987da915Sopenharmony_ci *     - decoded multi-page log records
21987da915Sopenharmony_ci *     - displayed textual logfile contents backward
22987da915Sopenharmony_ci *
23987da915Sopenharmony_ci *  Nov 2015
24987da915Sopenharmony_ci *     - made a general cleaning and redesigned as an ntfsprogs
25987da915Sopenharmony_ci *     - applied committed actions from logfile
26987da915Sopenharmony_ci */
27987da915Sopenharmony_ci
28987da915Sopenharmony_ci/*
29987da915Sopenharmony_ci * This program is free software; you can redistribute it and/or modify
30987da915Sopenharmony_ci * it under the terms of the GNU General Public License as published by
31987da915Sopenharmony_ci * the Free Software Foundation; either version 2 of the License, or
32987da915Sopenharmony_ci * (at your option) any later version.
33987da915Sopenharmony_ci *
34987da915Sopenharmony_ci * This program is distributed in the hope that it will be useful,
35987da915Sopenharmony_ci * but WITHOUT ANY WARRANTY; without even the implied warranty of
36987da915Sopenharmony_ci * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
37987da915Sopenharmony_ci * GNU General Public License for more details.
38987da915Sopenharmony_ci *
39987da915Sopenharmony_ci * You should have received a copy of the GNU General Public License
40987da915Sopenharmony_ci * along with this program (in the main directory of the NTFS-3G
41987da915Sopenharmony_ci * distribution in the file COPYING); if not, write to the Free Software
42987da915Sopenharmony_ci * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
43987da915Sopenharmony_ci */
44987da915Sopenharmony_ci
45987da915Sopenharmony_ci#define BASEBLKS 4 /* number of special blocks (always shown) */
46987da915Sopenharmony_ci#define BASEBLKS2 34 /* number of special blocks when version >= 2.0 */
47987da915Sopenharmony_ci#define RSTBLKS 2 /* number of restart blocks */
48987da915Sopenharmony_ci#define BUFFERCNT 64 /* number of block buffers - a power of 2 */
49987da915Sopenharmony_ci#define NTFSBLKLTH 512 /* usa block size */
50987da915Sopenharmony_ci#define SHOWATTRS 20 /* max attrs shown in a dump */
51987da915Sopenharmony_ci#define SHOWLISTS 10 /* max lcn or lsn shown in a list */
52987da915Sopenharmony_ci#define BLOCKBITS 9 /* This is only used to read the restart page */
53987da915Sopenharmony_ci#define MAXEXCEPTION 10 /* Max number of exceptions (option -x) */
54987da915Sopenharmony_ci#define MINRECSIZE 48 /* Minimal log record size */
55987da915Sopenharmony_ci#define MAXRECSIZE 65536 /* Maximal log record size (seen > 56000) */
56987da915Sopenharmony_ci
57987da915Sopenharmony_ci#include "config.h"
58987da915Sopenharmony_ci
59987da915Sopenharmony_ci#ifdef HAVE_STDLIB_H
60987da915Sopenharmony_ci#include <stdlib.h>
61987da915Sopenharmony_ci#endif
62987da915Sopenharmony_ci#ifdef HAVE_STDIO_H
63987da915Sopenharmony_ci#include <stdio.h>
64987da915Sopenharmony_ci#endif
65987da915Sopenharmony_ci#ifdef HAVE_UNISTD_H
66987da915Sopenharmony_ci#include <unistd.h>
67987da915Sopenharmony_ci#endif
68987da915Sopenharmony_ci#ifdef HAVE_FCNTL_H
69987da915Sopenharmony_ci#include <fcntl.h>
70987da915Sopenharmony_ci#endif
71987da915Sopenharmony_ci#ifdef HAVE_ERRNO_H
72987da915Sopenharmony_ci#include <errno.h>
73987da915Sopenharmony_ci#endif
74987da915Sopenharmony_ci#ifdef HAVE_STRING_H
75987da915Sopenharmony_ci#include <string.h>
76987da915Sopenharmony_ci#endif
77987da915Sopenharmony_ci#ifdef HAVE_GETOPT_H
78987da915Sopenharmony_ci#include <getopt.h>
79987da915Sopenharmony_ci#endif
80987da915Sopenharmony_ci#ifdef HAVE_MALLOC_H
81987da915Sopenharmony_ci#include <malloc.h>
82987da915Sopenharmony_ci#endif
83987da915Sopenharmony_ci#ifdef HAVE_TIME_H
84987da915Sopenharmony_ci#include <time.h>
85987da915Sopenharmony_ci#endif
86987da915Sopenharmony_ci
87987da915Sopenharmony_ci#include "types.h"
88987da915Sopenharmony_ci#include "endians.h"
89987da915Sopenharmony_ci#include "support.h"
90987da915Sopenharmony_ci#include "layout.h"
91987da915Sopenharmony_ci#include "param.h"
92987da915Sopenharmony_ci#include "ntfstime.h"
93987da915Sopenharmony_ci#include "device_io.h"
94987da915Sopenharmony_ci#include "device.h"
95987da915Sopenharmony_ci#include "logging.h"
96987da915Sopenharmony_ci#include "runlist.h"
97987da915Sopenharmony_ci#include "mft.h"
98987da915Sopenharmony_ci#include "inode.h"
99987da915Sopenharmony_ci#include "attrib.h"
100987da915Sopenharmony_ci#include "bitmap.h"
101987da915Sopenharmony_ci#include "index.h"
102987da915Sopenharmony_ci#include "volume.h"
103987da915Sopenharmony_ci#include "unistr.h"
104987da915Sopenharmony_ci#include "mst.h"
105987da915Sopenharmony_ci#include "logfile.h"
106987da915Sopenharmony_ci#include "ntfsrecover.h"
107987da915Sopenharmony_ci#include "utils.h"
108987da915Sopenharmony_ci#include "misc.h"
109987da915Sopenharmony_ci
110987da915Sopenharmony_citypedef struct {
111987da915Sopenharmony_ci	ntfs_volume *vol;
112987da915Sopenharmony_ci	FILE *file;
113987da915Sopenharmony_ci	struct ACTION_RECORD *firstaction;
114987da915Sopenharmony_ci	struct ACTION_RECORD *lastaction;
115987da915Sopenharmony_ci} CONTEXT;
116987da915Sopenharmony_ci
117987da915Sopenharmony_citypedef enum { T_OK, T_ERR, T_DONE } TRISTATE;
118987da915Sopenharmony_ci
119987da915Sopenharmony_ciRESTART_PAGE_HEADER log_header;
120987da915Sopenharmony_ciRESTART_AREA restart;
121987da915Sopenharmony_ciLOG_CLIENT_RECORD client;
122987da915Sopenharmony_ciu32 clustersz = 0;
123987da915Sopenharmony_ciint clusterbits;
124987da915Sopenharmony_ciu32 blocksz;
125987da915Sopenharmony_ciint blockbits;
126987da915Sopenharmony_ciint log_major;
127987da915Sopenharmony_ciu16 bytespersect;
128987da915Sopenharmony_ciu64 mftlcn;
129987da915Sopenharmony_ciu32 mftrecsz;
130987da915Sopenharmony_ciint mftrecbits;
131987da915Sopenharmony_ciu32 mftcnt; /* number of entries */
132987da915Sopenharmony_cintfs_inode *log_ni;
133987da915Sopenharmony_cintfs_attr *log_na;
134987da915Sopenharmony_ciu64 logfilelcn;
135987da915Sopenharmony_ciu32 logfilesz; /* bytes */
136987da915Sopenharmony_ciu64 redos_met;
137987da915Sopenharmony_ciu64 committed_lsn;
138987da915Sopenharmony_ciu64 synced_lsn;
139987da915Sopenharmony_ciu64 latest_lsn;
140987da915Sopenharmony_ciu64 restart_lsn;
141987da915Sopenharmony_ciu64 offset_mask; /* block number in an lsn */
142987da915Sopenharmony_ciunsigned long firstblk; /* first block to dump (option -r) */
143987da915Sopenharmony_ciunsigned long lastblk;  /* last block to dump (option -r) */
144987da915Sopenharmony_ciu64 firstlcn; /* first block to dump (option -c) */
145987da915Sopenharmony_ciu64 lastlcn;  /* last block to dump (option -c) */
146987da915Sopenharmony_ciBOOL optb; /* show the log backward */
147987da915Sopenharmony_ciBOOL optc; /* restrict to cluster range */
148987da915Sopenharmony_ciBOOL optd; /* device argument present*/
149987da915Sopenharmony_ciBOOL opth; /* show help */
150987da915Sopenharmony_ciBOOL opti; /* show invalid (stale) records */
151987da915Sopenharmony_ciBOOL optf; /* show full log */
152987da915Sopenharmony_ciBOOL optk; /* kill fast restart */
153987da915Sopenharmony_ciBOOL optn; /* do not apply modifications */
154987da915Sopenharmony_ciBOOL optp; /* count of transaction sets to play */
155987da915Sopenharmony_ciBOOL optr; /* show a range of blocks */
156987da915Sopenharmony_ciint opts; /* sync the file system */
157987da915Sopenharmony_ciBOOL optt; /* show transactions */
158987da915Sopenharmony_ciBOOL optu; /* count of transaction sets to undo */
159987da915Sopenharmony_ciint optv; /* verbose */
160987da915Sopenharmony_ciint optV; /* version */
161987da915Sopenharmony_ciint optx[MAXEXCEPTION + 1];
162987da915Sopenharmony_cistruct ATTR **attrtable;
163987da915Sopenharmony_ciunsigned int actionnum;
164987da915Sopenharmony_ciunsigned int attrcount;
165987da915Sopenharmony_ciunsigned int playcount;
166987da915Sopenharmony_ciunsigned int playedactions; // change the name
167987da915Sopenharmony_ciunsigned int redocount;
168987da915Sopenharmony_ciunsigned int undocount;
169987da915Sopenharmony_cistruct BUFFER *buffer_table[BASEBLKS + BUFFERCNT];
170987da915Sopenharmony_ciunsigned int redirect[BASEBLKS2];
171987da915Sopenharmony_ci
172987da915Sopenharmony_cistatic const le16 SDS[4] = {
173987da915Sopenharmony_ci	const_cpu_to_le16('$'), const_cpu_to_le16('S'),
174987da915Sopenharmony_ci	const_cpu_to_le16('D'), const_cpu_to_le16('S')
175987da915Sopenharmony_ci} ;
176987da915Sopenharmony_ci
177987da915Sopenharmony_cistatic const le16 I30[4] = {
178987da915Sopenharmony_ci	const_cpu_to_le16('$'), const_cpu_to_le16('I'),
179987da915Sopenharmony_ci	const_cpu_to_le16('3'), const_cpu_to_le16('0')
180987da915Sopenharmony_ci} ;
181987da915Sopenharmony_ci
182987da915Sopenharmony_ci/*
183987da915Sopenharmony_ci *		Byte address of a log block
184987da915Sopenharmony_ci */
185987da915Sopenharmony_ci
186987da915Sopenharmony_cistatic s64 loclogblk(CONTEXT *ctx, unsigned int blk)
187987da915Sopenharmony_ci{
188987da915Sopenharmony_ci	s64 loc;
189987da915Sopenharmony_ci	LCN lcn;
190987da915Sopenharmony_ci
191987da915Sopenharmony_ci	if (ctx->vol) {
192987da915Sopenharmony_ci		lcn = ntfs_attr_vcn_to_lcn(log_na,
193987da915Sopenharmony_ci				((s64)blk << blockbits) >> clusterbits);
194987da915Sopenharmony_ci		loc = lcn << clusterbits;
195987da915Sopenharmony_ci	} else {
196987da915Sopenharmony_ci		if (((s64)blk << blockbits) >= logfilesz)
197987da915Sopenharmony_ci			loc = -1;
198987da915Sopenharmony_ci		else
199987da915Sopenharmony_ci			loc = (logfilelcn << clusterbits)
200987da915Sopenharmony_ci				+ ((s64)blk << blockbits);
201987da915Sopenharmony_ci	}
202987da915Sopenharmony_ci	return (loc);
203987da915Sopenharmony_ci}
204987da915Sopenharmony_ci
205987da915Sopenharmony_ci/*
206987da915Sopenharmony_ci *		Deprotect a block
207987da915Sopenharmony_ci *	Only to be used for log buffers
208987da915Sopenharmony_ci *
209987da915Sopenharmony_ci *	Returns 0 if block was found correct
210987da915Sopenharmony_ci */
211987da915Sopenharmony_ci
212987da915Sopenharmony_cistatic int replaceusa(struct BUFFER *buffer, unsigned int lth)
213987da915Sopenharmony_ci{
214987da915Sopenharmony_ci	char *buf;
215987da915Sopenharmony_ci	RECORD_PAGE_HEADER *record;
216987da915Sopenharmony_ci	unsigned int j;
217987da915Sopenharmony_ci	BOOL err;
218987da915Sopenharmony_ci	unsigned int used;
219987da915Sopenharmony_ci	unsigned int xusa, nusa;
220987da915Sopenharmony_ci
221987da915Sopenharmony_ci	err = FALSE;
222987da915Sopenharmony_ci			/* Restart blocks have no protection */
223987da915Sopenharmony_ci	if (buffer->num >= RSTBLKS) {
224987da915Sopenharmony_ci			/* Do not check beyond used sectors */
225987da915Sopenharmony_ci		record = &buffer->block.record;
226987da915Sopenharmony_ci		used = blocksz;
227987da915Sopenharmony_ci		xusa = le16_to_cpu(record->usa_ofs);
228987da915Sopenharmony_ci		nusa = le16_to_cpu(record->usa_count);
229987da915Sopenharmony_ci		if (xusa && nusa
230987da915Sopenharmony_ci		   && ((xusa + 1) < lth)
231987da915Sopenharmony_ci		   && ((nusa - 1)*NTFSBLKLTH == lth)) {
232987da915Sopenharmony_ci			buf = buffer->block.data;
233987da915Sopenharmony_ci			for (j=1; (j<nusa) && ((j-1)*NTFSBLKLTH<used); j++)
234987da915Sopenharmony_ci				if ((buf[xusa] == buf[j*NTFSBLKLTH - 2])
235987da915Sopenharmony_ci				   && (buf[xusa+1] == buf[j*NTFSBLKLTH - 1])) {
236987da915Sopenharmony_ci					buf[j*NTFSBLKLTH - 2] = buf[xusa + 2*j];
237987da915Sopenharmony_ci					buf[j*NTFSBLKLTH - 1] = buf[xusa + 2*j + 1];
238987da915Sopenharmony_ci				} else {
239987da915Sopenharmony_ci					printf("* Update sequence number %d does not match\n",j);
240987da915Sopenharmony_ci					err = TRUE;
241987da915Sopenharmony_ci				}
242987da915Sopenharmony_ci		}
243987da915Sopenharmony_ci	}
244987da915Sopenharmony_ci   return (err);
245987da915Sopenharmony_ci   }
246987da915Sopenharmony_ci
247987da915Sopenharmony_ci/*
248987da915Sopenharmony_ci *		Dynamically allocate an attribute key.
249987da915Sopenharmony_ci *
250987da915Sopenharmony_ci *	As the possible values for a key depend on the version, we
251987da915Sopenharmony_ci *	cannot convert it to an index, so we make dichotomical searches
252987da915Sopenharmony_ci */
253987da915Sopenharmony_ci
254987da915Sopenharmony_cistruct ATTR *getattrentry(unsigned int key, unsigned int lth)
255987da915Sopenharmony_ci{
256987da915Sopenharmony_ci	struct ATTR *pa;
257987da915Sopenharmony_ci	struct ATTR **old;
258987da915Sopenharmony_ci	unsigned int low, mid, high;
259987da915Sopenharmony_ci
260987da915Sopenharmony_ci	low = 0;
261987da915Sopenharmony_ci	if (attrcount) {
262987da915Sopenharmony_ci		high = attrcount;
263987da915Sopenharmony_ci		while ((low + 1) < high) {
264987da915Sopenharmony_ci			mid = (low + high) >> 1;
265987da915Sopenharmony_ci			if (key < attrtable[mid]->key)
266987da915Sopenharmony_ci				high = mid;
267987da915Sopenharmony_ci			else
268987da915Sopenharmony_ci				if (key > attrtable[mid]->key)
269987da915Sopenharmony_ci					low = mid;
270987da915Sopenharmony_ci				else {
271987da915Sopenharmony_ci					low = mid;
272987da915Sopenharmony_ci					high = mid + 1;
273987da915Sopenharmony_ci				}
274987da915Sopenharmony_ci		}
275987da915Sopenharmony_ci	}
276987da915Sopenharmony_ci	if ((low < attrcount) && (attrtable[low]->key == key)) {
277987da915Sopenharmony_ci		pa = attrtable[low];
278987da915Sopenharmony_ci		if (pa->namelen < lth) {
279987da915Sopenharmony_ci			pa = (struct ATTR*)realloc(pa,
280987da915Sopenharmony_ci					sizeof(struct ATTR) + lth);
281987da915Sopenharmony_ci			attrtable[low] = pa;
282987da915Sopenharmony_ci		}
283987da915Sopenharmony_ci	} else {
284987da915Sopenharmony_ci		mid = low + 1;
285987da915Sopenharmony_ci                if (!low && attrcount && (attrtable[0]->key > key))
286987da915Sopenharmony_ci                   mid = 0;
287987da915Sopenharmony_ci		pa = (struct ATTR*)malloc(sizeof(struct ATTR) + lth);
288987da915Sopenharmony_ci		if (pa) {
289987da915Sopenharmony_ci			if (attrcount++) {
290987da915Sopenharmony_ci				old = attrtable;
291987da915Sopenharmony_ci				attrtable = (struct ATTR**)realloc(attrtable,
292987da915Sopenharmony_ci					attrcount*sizeof(struct ATTR*));
293987da915Sopenharmony_ci				if (attrtable) {
294987da915Sopenharmony_ci					high = attrcount;
295987da915Sopenharmony_ci					while (--high > mid)
296987da915Sopenharmony_ci						attrtable[high]
297987da915Sopenharmony_ci							= attrtable[high - 1];
298987da915Sopenharmony_ci					attrtable[mid] = pa;
299987da915Sopenharmony_ci				} else
300987da915Sopenharmony_ci					attrtable = old;
301987da915Sopenharmony_ci			} else {
302987da915Sopenharmony_ci				attrtable = (struct ATTR**)
303987da915Sopenharmony_ci						malloc(sizeof(struct ATTR*));
304987da915Sopenharmony_ci				attrtable[0] = pa;
305987da915Sopenharmony_ci			}
306987da915Sopenharmony_ci		pa->key = key;
307987da915Sopenharmony_ci		pa->namelen = 0;
308987da915Sopenharmony_ci		pa->type = const_cpu_to_le32(0);
309987da915Sopenharmony_ci		pa->inode = 0;
310987da915Sopenharmony_ci		}
311987da915Sopenharmony_ci	}
312987da915Sopenharmony_ci	return (pa);
313987da915Sopenharmony_ci}
314987da915Sopenharmony_ci
315987da915Sopenharmony_ci/*
316987da915Sopenharmony_ci *		Read blocks in a circular buffer
317987da915Sopenharmony_ci *
318987da915Sopenharmony_ci *	returns NULL if block cannot be read or it is found bad
319987da915Sopenharmony_ci *		otherwise returns the full unprotected block data
320987da915Sopenharmony_ci */
321987da915Sopenharmony_ci
322987da915Sopenharmony_cistatic const struct BUFFER *read_buffer(CONTEXT *ctx, unsigned int num)
323987da915Sopenharmony_ci{
324987da915Sopenharmony_ci	struct BUFFER *buffer;
325987da915Sopenharmony_ci	BOOL got;
326987da915Sopenharmony_ci	int k;
327987da915Sopenharmony_ci	unsigned int rnum;
328987da915Sopenharmony_ci
329987da915Sopenharmony_ci		/*
330987da915Sopenharmony_ci		 * The first four blocks are stored apart, to make
331987da915Sopenharmony_ci		 * sure pages 2 and 3 and the page which is logically
332987da915Sopenharmony_ci		 * before them can be accessed at the same time.
333987da915Sopenharmony_ci		 * (Only two blocks are stored apart if version >= 2.0)
334987da915Sopenharmony_ci		 * Also, block 0 is smaller because it has to be read
335987da915Sopenharmony_ci		 * before the block size is known.
336987da915Sopenharmony_ci		 * Note : the last block is supposed to have an odd
337987da915Sopenharmony_ci		 * number, and cannot be overwritten by block 4 (or 34
338987da915Sopenharmony_ci		 * if version >= 2.0) which follows logically.
339987da915Sopenharmony_ci		 */
340987da915Sopenharmony_ci	if ((num < RSTBLKS)
341987da915Sopenharmony_ci	    || ((log_major < 2) && (num < BASEBLKS)))
342987da915Sopenharmony_ci		buffer = buffer_table[num + BUFFERCNT];
343987da915Sopenharmony_ci	else
344987da915Sopenharmony_ci		buffer = buffer_table[num & (BUFFERCNT - 1)];
345987da915Sopenharmony_ci	if (buffer && (buffer->size < blocksz)) {
346987da915Sopenharmony_ci		free(buffer);
347987da915Sopenharmony_ci		buffer = (struct BUFFER*)NULL;
348987da915Sopenharmony_ci	}
349987da915Sopenharmony_ci	if (!buffer) {
350987da915Sopenharmony_ci		buffer = (struct BUFFER*)
351987da915Sopenharmony_ci			malloc(sizeof(struct BUFFER) + blocksz);
352987da915Sopenharmony_ci		buffer->size = blocksz;
353987da915Sopenharmony_ci		buffer->rnum = num + 1; /* forced to being read */
354987da915Sopenharmony_ci		buffer->safe = FALSE;
355987da915Sopenharmony_ci		if (num < BASEBLKS)
356987da915Sopenharmony_ci			buffer_table[num + BUFFERCNT] = buffer;
357987da915Sopenharmony_ci		else
358987da915Sopenharmony_ci			buffer_table[num & (BUFFERCNT - 1)] = buffer;
359987da915Sopenharmony_ci	}
360987da915Sopenharmony_ci	rnum = num;
361987da915Sopenharmony_ci	if (log_major >= 2) {
362987da915Sopenharmony_ci		for (k=RSTBLKS; k<BASEBLKS2; k++)
363987da915Sopenharmony_ci			if (redirect[k] == num)
364987da915Sopenharmony_ci				rnum = k;
365987da915Sopenharmony_ci	}
366987da915Sopenharmony_ci	if (buffer && (buffer->rnum != rnum)) {
367987da915Sopenharmony_ci		buffer->num = num;
368987da915Sopenharmony_ci		buffer->rnum = rnum;
369987da915Sopenharmony_ci		if (ctx->vol)
370987da915Sopenharmony_ci			got = (ntfs_attr_pread(log_na,(u64)rnum << blockbits,
371987da915Sopenharmony_ci                		blocksz, buffer->block.data) == blocksz);
372987da915Sopenharmony_ci		else
373987da915Sopenharmony_ci			got = !fseek(ctx->file, loclogblk(ctx, rnum), 0)
374987da915Sopenharmony_ci			    && (fread(buffer->block.data, blocksz,
375987da915Sopenharmony_ci						1, ctx->file) == 1);
376987da915Sopenharmony_ci		if (got) {
377987da915Sopenharmony_ci			char *data = buffer->block.data;
378987da915Sopenharmony_ci			buffer->headsz = sizeof(RECORD_PAGE_HEADER)
379987da915Sopenharmony_ci				+ ((2*getle16(data,6) - 1) | 7) + 1;
380987da915Sopenharmony_ci			buffer->safe = !replaceusa(buffer, blocksz);
381987da915Sopenharmony_ci		} else {
382987da915Sopenharmony_ci			buffer->safe = FALSE;
383987da915Sopenharmony_ci			fprintf(stderr,"** Could not read block %d\n", rnum);
384987da915Sopenharmony_ci		}
385987da915Sopenharmony_ci	}
386987da915Sopenharmony_ci	return (buffer && buffer->safe ? buffer : (const struct BUFFER*)NULL);
387987da915Sopenharmony_ci}
388987da915Sopenharmony_ci
389987da915Sopenharmony_civoid hexdump(const char *buf, unsigned int lth)
390987da915Sopenharmony_ci{
391987da915Sopenharmony_ci	unsigned int i,j,k;
392987da915Sopenharmony_ci
393987da915Sopenharmony_ci	for (i=0; i<lth; i+=16) {
394987da915Sopenharmony_ci		printf("%04x ",i);
395987da915Sopenharmony_ci		k = ((lth - i) < 16 ? lth : 16 + i);
396987da915Sopenharmony_ci		for (j=i; j<k; j++)
397987da915Sopenharmony_ci			printf((j & 3 ? "%02x" : " %02x"),buf[j] & 255);
398987da915Sopenharmony_ci		printf("%*c",(152 - 9*(j - i))/4,' ');
399987da915Sopenharmony_ci		for (j=i; j<k; j++)
400987da915Sopenharmony_ci			if ((buf[j] > 0x20) && (buf[j] < 0x7f))
401987da915Sopenharmony_ci				printf("%c",buf[j]);
402987da915Sopenharmony_ci			else
403987da915Sopenharmony_ci				printf(".");
404987da915Sopenharmony_ci		printf("\n");
405987da915Sopenharmony_ci	}
406987da915Sopenharmony_ci}
407987da915Sopenharmony_ci
408987da915Sopenharmony_ci/*
409987da915Sopenharmony_ci *	       Display a date
410987da915Sopenharmony_ci */
411987da915Sopenharmony_ci
412987da915Sopenharmony_cistatic void showdate(const char *text, le64 lestamp)
413987da915Sopenharmony_ci{
414987da915Sopenharmony_ci	time_t utime;
415987da915Sopenharmony_ci	struct tm *ptm;
416987da915Sopenharmony_ci	s64 stamp;
417987da915Sopenharmony_ci	const char *months[]
418987da915Sopenharmony_ci		= { "Jan", "Feb", "Mar", "Apr", "May", "Jun",
419987da915Sopenharmony_ci		    "Jul", "Aug", "Sep", "Oct", "Nov", "Dec" } ;
420987da915Sopenharmony_ci
421987da915Sopenharmony_ci	stamp = le64_to_cpu(lestamp);
422987da915Sopenharmony_ci	if ((stamp < ((2147000000 + 134774*86400LL)*10000000LL))
423987da915Sopenharmony_ci	    && (stamp > ((-2147000000 + 134774*86400LL)*10000000LL))) {
424987da915Sopenharmony_ci				/* date within traditional Unix limits */
425987da915Sopenharmony_ci		utime = stamp/10000000 - 134774*86400LL;
426987da915Sopenharmony_ci		ptm = gmtime(&utime);
427987da915Sopenharmony_ci		printf("%s %02d %3s %4d %2d:%02d:%02d UTC\n",
428987da915Sopenharmony_ci			text,
429987da915Sopenharmony_ci			ptm->tm_mday,months[ptm->tm_mon],ptm->tm_year+1900,
430987da915Sopenharmony_ci			ptm->tm_hour,ptm->tm_min,ptm->tm_sec);
431987da915Sopenharmony_ci	} else {
432987da915Sopenharmony_ci		u32 days;
433987da915Sopenharmony_ci		unsigned int year;
434987da915Sopenharmony_ci		int mon;
435987da915Sopenharmony_ci		int cnt;
436987da915Sopenharmony_ci
437987da915Sopenharmony_ci		days = stamp/(86400*10000000LL);
438987da915Sopenharmony_ci		year = 1601;
439987da915Sopenharmony_ci					/* periods of 400 years */
440987da915Sopenharmony_ci		cnt = days/146097;
441987da915Sopenharmony_ci		days -= 146097*cnt;
442987da915Sopenharmony_ci		year += 400*cnt;
443987da915Sopenharmony_ci					/* periods of 100 years */
444987da915Sopenharmony_ci		cnt = (3*days + 3)/109573;
445987da915Sopenharmony_ci		days -= 36524*cnt;
446987da915Sopenharmony_ci		year += 100*cnt;
447987da915Sopenharmony_ci					/* periods of 4 years */
448987da915Sopenharmony_ci		cnt = days/1461;
449987da915Sopenharmony_ci		days -= 1461L*cnt;
450987da915Sopenharmony_ci		year += 4*cnt;
451987da915Sopenharmony_ci					/* periods of a single year */
452987da915Sopenharmony_ci		cnt = (3*days + 3)/1096;
453987da915Sopenharmony_ci		days -= 365*cnt;
454987da915Sopenharmony_ci		year += cnt;
455987da915Sopenharmony_ci
456987da915Sopenharmony_ci		if ((!(year % 100) ? (year % 400) : (year % 4))
457987da915Sopenharmony_ci		    && (days > 58)) days++;
458987da915Sopenharmony_ci		if (days > 59) {
459987da915Sopenharmony_ci			mon = (5*days + 161)/153;
460987da915Sopenharmony_ci			days -= (153*mon - 162)/5;
461987da915Sopenharmony_ci		} else {
462987da915Sopenharmony_ci			mon = days/31 + 1;
463987da915Sopenharmony_ci			days -= 31*(mon - 1) - 1;
464987da915Sopenharmony_ci		}
465987da915Sopenharmony_ciif (mon > 12)
466987da915Sopenharmony_ci{
467987da915Sopenharmony_ciprintf("** Bad day stamp %lld days %lu mon %d year %u\n",
468987da915Sopenharmony_ci(long long)stamp,(unsigned long)days,mon,year);
469987da915Sopenharmony_ci}
470987da915Sopenharmony_ci		printf("%s %02u %3s %4u\n",text,
471987da915Sopenharmony_ci			(unsigned int)days,months[mon-1],(unsigned int)year);
472987da915Sopenharmony_ci	}
473987da915Sopenharmony_ci}
474987da915Sopenharmony_ci
475987da915Sopenharmony_civoid showname(const char *prefix, const char *name, int cnt)
476987da915Sopenharmony_ci{
477987da915Sopenharmony_ci	const le16 *n;
478987da915Sopenharmony_ci	int i;
479987da915Sopenharmony_ci	int c;
480987da915Sopenharmony_ci
481987da915Sopenharmony_ci	printf("%s",prefix);
482987da915Sopenharmony_ci	n = (const le16*)name;
483987da915Sopenharmony_ci	for (i=0; (i<cnt) && n[i]; i++) {
484987da915Sopenharmony_ci		c = le16_to_cpu(n[i]);
485987da915Sopenharmony_ci		if (c < 0x20)
486987da915Sopenharmony_ci			printf(".");
487987da915Sopenharmony_ci		else
488987da915Sopenharmony_ci			if (c < 0x80)
489987da915Sopenharmony_ci				printf("%c",c);
490987da915Sopenharmony_ci			else
491987da915Sopenharmony_ci				if (c < 0x800)
492987da915Sopenharmony_ci					printf("%c%c",
493987da915Sopenharmony_ci						(c >> 6) + 0xc0,
494987da915Sopenharmony_ci						(c & 63) + 0x80);
495987da915Sopenharmony_ci				else
496987da915Sopenharmony_ci					printf("%c%c%c",
497987da915Sopenharmony_ci						(c >> 12) + 0xe0,
498987da915Sopenharmony_ci						((c >> 6) & 63) + 0x80,
499987da915Sopenharmony_ci						(c & 63) + 0x80);
500987da915Sopenharmony_ci	}
501987da915Sopenharmony_ci	printf("\n");
502987da915Sopenharmony_ci}
503987da915Sopenharmony_ci
504987da915Sopenharmony_cistatic const char *commitment(u64 lsn)
505987da915Sopenharmony_ci{
506987da915Sopenharmony_ci	const char *commit;
507987da915Sopenharmony_ci	s64 diff;
508987da915Sopenharmony_ci
509987da915Sopenharmony_ci	/* Computations assume lsn could wraparound, they probably never do */
510987da915Sopenharmony_ci	diff = lsn - synced_lsn;
511987da915Sopenharmony_ci	if (diff <= 0)
512987da915Sopenharmony_ci		commit = "synced";
513987da915Sopenharmony_ci	else {
514987da915Sopenharmony_ci		diff = lsn - committed_lsn;
515987da915Sopenharmony_ci		if (diff <= 0)
516987da915Sopenharmony_ci			commit = "committed";
517987da915Sopenharmony_ci		else {
518987da915Sopenharmony_ci			/* may find lsn from older session */
519987da915Sopenharmony_ci			diff = lsn - latest_lsn;
520987da915Sopenharmony_ci			if (diff <= 0)
521987da915Sopenharmony_ci				commit = "*uncommitted*";
522987da915Sopenharmony_ci			else
523987da915Sopenharmony_ci				commit = "*stale*";
524987da915Sopenharmony_ci		}
525987da915Sopenharmony_ci	}
526987da915Sopenharmony_ci	return (commit);
527987da915Sopenharmony_ci}
528987da915Sopenharmony_ci
529987da915Sopenharmony_ciconst char *actionname(int op)
530987da915Sopenharmony_ci{
531987da915Sopenharmony_ci	static char buffer[24];
532987da915Sopenharmony_ci	const char *p;
533987da915Sopenharmony_ci
534987da915Sopenharmony_ci	switch (op) {
535987da915Sopenharmony_ci	case Noop :
536987da915Sopenharmony_ci		p = "Noop";
537987da915Sopenharmony_ci		break;
538987da915Sopenharmony_ci	case CompensationlogRecord :
539987da915Sopenharmony_ci		p = "CompensationlogRecord";
540987da915Sopenharmony_ci		break;
541987da915Sopenharmony_ci	case InitializeFileRecordSegment :
542987da915Sopenharmony_ci		p = "InitializeFileRecordSegment";
543987da915Sopenharmony_ci		break;
544987da915Sopenharmony_ci	case DeallocateFileRecordSegment :
545987da915Sopenharmony_ci		p = "DeallocateFileRecordSegment";
546987da915Sopenharmony_ci		break;
547987da915Sopenharmony_ci	case WriteEndofFileRecordSegment :
548987da915Sopenharmony_ci		p = "WriteEndofFileRecordSegment";
549987da915Sopenharmony_ci		break;
550987da915Sopenharmony_ci	case CreateAttribute :
551987da915Sopenharmony_ci		p = "CreateAttribute";
552987da915Sopenharmony_ci		break;
553987da915Sopenharmony_ci	case DeleteAttribute :
554987da915Sopenharmony_ci		p = "DeleteAttribute";
555987da915Sopenharmony_ci		break;
556987da915Sopenharmony_ci	case UpdateResidentValue :
557987da915Sopenharmony_ci		p = "UpdateResidentValue";
558987da915Sopenharmony_ci		break;
559987da915Sopenharmony_ci	case UpdateNonResidentValue :
560987da915Sopenharmony_ci		p = "UpdateNonResidentValue";
561987da915Sopenharmony_ci		break;
562987da915Sopenharmony_ci	case UpdateMappingPairs :
563987da915Sopenharmony_ci		p = "UpdateMappingPairs";
564987da915Sopenharmony_ci		break;
565987da915Sopenharmony_ci	case DeleteDirtyClusters :
566987da915Sopenharmony_ci		p = "DeleteDirtyClusters";
567987da915Sopenharmony_ci		break;
568987da915Sopenharmony_ci	case SetNewAttributeSizes :
569987da915Sopenharmony_ci		p = "SetNewAttributeSizes";
570987da915Sopenharmony_ci		break;
571987da915Sopenharmony_ci	case AddIndexEntryRoot :
572987da915Sopenharmony_ci		p = "AddIndexEntryRoot";
573987da915Sopenharmony_ci		break;
574987da915Sopenharmony_ci	case DeleteIndexEntryRoot :
575987da915Sopenharmony_ci		p = "DeleteIndexEntryRoot";
576987da915Sopenharmony_ci		break;
577987da915Sopenharmony_ci	case AddIndexEntryAllocation :
578987da915Sopenharmony_ci		p = "AddIndexEntryAllocation";
579987da915Sopenharmony_ci		break;
580987da915Sopenharmony_ci	case DeleteIndexEntryAllocation :
581987da915Sopenharmony_ci		p = "DeleteIndexEntryAllocation";
582987da915Sopenharmony_ci		break;
583987da915Sopenharmony_ci	case WriteEndOfIndexBuffer :
584987da915Sopenharmony_ci		p = "WriteEndOfIndexBuffer";
585987da915Sopenharmony_ci		break;
586987da915Sopenharmony_ci	case SetIndexEntryVcnRoot :
587987da915Sopenharmony_ci		p = "SetIndexEntryVcnRoot";
588987da915Sopenharmony_ci		break;
589987da915Sopenharmony_ci	case SetIndexEntryVcnAllocation :
590987da915Sopenharmony_ci		p = "SetIndexEntryVcnAllocation";
591987da915Sopenharmony_ci		break;
592987da915Sopenharmony_ci	case UpdateFileNameRoot :
593987da915Sopenharmony_ci		p = "UpdateFileNameRoot";
594987da915Sopenharmony_ci		break;
595987da915Sopenharmony_ci	case UpdateFileNameAllocation :
596987da915Sopenharmony_ci		p = "UpdateFileNameAllocation";
597987da915Sopenharmony_ci		break;
598987da915Sopenharmony_ci	case SetBitsInNonResidentBitMap :
599987da915Sopenharmony_ci		p = "SetBitsInNonResidentBitMap";
600987da915Sopenharmony_ci		break;
601987da915Sopenharmony_ci	case ClearBitsInNonResidentBitMap :
602987da915Sopenharmony_ci		p = "ClearBitsInNonResidentBitMap";
603987da915Sopenharmony_ci		break;
604987da915Sopenharmony_ci	case HotFix :
605987da915Sopenharmony_ci		p = "HotFix";
606987da915Sopenharmony_ci		break;
607987da915Sopenharmony_ci	case EndTopLevelAction :
608987da915Sopenharmony_ci		p = "EndTopLevelAction";
609987da915Sopenharmony_ci		break;
610987da915Sopenharmony_ci	case PrepareTransaction :
611987da915Sopenharmony_ci		p = "PrepareTransaction";
612987da915Sopenharmony_ci		break;
613987da915Sopenharmony_ci	case CommitTransaction :
614987da915Sopenharmony_ci		p = "CommitTransaction";
615987da915Sopenharmony_ci		break;
616987da915Sopenharmony_ci	case ForgetTransaction :
617987da915Sopenharmony_ci		p = "ForgetTransaction";
618987da915Sopenharmony_ci		break;
619987da915Sopenharmony_ci	case OpenNonResidentAttribute :
620987da915Sopenharmony_ci		p = "OpenNonResidentAttribute";
621987da915Sopenharmony_ci		break;
622987da915Sopenharmony_ci	case OpenAttributeTableDump :
623987da915Sopenharmony_ci		p = "OpenAttributeTableDump";
624987da915Sopenharmony_ci		break;
625987da915Sopenharmony_ci	case AttributeNamesDump :
626987da915Sopenharmony_ci		p = "AttributeNamesDump";
627987da915Sopenharmony_ci		break;
628987da915Sopenharmony_ci	case DirtyPageTableDump :
629987da915Sopenharmony_ci		p = "DirtyPageTableDump";
630987da915Sopenharmony_ci		break;
631987da915Sopenharmony_ci	case TransactionTableDump :
632987da915Sopenharmony_ci		p = "TransactionTableDump";
633987da915Sopenharmony_ci		break;
634987da915Sopenharmony_ci	case UpdateRecordDataRoot :
635987da915Sopenharmony_ci		p = "UpdateRecordDataRoot";
636987da915Sopenharmony_ci		break;
637987da915Sopenharmony_ci	case UpdateRecordDataAllocation :
638987da915Sopenharmony_ci		p = "UpdateRecordDataAllocation";
639987da915Sopenharmony_ci		break;
640987da915Sopenharmony_ci	case Win10Action35 :
641987da915Sopenharmony_ci		p = "Win10Action35";
642987da915Sopenharmony_ci		break;
643987da915Sopenharmony_ci	case Win10Action36 :
644987da915Sopenharmony_ci		p = "Win10Action36";
645987da915Sopenharmony_ci		break;
646987da915Sopenharmony_ci	case Win10Action37 :
647987da915Sopenharmony_ci		p = "Win10Action37";
648987da915Sopenharmony_ci		break;
649987da915Sopenharmony_ci	default  :
650987da915Sopenharmony_ci		sprintf(buffer,"*Unknown-Action-%d*",op);
651987da915Sopenharmony_ci		p = buffer;
652987da915Sopenharmony_ci		break;
653987da915Sopenharmony_ci	}
654987da915Sopenharmony_ci	return (p);
655987da915Sopenharmony_ci}
656987da915Sopenharmony_ci
657987da915Sopenharmony_cistatic const char *attrname(unsigned int key)
658987da915Sopenharmony_ci{
659987da915Sopenharmony_ci	static char name[256];
660987da915Sopenharmony_ci	const char *p;
661987da915Sopenharmony_ci	struct ATTR *pa;
662987da915Sopenharmony_ci	unsigned int i;
663987da915Sopenharmony_ci
664987da915Sopenharmony_ci	if ((key <= 65535) && !(key & 3)) {
665987da915Sopenharmony_ci		pa = getattrentry(key,0);
666987da915Sopenharmony_ci		if (pa) {
667987da915Sopenharmony_ci			if (!pa->namelen)
668987da915Sopenharmony_ci				p = "Unnamed";
669987da915Sopenharmony_ci			else {
670987da915Sopenharmony_ci				p = name;
671987da915Sopenharmony_ci					/* Assume ascii for now */
672987da915Sopenharmony_ci				for (i=0; 2*i<pa->namelen; i++)
673987da915Sopenharmony_ci					name[i] = le16_to_cpu(pa->name[i]);
674987da915Sopenharmony_ci				name[i] = 0;
675987da915Sopenharmony_ci			}
676987da915Sopenharmony_ci		} else
677987da915Sopenharmony_ci			p = "Undefined";
678987da915Sopenharmony_ci	} else
679987da915Sopenharmony_ci		p = "Invalid";
680987da915Sopenharmony_ci	return (p);
681987da915Sopenharmony_ci}
682987da915Sopenharmony_ci
683987da915Sopenharmony_ciint fixnamelen(const char *name, int len)
684987da915Sopenharmony_ci{
685987da915Sopenharmony_ci	int i;
686987da915Sopenharmony_ci
687987da915Sopenharmony_ci	i = 0;
688987da915Sopenharmony_ci	while ((i < len) && (name[i] || name[i + 1]))
689987da915Sopenharmony_ci		i += 2;
690987da915Sopenharmony_ci	return (i);
691987da915Sopenharmony_ci}
692987da915Sopenharmony_ci
693987da915Sopenharmony_ciconst char *mftattrname(ATTR_TYPES attr)
694987da915Sopenharmony_ci{
695987da915Sopenharmony_ci	static char badattr[24];
696987da915Sopenharmony_ci	const char *p;
697987da915Sopenharmony_ci
698987da915Sopenharmony_ci	switch (attr) {
699987da915Sopenharmony_ci	case AT_STANDARD_INFORMATION :
700987da915Sopenharmony_ci		p = "Standard-Information";
701987da915Sopenharmony_ci		break;
702987da915Sopenharmony_ci	case AT_ATTRIBUTE_LIST :
703987da915Sopenharmony_ci		p = "Attribute-List";
704987da915Sopenharmony_ci		break;
705987da915Sopenharmony_ci	case AT_FILE_NAME :
706987da915Sopenharmony_ci		p = "Name";
707987da915Sopenharmony_ci		break;
708987da915Sopenharmony_ci	case AT_OBJECT_ID :
709987da915Sopenharmony_ci		p = "Volume-Version";
710987da915Sopenharmony_ci		break;
711987da915Sopenharmony_ci	case AT_SECURITY_DESCRIPTOR :
712987da915Sopenharmony_ci		p = "Security-Descriptor";
713987da915Sopenharmony_ci		break;
714987da915Sopenharmony_ci	case AT_VOLUME_NAME :
715987da915Sopenharmony_ci		p = "Volume-Name";
716987da915Sopenharmony_ci		break;
717987da915Sopenharmony_ci	case AT_VOLUME_INFORMATION :
718987da915Sopenharmony_ci		p = "Volume-Information";
719987da915Sopenharmony_ci		break;
720987da915Sopenharmony_ci	case AT_DATA :
721987da915Sopenharmony_ci		p = "Data";
722987da915Sopenharmony_ci		break;
723987da915Sopenharmony_ci	case AT_INDEX_ROOT :
724987da915Sopenharmony_ci		p = "Index-Root";
725987da915Sopenharmony_ci		break;
726987da915Sopenharmony_ci	case AT_INDEX_ALLOCATION :
727987da915Sopenharmony_ci		p = "Index-Allocation";
728987da915Sopenharmony_ci		break;
729987da915Sopenharmony_ci	case AT_BITMAP :
730987da915Sopenharmony_ci		p = "Bitmap";
731987da915Sopenharmony_ci		break;
732987da915Sopenharmony_ci	case AT_REPARSE_POINT :
733987da915Sopenharmony_ci		p = "Reparse-Point";
734987da915Sopenharmony_ci		break;
735987da915Sopenharmony_ci	case AT_EA_INFORMATION :
736987da915Sopenharmony_ci		p = "EA-Information";
737987da915Sopenharmony_ci		break;
738987da915Sopenharmony_ci	case AT_EA :
739987da915Sopenharmony_ci		p = "EA";
740987da915Sopenharmony_ci		break;
741987da915Sopenharmony_ci	case AT_PROPERTY_SET :
742987da915Sopenharmony_ci		p = "Property-Set";
743987da915Sopenharmony_ci		break;
744987da915Sopenharmony_ci	case AT_LOGGED_UTILITY_STREAM :
745987da915Sopenharmony_ci		p = "Logged-Utility-Stream";
746987da915Sopenharmony_ci		break;
747987da915Sopenharmony_ci	case AT_END :
748987da915Sopenharmony_ci		p = "End";
749987da915Sopenharmony_ci		break;
750987da915Sopenharmony_ci	default :
751987da915Sopenharmony_ci		sprintf(badattr,"*0x%x-Unknown*",attr);
752987da915Sopenharmony_ci		p = badattr;
753987da915Sopenharmony_ci		break;
754987da915Sopenharmony_ci	}
755987da915Sopenharmony_ci	return (p);
756987da915Sopenharmony_ci}
757987da915Sopenharmony_ci
758987da915Sopenharmony_cistatic void showattribute(const char *prefix, const struct ATTR *pa)
759987da915Sopenharmony_ci{
760987da915Sopenharmony_ci	if (pa) {
761987da915Sopenharmony_ci		if (pa->type) {
762987da915Sopenharmony_ci			printf("%sattr 0x%x : inode %lld type %s",
763987da915Sopenharmony_ci				prefix, pa->key, (long long)pa->inode,
764987da915Sopenharmony_ci				mftattrname(pa->type));
765987da915Sopenharmony_ci			if (pa->namelen)
766987da915Sopenharmony_ci				showname(" name ",(const char*)pa->name,
767987da915Sopenharmony_ci					pa->namelen/2);
768987da915Sopenharmony_ci			else
769987da915Sopenharmony_ci				printf("\n");
770987da915Sopenharmony_ci		} else {
771987da915Sopenharmony_ci			if (pa->namelen) {
772987da915Sopenharmony_ci				printf("%sattr 0x%x : type Unknown",
773987da915Sopenharmony_ci						prefix, pa->key);
774987da915Sopenharmony_ci				showname(" name ",(const char*)pa->name,
775987da915Sopenharmony_ci						pa->namelen/2);
776987da915Sopenharmony_ci			} else
777987da915Sopenharmony_ci				printf("%s(definition of attr 0x%x not met)\n",
778987da915Sopenharmony_ci						prefix, pa->key);
779987da915Sopenharmony_ci		}
780987da915Sopenharmony_ci	}
781987da915Sopenharmony_ci}
782987da915Sopenharmony_ci
783987da915Sopenharmony_ci/*
784987da915Sopenharmony_ci *		Determine if an action acts on the MFT
785987da915Sopenharmony_ci */
786987da915Sopenharmony_ci
787987da915Sopenharmony_cistatic BOOL acts_on_mft(int op)
788987da915Sopenharmony_ci{
789987da915Sopenharmony_ci	BOOL onmft;
790987da915Sopenharmony_ci
791987da915Sopenharmony_ci			/* A few actions may have to be added to the list */
792987da915Sopenharmony_ci	switch (op) {
793987da915Sopenharmony_ci	case InitializeFileRecordSegment :
794987da915Sopenharmony_ci	case DeallocateFileRecordSegment :
795987da915Sopenharmony_ci	case CreateAttribute :
796987da915Sopenharmony_ci	case DeleteAttribute :
797987da915Sopenharmony_ci	case UpdateResidentValue :
798987da915Sopenharmony_ci	case UpdateMappingPairs :
799987da915Sopenharmony_ci	case SetNewAttributeSizes :
800987da915Sopenharmony_ci	case AddIndexEntryRoot :
801987da915Sopenharmony_ci	case DeleteIndexEntryRoot :
802987da915Sopenharmony_ci	case UpdateFileNameRoot :
803987da915Sopenharmony_ci	case WriteEndofFileRecordSegment :
804987da915Sopenharmony_ci	case Win10Action37 :
805987da915Sopenharmony_ci		onmft = TRUE;
806987da915Sopenharmony_ci		break;
807987da915Sopenharmony_ci	default :
808987da915Sopenharmony_ci		onmft = FALSE;
809987da915Sopenharmony_ci		break;
810987da915Sopenharmony_ci	}
811987da915Sopenharmony_ci	return (onmft);
812987da915Sopenharmony_ci}
813987da915Sopenharmony_ci
814987da915Sopenharmony_ciu32 get_undo_offset(const LOG_RECORD *logr)
815987da915Sopenharmony_ci{
816987da915Sopenharmony_ci	u32 offset;
817987da915Sopenharmony_ci
818987da915Sopenharmony_ci	if (logr->lcns_to_follow)
819987da915Sopenharmony_ci		offset = 0x30 + le16_to_cpu(logr->undo_offset);
820987da915Sopenharmony_ci	else
821987da915Sopenharmony_ci		offset = 0x28 + le16_to_cpu(logr->undo_offset);
822987da915Sopenharmony_ci	return (offset);
823987da915Sopenharmony_ci}
824987da915Sopenharmony_ci
825987da915Sopenharmony_ciu32 get_redo_offset(const LOG_RECORD *logr)
826987da915Sopenharmony_ci{
827987da915Sopenharmony_ci	u32 offset;
828987da915Sopenharmony_ci
829987da915Sopenharmony_ci	if (logr->lcns_to_follow)
830987da915Sopenharmony_ci		offset = 0x30 + le16_to_cpu(logr->redo_offset);
831987da915Sopenharmony_ci	else
832987da915Sopenharmony_ci		offset = 0x28 + le16_to_cpu(logr->redo_offset);
833987da915Sopenharmony_ci	return (offset);
834987da915Sopenharmony_ci}
835987da915Sopenharmony_ci
836987da915Sopenharmony_ciu32 get_extra_offset(const LOG_RECORD *logr)
837987da915Sopenharmony_ci{
838987da915Sopenharmony_ci	u32 uoffset;
839987da915Sopenharmony_ci	u32 roffset;
840987da915Sopenharmony_ci
841987da915Sopenharmony_ci	roffset = get_redo_offset(logr)
842987da915Sopenharmony_ci				+ le16_to_cpu(logr->redo_length);
843987da915Sopenharmony_ci	uoffset = get_undo_offset(logr)
844987da915Sopenharmony_ci				+ le16_to_cpu(logr->undo_length);
845987da915Sopenharmony_ci	return ((((uoffset > roffset ? uoffset : roffset) - 1) | 7) + 1);
846987da915Sopenharmony_ci}
847987da915Sopenharmony_ci
848987da915Sopenharmony_cistatic BOOL likelyop(const LOG_RECORD *logr)
849987da915Sopenharmony_ci{
850987da915Sopenharmony_ci	BOOL likely;
851987da915Sopenharmony_ci
852987da915Sopenharmony_ci	switch (logr->record_type) {
853987da915Sopenharmony_ci	case LOG_STANDARD : /* standard record */
854987da915Sopenharmony_ci	     /* Operations in range 0..LastAction-1, can be both null */
855987da915Sopenharmony_ci		likely = ((unsigned int)le16_to_cpu(logr->redo_operation)
856987da915Sopenharmony_ci						< LastAction)
857987da915Sopenharmony_ci		    && ((unsigned int)le16_to_cpu(logr->undo_operation)
858987da915Sopenharmony_ci						< LastAction)
859987da915Sopenharmony_ci	     /* Offsets aligned to 8 bytes */
860987da915Sopenharmony_ci		    && !(le16_to_cpu(logr->redo_offset) & 7)
861987da915Sopenharmony_ci		    && !(le16_to_cpu(logr->undo_offset) & 7)
862987da915Sopenharmony_ci	     /* transaction id must not be null */
863987da915Sopenharmony_ci		    && logr->transaction_id
864987da915Sopenharmony_ci	     /* client data length aligned to 8 bytes */
865987da915Sopenharmony_ci		    && !(le32_to_cpu(logr->client_data_length) & 7)
866987da915Sopenharmony_ci	     /* client data length less than 64K (131K ?) */
867987da915Sopenharmony_ci		    && (le32_to_cpu(logr->client_data_length) < MAXRECSIZE)
868987da915Sopenharmony_ci	     /* if there is redo data, offset must be >= 0x28 */
869987da915Sopenharmony_ci		    && (!le16_to_cpu(logr->redo_length)
870987da915Sopenharmony_ci		       || ((unsigned int)le16_to_cpu(logr->redo_offset) >= 0x28))
871987da915Sopenharmony_ci	     /* if there is undo data, offset must be >= 0x28 */
872987da915Sopenharmony_ci		    && (!le16_to_cpu(logr->undo_length)
873987da915Sopenharmony_ci		       || ((unsigned int)le16_to_cpu(logr->undo_offset) >= 0x28));
874987da915Sopenharmony_ci	     /* undo data and redo data should be contiguous when both present */
875987da915Sopenharmony_ci		if (likely && logr->redo_length && logr->undo_length) {
876987da915Sopenharmony_ci	     /* undo and redo data may be the same when both present and same size */
877987da915Sopenharmony_ci			if (logr->undo_offset == logr->redo_offset) {
878987da915Sopenharmony_ci				if (logr->redo_length != logr->undo_length)
879987da915Sopenharmony_ci					likely = FALSE;
880987da915Sopenharmony_ci			} else {
881987da915Sopenharmony_ci				if (le16_to_cpu(logr->redo_offset)
882987da915Sopenharmony_ci					< le16_to_cpu(logr->undo_offset)) {
883987da915Sopenharmony_ci			/* undo expected just after redo */
884987da915Sopenharmony_ci					if ((((le16_to_cpu(logr->redo_offset)
885987da915Sopenharmony_ci					    + le16_to_cpu(logr->redo_length)
886987da915Sopenharmony_ci					    - 1) | 7) + 1)
887987da915Sopenharmony_ci					    != le16_to_cpu(logr->undo_offset))
888987da915Sopenharmony_ci						likely = FALSE;
889987da915Sopenharmony_ci				} else {
890987da915Sopenharmony_ci			/* redo expected just after undo */
891987da915Sopenharmony_ci					if ((((le16_to_cpu(logr->undo_offset)
892987da915Sopenharmony_ci					    + le16_to_cpu(logr->undo_length)
893987da915Sopenharmony_ci					    - 1) | 7) + 1)
894987da915Sopenharmony_ci					    != le16_to_cpu(logr->redo_offset))
895987da915Sopenharmony_ci						likely = FALSE;
896987da915Sopenharmony_ci				}
897987da915Sopenharmony_ci			}
898987da915Sopenharmony_ci		}
899987da915Sopenharmony_ci		break;
900987da915Sopenharmony_ci	case LOG_CHECKPOINT : /* check-point */
901987da915Sopenharmony_ci	     /*
902987da915Sopenharmony_ci	      * undo and redo operations are null
903987da915Sopenharmony_ci	      * or CompensationlogRecord with no data
904987da915Sopenharmony_ci	      */
905987da915Sopenharmony_ci		likely = (!logr->redo_operation
906987da915Sopenharmony_ci			|| ((logr->redo_operation == const_cpu_to_le16(1))
907987da915Sopenharmony_ci			    && !logr->redo_length))
908987da915Sopenharmony_ci		    && (!logr->undo_operation
909987da915Sopenharmony_ci			|| ((logr->undo_operation == const_cpu_to_le16(1))
910987da915Sopenharmony_ci			    && !logr->undo_length))
911987da915Sopenharmony_ci	     /* transaction id must be null */
912987da915Sopenharmony_ci		    && !logr->transaction_id
913987da915Sopenharmony_ci	     /* client_data_length is 0x68 or 0x70 (Vista and subsequent) */
914987da915Sopenharmony_ci		    && ((le32_to_cpu(logr->client_data_length) == 0x68)
915987da915Sopenharmony_ci			|| (le32_to_cpu(logr->client_data_length) == 0x70));
916987da915Sopenharmony_ci		break;
917987da915Sopenharmony_ci	default :
918987da915Sopenharmony_ci		likely = FALSE;
919987da915Sopenharmony_ci		break;
920987da915Sopenharmony_ci	}
921987da915Sopenharmony_ci	return (likely);
922987da915Sopenharmony_ci}
923987da915Sopenharmony_ci
924987da915Sopenharmony_ci/*
925987da915Sopenharmony_ci *		Search for a likely record in a block
926987da915Sopenharmony_ci *
927987da915Sopenharmony_ci *	Must not be used when syncing.
928987da915Sopenharmony_ci *
929987da915Sopenharmony_ci *	Returns 0 when not found
930987da915Sopenharmony_ci */
931987da915Sopenharmony_ci
932987da915Sopenharmony_cistatic u16 searchlikely(const struct BUFFER *buf)
933987da915Sopenharmony_ci{
934987da915Sopenharmony_ci	const LOG_RECORD *logr;
935987da915Sopenharmony_ci	const char *data;
936987da915Sopenharmony_ci	u16 k;
937987da915Sopenharmony_ci
938987da915Sopenharmony_ci	if (opts)
939987da915Sopenharmony_ci		printf("** Error : searchlikely() used for syncing\n");
940987da915Sopenharmony_ci        data = buf->block.data;
941987da915Sopenharmony_ci   	k = buf->headsz;
942987da915Sopenharmony_ci	logr = (const LOG_RECORD*)&data[k];
943987da915Sopenharmony_ci	if (!likelyop(logr)) {
944987da915Sopenharmony_ci		do {
945987da915Sopenharmony_ci			k += 8;
946987da915Sopenharmony_ci			logr = (const LOG_RECORD*)&data[k];
947987da915Sopenharmony_ci		} while ((k <= (blocksz - LOG_RECORD_HEAD_SZ))
948987da915Sopenharmony_ci		    && !likelyop(logr));
949987da915Sopenharmony_ci		if (k > (blocksz - LOG_RECORD_HEAD_SZ))
950987da915Sopenharmony_ci			k = 0;
951987da915Sopenharmony_ci	}
952987da915Sopenharmony_ci	return (k);
953987da915Sopenharmony_ci}
954987da915Sopenharmony_ci
955987da915Sopenharmony_ci/*
956987da915Sopenharmony_ci *	From a previous block, determine the location of first record
957987da915Sopenharmony_ci *
958987da915Sopenharmony_ci *	The previous block must have the beginning of an overlapping
959987da915Sopenharmony_ci *	record, and the current block must have the beginning of next
960987da915Sopenharmony_ci *	record (which can overlap on next blocks).
961987da915Sopenharmony_ci *	The argument "skipped" is the number of blocks in-between.
962987da915Sopenharmony_ci *
963987da915Sopenharmony_ci *	Note : the overlapping record from previous block does not reach
964987da915Sopenharmony_ci *	the current block when it ends near the end of the last skipped block.
965987da915Sopenharmony_ci *
966987da915Sopenharmony_ci *	Returns 0 if some bad condition is found
967987da915Sopenharmony_ci *	Returns near blocksz when there is no beginning of record in
968987da915Sopenharmony_ci *		the current block
969987da915Sopenharmony_ci */
970987da915Sopenharmony_ci
971987da915Sopenharmony_cistatic u16 firstrecord(int skipped, const struct BUFFER *buf,
972987da915Sopenharmony_ci		   const struct BUFFER *prevbuf)
973987da915Sopenharmony_ci{
974987da915Sopenharmony_ci	const RECORD_PAGE_HEADER *rph;
975987da915Sopenharmony_ci	const RECORD_PAGE_HEADER *prevrph;
976987da915Sopenharmony_ci	const LOG_RECORD *logr;
977987da915Sopenharmony_ci	const char *data;
978987da915Sopenharmony_ci	const char *prevdata;
979987da915Sopenharmony_ci	u16 k;
980987da915Sopenharmony_ci	u16 blkheadsz;
981987da915Sopenharmony_ci	s32 size;
982987da915Sopenharmony_ci
983987da915Sopenharmony_ci	rph = &buf->block.record;
984987da915Sopenharmony_ci	data = buf->block.data;
985987da915Sopenharmony_ci	if (prevbuf) {
986987da915Sopenharmony_ci		prevrph = &prevbuf->block.record;
987987da915Sopenharmony_ci		prevdata = prevbuf->block.data;
988987da915Sopenharmony_ci		blkheadsz = prevbuf->headsz;
989987da915Sopenharmony_ci		/* From previous page, determine where the current one starts */
990987da915Sopenharmony_ci		k = le16_to_cpu(prevrph->next_record_offset);
991987da915Sopenharmony_ci		/* a null value means there is no full record in next block */
992987da915Sopenharmony_ci		if (!k)
993987da915Sopenharmony_ci			k = blkheadsz;
994987da915Sopenharmony_ci	} else
995987da915Sopenharmony_ci		k = 0;
996987da915Sopenharmony_ci		/* Minimal size is apparently 48 : offset of redo_operation */
997987da915Sopenharmony_ci	if (k && ((blocksz - k) >= LOG_RECORD_HEAD_SZ)) {
998987da915Sopenharmony_ci		logr = (const LOG_RECORD*)&prevdata[k];
999987da915Sopenharmony_ci		if (!logr->client_data_length) {
1000987da915Sopenharmony_ci			/*
1001987da915Sopenharmony_ci			 * Sometimes the end of record is free space.
1002987da915Sopenharmony_ci			 * This apparently means reaching the end of
1003987da915Sopenharmony_ci			 * a previous session, and must be considered
1004987da915Sopenharmony_ci			 * as an error.
1005987da915Sopenharmony_ci			 * We however tolerate this, unless syncing
1006987da915Sopenharmony_ci			 * is requested.
1007987da915Sopenharmony_ci			 */
1008987da915Sopenharmony_ci			printf("* Reaching free space at end of block %d\n",
1009987da915Sopenharmony_ci					(int)prevbuf->num);
1010987da915Sopenharmony_ci			/* As a consequence, there cannot be skipped blocks */
1011987da915Sopenharmony_ci			if (skipped) {
1012987da915Sopenharmony_ci				printf("*** Inconsistency : blocks skipped after free space\n");
1013987da915Sopenharmony_ci				k = 0; /* error returned */
1014987da915Sopenharmony_ci			}
1015987da915Sopenharmony_ci			if (opts)
1016987da915Sopenharmony_ci				k = 0;
1017987da915Sopenharmony_ci			else {
1018987da915Sopenharmony_ci				k = searchlikely(buf);
1019987da915Sopenharmony_ci				printf("* Skipping over free space\n");
1020987da915Sopenharmony_ci			}
1021987da915Sopenharmony_ci		} else {
1022987da915Sopenharmony_ci			size = le32_to_cpu(logr->client_data_length)
1023987da915Sopenharmony_ci					+ LOG_RECORD_HEAD_SZ;
1024987da915Sopenharmony_ci			if ((size < MINRECSIZE) || (size > MAXRECSIZE)) {
1025987da915Sopenharmony_ci				printf("** Bad record size %ld in block %ld"
1026987da915Sopenharmony_ci					" offset 0x%x\n",
1027987da915Sopenharmony_ci					(long)size,(long)prevbuf->num,(int)k);
1028987da915Sopenharmony_ci				k = blkheadsz;
1029987da915Sopenharmony_ci			} else {
1030987da915Sopenharmony_ci				if ((int)(blocksz - k) >= size)
1031987da915Sopenharmony_ci					printf("*** Inconsistency : the final"
1032987da915Sopenharmony_ci						" record does not overlap\n");
1033987da915Sopenharmony_ci				k += size - (blocksz - blkheadsz)*(skipped + 1);
1034987da915Sopenharmony_ci			}
1035987da915Sopenharmony_ci			if ((k <= blkheadsz)
1036987da915Sopenharmony_ci			    && (k > (blkheadsz - LOG_RECORD_HEAD_SZ))) {
1037987da915Sopenharmony_ci			/* There were not enough space in the last skipped block */
1038987da915Sopenharmony_ci				k = blkheadsz;
1039987da915Sopenharmony_ci			} else {
1040987da915Sopenharmony_ci				if (optv
1041987da915Sopenharmony_ci				    && ((blocksz - k) < LOG_RECORD_HEAD_SZ)) {
1042987da915Sopenharmony_ci					/* Not an error : just no space */
1043987da915Sopenharmony_ci					printf("No minimal record space\n");
1044987da915Sopenharmony_ci				}
1045987da915Sopenharmony_ci				if (optv >= 2)
1046987da915Sopenharmony_ci					printf("Overlapping record from block %d,"
1047987da915Sopenharmony_ci						" starting at offset 0x%x\n",
1048987da915Sopenharmony_ci						(int)prevbuf->num,(int)k);
1049987da915Sopenharmony_ci			}
1050987da915Sopenharmony_ci		}
1051987da915Sopenharmony_ci	} else {
1052987da915Sopenharmony_ci		k = buf->headsz;
1053987da915Sopenharmony_ci		if (optv >= 2) {
1054987da915Sopenharmony_ci			if (prevbuf)
1055987da915Sopenharmony_ci				printf("No minimal record from block %d,"
1056987da915Sopenharmony_ci					" starting at offset 0x%x\n",
1057987da915Sopenharmony_ci					(int)prevbuf->num, (int)k);
1058987da915Sopenharmony_ci			else
1059987da915Sopenharmony_ci				printf("No block before %d,"
1060987da915Sopenharmony_ci					" starting at offset 0x%x\n",
1061987da915Sopenharmony_ci					(int)buf->num, (int)k);
1062987da915Sopenharmony_ci		}
1063987da915Sopenharmony_ci	}
1064987da915Sopenharmony_ci		/*
1065987da915Sopenharmony_ci		 * In a wraparound situation, there is frequently no
1066987da915Sopenharmony_ci		 * match... because there were no wraparound.
1067987da915Sopenharmony_ci		 * Return an error if syncing is requested, otherwise
1068987da915Sopenharmony_ci		 * try to find a starting record.
1069987da915Sopenharmony_ci		 */
1070987da915Sopenharmony_ci	if (k && prevbuf && (prevbuf->num > buf->num)) {
1071987da915Sopenharmony_ci		logr = (const LOG_RECORD*)&data[k];
1072987da915Sopenharmony_ci			/* Accept reaching the end with no record beginning */
1073987da915Sopenharmony_ci		if ((k != le16_to_cpu(rph->next_record_offset))
1074987da915Sopenharmony_ci		    && !likelyop(logr)) {
1075987da915Sopenharmony_ci			if (opts) {
1076987da915Sopenharmony_ci				k = 0;
1077987da915Sopenharmony_ci				printf("** Could not wraparound\n");
1078987da915Sopenharmony_ci			} else {
1079987da915Sopenharmony_ci				k = searchlikely(buf);
1080987da915Sopenharmony_ci				printf("* Skipping over bad wraparound\n");
1081987da915Sopenharmony_ci			}
1082987da915Sopenharmony_ci		}
1083987da915Sopenharmony_ci	}
1084987da915Sopenharmony_ci	return (k);
1085987da915Sopenharmony_ci}
1086987da915Sopenharmony_ci
1087987da915Sopenharmony_ci/*
1088987da915Sopenharmony_ci *		Find the block which defines the first record in current one
1089987da915Sopenharmony_ci *
1090987da915Sopenharmony_ci *	Either the wanted block has the beginning of a record overlapping
1091987da915Sopenharmony_ci *	on current one, or it ends in such as there is no space for an
1092987da915Sopenharmony_ci *	overlapping one.
1093987da915Sopenharmony_ci *
1094987da915Sopenharmony_ci *	Returns 0 if the previous block cannot be determined.
1095987da915Sopenharmony_ci */
1096987da915Sopenharmony_ci
1097987da915Sopenharmony_cistatic const struct BUFFER *findprevious(CONTEXT *ctx, const struct BUFFER *buf)
1098987da915Sopenharmony_ci{
1099987da915Sopenharmony_ci	const struct BUFFER *prevbuf;
1100987da915Sopenharmony_ci	const struct BUFFER *savebuf;
1101987da915Sopenharmony_ci	const RECORD_PAGE_HEADER *rph;
1102987da915Sopenharmony_ci	int skipped;
1103987da915Sopenharmony_ci	int prevblk;
1104987da915Sopenharmony_ci	BOOL prevmiddle;
1105987da915Sopenharmony_ci	BOOL error;
1106987da915Sopenharmony_ci	u16 endoff;
1107987da915Sopenharmony_ci
1108987da915Sopenharmony_ci	error = FALSE;
1109987da915Sopenharmony_ci	prevblk = buf->num;
1110987da915Sopenharmony_ci	savebuf = (struct BUFFER*)NULL;
1111987da915Sopenharmony_ci	skipped = 0;
1112987da915Sopenharmony_ci	do {
1113987da915Sopenharmony_ci		prevmiddle = FALSE;
1114987da915Sopenharmony_ci		if (prevblk > (log_major < 2 ? BASEBLKS : BASEBLKS2))
1115987da915Sopenharmony_ci			prevblk--;
1116987da915Sopenharmony_ci		else
1117987da915Sopenharmony_ci			if (prevblk == (log_major < 2 ? BASEBLKS : BASEBLKS2))
1118987da915Sopenharmony_ci				prevblk = (logfilesz >> blockbits) - 1;
1119987da915Sopenharmony_ci			else {
1120987da915Sopenharmony_ci				rph = &buf->block.record;
1121987da915Sopenharmony_ci				if (log_major < 2)
1122987da915Sopenharmony_ci					prevblk = (sle64_to_cpu(
1123987da915Sopenharmony_ci						rph->copy.file_offset)
1124987da915Sopenharmony_ci							>> blockbits) - 1;
1125987da915Sopenharmony_ci				else
1126987da915Sopenharmony_ci					prevblk = (sle64_to_cpu(
1127987da915Sopenharmony_ci						rph->copy.last_lsn)
1128987da915Sopenharmony_ci						    & offset_mask)
1129987da915Sopenharmony_ci							>> (blockbits - 3);
1130987da915Sopenharmony_ci				/*
1131987da915Sopenharmony_ci				 * If an initial block leads to block 4, it
1132987da915Sopenharmony_ci				 * can mean the last block or no previous
1133987da915Sopenharmony_ci				 * block at all. Using the last block is safer,
1134987da915Sopenharmony_ci				 * its lsn will indicate whether it is stale.
1135987da915Sopenharmony_ci				 */
1136987da915Sopenharmony_ci				if (prevblk
1137987da915Sopenharmony_ci				    < (log_major < 2 ? BASEBLKS : BASEBLKS2))
1138987da915Sopenharmony_ci					prevblk = (logfilesz >> blockbits) - 1;
1139987da915Sopenharmony_ci			}
1140987da915Sopenharmony_ci		/* No previous block if the log only consists of block 2 or 3 */
1141987da915Sopenharmony_ci		if (prevblk < BASEBLKS) {
1142987da915Sopenharmony_ci			prevbuf = (struct BUFFER*)NULL;
1143987da915Sopenharmony_ci			error = TRUE; /* not a real error */
1144987da915Sopenharmony_ci		} else {
1145987da915Sopenharmony_ci			prevbuf = read_buffer(ctx, prevblk);
1146987da915Sopenharmony_ci			if (prevbuf) {
1147987da915Sopenharmony_ci				rph = &prevbuf->block.record;
1148987da915Sopenharmony_ci				prevmiddle = !(rph->flags
1149987da915Sopenharmony_ci						& const_cpu_to_le32(1))
1150987da915Sopenharmony_ci					|| !rph->next_record_offset;
1151987da915Sopenharmony_ci				if (prevmiddle) {
1152987da915Sopenharmony_ci					savebuf = prevbuf;
1153987da915Sopenharmony_ci					skipped++;
1154987da915Sopenharmony_ci				}
1155987da915Sopenharmony_ci			} else {
1156987da915Sopenharmony_ci				error = TRUE;
1157987da915Sopenharmony_ci				printf("** Could not read block %d\n",
1158987da915Sopenharmony_ci								(int)prevblk);
1159987da915Sopenharmony_ci			}
1160987da915Sopenharmony_ci		}
1161987da915Sopenharmony_ci	} while (prevmiddle && !error);
1162987da915Sopenharmony_ci
1163987da915Sopenharmony_ci	if (!prevmiddle && !error && skipped) {
1164987da915Sopenharmony_ci	 /* No luck if there is not enough space in this record */
1165987da915Sopenharmony_ci		rph = &prevbuf->block.record;
1166987da915Sopenharmony_ci		endoff = le16_to_cpu(rph->next_record_offset);
1167987da915Sopenharmony_ci		if (endoff > (blocksz - LOG_RECORD_HEAD_SZ)) {
1168987da915Sopenharmony_ci			prevbuf = savebuf;
1169987da915Sopenharmony_ci		}
1170987da915Sopenharmony_ci	}
1171987da915Sopenharmony_ci	return (error ? (struct BUFFER*)NULL : prevbuf);
1172987da915Sopenharmony_ci}
1173987da915Sopenharmony_ci
1174987da915Sopenharmony_civoid copy_attribute(struct ATTR *pa, const char *buf, int length)
1175987da915Sopenharmony_ci{
1176987da915Sopenharmony_ci	const ATTR_NEW *panew;
1177987da915Sopenharmony_ci	ATTR_OLD old_aligned;
1178987da915Sopenharmony_ci
1179987da915Sopenharmony_ci	if (pa) {
1180987da915Sopenharmony_ci		switch (length) {
1181987da915Sopenharmony_ci		case sizeof(ATTR_NEW) :
1182987da915Sopenharmony_ci			panew = (const ATTR_NEW*)buf;
1183987da915Sopenharmony_ci			pa->type = panew->type;
1184987da915Sopenharmony_ci			pa->lsn = sle64_to_cpu(panew->lsn);
1185987da915Sopenharmony_ci			pa->inode = MREF(le64_to_cpu(panew->inode));
1186987da915Sopenharmony_ci			break;
1187987da915Sopenharmony_ci		case sizeof(ATTR_OLD) :
1188987da915Sopenharmony_ci				/* Badly aligned, first realign */
1189987da915Sopenharmony_ci			memcpy(&old_aligned,buf,sizeof(old_aligned));
1190987da915Sopenharmony_ci			pa->type = old_aligned.type;
1191987da915Sopenharmony_ci			pa->lsn = sle64_to_cpu(old_aligned.lsn);
1192987da915Sopenharmony_ci			pa->inode = MREF(le64_to_cpu(old_aligned.inode));
1193987da915Sopenharmony_ci			break;
1194987da915Sopenharmony_ci		default :
1195987da915Sopenharmony_ci			printf("** Unexpected attribute format, length %d\n",
1196987da915Sopenharmony_ci					length);
1197987da915Sopenharmony_ci		}
1198987da915Sopenharmony_ci	}
1199987da915Sopenharmony_ci}
1200987da915Sopenharmony_ci
1201987da915Sopenharmony_cistatic int refresh_attributes(const struct ACTION_RECORD *firstaction)
1202987da915Sopenharmony_ci{
1203987da915Sopenharmony_ci	const struct ACTION_RECORD *action;
1204987da915Sopenharmony_ci	const LOG_RECORD *logr;
1205987da915Sopenharmony_ci	struct ATTR *pa;
1206987da915Sopenharmony_ci	const char *buf;
1207987da915Sopenharmony_ci	u32 extra;
1208987da915Sopenharmony_ci	u32 length;
1209987da915Sopenharmony_ci	u32 len;
1210987da915Sopenharmony_ci	u32 key;
1211987da915Sopenharmony_ci	u32 x;
1212987da915Sopenharmony_ci	u32 i;
1213987da915Sopenharmony_ci	u32 step;
1214987da915Sopenharmony_ci	u32 used;
1215987da915Sopenharmony_ci
1216987da915Sopenharmony_ci	for (action=firstaction; action; action=action->next) {
1217987da915Sopenharmony_ci		logr = &action->record;
1218987da915Sopenharmony_ci		buf = ((const char*)logr) + get_redo_offset(logr);
1219987da915Sopenharmony_ci		length = le16_to_cpu(logr->redo_length);
1220987da915Sopenharmony_ci		switch (le16_to_cpu(action->record.redo_operation)) {
1221987da915Sopenharmony_ci		case OpenNonResidentAttribute :
1222987da915Sopenharmony_ci			extra = get_extra_offset(logr)
1223987da915Sopenharmony_ci						- get_redo_offset(logr);
1224987da915Sopenharmony_ci			if (logr->undo_length) {
1225987da915Sopenharmony_ci				len = le32_to_cpu(logr->client_data_length)
1226987da915Sopenharmony_ci					+ LOG_RECORD_HEAD_SZ
1227987da915Sopenharmony_ci					- get_extra_offset(logr);
1228987da915Sopenharmony_ci				/* this gives a length aligned modulo 8 */
1229987da915Sopenharmony_ci				len = fixnamelen(&buf[extra], len);
1230987da915Sopenharmony_ci			} else
1231987da915Sopenharmony_ci				len = 0;
1232987da915Sopenharmony_ci			pa = getattrentry(le16_to_cpu(logr->target_attribute),
1233987da915Sopenharmony_ci						len);
1234987da915Sopenharmony_ci			if (pa) {
1235987da915Sopenharmony_ci				copy_attribute(pa, buf, length);
1236987da915Sopenharmony_ci				pa->namelen = len;
1237987da915Sopenharmony_ci				if (len) {
1238987da915Sopenharmony_ci					memcpy(pa->name,&buf[extra],len);
1239987da915Sopenharmony_ci				}
1240987da915Sopenharmony_ci			}
1241987da915Sopenharmony_ci			break;
1242987da915Sopenharmony_ci		case OpenAttributeTableDump :
1243987da915Sopenharmony_ci			i = 24;
1244987da915Sopenharmony_ci			step = getle16(buf, 8);
1245987da915Sopenharmony_ci			used = getle16(buf, 12);
1246987da915Sopenharmony_ci	    		/*
1247987da915Sopenharmony_ci			 * Changed from Win10, formerly we got step = 44.
1248987da915Sopenharmony_ci			 * The record layout has also changed
1249987da915Sopenharmony_ci			 */
1250987da915Sopenharmony_ci			for (x=0; (x<used) && (i<length); i+=step, x++) {
1251987da915Sopenharmony_ci				pa = getattrentry(i,0);
1252987da915Sopenharmony_ci				if (pa) {
1253987da915Sopenharmony_ci					copy_attribute(pa, buf + i, step);
1254987da915Sopenharmony_ci				}
1255987da915Sopenharmony_ci			}
1256987da915Sopenharmony_ci			break;
1257987da915Sopenharmony_ci		case AttributeNamesDump :
1258987da915Sopenharmony_ci			i = 8;
1259987da915Sopenharmony_ci			if (i < length) {
1260987da915Sopenharmony_ci				x = 0;
1261987da915Sopenharmony_ci				do {
1262987da915Sopenharmony_ci					len = getle16(buf, i + 2);
1263987da915Sopenharmony_ci					key = getle16(buf, i);
1264987da915Sopenharmony_ci					if (len > 510) {
1265987da915Sopenharmony_ci						printf("** Error : bad"
1266987da915Sopenharmony_ci							" attribute name"
1267987da915Sopenharmony_ci							" length %d\n",
1268987da915Sopenharmony_ci							len);
1269987da915Sopenharmony_ci						key = 0;
1270987da915Sopenharmony_ci					}
1271987da915Sopenharmony_ci					if (key) { /* Apparently, may have to stop before reaching the end */
1272987da915Sopenharmony_ci						pa = getattrentry(key,len);
1273987da915Sopenharmony_ci						if (pa) {
1274987da915Sopenharmony_ci							pa->namelen = len;
1275987da915Sopenharmony_ci							memcpy(pa->name,
1276987da915Sopenharmony_ci								&buf[i+4],len);
1277987da915Sopenharmony_ci						}
1278987da915Sopenharmony_ci						i += len + 6;
1279987da915Sopenharmony_ci						x++;
1280987da915Sopenharmony_ci					}
1281987da915Sopenharmony_ci				} while (key && (i < length));
1282987da915Sopenharmony_ci			}
1283987da915Sopenharmony_ci			break;
1284987da915Sopenharmony_ci		default :
1285987da915Sopenharmony_ci			break;
1286987da915Sopenharmony_ci		}
1287987da915Sopenharmony_ci	}
1288987da915Sopenharmony_ci	return (0);
1289987da915Sopenharmony_ci}
1290987da915Sopenharmony_ci
1291987da915Sopenharmony_ci/*
1292987da915Sopenharmony_ci *              Display a fixup
1293987da915Sopenharmony_ci */
1294987da915Sopenharmony_ci
1295987da915Sopenharmony_cistatic void fixup(CONTEXT *ctx, const LOG_RECORD *logr, const char *buf,
1296987da915Sopenharmony_ci				BOOL redo)
1297987da915Sopenharmony_ci{
1298987da915Sopenharmony_ci	struct ATTR *pa;
1299987da915Sopenharmony_ci	int action;
1300987da915Sopenharmony_ci	int attr;
1301987da915Sopenharmony_ci	int offs;
1302987da915Sopenharmony_ci	s32 length;
1303987da915Sopenharmony_ci	int extra;
1304987da915Sopenharmony_ci	s32 i;
1305987da915Sopenharmony_ci	int p;
1306987da915Sopenharmony_ci	s32 base;
1307987da915Sopenharmony_ci	u16 firstpos; /* position of first mft attribute */
1308987da915Sopenharmony_ci	le32 v;
1309987da915Sopenharmony_ci	ATTR_TYPES mftattr;
1310987da915Sopenharmony_ci	le64 w;
1311987da915Sopenharmony_ci	le64 inode;
1312987da915Sopenharmony_ci	le64 size;
1313987da915Sopenharmony_ci	int lth;
1314987da915Sopenharmony_ci	int len;
1315987da915Sopenharmony_ci
1316987da915Sopenharmony_ci	attr = le16_to_cpu(logr->target_attribute);
1317987da915Sopenharmony_ci	offs = le16_to_cpu(logr->attribute_offset);
1318987da915Sopenharmony_ci	if (redo) {
1319987da915Sopenharmony_ci		action = le16_to_cpu(logr->redo_operation);
1320987da915Sopenharmony_ci		length = le16_to_cpu(logr->redo_length);
1321987da915Sopenharmony_ci	} else {
1322987da915Sopenharmony_ci		action = le16_to_cpu(logr->undo_operation);
1323987da915Sopenharmony_ci		length = le16_to_cpu(logr->undo_length);
1324987da915Sopenharmony_ci	}
1325987da915Sopenharmony_ci	if (redo)
1326987da915Sopenharmony_ci		printf("redo fixup %dR %s attr 0x%x offs 0x%x\n",
1327987da915Sopenharmony_ci			actionnum, actionname(action), attr, offs);
1328987da915Sopenharmony_ci	else
1329987da915Sopenharmony_ci		printf("undo fixup %dU %s attr 0x%x offs 0x%x\n",
1330987da915Sopenharmony_ci			actionnum, actionname(action), attr, offs);
1331987da915Sopenharmony_ci	switch (action) {
1332987da915Sopenharmony_ci	case InitializeFileRecordSegment : /* 2 */
1333987da915Sopenharmony_ci			/*
1334987da915Sopenharmony_ci			 * When this is a redo (with a NoOp undo), the
1335987da915Sopenharmony_ci			 *   full MFT record is logged.
1336987da915Sopenharmony_ci			 * When this is an undo (with DeallocateFileRecordSegment redo),
1337987da915Sopenharmony_ci			 *   only the header of the MFT record is logged.
1338987da915Sopenharmony_ci			 */
1339987da915Sopenharmony_ci		if (!ctx->vol && !mftrecsz && (length > 8)) {
1340987da915Sopenharmony_ci			/* mftrecsz can be determined from usa_count */
1341987da915Sopenharmony_ci			mftrecsz = (getle16(buf,6) - 1)*512;
1342987da915Sopenharmony_ci			mftrecbits = 1;
1343987da915Sopenharmony_ci			while ((u32)(1 << mftrecbits) < mftrecsz)
1344987da915Sopenharmony_ci				mftrecbits++;
1345987da915Sopenharmony_ci		}
1346987da915Sopenharmony_ci		printf("   new base MFT record, attr 0x%x (%s)\n",attr,attrname(attr));
1347987da915Sopenharmony_ci		printf("   inode      %lld\n",
1348987da915Sopenharmony_ci				(((long long)sle64_to_cpu(logr->target_vcn)
1349987da915Sopenharmony_ci					<< clusterbits)
1350987da915Sopenharmony_ci				+ (le16_to_cpu(logr->cluster_index) << 9))
1351987da915Sopenharmony_ci					>> mftrecbits);
1352987da915Sopenharmony_ci		if (length >= 18)
1353987da915Sopenharmony_ci			printf("   seq number 0x%04x\n",(int)getle16(buf, 16));
1354987da915Sopenharmony_ci		if (length >= 20)
1355987da915Sopenharmony_ci			printf("   link count %d\n",(int)getle16(buf, 18));
1356987da915Sopenharmony_ci		if (length >= 24) {
1357987da915Sopenharmony_ci			u16 flags;
1358987da915Sopenharmony_ci
1359987da915Sopenharmony_ci			flags = getle16(buf, 22);
1360987da915Sopenharmony_ci			printf("   flags      0x%x",(int)flags);
1361987da915Sopenharmony_ci			switch (flags & 3) {
1362987da915Sopenharmony_ci			case 1 :
1363987da915Sopenharmony_ci				printf(" (file in use)\n");
1364987da915Sopenharmony_ci				break;
1365987da915Sopenharmony_ci			case 3 :
1366987da915Sopenharmony_ci				printf(" (directory in use)\n");
1367987da915Sopenharmony_ci				break;
1368987da915Sopenharmony_ci			default :
1369987da915Sopenharmony_ci				printf(" (not in use)\n");
1370987da915Sopenharmony_ci				break;
1371987da915Sopenharmony_ci			}
1372987da915Sopenharmony_ci		}
1373987da915Sopenharmony_ci		base = getle16(buf, 4) + ((getle16(buf, 6)*2 - 1) | 7) + 1;
1374987da915Sopenharmony_ci		while (base < length) {
1375987da915Sopenharmony_ci			mftattr = feedle32(buf, base);
1376987da915Sopenharmony_ci			printf("   attrib 0x%lx (%s) at offset 0x%x\n",
1377987da915Sopenharmony_ci				(long)le32_to_cpu(mftattr),
1378987da915Sopenharmony_ci				mftattrname(mftattr), (int)base);
1379987da915Sopenharmony_ci		if (mftattr == AT_FILE_NAME) {
1380987da915Sopenharmony_ci			showname("      name ",&buf[base + 90],
1381987da915Sopenharmony_ci					buf[base + 88] & 255);
1382987da915Sopenharmony_ci			inode = feedle64(buf, base + 24);
1383987da915Sopenharmony_ci			printf("      parent dir inode %lld\n",
1384987da915Sopenharmony_ci					(long long)MREF(le64_to_cpu(inode)));
1385987da915Sopenharmony_ci		}
1386987da915Sopenharmony_ci		lth =  getle32(buf, base + 4);
1387987da915Sopenharmony_ci		if ((lth <= 0) || (lth & 7))
1388987da915Sopenharmony_ci			base = length;
1389987da915Sopenharmony_ci		else
1390987da915Sopenharmony_ci			base += lth;
1391987da915Sopenharmony_ci		}
1392987da915Sopenharmony_ci		break;
1393987da915Sopenharmony_ci	case DeallocateFileRecordSegment : /* 3 */
1394987da915Sopenharmony_ci		printf("   free base MFT record, attr 0x%x (%s)\n",
1395987da915Sopenharmony_ci				attr,attrname(attr));
1396987da915Sopenharmony_ci		printf("   inode %lld\n",
1397987da915Sopenharmony_ci		    (((long long)sle64_to_cpu(logr->target_vcn) << clusterbits)
1398987da915Sopenharmony_ci		    + (le16_to_cpu(logr->cluster_index) << 9)) >> mftrecbits);
1399987da915Sopenharmony_ci		break;
1400987da915Sopenharmony_ci	case CreateAttribute : /* 5 */
1401987da915Sopenharmony_ci		pa = getattrentry(attr,0);
1402987da915Sopenharmony_ci		base = 24;
1403987da915Sopenharmony_ci		/* Assume the beginning of the attribute is always present */
1404987da915Sopenharmony_ci		switch (getle32(buf,0)) {
1405987da915Sopenharmony_ci		case 0x30 :
1406987da915Sopenharmony_ci			printf("   create file name, attr 0x%x\n",attr);
1407987da915Sopenharmony_ci			if (pa)
1408987da915Sopenharmony_ci				showattribute("      ",pa);
1409987da915Sopenharmony_ci			showname("   file ",
1410987da915Sopenharmony_ci				&buf[base + 66],buf[base + 64] & 255);
1411987da915Sopenharmony_ci			if (base >= -8)
1412987da915Sopenharmony_ci				showdate("   created  ",feedle64(buf,base + 8));
1413987da915Sopenharmony_ci			if (base >= -16)
1414987da915Sopenharmony_ci				showdate("   modified ",feedle64(buf,base + 16));
1415987da915Sopenharmony_ci			if (base >= -24)
1416987da915Sopenharmony_ci				showdate("   changed  ",feedle64(buf,base + 24));
1417987da915Sopenharmony_ci			if (base >= -32)
1418987da915Sopenharmony_ci				showdate("   read     ",feedle64(buf,base + 32));
1419987da915Sopenharmony_ci			size = feedle64(buf,base + 40);
1420987da915Sopenharmony_ci			printf("   allocated size %lld\n",
1421987da915Sopenharmony_ci					(long long)le64_to_cpu(size));
1422987da915Sopenharmony_ci			size = feedle64(buf,base + 48);
1423987da915Sopenharmony_ci			printf("   real size %lld\n",
1424987da915Sopenharmony_ci					(long long)le64_to_cpu(size));
1425987da915Sopenharmony_ci			v = feedle32(buf,base + 56);
1426987da915Sopenharmony_ci			printf("   DOS flags 0x%lx\n",
1427987da915Sopenharmony_ci					(long)le32_to_cpu(v));
1428987da915Sopenharmony_ci			break;
1429987da915Sopenharmony_ci		case 0x80 :
1430987da915Sopenharmony_ci			printf("   create a data stream, attr 0x%x\n",attr);
1431987da915Sopenharmony_ci			break;
1432987da915Sopenharmony_ci		case 0xc0 :
1433987da915Sopenharmony_ci			printf("   create reparse data\n");
1434987da915Sopenharmony_ci			if (pa)
1435987da915Sopenharmony_ci				showattribute("      ",pa);
1436987da915Sopenharmony_ci			printf("   tag 0x%lx\n",(long)getle32(buf, base));
1437987da915Sopenharmony_ci			showname("   print name ",
1438987da915Sopenharmony_ci				&buf[base + 20 + getle16(buf, base + 12)],
1439987da915Sopenharmony_ci				getle16(buf, base + 14)/2);
1440987da915Sopenharmony_ci			break;
1441987da915Sopenharmony_ci		}
1442987da915Sopenharmony_ci		break;
1443987da915Sopenharmony_ci      case UpdateResidentValue : /* 7 */
1444987da915Sopenharmony_ci		/*
1445987da915Sopenharmony_ci		 * The record offset designates the mft attribute offset,
1446987da915Sopenharmony_ci		 * offs and length define a right-justified window in this
1447987da915Sopenharmony_ci		 * attribute.
1448987da915Sopenharmony_ci		 * At this stage, we do not know which kind of mft
1449987da915Sopenharmony_ci		 * attribute this is about, we assume this is standard
1450987da915Sopenharmony_ci		 * information when it is the first attribute in the
1451987da915Sopenharmony_ci		 * record.
1452987da915Sopenharmony_ci		 */
1453987da915Sopenharmony_ci	 	base = 0x18 - offs; /* p 8 */
1454987da915Sopenharmony_ci		pa = getattrentry(attr,0);
1455987da915Sopenharmony_ci		firstpos = 0x30 + (((mftrecsz/512 + 1)*2 - 1 ) | 7) + 1;
1456987da915Sopenharmony_ci		if (pa
1457987da915Sopenharmony_ci		   && !pa->inode
1458987da915Sopenharmony_ci		   && (pa->type == const_cpu_to_le32(0x80))
1459987da915Sopenharmony_ci		   && !(offs & 3)
1460987da915Sopenharmony_ci		   && (le16_to_cpu(logr->record_offset) == firstpos)) {
1461987da915Sopenharmony_ci			printf("   set standard information, attr 0x%x\n",attr);
1462987da915Sopenharmony_ci			showattribute("      ",pa);
1463987da915Sopenharmony_ci	    		if ((base >= 0) && ((base + 8) <= length))
1464987da915Sopenharmony_ci	       			showdate("   created  ",
1465987da915Sopenharmony_ci						feedle64(buf,base));
1466987da915Sopenharmony_ci	    		if (((base + 8) >= 0) && ((base + 16) <= length))
1467987da915Sopenharmony_ci	       			showdate("   modified ",
1468987da915Sopenharmony_ci						feedle64(buf,base + 8));
1469987da915Sopenharmony_ci	    		if (((base + 16) >= 0) && ((base + 24) <= length))
1470987da915Sopenharmony_ci	       			showdate("   changed  ",
1471987da915Sopenharmony_ci						feedle64(buf,base + 16));
1472987da915Sopenharmony_ci	    		if (((base + 24) >= 0) && ((base + 32) <= length))
1473987da915Sopenharmony_ci	       			showdate("   read     ",
1474987da915Sopenharmony_ci						feedle64(buf,base + 24));
1475987da915Sopenharmony_ci	    		if (((base + 32) >= 0) && ((base + 36) <= length)) {
1476987da915Sopenharmony_ci	       			v = feedle32(buf, base + 32);
1477987da915Sopenharmony_ci	       			printf("   DOS flags 0x%lx\n",
1478987da915Sopenharmony_ci						(long)le32_to_cpu(v));
1479987da915Sopenharmony_ci	       		}
1480987da915Sopenharmony_ci	    		if (((base + 52) >= 0) && ((base + 56) <= length)) {
1481987da915Sopenharmony_ci	       			v = feedle32(buf, base + 52);
1482987da915Sopenharmony_ci	       			printf("   security id 0x%lx\n",
1483987da915Sopenharmony_ci						(long)le32_to_cpu(v));
1484987da915Sopenharmony_ci	       		}
1485987da915Sopenharmony_ci	    		if (((base + 64) >= 0) && ((base + 72) <= length)) {
1486987da915Sopenharmony_ci				/*
1487987da915Sopenharmony_ci				 * This is badly aligned for Sparc when
1488987da915Sopenharmony_ci				 * stamps not present and base == 52
1489987da915Sopenharmony_ci				 */
1490987da915Sopenharmony_ci				memcpy(&w, &buf[base + 64], 8);
1491987da915Sopenharmony_ci	       			printf("   journal idx 0x%llx\n",
1492987da915Sopenharmony_ci						(long long)le64_to_cpu(w));
1493987da915Sopenharmony_ci	       		}
1494987da915Sopenharmony_ci	    	} else {
1495987da915Sopenharmony_ci			printf("   set an MFT attribute at offset 0x%x, attr 0x%x\n",
1496987da915Sopenharmony_ci					(int)offs, attr);
1497987da915Sopenharmony_ci			if (pa)
1498987da915Sopenharmony_ci				showattribute("      ",pa);
1499987da915Sopenharmony_ci		}
1500987da915Sopenharmony_ci	 	break;
1501987da915Sopenharmony_ci	case UpdateNonResidentValue : /* 8 */
1502987da915Sopenharmony_ci		printf("   set attr 0x%x (%s)\n",attr,attrname(attr));
1503987da915Sopenharmony_ci		pa = getattrentry(attr,0);
1504987da915Sopenharmony_ci		if (pa)
1505987da915Sopenharmony_ci			showattribute("      ",pa);
1506987da915Sopenharmony_ci		base = 0; /* ? */
1507987da915Sopenharmony_ci// Should not be decoded, unless attr is of identified type (I30, ...)
1508987da915Sopenharmony_ci		if (pa && (pa->namelen == 8) && !memcmp(pa->name, SDS, 8)) {
1509987da915Sopenharmony_ci			if (length >= 4)
1510987da915Sopenharmony_ci				printf("   security hash 0x%lx\n",
1511987da915Sopenharmony_ci						(long)getle32(buf, 0));
1512987da915Sopenharmony_ci			if (length >= 8)
1513987da915Sopenharmony_ci				printf("   security id 0x%lx\n",
1514987da915Sopenharmony_ci						(long)getle32(buf, 4));
1515987da915Sopenharmony_ci			if (length >= 20)
1516987da915Sopenharmony_ci				printf("   entry size  %ld\n",
1517987da915Sopenharmony_ci						(long)getle32(buf, 16));
1518987da915Sopenharmony_ci		}
1519987da915Sopenharmony_ci		if (pa && (pa->namelen == 8) && !memcmp(pa->name, I30, 8)) {
1520987da915Sopenharmony_ci			if (!memcmp(buf, "INDX", 4))
1521987da915Sopenharmony_ci				base = 64; /* full record */
1522987da915Sopenharmony_ci			else
1523987da915Sopenharmony_ci				base = 0;  /* entries */
1524987da915Sopenharmony_ci			inode = feedle64(buf, base);
1525987da915Sopenharmony_ci			printf("   inode  %lld\n",
1526987da915Sopenharmony_ci			     (long long)MREF(le64_to_cpu(inode)));
1527987da915Sopenharmony_ci			inode = feedle64(buf, base + 16);
1528987da915Sopenharmony_ci			printf("   parent inode %lld\n",
1529987da915Sopenharmony_ci			     (long long)MREF(le64_to_cpu(inode)));
1530987da915Sopenharmony_ci			showname("   file    ",&buf[base + 82],
1531987da915Sopenharmony_ci							buf[base + 80] & 255);
1532987da915Sopenharmony_ci			showdate("   date    ",feedle64(buf, base + 32));
1533987da915Sopenharmony_ci		}
1534987da915Sopenharmony_ci		break;
1535987da915Sopenharmony_ci	case UpdateMappingPairs : /* 9 */
1536987da915Sopenharmony_ci		printf("   update runlist in attr 0x%x (%s)\n",attr,
1537987da915Sopenharmony_ci				attrname(attr));
1538987da915Sopenharmony_ci	       /* argument is a compressed runlist (or part of it ?) */
1539987da915Sopenharmony_ci	       /* stop when finding 00 */
1540987da915Sopenharmony_ci		break;
1541987da915Sopenharmony_ci	case SetNewAttributeSizes : /* 11 */
1542987da915Sopenharmony_ci		printf("   set sizes in attr 0x%x (%s)\n",attr,attrname(attr));
1543987da915Sopenharmony_ci		base = 0; /* left justified ? */
1544987da915Sopenharmony_ci		size = feedle64(buf,0);
1545987da915Sopenharmony_ci		printf("     allocated size %lld\n",(long long)le64_to_cpu(size));
1546987da915Sopenharmony_ci		size = feedle64(buf,8);
1547987da915Sopenharmony_ci		printf("          real size %lld\n",(long long)le64_to_cpu(size));
1548987da915Sopenharmony_ci		size = feedle64(buf,16);
1549987da915Sopenharmony_ci		printf("   initialized size %lld\n",(long long)le64_to_cpu(size));
1550987da915Sopenharmony_ci		break;
1551987da915Sopenharmony_ci	case AddIndexEntryRoot : /* 12 */
1552987da915Sopenharmony_ci	case AddIndexEntryAllocation : /* 14 */
1553987da915Sopenharmony_ci		/*
1554987da915Sopenharmony_ci		 * The record offset designates the mft attribute offset,
1555987da915Sopenharmony_ci		 * offs and length define a left-justified window in this
1556987da915Sopenharmony_ci		 * attribute.
1557987da915Sopenharmony_ci		 */
1558987da915Sopenharmony_ci		if (action == AddIndexEntryRoot)
1559987da915Sopenharmony_ci			printf("   add resident index entry, attr 0x%x\n",attr);
1560987da915Sopenharmony_ci		else
1561987da915Sopenharmony_ci			printf("   add nonres index entry, attr 0x%x\n",attr);
1562987da915Sopenharmony_ci		pa = getattrentry(attr,0);
1563987da915Sopenharmony_ci		if (pa)
1564987da915Sopenharmony_ci			showattribute("      ",pa);
1565987da915Sopenharmony_ci		base = 0;
1566987da915Sopenharmony_ci		p = getle16(buf, base + 8);
1567987da915Sopenharmony_ci		/* index types may be discriminated by inode in base+0 */
1568987da915Sopenharmony_ci		switch (p) { /* size of index entry */
1569987da915Sopenharmony_ci		case 32 :  /* $R entry */
1570987da915Sopenharmony_ci			memcpy(&inode, &buf[base + 20], 8); /* bad align */
1571987da915Sopenharmony_ci			printf("   $R reparse index\n");
1572987da915Sopenharmony_ci			printf("   reparsed inode 0x%016llx\n",
1573987da915Sopenharmony_ci					(long long)le64_to_cpu(inode));
1574987da915Sopenharmony_ci			printf("   reparse tag 0x%lx\n",
1575987da915Sopenharmony_ci					(long)getle32(buf, 16));
1576987da915Sopenharmony_ci			break;
1577987da915Sopenharmony_ci		case 40 :  /* $SII entry */
1578987da915Sopenharmony_ci			printf("   $SII security id index\n");
1579987da915Sopenharmony_ci			printf("   security id 0x%lx\n",
1580987da915Sopenharmony_ci					(long)getle32(buf, 16));
1581987da915Sopenharmony_ci			printf("   security hash 0x%lx\n",
1582987da915Sopenharmony_ci					(long)getle32(buf, 20));
1583987da915Sopenharmony_ci			break;
1584987da915Sopenharmony_ci		case 48 :  /* $SDH entry */
1585987da915Sopenharmony_ci			printf("   $SDH security id index\n");
1586987da915Sopenharmony_ci			printf("   security id 0x%lx\n",
1587987da915Sopenharmony_ci					(long)getle32(buf, 20));
1588987da915Sopenharmony_ci			printf("   security hash 0x%lx\n",
1589987da915Sopenharmony_ci					(long)getle32(buf, 16));
1590987da915Sopenharmony_ci			break;
1591987da915Sopenharmony_ci		default :
1592987da915Sopenharmony_ci		  /* directory index are at least 84 bytes long, ntfsdoc p 98 */
1593987da915Sopenharmony_ci		  /* have everything needed to create the index */
1594987da915Sopenharmony_ci			lth = buf[base + 80] & 255;
1595987da915Sopenharmony_ci		  /* consistency of file name length */
1596987da915Sopenharmony_ci			if (getle16(buf,10) == (u32)(2*lth + 66)) {
1597987da915Sopenharmony_ci				printf("   directory index\n");
1598987da915Sopenharmony_ci				inode = feedle64(buf,16);
1599987da915Sopenharmony_ci				printf("   parent dir inode %lld\n",
1600987da915Sopenharmony_ci					(long long)MREF(le64_to_cpu(inode)));
1601987da915Sopenharmony_ci				if (feedle32(buf,72)
1602987da915Sopenharmony_ci						& const_cpu_to_le32(0x10000000))
1603987da915Sopenharmony_ci					showname("   file (dir) ",
1604987da915Sopenharmony_ci						&buf[base + 82],
1605987da915Sopenharmony_ci						buf[base + 80] & 255);
1606987da915Sopenharmony_ci				else
1607987da915Sopenharmony_ci					showname("   file ",
1608987da915Sopenharmony_ci						&buf[base + 82],
1609987da915Sopenharmony_ci						buf[base + 80] & 255);
1610987da915Sopenharmony_ci				inode = feedle64(buf,0);
1611987da915Sopenharmony_ci				printf("   file inode %lld\n",
1612987da915Sopenharmony_ci					(long long)MREF(le64_to_cpu(inode)));
1613987da915Sopenharmony_ci				size = feedle64(buf,64);
1614987da915Sopenharmony_ci				printf("   file size %lld\n",
1615987da915Sopenharmony_ci					(long long)le64_to_cpu(size));
1616987da915Sopenharmony_ci				showdate("   created  ",
1617987da915Sopenharmony_ci						feedle64(buf,base + 24));
1618987da915Sopenharmony_ci				showdate("   modified ",
1619987da915Sopenharmony_ci						feedle64(buf,base + 32));
1620987da915Sopenharmony_ci				showdate("   changed  ",
1621987da915Sopenharmony_ci						feedle64(buf,base + 40));
1622987da915Sopenharmony_ci				showdate("   read     ",
1623987da915Sopenharmony_ci						feedle64(buf,base + 48));
1624987da915Sopenharmony_ci			} else
1625987da915Sopenharmony_ci				printf("   unknown index type\n");
1626987da915Sopenharmony_ci			break;
1627987da915Sopenharmony_ci			}
1628987da915Sopenharmony_ci		break;
1629987da915Sopenharmony_ci	case SetIndexEntryVcnRoot : /* 17 */
1630987da915Sopenharmony_ci		printf("   set vcn of non-resident index root, attr 0x%x\n",
1631987da915Sopenharmony_ci				attr);
1632987da915Sopenharmony_ci		pa = getattrentry(attr,0);
1633987da915Sopenharmony_ci		if (pa)
1634987da915Sopenharmony_ci			showattribute("      ",pa);
1635987da915Sopenharmony_ci		printf("   vcn %lld\n", (long long)getle64(buf,0));
1636987da915Sopenharmony_ci		break;
1637987da915Sopenharmony_ci	case UpdateFileNameRoot : /* 19 */
1638987da915Sopenharmony_ci		/*
1639987da915Sopenharmony_ci		 * Update an entry in a resident directory index.
1640987da915Sopenharmony_ci		 * The record offset designates the mft attribute offset,
1641987da915Sopenharmony_ci		 * offs and length define a right-justified window in this
1642987da915Sopenharmony_ci		 * attribute.
1643987da915Sopenharmony_ci		 */
1644987da915Sopenharmony_ci		printf("   set directory resident entry, attr 0x%x\n",attr);
1645987da915Sopenharmony_ci		base = length - 0x50;
1646987da915Sopenharmony_ci		pa = getattrentry(attr,0);
1647987da915Sopenharmony_ci		if (pa)
1648987da915Sopenharmony_ci			showattribute("      ",pa);
1649987da915Sopenharmony_ci		if (pa
1650987da915Sopenharmony_ci		   && !pa->inode
1651987da915Sopenharmony_ci		   && (pa->type == const_cpu_to_le32(0x80))
1652987da915Sopenharmony_ci		   && !(offs & 3)) {
1653987da915Sopenharmony_ci			if (base >= -24)
1654987da915Sopenharmony_ci				showdate("   created  ",feedle64(buf,
1655987da915Sopenharmony_ci							base + 24));
1656987da915Sopenharmony_ci			if (base >= -32)
1657987da915Sopenharmony_ci				showdate("   modified ",feedle64(buf,
1658987da915Sopenharmony_ci							base + 32));
1659987da915Sopenharmony_ci			if (base >= -40)
1660987da915Sopenharmony_ci				showdate("   changed  ",feedle64(buf,
1661987da915Sopenharmony_ci							base + 40));
1662987da915Sopenharmony_ci			if (base >= -48)
1663987da915Sopenharmony_ci				showdate("   read     ",feedle64(buf,
1664987da915Sopenharmony_ci							base + 48));
1665987da915Sopenharmony_ci			if (base >= -56) {
1666987da915Sopenharmony_ci				size = feedle64(buf,base + 56);
1667987da915Sopenharmony_ci				printf("   allocated size %lld\n",
1668987da915Sopenharmony_ci						(long long)le64_to_cpu(size));
1669987da915Sopenharmony_ci			}
1670987da915Sopenharmony_ci			if (base >= -64) {
1671987da915Sopenharmony_ci				size = feedle64(buf,base + 64);
1672987da915Sopenharmony_ci				printf("   real size %lld\n",
1673987da915Sopenharmony_ci						(long long)le64_to_cpu(size));
1674987da915Sopenharmony_ci			}
1675987da915Sopenharmony_ci			if (base > -72) {
1676987da915Sopenharmony_ci				v = feedle32(buf,base + 72);
1677987da915Sopenharmony_ci				printf("   DOS flags 0x%lx\n",
1678987da915Sopenharmony_ci						(long)le32_to_cpu(v));
1679987da915Sopenharmony_ci			}
1680987da915Sopenharmony_ci		} else {
1681987da915Sopenharmony_ci			/* Usually caused by attr not yet defined */
1682987da915Sopenharmony_ci			if (pa && pa->type)
1683987da915Sopenharmony_ci				printf("** Unexpected index parameters\n");
1684987da915Sopenharmony_ci		}
1685987da915Sopenharmony_ci		break;
1686987da915Sopenharmony_ci	case UpdateFileNameAllocation : /* 20 */
1687987da915Sopenharmony_ci		     /* update entry in directory index */
1688987da915Sopenharmony_ci		     /* only dates, sizes and attrib */
1689987da915Sopenharmony_ci		base = length - 64; /* p 12 */
1690987da915Sopenharmony_ci		printf("   set directory nonres entry, attr 0x%x\n",attr);
1691987da915Sopenharmony_ci		pa = getattrentry(attr,0);
1692987da915Sopenharmony_ci		if (pa)
1693987da915Sopenharmony_ci			showattribute("      ",pa);
1694987da915Sopenharmony_ci		if (base >= -8)
1695987da915Sopenharmony_ci			showdate("   created  ",feedle64(buf, base + 8));
1696987da915Sopenharmony_ci		if (base >= -16)
1697987da915Sopenharmony_ci			showdate("   modified ",feedle64(buf, base + 16));
1698987da915Sopenharmony_ci		if (base >= -24)
1699987da915Sopenharmony_ci			showdate("   changed  ",feedle64(buf, base + 24));
1700987da915Sopenharmony_ci		if (base >= -32)
1701987da915Sopenharmony_ci			showdate("   read     ",*(const le64*)&buf[base + 32]);
1702987da915Sopenharmony_ci		if (base >= -40) {
1703987da915Sopenharmony_ci			size = feedle64(buf, base + 40);
1704987da915Sopenharmony_ci			printf("   allocated size %lld\n",
1705987da915Sopenharmony_ci						(long long)le64_to_cpu(size));
1706987da915Sopenharmony_ci		}
1707987da915Sopenharmony_ci		if (base >= -48) {
1708987da915Sopenharmony_ci			size = feedle64(buf, base + 48);
1709987da915Sopenharmony_ci			printf("   real size %lld\n",
1710987da915Sopenharmony_ci						(long long)le64_to_cpu(size));
1711987da915Sopenharmony_ci		}
1712987da915Sopenharmony_ci		if (base >= -56) {
1713987da915Sopenharmony_ci			v = feedle32(buf, base + 56);
1714987da915Sopenharmony_ci			printf("   DOS flags 0x%lx\n",(long)le32_to_cpu(v));
1715987da915Sopenharmony_ci		}
1716987da915Sopenharmony_ci		break;
1717987da915Sopenharmony_ci      	case SetBitsInNonResidentBitMap : /* 21 */
1718987da915Sopenharmony_ci	case ClearBitsInNonResidentBitMap : /* 22 */
1719987da915Sopenharmony_ci		if (action == SetBitsInNonResidentBitMap)
1720987da915Sopenharmony_ci			printf("   SetBitsInNonResidentBitMap, attr 0x%x\n",
1721987da915Sopenharmony_ci					attr);
1722987da915Sopenharmony_ci		else
1723987da915Sopenharmony_ci			printf("   ClearBitsInNonResidentBitMap, attr 0x%x\n",
1724987da915Sopenharmony_ci					attr);
1725987da915Sopenharmony_ci		pa = getattrentry(attr,0);
1726987da915Sopenharmony_ci		if (pa)
1727987da915Sopenharmony_ci			showattribute("      ",pa);
1728987da915Sopenharmony_ci		v = feedle32(buf, 0);
1729987da915Sopenharmony_ci		printf("   first bit %ld\n",(long)le32_to_cpu(v));
1730987da915Sopenharmony_ci		v = feedle32(buf, 4);
1731987da915Sopenharmony_ci		printf("   bit count %ld\n",(long)le32_to_cpu(v));
1732987da915Sopenharmony_ci		break;
1733987da915Sopenharmony_ci	case OpenNonResidentAttribute : /* 28 */
1734987da915Sopenharmony_ci		printf("   OpenNonResidentAttribute, attr 0x%x\n",attr);
1735987da915Sopenharmony_ci		extra = get_extra_offset(logr)
1736987da915Sopenharmony_ci			- (redo ? get_redo_offset(logr)
1737987da915Sopenharmony_ci				: get_undo_offset(logr));
1738987da915Sopenharmony_ci		if (logr->undo_length) {
1739987da915Sopenharmony_ci			len = le32_to_cpu(logr->client_data_length)
1740987da915Sopenharmony_ci				+ LOG_RECORD_HEAD_SZ
1741987da915Sopenharmony_ci				- get_extra_offset(logr);
1742987da915Sopenharmony_ci			/* this gives a length aligned modulo 8 */
1743987da915Sopenharmony_ci			len = fixnamelen(&buf[extra], len);
1744987da915Sopenharmony_ci		} else
1745987da915Sopenharmony_ci			len = 0;
1746987da915Sopenharmony_ci		pa = getattrentry(attr,len);
1747987da915Sopenharmony_ci		if (pa && redo) {
1748987da915Sopenharmony_ci			/*
1749987da915Sopenharmony_ci			 * If this is a redo, collect the attribute data.
1750987da915Sopenharmony_ci			 * This should only be done when walking forward.
1751987da915Sopenharmony_ci			 */
1752987da915Sopenharmony_ci			copy_attribute(pa, buf, length);
1753987da915Sopenharmony_ci			pa->namelen = len;
1754987da915Sopenharmony_ci			if (len)
1755987da915Sopenharmony_ci				memcpy(pa->name,&buf[extra],len);
1756987da915Sopenharmony_ci			printf("   MFT attribute 0x%lx (%s)\n",
1757987da915Sopenharmony_ci				(long)le32_to_cpu(pa->type),
1758987da915Sopenharmony_ci				mftattrname(pa->type));
1759987da915Sopenharmony_ci			printf("   lsn   0x%016llx\n",
1760987da915Sopenharmony_ci				(long long)pa->lsn);
1761987da915Sopenharmony_ci			printf("   inode %lld\n",
1762987da915Sopenharmony_ci				(long long)pa->inode);
1763987da915Sopenharmony_ci		}
1764987da915Sopenharmony_ci		if (logr->undo_length)
1765987da915Sopenharmony_ci			showname("   extra : attr name ", &buf[extra], len/2);
1766987da915Sopenharmony_ci		if (!redo && length) {
1767987da915Sopenharmony_ci			printf("   * undo attr not shown\n");
1768987da915Sopenharmony_ci		}
1769987da915Sopenharmony_ci		break;
1770987da915Sopenharmony_ci	case OpenAttributeTableDump : /* 29 */
1771987da915Sopenharmony_ci		printf("   OpenAttributeTableDump, attr 0x%x (%s)\n",
1772987da915Sopenharmony_ci				attr,attrname(attr));
1773987da915Sopenharmony_ci		i = 24;
1774987da915Sopenharmony_ci		if (i < length) {
1775987da915Sopenharmony_ci			int x;
1776987da915Sopenharmony_ci			int more;
1777987da915Sopenharmony_ci			int step;
1778987da915Sopenharmony_ci			int used;
1779987da915Sopenharmony_ci
1780987da915Sopenharmony_ci			step = getle16(buf, 8);
1781987da915Sopenharmony_ci			used = getle16(buf, 12);
1782987da915Sopenharmony_ci			    /*
1783987da915Sopenharmony_ci			     * Changed from Win10, formerly we got step = 44.
1784987da915Sopenharmony_ci			     * The record layout has also changed
1785987da915Sopenharmony_ci			     */
1786987da915Sopenharmony_ci			if ((step != sizeof(ATTR_OLD))
1787987da915Sopenharmony_ci			    && (step != sizeof(ATTR_NEW))) {
1788987da915Sopenharmony_ci				printf("   ** Unexpected step %d\n",step);
1789987da915Sopenharmony_ci			}
1790987da915Sopenharmony_ci			more = 0;
1791987da915Sopenharmony_ci			for (x=0; (x<used) && (i<length); i+=step, x++) {
1792987da915Sopenharmony_ci				pa = getattrentry(i,0);
1793987da915Sopenharmony_ci				if (pa) {
1794987da915Sopenharmony_ci					copy_attribute(pa, &buf[i], step);
1795987da915Sopenharmony_ci					if (x <= SHOWATTRS) {
1796987da915Sopenharmony_ci						printf("   attr 0x%x inode %lld"
1797987da915Sopenharmony_ci							" type %s",
1798987da915Sopenharmony_ci							(int)i,
1799987da915Sopenharmony_ci							(long long)pa->inode,
1800987da915Sopenharmony_ci							mftattrname(pa->type));
1801987da915Sopenharmony_ci						if (pa->namelen)
1802987da915Sopenharmony_ci							showname(" name ",
1803987da915Sopenharmony_ci								(char*)pa->name,
1804987da915Sopenharmony_ci								pa->namelen/2);
1805987da915Sopenharmony_ci						else
1806987da915Sopenharmony_ci							printf("\n");
1807987da915Sopenharmony_ci					} else
1808987da915Sopenharmony_ci						more++;
1809987da915Sopenharmony_ci				}
1810987da915Sopenharmony_ci			}
1811987da915Sopenharmony_ci			if (more)
1812987da915Sopenharmony_ci				printf("   (%d more attrs not shown)\n",more);
1813987da915Sopenharmony_ci		}
1814987da915Sopenharmony_ci		break;
1815987da915Sopenharmony_ci	case AttributeNamesDump : /* 30 */
1816987da915Sopenharmony_ci		printf("   AttributeNamesDump, attr 0x%x (%s)\n",
1817987da915Sopenharmony_ci			       attr,attrname(attr));
1818987da915Sopenharmony_ci		i = 8;
1819987da915Sopenharmony_ci		if (i < length) {
1820987da915Sopenharmony_ci			unsigned int l;
1821987da915Sopenharmony_ci			unsigned int key;
1822987da915Sopenharmony_ci			int x;
1823987da915Sopenharmony_ci			int more;
1824987da915Sopenharmony_ci
1825987da915Sopenharmony_ci			more = 0;
1826987da915Sopenharmony_ci			x = 0;
1827987da915Sopenharmony_ci			do {
1828987da915Sopenharmony_ci				l = le16_to_cpu(*(const le16*)&buf[i+2]);
1829987da915Sopenharmony_ci				key = le16_to_cpu(*(const le16*)&buf[i]);
1830987da915Sopenharmony_ci				if (l > 510) {
1831987da915Sopenharmony_ci					printf("** Error : bad attribute name"
1832987da915Sopenharmony_ci							" length %d\n",l);
1833987da915Sopenharmony_ci					key = 0;
1834987da915Sopenharmony_ci				}
1835987da915Sopenharmony_ci		 /* Apparently, may have to stop before reaching the end */
1836987da915Sopenharmony_ci				if (key) {
1837987da915Sopenharmony_ci					pa = getattrentry(key,l);
1838987da915Sopenharmony_ci					if (pa) {
1839987da915Sopenharmony_ci						pa->namelen = l;
1840987da915Sopenharmony_ci						memcpy(pa->name,&buf[i+4],l);
1841987da915Sopenharmony_ci					}
1842987da915Sopenharmony_ci					if (x < SHOWATTRS) {
1843987da915Sopenharmony_ci						printf("   attr 0x%x is",key);
1844987da915Sopenharmony_ci						showname("  ",&buf[i+4],l/2);
1845987da915Sopenharmony_ci					} else
1846987da915Sopenharmony_ci						more++;
1847987da915Sopenharmony_ci					i += l + 6;
1848987da915Sopenharmony_ci					x++;
1849987da915Sopenharmony_ci				}
1850987da915Sopenharmony_ci			} while (key && (i < length));
1851987da915Sopenharmony_ci			if (more)
1852987da915Sopenharmony_ci				printf("   (%d more attrs not shown)\n",more);
1853987da915Sopenharmony_ci		}
1854987da915Sopenharmony_ci		break;
1855987da915Sopenharmony_ci	default :
1856987da915Sopenharmony_ci		break;
1857987da915Sopenharmony_ci	}
1858987da915Sopenharmony_ci}
1859987da915Sopenharmony_ci
1860987da915Sopenharmony_cistatic void detaillogr(CONTEXT *ctx, const LOG_RECORD *logr)
1861987da915Sopenharmony_ci{
1862987da915Sopenharmony_ci	u64 lcn;
1863987da915Sopenharmony_ci	u64 baselcn;
1864987da915Sopenharmony_ci	unsigned int i;
1865987da915Sopenharmony_ci	unsigned int off;
1866987da915Sopenharmony_ci	unsigned int undo;
1867987da915Sopenharmony_ci	unsigned int redo;
1868987da915Sopenharmony_ci	unsigned int extra;
1869987da915Sopenharmony_ci	unsigned int end;
1870987da915Sopenharmony_ci	unsigned int listsize;
1871987da915Sopenharmony_ci	BOOL onmft;
1872987da915Sopenharmony_ci
1873987da915Sopenharmony_ci	switch (logr->record_type) {
1874987da915Sopenharmony_ci	case LOG_STANDARD :
1875987da915Sopenharmony_ci		onmft = logr->cluster_index
1876987da915Sopenharmony_ci			|| acts_on_mft(le16_to_cpu(logr->redo_operation))
1877987da915Sopenharmony_ci			|| acts_on_mft(le16_to_cpu(logr->undo_operation));
1878987da915Sopenharmony_ci		printf("redo_operation         %04x %s\n",
1879987da915Sopenharmony_ci			(int)le16_to_cpu(logr->redo_operation),
1880987da915Sopenharmony_ci			actionname(le16_to_cpu(logr->redo_operation)));
1881987da915Sopenharmony_ci		printf("undo_operation         %04x %s\n",
1882987da915Sopenharmony_ci			(int)le16_to_cpu(logr->undo_operation),
1883987da915Sopenharmony_ci			actionname(le16_to_cpu(logr->undo_operation)));
1884987da915Sopenharmony_ci		printf("redo_offset            %04x\n",
1885987da915Sopenharmony_ci			(int)le16_to_cpu(logr->redo_offset));
1886987da915Sopenharmony_ci		printf("redo_length            %04x\n",
1887987da915Sopenharmony_ci			(int)le16_to_cpu(logr->redo_length));
1888987da915Sopenharmony_ci		printf("undo_offset            %04x\n",
1889987da915Sopenharmony_ci			(int)le16_to_cpu(logr->undo_offset));
1890987da915Sopenharmony_ci		printf("undo_length            %04x\n",
1891987da915Sopenharmony_ci			(int)le16_to_cpu(logr->undo_length));
1892987da915Sopenharmony_ci		printf("target_attribute       %04x\n",
1893987da915Sopenharmony_ci			(int)le16_to_cpu(logr->target_attribute));
1894987da915Sopenharmony_ci		printf("lcns_to_follow         %04x\n",
1895987da915Sopenharmony_ci			(int)le16_to_cpu(logr->lcns_to_follow));
1896987da915Sopenharmony_ci		printf("record_offset          %04x\n",
1897987da915Sopenharmony_ci			(int)le16_to_cpu(logr->record_offset));
1898987da915Sopenharmony_ci		printf("attribute_offset       %04x\n",
1899987da915Sopenharmony_ci			(int)le16_to_cpu(logr->attribute_offset));
1900987da915Sopenharmony_ci		printf("cluster_index          %04x\n",
1901987da915Sopenharmony_ci			(int)le16_to_cpu(logr->cluster_index));
1902987da915Sopenharmony_ci		printf("attribute_flags        %04x\n",
1903987da915Sopenharmony_ci			(int)le16_to_cpu(logr->attribute_flags));
1904987da915Sopenharmony_ci		if (mftrecbits && onmft)
1905987da915Sopenharmony_ci			printf("target_vcn             %016llx (inode %lld)\n",
1906987da915Sopenharmony_ci				(long long)sle64_to_cpu(logr->target_vcn),
1907987da915Sopenharmony_ci				(((long long)sle64_to_cpu(logr->target_vcn)
1908987da915Sopenharmony_ci					<< clusterbits)
1909987da915Sopenharmony_ci				+ (le16_to_cpu(logr->cluster_index) << 9))
1910987da915Sopenharmony_ci					 >> mftrecbits);
1911987da915Sopenharmony_ci		else
1912987da915Sopenharmony_ci			printf("target_vcn             %016llx\n",
1913987da915Sopenharmony_ci				(long long)sle64_to_cpu(logr->target_vcn));
1914987da915Sopenharmony_ci			/* Compute a base for the current run of mft */
1915987da915Sopenharmony_ci		baselcn = sle64_to_cpu(logr->lcn_list[0])
1916987da915Sopenharmony_ci					- sle64_to_cpu(logr->target_vcn);
1917987da915Sopenharmony_ci		for (i=0; i<le16_to_cpu(logr->lcns_to_follow)
1918987da915Sopenharmony_ci						&& (i<SHOWLISTS); i++) {
1919987da915Sopenharmony_ci			lcn = sle64_to_cpu(logr->lcn_list[i]);
1920987da915Sopenharmony_ci			printf("  (%d offs 0x%x) lcn    %016llx",i,
1921987da915Sopenharmony_ci				(int)(8*i + sizeof(LOG_RECORD) - 8),
1922987da915Sopenharmony_ci				(long long)lcn);
1923987da915Sopenharmony_ci			lcn &= 0xffffffffffffULL;
1924987da915Sopenharmony_ci			if (mftrecsz && onmft) {
1925987da915Sopenharmony_ci				if (clustersz > mftrecsz)
1926987da915Sopenharmony_ci					printf(" (MFT records for inodes"
1927987da915Sopenharmony_ci						" %lld-%lld)\n",
1928987da915Sopenharmony_ci						(long long)((lcn - baselcn)
1929987da915Sopenharmony_ci							*clustersz/mftrecsz),
1930987da915Sopenharmony_ci						(long long)((lcn + 1 - baselcn)
1931987da915Sopenharmony_ci							*clustersz/mftrecsz - 1));
1932987da915Sopenharmony_ci				else
1933987da915Sopenharmony_ci					printf(" (MFT record for inode %lld)\n",
1934987da915Sopenharmony_ci						(long long)((lcn - baselcn)
1935987da915Sopenharmony_ci							*clustersz/mftrecsz));
1936987da915Sopenharmony_ci				printf("     assuming record for inode %lld\n",
1937987da915Sopenharmony_ci					(long long)((lcn - baselcn)
1938987da915Sopenharmony_ci						*clustersz/mftrecsz
1939987da915Sopenharmony_ci					+ (le16_to_cpu(logr->cluster_index)
1940987da915Sopenharmony_ci						 >> 1)));
1941987da915Sopenharmony_ci			} else
1942987da915Sopenharmony_ci				printf("\n");
1943987da915Sopenharmony_ci		}
1944987da915Sopenharmony_ci                /*
1945987da915Sopenharmony_ci                 *  redo_offset and undo_offset are considered unsafe
1946987da915Sopenharmony_ci		 *  (actually they are safe when you know the logic)
1947987da915Sopenharmony_ci                 *  2) redo : redo (defined by redo_offset)
1948987da915Sopenharmony_ci                 *  3) undo : undo (defined by undo_offset)
1949987da915Sopenharmony_ci                 *  4) extra : unknown data (end of undo to data_length)
1950987da915Sopenharmony_ci                 */
1951987da915Sopenharmony_ci         end = le32_to_cpu(logr->client_data_length) + LOG_RECORD_HEAD_SZ;
1952987da915Sopenharmony_ci         if (logr->redo_length && logr->undo_length)
1953987da915Sopenharmony_ci            {
1954987da915Sopenharmony_ci                          /* both undo and redo are present */
1955987da915Sopenharmony_ci            if (le16_to_cpu(logr->undo_offset) <=
1956987da915Sopenharmony_ci						le16_to_cpu(logr->redo_offset))
1957987da915Sopenharmony_ci               {
1958987da915Sopenharmony_ci               undo = sizeof(LOG_RECORD) - 8
1959987da915Sopenharmony_ci					+ 8*le16_to_cpu(logr->lcns_to_follow);
1960987da915Sopenharmony_ci               if (logr->redo_offset == logr->undo_offset)
1961987da915Sopenharmony_ci                  redo = undo;
1962987da915Sopenharmony_ci               else
1963987da915Sopenharmony_ci                  redo = undo + ((le16_to_cpu(logr->undo_length) - 1) | 7) + 1;
1964987da915Sopenharmony_ci               extra = redo + ((le16_to_cpu(logr->redo_length) - 1) | 7) + 1;
1965987da915Sopenharmony_ci               }
1966987da915Sopenharmony_ci            else
1967987da915Sopenharmony_ci               {
1968987da915Sopenharmony_ci               redo = sizeof(LOG_RECORD) - 8
1969987da915Sopenharmony_ci					+ 8*le16_to_cpu(logr->lcns_to_follow);
1970987da915Sopenharmony_ci               undo = redo + ((le16_to_cpu(logr->redo_length) - 1) | 7) + 1;
1971987da915Sopenharmony_ci               extra = undo + ((le16_to_cpu(logr->undo_length) - 1) | 7) + 1;
1972987da915Sopenharmony_ci               }
1973987da915Sopenharmony_ci            }
1974987da915Sopenharmony_ci         else
1975987da915Sopenharmony_ci            if (logr->redo_length)
1976987da915Sopenharmony_ci               {
1977987da915Sopenharmony_ci                                  /* redo and not undo */
1978987da915Sopenharmony_ci               redo = undo = sizeof(LOG_RECORD) - 8
1979987da915Sopenharmony_ci					+ 8*le16_to_cpu(logr->lcns_to_follow);
1980987da915Sopenharmony_ci               extra = redo + ((le16_to_cpu(logr->redo_length) - 1) | 7) + 1;
1981987da915Sopenharmony_ci               }
1982987da915Sopenharmony_ci            else
1983987da915Sopenharmony_ci               {
1984987da915Sopenharmony_ci                                  /* optional undo and not redo */
1985987da915Sopenharmony_ci               redo = undo = sizeof(LOG_RECORD) - 8
1986987da915Sopenharmony_ci					+ 8*le16_to_cpu(logr->lcns_to_follow);
1987987da915Sopenharmony_ci               extra = undo + ((le16_to_cpu(logr->undo_length) - 1) | 7) + 1;
1988987da915Sopenharmony_ci               }
1989987da915Sopenharmony_ci
1990987da915Sopenharmony_ci         printf("redo 0x%x (%u) undo 0x%x (%u) extra 0x%x (%d)\n",
1991987da915Sopenharmony_ci                  redo,(int)(((le16_to_cpu(logr->redo_length) - 1) | 7) + 1),
1992987da915Sopenharmony_ci                  undo,(int)(((le16_to_cpu(logr->undo_length) - 1) | 7) + 1),
1993987da915Sopenharmony_ci                  extra,(int)(end > extra ? end - extra : 0));
1994987da915Sopenharmony_ci
1995987da915Sopenharmony_ci	if (logr->redo_length && (get_redo_offset(logr) != redo))
1996987da915Sopenharmony_ci		printf("** Unexpected redo offset 0x%x %u (%u)\n",
1997987da915Sopenharmony_ci			get_redo_offset(logr),(int)redo,
1998987da915Sopenharmony_ci			(int)le16_to_cpu(logr->lcns_to_follow));
1999987da915Sopenharmony_ci	if (logr->undo_length && (get_undo_offset(logr) != undo))
2000987da915Sopenharmony_ci		printf("** Unexpected undo offset 0x%x %u (%u)\n",
2001987da915Sopenharmony_ci			get_undo_offset(logr),(int)undo,
2002987da915Sopenharmony_ci			(int)le16_to_cpu(logr->lcns_to_follow));
2003987da915Sopenharmony_ci	if (get_extra_offset(logr) != extra)
2004987da915Sopenharmony_ci		printf("** Unexpected extra offset 0x%x %u (%u)\n",
2005987da915Sopenharmony_ci			get_extra_offset(logr),(int)extra,
2006987da915Sopenharmony_ci			(int)le16_to_cpu(logr->lcns_to_follow));
2007987da915Sopenharmony_ci
2008987da915Sopenharmony_ci         if (extra <= end)
2009987da915Sopenharmony_ci            {
2010987da915Sopenharmony_ci                                       /* show redo data */
2011987da915Sopenharmony_ci            if (logr->redo_length)
2012987da915Sopenharmony_ci               {
2013987da915Sopenharmony_ci               if (logr->lcns_to_follow)
2014987da915Sopenharmony_ci                  {
2015987da915Sopenharmony_ci                  off = le16_to_cpu(logr->record_offset)
2016987da915Sopenharmony_ci					+ le16_to_cpu(logr->attribute_offset);
2017987da915Sopenharmony_ci                  printf("redo data (new data) cluster 0x%llx pos 0x%x :\n",
2018987da915Sopenharmony_ci                        (long long)sle64_to_cpu(logr->lcn_list[off
2019987da915Sopenharmony_ci						>> clusterbits]),
2020987da915Sopenharmony_ci                        (int)(off & (clustersz - 1)));
2021987da915Sopenharmony_ci                  }
2022987da915Sopenharmony_ci               else
2023987da915Sopenharmony_ci			printf("redo data (new data) at offs 0x%x :\n",redo);
2024987da915Sopenharmony_ci               if ((u32)(redo + le16_to_cpu(logr->redo_length))
2025987da915Sopenharmony_ci                    <= end)
2026987da915Sopenharmony_ci                  {
2027987da915Sopenharmony_ci                  hexdump((const char*)logr
2028987da915Sopenharmony_ci				+ redo,le16_to_cpu(logr->redo_length));
2029987da915Sopenharmony_ci                  fixup(ctx, logr, (const char*)logr + redo, TRUE);
2030987da915Sopenharmony_ci                  }
2031987da915Sopenharmony_ci               else printf("redo data overflowing from record\n");
2032987da915Sopenharmony_ci               }
2033987da915Sopenharmony_ci            else
2034987da915Sopenharmony_ci               {
2035987da915Sopenharmony_ci               printf("no redo data (new data)\n");
2036987da915Sopenharmony_ci               fixup(ctx, logr, (const char*)logr + redo, TRUE);
2037987da915Sopenharmony_ci               }
2038987da915Sopenharmony_ci
2039987da915Sopenharmony_ci                                     /* show undo data */
2040987da915Sopenharmony_ci            if (logr->undo_length)
2041987da915Sopenharmony_ci               {
2042987da915Sopenharmony_ci               if (logr->lcns_to_follow)
2043987da915Sopenharmony_ci                   {
2044987da915Sopenharmony_ci                   off = le16_to_cpu(logr->record_offset)
2045987da915Sopenharmony_ci					+ le16_to_cpu(logr->attribute_offset);
2046987da915Sopenharmony_ci                   printf("undo data (old data) cluster 0x%llx pos 0x%x :\n",
2047987da915Sopenharmony_ci                         (long long)sle64_to_cpu(logr->lcn_list[off
2048987da915Sopenharmony_ci							>> clusterbits]),
2049987da915Sopenharmony_ci                         (int)(off & (clustersz - 1)));
2050987da915Sopenharmony_ci                   }
2051987da915Sopenharmony_ci               else printf("undo data (old data) at offs 0x%x :\n",undo);
2052987da915Sopenharmony_ci               if ((u32)(undo + le16_to_cpu(logr->undo_length)) <= end)
2053987da915Sopenharmony_ci                  {
2054987da915Sopenharmony_ci                  if ((undo + le16_to_cpu(logr->undo_length)) < 2*blocksz)
2055987da915Sopenharmony_ci                     {
2056987da915Sopenharmony_ci                     hexdump((const char*)logr
2057987da915Sopenharmony_ci					+ undo,le16_to_cpu(logr->undo_length));
2058987da915Sopenharmony_ci                     fixup(ctx, logr, (const char*)logr + undo, FALSE);
2059987da915Sopenharmony_ci                     }
2060987da915Sopenharmony_ci                  else printf("undo data overflowing from two blocks\n");
2061987da915Sopenharmony_ci                  }
2062987da915Sopenharmony_ci               else printf("undo data overflowing from record\n");
2063987da915Sopenharmony_ci               }
2064987da915Sopenharmony_ci            else
2065987da915Sopenharmony_ci               {
2066987da915Sopenharmony_ci               printf("no undo data (old data)\n");
2067987da915Sopenharmony_ci               fixup(ctx, logr, (const char*)logr + undo, FALSE);
2068987da915Sopenharmony_ci               }
2069987da915Sopenharmony_ci
2070987da915Sopenharmony_ci                                    /* show extra data, if any */
2071987da915Sopenharmony_ci            if (extra != end)
2072987da915Sopenharmony_ci               {
2073987da915Sopenharmony_ci               if (end > blocksz)
2074987da915Sopenharmony_ci                  printf("invalid extra data size\n");
2075987da915Sopenharmony_ci               else
2076987da915Sopenharmony_ci                  {
2077987da915Sopenharmony_ci                  printf("extra data at offs 0x%x\n",extra);
2078987da915Sopenharmony_ci                  hexdump((const char*)logr + extra,
2079987da915Sopenharmony_ci                            end - extra);
2080987da915Sopenharmony_ci                  }
2081987da915Sopenharmony_ci               }
2082987da915Sopenharmony_ci            }
2083987da915Sopenharmony_ci         else
2084987da915Sopenharmony_ci            {
2085987da915Sopenharmony_ci			/* sometimes the designated data overflows */
2086987da915Sopenharmony_ci            if (logr->redo_length
2087987da915Sopenharmony_ci              && ((u32)(redo + le16_to_cpu(logr->redo_length)) > end))
2088987da915Sopenharmony_ci                printf("* redo data overflows from record\n");
2089987da915Sopenharmony_ci            if (logr->undo_length
2090987da915Sopenharmony_ci              && ((u32)(undo + le16_to_cpu(logr->undo_length)) > end))
2091987da915Sopenharmony_ci                printf("* undo data overflows from record\n");
2092987da915Sopenharmony_ci	    }
2093987da915Sopenharmony_ci         	break;
2094987da915Sopenharmony_ci	case LOG_CHECKPOINT :
2095987da915Sopenharmony_ci		printf("---> checkpoint record\n");
2096987da915Sopenharmony_ci		printf("redo_operation         %04x %s\n",
2097987da915Sopenharmony_ci			(int)le16_to_cpu(logr->redo_operation),
2098987da915Sopenharmony_ci			actionname(le16_to_cpu(logr->redo_operation)));
2099987da915Sopenharmony_ci		printf("undo_operation         %04x %s\n",
2100987da915Sopenharmony_ci			(int)le16_to_cpu(logr->undo_operation),
2101987da915Sopenharmony_ci			actionname(le16_to_cpu(logr->undo_operation)));
2102987da915Sopenharmony_ci		printf("redo_offset            %04x\n",
2103987da915Sopenharmony_ci			(int)le16_to_cpu(logr->redo_offset));
2104987da915Sopenharmony_ci		printf("redo_length            %04x\n",
2105987da915Sopenharmony_ci			(int)le16_to_cpu(logr->redo_length));
2106987da915Sopenharmony_ci		printf("transaction_lsn        %016llx\n",
2107987da915Sopenharmony_ci			(long long)sle64_to_cpu(logr->transaction_lsn));
2108987da915Sopenharmony_ci		printf("attributes_lsn         %016llx\n",
2109987da915Sopenharmony_ci			(long long)sle64_to_cpu(logr->attributes_lsn));
2110987da915Sopenharmony_ci		printf("names_lsn              %016llx\n",
2111987da915Sopenharmony_ci			(long long)sle64_to_cpu(logr->names_lsn));
2112987da915Sopenharmony_ci		printf("dirty_pages_lsn        %016llx\n",
2113987da915Sopenharmony_ci			(long long)sle64_to_cpu(logr->dirty_pages_lsn));
2114987da915Sopenharmony_ci		listsize = le32_to_cpu(logr->client_data_length)
2115987da915Sopenharmony_ci				+ LOG_RECORD_HEAD_SZ
2116987da915Sopenharmony_ci				- offsetof(LOG_RECORD, unknown_list);
2117987da915Sopenharmony_ci		if (listsize > 8*SHOWLISTS)
2118987da915Sopenharmony_ci			listsize = 8*SHOWLISTS;
2119987da915Sopenharmony_ci		for (i=0; 8*i<listsize; i++)
2120987da915Sopenharmony_ci			printf("unknown-%u              %016llx\n",i,
2121987da915Sopenharmony_ci				(long long)le64_to_cpu(logr->unknown_list[i]));
2122987da915Sopenharmony_ci		break;
2123987da915Sopenharmony_ci	default :
2124987da915Sopenharmony_ci		printf("** Unknown action type\n");
2125987da915Sopenharmony_ci		if (le32_to_cpu(logr->client_data_length) < blocksz) {
2126987da915Sopenharmony_ci			printf("client_data for record type %ld\n",
2127987da915Sopenharmony_ci				(long)le32_to_cpu(logr->record_type));
2128987da915Sopenharmony_ci			hexdump((const char*)&logr->redo_operation,
2129987da915Sopenharmony_ci				le32_to_cpu(logr->client_data_length));
2130987da915Sopenharmony_ci		} else
2131987da915Sopenharmony_ci			printf("** Bad client data\n");
2132987da915Sopenharmony_ci		break;
2133987da915Sopenharmony_ci	}
2134987da915Sopenharmony_ci}
2135987da915Sopenharmony_ci
2136987da915Sopenharmony_ciBOOL within_lcn_range(const LOG_RECORD *logr)
2137987da915Sopenharmony_ci{
2138987da915Sopenharmony_ci	u64 lcn;
2139987da915Sopenharmony_ci	unsigned int i;
2140987da915Sopenharmony_ci	BOOL within;
2141987da915Sopenharmony_ci
2142987da915Sopenharmony_ci	within = FALSE;
2143987da915Sopenharmony_ci   	switch (logr->record_type) {
2144987da915Sopenharmony_ci      	case LOG_STANDARD :
2145987da915Sopenharmony_ci         	for (i=0; i<le16_to_cpu(logr->lcns_to_follow); i++) {
2146987da915Sopenharmony_ci			lcn = MREF(sle64_to_cpu(logr->lcn_list[i]));
2147987da915Sopenharmony_ci			if ((lcn >= firstlcn) && (lcn <= lastlcn))
2148987da915Sopenharmony_ci				within = TRUE;
2149987da915Sopenharmony_ci		}
2150987da915Sopenharmony_ci		break;
2151987da915Sopenharmony_ci	default :
2152987da915Sopenharmony_ci		break;
2153987da915Sopenharmony_ci	}
2154987da915Sopenharmony_ci	return (within);
2155987da915Sopenharmony_ci}
2156987da915Sopenharmony_ci
2157987da915Sopenharmony_cistatic void showlogr(CONTEXT *ctx, int k, const LOG_RECORD *logr)
2158987da915Sopenharmony_ci{
2159987da915Sopenharmony_ci	s32 diff;
2160987da915Sopenharmony_ci
2161987da915Sopenharmony_ci	if (optv && (!optc || within_lcn_range(logr))) {
2162987da915Sopenharmony_ci		diff = sle64_to_cpu(logr->this_lsn) - synced_lsn;
2163987da915Sopenharmony_ci		printf("this_lsn               %016llx (synced%s%ld) %s\n",
2164987da915Sopenharmony_ci			(long long)sle64_to_cpu(logr->this_lsn),
2165987da915Sopenharmony_ci			(diff < 0 ? "" : "+"),(long)diff,
2166987da915Sopenharmony_ci			commitment(diff + synced_lsn));
2167987da915Sopenharmony_ci		printf("client_previous_lsn    %016llx\n",
2168987da915Sopenharmony_ci			(long long)sle64_to_cpu(logr->client_previous_lsn));
2169987da915Sopenharmony_ci		printf("client_undo_next_lsn   %016llx\n",
2170987da915Sopenharmony_ci			(long long)sle64_to_cpu(logr->client_undo_next_lsn));
2171987da915Sopenharmony_ci		printf("client_data_length     %08lx\n",
2172987da915Sopenharmony_ci			(long)le32_to_cpu(logr->client_data_length));
2173987da915Sopenharmony_ci		printf("seq_number             %d\n",
2174987da915Sopenharmony_ci			(int)le16_to_cpu(logr->client_id.seq_number));
2175987da915Sopenharmony_ci		printf("client_index           %d\n",
2176987da915Sopenharmony_ci			(int)le16_to_cpu(logr->client_id.client_index));
2177987da915Sopenharmony_ci		printf("record_type            %08lx\n",
2178987da915Sopenharmony_ci			(long)le32_to_cpu(logr->record_type));
2179987da915Sopenharmony_ci		printf("transaction_id         %08lx\n",
2180987da915Sopenharmony_ci			(long)le32_to_cpu(logr->transaction_id));
2181987da915Sopenharmony_ci		printf("log_record_flags       %04x\n",
2182987da915Sopenharmony_ci			(int)le16_to_cpu(logr->log_record_flags));
2183987da915Sopenharmony_ci		printf("reserved1              %04x %04x %04x\n",
2184987da915Sopenharmony_ci			(int)le16_to_cpu(logr->reserved_or_alignment[0]),
2185987da915Sopenharmony_ci		(int)le16_to_cpu(logr->reserved_or_alignment[1]),
2186987da915Sopenharmony_ci		(int)le16_to_cpu(logr->reserved_or_alignment[2]));
2187987da915Sopenharmony_ci		detaillogr(ctx, logr);
2188987da915Sopenharmony_ci	}
2189987da915Sopenharmony_ci	if (optt) {
2190987da915Sopenharmony_ci		const char *state;
2191987da915Sopenharmony_ci
2192987da915Sopenharmony_ci		if (logr->record_type == LOG_CHECKPOINT)
2193987da915Sopenharmony_ci			state = "--checkpoint--";
2194987da915Sopenharmony_ci		else
2195987da915Sopenharmony_ci			state = commitment(sle64_to_cpu(logr->this_lsn));
2196987da915Sopenharmony_ci		printf("      at %04x  %016llx %s (%ld) %s\n",k,
2197987da915Sopenharmony_ci			(long long)sle64_to_cpu(logr->this_lsn),
2198987da915Sopenharmony_ci			state,
2199987da915Sopenharmony_ci			(long)(sle64_to_cpu(logr->this_lsn) - synced_lsn),
2200987da915Sopenharmony_ci			actionname(le16_to_cpu(logr->redo_operation)));
2201987da915Sopenharmony_ci		if (logr->client_previous_lsn || logr->client_undo_next_lsn) {
2202987da915Sopenharmony_ci			if (logr->client_previous_lsn
2203987da915Sopenharmony_ci					== logr->client_undo_next_lsn) {
2204987da915Sopenharmony_ci				printf("                               "
2205987da915Sopenharmony_ci					" previous and undo %016llx\n",
2206987da915Sopenharmony_ci					(long long)sle64_to_cpu(
2207987da915Sopenharmony_ci						logr->client_previous_lsn));
2208987da915Sopenharmony_ci			} else {
2209987da915Sopenharmony_ci				printf("                               "
2210987da915Sopenharmony_ci					" previous %016llx",
2211987da915Sopenharmony_ci					(long long)sle64_to_cpu(
2212987da915Sopenharmony_ci						logr->client_previous_lsn));
2213987da915Sopenharmony_ci
2214987da915Sopenharmony_ci				if (logr->client_undo_next_lsn)
2215987da915Sopenharmony_ci					printf(" undo %016llx\n",
2216987da915Sopenharmony_ci						(long long)sle64_to_cpu(
2217987da915Sopenharmony_ci						logr->client_undo_next_lsn));
2218987da915Sopenharmony_ci				else
2219987da915Sopenharmony_ci					printf("\n");
2220987da915Sopenharmony_ci			}
2221987da915Sopenharmony_ci		}
2222987da915Sopenharmony_ci	}
2223987da915Sopenharmony_ci}
2224987da915Sopenharmony_ci
2225987da915Sopenharmony_ci/*
2226987da915Sopenharmony_ci *		Mark transactions which should be redone
2227987da915Sopenharmony_ci */
2228987da915Sopenharmony_ci
2229987da915Sopenharmony_cistatic void mark_transactions(struct ACTION_RECORD *lastaction)
2230987da915Sopenharmony_ci{
2231987da915Sopenharmony_ci	struct ACTION_RECORD *action;
2232987da915Sopenharmony_ci	const LOG_RECORD *logr;
2233987da915Sopenharmony_ci	le32 id;
2234987da915Sopenharmony_ci	int actives;
2235987da915Sopenharmony_ci	BOOL more;
2236987da915Sopenharmony_ci	BOOL committed;
2237987da915Sopenharmony_ci
2238987da915Sopenharmony_ci	actives = 0;
2239987da915Sopenharmony_ci	do {
2240987da915Sopenharmony_ci		more = FALSE;
2241987da915Sopenharmony_ci		id = const_cpu_to_le32(0);
2242987da915Sopenharmony_ci		for (action=lastaction; action; action=action->prev) {
2243987da915Sopenharmony_ci			logr = &action->record;
2244987da915Sopenharmony_ci			if ((logr->redo_operation
2245987da915Sopenharmony_ci				== const_cpu_to_le16(ForgetTransaction))
2246987da915Sopenharmony_ci			    && !(action->flags & ACTION_TO_REDO)
2247987da915Sopenharmony_ci			    && !id) {
2248987da915Sopenharmony_ci				id = logr->transaction_id;
2249987da915Sopenharmony_ci				action->flags |= ACTION_TO_REDO;
2250987da915Sopenharmony_ci				if (optv)
2251987da915Sopenharmony_ci					printf("Marking transaction 0x%x\n",
2252987da915Sopenharmony_ci						(int)le32_to_cpu(id));
2253987da915Sopenharmony_ci			}
2254987da915Sopenharmony_ci			committed = ((s64)(sle64_to_cpu(logr->this_lsn)
2255987da915Sopenharmony_ci					- committed_lsn)) <= 0;
2256987da915Sopenharmony_ci			if (!logr->transaction_id
2257987da915Sopenharmony_ci			    && committed)
2258987da915Sopenharmony_ci				action->flags |= ACTION_TO_REDO;
2259987da915Sopenharmony_ci			if (id
2260987da915Sopenharmony_ci			    && (logr->transaction_id == id)
2261987da915Sopenharmony_ci			    && committed) {
2262987da915Sopenharmony_ci				action->flags |= ACTION_TO_REDO;
2263987da915Sopenharmony_ci				more = TRUE;
2264987da915Sopenharmony_ci			}
2265987da915Sopenharmony_ci		}
2266987da915Sopenharmony_ci	if (more)
2267987da915Sopenharmony_ci		actives++;
2268987da915Sopenharmony_ci	} while (more);
2269987da915Sopenharmony_ci		/*
2270987da915Sopenharmony_ci		 * Show unmarked (aborted) actions
2271987da915Sopenharmony_ci		 */
2272987da915Sopenharmony_ci	if (optv) {
2273987da915Sopenharmony_ci		for (action=lastaction; action; action=action->prev) {
2274987da915Sopenharmony_ci			logr = &action->record;
2275987da915Sopenharmony_ci			if (logr->transaction_id
2276987da915Sopenharmony_ci			   && !(action->flags & ACTION_TO_REDO))
2277987da915Sopenharmony_ci				printf("** Action %d was aborted\n",
2278987da915Sopenharmony_ci					(int)action->num);
2279987da915Sopenharmony_ci		}
2280987da915Sopenharmony_ci	}
2281987da915Sopenharmony_ci	if (optv && (actives > 1))
2282987da915Sopenharmony_ci		printf("%d active transactions in set\n",actives);
2283987da915Sopenharmony_ci}
2284987da915Sopenharmony_ci
2285987da915Sopenharmony_ci/*
2286987da915Sopenharmony_ci *		Enqueue an action and play the queued actions on end of set
2287987da915Sopenharmony_ci */
2288987da915Sopenharmony_ci
2289987da915Sopenharmony_cistatic TRISTATE enqueue_action(CONTEXT *ctx, const LOG_RECORD *logr,
2290987da915Sopenharmony_ci				int size, int num)
2291987da915Sopenharmony_ci{
2292987da915Sopenharmony_ci	struct ACTION_RECORD *action;
2293987da915Sopenharmony_ci	TRISTATE state;
2294987da915Sopenharmony_ci	int err;
2295987da915Sopenharmony_ci
2296987da915Sopenharmony_ci	err = 1;
2297987da915Sopenharmony_ci	state = T_ERR;
2298987da915Sopenharmony_ci		/* enqueue record */
2299987da915Sopenharmony_ci	action = (struct ACTION_RECORD*)
2300987da915Sopenharmony_ci			malloc(size + offsetof(struct ACTION_RECORD, record));
2301987da915Sopenharmony_ci	if (action) {
2302987da915Sopenharmony_ci		memcpy(&action->record, logr, size);
2303987da915Sopenharmony_ci		action->num = num;
2304987da915Sopenharmony_ci		action->flags = 0;
2305987da915Sopenharmony_ci		/* enqueue ahead of list, firstaction is the oldest one */
2306987da915Sopenharmony_ci		action->prev = (struct ACTION_RECORD*)NULL;
2307987da915Sopenharmony_ci		action->next = ctx->firstaction;
2308987da915Sopenharmony_ci		if (ctx->firstaction)
2309987da915Sopenharmony_ci			ctx->firstaction->prev = action;
2310987da915Sopenharmony_ci		else
2311987da915Sopenharmony_ci			ctx->lastaction = action;
2312987da915Sopenharmony_ci		ctx->firstaction = action;
2313987da915Sopenharmony_ci		err = 0;
2314987da915Sopenharmony_ci		state = T_OK;
2315987da915Sopenharmony_ci		if ((optp || optu)
2316987da915Sopenharmony_ci		    && (logr->record_type == LOG_CHECKPOINT)) {
2317987da915Sopenharmony_ci			/* if chkp process queue, and increment count */
2318987da915Sopenharmony_ci			playedactions++;
2319987da915Sopenharmony_ci			if (playedactions <= playcount) {
2320987da915Sopenharmony_ci				if (optv)
2321987da915Sopenharmony_ci					printf("* Refreshing attributes\n");
2322987da915Sopenharmony_ci				err = refresh_attributes(ctx->firstaction);
2323987da915Sopenharmony_ci				if (optv)
2324987da915Sopenharmony_ci					printf("* Undoing transaction set %d"
2325987da915Sopenharmony_ci						" (actions %d->%d)\n",
2326987da915Sopenharmony_ci						(int)playedactions,
2327987da915Sopenharmony_ci						(int)ctx->lastaction->num,
2328987da915Sopenharmony_ci						(int)ctx->firstaction->num);
2329987da915Sopenharmony_ci				err = play_undos(ctx->vol, ctx->lastaction);
2330987da915Sopenharmony_ci				if (err)
2331987da915Sopenharmony_ci					printf("* Undoing transaction"
2332987da915Sopenharmony_ci							" set failed\n");
2333987da915Sopenharmony_ci			}
2334987da915Sopenharmony_ci			if (!err && optp && (playedactions == playcount)) {
2335987da915Sopenharmony_ci				if (optv)
2336987da915Sopenharmony_ci					printf("* Redoing transaction set %d"
2337987da915Sopenharmony_ci						" (actions %d->%d)\n",
2338987da915Sopenharmony_ci						(int)playedactions,
2339987da915Sopenharmony_ci						(int)ctx->firstaction->num,
2340987da915Sopenharmony_ci						(int)ctx->lastaction->num);
2341987da915Sopenharmony_ci				mark_transactions(ctx->lastaction);
2342987da915Sopenharmony_ci				err = play_redos(ctx->vol, ctx->firstaction);
2343987da915Sopenharmony_ci				if (err)
2344987da915Sopenharmony_ci					printf("* Redoing transaction"
2345987da915Sopenharmony_ci							" set failed\n");
2346987da915Sopenharmony_ci			}
2347987da915Sopenharmony_ci			if (err)
2348987da915Sopenharmony_ci				state = T_ERR;
2349987da915Sopenharmony_ci			else
2350987da915Sopenharmony_ci				if (playedactions == playcount)
2351987da915Sopenharmony_ci					state = T_DONE;
2352987da915Sopenharmony_ci				/* free queue */
2353987da915Sopenharmony_ci			while (ctx->firstaction) {
2354987da915Sopenharmony_ci				action = ctx->firstaction->next;
2355987da915Sopenharmony_ci				free(ctx->firstaction);
2356987da915Sopenharmony_ci				ctx->firstaction = action;
2357987da915Sopenharmony_ci			}
2358987da915Sopenharmony_ci			ctx->lastaction = (struct ACTION_RECORD*)NULL;
2359987da915Sopenharmony_ci 		}
2360987da915Sopenharmony_ci		if (opts
2361987da915Sopenharmony_ci		    && ((s64)(sle64_to_cpu(logr->this_lsn) - synced_lsn) <= 0)) {
2362987da915Sopenharmony_ci			if (optv)
2363987da915Sopenharmony_ci				printf("* Refreshing attributes\n");
2364987da915Sopenharmony_ci// should refresh backward ?
2365987da915Sopenharmony_ci			err = refresh_attributes(ctx->firstaction);
2366987da915Sopenharmony_ci			mark_transactions(ctx->lastaction);
2367987da915Sopenharmony_ci			if (!err) {
2368987da915Sopenharmony_ci				if (optv)
2369987da915Sopenharmony_ci					printf("* Syncing actions %d->%d\n",
2370987da915Sopenharmony_ci						(int)ctx->firstaction->num,
2371987da915Sopenharmony_ci						(int)ctx->lastaction->num);
2372987da915Sopenharmony_ci				err = play_redos(ctx->vol, ctx->firstaction);
2373987da915Sopenharmony_ci			}
2374987da915Sopenharmony_ci			if (err) {
2375987da915Sopenharmony_ci				printf("* Syncing actions failed\n");
2376987da915Sopenharmony_ci				state = T_ERR;
2377987da915Sopenharmony_ci			} else
2378987da915Sopenharmony_ci				state = T_DONE;
2379987da915Sopenharmony_ci		}
2380987da915Sopenharmony_ci	}
2381987da915Sopenharmony_ci	return (state);
2382987da915Sopenharmony_ci}
2383987da915Sopenharmony_ci
2384987da915Sopenharmony_ci
2385987da915Sopenharmony_cistatic void showheadrcrd(u32 blk, const RECORD_PAGE_HEADER *rph)
2386987da915Sopenharmony_ci{
2387987da915Sopenharmony_ci	s32 diff;
2388987da915Sopenharmony_ci
2389987da915Sopenharmony_ci	if (optv) {
2390987da915Sopenharmony_ci		printf("magic              %08lx\n",
2391987da915Sopenharmony_ci			(long)le32_to_cpu(rph->magic));
2392987da915Sopenharmony_ci		printf("usa_ofs            %04x\n",
2393987da915Sopenharmony_ci			(int)le16_to_cpu(rph->usa_ofs));
2394987da915Sopenharmony_ci		printf("usa_count          %04x\n",
2395987da915Sopenharmony_ci			(int)le16_to_cpu(rph->usa_count));
2396987da915Sopenharmony_ci		if (blk < 4)
2397987da915Sopenharmony_ci			printf("file_offset        %016llx\n",
2398987da915Sopenharmony_ci				(long long)sle64_to_cpu(rph->copy.file_offset));
2399987da915Sopenharmony_ci		else {
2400987da915Sopenharmony_ci			diff = sle64_to_cpu(rph->copy.last_lsn) - synced_lsn;
2401987da915Sopenharmony_ci			printf("last_lsn           %016llx"
2402987da915Sopenharmony_ci				" (synced%s%ld)\n",
2403987da915Sopenharmony_ci				(long long)sle64_to_cpu(rph->copy.last_lsn),
2404987da915Sopenharmony_ci				(diff < 0 ? "" : "+"),(long)diff);
2405987da915Sopenharmony_ci		}
2406987da915Sopenharmony_ci		printf("flags              %08lx\n",
2407987da915Sopenharmony_ci			(long)le32_to_cpu(rph->flags));
2408987da915Sopenharmony_ci		printf("page_count         %d\n",
2409987da915Sopenharmony_ci			(int)le16_to_cpu(rph->page_count));
2410987da915Sopenharmony_ci		printf("page_position      %d\n",
2411987da915Sopenharmony_ci			(int)le16_to_cpu(rph->page_position));
2412987da915Sopenharmony_ci		printf("next_record_offset %04x\n",
2413987da915Sopenharmony_ci			(int)le16_to_cpu(rph->next_record_offset));
2414987da915Sopenharmony_ci		printf("reserved4          %04x %04x %04x\n",
2415987da915Sopenharmony_ci			(int)le16_to_cpu(rph->reserved[0]),
2416987da915Sopenharmony_ci			(int)le16_to_cpu(rph->reserved[1]),
2417987da915Sopenharmony_ci			(int)le16_to_cpu(rph->reserved[2]));
2418987da915Sopenharmony_ci		diff = sle64_to_cpu(rph->last_end_lsn) - synced_lsn;
2419987da915Sopenharmony_ci		printf("last_end_lsn       %016llx (synced%s%ld)\n",
2420987da915Sopenharmony_ci			(long long)sle64_to_cpu(rph->last_end_lsn),
2421987da915Sopenharmony_ci			(diff < 0 ? "" : "+"),(long)diff);
2422987da915Sopenharmony_ci		printf("usn                %04x\n",
2423987da915Sopenharmony_ci			(int)getle16(rph,le16_to_cpu(rph->usa_ofs)));
2424987da915Sopenharmony_ci		printf("\n");
2425987da915Sopenharmony_ci	} else {
2426987da915Sopenharmony_ci		if (optt) {
2427987da915Sopenharmony_ci			const char *state;
2428987da915Sopenharmony_ci
2429987da915Sopenharmony_ci			state = commitment(sle64_to_cpu(rph->copy.last_lsn));
2430987da915Sopenharmony_ci			diff = sle64_to_cpu(rph->copy.last_lsn) - synced_lsn;
2431987da915Sopenharmony_ci			printf("   last        %016llx (synced%s%ld) %s\n",
2432987da915Sopenharmony_ci				(long long)sle64_to_cpu(rph->copy.last_lsn),
2433987da915Sopenharmony_ci				(diff < 0 ? "" : "+"),(long)diff, state);
2434987da915Sopenharmony_ci			state = commitment(sle64_to_cpu(rph->last_end_lsn));
2435987da915Sopenharmony_ci			diff = sle64_to_cpu(rph->last_end_lsn) - synced_lsn;
2436987da915Sopenharmony_ci			printf("   last_end    %016llx (synced%s%ld) %s\n",
2437987da915Sopenharmony_ci				(long long)sle64_to_cpu(rph->last_end_lsn),
2438987da915Sopenharmony_ci				(diff < 0 ? "" : "+"),(long)diff, state);
2439987da915Sopenharmony_ci		}
2440987da915Sopenharmony_ci	}
2441987da915Sopenharmony_ci}
2442987da915Sopenharmony_ci
2443987da915Sopenharmony_ci/*
2444987da915Sopenharmony_ci *		Analyze and display an action overlapping log blocks
2445987da915Sopenharmony_ci *
2446987da915Sopenharmony_ci *	Returns the position of first action in next block. If this is
2447987da915Sopenharmony_ci *	greater than a block size (for actions overlapping more than
2448987da915Sopenharmony_ci *	two blocks), then some blocks have to be skipped.
2449987da915Sopenharmony_ci *
2450987da915Sopenharmony_ci *	Returns 0 in case of error
2451987da915Sopenharmony_ci */
2452987da915Sopenharmony_ci
2453987da915Sopenharmony_cistatic u16 overlapshow(CONTEXT *ctx, u16 k, u32 blk, const struct BUFFER *buf,
2454987da915Sopenharmony_ci			const struct BUFFER *nextbuf)
2455987da915Sopenharmony_ci{
2456987da915Sopenharmony_ci	const LOG_RECORD *logr;
2457987da915Sopenharmony_ci	const char *data;
2458987da915Sopenharmony_ci	const char *nextdata;
2459987da915Sopenharmony_ci	char *fullrec;
2460987da915Sopenharmony_ci	u32 size;
2461987da915Sopenharmony_ci	u32 nextspace;
2462987da915Sopenharmony_ci	u32 space;
2463987da915Sopenharmony_ci	BOOL likely;
2464987da915Sopenharmony_ci	u16 blkheadsz;
2465987da915Sopenharmony_ci
2466987da915Sopenharmony_ci	data = buf->block.data;
2467987da915Sopenharmony_ci	logr = (const LOG_RECORD*)&data[k];
2468987da915Sopenharmony_ci	size = le32_to_cpu(logr->client_data_length) + LOG_RECORD_HEAD_SZ;
2469987da915Sopenharmony_ci	blkheadsz = buf->headsz;
2470987da915Sopenharmony_ci	if (nextbuf && (blk >= BASEBLKS)) {
2471987da915Sopenharmony_ci		nextdata = nextbuf->block.data;
2472987da915Sopenharmony_ci		space = blocksz - k;
2473987da915Sopenharmony_ci		nextspace = blocksz - blkheadsz;
2474987da915Sopenharmony_ci		if ((space >= LOG_RECORD_HEAD_SZ)
2475987da915Sopenharmony_ci		    && (size > space)) {
2476987da915Sopenharmony_ci			fullrec = (char*)malloc(size);
2477987da915Sopenharmony_ci			if (size <= (space + nextspace)) {
2478987da915Sopenharmony_ci				/* Overlap on two blocks */
2479987da915Sopenharmony_ci				memcpy(fullrec,&data[k],space);
2480987da915Sopenharmony_ci				memcpy(&fullrec[space],
2481987da915Sopenharmony_ci					nextdata + blkheadsz,
2482987da915Sopenharmony_ci					size - space);
2483987da915Sopenharmony_ci				likely = likelyop((LOG_RECORD*)fullrec);
2484987da915Sopenharmony_ci				actionnum++;
2485987da915Sopenharmony_ci				if (optv) {
2486987da915Sopenharmony_ci					printf("\nOverlapping record %u at 0x%x"
2487987da915Sopenharmony_ci						" size %d (next at 0x%x)\n",
2488987da915Sopenharmony_ci						(int)actionnum,(int)k,
2489987da915Sopenharmony_ci						(int)size, (int)(k + size));
2490987da915Sopenharmony_ci					printf("Overlap marked for block %ld"
2491987da915Sopenharmony_ci						" space %d likely %d\n",
2492987da915Sopenharmony_ci						(long)blk,(int)space,likely);
2493987da915Sopenharmony_ci				}
2494987da915Sopenharmony_ci				if (likely)
2495987da915Sopenharmony_ci					showlogr(ctx, k,
2496987da915Sopenharmony_ci						(LOG_RECORD*)fullrec);
2497987da915Sopenharmony_ci				else
2498987da915Sopenharmony_ci					printf("** Skipping unlikely"
2499987da915Sopenharmony_ci						" overlapping record\n");
2500987da915Sopenharmony_ci				k += size - blocksz + blkheadsz;
2501987da915Sopenharmony_ci			} else {
2502987da915Sopenharmony_ci				const struct BUFFER *midbuf;
2503987da915Sopenharmony_ci				int skip;
2504987da915Sopenharmony_ci				u32 next;
2505987da915Sopenharmony_ci				u32 pos;
2506987da915Sopenharmony_ci				int i;
2507987da915Sopenharmony_ci
2508987da915Sopenharmony_ci			/*
2509987da915Sopenharmony_ci			 * The maximum size of of log record is 131104
2510987da915Sopenharmony_ci			 * (when both offset and length are 65528 for
2511987da915Sopenharmony_ci			 * redo or undo).
2512987da915Sopenharmony_ci			 * So up to 33 log blocks (useful size 4032)
2513987da915Sopenharmony_ci			 * could be needed. However never both undo and
2514987da915Sopenharmony_ci			 * redo have been found big, and 17 should be
2515987da915Sopenharmony_ci			 * the real maximum.
2516987da915Sopenharmony_ci			 */
2517987da915Sopenharmony_ci				if (optv)
2518987da915Sopenharmony_ci					printf("More than two blocks required"
2519987da915Sopenharmony_ci						" (size %lu)\n",(long)size);
2520987da915Sopenharmony_ci				memcpy(fullrec,&data[k],space);
2521987da915Sopenharmony_ci
2522987da915Sopenharmony_ci				skip = (size - space - 1)/nextspace;
2523987da915Sopenharmony_ci				pos = space;
2524987da915Sopenharmony_ci				likely = TRUE;
2525987da915Sopenharmony_ci				for (i=1; (i<=skip) && likely; i++) {
2526987da915Sopenharmony_ci					midbuf = read_buffer(ctx, blk + i);
2527987da915Sopenharmony_ci					if (midbuf) {
2528987da915Sopenharmony_ci						memcpy(&fullrec[pos],
2529987da915Sopenharmony_ci							&midbuf->block
2530987da915Sopenharmony_ci							    .data[blkheadsz],
2531987da915Sopenharmony_ci							nextspace);
2532987da915Sopenharmony_ci						pos += nextspace;
2533987da915Sopenharmony_ci					} else
2534987da915Sopenharmony_ci						likely = FALSE;
2535987da915Sopenharmony_ci				}
2536987da915Sopenharmony_ci				if (pos >= size) {
2537987da915Sopenharmony_ci					printf("** Error : bad big overlap"
2538987da915Sopenharmony_ci						" pos %d size %d\n",
2539987da915Sopenharmony_ci						(int)pos,(int)size);
2540987da915Sopenharmony_ci					likely = FALSE;
2541987da915Sopenharmony_ci				}
2542987da915Sopenharmony_ci				midbuf = read_buffer(ctx, blk + skip + 1);
2543987da915Sopenharmony_ci				if (midbuf)
2544987da915Sopenharmony_ci					memcpy(&fullrec[pos],
2545987da915Sopenharmony_ci						&midbuf->block.data[blkheadsz],
2546987da915Sopenharmony_ci						size - pos);
2547987da915Sopenharmony_ci				else
2548987da915Sopenharmony_ci					likely = FALSE;
2549987da915Sopenharmony_ci				if (!likelyop((LOG_RECORD*)fullrec))
2550987da915Sopenharmony_ci					likely = FALSE;
2551987da915Sopenharmony_ci				actionnum++;
2552987da915Sopenharmony_ci				if (optv) {
2553987da915Sopenharmony_ci					printf("\nBig overlapping record %u at "
2554987da915Sopenharmony_ci						"0x%x size %u (next at 0x%x)\n",
2555987da915Sopenharmony_ci						(int)actionnum,(int)k,(int)size,
2556987da915Sopenharmony_ci						(int)(k + size));
2557987da915Sopenharmony_ci					printf("Overlap marked for block %ld"
2558987da915Sopenharmony_ci						" space %d likely %d\n",
2559987da915Sopenharmony_ci						(long)blk,(int)space,likely);
2560987da915Sopenharmony_ci				}
2561987da915Sopenharmony_ci				if (likely)
2562987da915Sopenharmony_ci					showlogr(ctx, k,
2563987da915Sopenharmony_ci						(LOG_RECORD*)fullrec);
2564987da915Sopenharmony_ci				else
2565987da915Sopenharmony_ci					printf("** Skipping unlikely"
2566987da915Sopenharmony_ci						" overlapping record\n");
2567987da915Sopenharmony_ci				/* next and skip are only for displaying */
2568987da915Sopenharmony_ci				next = (size - space) % nextspace
2569987da915Sopenharmony_ci							+ blkheadsz;
2570987da915Sopenharmony_ci				if ((blocksz - next) < LOG_RECORD_HEAD_SZ)
2571987da915Sopenharmony_ci					next = blkheadsz;
2572987da915Sopenharmony_ci				if (next == blkheadsz)
2573987da915Sopenharmony_ci					skip++;
2574987da915Sopenharmony_ci				if (optv)
2575987da915Sopenharmony_ci					printf("Next record expected in"
2576987da915Sopenharmony_ci						" block %lu index 0x%x\n",
2577987da915Sopenharmony_ci						(long)(blk + skip + 1),next);
2578987da915Sopenharmony_ci					/* Quick check, with no consequences */
2579987da915Sopenharmony_ci				if (firstrecord(skip,buf,buf) != next)
2580987da915Sopenharmony_ci					printf("** Error next != firstrecord"
2581987da915Sopenharmony_ci						" after block %d\n",blk);
2582987da915Sopenharmony_ci				k += size - blocksz + blkheadsz;
2583987da915Sopenharmony_ci			}
2584987da915Sopenharmony_ci			if (!likely)
2585987da915Sopenharmony_ci				k = 0;
2586987da915Sopenharmony_ci			else
2587987da915Sopenharmony_ci				if (!k)
2588987da915Sopenharmony_ci					printf("* Bad return from overlap()\n");
2589987da915Sopenharmony_ci			free(fullrec);
2590987da915Sopenharmony_ci		} else {
2591987da915Sopenharmony_ci			/* No conditions for overlap, usually a new session */
2592987da915Sopenharmony_ci			printf("* No block found overlapping on block %d\n",
2593987da915Sopenharmony_ci					(int)blk);
2594987da915Sopenharmony_ci			k = 0;
2595987da915Sopenharmony_ci		}
2596987da915Sopenharmony_ci	} else {
2597987da915Sopenharmony_ci		/* blocks 2, 3 and the last one have no next block */
2598987da915Sopenharmony_ci		k = 0;
2599987da915Sopenharmony_ci	}
2600987da915Sopenharmony_ci	return (k);
2601987da915Sopenharmony_ci}
2602987da915Sopenharmony_ci
2603987da915Sopenharmony_ci/*
2604987da915Sopenharmony_ci *		Analyze and forward display the actions in a log block
2605987da915Sopenharmony_ci *
2606987da915Sopenharmony_ci *	Returns the position of first action in next block. If this is
2607987da915Sopenharmony_ci *	greater than a block size, then some blocks have to be skipped.
2608987da915Sopenharmony_ci *
2609987da915Sopenharmony_ci *	Returns 0 in case of error
2610987da915Sopenharmony_ci */
2611987da915Sopenharmony_ci
2612987da915Sopenharmony_cistatic u16 forward_rcrd(CONTEXT *ctx, u32 blk, u16 pos,
2613987da915Sopenharmony_ci			const struct BUFFER *buf, const struct BUFFER *nextbuf)
2614987da915Sopenharmony_ci{
2615987da915Sopenharmony_ci	const RECORD_PAGE_HEADER *rph;
2616987da915Sopenharmony_ci	const LOG_RECORD *logr;
2617987da915Sopenharmony_ci	const char *data;
2618987da915Sopenharmony_ci	u16 k;
2619987da915Sopenharmony_ci	u16 endoff;
2620987da915Sopenharmony_ci	BOOL stop;
2621987da915Sopenharmony_ci
2622987da915Sopenharmony_ci	rph = &buf->block.record;
2623987da915Sopenharmony_ci	if (rph && (rph->magic == magic_RCRD)) {
2624987da915Sopenharmony_ci		data = buf->block.data;
2625987da915Sopenharmony_ci		showheadrcrd(blk, rph);
2626987da915Sopenharmony_ci		k = buf->headsz;
2627987da915Sopenharmony_ci		if ((k < pos) && (pos < blocksz)) {
2628987da915Sopenharmony_ci			k = ((pos - 1) | 7) + 1;
2629987da915Sopenharmony_ci		}
2630987da915Sopenharmony_ci// TODO check bad start > blocksz - 48
2631987da915Sopenharmony_ci		logr = (const LOG_RECORD*)&data[k];
2632987da915Sopenharmony_ci		stop = FALSE;
2633987da915Sopenharmony_ci		if (!likelyop(logr)) {
2634987da915Sopenharmony_ci			if (optv)
2635987da915Sopenharmony_ci				printf("* Bad start 0x%x for block %d\n",
2636987da915Sopenharmony_ci					(int)pos,(int)blk);
2637987da915Sopenharmony_ci			k = searchlikely(buf);
2638987da915Sopenharmony_ci			if ((k + sizeof(LOG_RECORD)) > blocksz) {
2639987da915Sopenharmony_ci				printf("No likely full record in block %lu\n",
2640987da915Sopenharmony_ci						(unsigned long)blk);
2641987da915Sopenharmony_ci		      /* there can be a partial one */
2642987da915Sopenharmony_ci				k = le16_to_cpu(rph->next_record_offset);
2643987da915Sopenharmony_ci				if ((k < (u16)sizeof(RECORD_PAGE_HEADER))
2644987da915Sopenharmony_ci				    || ((blocksz - k) < LOG_RECORD_HEAD_SZ))
2645987da915Sopenharmony_ci					stop = TRUE;
2646987da915Sopenharmony_ci			} else {
2647987da915Sopenharmony_ci				if (optv)
2648987da915Sopenharmony_ci					printf("First record computed at"
2649987da915Sopenharmony_ci						" offset 0x%x\n", (int)k);
2650987da915Sopenharmony_ci			}
2651987da915Sopenharmony_ci		}
2652987da915Sopenharmony_ci		while (!stop) {
2653987da915Sopenharmony_ci			s32 size;
2654987da915Sopenharmony_ci
2655987da915Sopenharmony_ci			logr = (const LOG_RECORD*)&data[k];
2656987da915Sopenharmony_ci			size = le32_to_cpu(logr->client_data_length)
2657987da915Sopenharmony_ci						+ LOG_RECORD_HEAD_SZ;
2658987da915Sopenharmony_ci			if ((size < MINRECSIZE)
2659987da915Sopenharmony_ci			    || (size > MAXRECSIZE)
2660987da915Sopenharmony_ci			    || (size & 7)) {
2661987da915Sopenharmony_ci				printf("** Bad record size %ld in block %ld"
2662987da915Sopenharmony_ci					" offset 0x%x\n",
2663987da915Sopenharmony_ci					(long)size, (long)buf->num, (int)k);
2664987da915Sopenharmony_ci				showlogr(ctx, k, logr);
2665987da915Sopenharmony_ci				k = 0;
2666987da915Sopenharmony_ci				stop = TRUE;
2667987da915Sopenharmony_ci			} else {
2668987da915Sopenharmony_ci				endoff = le16_to_cpu(rph->next_record_offset);
2669987da915Sopenharmony_ci				if (((u32)(k + size) <= blocksz)
2670987da915Sopenharmony_ci				    && ((u32)(k + size) <= endoff)) {
2671987da915Sopenharmony_ci					actionnum++;
2672987da915Sopenharmony_ci					if (optv) {
2673987da915Sopenharmony_ci						printf("\n* log action %u at"
2674987da915Sopenharmony_ci							" 0x%x size %d (next"
2675987da915Sopenharmony_ci							" at 0x%x)\n",
2676987da915Sopenharmony_ci							actionnum,k,size,
2677987da915Sopenharmony_ci							k + size);
2678987da915Sopenharmony_ci					}
2679987da915Sopenharmony_ci					showlogr(ctx, k, logr);
2680987da915Sopenharmony_ci					if (!logr->client_data_length) {
2681987da915Sopenharmony_ci						printf("** Bad"
2682987da915Sopenharmony_ci						    " client_data_length\n");
2683987da915Sopenharmony_ci						stop = TRUE;
2684987da915Sopenharmony_ci					}
2685987da915Sopenharmony_ci					k += size;
2686987da915Sopenharmony_ci					if ((blocksz - k)
2687987da915Sopenharmony_ci							< LOG_RECORD_HEAD_SZ) {
2688987da915Sopenharmony_ci						k = nextbuf->headsz;
2689987da915Sopenharmony_ci						stop = TRUE;
2690987da915Sopenharmony_ci					}
2691987da915Sopenharmony_ci				} else {
2692987da915Sopenharmony_ci					k = overlapshow(ctx, k, blk,
2693987da915Sopenharmony_ci								buf, nextbuf);
2694987da915Sopenharmony_ci					stop = TRUE;
2695987da915Sopenharmony_ci	       			}
2696987da915Sopenharmony_ci	    		}
2697987da915Sopenharmony_ci		}
2698987da915Sopenharmony_ci	} else {
2699987da915Sopenharmony_ci		printf("** Not a RCRD record, MAGIC 0x%08lx\n",
2700987da915Sopenharmony_ci			(long)le32_to_cpu(rph->magic));
2701987da915Sopenharmony_ci		k = 0;
2702987da915Sopenharmony_ci	}
2703987da915Sopenharmony_ci	return (k);
2704987da915Sopenharmony_ci}
2705987da915Sopenharmony_ci
2706987da915Sopenharmony_ci/*
2707987da915Sopenharmony_ci *                Display a restart page
2708987da915Sopenharmony_ci */
2709987da915Sopenharmony_ci
2710987da915Sopenharmony_cistatic void showrest(const RESTART_PAGE_HEADER *rest)
2711987da915Sopenharmony_ci{
2712987da915Sopenharmony_ci	const RESTART_AREA *resa;
2713987da915Sopenharmony_ci	const LOG_CLIENT_RECORD *rcli;
2714987da915Sopenharmony_ci	const char *data;
2715987da915Sopenharmony_ci
2716987da915Sopenharmony_ci	data = (const char*)rest;
2717987da915Sopenharmony_ci	if ((rest->magic == magic_RSTR)
2718987da915Sopenharmony_ci			|| (rest->magic == magic_CHKD)) {
2719987da915Sopenharmony_ci		if (optv) {
2720987da915Sopenharmony_ci			printf("magic                  %08lx\n",
2721987da915Sopenharmony_ci				(long)le32_to_cpu(rest->magic));
2722987da915Sopenharmony_ci			printf("usa_ofs                %04x\n",
2723987da915Sopenharmony_ci				(int)le16_to_cpu(rest->usa_ofs));
2724987da915Sopenharmony_ci			printf("usa_count              %04x\n",
2725987da915Sopenharmony_ci				(int)le16_to_cpu(rest->usa_count));
2726987da915Sopenharmony_ci			printf("chkdsk_lsn             %016llx\n",
2727987da915Sopenharmony_ci				(long long)sle64_to_cpu(rest->chkdsk_lsn));
2728987da915Sopenharmony_ci			printf("system_page_size       %08lx\n",
2729987da915Sopenharmony_ci				(long)le32_to_cpu(rest->system_page_size));
2730987da915Sopenharmony_ci			printf("log_page_size          %08lx\n",
2731987da915Sopenharmony_ci				(long)le32_to_cpu(rest->log_page_size));
2732987da915Sopenharmony_ci			printf("restart_area_offset    %04x\n",
2733987da915Sopenharmony_ci				(int)le16_to_cpu(rest->restart_area_offset));
2734987da915Sopenharmony_ci			printf("minor_vers             %d\n",
2735987da915Sopenharmony_ci				(int)sle16_to_cpu(rest->minor_ver));
2736987da915Sopenharmony_ci			printf("major_vers             %d\n",
2737987da915Sopenharmony_ci				(int)sle16_to_cpu(rest->major_ver));
2738987da915Sopenharmony_ci			printf("usn                    %04x\n",
2739987da915Sopenharmony_ci				(int)le16_to_cpu(rest->usn));
2740987da915Sopenharmony_ci			printf("\n");
2741987da915Sopenharmony_ci		} else {
2742987da915Sopenharmony_ci			if (optt)
2743987da915Sopenharmony_ci				printf("    chkdsk         %016llx\n",
2744987da915Sopenharmony_ci				    (long long)sle64_to_cpu(rest->chkdsk_lsn));
2745987da915Sopenharmony_ci		}
2746987da915Sopenharmony_ci		resa = (const RESTART_AREA*)
2747987da915Sopenharmony_ci				&data[le16_to_cpu(rest->restart_area_offset)];
2748987da915Sopenharmony_ci		if (optv) {
2749987da915Sopenharmony_ci			printf("current_lsn            %016llx\n",
2750987da915Sopenharmony_ci				(long long)sle64_to_cpu(resa->current_lsn));
2751987da915Sopenharmony_ci			printf("log_clients            %04x\n",
2752987da915Sopenharmony_ci				(int)le16_to_cpu(resa->log_clients));
2753987da915Sopenharmony_ci			printf("client_free_list       %04x\n",
2754987da915Sopenharmony_ci				(int)le16_to_cpu(resa->client_free_list));
2755987da915Sopenharmony_ci			printf("client_in_use_list     %04x\n",
2756987da915Sopenharmony_ci				(int)le16_to_cpu(resa->client_in_use_list));
2757987da915Sopenharmony_ci			printf("flags                  %04x\n",
2758987da915Sopenharmony_ci				(int)le16_to_cpu(resa->flags));
2759987da915Sopenharmony_ci			printf("seq_number_bits        %08lx\n",
2760987da915Sopenharmony_ci				(long)le32_to_cpu(resa->seq_number_bits));
2761987da915Sopenharmony_ci			printf("restart_area_length    %04x\n",
2762987da915Sopenharmony_ci				(int)le16_to_cpu(resa->restart_area_length));
2763987da915Sopenharmony_ci			printf("client_array_offset    %04x\n",
2764987da915Sopenharmony_ci				(int)le16_to_cpu(resa->client_array_offset));
2765987da915Sopenharmony_ci			printf("file_size              %016llx\n",
2766987da915Sopenharmony_ci				(long long)sle64_to_cpu(resa->file_size));
2767987da915Sopenharmony_ci			printf("last_lsn_data_len      %08lx\n",
2768987da915Sopenharmony_ci				(long)le32_to_cpu(resa->last_lsn_data_length));
2769987da915Sopenharmony_ci			printf("record_length          %04x\n",
2770987da915Sopenharmony_ci				(int)le16_to_cpu(resa->log_record_header_length));
2771987da915Sopenharmony_ci			printf("log_page_data_offs     %04x\n",
2772987da915Sopenharmony_ci				(int)le16_to_cpu(resa->log_page_data_offset));
2773987da915Sopenharmony_ci			printf("restart_log_open_count %08lx\n",
2774987da915Sopenharmony_ci				(long)le32_to_cpu(resa->restart_log_open_count));
2775987da915Sopenharmony_ci			printf("\n");
2776987da915Sopenharmony_ci		} else {
2777987da915Sopenharmony_ci			if (optt)
2778987da915Sopenharmony_ci				printf("    latest         %016llx\n",
2779987da915Sopenharmony_ci				    (long long)sle64_to_cpu(resa->current_lsn));
2780987da915Sopenharmony_ci		}
2781987da915Sopenharmony_ci
2782987da915Sopenharmony_ci		rcli = (const LOG_CLIENT_RECORD*)
2783987da915Sopenharmony_ci				&data[le16_to_cpu(rest->restart_area_offset)
2784987da915Sopenharmony_ci				+ le16_to_cpu(resa->client_array_offset)];
2785987da915Sopenharmony_ci		if (optv) {
2786987da915Sopenharmony_ci			printf("oldest_lsn             %016llx\n",
2787987da915Sopenharmony_ci				(long long)sle64_to_cpu(rcli->oldest_lsn));
2788987da915Sopenharmony_ci			printf("client_restart_lsn     %016llx\n",
2789987da915Sopenharmony_ci				(long long)sle64_to_cpu(rcli->client_restart_lsn));
2790987da915Sopenharmony_ci			printf("prev_client            %04x\n",
2791987da915Sopenharmony_ci				(int)le16_to_cpu(rcli->prev_client));
2792987da915Sopenharmony_ci			printf("next_client            %04x\n",
2793987da915Sopenharmony_ci				(int)le16_to_cpu(rcli->next_client));
2794987da915Sopenharmony_ci			printf("seq_number             %04x\n",
2795987da915Sopenharmony_ci				(int)le16_to_cpu(rcli->seq_number));
2796987da915Sopenharmony_ci			printf("client_name_length     %08x\n",
2797987da915Sopenharmony_ci				(int)le32_to_cpu(rcli->client_name_length));
2798987da915Sopenharmony_ci			showname("client_name            ",
2799987da915Sopenharmony_ci				(const char*)rcli->client_name,
2800987da915Sopenharmony_ci				le32_to_cpu(rcli->client_name_length) >> 1);
2801987da915Sopenharmony_ci		} else {
2802987da915Sopenharmony_ci			if (optt) {
2803987da915Sopenharmony_ci				printf("    synced         %016llx\n",
2804987da915Sopenharmony_ci					(long long)sle64_to_cpu(
2805987da915Sopenharmony_ci						rcli->oldest_lsn));
2806987da915Sopenharmony_ci				printf("    committed      %016llx\n",
2807987da915Sopenharmony_ci					(long long)sle64_to_cpu(
2808987da915Sopenharmony_ci						rcli->client_restart_lsn));
2809987da915Sopenharmony_ci			}
2810987da915Sopenharmony_ci		}
2811987da915Sopenharmony_ci	} else
2812987da915Sopenharmony_ci		printf("Not a RSTR or CHKD record, MAGIC 0x%08lx\n",
2813987da915Sopenharmony_ci			(long)le32_to_cpu(rest->magic));
2814987da915Sopenharmony_ci}
2815987da915Sopenharmony_ci
2816987da915Sopenharmony_cistatic BOOL dorest(CONTEXT *ctx, unsigned long blk,
2817987da915Sopenharmony_ci			const RESTART_PAGE_HEADER *rph, BOOL initial)
2818987da915Sopenharmony_ci{
2819987da915Sopenharmony_ci	const RESTART_AREA *resa;
2820987da915Sopenharmony_ci	const LOG_CLIENT_RECORD *rcli;
2821987da915Sopenharmony_ci	const char *data;
2822987da915Sopenharmony_ci	s64 diff;
2823987da915Sopenharmony_ci	int offs;
2824987da915Sopenharmony_ci	int size;
2825987da915Sopenharmony_ci	BOOL change;
2826987da915Sopenharmony_ci	BOOL dirty;
2827987da915Sopenharmony_ci
2828987da915Sopenharmony_ci	data = (const char*)rph;
2829987da915Sopenharmony_ci	offs = le16_to_cpu(rph->restart_area_offset);
2830987da915Sopenharmony_ci	resa = (const RESTART_AREA*)&data[offs];
2831987da915Sopenharmony_ci	rcli = (const LOG_CLIENT_RECORD*)&data[offs
2832987da915Sopenharmony_ci				+ le16_to_cpu(resa->client_array_offset)];
2833987da915Sopenharmony_ci	if (initial) {
2834987da915Sopenharmony_ci		/* Information from block initially found best */
2835987da915Sopenharmony_ci		latest_lsn = sle64_to_cpu(resa->current_lsn);
2836987da915Sopenharmony_ci		committed_lsn = sle64_to_cpu(rcli->client_restart_lsn);
2837987da915Sopenharmony_ci		synced_lsn = sle64_to_cpu(rcli->oldest_lsn);
2838987da915Sopenharmony_ci		memcpy(&log_header, rph,
2839987da915Sopenharmony_ci				sizeof(RESTART_PAGE_HEADER));
2840987da915Sopenharmony_ci		offs = le16_to_cpu(log_header.restart_area_offset);
2841987da915Sopenharmony_ci		memcpy(&restart, &data[offs],
2842987da915Sopenharmony_ci				sizeof(RESTART_AREA));
2843987da915Sopenharmony_ci		offs += le16_to_cpu(restart.client_array_offset);
2844987da915Sopenharmony_ci		memcpy(&client, &data[offs],
2845987da915Sopenharmony_ci				sizeof(LOG_CLIENT_RECORD));
2846987da915Sopenharmony_ci		dirty = !(resa->flags & RESTART_VOLUME_IS_CLEAN);
2847987da915Sopenharmony_ci		if (optv || optt)
2848987da915Sopenharmony_ci			printf("* Using initial restart page,"
2849987da915Sopenharmony_ci				" syncing from 0x%llx, %s\n",
2850987da915Sopenharmony_ci				(long long)synced_lsn,
2851987da915Sopenharmony_ci				(dirty ? "dirty" : "clean"));
2852987da915Sopenharmony_ci			 /* Get the block page size */
2853987da915Sopenharmony_ci		blocksz = le32_to_cpu(rph->log_page_size);
2854987da915Sopenharmony_ci		if (optv)
2855987da915Sopenharmony_ci			printf("* Block size %ld bytes\n", (long)blocksz);
2856987da915Sopenharmony_ci		blockbits = 1;
2857987da915Sopenharmony_ci		while ((u32)(1 << blockbits) < blocksz)
2858987da915Sopenharmony_ci			blockbits++;
2859987da915Sopenharmony_ci	} else {
2860987da915Sopenharmony_ci		size = offs + le16_to_cpu(resa->restart_area_length);
2861987da915Sopenharmony_ci		if (optv) {
2862987da915Sopenharmony_ci			if (optv >= 2)
2863987da915Sopenharmony_ci				hexdump(data,size);
2864987da915Sopenharmony_ci			printf("* RSTR in block %ld 0x%lx (addr 0x%llx)\n",
2865987da915Sopenharmony_ci					(long)blk,(long)blk,
2866987da915Sopenharmony_ci					(long long)loclogblk(ctx, blk));
2867987da915Sopenharmony_ci		} else {
2868987da915Sopenharmony_ci			if (optt)
2869987da915Sopenharmony_ci				printf("restart %ld\n",(long)blk);
2870987da915Sopenharmony_ci		}
2871987da915Sopenharmony_ci		showrest(rph);
2872987da915Sopenharmony_ci		/* Information from an older restart block if requested */
2873987da915Sopenharmony_ci		dirty = !(restart.flags & RESTART_VOLUME_IS_CLEAN);
2874987da915Sopenharmony_ci		diff = sle64_to_cpu(rcli->client_restart_lsn) - committed_lsn;
2875987da915Sopenharmony_ci		if (ctx->vol) {
2876987da915Sopenharmony_ci			change = (opts > 1) && (diff < 0);
2877987da915Sopenharmony_ci		} else {
2878987da915Sopenharmony_ci			change = (opts > 1 ? diff < 0 : diff > 0);
2879987da915Sopenharmony_ci		}
2880987da915Sopenharmony_ci		if (change) {
2881987da915Sopenharmony_ci			committed_lsn = sle64_to_cpu(rcli->client_restart_lsn);
2882987da915Sopenharmony_ci			synced_lsn = sle64_to_cpu(rcli->oldest_lsn);
2883987da915Sopenharmony_ci			latest_lsn = sle64_to_cpu(resa->current_lsn);
2884987da915Sopenharmony_ci			memcpy(&log_header, rph,
2885987da915Sopenharmony_ci					sizeof(RESTART_PAGE_HEADER));
2886987da915Sopenharmony_ci			offs = le16_to_cpu(log_header.restart_area_offset);
2887987da915Sopenharmony_ci			memcpy(&restart, &data[offs],
2888987da915Sopenharmony_ci					sizeof(RESTART_AREA));
2889987da915Sopenharmony_ci			offs += le16_to_cpu(restart.client_array_offset);
2890987da915Sopenharmony_ci			memcpy(&client, &data[offs],
2891987da915Sopenharmony_ci					sizeof(LOG_CLIENT_RECORD));
2892987da915Sopenharmony_ci			dirty = !(resa->flags & RESTART_VOLUME_IS_CLEAN);
2893987da915Sopenharmony_ci			if (optv || optt)
2894987da915Sopenharmony_ci				printf("* Using %s restart page,"
2895987da915Sopenharmony_ci					" syncing from 0x%llx, %s\n",
2896987da915Sopenharmony_ci					(diff < 0 ? "older" : "newer"),
2897987da915Sopenharmony_ci					(long long)synced_lsn,
2898987da915Sopenharmony_ci					(dirty ? "dirty" : "clean"));
2899987da915Sopenharmony_ci		}
2900987da915Sopenharmony_ci	}
2901987da915Sopenharmony_ci	restart_lsn = synced_lsn;
2902987da915Sopenharmony_ci	offset_mask = ((u64)1 << (64 - le32_to_cpu(restart.seq_number_bits)))
2903987da915Sopenharmony_ci				- (1 << (blockbits - 3));
2904987da915Sopenharmony_ci	return (dirty);
2905987da915Sopenharmony_ci}
2906987da915Sopenharmony_ci
2907987da915Sopenharmony_ci/*
2908987da915Sopenharmony_ci *		Read and process the first restart block
2909987da915Sopenharmony_ci *
2910987da915Sopenharmony_ci *	In full mode, both restart page are silently analyzed by the
2911987da915Sopenharmony_ci *	library and the most recent readable one is used to define the
2912987da915Sopenharmony_ci *	sync parameters.
2913987da915Sopenharmony_ci *
2914987da915Sopenharmony_ci *	Returns the first restart buffer
2915987da915Sopenharmony_ci *		or NULL if the restart block is not valid
2916987da915Sopenharmony_ci */
2917987da915Sopenharmony_ci
2918987da915Sopenharmony_ci
2919987da915Sopenharmony_cistatic const struct BUFFER *read_restart(CONTEXT *ctx)
2920987da915Sopenharmony_ci{
2921987da915Sopenharmony_ci	const struct BUFFER *buf;
2922987da915Sopenharmony_ci	BOOL bad;
2923987da915Sopenharmony_ci	int blk;
2924987da915Sopenharmony_ci	int major, minor;
2925987da915Sopenharmony_ci
2926987da915Sopenharmony_ci	bad = FALSE;
2927987da915Sopenharmony_ci	for (blk=0; blk<BASEBLKS2; blk++)
2928987da915Sopenharmony_ci		redirect[blk] = 0;
2929987da915Sopenharmony_ci	log_major = 0; /* needed for reading into a buffer */
2930987da915Sopenharmony_ci	if (ctx->vol) {
2931987da915Sopenharmony_ci		RESTART_PAGE_HEADER *rph;
2932987da915Sopenharmony_ci
2933987da915Sopenharmony_ci		rph = (RESTART_PAGE_HEADER*)NULL;
2934987da915Sopenharmony_ci		/* Full mode : use the restart page selected by the library */
2935987da915Sopenharmony_ci		if (ntfs_check_logfile(log_na, &rph)) {
2936987da915Sopenharmony_ci			/* rph is left unchanged for a wiped out log file */
2937987da915Sopenharmony_ci			if (rph) {
2938987da915Sopenharmony_ci				dorest(ctx, 0, rph, TRUE);
2939987da915Sopenharmony_ci				free(rph);
2940987da915Sopenharmony_ci				buf = read_buffer(ctx,0);
2941987da915Sopenharmony_ci			} else {
2942987da915Sopenharmony_ci				buf = (const struct BUFFER*)NULL;
2943987da915Sopenharmony_ci				printf("** The log file has been wiped out\n");
2944987da915Sopenharmony_ci			}
2945987da915Sopenharmony_ci		} else {
2946987da915Sopenharmony_ci			buf = (const struct BUFFER*)NULL;
2947987da915Sopenharmony_ci			printf("** Could not get any restart page\n");
2948987da915Sopenharmony_ci		}
2949987da915Sopenharmony_ci	} else {
2950987da915Sopenharmony_ci		/* Reduced mode : rely on first restart page */
2951987da915Sopenharmony_ci		blockbits = BLOCKBITS;	/* Until the correct value is read */
2952987da915Sopenharmony_ci		blocksz = 1L << blockbits;
2953987da915Sopenharmony_ci		buf = read_buffer(ctx,0);
2954987da915Sopenharmony_ci	}
2955987da915Sopenharmony_ci	if (buf) {
2956987da915Sopenharmony_ci		NTFS_RECORD_TYPES magic;
2957987da915Sopenharmony_ci
2958987da915Sopenharmony_ci		magic = buf->block.restart.magic;
2959987da915Sopenharmony_ci		switch (magic) {
2960987da915Sopenharmony_ci		case magic_RSTR :
2961987da915Sopenharmony_ci			break;
2962987da915Sopenharmony_ci		case magic_CHKD :
2963987da915Sopenharmony_ci			printf("** The log file has been obsoleted by chkdsk\n");
2964987da915Sopenharmony_ci			bad = TRUE;
2965987da915Sopenharmony_ci			break;
2966987da915Sopenharmony_ci		case magic_empty :
2967987da915Sopenharmony_ci			printf("** The log file has been wiped out\n");
2968987da915Sopenharmony_ci			bad = TRUE;
2969987da915Sopenharmony_ci			break;
2970987da915Sopenharmony_ci		default :
2971987da915Sopenharmony_ci			printf("** Invalid restart block\n");
2972987da915Sopenharmony_ci			bad = TRUE;
2973987da915Sopenharmony_ci			break;
2974987da915Sopenharmony_ci		}
2975987da915Sopenharmony_ci		if (!bad && !ctx->vol)
2976987da915Sopenharmony_ci			dorest(ctx, 0, &buf->block.restart, TRUE);
2977987da915Sopenharmony_ci		major = sle16_to_cpu(buf->block.restart.major_ver);
2978987da915Sopenharmony_ci		minor = sle16_to_cpu(buf->block.restart.minor_ver);
2979987da915Sopenharmony_ci		if ((major == 2) && (minor == 0)) {
2980987da915Sopenharmony_ci			if (!optk) {
2981987da915Sopenharmony_ci				printf("** Fast restart mode detected,"
2982987da915Sopenharmony_ci						" data could be lost\n");
2983987da915Sopenharmony_ci				printf("   Use option --kill-fast-restart"
2984987da915Sopenharmony_ci						" to bypass\n");
2985987da915Sopenharmony_ci				bad = TRUE;
2986987da915Sopenharmony_ci			}
2987987da915Sopenharmony_ci		} else
2988987da915Sopenharmony_ci			if ((major != 1) || (minor != 1)) {
2989987da915Sopenharmony_ci				printf("** Unsupported $LogFile version %d.%d\n",
2990987da915Sopenharmony_ci					major, minor);
2991987da915Sopenharmony_ci				bad = TRUE;
2992987da915Sopenharmony_ci			}
2993987da915Sopenharmony_ci		log_major = major;
2994987da915Sopenharmony_ci		if (bad) {
2995987da915Sopenharmony_ci			buf = (const struct BUFFER*)NULL;
2996987da915Sopenharmony_ci		}
2997987da915Sopenharmony_ci	}
2998987da915Sopenharmony_ci	return (buf);
2999987da915Sopenharmony_ci}
3000987da915Sopenharmony_ci
3001987da915Sopenharmony_ci/*
3002987da915Sopenharmony_ci *		Mark the logfile as synced
3003987da915Sopenharmony_ci */
3004987da915Sopenharmony_ci
3005987da915Sopenharmony_cistatic int reset_logfile(CONTEXT *ctx __attribute__((unused)))
3006987da915Sopenharmony_ci{
3007987da915Sopenharmony_ci	char *buffer;
3008987da915Sopenharmony_ci	int off;
3009987da915Sopenharmony_ci	int err;
3010987da915Sopenharmony_ci
3011987da915Sopenharmony_ci	err = 1;
3012987da915Sopenharmony_ci	buffer = (char*)malloc(blocksz);
3013987da915Sopenharmony_ci	if (buffer) {
3014987da915Sopenharmony_ci		memset(buffer, 0, blocksz);
3015987da915Sopenharmony_ci		restart.client_in_use_list = LOGFILE_NO_CLIENT;
3016987da915Sopenharmony_ci		restart.flags |= RESTART_VOLUME_IS_CLEAN;
3017987da915Sopenharmony_ci		client.oldest_lsn = cpu_to_sle64(restart_lsn);
3018987da915Sopenharmony_ci		/* Set $LogFile version to 1.1 so that volume can be mounted */
3019987da915Sopenharmony_ci		log_header.major_ver = const_cpu_to_sle16(1);
3020987da915Sopenharmony_ci		log_header.minor_ver = const_cpu_to_sle16(1);
3021987da915Sopenharmony_ci		memcpy(buffer, &log_header,
3022987da915Sopenharmony_ci					sizeof(RESTART_PAGE_HEADER));
3023987da915Sopenharmony_ci		off = le16_to_cpu(log_header.restart_area_offset);
3024987da915Sopenharmony_ci		memcpy(&buffer[off], &restart,
3025987da915Sopenharmony_ci					sizeof(RESTART_AREA));
3026987da915Sopenharmony_ci		off += le16_to_cpu(restart.client_array_offset);
3027987da915Sopenharmony_ci		memcpy(&buffer[off], &client,
3028987da915Sopenharmony_ci					sizeof(LOG_CLIENT_RECORD));
3029987da915Sopenharmony_ci		if (!ntfs_mst_pre_write_fixup((NTFS_RECORD*)buffer, blocksz)
3030987da915Sopenharmony_ci		    && (ntfs_attr_pwrite(log_na, 0,
3031987da915Sopenharmony_ci                		blocksz, buffer) == blocksz)
3032987da915Sopenharmony_ci		    && (ntfs_attr_pwrite(log_na, (u64)1 << blockbits,
3033987da915Sopenharmony_ci                		blocksz, buffer) == blocksz))
3034987da915Sopenharmony_ci			err = 0;
3035987da915Sopenharmony_ci		free(buffer);
3036987da915Sopenharmony_ci	}
3037987da915Sopenharmony_ci	return (err);
3038987da915Sopenharmony_ci}
3039987da915Sopenharmony_ci
3040987da915Sopenharmony_ci/*
3041987da915Sopenharmony_ci *		Determine the most recent valid record block
3042987da915Sopenharmony_ci */
3043987da915Sopenharmony_ci
3044987da915Sopenharmony_cistatic const struct BUFFER *best_start(const struct BUFFER *buf,
3045987da915Sopenharmony_ci				const struct BUFFER *altbuf)
3046987da915Sopenharmony_ci{
3047987da915Sopenharmony_ci	const struct BUFFER *best;
3048987da915Sopenharmony_ci	const RECORD_PAGE_HEADER *head;
3049987da915Sopenharmony_ci	const RECORD_PAGE_HEADER *althead;
3050987da915Sopenharmony_ci	s64 diff;
3051987da915Sopenharmony_ci
3052987da915Sopenharmony_ci	if (!buf || !altbuf)
3053987da915Sopenharmony_ci		best = (buf ? buf : altbuf);
3054987da915Sopenharmony_ci	else {
3055987da915Sopenharmony_ci		head = &buf->block.record;
3056987da915Sopenharmony_ci		althead = &altbuf->block.record;
3057987da915Sopenharmony_ci		/* determine most recent, caring for wraparounds */
3058987da915Sopenharmony_ci		diff = sle64_to_cpu(althead->last_end_lsn)
3059987da915Sopenharmony_ci					- sle64_to_cpu(head->last_end_lsn);
3060987da915Sopenharmony_ci		if (diff > 0)
3061987da915Sopenharmony_ci			best = altbuf;
3062987da915Sopenharmony_ci		else
3063987da915Sopenharmony_ci			best = buf;
3064987da915Sopenharmony_ci	}
3065987da915Sopenharmony_ci	if (best && (best->block.record.magic != magic_RCRD))
3066987da915Sopenharmony_ci		best = (const struct BUFFER*)NULL;
3067987da915Sopenharmony_ci	return (best);
3068987da915Sopenharmony_ci}
3069987da915Sopenharmony_ci
3070987da915Sopenharmony_ci/*
3071987da915Sopenharmony_ci *                 Interpret the boot data
3072987da915Sopenharmony_ci *
3073987da915Sopenharmony_ci *	Probably not needed any more, use ctx->vol
3074987da915Sopenharmony_ci */
3075987da915Sopenharmony_ci
3076987da915Sopenharmony_cistatic BOOL getboot(const char *buf)
3077987da915Sopenharmony_ci{
3078987da915Sopenharmony_ci	u64 sectors;
3079987da915Sopenharmony_ci	u64 clusters;
3080987da915Sopenharmony_ci	u16 sectpercluster;
3081987da915Sopenharmony_ci	BOOL ok;
3082987da915Sopenharmony_ci
3083987da915Sopenharmony_ci	ok = TRUE;
3084987da915Sopenharmony_ci	/* Beware : bad alignment */
3085987da915Sopenharmony_ci	bytespersect = (buf[11] & 255) + ((buf[12] & 255) << 8);
3086987da915Sopenharmony_ci	sectpercluster = buf[13] & 255;
3087987da915Sopenharmony_ci	clustersz = bytespersect * (u32)sectpercluster;
3088987da915Sopenharmony_ci	clusterbits = 1;
3089987da915Sopenharmony_ci	while ((u32)(1 << clusterbits) < clustersz)
3090987da915Sopenharmony_ci		clusterbits++;
3091987da915Sopenharmony_ci	sectors = getle64(buf, 0x28);
3092987da915Sopenharmony_ci	clusters = sectors/sectpercluster;
3093987da915Sopenharmony_ci	mftlcn = getle64(buf, 0x30);
3094987da915Sopenharmony_ci	if (buf[0x40] & 0x80)
3095987da915Sopenharmony_ci		mftrecsz = 1 << (16 - (buf[0x40] & 15));
3096987da915Sopenharmony_ci	else
3097987da915Sopenharmony_ci		mftrecsz = (buf[0x40] & 127)*clustersz;
3098987da915Sopenharmony_ci	mftrecbits = 1;
3099987da915Sopenharmony_ci	while ((u32)(1 << mftrecbits) < mftrecsz)
3100987da915Sopenharmony_ci		mftrecbits++;
3101987da915Sopenharmony_ci	if (optv) {
3102987da915Sopenharmony_ci		if ((long long)sectors*bytespersect > 10000000000LL)
3103987da915Sopenharmony_ci			printf("Capacity %lld bytes (%lld GB)\n",
3104987da915Sopenharmony_ci				(long long)sectors*bytespersect,
3105987da915Sopenharmony_ci				(long long)sectors*bytespersect/1000000000);
3106987da915Sopenharmony_ci		else
3107987da915Sopenharmony_ci			printf("Capacity %lld bytes (%lld MB)\n",
3108987da915Sopenharmony_ci				(long long)sectors*bytespersect,
3109987da915Sopenharmony_ci				(long long)sectors*bytespersect/1000000);
3110987da915Sopenharmony_ci		printf("sectors %lld (0x%llx), sector size %d\n",
3111987da915Sopenharmony_ci				(long long)sectors,(long long)sectors,
3112987da915Sopenharmony_ci				(int)bytespersect);
3113987da915Sopenharmony_ci		printf("clusters %lld (0x%llx), cluster size %d (%d bits)\n",
3114987da915Sopenharmony_ci				(long long)clusters,(long long)clusters,
3115987da915Sopenharmony_ci				(int)clustersz,(int)clusterbits);
3116987da915Sopenharmony_ci		printf("MFT at cluster %lld (0x%llx), entry size %lu\n",
3117987da915Sopenharmony_ci				(long long)mftlcn,(long long)mftlcn,
3118987da915Sopenharmony_ci				(unsigned long)mftrecsz);
3119987da915Sopenharmony_ci		if (mftrecsz > clustersz)
3120987da915Sopenharmony_ci			printf("%ld clusters per MFT entry\n",
3121987da915Sopenharmony_ci				(long)(mftrecsz/clustersz));
3122987da915Sopenharmony_ci		else
3123987da915Sopenharmony_ci			printf("%ld MFT entries per cluster\n",
3124987da915Sopenharmony_ci				(long)(clustersz/mftrecsz));
3125987da915Sopenharmony_ci	}
3126987da915Sopenharmony_ci	return (ok);
3127987da915Sopenharmony_ci}
3128987da915Sopenharmony_ci
3129987da915Sopenharmony_cistatic int locatelogfile(CONTEXT *ctx)
3130987da915Sopenharmony_ci{
3131987da915Sopenharmony_ci	int err;
3132987da915Sopenharmony_ci
3133987da915Sopenharmony_ci	err = 1;
3134987da915Sopenharmony_ci	log_ni = ntfs_inode_open(ctx->vol, FILE_LogFile);
3135987da915Sopenharmony_ci	if (log_ni) {
3136987da915Sopenharmony_ci		log_na = ntfs_attr_open(log_ni, AT_DATA, AT_UNNAMED, 0);
3137987da915Sopenharmony_ci		if (log_na) {
3138987da915Sopenharmony_ci			logfilesz = log_na->data_size;
3139987da915Sopenharmony_ci			err = 0;
3140987da915Sopenharmony_ci		}
3141987da915Sopenharmony_ci	}
3142987da915Sopenharmony_ci	return (err);
3143987da915Sopenharmony_ci}
3144987da915Sopenharmony_ci
3145987da915Sopenharmony_ci/*
3146987da915Sopenharmony_ci *		Analyze a $LogFile copy
3147987da915Sopenharmony_ci *
3148987da915Sopenharmony_ci *	A $LogFile cannot be played. It can be however be analyzed in
3149987da915Sopenharmony_ci *	stand-alone mode.
3150987da915Sopenharmony_ci *	The location of the $MFT will have to be determined elsewhere.
3151987da915Sopenharmony_ci */
3152987da915Sopenharmony_ci
3153987da915Sopenharmony_cistatic BOOL getlogfiledata(CONTEXT *ctx, const char *boot)
3154987da915Sopenharmony_ci{
3155987da915Sopenharmony_ci	const RESTART_PAGE_HEADER *rph;
3156987da915Sopenharmony_ci	const RESTART_AREA *rest;
3157987da915Sopenharmony_ci	BOOL ok;
3158987da915Sopenharmony_ci	u32 off;
3159987da915Sopenharmony_ci	s64 size;
3160987da915Sopenharmony_ci	u32 system_page_size;
3161987da915Sopenharmony_ci	u32 log_page_size;
3162987da915Sopenharmony_ci
3163987da915Sopenharmony_ci	ok = FALSE;
3164987da915Sopenharmony_ci	fseek(ctx->file,0L,2);
3165987da915Sopenharmony_ci	size = ftell(ctx->file);
3166987da915Sopenharmony_ci	rph = (const RESTART_PAGE_HEADER*)boot;
3167987da915Sopenharmony_ci	off = le16_to_cpu(rph->restart_area_offset);
3168987da915Sopenharmony_ci	/*
3169987da915Sopenharmony_ci	 * If the system or log page sizes are smaller than the ntfs block size
3170987da915Sopenharmony_ci	 * or either is not a power of 2 we cannot handle this log file.
3171987da915Sopenharmony_ci	 */
3172987da915Sopenharmony_ci	system_page_size = le32_to_cpu(rph->system_page_size);
3173987da915Sopenharmony_ci	log_page_size = le32_to_cpu(rph->log_page_size);
3174987da915Sopenharmony_ci	if (system_page_size < NTFS_BLOCK_SIZE ||
3175987da915Sopenharmony_ci			log_page_size < NTFS_BLOCK_SIZE ||
3176987da915Sopenharmony_ci			system_page_size & (system_page_size - 1) ||
3177987da915Sopenharmony_ci			log_page_size & (log_page_size - 1)) {
3178987da915Sopenharmony_ci		printf("** Unsupported page size.\n");
3179987da915Sopenharmony_ci		goto out;
3180987da915Sopenharmony_ci	}
3181987da915Sopenharmony_ci	if (off & 7 || off > system_page_size) {
3182987da915Sopenharmony_ci		printf("** Inconsistent restart area offset.\n");
3183987da915Sopenharmony_ci		goto out;
3184987da915Sopenharmony_ci	}
3185987da915Sopenharmony_ci	rest = (const RESTART_AREA*)&boot[off];
3186987da915Sopenharmony_ci
3187987da915Sopenharmony_ci		/* estimate cluster size from log file size (unreliable) */
3188987da915Sopenharmony_ci	switch (le32_to_cpu(rest->seq_number_bits)) {
3189987da915Sopenharmony_ci	case 45 : clustersz = 512; break;
3190987da915Sopenharmony_ci	case 43 : clustersz = 1024; break; /* can be 1024 or 2048 */
3191987da915Sopenharmony_ci	case 40 :
3192987da915Sopenharmony_ci	default : clustersz = 4096; break;
3193987da915Sopenharmony_ci	}
3194987da915Sopenharmony_ci
3195987da915Sopenharmony_ci	clusterbits = 1;
3196987da915Sopenharmony_ci	while ((u32)(1 << clusterbits) < clustersz)
3197987da915Sopenharmony_ci		clusterbits++;
3198987da915Sopenharmony_ci	printf("* Assuming cluster size %ld\n",(long)clustersz);
3199987da915Sopenharmony_ci	logfilelcn = 0;
3200987da915Sopenharmony_ci	logfilesz = size;
3201987da915Sopenharmony_ci	if (optv)
3202987da915Sopenharmony_ci		printf("Log file size %lld bytes, cluster size %ld\n",
3203987da915Sopenharmony_ci			(long long)size, (long)clustersz);
3204987da915Sopenharmony_ci	/* Have to wait an InitializeFileRecordSegment to get these values */
3205987da915Sopenharmony_ci	mftrecsz = 0;
3206987da915Sopenharmony_ci	mftrecbits = 0;
3207987da915Sopenharmony_ci	ok = TRUE;
3208987da915Sopenharmony_ciout:
3209987da915Sopenharmony_ci	return (ok);
3210987da915Sopenharmony_ci}
3211987da915Sopenharmony_ci
3212987da915Sopenharmony_ci/*
3213987da915Sopenharmony_ci *                 Get basic volume data
3214987da915Sopenharmony_ci *
3215987da915Sopenharmony_ci *	Locate the MFT and Logfile
3216987da915Sopenharmony_ci *	Not supposed to read the first log block...
3217987da915Sopenharmony_ci */
3218987da915Sopenharmony_ci
3219987da915Sopenharmony_cistatic BOOL getvolumedata(CONTEXT *ctx, char *boot)
3220987da915Sopenharmony_ci{
3221987da915Sopenharmony_ci	const RESTART_AREA *rest;
3222987da915Sopenharmony_ci	BOOL ok;
3223987da915Sopenharmony_ci
3224987da915Sopenharmony_ci	ok = FALSE;
3225987da915Sopenharmony_ci	rest = (const RESTART_AREA*)NULL;
3226987da915Sopenharmony_ci	if (ctx->vol) {
3227987da915Sopenharmony_ci		getboot(boot);
3228987da915Sopenharmony_ci		mftlcn = ctx->vol->mft_lcn;
3229987da915Sopenharmony_ci		mftcnt = ctx->vol->mft_na->data_size/mftrecsz;
3230987da915Sopenharmony_ci		if (!locatelogfile(ctx))
3231987da915Sopenharmony_ci			ok = TRUE;
3232987da915Sopenharmony_ci		else {
3233987da915Sopenharmony_ci			fprintf(stderr,"** Could not read the log file\n");
3234987da915Sopenharmony_ci		}
3235987da915Sopenharmony_ci	} else {
3236987da915Sopenharmony_ci		if (ctx->file
3237987da915Sopenharmony_ci		    && (!memcmp(boot,"RSTR",4) || !memcmp(boot,"CHKD",4))) {
3238987da915Sopenharmony_ci			printf("* Assuming a log file copy\n");
3239987da915Sopenharmony_ci			ok = getlogfiledata(ctx, boot);
3240987da915Sopenharmony_ci			if (!ok)
3241987da915Sopenharmony_ci				goto out;
3242987da915Sopenharmony_ci		} else
3243987da915Sopenharmony_ci			fprintf(stderr,"** Not an NTFS image or log file\n");
3244987da915Sopenharmony_ci		}
3245987da915Sopenharmony_ci// TODO get rest ?, meaningful ?
3246987da915Sopenharmony_ci	if (ok && rest) {
3247987da915Sopenharmony_ci		if (rest->client_in_use_list
3248987da915Sopenharmony_ci		   || !(rest->flags & const_cpu_to_le16(2)))
3249987da915Sopenharmony_ci			printf("Volume was not unmounted safely\n");
3250987da915Sopenharmony_ci		else
3251987da915Sopenharmony_ci			printf("Volume was unmounted safely\n");
3252987da915Sopenharmony_ci		if (le16_to_cpu(rest->client_in_use_list) > 1)
3253987da915Sopenharmony_ci			printf("** multiple clients not implemented\n");
3254987da915Sopenharmony_ci	}
3255987da915Sopenharmony_ciout:
3256987da915Sopenharmony_ci	return (ok);
3257987da915Sopenharmony_ci}
3258987da915Sopenharmony_ci
3259987da915Sopenharmony_ci/*
3260987da915Sopenharmony_ci *		Open the volume (or the log file) and gets its parameters
3261987da915Sopenharmony_ci *
3262987da915Sopenharmony_ci *	Returns TRUE if successful
3263987da915Sopenharmony_ci */
3264987da915Sopenharmony_ci
3265987da915Sopenharmony_cistatic BOOL open_volume(CONTEXT *ctx, const char *device_name)
3266987da915Sopenharmony_ci{
3267987da915Sopenharmony_ci	union {
3268987da915Sopenharmony_ci		char buf[1024];
3269987da915Sopenharmony_ci		 /* alignment may be needed in getboot() */
3270987da915Sopenharmony_ci		long long force_align;
3271987da915Sopenharmony_ci	} boot;
3272987da915Sopenharmony_ci	BOOL ok;
3273987da915Sopenharmony_ci	int got;
3274987da915Sopenharmony_ci
3275987da915Sopenharmony_ci	ok =FALSE;
3276987da915Sopenharmony_ci		/*
3277987da915Sopenharmony_ci		 * First check the boot sector, to avoid library errors
3278987da915Sopenharmony_ci		 * when trying to mount a log file.
3279987da915Sopenharmony_ci		 * If the device cannot be fopened or fread, then it is
3280987da915Sopenharmony_ci		 * unlikely to be a file.
3281987da915Sopenharmony_ci		 */
3282987da915Sopenharmony_ci	ctx->vol = (ntfs_volume*)NULL;
3283987da915Sopenharmony_ci	ctx->file = fopen(device_name, "rb");
3284987da915Sopenharmony_ci	if (ctx->file) {
3285987da915Sopenharmony_ci		got = fread(boot.buf,1,1024,ctx->file);
3286987da915Sopenharmony_ci		if ((got == 1024)
3287987da915Sopenharmony_ci		    && (!memcmp(boot.buf, "RSTR", 4)
3288987da915Sopenharmony_ci				|| !memcmp(boot.buf, "CHKD", 4))) {
3289987da915Sopenharmony_ci			/* This appears to be a log file */
3290987da915Sopenharmony_ci			ctx->vol = (ntfs_volume*)NULL;
3291987da915Sopenharmony_ci			ok = getvolumedata(ctx, boot.buf);
3292987da915Sopenharmony_ci			if (!ok) {
3293987da915Sopenharmony_ci				fclose(ctx->file);
3294987da915Sopenharmony_ci				goto out;
3295987da915Sopenharmony_ci			}
3296987da915Sopenharmony_ci		} else {
3297987da915Sopenharmony_ci			fclose(ctx->file);
3298987da915Sopenharmony_ci		}
3299987da915Sopenharmony_ci	}
3300987da915Sopenharmony_ci	if (!ok) {
3301987da915Sopenharmony_ci		/* Not a log file, assume an ntfs device, mount it */
3302987da915Sopenharmony_ci		ctx->file = (FILE*)NULL;
3303987da915Sopenharmony_ci		ctx->vol = ntfs_mount(device_name,
3304987da915Sopenharmony_ci			((optk || optp || optu || opts) && !optn
3305987da915Sopenharmony_ci				? NTFS_MNT_FORENSIC : NTFS_MNT_RDONLY));
3306987da915Sopenharmony_ci		if (ctx->vol) {
3307987da915Sopenharmony_ci			ok = getvolumedata(ctx, boot.buf);
3308987da915Sopenharmony_ci			if (!ok)
3309987da915Sopenharmony_ci				ntfs_umount(ctx->vol, TRUE);
3310987da915Sopenharmony_ci		}
3311987da915Sopenharmony_ci	}
3312987da915Sopenharmony_ciout:
3313987da915Sopenharmony_ci	return (ok);
3314987da915Sopenharmony_ci}
3315987da915Sopenharmony_ci
3316987da915Sopenharmony_cistatic u16 dorcrd(CONTEXT *ctx, u32 blk, u16 pos, const struct BUFFER *buf,
3317987da915Sopenharmony_ci			const struct BUFFER *nextbuf)
3318987da915Sopenharmony_ci{
3319987da915Sopenharmony_ci	if (optv) {
3320987da915Sopenharmony_ci		if (optv >= 2)
3321987da915Sopenharmony_ci			hexdump(buf->block.data,blocksz);
3322987da915Sopenharmony_ci		printf("* RCRD in block %ld 0x%lx (addr 0x%llx)"
3323987da915Sopenharmony_ci			" from pos 0x%x\n",
3324987da915Sopenharmony_ci			(long)blk,(long)blk,
3325987da915Sopenharmony_ci			(long long)loclogblk(ctx, blk),(int)pos);
3326987da915Sopenharmony_ci	} else {
3327987da915Sopenharmony_ci		if (optt)
3328987da915Sopenharmony_ci			printf("block %ld\n",(long)blk);
3329987da915Sopenharmony_ci	}
3330987da915Sopenharmony_ci	return (forward_rcrd(ctx, blk, pos, buf, nextbuf));
3331987da915Sopenharmony_ci}
3332987da915Sopenharmony_ci
3333987da915Sopenharmony_ci/*
3334987da915Sopenharmony_ci *		Concatenate and process a record overlapping on several blocks
3335987da915Sopenharmony_ci */
3336987da915Sopenharmony_ci
3337987da915Sopenharmony_cistatic TRISTATE backoverlap(CONTEXT *ctx, int blk,
3338987da915Sopenharmony_ci			const char *data, const char *nextdata, int k)
3339987da915Sopenharmony_ci{
3340987da915Sopenharmony_ci	const LOG_RECORD *logr;
3341987da915Sopenharmony_ci	char *fullrec;
3342987da915Sopenharmony_ci	s32 size;
3343987da915Sopenharmony_ci	int space;
3344987da915Sopenharmony_ci	int nextspace;
3345987da915Sopenharmony_ci	TRISTATE state;
3346987da915Sopenharmony_ci	u16 blkheadsz;
3347987da915Sopenharmony_ci
3348987da915Sopenharmony_ci	logr = (const LOG_RECORD*)&data[k];
3349987da915Sopenharmony_ci	state = T_ERR;
3350987da915Sopenharmony_ci	size = le32_to_cpu(logr->client_data_length) + LOG_RECORD_HEAD_SZ;
3351987da915Sopenharmony_ci	space = blocksz - k;
3352987da915Sopenharmony_ci	blkheadsz = sizeof(RECORD_PAGE_HEADER)
3353987da915Sopenharmony_ci			+ ((2*getle16(data,6) - 1) | 7) + 1;
3354987da915Sopenharmony_ci	nextspace = blocksz - blkheadsz;
3355987da915Sopenharmony_ci	if ((space >= LOG_RECORD_HEAD_SZ)
3356987da915Sopenharmony_ci	    && (size > space)
3357987da915Sopenharmony_ci	    && (size < MAXRECSIZE)) {
3358987da915Sopenharmony_ci		fullrec = (char*)malloc(size);
3359987da915Sopenharmony_ci		memcpy(fullrec,&data[k],space);
3360987da915Sopenharmony_ci		if (size <= (space + nextspace))
3361987da915Sopenharmony_ci			memcpy(&fullrec[space], nextdata + blkheadsz,
3362987da915Sopenharmony_ci						size - space);
3363987da915Sopenharmony_ci		else {
3364987da915Sopenharmony_ci			const struct BUFFER *morebuf;
3365987da915Sopenharmony_ci			const char *moredata;
3366987da915Sopenharmony_ci			int total;
3367987da915Sopenharmony_ci			int more;
3368987da915Sopenharmony_ci			unsigned int mblk;
3369987da915Sopenharmony_ci
3370987da915Sopenharmony_ci			if (optv)
3371987da915Sopenharmony_ci				printf("* big record, size %d\n",size);
3372987da915Sopenharmony_ci			total = space;
3373987da915Sopenharmony_ci			mblk = blk + 1;
3374987da915Sopenharmony_ci			while (total < size) {
3375987da915Sopenharmony_ci				if (mblk >= (logfilesz >> blockbits))
3376987da915Sopenharmony_ci					mblk = (log_major < 2 ? BASEBLKS
3377987da915Sopenharmony_ci							: BASEBLKS2);
3378987da915Sopenharmony_ci				more = size - total;
3379987da915Sopenharmony_ci				if (more > nextspace)
3380987da915Sopenharmony_ci					more = nextspace;
3381987da915Sopenharmony_ci				morebuf = read_buffer(ctx, mblk);
3382987da915Sopenharmony_ci				if (morebuf) {
3383987da915Sopenharmony_ci					moredata = morebuf->block.data;
3384987da915Sopenharmony_ci					memcpy(&fullrec[total],
3385987da915Sopenharmony_ci						moredata + blkheadsz, more);
3386987da915Sopenharmony_ci				}
3387987da915Sopenharmony_ci				total += more;
3388987da915Sopenharmony_ci				mblk++;
3389987da915Sopenharmony_ci			}
3390987da915Sopenharmony_ci		}
3391987da915Sopenharmony_ci
3392987da915Sopenharmony_ci		state = (likelyop((LOG_RECORD*)fullrec) ? T_OK : T_ERR);
3393987da915Sopenharmony_ci		actionnum++;
3394987da915Sopenharmony_ci		if (optv) {
3395987da915Sopenharmony_ci			printf("\nOverlapping backward action %d at 0x%x"
3396987da915Sopenharmony_ci				" size %d (next at 0x%x)\n",
3397987da915Sopenharmony_ci				(int)actionnum,(int)k,
3398987da915Sopenharmony_ci				(int)size,(int)(k + size));
3399987da915Sopenharmony_ci			printf("Overlap marked for block %ld space %d"
3400987da915Sopenharmony_ci				" likely %d\n",
3401987da915Sopenharmony_ci				(long)blk,(int)space,(state == T_OK));
3402987da915Sopenharmony_ci		}
3403987da915Sopenharmony_ci		if (state == T_OK) {
3404987da915Sopenharmony_ci			showlogr(ctx, k, (LOG_RECORD*)fullrec);
3405987da915Sopenharmony_ci			if (optp || optu || opts)
3406987da915Sopenharmony_ci				state = enqueue_action(ctx,
3407987da915Sopenharmony_ci						(LOG_RECORD*)fullrec,
3408987da915Sopenharmony_ci						size, actionnum);
3409987da915Sopenharmony_ci		} else {
3410987da915Sopenharmony_ci			/* Try to go on unless playing actions */
3411987da915Sopenharmony_ci			if (optb && (state == T_ERR))
3412987da915Sopenharmony_ci				state = T_OK;
3413987da915Sopenharmony_ci		}
3414987da915Sopenharmony_ci		free(fullrec);
3415987da915Sopenharmony_ci	} else {
3416987da915Sopenharmony_ci			/* Error conditions */
3417987da915Sopenharmony_ci		if ((size < MINRECSIZE) || (size > MAXRECSIZE)) {
3418987da915Sopenharmony_ci			printf("** Invalid record size %ld"
3419987da915Sopenharmony_ci					" in block %ld\n",
3420987da915Sopenharmony_ci					(long)size,(long)blk);
3421987da915Sopenharmony_ci		} else
3422987da915Sopenharmony_ci			printf("** Inconsistency : the final"
3423987da915Sopenharmony_ci						" record in block %ld"
3424987da915Sopenharmony_ci						" does not overlap\n",
3425987da915Sopenharmony_ci						(long)blk);
3426987da915Sopenharmony_ci			/* Do not abort, unless playing actions */
3427987da915Sopenharmony_ci		state = (optb ? T_OK : T_ERR);
3428987da915Sopenharmony_ci	}
3429987da915Sopenharmony_ci	return (state);
3430987da915Sopenharmony_ci}
3431987da915Sopenharmony_ci
3432987da915Sopenharmony_cistatic TRISTATE backward_rcrd(CONTEXT *ctx, u32 blk, int skipped,
3433987da915Sopenharmony_ci                  const struct BUFFER *buf, const struct BUFFER *prevbuf,
3434987da915Sopenharmony_ci                  const struct BUFFER *nextbuf)
3435987da915Sopenharmony_ci{
3436987da915Sopenharmony_ci	u16 poslist[75]; /* 4096/sizeof(LOG_RECORD) */
3437987da915Sopenharmony_ci	const RECORD_PAGE_HEADER *rph;
3438987da915Sopenharmony_ci	const RECORD_PAGE_HEADER *prevrph;
3439987da915Sopenharmony_ci	const LOG_RECORD *logr;
3440987da915Sopenharmony_ci	const char *data;
3441987da915Sopenharmony_ci	const char *nextdata;
3442987da915Sopenharmony_ci	BOOL stop;
3443987da915Sopenharmony_ci	TRISTATE state;
3444987da915Sopenharmony_ci	s32 size;
3445987da915Sopenharmony_ci	int cnt;
3446987da915Sopenharmony_ci	u16 k;
3447987da915Sopenharmony_ci	u16 endoff;
3448987da915Sopenharmony_ci	int j;
3449987da915Sopenharmony_ci
3450987da915Sopenharmony_ci	state = T_ERR;
3451987da915Sopenharmony_ci	rph = &buf->block.record;
3452987da915Sopenharmony_ci	prevrph = (RECORD_PAGE_HEADER*)NULL;
3453987da915Sopenharmony_ci	if (prevbuf)
3454987da915Sopenharmony_ci		prevrph = &prevbuf->block.record;
3455987da915Sopenharmony_ci	data = buf->block.data;
3456987da915Sopenharmony_ci	if (rph && (rph->magic == magic_RCRD)
3457987da915Sopenharmony_ci	    && (!prevrph || (prevrph->magic == magic_RCRD))) {
3458987da915Sopenharmony_ci		if (optv) {
3459987da915Sopenharmony_ci			if (optv >= 2)
3460987da915Sopenharmony_ci				hexdump(data,blocksz);
3461987da915Sopenharmony_ci			if (buf->rnum != blk)
3462987da915Sopenharmony_ci				printf("* RCRD for block %ld 0x%lx"
3463987da915Sopenharmony_ci				     " in block %ld (addr 0x%llx)\n",
3464987da915Sopenharmony_ci				     (long)blk,(long)blk,(long)buf->rnum,
3465987da915Sopenharmony_ci				     (long long)loclogblk(ctx, blk));
3466987da915Sopenharmony_ci			else
3467987da915Sopenharmony_ci				printf("* RCRD in block %ld 0x%lx (addr 0x%llx)\n",
3468987da915Sopenharmony_ci				     (long)blk,(long)blk,
3469987da915Sopenharmony_ci				     (long long)loclogblk(ctx, blk));
3470987da915Sopenharmony_ci		} else {
3471987da915Sopenharmony_ci			if (optt)
3472987da915Sopenharmony_ci				printf("block %ld\n",(long)blk);
3473987da915Sopenharmony_ci		}
3474987da915Sopenharmony_ci		showheadrcrd(blk, rph);
3475987da915Sopenharmony_ci		if (!prevbuf)
3476987da915Sopenharmony_ci			k = buf->headsz;
3477987da915Sopenharmony_ci		else
3478987da915Sopenharmony_ci			k = firstrecord(skipped, buf, prevbuf);
3479987da915Sopenharmony_ci		logr = (const LOG_RECORD*)&data[k];
3480987da915Sopenharmony_ci		cnt = 0;
3481987da915Sopenharmony_ci	   /* check whether there is at least one beginning of record */
3482987da915Sopenharmony_ci		endoff = le16_to_cpu(rph->next_record_offset);
3483987da915Sopenharmony_ci		if (k && ((k < endoff) || !endoff)) {
3484987da915Sopenharmony_ci			logr = (const LOG_RECORD*)&data[k];
3485987da915Sopenharmony_ci			if (likelyop(logr)) {
3486987da915Sopenharmony_ci				stop = FALSE;
3487987da915Sopenharmony_ci				state = T_OK;
3488987da915Sopenharmony_ci				if (optv)
3489987da915Sopenharmony_ci					printf("First record checked"
3490987da915Sopenharmony_ci						" at offset 0x%x\n", (int)k);
3491987da915Sopenharmony_ci			} else {
3492987da915Sopenharmony_ci				printf("** Bad first record at offset 0x%x\n",
3493987da915Sopenharmony_ci								(int)k);
3494987da915Sopenharmony_ci				if (optv)
3495987da915Sopenharmony_ci					showlogr(ctx, k,logr);
3496987da915Sopenharmony_ci				k = searchlikely(buf);
3497987da915Sopenharmony_ci				stop = !k;
3498987da915Sopenharmony_ci				if (stop) {
3499987da915Sopenharmony_ci					printf("** Could not recover,"
3500987da915Sopenharmony_ci						" stopping at block %d\n",
3501987da915Sopenharmony_ci						(int)blk);
3502987da915Sopenharmony_ci					state = T_ERR;
3503987da915Sopenharmony_ci				} else {
3504987da915Sopenharmony_ci					/* Try to go on, unless running */
3505987da915Sopenharmony_ci					if (optb)
3506987da915Sopenharmony_ci						state = T_OK;
3507987da915Sopenharmony_ci				}
3508987da915Sopenharmony_ci			}
3509987da915Sopenharmony_ci			while (!stop) {
3510987da915Sopenharmony_ci				logr = (const LOG_RECORD*)&data[k];
3511987da915Sopenharmony_ci				size = le32_to_cpu(logr->client_data_length)
3512987da915Sopenharmony_ci						+ LOG_RECORD_HEAD_SZ;
3513987da915Sopenharmony_ci				if ((size < MINRECSIZE)
3514987da915Sopenharmony_ci				    || (size > MAXRECSIZE)
3515987da915Sopenharmony_ci				    || (size & 7)) {
3516987da915Sopenharmony_ci					printf("** Bad size %ld in block %ld"
3517987da915Sopenharmony_ci						" offset 0x%x, stopping\n",
3518987da915Sopenharmony_ci						(long)size,(long)blk,(int)k);
3519987da915Sopenharmony_ci					stop = TRUE;
3520987da915Sopenharmony_ci				} else {
3521987da915Sopenharmony_ci					if (((u32)(k + size) <= blocksz)
3522987da915Sopenharmony_ci					    && ((u32)(k + size) <= endoff)) {
3523987da915Sopenharmony_ci						poslist[cnt++] = k;
3524987da915Sopenharmony_ci						if (!logr->client_data_length)
3525987da915Sopenharmony_ci							stop = TRUE;
3526987da915Sopenharmony_ci						k += size;
3527987da915Sopenharmony_ci						if ((u32)(k
3528987da915Sopenharmony_ci						    + LOG_RECORD_HEAD_SZ)
3529987da915Sopenharmony_ci						    > blocksz)
3530987da915Sopenharmony_ci							stop = TRUE;
3531987da915Sopenharmony_ci					} else {
3532987da915Sopenharmony_ci						stop = TRUE;
3533987da915Sopenharmony_ci					}
3534987da915Sopenharmony_ci				}
3535987da915Sopenharmony_ci			}
3536987da915Sopenharmony_ci		} else {
3537987da915Sopenharmony_ci			stop = TRUE;
3538987da915Sopenharmony_ci			state = (k ? T_OK : T_ERR);
3539987da915Sopenharmony_ci		}
3540987da915Sopenharmony_ci		      /* Now examine an overlapping record */
3541987da915Sopenharmony_ci		if (k
3542987da915Sopenharmony_ci		    && ((k == endoff) || !endoff)
3543987da915Sopenharmony_ci		    && ((u32)(k + LOG_RECORD_HEAD_SZ) <= blocksz)) {
3544987da915Sopenharmony_ci			if (nextbuf && (blk >= BASEBLKS)) {
3545987da915Sopenharmony_ci				nextdata = nextbuf->block.data;
3546987da915Sopenharmony_ci				state = backoverlap(ctx, blk,
3547987da915Sopenharmony_ci						data, nextdata, k);
3548987da915Sopenharmony_ci			}
3549987da915Sopenharmony_ci		}
3550987da915Sopenharmony_ci		for (j=cnt-1; (j>=0) && (state==T_OK); j--) {
3551987da915Sopenharmony_ci			k = poslist[j];
3552987da915Sopenharmony_ci			logr = (const LOG_RECORD*)&data[k];
3553987da915Sopenharmony_ci			size = le32_to_cpu(logr->client_data_length)
3554987da915Sopenharmony_ci					+ LOG_RECORD_HEAD_SZ;
3555987da915Sopenharmony_ci			actionnum++;
3556987da915Sopenharmony_ci			if (optv && (!optc || within_lcn_range(logr))) {
3557987da915Sopenharmony_ci				printf("\n* log backward action %u at 0x%x"
3558987da915Sopenharmony_ci					" size %d (next at 0x%x)\n",
3559987da915Sopenharmony_ci					actionnum, k, size, k + size);
3560987da915Sopenharmony_ci			}
3561987da915Sopenharmony_ci			if ((optv | optt)
3562987da915Sopenharmony_ci			    && (!nextbuf && (j == (cnt - 1)))) {
3563987da915Sopenharmony_ci				printf("* This is the latest record\n");
3564987da915Sopenharmony_ci				if (logr->this_lsn == restart.current_lsn)
3565987da915Sopenharmony_ci					printf("   its lsn matches the global"
3566987da915Sopenharmony_ci						" restart lsn\n");
3567987da915Sopenharmony_ci				if (logr->this_lsn == client.client_restart_lsn)
3568987da915Sopenharmony_ci					printf("   its lsn matches the client"
3569987da915Sopenharmony_ci						" restart lsn\n");
3570987da915Sopenharmony_ci				if (logr->client_data_length
3571987da915Sopenharmony_ci				    == restart.last_lsn_data_length)
3572987da915Sopenharmony_ci					printf("   its length matches the"
3573987da915Sopenharmony_ci						" last record length\n");
3574987da915Sopenharmony_ci			}
3575987da915Sopenharmony_ci		showlogr(ctx, k, logr);
3576987da915Sopenharmony_ci		if (optp || optu || opts)
3577987da915Sopenharmony_ci			state = enqueue_action(ctx, logr, size, actionnum);
3578987da915Sopenharmony_ci		}
3579987da915Sopenharmony_ci	}
3580987da915Sopenharmony_ci	return (state);
3581987da915Sopenharmony_ci}
3582987da915Sopenharmony_ci
3583987da915Sopenharmony_cistatic int walkback(CONTEXT *ctx, const struct BUFFER *buf, u32 blk,
3584987da915Sopenharmony_ci			const struct BUFFER *prevbuf, u32 prevblk)
3585987da915Sopenharmony_ci{
3586987da915Sopenharmony_ci	const struct BUFFER *nextbuf;
3587987da915Sopenharmony_ci	NTFS_RECORD_TYPES magic;
3588987da915Sopenharmony_ci	u32 stopblk;
3589987da915Sopenharmony_ci	TRISTATE state;
3590987da915Sopenharmony_ci
3591987da915Sopenharmony_ci	if (optv) {
3592987da915Sopenharmony_ci		if ((log_major >= 2) && (buf->rnum != blk))
3593987da915Sopenharmony_ci			printf("\n* block %d for block %d at 0x%llx\n",
3594987da915Sopenharmony_ci					(int)buf->rnum,(int)blk,
3595987da915Sopenharmony_ci					(long long)loclogblk(ctx, buf->rnum));
3596987da915Sopenharmony_ci		else
3597987da915Sopenharmony_ci			printf("\n* block %d at 0x%llx\n",(int)blk,
3598987da915Sopenharmony_ci					(long long)loclogblk(ctx, blk));
3599987da915Sopenharmony_ci	}
3600987da915Sopenharmony_ci	ctx->firstaction = (struct ACTION_RECORD*)NULL;
3601987da915Sopenharmony_ci	ctx->lastaction = (struct ACTION_RECORD*)NULL;
3602987da915Sopenharmony_ci	nextbuf = (const struct BUFFER*)NULL;
3603987da915Sopenharmony_ci	stopblk = prevblk + 2; // wraparound !
3604987da915Sopenharmony_ci	state = backward_rcrd(ctx, blk, 0, buf,
3605987da915Sopenharmony_ci			prevbuf, (struct BUFFER*)NULL);
3606987da915Sopenharmony_ci	while ((state == T_OK)
3607987da915Sopenharmony_ci	   && !((blk > stopblk) && (prevblk <= stopblk))
3608987da915Sopenharmony_ci	   && (!(optp || optu) || (playedactions < playcount))) {
3609987da915Sopenharmony_ci		int skipped;
3610987da915Sopenharmony_ci
3611987da915Sopenharmony_ci		nextbuf = buf;
3612987da915Sopenharmony_ci		buf = prevbuf;
3613987da915Sopenharmony_ci		blk = prevblk;
3614987da915Sopenharmony_ci		skipped = 0;
3615987da915Sopenharmony_ci		prevbuf = findprevious(ctx, buf);
3616987da915Sopenharmony_ci		if (prevbuf) {
3617987da915Sopenharmony_ci			prevblk = prevbuf->num;
3618987da915Sopenharmony_ci			if (prevblk < blk)
3619987da915Sopenharmony_ci				skipped = blk - prevblk - 1;
3620987da915Sopenharmony_ci			else
3621987da915Sopenharmony_ci				skipped = blk - prevblk - 1
3622987da915Sopenharmony_ci					+ (logfilesz >> blockbits)
3623987da915Sopenharmony_ci					- (log_major < 2 ? BASEBLKS
3624987da915Sopenharmony_ci							: BASEBLKS2);
3625987da915Sopenharmony_ci			magic = prevbuf->block.record.magic;
3626987da915Sopenharmony_ci			switch (magic) {
3627987da915Sopenharmony_ci			case magic_RCRD :
3628987da915Sopenharmony_ci				break;
3629987da915Sopenharmony_ci			case magic_CHKD :
3630987da915Sopenharmony_ci				printf("** Unexpected block type CHKD\n");
3631987da915Sopenharmony_ci				break;
3632987da915Sopenharmony_ci			case magic_RSTR :
3633987da915Sopenharmony_ci				printf("** Unexpected block type RSTR\n");
3634987da915Sopenharmony_ci				break;
3635987da915Sopenharmony_ci			default :
3636987da915Sopenharmony_ci				printf("** Invalid block %d\n",(int)prevblk);
3637987da915Sopenharmony_ci				break;
3638987da915Sopenharmony_ci			}
3639987da915Sopenharmony_ci			if (optv) {
3640987da915Sopenharmony_ci				if (skipped)
3641987da915Sopenharmony_ci					printf("\n* block %ld at 0x%llx (block"
3642987da915Sopenharmony_ci						" %ld used as previous one)\n",
3643987da915Sopenharmony_ci						(long)blk,
3644987da915Sopenharmony_ci						(long long)loclogblk(ctx, blk),
3645987da915Sopenharmony_ci						(long)prevblk);
3646987da915Sopenharmony_ci				else
3647987da915Sopenharmony_ci					if ((log_major >= 2)
3648987da915Sopenharmony_ci					    && (buf->rnum != blk))
3649987da915Sopenharmony_ci						printf("\n* block %ld for block %ld at 0x%llx\n",
3650987da915Sopenharmony_ci							(long)buf->rnum,
3651987da915Sopenharmony_ci							(long)blk,
3652987da915Sopenharmony_ci							(long long)loclogblk(
3653987da915Sopenharmony_ci							    ctx,buf->rnum));
3654987da915Sopenharmony_ci					else
3655987da915Sopenharmony_ci						printf("\n* block %ld at 0x%llx\n",
3656987da915Sopenharmony_ci							(long)blk,
3657987da915Sopenharmony_ci							(long long)loclogblk(
3658987da915Sopenharmony_ci								ctx, blk));
3659987da915Sopenharmony_ci			}
3660987da915Sopenharmony_ci			state = backward_rcrd(ctx, blk, skipped,
3661987da915Sopenharmony_ci						buf, prevbuf, nextbuf);
3662987da915Sopenharmony_ci		} else {
3663987da915Sopenharmony_ci			fprintf(stderr,"** Could not read block %lu\n",
3664987da915Sopenharmony_ci								(long)prevblk);
3665987da915Sopenharmony_ci			state = T_ERR;
3666987da915Sopenharmony_ci		}
3667987da915Sopenharmony_ci	}
3668987da915Sopenharmony_ci	if ((blk > stopblk) && (prevblk <= stopblk))
3669987da915Sopenharmony_ci		printf("* Earliest block reached\n");
3670987da915Sopenharmony_ci	if ((optp || optu) && (playedactions >= playcount))
3671987da915Sopenharmony_ci		printf("* Transaction set count reached\n");
3672987da915Sopenharmony_ci	if (opts)
3673987da915Sopenharmony_ci		printf("* %s %s after playing %u actions\n",
3674987da915Sopenharmony_ci				(optn ? "Sync simulation" : "Syncing"),
3675987da915Sopenharmony_ci				(state == T_ERR ? "failed" : "successful"),
3676987da915Sopenharmony_ci				redocount);
3677987da915Sopenharmony_ci			/* free queue */
3678987da915Sopenharmony_ci	while (ctx->firstaction) {
3679987da915Sopenharmony_ci		struct ACTION_RECORD *action;
3680987da915Sopenharmony_ci
3681987da915Sopenharmony_ci		action = ctx->firstaction->next;
3682987da915Sopenharmony_ci		free(ctx->firstaction);
3683987da915Sopenharmony_ci		ctx->firstaction = action;
3684987da915Sopenharmony_ci		}
3685987da915Sopenharmony_ci	ctx->lastaction = (struct ACTION_RECORD*)NULL;
3686987da915Sopenharmony_ci	return (state == T_ERR ? 1 : 0);
3687987da915Sopenharmony_ci}
3688987da915Sopenharmony_ci
3689987da915Sopenharmony_ci/*
3690987da915Sopenharmony_ci *		Find the latest log block
3691987da915Sopenharmony_ci *
3692987da915Sopenharmony_ci *	Usually, the latest block is either block 2 or 3 which act as
3693987da915Sopenharmony_ci *	temporary block before being copied to target location.
3694987da915Sopenharmony_ci *	However under some unknown condition the block are written
3695987da915Sopenharmony_ci *	immediately to target location, and we have to scan for the
3696987da915Sopenharmony_ci *	latest one.
3697987da915Sopenharmony_ci *	Currently this is not checked for logfile version 2.x which
3698987da915Sopenharmony_ci *	use a different layout of temporary blocks.
3699987da915Sopenharmony_ci */
3700987da915Sopenharmony_ci
3701987da915Sopenharmony_cistatic const struct BUFFER *find_latest_block(CONTEXT *ctx, u32 baseblk,
3702987da915Sopenharmony_ci			const struct BUFFER *basebuf)
3703987da915Sopenharmony_ci{
3704987da915Sopenharmony_ci	le64 offset;
3705987da915Sopenharmony_ci	leLSN prevlsn;
3706987da915Sopenharmony_ci	leLSN curlsn;
3707987da915Sopenharmony_ci	u32 curblk;
3708987da915Sopenharmony_ci	u32 prevblk;
3709987da915Sopenharmony_ci	const struct BUFFER *prevbuf;
3710987da915Sopenharmony_ci	const struct BUFFER *curbuf;
3711987da915Sopenharmony_ci
3712987da915Sopenharmony_ci	offset = basebuf->block.record.copy.file_offset;
3713987da915Sopenharmony_ci	curbuf = (const struct BUFFER*)NULL;
3714987da915Sopenharmony_ci	curlsn = const_cpu_to_le64(0);
3715987da915Sopenharmony_ci	prevblk = 0;
3716987da915Sopenharmony_ci	curblk = baseblk;
3717987da915Sopenharmony_ci	do {
3718987da915Sopenharmony_ci		if (curblk < BASEBLKS) {
3719987da915Sopenharmony_ci			prevbuf = basebuf;
3720987da915Sopenharmony_ci			prevlsn = basebuf->block.record.last_end_lsn;
3721987da915Sopenharmony_ci			prevblk = baseblk;
3722987da915Sopenharmony_ci			curblk = le64_to_cpu(offset) >> blockbits;
3723987da915Sopenharmony_ci		} else {
3724987da915Sopenharmony_ci			if (optv)
3725987da915Sopenharmony_ci				printf("block %d is more recent than block %d\n",
3726987da915Sopenharmony_ci					(int)curblk, (int)prevblk);
3727987da915Sopenharmony_ci			prevbuf = curbuf;
3728987da915Sopenharmony_ci			prevlsn = curlsn;
3729987da915Sopenharmony_ci			prevblk = curblk;
3730987da915Sopenharmony_ci			curblk++;
3731987da915Sopenharmony_ci			if (curblk >= (logfilesz >> blockbits))
3732987da915Sopenharmony_ci				curblk = (log_major < 2 ? BASEBLKS : BASEBLKS2);
3733987da915Sopenharmony_ci		}
3734987da915Sopenharmony_ci		curbuf = read_buffer(ctx, curblk);
3735987da915Sopenharmony_ci		if (curbuf && (curbuf->block.record.magic == magic_RCRD)) {
3736987da915Sopenharmony_ci			curlsn = curbuf->block.record.copy.last_lsn;
3737987da915Sopenharmony_ci		}
3738987da915Sopenharmony_ci	} while (curbuf
3739987da915Sopenharmony_ci		&& (curbuf->block.record.magic == magic_RCRD)
3740987da915Sopenharmony_ci		&& (le64_to_cpu(curlsn) > le64_to_cpu(prevlsn)));
3741987da915Sopenharmony_ci	if (optv)
3742987da915Sopenharmony_ci		printf("Block %d is the latest one\n",(int)prevblk);
3743987da915Sopenharmony_ci	return (prevbuf);
3744987da915Sopenharmony_ci}
3745987da915Sopenharmony_ci
3746987da915Sopenharmony_ci/*
3747987da915Sopenharmony_ci *		Determine the sequencing of blocks (when version >= 2.0)
3748987da915Sopenharmony_ci *
3749987da915Sopenharmony_ci *	Blocks 2..17 and 18..33 are temporary blocks being filled until
3750987da915Sopenharmony_ci *	they are copied to their target locations, so there are three
3751987da915Sopenharmony_ci *	possible location for recent blocks.
3752987da915Sopenharmony_ci *
3753987da915Sopenharmony_ci *	Returns the latest target block number
3754987da915Sopenharmony_ci */
3755987da915Sopenharmony_ci
3756987da915Sopenharmony_cistatic int block_sequence(CONTEXT *ctx)
3757987da915Sopenharmony_ci{
3758987da915Sopenharmony_ci	const struct BUFFER *buf;
3759987da915Sopenharmony_ci	int blk;
3760987da915Sopenharmony_ci	int k;
3761987da915Sopenharmony_ci	int target_blk;
3762987da915Sopenharmony_ci	int latest_blk;
3763987da915Sopenharmony_ci	s64 final_lsn;
3764987da915Sopenharmony_ci	s64 last_lsn;
3765987da915Sopenharmony_ci	s64 last_lsn12;
3766987da915Sopenharmony_ci	s64 last_lsn1, last_lsn2;
3767987da915Sopenharmony_ci
3768987da915Sopenharmony_ci	final_lsn = 0;
3769987da915Sopenharmony_ci	for (blk=RSTBLKS; 2*blk<(RSTBLKS+BASEBLKS2); blk++) {
3770987da915Sopenharmony_ci			/* First temporary block */
3771987da915Sopenharmony_ci		last_lsn1 = 0;
3772987da915Sopenharmony_ci		buf = read_buffer(ctx, blk);
3773987da915Sopenharmony_ci		if (buf && (buf->block.record.magic == magic_RCRD)) {
3774987da915Sopenharmony_ci			last_lsn1 = le64_to_cpu(
3775987da915Sopenharmony_ci					buf->block.record.copy.last_lsn);
3776987da915Sopenharmony_ci			if (!final_lsn
3777987da915Sopenharmony_ci			    || ((s64)(last_lsn1 - final_lsn) > 0))
3778987da915Sopenharmony_ci				final_lsn = last_lsn1;
3779987da915Sopenharmony_ci		}
3780987da915Sopenharmony_ci			/* Second temporary block */
3781987da915Sopenharmony_ci		buf = read_buffer(ctx, blk + (BASEBLKS2 - RSTBLKS)/2);
3782987da915Sopenharmony_ci		last_lsn2 = 0;
3783987da915Sopenharmony_ci		if (buf && (buf->block.record.magic == magic_RCRD)) {
3784987da915Sopenharmony_ci			last_lsn2 = le64_to_cpu(
3785987da915Sopenharmony_ci					buf->block.record.copy.last_lsn);
3786987da915Sopenharmony_ci			if (!final_lsn
3787987da915Sopenharmony_ci			    || ((s64)(last_lsn2 - final_lsn) > 0))
3788987da915Sopenharmony_ci				final_lsn = last_lsn2;
3789987da915Sopenharmony_ci		}
3790987da915Sopenharmony_ci			/* the latest last_lsn defines the target block */
3791987da915Sopenharmony_ci		last_lsn12 = 0;
3792987da915Sopenharmony_ci		latest_blk = 0;
3793987da915Sopenharmony_ci		if (last_lsn1 || last_lsn2) {
3794987da915Sopenharmony_ci			if (!last_lsn2
3795987da915Sopenharmony_ci			    || ((s64)(last_lsn1 - last_lsn2) > 0)) {
3796987da915Sopenharmony_ci				last_lsn12 = last_lsn1;
3797987da915Sopenharmony_ci				latest_blk = blk;
3798987da915Sopenharmony_ci			}
3799987da915Sopenharmony_ci			if (!last_lsn1
3800987da915Sopenharmony_ci			    || ((s64)(last_lsn1 - last_lsn2) <= 0)) {
3801987da915Sopenharmony_ci				last_lsn12 = last_lsn2;
3802987da915Sopenharmony_ci				latest_blk = blk + (BASEBLKS2 - RSTBLKS)/2;
3803987da915Sopenharmony_ci			}
3804987da915Sopenharmony_ci		}
3805987da915Sopenharmony_ci		last_lsn = 0;
3806987da915Sopenharmony_ci		target_blk = 0;
3807987da915Sopenharmony_ci		if (last_lsn12) {
3808987da915Sopenharmony_ci			target_blk = (last_lsn12 & offset_mask)
3809987da915Sopenharmony_ci							>> (blockbits - 3);
3810987da915Sopenharmony_ci			buf = read_buffer(ctx, target_blk);
3811987da915Sopenharmony_ci			if (buf && (buf->block.record.magic == magic_RCRD)) {
3812987da915Sopenharmony_ci				last_lsn = le64_to_cpu(
3813987da915Sopenharmony_ci					buf->block.record.copy.last_lsn);
3814987da915Sopenharmony_ci				if (!final_lsn
3815987da915Sopenharmony_ci				    || ((s64)(last_lsn - final_lsn) > 0))
3816987da915Sopenharmony_ci					final_lsn = last_lsn;
3817987da915Sopenharmony_ci			}
3818987da915Sopenharmony_ci		}
3819987da915Sopenharmony_ci			/* redirect to the latest block */
3820987da915Sopenharmony_ci		if (latest_blk
3821987da915Sopenharmony_ci		    && (!last_lsn || ((s64)(last_lsn - last_lsn12) < 0)))
3822987da915Sopenharmony_ci			redirect[latest_blk] = target_blk;
3823987da915Sopenharmony_ci	}
3824987da915Sopenharmony_ci	if (optv) {
3825987da915Sopenharmony_ci		printf("\n Blocks redirected :\n");
3826987da915Sopenharmony_ci		for (k=RSTBLKS; k<BASEBLKS2; k++)
3827987da915Sopenharmony_ci			if (redirect[k])
3828987da915Sopenharmony_ci				printf("* block %d to block %d\n",
3829987da915Sopenharmony_ci					(int)redirect[k],(int)k);
3830987da915Sopenharmony_ci	}
3831987da915Sopenharmony_ci	latest_lsn = final_lsn;
3832987da915Sopenharmony_ci	blk = (final_lsn & offset_mask) >> (blockbits - 3);
3833987da915Sopenharmony_ci	if (optv > 1)
3834987da915Sopenharmony_ci		printf("final lsn %llx in blk %d\n",(long long)final_lsn,blk);
3835987da915Sopenharmony_ci	return (blk);
3836987da915Sopenharmony_ci}
3837987da915Sopenharmony_ci
3838987da915Sopenharmony_cistatic int walk(CONTEXT *ctx)
3839987da915Sopenharmony_ci{
3840987da915Sopenharmony_ci	const struct BUFFER *buf;
3841987da915Sopenharmony_ci	const struct BUFFER *nextbuf;
3842987da915Sopenharmony_ci	const struct BUFFER *prevbuf;
3843987da915Sopenharmony_ci	const struct BUFFER *startbuf;
3844987da915Sopenharmony_ci	const NTFS_RECORD *record;
3845987da915Sopenharmony_ci	const RECORD_PAGE_HEADER *rph;
3846987da915Sopenharmony_ci	NTFS_RECORD_TYPES magic;
3847987da915Sopenharmony_ci	u32 blk;
3848987da915Sopenharmony_ci	u32 nextblk;
3849987da915Sopenharmony_ci	u32 prevblk;
3850987da915Sopenharmony_ci	u32 finalblk;
3851987da915Sopenharmony_ci	int err;
3852987da915Sopenharmony_ci	u16 blkheadsz;
3853987da915Sopenharmony_ci	u16 pos;
3854987da915Sopenharmony_ci	BOOL dirty;
3855987da915Sopenharmony_ci	BOOL done;
3856987da915Sopenharmony_ci
3857987da915Sopenharmony_ci	buf = (struct BUFFER*)NULL;
3858987da915Sopenharmony_ci	nextbuf = (struct BUFFER*)NULL;
3859987da915Sopenharmony_ci	if (optb || optp || optu || opts) {
3860987da915Sopenharmony_ci		prevbuf = (struct BUFFER*)NULL;
3861987da915Sopenharmony_ci	}
3862987da915Sopenharmony_ci	done = FALSE;
3863987da915Sopenharmony_ci	dirty = TRUE;
3864987da915Sopenharmony_ci	finalblk = 0;
3865987da915Sopenharmony_ci	err = 0;
3866987da915Sopenharmony_ci	blk = 0;
3867987da915Sopenharmony_ci	pos = 0;
3868987da915Sopenharmony_ci			/* read and process the first restart block */
3869987da915Sopenharmony_ci	buf = read_restart(ctx);
3870987da915Sopenharmony_ci	if (buf) {
3871987da915Sopenharmony_ci		if (optv)
3872987da915Sopenharmony_ci			printf("\n* block %d at 0x%llx\n",(int)blk,
3873987da915Sopenharmony_ci					(long long)loclogblk(ctx, blk));
3874987da915Sopenharmony_ci	} else {
3875987da915Sopenharmony_ci		done = TRUE;
3876987da915Sopenharmony_ci		err = 1;
3877987da915Sopenharmony_ci	}
3878987da915Sopenharmony_ci
3879987da915Sopenharmony_ci	nextblk = blk + 1;
3880987da915Sopenharmony_ci	while (!done) {
3881987da915Sopenharmony_ci		 /* next block is needed to process the current one */
3882987da915Sopenharmony_ci		if ((nextblk >= (logfilesz >> blockbits)) && (optr || optf))
3883987da915Sopenharmony_ci			nextbuf = read_buffer(ctx,
3884987da915Sopenharmony_ci					(log_major < 2 ? BASEBLKS : BASEBLKS2));
3885987da915Sopenharmony_ci		else
3886987da915Sopenharmony_ci			nextbuf = read_buffer(ctx,nextblk);
3887987da915Sopenharmony_ci		if (nextbuf) {
3888987da915Sopenharmony_ci			record = (const NTFS_RECORD*)&nextbuf->block.data;
3889987da915Sopenharmony_ci			blkheadsz = nextbuf->headsz;
3890987da915Sopenharmony_ci			magic = record->magic;
3891987da915Sopenharmony_ci			switch (magic) {
3892987da915Sopenharmony_ci			case magic_CHKD :
3893987da915Sopenharmony_ci			case magic_RSTR :
3894987da915Sopenharmony_ci			case magic_RCRD :
3895987da915Sopenharmony_ci				break;
3896987da915Sopenharmony_ci			default :
3897987da915Sopenharmony_ci				printf("** Invalid block\n");
3898987da915Sopenharmony_ci				err = 1;
3899987da915Sopenharmony_ci				break;
3900987da915Sopenharmony_ci			}
3901987da915Sopenharmony_ci			magic = buf->block.record.magic;
3902987da915Sopenharmony_ci			switch (magic) {
3903987da915Sopenharmony_ci			case magic_CHKD :
3904987da915Sopenharmony_ci			case magic_RSTR :
3905987da915Sopenharmony_ci				dirty = dorest(ctx, blk, &buf->block.restart,
3906987da915Sopenharmony_ci								FALSE);
3907987da915Sopenharmony_ci				break;
3908987da915Sopenharmony_ci			case magic_RCRD :
3909987da915Sopenharmony_ci				if (blk < BASEBLKS)
3910987da915Sopenharmony_ci					pos = buf->headsz;
3911987da915Sopenharmony_ci				pos = dorcrd(ctx, blk, pos, buf, nextbuf);
3912987da915Sopenharmony_ci				while (pos >= blocksz) {
3913987da915Sopenharmony_ci					if (optv > 1)
3914987da915Sopenharmony_ci						printf("Skipping block %d"
3915987da915Sopenharmony_ci						" pos 0x%x\n",
3916987da915Sopenharmony_ci						(int)nextblk,(int)pos);
3917987da915Sopenharmony_ci					pos -= (blocksz - blkheadsz);
3918987da915Sopenharmony_ci					nextblk++;
3919987da915Sopenharmony_ci					}
3920987da915Sopenharmony_ci				if ((blocksz - pos) < LOG_RECORD_HEAD_SZ) {
3921987da915Sopenharmony_ci					pos = 0;
3922987da915Sopenharmony_ci					nextblk++;
3923987da915Sopenharmony_ci				}
3924987da915Sopenharmony_ci				if (nextblk != (blk + 1)) {
3925987da915Sopenharmony_ci					nextbuf = read_buffer(ctx,nextblk);
3926987da915Sopenharmony_ci				}
3927987da915Sopenharmony_ci				break;
3928987da915Sopenharmony_ci			default :
3929987da915Sopenharmony_ci				if (!~magic) {
3930987da915Sopenharmony_ci					if (optv)
3931987da915Sopenharmony_ci						printf("   empty block\n");
3932987da915Sopenharmony_ci				}
3933987da915Sopenharmony_ci				break;
3934987da915Sopenharmony_ci			}
3935987da915Sopenharmony_ci		} else {
3936987da915Sopenharmony_ci			fprintf(stderr,"* Could not read block %d\n",nextblk);
3937987da915Sopenharmony_ci			if (ctx->vol) {
3938987da915Sopenharmony_ci			/* In full mode, ignore errors on restart blocks */
3939987da915Sopenharmony_ci				if (blk >= RSTBLKS) {
3940987da915Sopenharmony_ci					done = TRUE;
3941987da915Sopenharmony_ci					err = 1;
3942987da915Sopenharmony_ci				}
3943987da915Sopenharmony_ci			} else {
3944987da915Sopenharmony_ci				done = TRUE;
3945987da915Sopenharmony_ci				err = 1;
3946987da915Sopenharmony_ci			}
3947987da915Sopenharmony_ci		}
3948987da915Sopenharmony_ci		blk = nextblk;
3949987da915Sopenharmony_ci		nextblk++;
3950987da915Sopenharmony_ci
3951987da915Sopenharmony_ci		if (!optr && (log_major >= 2) && (nextblk == RSTBLKS)) {
3952987da915Sopenharmony_ci			finalblk = block_sequence(ctx);
3953987da915Sopenharmony_ci			if (!finalblk) {
3954987da915Sopenharmony_ci				done = TRUE;
3955987da915Sopenharmony_ci				err = 1;
3956987da915Sopenharmony_ci			}
3957987da915Sopenharmony_ci		}
3958987da915Sopenharmony_ci
3959987da915Sopenharmony_ci		if (optr) { /* Only selected range */
3960987da915Sopenharmony_ci			u32 endblk;
3961987da915Sopenharmony_ci
3962987da915Sopenharmony_ci			endblk = (log_major < 2 ? BASEBLKS : RSTBLKS);
3963987da915Sopenharmony_ci			if ((nextblk == endblk) && (nextblk < firstblk))
3964987da915Sopenharmony_ci				 nextblk = firstblk;
3965987da915Sopenharmony_ci			if ((blk >= endblk) && (blk > lastblk))
3966987da915Sopenharmony_ci				done = TRUE;
3967987da915Sopenharmony_ci		} else
3968987da915Sopenharmony_ci			if (optf) { /* Full log, forward */
3969987da915Sopenharmony_ci				if (blk*blocksz >= logfilesz)
3970987da915Sopenharmony_ci					done = TRUE;
3971987da915Sopenharmony_ci			} else
3972987da915Sopenharmony_ci				if (optb || optp || optu || opts
3973987da915Sopenharmony_ci				    || (log_major >= 2)) {
3974987da915Sopenharmony_ci					/* Restart blocks only (2 blocks) */
3975987da915Sopenharmony_ci					if (blk >= RSTBLKS)
3976987da915Sopenharmony_ci						done = TRUE;
3977987da915Sopenharmony_ci				} else { /* Base blocks only (4 blocks) */
3978987da915Sopenharmony_ci					if (blk >= BASEBLKS)
3979987da915Sopenharmony_ci						done = TRUE;
3980987da915Sopenharmony_ci				}
3981987da915Sopenharmony_ci		if (!done) {
3982987da915Sopenharmony_ci			buf = nextbuf;
3983987da915Sopenharmony_ci			if (blk >= RSTBLKS && blk < BASEBLKS) {
3984987da915Sopenharmony_ci				/* The latest buf may be more recent
3985987da915Sopenharmony_ci				   than restart */
3986987da915Sopenharmony_ci				rph = &buf->block.record;
3987987da915Sopenharmony_ci				if ((s64)(sle64_to_cpu(rph->last_end_lsn)
3988987da915Sopenharmony_ci					  - committed_lsn) > 0) {
3989987da915Sopenharmony_ci					committed_lsn =
3990987da915Sopenharmony_ci						sle64_to_cpu(rph->last_end_lsn);
3991987da915Sopenharmony_ci					if (optv)
3992987da915Sopenharmony_ci						printf("* Restart page was "
3993987da915Sopenharmony_ci						       "obsolete, updated "
3994987da915Sopenharmony_ci						       "committed lsn\n");
3995987da915Sopenharmony_ci				}
3996987da915Sopenharmony_ci			}
3997987da915Sopenharmony_ci			if (optv)
3998987da915Sopenharmony_ci				printf("\n* block %d at 0x%llx\n",(int)blk,
3999987da915Sopenharmony_ci					(long long)loclogblk(ctx, blk));
4000987da915Sopenharmony_ci		}
4001987da915Sopenharmony_ci	}
4002987da915Sopenharmony_ci	if (optv && opts && !dirty)
4003987da915Sopenharmony_ci		printf("* Volume is clean, nothing to do\n");
4004987da915Sopenharmony_ci	if (log_major >= 2)
4005987da915Sopenharmony_ci		blk = finalblk;
4006987da915Sopenharmony_ci	if (!err
4007987da915Sopenharmony_ci	    && (optb || optp || optu || (opts && dirty))) {
4008987da915Sopenharmony_ci		playedactions = 0;
4009987da915Sopenharmony_ci		ctx->firstaction = (struct ACTION_RECORD*)NULL;
4010987da915Sopenharmony_ci		ctx->lastaction = (struct ACTION_RECORD*)NULL;
4011987da915Sopenharmony_ci		if (log_major < 2) {
4012987da915Sopenharmony_ci			buf = nextbuf;
4013987da915Sopenharmony_ci			nextbuf = read_buffer(ctx, blk+1);
4014987da915Sopenharmony_ci			startbuf = best_start(buf,nextbuf);
4015987da915Sopenharmony_ci			if (startbuf && (startbuf == nextbuf)) {
4016987da915Sopenharmony_ci				/* nextbuf is better, show blk */
4017987da915Sopenharmony_ci				if (optv && buf) {
4018987da915Sopenharmony_ci					printf("* Ignored block %d at 0x%llx\n",
4019987da915Sopenharmony_ci						(int)blk,
4020987da915Sopenharmony_ci						(long long)loclogblk(ctx, blk));
4021987da915Sopenharmony_ci					if (optv >= 2)
4022987da915Sopenharmony_ci						hexdump(buf->block.data,
4023987da915Sopenharmony_ci								blocksz);
4024987da915Sopenharmony_ci					showheadrcrd(blk, &buf->block.record);
4025987da915Sopenharmony_ci				}
4026987da915Sopenharmony_ci				blk++;
4027987da915Sopenharmony_ci				buf = nextbuf;
4028987da915Sopenharmony_ci			} else {
4029987da915Sopenharmony_ci				/* buf is better, show blk + 1 */
4030987da915Sopenharmony_ci				if (optv && nextbuf) {
4031987da915Sopenharmony_ci					printf("* Ignored block %d at 0x%llx\n",
4032987da915Sopenharmony_ci						(int)(blk + 1),
4033987da915Sopenharmony_ci						(long long)loclogblk(ctx,
4034987da915Sopenharmony_ci								blk + 1));
4035987da915Sopenharmony_ci					if (optv >= 2)
4036987da915Sopenharmony_ci						hexdump(nextbuf->block.data,
4037987da915Sopenharmony_ci								blocksz);
4038987da915Sopenharmony_ci					showheadrcrd(blk + 1,
4039987da915Sopenharmony_ci							&nextbuf->block.record);
4040987da915Sopenharmony_ci				}
4041987da915Sopenharmony_ci			}
4042987da915Sopenharmony_ci			if (startbuf && opts) {
4043987da915Sopenharmony_ci				buf = startbuf = find_latest_block(ctx,
4044987da915Sopenharmony_ci						blk, startbuf);
4045987da915Sopenharmony_ci				latest_lsn = le64_to_cpu(
4046987da915Sopenharmony_ci					buf->block.record.last_end_lsn);
4047987da915Sopenharmony_ci			}
4048987da915Sopenharmony_ci		} else {
4049987da915Sopenharmony_ci			buf = startbuf = read_buffer(ctx, blk);
4050987da915Sopenharmony_ci			nextbuf = (const struct BUFFER*)NULL;
4051987da915Sopenharmony_ci		}
4052987da915Sopenharmony_ci		if (startbuf) {
4053987da915Sopenharmony_ci			/* The latest buf may be more recent than restart */
4054987da915Sopenharmony_ci			rph = &buf->block.record;
4055987da915Sopenharmony_ci			if ((s64)(sle64_to_cpu(rph->last_end_lsn)
4056987da915Sopenharmony_ci					- committed_lsn) > 0) {
4057987da915Sopenharmony_ci				committed_lsn = sle64_to_cpu(rph->last_end_lsn);
4058987da915Sopenharmony_ci				if (optv)
4059987da915Sopenharmony_ci					printf("* Restart page was obsolete\n");
4060987da915Sopenharmony_ci			}
4061987da915Sopenharmony_ci			nextbuf = (const struct BUFFER*)NULL;
4062987da915Sopenharmony_ci			prevbuf = findprevious(ctx, buf);
4063987da915Sopenharmony_ci			if (prevbuf) {
4064987da915Sopenharmony_ci				prevblk = prevbuf->num;
4065987da915Sopenharmony_ci				magic = prevbuf->block.record.magic;
4066987da915Sopenharmony_ci				switch (magic) {
4067987da915Sopenharmony_ci				case magic_RCRD :
4068987da915Sopenharmony_ci					break;
4069987da915Sopenharmony_ci				case magic_CHKD :
4070987da915Sopenharmony_ci					printf("** Unexpected block type CHKD\n");
4071987da915Sopenharmony_ci					err = 1;
4072987da915Sopenharmony_ci					break;
4073987da915Sopenharmony_ci				case magic_RSTR :
4074987da915Sopenharmony_ci					err = 1;
4075987da915Sopenharmony_ci					printf("** Unexpected block type RSTR\n");
4076987da915Sopenharmony_ci					break;
4077987da915Sopenharmony_ci				default :
4078987da915Sopenharmony_ci					err = 1;
4079987da915Sopenharmony_ci					printf("** Invalid block\n");
4080987da915Sopenharmony_ci					break;
4081987da915Sopenharmony_ci				}
4082987da915Sopenharmony_ci			} else
4083987da915Sopenharmony_ci				prevblk = BASEBLKS;
4084987da915Sopenharmony_ci			if (!err)
4085987da915Sopenharmony_ci				err = walkback(ctx, buf, blk,
4086987da915Sopenharmony_ci							prevbuf, prevblk);
4087987da915Sopenharmony_ci		} else {
4088987da915Sopenharmony_ci			fprintf(stderr,"** No valid start block, aborting\n");
4089987da915Sopenharmony_ci			err = 1;
4090987da915Sopenharmony_ci		}
4091987da915Sopenharmony_ci	}
4092987da915Sopenharmony_ci	return (err);
4093987da915Sopenharmony_ci}
4094987da915Sopenharmony_ci
4095987da915Sopenharmony_ciBOOL exception(int num)
4096987da915Sopenharmony_ci{
4097987da915Sopenharmony_ci	int i;
4098987da915Sopenharmony_ci
4099987da915Sopenharmony_ci	i = 0;
4100987da915Sopenharmony_ci	while ((i < 10) && optx[i] && (optx[i] != num))
4101987da915Sopenharmony_ci		i++;
4102987da915Sopenharmony_ci	return (optx[i] == num);
4103987da915Sopenharmony_ci}
4104987da915Sopenharmony_ci
4105987da915Sopenharmony_cistatic void version(void)
4106987da915Sopenharmony_ci{
4107987da915Sopenharmony_ci	printf("\n%s v%s (libntfs-3g) - Recover updates committed by Windows"
4108987da915Sopenharmony_ci			" on an NTFS Volume.\n\n", "ntfsrecover", VERSION);
4109987da915Sopenharmony_ci	printf("Copyright (c) 2012-2017 Jean-Pierre Andre\n");
4110987da915Sopenharmony_ci	printf("\n%s\n%s%s\n", ntfs_gpl, ntfs_bugs, ntfs_home);
4111987da915Sopenharmony_ci}
4112987da915Sopenharmony_ci
4113987da915Sopenharmony_cistatic void usage(void)
4114987da915Sopenharmony_ci{
4115987da915Sopenharmony_ci	fprintf(stderr,"Usage : for recovering the updates committed by Windows :\n");
4116987da915Sopenharmony_ci	fprintf(stderr,"        ntfsrecover partition\n");
4117987da915Sopenharmony_ci	fprintf(stderr,"                (e.g. ntfsrecover /dev/sda1)\n");
4118987da915Sopenharmony_ci	fprintf(stderr,"Advanced : ntfsrecover [-b] [-c first-last] [-i] [-f] [-n] [-p count]\n");
4119987da915Sopenharmony_ci	fprintf(stderr,"                    [-r first-last] [-t] [-u count] [-v] partition\n");
4120987da915Sopenharmony_ci	fprintf(stderr,"	   -b : show the full log backward\n");
4121987da915Sopenharmony_ci	fprintf(stderr,"	   -c : restrict to the actions related to cluster range\n");
4122987da915Sopenharmony_ci	fprintf(stderr,"	   -i : show invalid (stale) records\n");
4123987da915Sopenharmony_ci	fprintf(stderr,"	   -f : show the full log forward\n");
4124987da915Sopenharmony_ci	fprintf(stderr,"	   -h : show this help information\n");
4125987da915Sopenharmony_ci	fprintf(stderr,"	   -k : kill fast restart data\n");
4126987da915Sopenharmony_ci	fprintf(stderr,"	   -n : do not apply any modification\n");
4127987da915Sopenharmony_ci	fprintf(stderr,"	   -p : undo the latest count transaction sets and play one\n");
4128987da915Sopenharmony_ci	fprintf(stderr,"	   -r : show a range of log blocks forward\n");
4129987da915Sopenharmony_ci	fprintf(stderr,"	   -s : sync the committed changes (default)\n");
4130987da915Sopenharmony_ci	fprintf(stderr,"	   -t : show transactions\n");
4131987da915Sopenharmony_ci	fprintf(stderr,"	   -u : undo the latest count transaction sets\n");
4132987da915Sopenharmony_ci	fprintf(stderr,"	   -v : show more information (-vv yet more)\n");
4133987da915Sopenharmony_ci	fprintf(stderr,"	   -V : show version and exit\n");
4134987da915Sopenharmony_ci}
4135987da915Sopenharmony_ci
4136987da915Sopenharmony_ci/*
4137987da915Sopenharmony_ci *		Process command options
4138987da915Sopenharmony_ci */
4139987da915Sopenharmony_ci
4140987da915Sopenharmony_cistatic BOOL getoptions(int argc, char *argv[])
4141987da915Sopenharmony_ci{
4142987da915Sopenharmony_ci	int c;
4143987da915Sopenharmony_ci	int xcount;
4144987da915Sopenharmony_ci	u32 xval;
4145987da915Sopenharmony_ci	char *endptr;
4146987da915Sopenharmony_ci	BOOL err;
4147987da915Sopenharmony_ci	static const char *sopt = "-bc:hifknp:r:stu:vVx:";
4148987da915Sopenharmony_ci	static const struct option lopt[] = {
4149987da915Sopenharmony_ci		{ "backward",		no_argument,		NULL, 'b' },
4150987da915Sopenharmony_ci		{ "clusters",		required_argument,	NULL, 'c' },
4151987da915Sopenharmony_ci		{ "forward",		no_argument,		NULL, 'f' },
4152987da915Sopenharmony_ci		{ "help",		no_argument,		NULL, 'h' },
4153987da915Sopenharmony_ci		{ "kill-fast-restart",	no_argument,		NULL, 'k' },
4154987da915Sopenharmony_ci		{ "no-action",		no_argument,		NULL, 'n' },
4155987da915Sopenharmony_ci		{ "play",		required_argument,	NULL, 'p' },
4156987da915Sopenharmony_ci		{ "range",		required_argument,	NULL, 'r' },
4157987da915Sopenharmony_ci		{ "sync",		no_argument,		NULL, 's' },
4158987da915Sopenharmony_ci		{ "transactions",	no_argument,		NULL, 't' },
4159987da915Sopenharmony_ci		{ "undo",		required_argument,	NULL, 'u' },
4160987da915Sopenharmony_ci		{ "verbose",		no_argument,		NULL, 'v' },
4161987da915Sopenharmony_ci		{ "version",		no_argument,		NULL, 'V' },
4162987da915Sopenharmony_ci		{ "exceptions",		required_argument,	NULL, 'x' },
4163987da915Sopenharmony_ci		{ NULL, 		0, NULL, 0 }
4164987da915Sopenharmony_ci	};
4165987da915Sopenharmony_ci
4166987da915Sopenharmony_ci	err = FALSE;
4167987da915Sopenharmony_ci	optb = FALSE;
4168987da915Sopenharmony_ci	optc = FALSE;
4169987da915Sopenharmony_ci	optd = FALSE;
4170987da915Sopenharmony_ci	optf = FALSE;
4171987da915Sopenharmony_ci	opth = FALSE;
4172987da915Sopenharmony_ci	opti = FALSE;
4173987da915Sopenharmony_ci	optk = FALSE;
4174987da915Sopenharmony_ci	optn = FALSE;
4175987da915Sopenharmony_ci	optp = FALSE;
4176987da915Sopenharmony_ci	optr = FALSE;
4177987da915Sopenharmony_ci	opts = 0;
4178987da915Sopenharmony_ci	optt = FALSE;
4179987da915Sopenharmony_ci	optu = FALSE;
4180987da915Sopenharmony_ci	optv = 0;
4181987da915Sopenharmony_ci	optV = FALSE;
4182987da915Sopenharmony_ci	optx[0] = 0;
4183987da915Sopenharmony_ci
4184987da915Sopenharmony_ci	while ((c = getopt_long(argc, argv, sopt, lopt, NULL)) != -1) {
4185987da915Sopenharmony_ci		switch (c) {
4186987da915Sopenharmony_ci		case 1:	/* A non-option argument */
4187987da915Sopenharmony_ci			if (optind == argc)
4188987da915Sopenharmony_ci				optd = TRUE;
4189987da915Sopenharmony_ci			else {
4190987da915Sopenharmony_ci				fprintf(stderr, "Device must be the"
4191987da915Sopenharmony_ci						" last argument.\n");
4192987da915Sopenharmony_ci				err = TRUE;
4193987da915Sopenharmony_ci			}
4194987da915Sopenharmony_ci			break;
4195987da915Sopenharmony_ci		case 'b':
4196987da915Sopenharmony_ci			optb = TRUE;
4197987da915Sopenharmony_ci			break;
4198987da915Sopenharmony_ci		case 'c':
4199987da915Sopenharmony_ci			firstlcn = strtoull(optarg, &endptr, 0);
4200987da915Sopenharmony_ci			lastlcn = firstlcn;
4201987da915Sopenharmony_ci			if (*endptr == '-')
4202987da915Sopenharmony_ci				lastlcn = strtoull(++endptr, &endptr, 0);
4203987da915Sopenharmony_ci			if (*endptr || (lastlcn < firstlcn)) {
4204987da915Sopenharmony_ci				fprintf(stderr,"Bad cluster range\n");
4205987da915Sopenharmony_ci				err = TRUE;
4206987da915Sopenharmony_ci			} else
4207987da915Sopenharmony_ci				optc = TRUE;
4208987da915Sopenharmony_ci			break;
4209987da915Sopenharmony_ci		case 'f':
4210987da915Sopenharmony_ci			optf = TRUE;
4211987da915Sopenharmony_ci			break;
4212987da915Sopenharmony_ci		case '?':
4213987da915Sopenharmony_ci		case 'h':
4214987da915Sopenharmony_ci			opth = TRUE;
4215987da915Sopenharmony_ci			break;
4216987da915Sopenharmony_ci		case 'k':
4217987da915Sopenharmony_ci			optk = TRUE;
4218987da915Sopenharmony_ci			break;
4219987da915Sopenharmony_ci		case 'n':
4220987da915Sopenharmony_ci			optn = TRUE;
4221987da915Sopenharmony_ci			break;
4222987da915Sopenharmony_ci		case 'p':
4223987da915Sopenharmony_ci			playcount = strtoull(optarg, &endptr, 0);
4224987da915Sopenharmony_ci			if (*endptr) {
4225987da915Sopenharmony_ci				fprintf(stderr,"Bad play count\n");
4226987da915Sopenharmony_ci				err = TRUE;
4227987da915Sopenharmony_ci			} else
4228987da915Sopenharmony_ci				optp = TRUE;
4229987da915Sopenharmony_ci			break;
4230987da915Sopenharmony_ci		case 'r' :
4231987da915Sopenharmony_ci			firstblk = strtoull(optarg, &endptr, 0);
4232987da915Sopenharmony_ci			lastblk = firstblk;
4233987da915Sopenharmony_ci			if (*endptr == '-')
4234987da915Sopenharmony_ci				lastblk = strtoull(++endptr, &endptr, 0);
4235987da915Sopenharmony_ci			if (*endptr || (lastblk < firstblk)) {
4236987da915Sopenharmony_ci				fprintf(stderr,"Bad log block range\n");
4237987da915Sopenharmony_ci				err = TRUE;
4238987da915Sopenharmony_ci			} else
4239987da915Sopenharmony_ci				optr = TRUE;
4240987da915Sopenharmony_ci			break;
4241987da915Sopenharmony_ci		case 's':
4242987da915Sopenharmony_ci			opts++;
4243987da915Sopenharmony_ci			break;
4244987da915Sopenharmony_ci		case 't':
4245987da915Sopenharmony_ci			optt = TRUE;
4246987da915Sopenharmony_ci			break;
4247987da915Sopenharmony_ci		case 'u':
4248987da915Sopenharmony_ci			playcount = strtoull(optarg, &endptr, 0);
4249987da915Sopenharmony_ci			if (*endptr) {
4250987da915Sopenharmony_ci				fprintf(stderr,"Bad undo count\n");
4251987da915Sopenharmony_ci				err = TRUE;
4252987da915Sopenharmony_ci			} else
4253987da915Sopenharmony_ci				optu = TRUE;
4254987da915Sopenharmony_ci			break;
4255987da915Sopenharmony_ci		case 'v':
4256987da915Sopenharmony_ci			optv++;
4257987da915Sopenharmony_ci			break;
4258987da915Sopenharmony_ci		case 'V':
4259987da915Sopenharmony_ci			optV = TRUE;
4260987da915Sopenharmony_ci			break;
4261987da915Sopenharmony_ci		case 'x':
4262987da915Sopenharmony_ci				/*
4263987da915Sopenharmony_ci				 * Undocumented : actions to execute, though
4264987da915Sopenharmony_ci				 * they should be skipped under normal rules.
4265987da915Sopenharmony_ci				 */
4266987da915Sopenharmony_ci			xcount = 0;
4267987da915Sopenharmony_ci			xval = strtoull(optarg, &endptr, 0);
4268987da915Sopenharmony_ci			while ((*endptr == ',')
4269987da915Sopenharmony_ci			    && (xcount < (MAXEXCEPTION - 1))) {
4270987da915Sopenharmony_ci				optx[xcount++] = xval;
4271987da915Sopenharmony_ci				xval = strtoull(++endptr, &endptr, 0);
4272987da915Sopenharmony_ci			}
4273987da915Sopenharmony_ci			if (*endptr || (xcount >= MAXEXCEPTION)) {
4274987da915Sopenharmony_ci				fprintf(stderr,"Bad exception list\n");
4275987da915Sopenharmony_ci				err = TRUE;
4276987da915Sopenharmony_ci			} else {
4277987da915Sopenharmony_ci				optx[xcount++] = xval;
4278987da915Sopenharmony_ci				optx[xcount] = 0;
4279987da915Sopenharmony_ci			}
4280987da915Sopenharmony_ci			break;
4281987da915Sopenharmony_ci		default:
4282987da915Sopenharmony_ci			fprintf(stderr,"Unknown option '%s'.\n",
4283987da915Sopenharmony_ci							argv[optind - 1]);
4284987da915Sopenharmony_ci			err = TRUE;
4285987da915Sopenharmony_ci		}
4286987da915Sopenharmony_ci	}
4287987da915Sopenharmony_ci
4288987da915Sopenharmony_ci	if (!optd && !optV && !opth) {
4289987da915Sopenharmony_ci		fprintf(stderr,"Device argument is missing\n");
4290987da915Sopenharmony_ci		err = TRUE;
4291987da915Sopenharmony_ci	}
4292987da915Sopenharmony_ci	if (!(optb || optf || optp || optr || opts || optt || optu || optV))
4293987da915Sopenharmony_ci		opts = 1;
4294987da915Sopenharmony_ci	if (optb && (optf || optr || opts)) {
4295987da915Sopenharmony_ci		fprintf(stderr,"Options -f, -r and -s are incompatible with -b\n");
4296987da915Sopenharmony_ci		err = TRUE;
4297987da915Sopenharmony_ci	}
4298987da915Sopenharmony_ci	if (optf && (optp || opts || optu)) {
4299987da915Sopenharmony_ci		fprintf(stderr,"Options -p, -s and -u are incompatible with -f\n");
4300987da915Sopenharmony_ci		err = TRUE;
4301987da915Sopenharmony_ci	}
4302987da915Sopenharmony_ci	if (optp && (optr || opts || optt || optu)) {
4303987da915Sopenharmony_ci		fprintf(stderr,"Options -r, -s, -t and -u are incompatible with -p\n");
4304987da915Sopenharmony_ci		err = TRUE;
4305987da915Sopenharmony_ci	}
4306987da915Sopenharmony_ci	if (optr && (opts || optu)) {
4307987da915Sopenharmony_ci		fprintf(stderr,"Options -s and -u are incompatible with -r\n");
4308987da915Sopenharmony_ci		err = TRUE;
4309987da915Sopenharmony_ci	}
4310987da915Sopenharmony_ci	if (opts && (optt || optu)) {
4311987da915Sopenharmony_ci		fprintf(stderr,"Options -t and -u are incompatible with -s\n");
4312987da915Sopenharmony_ci		err = TRUE;
4313987da915Sopenharmony_ci	}
4314987da915Sopenharmony_ci
4315987da915Sopenharmony_ci	if (opth || err)
4316987da915Sopenharmony_ci		usage();
4317987da915Sopenharmony_ci	else
4318987da915Sopenharmony_ci		if (optV)
4319987da915Sopenharmony_ci			version();
4320987da915Sopenharmony_ci	return (!err);
4321987da915Sopenharmony_ci}
4322987da915Sopenharmony_ci
4323987da915Sopenharmony_ci/*
4324987da915Sopenharmony_ci *		Quick checks on the layout of needed structs
4325987da915Sopenharmony_ci */
4326987da915Sopenharmony_ci
4327987da915Sopenharmony_cistatic BOOL checkstructs(void)
4328987da915Sopenharmony_ci{
4329987da915Sopenharmony_ci	BOOL ok;
4330987da915Sopenharmony_ci
4331987da915Sopenharmony_ci	ok = TRUE;
4332987da915Sopenharmony_ci   	if (sizeof(RECORD_PAGE_HEADER) != 40) {
4333987da915Sopenharmony_ci      		fprintf(stderr,
4334987da915Sopenharmony_ci			"* error : bad sizeof(RECORD_PAGE_HEADER) %d\n",
4335987da915Sopenharmony_ci			(int)sizeof(RECORD_PAGE_HEADER));
4336987da915Sopenharmony_ci		ok = FALSE;
4337987da915Sopenharmony_ci	}
4338987da915Sopenharmony_ci	if (sizeof(LOG_RECORD) != 88) {
4339987da915Sopenharmony_ci      		fprintf(stderr,
4340987da915Sopenharmony_ci			"* error : bad sizeof(LOG_RECORD) %d\n",
4341987da915Sopenharmony_ci			(int)sizeof(LOG_RECORD));
4342987da915Sopenharmony_ci		ok = FALSE;
4343987da915Sopenharmony_ci	}
4344987da915Sopenharmony_ci   	if (sizeof(RESTART_PAGE_HEADER) != 32) {
4345987da915Sopenharmony_ci      		fprintf(stderr,
4346987da915Sopenharmony_ci			"* error : bad sizeof(RESTART_PAGE_HEADER) %d\n",
4347987da915Sopenharmony_ci			(int)sizeof(RESTART_PAGE_HEADER));
4348987da915Sopenharmony_ci		ok = FALSE;
4349987da915Sopenharmony_ci	}
4350987da915Sopenharmony_ci   	if (sizeof(RESTART_AREA) != 48) {
4351987da915Sopenharmony_ci      		fprintf(stderr,
4352987da915Sopenharmony_ci			"* error : bad sizeof(RESTART_AREA) %d\n",
4353987da915Sopenharmony_ci			(int)sizeof(RESTART_AREA));
4354987da915Sopenharmony_ci		ok = FALSE;
4355987da915Sopenharmony_ci	}
4356987da915Sopenharmony_ci   	if (sizeof(ATTR_OLD) != 44) {
4357987da915Sopenharmony_ci      		fprintf(stderr,
4358987da915Sopenharmony_ci			"* error : bad sizeof(ATTR_OLD) %d\n",
4359987da915Sopenharmony_ci			(int)sizeof(ATTR_OLD));
4360987da915Sopenharmony_ci		ok = FALSE;
4361987da915Sopenharmony_ci	}
4362987da915Sopenharmony_ci   	if (sizeof(ATTR_NEW) != 40) {
4363987da915Sopenharmony_ci      		fprintf(stderr,
4364987da915Sopenharmony_ci			"* error : bad sizeof(ATTR_NEW) %d\n",
4365987da915Sopenharmony_ci			(int)sizeof(ATTR_NEW));
4366987da915Sopenharmony_ci		ok = FALSE;
4367987da915Sopenharmony_ci	}
4368987da915Sopenharmony_ci	if (LastAction != 38) {
4369987da915Sopenharmony_ci      		fprintf(stderr,
4370987da915Sopenharmony_ci			"* error : bad action list, %d actions\n",
4371987da915Sopenharmony_ci			(int)LastAction);
4372987da915Sopenharmony_ci		ok = FALSE;
4373987da915Sopenharmony_ci	}
4374987da915Sopenharmony_ci	return (ok);
4375987da915Sopenharmony_ci}
4376987da915Sopenharmony_ci
4377987da915Sopenharmony_ciint main(int argc, char *argv[])
4378987da915Sopenharmony_ci{
4379987da915Sopenharmony_ci	CONTEXT ctx;
4380987da915Sopenharmony_ci	unsigned int i;
4381987da915Sopenharmony_ci	int err;
4382987da915Sopenharmony_ci
4383987da915Sopenharmony_ci	err = 1;
4384987da915Sopenharmony_ci	if (checkstructs()
4385987da915Sopenharmony_ci	    && getoptions(argc,argv)) {
4386987da915Sopenharmony_ci		if (optV || opth) {
4387987da915Sopenharmony_ci			err = 0;
4388987da915Sopenharmony_ci		} else {
4389987da915Sopenharmony_ci			redocount = 0;
4390987da915Sopenharmony_ci			undocount = 0;
4391987da915Sopenharmony_ci			actionnum = 0;
4392987da915Sopenharmony_ci			attrcount = 0;
4393987da915Sopenharmony_ci			redos_met = 0;
4394987da915Sopenharmony_ci			attrtable = (struct ATTR**)NULL;
4395987da915Sopenharmony_ci			for (i=0; i<(BUFFERCNT + BASEBLKS); i++)
4396987da915Sopenharmony_ci				buffer_table[i] = (struct BUFFER*)NULL;
4397987da915Sopenharmony_ci			ntfs_log_set_handler(ntfs_log_handler_outerr);
4398987da915Sopenharmony_ci			if (open_volume(&ctx, argv[argc - 1])) {
4399987da915Sopenharmony_ci				if (!ctx.vol
4400987da915Sopenharmony_ci				    && (opts || optp || optu)) {
4401987da915Sopenharmony_ci					printf("Options -s, -p and -u"
4402987da915Sopenharmony_ci						" require a full device\n");
4403987da915Sopenharmony_ci					err = 1;
4404987da915Sopenharmony_ci				} else {
4405987da915Sopenharmony_ci					err = walk(&ctx);
4406987da915Sopenharmony_ci					if (ctx.vol) {
4407987da915Sopenharmony_ci						if ((optp || optu || opts)
4408987da915Sopenharmony_ci						    && !err
4409987da915Sopenharmony_ci						    && !optn) {
4410987da915Sopenharmony_ci							reset_logfile(&ctx);
4411987da915Sopenharmony_ci						}
4412987da915Sopenharmony_ci						ntfs_attr_close(log_na);
4413987da915Sopenharmony_ci						ntfs_inode_close(log_ni);
4414987da915Sopenharmony_ci						ntfs_umount(ctx.vol, TRUE);
4415987da915Sopenharmony_ci					} else
4416987da915Sopenharmony_ci						fclose(ctx.file);
4417987da915Sopenharmony_ci				}
4418987da915Sopenharmony_ci			} else
4419987da915Sopenharmony_ci				fprintf(stderr,"Could not open %s\n",
4420987da915Sopenharmony_ci							argv[argc - 1]);
4421987da915Sopenharmony_ci			for (i=0; i<(BUFFERCNT + BASEBLKS); i++)
4422987da915Sopenharmony_ci				free(buffer_table[i]);
4423987da915Sopenharmony_ci			for (i=0; i<attrcount; i++)
4424987da915Sopenharmony_ci				free(attrtable[i]);
4425987da915Sopenharmony_ci			free(attrtable);
4426987da915Sopenharmony_ci			if (ctx.vol) {
4427987da915Sopenharmony_ci				freeclusterentry((struct STORE*)NULL);
4428987da915Sopenharmony_ci				show_redos();
4429987da915Sopenharmony_ci			}
4430987da915Sopenharmony_ci		}
4431987da915Sopenharmony_ci	}
4432987da915Sopenharmony_ci	if (err)
4433987da915Sopenharmony_ci		exit(1);
4434987da915Sopenharmony_ci	return (0);
4435987da915Sopenharmony_ci}
4436