1987da915Sopenharmony_ci/*
2987da915Sopenharmony_ci *		Redo or undo a list of logged actions
3987da915Sopenharmony_ci *
4987da915Sopenharmony_ci * Copyright (c) 2014-2017 Jean-Pierre Andre
5987da915Sopenharmony_ci *
6987da915Sopenharmony_ci */
7987da915Sopenharmony_ci
8987da915Sopenharmony_ci/*
9987da915Sopenharmony_ci * This program is free software; you can redistribute it and/or modify
10987da915Sopenharmony_ci * it under the terms of the GNU General Public License as published by
11987da915Sopenharmony_ci * the Free Software Foundation; either version 2 of the License, or
12987da915Sopenharmony_ci * (at your option) any later version.
13987da915Sopenharmony_ci *
14987da915Sopenharmony_ci * This program is distributed in the hope that it will be useful,
15987da915Sopenharmony_ci * but WITHOUT ANY WARRANTY; without even the implied warranty of
16987da915Sopenharmony_ci * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
17987da915Sopenharmony_ci * GNU General Public License for more details.
18987da915Sopenharmony_ci *
19987da915Sopenharmony_ci * You should have received a copy of the GNU General Public License
20987da915Sopenharmony_ci * along with this program (in the main directory of the NTFS-3G
21987da915Sopenharmony_ci * distribution in the file COPYING); if not, write to the Free Software
22987da915Sopenharmony_ci * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
23987da915Sopenharmony_ci */
24987da915Sopenharmony_ci
25987da915Sopenharmony_ci#include "config.h"
26987da915Sopenharmony_ci
27987da915Sopenharmony_ci#ifdef HAVE_STDLIB_H
28987da915Sopenharmony_ci#include <stdlib.h>
29987da915Sopenharmony_ci#endif
30987da915Sopenharmony_ci#ifdef HAVE_STDIO_H
31987da915Sopenharmony_ci#include <stdio.h>
32987da915Sopenharmony_ci#endif
33987da915Sopenharmony_ci#ifdef HAVE_UNISTD_H
34987da915Sopenharmony_ci#include <unistd.h>
35987da915Sopenharmony_ci#endif
36987da915Sopenharmony_ci#ifdef HAVE_FCNTL_H
37987da915Sopenharmony_ci#include <fcntl.h>
38987da915Sopenharmony_ci#endif
39987da915Sopenharmony_ci#ifdef HAVE_ERRNO_H
40987da915Sopenharmony_ci#include <errno.h>
41987da915Sopenharmony_ci#endif
42987da915Sopenharmony_ci#ifdef HAVE_STRING_H
43987da915Sopenharmony_ci#include <string.h>
44987da915Sopenharmony_ci#endif
45987da915Sopenharmony_ci#ifdef HAVE_MALLOC_H
46987da915Sopenharmony_ci#include <malloc.h>
47987da915Sopenharmony_ci#endif
48987da915Sopenharmony_ci#ifdef HAVE_TIME_H
49987da915Sopenharmony_ci#include <time.h>
50987da915Sopenharmony_ci#endif
51987da915Sopenharmony_ci
52987da915Sopenharmony_ci#include "types.h"
53987da915Sopenharmony_ci#include "endians.h"
54987da915Sopenharmony_ci#include "support.h"
55987da915Sopenharmony_ci#include "layout.h"
56987da915Sopenharmony_ci#include "param.h"
57987da915Sopenharmony_ci#include "ntfstime.h"
58987da915Sopenharmony_ci#include "device_io.h"
59987da915Sopenharmony_ci#include "device.h"
60987da915Sopenharmony_ci#include "logging.h"
61987da915Sopenharmony_ci#include "runlist.h"
62987da915Sopenharmony_ci#include "mft.h"
63987da915Sopenharmony_ci#include "inode.h"
64987da915Sopenharmony_ci#include "attrib.h"
65987da915Sopenharmony_ci#include "bitmap.h"
66987da915Sopenharmony_ci#include "index.h"
67987da915Sopenharmony_ci#include "volume.h"
68987da915Sopenharmony_ci#include "unistr.h"
69987da915Sopenharmony_ci#include "mst.h"
70987da915Sopenharmony_ci#include "logfile.h"
71987da915Sopenharmony_ci#include "ntfsrecover.h"
72987da915Sopenharmony_ci#include "misc.h"
73987da915Sopenharmony_ci
74987da915Sopenharmony_cistruct STORE {
75987da915Sopenharmony_ci	struct STORE *upper;
76987da915Sopenharmony_ci	struct STORE *lower;
77987da915Sopenharmony_ci	LCN lcn;
78987da915Sopenharmony_ci	char data[1];
79987da915Sopenharmony_ci} ;
80987da915Sopenharmony_ci
81987da915Sopenharmony_ci#define dump hexdump
82987da915Sopenharmony_ci
83987da915Sopenharmony_cistruct STORE *cluster_door = (struct STORE*)NULL;
84987da915Sopenharmony_ci
85987da915Sopenharmony_ci/* check whether a MFT or INDX record is older than action */
86987da915Sopenharmony_ci#define older_record(rec, logr) ((s64)(sle64_to_cpu((rec)->lsn) \
87987da915Sopenharmony_ci			- sle64_to_cpu((logr)->this_lsn)) < 0)
88987da915Sopenharmony_ci/* check whether a MFT or INDX record is newer than action */
89987da915Sopenharmony_ci#define newer_record(rec, logr) ((s64)(sle64_to_cpu((rec)->lsn) \
90987da915Sopenharmony_ci			- sle64_to_cpu((logr)->this_lsn)) > 0)
91987da915Sopenharmony_ci
92987da915Sopenharmony_ci/*
93987da915Sopenharmony_ci *		A few functions for debugging
94987da915Sopenharmony_ci */
95987da915Sopenharmony_ci
96987da915Sopenharmony_cistatic int matchcount(const char *d, const char *s, int n)
97987da915Sopenharmony_ci{
98987da915Sopenharmony_ci	int m;
99987da915Sopenharmony_ci
100987da915Sopenharmony_ci	m = 0;
101987da915Sopenharmony_ci	while ((--n >= 0) && (*d++ == *s++)) m++;
102987da915Sopenharmony_ci	return (m);
103987da915Sopenharmony_ci}
104987da915Sopenharmony_ci
105987da915Sopenharmony_ci/*
106987da915Sopenharmony_cistatic void locate(const char *s, int n, const char *p, int m)
107987da915Sopenharmony_ci{
108987da915Sopenharmony_ci	int i,j;
109987da915Sopenharmony_ci
110987da915Sopenharmony_ci	for (i=0; i<=(n - m); i++)
111987da915Sopenharmony_ci		if (s[i] == *p) {
112987da915Sopenharmony_ci			j = 1;
113987da915Sopenharmony_ci			while ((j < m) && (s[i + j] == p[j]))
114987da915Sopenharmony_ci				j++;
115987da915Sopenharmony_ci			if (j == m)
116987da915Sopenharmony_ci				printf("=== found at offset 0x%x %d\n",i,i);
117987da915Sopenharmony_ci		}
118987da915Sopenharmony_ci}
119987da915Sopenharmony_ci*/
120987da915Sopenharmony_ci
121987da915Sopenharmony_cistatic u64 inode_number(const LOG_RECORD *logr)
122987da915Sopenharmony_ci{
123987da915Sopenharmony_ci	u64 offset;
124987da915Sopenharmony_ci
125987da915Sopenharmony_ci	offset = ((u64)sle64_to_cpu(logr->target_vcn)
126987da915Sopenharmony_ci					<< clusterbits)
127987da915Sopenharmony_ci		+ ((u32)le16_to_cpu(logr->cluster_index)
128987da915Sopenharmony_ci					<< NTFS_BLOCK_SIZE_BITS);
129987da915Sopenharmony_ci	return (offset >> mftrecbits);
130987da915Sopenharmony_ci}
131987da915Sopenharmony_ci
132987da915Sopenharmony_ci/*
133987da915Sopenharmony_ci *		Find an in-memory copy of a needed cluster
134987da915Sopenharmony_ci *
135987da915Sopenharmony_ci *	Optionally, allocate a copy.
136987da915Sopenharmony_ci */
137987da915Sopenharmony_ci
138987da915Sopenharmony_cistatic struct STORE *getclusterentry(LCN lcn, BOOL create)
139987da915Sopenharmony_ci{
140987da915Sopenharmony_ci	struct STORE **current;
141987da915Sopenharmony_ci	struct STORE *newone;
142987da915Sopenharmony_ci
143987da915Sopenharmony_ci	current = &cluster_door;
144987da915Sopenharmony_ci		/* A minimal binary tree should be enough */
145987da915Sopenharmony_ci	while (*current && (lcn != (*current)->lcn)) {
146987da915Sopenharmony_ci		if (lcn > (*current)->lcn)
147987da915Sopenharmony_ci			current = &(*current)->upper;
148987da915Sopenharmony_ci		else
149987da915Sopenharmony_ci			current = &(*current)->lower;
150987da915Sopenharmony_ci	}
151987da915Sopenharmony_ci	if (create && !*current) {
152987da915Sopenharmony_ci		newone = (struct STORE*)malloc(sizeof(struct STORE)
153987da915Sopenharmony_ci						+ clustersz);
154987da915Sopenharmony_ci		if (newone) {
155987da915Sopenharmony_ci			newone->upper = (struct STORE*)NULL;
156987da915Sopenharmony_ci			newone->lower = (struct STORE*)NULL;
157987da915Sopenharmony_ci			newone->lcn = lcn;
158987da915Sopenharmony_ci			*current = newone;
159987da915Sopenharmony_ci		}
160987da915Sopenharmony_ci	}
161987da915Sopenharmony_ci	return (*current);
162987da915Sopenharmony_ci}
163987da915Sopenharmony_ci
164987da915Sopenharmony_civoid freeclusterentry(struct STORE *entry)
165987da915Sopenharmony_ci{
166987da915Sopenharmony_ci	if (!entry) {
167987da915Sopenharmony_ci		if (cluster_door)
168987da915Sopenharmony_ci			freeclusterentry(cluster_door);
169987da915Sopenharmony_ci		cluster_door = (struct STORE*)NULL;
170987da915Sopenharmony_ci	} else {
171987da915Sopenharmony_ci		if (optv)
172987da915Sopenharmony_ci			printf("* cluster 0x%llx %s updated\n",
173987da915Sopenharmony_ci					(long long)entry->lcn,
174987da915Sopenharmony_ci					(optn ? "would be" : "was"));
175987da915Sopenharmony_ci		if (entry->upper)
176987da915Sopenharmony_ci			freeclusterentry(entry->upper);
177987da915Sopenharmony_ci		if (entry->lower)
178987da915Sopenharmony_ci			freeclusterentry(entry->lower);
179987da915Sopenharmony_ci		free(entry);
180987da915Sopenharmony_ci	}
181987da915Sopenharmony_ci}
182987da915Sopenharmony_ci
183987da915Sopenharmony_ci/*
184987da915Sopenharmony_ci *		Check whether an attribute type is a valid one
185987da915Sopenharmony_ci */
186987da915Sopenharmony_ci
187987da915Sopenharmony_cistatic BOOL valid_type(ATTR_TYPES type)
188987da915Sopenharmony_ci{
189987da915Sopenharmony_ci	BOOL ok;
190987da915Sopenharmony_ci
191987da915Sopenharmony_ci	switch (type) {
192987da915Sopenharmony_ci	case AT_STANDARD_INFORMATION :
193987da915Sopenharmony_ci	case AT_ATTRIBUTE_LIST :
194987da915Sopenharmony_ci	case AT_FILE_NAME :
195987da915Sopenharmony_ci	case AT_OBJECT_ID :
196987da915Sopenharmony_ci	case AT_SECURITY_DESCRIPTOR :
197987da915Sopenharmony_ci	case AT_VOLUME_NAME :
198987da915Sopenharmony_ci	case AT_VOLUME_INFORMATION :
199987da915Sopenharmony_ci	case AT_DATA :
200987da915Sopenharmony_ci	case AT_INDEX_ROOT :
201987da915Sopenharmony_ci	case AT_INDEX_ALLOCATION :
202987da915Sopenharmony_ci	case AT_BITMAP :
203987da915Sopenharmony_ci	case AT_REPARSE_POINT :
204987da915Sopenharmony_ci	case AT_EA_INFORMATION :
205987da915Sopenharmony_ci	case AT_EA :
206987da915Sopenharmony_ci	case AT_PROPERTY_SET :
207987da915Sopenharmony_ci	case AT_LOGGED_UTILITY_STREAM :
208987da915Sopenharmony_ci	case AT_FIRST_USER_DEFINED_ATTRIBUTE :
209987da915Sopenharmony_ci	case AT_END :
210987da915Sopenharmony_ci		ok = TRUE;
211987da915Sopenharmony_ci		break;
212987da915Sopenharmony_ci	default :
213987da915Sopenharmony_ci		ok = FALSE;
214987da915Sopenharmony_ci		break;
215987da915Sopenharmony_ci	}
216987da915Sopenharmony_ci	return (ok);
217987da915Sopenharmony_ci}
218987da915Sopenharmony_ci
219987da915Sopenharmony_ci/*
220987da915Sopenharmony_ci *		Rough check of sanity of an index list
221987da915Sopenharmony_ci */
222987da915Sopenharmony_ci
223987da915Sopenharmony_cistatic int sanity_indx_list(const char *buffer, u32 k, u32 end)
224987da915Sopenharmony_ci{
225987da915Sopenharmony_ci	le64 inode;
226987da915Sopenharmony_ci	int err;
227987da915Sopenharmony_ci	int lth;
228987da915Sopenharmony_ci	BOOL done;
229987da915Sopenharmony_ci
230987da915Sopenharmony_ci	err = 0;
231987da915Sopenharmony_ci	done = FALSE;
232987da915Sopenharmony_ci	while ((k <= end) && !done && !err) {
233987da915Sopenharmony_ci		lth = getle16(buffer,k+8);
234987da915Sopenharmony_ci		if (optv > 1)
235987da915Sopenharmony_ci			/* Usual indexes can be determined from size */
236987da915Sopenharmony_ci			switch (lth) {
237987da915Sopenharmony_ci			case 16 : /* final without subnode */
238987da915Sopenharmony_ci			case 24 : /* final with subnode */
239987da915Sopenharmony_ci				printf("index to none lth 0x%x"
240987da915Sopenharmony_ci					" flags 0x%x pos 0x%x\n",
241987da915Sopenharmony_ci					(int)lth,
242987da915Sopenharmony_ci					(int)getle16(buffer,k+12),(int)k);
243987da915Sopenharmony_ci				break;
244987da915Sopenharmony_ci			case 32 : /* $R in $Reparse */
245987da915Sopenharmony_ci					/* Badly aligned */
246987da915Sopenharmony_ci				memcpy(&inode, &buffer[k + 20], 8);
247987da915Sopenharmony_ci				printf("index to reparse of 0x%016llx lth 0x%x"
248987da915Sopenharmony_ci					" flags 0x%x pos 0x%x\n",
249987da915Sopenharmony_ci					(long long)le64_to_cpu(inode),
250987da915Sopenharmony_ci					(int)lth,
251987da915Sopenharmony_ci					(int)getle16(buffer,k+12),(int)k);
252987da915Sopenharmony_ci				break;
253987da915Sopenharmony_ci			case 40 : /* $SII in $Secure */
254987da915Sopenharmony_ci				printf("index to securid 0x%lx lth 0x%x"
255987da915Sopenharmony_ci					" flags 0x%x pos 0x%x\n",
256987da915Sopenharmony_ci					(long)getle32(buffer,k + 16),
257987da915Sopenharmony_ci					(int)lth,
258987da915Sopenharmony_ci					(int)getle16(buffer,k+12),(int)k);
259987da915Sopenharmony_ci				break;
260987da915Sopenharmony_ci			case 48 : /* $SDH in $Secure */
261987da915Sopenharmony_ci				printf("index to securid 0x%lx lth 0x%x"
262987da915Sopenharmony_ci					" flags 0x%x pos 0x%x\n",
263987da915Sopenharmony_ci					(long)getle32(buffer,k + 20),
264987da915Sopenharmony_ci					(int)lth,
265987da915Sopenharmony_ci					(int)getle16(buffer,k+12),(int)k);
266987da915Sopenharmony_ci				break;
267987da915Sopenharmony_ci			default : /* at least 80 */
268987da915Sopenharmony_ci				printf("index to inode 0x%016llx lth 0x%x"
269987da915Sopenharmony_ci					" flags 0x%x pos 0x%x\n",
270987da915Sopenharmony_ci					(long long)getle64(buffer,k),
271987da915Sopenharmony_ci					(int)lth,
272987da915Sopenharmony_ci					(int)getle16(buffer,k+12),(int)k);
273987da915Sopenharmony_ci				if ((lth < 80) || (lth & 7)) {
274987da915Sopenharmony_ci					printf("** Invalid index record"
275987da915Sopenharmony_ci						" length %d\n",lth);
276987da915Sopenharmony_ci					err = 1;
277987da915Sopenharmony_ci				}
278987da915Sopenharmony_ci			}
279987da915Sopenharmony_ci		done = (feedle16(buffer,k+12) & INDEX_ENTRY_END) || !lth;
280987da915Sopenharmony_ci		if (lth & 7) {
281987da915Sopenharmony_ci			if (optv <= 1) /* Do not repeat the warning */
282987da915Sopenharmony_ci				printf("** Invalid index record length %d\n",
283987da915Sopenharmony_ci								lth);
284987da915Sopenharmony_ci			err = 1;
285987da915Sopenharmony_ci		} else
286987da915Sopenharmony_ci	   		k += lth;
287987da915Sopenharmony_ci   	}
288987da915Sopenharmony_ci	if (k != end) {
289987da915Sopenharmony_ci		printf("** Bad index record length %ld (computed %ld)\n",
290987da915Sopenharmony_ci					(long)end, (long)k);
291987da915Sopenharmony_ci		err = 1;
292987da915Sopenharmony_ci	}
293987da915Sopenharmony_ci	if (!done) {
294987da915Sopenharmony_ci		printf("** Missing end of index mark\n");
295987da915Sopenharmony_ci		err = 1;
296987da915Sopenharmony_ci	}
297987da915Sopenharmony_ci	return (err);
298987da915Sopenharmony_ci}
299987da915Sopenharmony_ci
300987da915Sopenharmony_ci/*
301987da915Sopenharmony_ci *		Rough check of sanity of an mft record
302987da915Sopenharmony_ci */
303987da915Sopenharmony_ci
304987da915Sopenharmony_cistatic int sanity_mft(const char *buffer)
305987da915Sopenharmony_ci{
306987da915Sopenharmony_ci	const MFT_RECORD *record;
307987da915Sopenharmony_ci	const ATTR_RECORD *attr;
308987da915Sopenharmony_ci	u64 instances;
309987da915Sopenharmony_ci	u32 k;
310987da915Sopenharmony_ci	u32 type;
311987da915Sopenharmony_ci	u32 prevtype;
312987da915Sopenharmony_ci	u16 nextinstance;
313987da915Sopenharmony_ci	u16 instance;
314987da915Sopenharmony_ci	int err;
315987da915Sopenharmony_ci
316987da915Sopenharmony_ci	err = 0;
317987da915Sopenharmony_ci	record = (const MFT_RECORD*)buffer;
318987da915Sopenharmony_ci	nextinstance = le16_to_cpu(record->next_attr_instance);
319987da915Sopenharmony_ci	instances = 0;
320987da915Sopenharmony_ci	k = le16_to_cpu(record->attrs_offset);
321987da915Sopenharmony_ci	attr = (const ATTR_RECORD*)&buffer[k];
322987da915Sopenharmony_ci	prevtype = 0;
323987da915Sopenharmony_ci	while ((k < mftrecsz)
324987da915Sopenharmony_ci	    && (attr->type != AT_END)
325987da915Sopenharmony_ci	    && valid_type(attr->type)) {
326987da915Sopenharmony_ci		type = le32_to_cpu(attr->type);
327987da915Sopenharmony_ci		if (type < prevtype) {
328987da915Sopenharmony_ci			printf("** Bad type ordering 0x%lx after 0x%lx\n",
329987da915Sopenharmony_ci				(long)type, (long)prevtype);
330987da915Sopenharmony_ci			err = 1;
331987da915Sopenharmony_ci		}
332987da915Sopenharmony_ci		instance = le16_to_cpu(attr->instance);
333987da915Sopenharmony_ci		/* Can nextinstance wrap around ? */
334987da915Sopenharmony_ci		if (instance >= nextinstance) {
335987da915Sopenharmony_ci			printf("** Bad attr instance %d (max %d)\n",
336987da915Sopenharmony_ci					(int)instance, (int)nextinstance - 1);
337987da915Sopenharmony_ci			err = 1;
338987da915Sopenharmony_ci		}
339987da915Sopenharmony_ci		if (instance < 64) {
340987da915Sopenharmony_ci			/* Only check up to 64 */
341987da915Sopenharmony_ci			if (((u64)1 << instance) & instances) {
342987da915Sopenharmony_ci				printf("** Duplicated attr instance %d\n",
343987da915Sopenharmony_ci					(int)instance);
344987da915Sopenharmony_ci			}
345987da915Sopenharmony_ci			instances |= (u64)1 << instance;
346987da915Sopenharmony_ci		}
347987da915Sopenharmony_ci		if (optv > 1) {
348987da915Sopenharmony_ci			if ((attr->type == AT_FILE_NAME)
349987da915Sopenharmony_ci			   && buffer[k + 88]) {
350987da915Sopenharmony_ci				printf("attr %08lx offs 0x%x nres %d",
351987da915Sopenharmony_ci					(long)type, (int)k,
352987da915Sopenharmony_ci					(int)attr->non_resident);
353987da915Sopenharmony_ci				showname(" ",&buffer[k+90],
354987da915Sopenharmony_ci					buffer[k + 88] & 255);
355987da915Sopenharmony_ci			} else
356987da915Sopenharmony_ci				printf("attr %08lx offs 0x%x nres %d\n",
357987da915Sopenharmony_ci					(long)type, (int)k,
358987da915Sopenharmony_ci					(int)attr->non_resident);
359987da915Sopenharmony_ci		}
360987da915Sopenharmony_ci		if ((attr->type == AT_INDEX_ROOT)
361987da915Sopenharmony_ci		    && sanity_indx_list(buffer,
362987da915Sopenharmony_ci				k + le16_to_cpu(attr->value_offset) + 32,
363987da915Sopenharmony_ci				k + le32_to_cpu(attr->length))) {
364987da915Sopenharmony_ci			err = 1;
365987da915Sopenharmony_ci		}
366987da915Sopenharmony_ci		k += le32_to_cpu(attr->length);
367987da915Sopenharmony_ci		attr = (const ATTR_RECORD*)&buffer[k];
368987da915Sopenharmony_ci		prevtype = type;
369987da915Sopenharmony_ci	}
370987da915Sopenharmony_ci	if ((optv > 1) && (attr->type == AT_END))
371987da915Sopenharmony_ci		printf("attr %08lx offs 0x%x\n",
372987da915Sopenharmony_ci				(long)le32_to_cpu(attr->type), (int)k);
373987da915Sopenharmony_ci	if ((attr->type != AT_END)
374987da915Sopenharmony_ci	    || (le32_to_cpu(record->bytes_in_use) != (k + 8))
375987da915Sopenharmony_ci	    || (le32_to_cpu(record->bytes_allocated) < (k + 8))) {
376987da915Sopenharmony_ci		printf("** Bad MFT record length %ld"
377987da915Sopenharmony_ci				" (computed %ld allocated %ld)\n",
378987da915Sopenharmony_ci				(long)le32_to_cpu(record->bytes_in_use),
379987da915Sopenharmony_ci				(long)(k + 8),
380987da915Sopenharmony_ci				(long)le32_to_cpu(record->bytes_allocated));
381987da915Sopenharmony_ci		err = 1;
382987da915Sopenharmony_ci	}
383987da915Sopenharmony_ci	return (err);
384987da915Sopenharmony_ci}
385987da915Sopenharmony_ci
386987da915Sopenharmony_ci/*
387987da915Sopenharmony_ci *		Rough check of sanity of an index block
388987da915Sopenharmony_ci */
389987da915Sopenharmony_ci
390987da915Sopenharmony_cistatic int sanity_indx(ntfs_volume *vol, const char *buffer)
391987da915Sopenharmony_ci{
392987da915Sopenharmony_ci	const INDEX_BLOCK *indx;
393987da915Sopenharmony_ci	u32 k;
394987da915Sopenharmony_ci	int err;
395987da915Sopenharmony_ci
396987da915Sopenharmony_ci	err = 0;
397987da915Sopenharmony_ci	indx = (const INDEX_BLOCK*)buffer;
398987da915Sopenharmony_ci	k = offsetof(INDEX_BLOCK, index) +
399987da915Sopenharmony_ci		le32_to_cpu(indx->index.entries_offset);
400987da915Sopenharmony_ci	err = sanity_indx_list(buffer, k,
401987da915Sopenharmony_ci				le32_to_cpu(indx->index.index_length) + 24);
402987da915Sopenharmony_ci	if ((le32_to_cpu(indx->index.index_length)
403987da915Sopenharmony_ci		> le32_to_cpu(indx->index.allocated_size))
404987da915Sopenharmony_ci	    || (le32_to_cpu(indx->index.allocated_size)
405987da915Sopenharmony_ci		!= (vol->indx_record_size - 24))) {
406987da915Sopenharmony_ci		printf("** Bad index length %ld"
407987da915Sopenharmony_ci				" (usable %ld allocated %ld)\n",
408987da915Sopenharmony_ci				(long)le32_to_cpu(indx->index.index_length),
409987da915Sopenharmony_ci				(long)(vol->indx_record_size - 24),
410987da915Sopenharmony_ci				(long)le32_to_cpu(indx->index.allocated_size));
411987da915Sopenharmony_ci		err = 1;
412987da915Sopenharmony_ci	}
413987da915Sopenharmony_ci	return (err);
414987da915Sopenharmony_ci}
415987da915Sopenharmony_ci
416987da915Sopenharmony_ci
417987da915Sopenharmony_ci/*
418987da915Sopenharmony_ci *		Allocate a buffer and read a full set of raw clusters
419987da915Sopenharmony_ci *
420987da915Sopenharmony_ci *	Do not use for accessing $LogFile.
421987da915Sopenharmony_ci *	With option -n reading is first attempted from the memory store
422987da915Sopenharmony_ci */
423987da915Sopenharmony_ci
424987da915Sopenharmony_cistatic char *read_raw(ntfs_volume *vol, const LOG_RECORD *logr)
425987da915Sopenharmony_ci{
426987da915Sopenharmony_ci	char *buffer;
427987da915Sopenharmony_ci	char *target;
428987da915Sopenharmony_ci	struct STORE *store;
429987da915Sopenharmony_ci	LCN lcn;
430987da915Sopenharmony_ci	int count;
431987da915Sopenharmony_ci	int i;
432987da915Sopenharmony_ci	BOOL fail;
433987da915Sopenharmony_ci
434987da915Sopenharmony_ci	count = le16_to_cpu(logr->lcns_to_follow);
435987da915Sopenharmony_ci	if (!count) {
436987da915Sopenharmony_ci		printf("** Error : no lcn to read from\n");
437987da915Sopenharmony_ci		buffer = (char*)NULL;
438987da915Sopenharmony_ci	} else
439987da915Sopenharmony_ci		buffer = (char*)malloc(clustersz*count);
440987da915Sopenharmony_ci// TODO error messages
441987da915Sopenharmony_ci	if (buffer) {
442987da915Sopenharmony_ci		fail = FALSE;
443987da915Sopenharmony_ci		for (i=0; (i<count) && !fail; i++) {
444987da915Sopenharmony_ci			store = (struct STORE*)NULL;
445987da915Sopenharmony_ci			lcn = sle64_to_cpu(logr->lcn_list[i]);
446987da915Sopenharmony_ci			target = buffer + clustersz*i;
447987da915Sopenharmony_ci			if (optn) {
448987da915Sopenharmony_ci				store = getclusterentry(lcn, FALSE);
449987da915Sopenharmony_ci				if (store) {
450987da915Sopenharmony_ci					memcpy(target, store->data, clustersz);
451987da915Sopenharmony_ci				if (optv)
452987da915Sopenharmony_ci					printf("== lcn 0x%llx from store\n",
453987da915Sopenharmony_ci							(long long)lcn);
454987da915Sopenharmony_ci				if ((optv > 1) && optc
455987da915Sopenharmony_ci				    && within_lcn_range(logr))
456987da915Sopenharmony_ci					dump(store->data, clustersz);
457987da915Sopenharmony_ci				}
458987da915Sopenharmony_ci			}
459987da915Sopenharmony_ci			if (!store
460987da915Sopenharmony_ci			   && (ntfs_pread(vol->dev, lcn << clusterbits,
461987da915Sopenharmony_ci                			clustersz, target) != clustersz)) {
462987da915Sopenharmony_ci				fail = TRUE;
463987da915Sopenharmony_ci			} else {
464987da915Sopenharmony_ci				if (!store) {
465987da915Sopenharmony_ci					if (optv)
466987da915Sopenharmony_ci						printf("== lcn 0x%llx"
467987da915Sopenharmony_ci							" from device\n",
468987da915Sopenharmony_ci							(long long)lcn);
469987da915Sopenharmony_ci					if ((optv > 1) && optc
470987da915Sopenharmony_ci					    && within_lcn_range(logr))
471987da915Sopenharmony_ci						dump(target, clustersz);
472987da915Sopenharmony_ci				}
473987da915Sopenharmony_ci			}
474987da915Sopenharmony_ci		}
475987da915Sopenharmony_ci		if (fail) {
476987da915Sopenharmony_ci			printf("** Could not read cluster 0x%llx\n",
477987da915Sopenharmony_ci					(long long)lcn);
478987da915Sopenharmony_ci			free(buffer);
479987da915Sopenharmony_ci			buffer = (char*)NULL;
480987da915Sopenharmony_ci		}
481987da915Sopenharmony_ci	}
482987da915Sopenharmony_ci	return (buffer);
483987da915Sopenharmony_ci}
484987da915Sopenharmony_ci
485987da915Sopenharmony_ci/*
486987da915Sopenharmony_ci *		Write a full set of raw clusters
487987da915Sopenharmony_ci *
488987da915Sopenharmony_ci *	Do not use for accessing $LogFile.
489987da915Sopenharmony_ci *	With option -n a copy of the buffer is kept in memory for later use.
490987da915Sopenharmony_ci */
491987da915Sopenharmony_ci
492987da915Sopenharmony_cistatic int write_raw(ntfs_volume *vol, const LOG_RECORD *logr,
493987da915Sopenharmony_ci					char *buffer)
494987da915Sopenharmony_ci{
495987da915Sopenharmony_ci	int err;
496987da915Sopenharmony_ci	struct STORE *store;
497987da915Sopenharmony_ci	LCN lcn;
498987da915Sopenharmony_ci	char *source;
499987da915Sopenharmony_ci	int count;
500987da915Sopenharmony_ci	int i;
501987da915Sopenharmony_ci
502987da915Sopenharmony_ci	err = 0;
503987da915Sopenharmony_ci	count = le16_to_cpu(logr->lcns_to_follow);
504987da915Sopenharmony_ci	if (!count)
505987da915Sopenharmony_ci		printf("** Error : no lcn to write to\n");
506987da915Sopenharmony_ci	if (optn) {
507987da915Sopenharmony_ci		for (i=0; (i<count) && !err; i++) {
508987da915Sopenharmony_ci			lcn = sle64_to_cpu(logr->lcn_list[i]);
509987da915Sopenharmony_ci			source = buffer + clustersz*i;
510987da915Sopenharmony_ci			store = getclusterentry(lcn, TRUE);
511987da915Sopenharmony_ci			if (store) {
512987da915Sopenharmony_ci				memcpy(store->data, source, clustersz);
513987da915Sopenharmony_ci				if (optv)
514987da915Sopenharmony_ci					printf("== lcn 0x%llx to store\n",
515987da915Sopenharmony_ci							(long long)lcn);
516987da915Sopenharmony_ci				if ((optv > 1) && optc
517987da915Sopenharmony_ci				    && within_lcn_range(logr))
518987da915Sopenharmony_ci					dump(store->data, clustersz);
519987da915Sopenharmony_ci			} else {
520987da915Sopenharmony_ci				printf("** Could not store cluster 0x%llx\n",
521987da915Sopenharmony_ci					(long long)lcn);
522987da915Sopenharmony_ci				err = 1;
523987da915Sopenharmony_ci			}
524987da915Sopenharmony_ci		}
525987da915Sopenharmony_ci	} else {
526987da915Sopenharmony_ci		for (i=0; (i<count) && !err; i++) {
527987da915Sopenharmony_ci			lcn = sle64_to_cpu(logr->lcn_list[i]);
528987da915Sopenharmony_ci			if (optv)
529987da915Sopenharmony_ci				printf("== lcn 0x%llx to device\n",
530987da915Sopenharmony_ci							(long long)lcn);
531987da915Sopenharmony_ci			source = buffer + clustersz*i;
532987da915Sopenharmony_ci			if (ntfs_pwrite(vol->dev, lcn << clusterbits,
533987da915Sopenharmony_ci        	       			clustersz, source) != clustersz) {
534987da915Sopenharmony_ci				printf("** Could not write cluster 0x%llx\n",
535987da915Sopenharmony_ci						(long long)lcn);
536987da915Sopenharmony_ci				err = 1;
537987da915Sopenharmony_ci			}
538987da915Sopenharmony_ci		}
539987da915Sopenharmony_ci	}
540987da915Sopenharmony_ci	return (err);
541987da915Sopenharmony_ci}
542987da915Sopenharmony_ci
543987da915Sopenharmony_ci/*
544987da915Sopenharmony_ci *		Write a full set of raw clusters to mft_mirr
545987da915Sopenharmony_ci */
546987da915Sopenharmony_ci
547987da915Sopenharmony_cistatic int write_mirr(ntfs_volume *vol, const LOG_RECORD *logr,
548987da915Sopenharmony_ci					char *buffer)
549987da915Sopenharmony_ci{
550987da915Sopenharmony_ci	int err;
551987da915Sopenharmony_ci	LCN lcn;
552987da915Sopenharmony_ci	char *source;
553987da915Sopenharmony_ci	int count;
554987da915Sopenharmony_ci	int i;
555987da915Sopenharmony_ci
556987da915Sopenharmony_ci	err = 0;
557987da915Sopenharmony_ci	count = le16_to_cpu(logr->lcns_to_follow);
558987da915Sopenharmony_ci	if (!count)
559987da915Sopenharmony_ci		printf("** Error : no lcn to write to\n");
560987da915Sopenharmony_ci	if (!optn) {
561987da915Sopenharmony_ci		for (i=0; (i<count) && !err; i++) {
562987da915Sopenharmony_ci			lcn = ntfs_attr_vcn_to_lcn(vol->mftmirr_na,
563987da915Sopenharmony_ci				sle64_to_cpu(logr->target_vcn) + i);
564987da915Sopenharmony_ci			source = buffer + clustersz*i;
565987da915Sopenharmony_ci			if ((lcn < 0)
566987da915Sopenharmony_ci			    || (ntfs_pwrite(vol->dev, lcn << clusterbits,
567987da915Sopenharmony_ci        	       			clustersz, source) != clustersz)) {
568987da915Sopenharmony_ci				printf("** Could not write cluster 0x%llx\n",
569987da915Sopenharmony_ci						(long long)lcn);
570987da915Sopenharmony_ci				err = 1;
571987da915Sopenharmony_ci			}
572987da915Sopenharmony_ci		}
573987da915Sopenharmony_ci	}
574987da915Sopenharmony_ci	return (err);
575987da915Sopenharmony_ci}
576987da915Sopenharmony_ci
577987da915Sopenharmony_ci/*
578987da915Sopenharmony_ci *		Allocate a buffer and read a single protected record
579987da915Sopenharmony_ci */
580987da915Sopenharmony_ci
581987da915Sopenharmony_cistatic char *read_protected(ntfs_volume *vol, const LOG_RECORD *logr,
582987da915Sopenharmony_ci			u32 size, BOOL warn)
583987da915Sopenharmony_ci{
584987da915Sopenharmony_ci	char *buffer;
585987da915Sopenharmony_ci	char *full;
586987da915Sopenharmony_ci	u32 pos;
587987da915Sopenharmony_ci	LCN lcn;
588987da915Sopenharmony_ci
589987da915Sopenharmony_ci		/* read full clusters */
590987da915Sopenharmony_ci	buffer = read_raw(vol, logr);
591987da915Sopenharmony_ci		/*
592987da915Sopenharmony_ci		 * if the record is smaller than a cluster,
593987da915Sopenharmony_ci		 * make a partial copy and free the full buffer
594987da915Sopenharmony_ci		 */
595987da915Sopenharmony_ci	if (buffer && (size < clustersz)) {
596987da915Sopenharmony_ci		full = buffer;
597987da915Sopenharmony_ci		buffer = (char*)malloc(size);
598987da915Sopenharmony_ci		if (buffer) {
599987da915Sopenharmony_ci			pos = le16_to_cpu(logr->cluster_index)
600987da915Sopenharmony_ci					<< NTFS_BLOCK_SIZE_BITS;
601987da915Sopenharmony_ci			memcpy(buffer, full + pos, size);
602987da915Sopenharmony_ci		}
603987da915Sopenharmony_ci		free(full);
604987da915Sopenharmony_ci	}
605987da915Sopenharmony_ci	if (buffer && (ntfs_mst_post_read_fixup_warn(
606987da915Sopenharmony_ci				(NTFS_RECORD*)buffer, size, FALSE) < 0)) {
607987da915Sopenharmony_ci		if (warn) {
608987da915Sopenharmony_ci			lcn = sle64_to_cpu(logr->lcn_list[0]);
609987da915Sopenharmony_ci			printf("** Invalid protected record at 0x%llx"
610987da915Sopenharmony_ci					" index %d\n",
611987da915Sopenharmony_ci					(long long)lcn,
612987da915Sopenharmony_ci					(int)le16_to_cpu(logr->cluster_index));
613987da915Sopenharmony_ci		}
614987da915Sopenharmony_ci		free(buffer);
615987da915Sopenharmony_ci		buffer = (char*)NULL;
616987da915Sopenharmony_ci	}
617987da915Sopenharmony_ci	return (buffer);
618987da915Sopenharmony_ci}
619987da915Sopenharmony_ci
620987da915Sopenharmony_ci/*
621987da915Sopenharmony_ci *		Protect a single record, write, and deallocate the buffer
622987da915Sopenharmony_ci *
623987da915Sopenharmony_ci *	With option -n a copy of the buffer is kept in protected form in
624987da915Sopenharmony_ci *	memory for later use.
625987da915Sopenharmony_ci *	As the store only knows about clusters, if the record is smaller
626987da915Sopenharmony_ci *	than a cluster, have to read, merge and write.
627987da915Sopenharmony_ci */
628987da915Sopenharmony_ci
629987da915Sopenharmony_cistatic int write_protected(ntfs_volume *vol, const LOG_RECORD *logr,
630987da915Sopenharmony_ci				char *buffer, u32 size)
631987da915Sopenharmony_ci{
632987da915Sopenharmony_ci	MFT_RECORD *record;
633987da915Sopenharmony_ci	INDEX_BLOCK *indx;
634987da915Sopenharmony_ci	char *full;
635987da915Sopenharmony_ci	u32 pos;
636987da915Sopenharmony_ci	BOOL mftmirr;
637987da915Sopenharmony_ci	BOOL checked;
638987da915Sopenharmony_ci	int err;
639987da915Sopenharmony_ci
640987da915Sopenharmony_ci	err = 0;
641987da915Sopenharmony_ci	mftmirr = FALSE;
642987da915Sopenharmony_ci	checked = FALSE;
643987da915Sopenharmony_ci	if ((size == mftrecsz) && !memcmp(buffer,"FILE",4)) {
644987da915Sopenharmony_ci		record = (MFT_RECORD*)buffer;
645987da915Sopenharmony_ci		if (optv)
646987da915Sopenharmony_ci			printf("update inode %ld lsn 0x%llx"
647987da915Sopenharmony_ci				" (record %s than action 0x%llx)\n",
648987da915Sopenharmony_ci				(long)le32_to_cpu(record->mft_record_number),
649987da915Sopenharmony_ci				(long long)sle64_to_cpu(record->lsn),
650987da915Sopenharmony_ci				((s64)(sle64_to_cpu(record->lsn)
651987da915Sopenharmony_ci				    - sle64_to_cpu(logr->this_lsn)) < 0 ?
652987da915Sopenharmony_ci					"older" : "newer"),
653987da915Sopenharmony_ci				(long long)sle64_to_cpu(logr->this_lsn));
654987da915Sopenharmony_ci		if (optv > 1)
655987da915Sopenharmony_ci			printf("mft vcn %lld index %d\n",
656987da915Sopenharmony_ci				(long long)sle64_to_cpu(logr->target_vcn),
657987da915Sopenharmony_ci				(int)le16_to_cpu(logr->cluster_index));
658987da915Sopenharmony_ci		err = sanity_mft(buffer);
659987da915Sopenharmony_ci			/* Should set to some previous lsn for undos */
660987da915Sopenharmony_ci		if (opts)
661987da915Sopenharmony_ci			record->lsn = logr->this_lsn;
662987da915Sopenharmony_ci		/* Duplicate on mftmirr if not overflowing its size */
663987da915Sopenharmony_ci		mftmirr = (((u64)sle64_to_cpu(logr->target_vcn)
664987da915Sopenharmony_ci				+ le16_to_cpu(logr->lcns_to_follow))
665987da915Sopenharmony_ci				<< clusterbits)
666987da915Sopenharmony_ci			<= (((u64)vol->mftmirr_size) << mftrecbits);
667987da915Sopenharmony_ci		checked = TRUE;
668987da915Sopenharmony_ci	}
669987da915Sopenharmony_ci	if ((size == vol->indx_record_size) && !memcmp(buffer,"INDX",4)) {
670987da915Sopenharmony_ci		indx = (INDEX_BLOCK*)buffer;
671987da915Sopenharmony_ci		if (optv)
672987da915Sopenharmony_ci			printf("update index lsn 0x%llx"
673987da915Sopenharmony_ci				" (index %s than action 0x%llx)\n",
674987da915Sopenharmony_ci				(long long)sle64_to_cpu(indx->lsn),
675987da915Sopenharmony_ci				((s64)(sle64_to_cpu(indx->lsn)
676987da915Sopenharmony_ci				    - sle64_to_cpu(logr->this_lsn)) < 0 ?
677987da915Sopenharmony_ci					"older" : "newer"),
678987da915Sopenharmony_ci				(long long)sle64_to_cpu(logr->this_lsn));
679987da915Sopenharmony_ci		err = sanity_indx(vol, buffer);
680987da915Sopenharmony_ci			/* Should set to some previous lsn for undos */
681987da915Sopenharmony_ci		if (opts)
682987da915Sopenharmony_ci			indx->lsn = logr->this_lsn;
683987da915Sopenharmony_ci		checked = TRUE;
684987da915Sopenharmony_ci	}
685987da915Sopenharmony_ci	if (!checked) {
686987da915Sopenharmony_ci		printf("** Error : writing protected record of unknown type\n");
687987da915Sopenharmony_ci		err = 1;
688987da915Sopenharmony_ci	}
689987da915Sopenharmony_ci	if (!err) {
690987da915Sopenharmony_ci		if (!ntfs_mst_pre_write_fixup((NTFS_RECORD*)buffer, size)) {
691987da915Sopenharmony_ci			/*
692987da915Sopenharmony_ci			 * If the record is smaller than a cluster, get a full
693987da915Sopenharmony_ci			 * cluster, merge and write.
694987da915Sopenharmony_ci			 */
695987da915Sopenharmony_ci			if (size < clustersz) {
696987da915Sopenharmony_ci				full = read_raw(vol, logr);
697987da915Sopenharmony_ci				if (full) {
698987da915Sopenharmony_ci					pos = le16_to_cpu(logr->cluster_index)
699987da915Sopenharmony_ci						<< NTFS_BLOCK_SIZE_BITS;
700987da915Sopenharmony_ci					memcpy(full + pos, buffer, size);
701987da915Sopenharmony_ci					err = write_raw(vol, logr, full);
702987da915Sopenharmony_ci					if (!err && mftmirr && !optn)
703987da915Sopenharmony_ci						err = write_mirr(vol, logr,
704987da915Sopenharmony_ci								full);
705987da915Sopenharmony_ci					free(full);
706987da915Sopenharmony_ci				} else
707987da915Sopenharmony_ci					err = 1;
708987da915Sopenharmony_ci			} else {
709987da915Sopenharmony_ci					/* write full clusters */
710987da915Sopenharmony_ci				err = write_raw(vol, logr, buffer);
711987da915Sopenharmony_ci				if (!err && mftmirr && !optn)
712987da915Sopenharmony_ci					err = write_mirr(vol, logr, buffer);
713987da915Sopenharmony_ci			}
714987da915Sopenharmony_ci		} else {
715987da915Sopenharmony_ci			printf("** Failed to protect record\n");
716987da915Sopenharmony_ci			err = 1;
717987da915Sopenharmony_ci		}
718987da915Sopenharmony_ci	}
719987da915Sopenharmony_ci	return (err);
720987da915Sopenharmony_ci}
721987da915Sopenharmony_ci
722987da915Sopenharmony_ci/*
723987da915Sopenharmony_ci *		Resize attribute records
724987da915Sopenharmony_ci *
725987da915Sopenharmony_ci *	The attribute value is resized to new size, but the attribute
726987da915Sopenharmony_ci *	and MFT record must be kept aligned to 8 bytes.
727987da915Sopenharmony_ci */
728987da915Sopenharmony_ci
729987da915Sopenharmony_cistatic int resize_attribute(MFT_RECORD *entry, ATTR_RECORD *attr, INDEX_ROOT *index,
730987da915Sopenharmony_ci			int rawresize, int resize)
731987da915Sopenharmony_ci{
732987da915Sopenharmony_ci	int err;
733987da915Sopenharmony_ci	u32 newlength;
734987da915Sopenharmony_ci	u32 newused;
735987da915Sopenharmony_ci	u32 newvalue;
736987da915Sopenharmony_ci	u32 indexlth;
737987da915Sopenharmony_ci	u32 indexalloc;
738987da915Sopenharmony_ci
739987da915Sopenharmony_ci	err = 0;
740987da915Sopenharmony_ci	if (attr) {
741987da915Sopenharmony_ci		newvalue = le32_to_cpu(attr->value_length) + rawresize;
742987da915Sopenharmony_ci		attr->value_length = cpu_to_le32(newvalue);
743987da915Sopenharmony_ci		newlength = le32_to_cpu(attr->length) + resize;
744987da915Sopenharmony_ci		attr->length = cpu_to_le32(newlength);
745987da915Sopenharmony_ci	}
746987da915Sopenharmony_ci	if (entry) {
747987da915Sopenharmony_ci		newused = le32_to_cpu(entry->bytes_in_use) + resize;
748987da915Sopenharmony_ci		entry->bytes_in_use = cpu_to_le32(newused);
749987da915Sopenharmony_ci	}
750987da915Sopenharmony_ci	if (index) {
751987da915Sopenharmony_ci		indexlth = le32_to_cpu(index->index.index_length) + resize;
752987da915Sopenharmony_ci		index->index.index_length = cpu_to_le32(indexlth);
753987da915Sopenharmony_ci		indexalloc = le32_to_cpu(index->index.allocated_size) + resize;
754987da915Sopenharmony_ci		index->index.allocated_size = cpu_to_le32(indexalloc);
755987da915Sopenharmony_ci	}
756987da915Sopenharmony_ci	return (err);
757987da915Sopenharmony_ci}
758987da915Sopenharmony_ci
759987da915Sopenharmony_ci/*
760987da915Sopenharmony_ci *		Adjust the next attribute instance
761987da915Sopenharmony_ci *
762987da915Sopenharmony_ci *	If a newly created attribute matches the next instance, then
763987da915Sopenharmony_ci *	the next instance has to be incremented.
764987da915Sopenharmony_ci *
765987da915Sopenharmony_ci *	Do the opposite when undoing an attribute creation, but
766987da915Sopenharmony_ci *	do not change the next instance when deleting an attribute
767987da915Sopenharmony_ci *	or undoing the deletion.
768987da915Sopenharmony_ci */
769987da915Sopenharmony_ci
770987da915Sopenharmony_cistatic void adjust_instance(const ATTR_RECORD *attr, MFT_RECORD *entry, int increment)
771987da915Sopenharmony_ci{
772987da915Sopenharmony_ci	u16 instance;
773987da915Sopenharmony_ci
774987da915Sopenharmony_ci	if (increment > 0) {
775987da915Sopenharmony_ci			/* Allocating a new instance ? */
776987da915Sopenharmony_ci		if (attr->instance == entry->next_attr_instance) {
777987da915Sopenharmony_ci			instance = (le16_to_cpu(entry->next_attr_instance)
778987da915Sopenharmony_ci					+ 1) & 0xffff;
779987da915Sopenharmony_ci			entry->next_attr_instance = cpu_to_le16(instance);
780987da915Sopenharmony_ci		}
781987da915Sopenharmony_ci	}
782987da915Sopenharmony_ci	if (increment < 0) {
783987da915Sopenharmony_ci			/* Freeing the latest instance ? */
784987da915Sopenharmony_ci		instance = (le16_to_cpu(entry->next_attr_instance)
785987da915Sopenharmony_ci					- 1) & 0xffff;
786987da915Sopenharmony_ci		if (attr->instance == cpu_to_le16(instance))
787987da915Sopenharmony_ci			entry->next_attr_instance = attr->instance;
788987da915Sopenharmony_ci	}
789987da915Sopenharmony_ci}
790987da915Sopenharmony_ci
791987da915Sopenharmony_ci/*
792987da915Sopenharmony_ci *		Adjust the highest vcn according to mapping pairs
793987da915Sopenharmony_ci *
794987da915Sopenharmony_ci *	The runlist has to be fully recomputed
795987da915Sopenharmony_ci */
796987da915Sopenharmony_ci
797987da915Sopenharmony_cistatic int adjust_high_vcn(ntfs_volume *vol, ATTR_RECORD *attr)
798987da915Sopenharmony_ci{
799987da915Sopenharmony_ci	runlist_element *rl;
800987da915Sopenharmony_ci	runlist_element *xrl;
801987da915Sopenharmony_ci	VCN high_vcn;
802987da915Sopenharmony_ci	int err;
803987da915Sopenharmony_ci
804987da915Sopenharmony_ci	err = 1;
805987da915Sopenharmony_ci	attr->highest_vcn = const_cpu_to_sle64(0);
806987da915Sopenharmony_ci	rl = ntfs_mapping_pairs_decompress(vol, attr, (runlist_element*)NULL);
807987da915Sopenharmony_ci	if (rl) {
808987da915Sopenharmony_ci		xrl = rl;
809987da915Sopenharmony_ci		if (xrl->length)
810987da915Sopenharmony_ci			xrl++;
811987da915Sopenharmony_ci		while ((xrl->length) && (xrl->lcn != LCN_RL_NOT_MAPPED))
812987da915Sopenharmony_ci			xrl++;
813987da915Sopenharmony_ci		high_vcn = xrl->vcn - 1;
814987da915Sopenharmony_ci		attr->highest_vcn = cpu_to_sle64(high_vcn);
815987da915Sopenharmony_ci		free(rl);
816987da915Sopenharmony_ci		err = 0;
817987da915Sopenharmony_ci	} else {
818987da915Sopenharmony_ci		printf("** Failed to decompress the runlist\n");
819987da915Sopenharmony_ci		dump((char*)attr,128);
820987da915Sopenharmony_ci	}
821987da915Sopenharmony_ci	return (err);
822987da915Sopenharmony_ci}
823987da915Sopenharmony_ci
824987da915Sopenharmony_ci/*
825987da915Sopenharmony_ci *		Check index match, to be used for undos only
826987da915Sopenharmony_ci *
827987da915Sopenharmony_ci *	The action UpdateFileNameRoot updates the time stamps and/or the
828987da915Sopenharmony_ci *	sizes, but the lsn is not updated in the index record.
829987da915Sopenharmony_ci *	As a consequence such UpdateFileNameRoot are not always undone
830987da915Sopenharmony_ci *	and the actual record does not fully match the undo data.
831987da915Sopenharmony_ci *	We however accept the match if the parent directory and the name
832987da915Sopenharmony_ci *	match.
833987da915Sopenharmony_ci *	Alternate workaround : do not check the lsn when undoing
834987da915Sopenharmony_ci *	UpdateFileNameRoot
835987da915Sopenharmony_ci */
836987da915Sopenharmony_ci
837987da915Sopenharmony_cistatic BOOL index_match_undo(const char *first, const char *second, int length)
838987da915Sopenharmony_ci{
839987da915Sopenharmony_ci	int len;
840987da915Sopenharmony_ci	BOOL match;
841987da915Sopenharmony_ci
842987da915Sopenharmony_ci	match = !memcmp(first, second, length);
843987da915Sopenharmony_ci	if (!match) {
844987da915Sopenharmony_ci		if (optv) {
845987da915Sopenharmony_ci			printf("The existing index does not match :\n");
846987da915Sopenharmony_ci			dump(second,length);
847987da915Sopenharmony_ci		}
848987da915Sopenharmony_ci		len = (first[80] & 255)*2 + 2;
849987da915Sopenharmony_ci		match = (feedle64(first, 16) == feedle64(second, 16))
850987da915Sopenharmony_ci		    && !memcmp(first + 80, second + 80, len);
851987da915Sopenharmony_ci		if (match && optv)
852987da915Sopenharmony_ci			printf("However parent dir and name do match\n");
853987da915Sopenharmony_ci	}
854987da915Sopenharmony_ci	return (match);
855987da915Sopenharmony_ci}
856987da915Sopenharmony_ci
857987da915Sopenharmony_ci
858987da915Sopenharmony_ci/*
859987da915Sopenharmony_ci *		Generic idempotent change to a resident attribute
860987da915Sopenharmony_ci */
861987da915Sopenharmony_ci
862987da915Sopenharmony_cistatic int change_resident(ntfs_volume *vol, const struct ACTION_RECORD *action,
863987da915Sopenharmony_ci		char *buffer, const char *data, u32 target, u32 length)
864987da915Sopenharmony_ci{
865987da915Sopenharmony_ci	LCN lcn;
866987da915Sopenharmony_ci	ATTR_RECORD *attr;
867987da915Sopenharmony_ci	u32 attrend;
868987da915Sopenharmony_ci	int err;
869987da915Sopenharmony_ci	int changed;
870987da915Sopenharmony_ci
871987da915Sopenharmony_ci	err = 1;
872987da915Sopenharmony_ci	if (action->record.undo_length != action->record.redo_length)
873987da915Sopenharmony_ci		printf("** Error size change in change_resident\n");
874987da915Sopenharmony_ci	if (optv > 1) {
875987da915Sopenharmony_ci		lcn = sle64_to_cpu(action->record.lcn_list[0]);
876987da915Sopenharmony_ci		printf("-> inode %lld lcn 0x%llx target 0x%x length %d\n",
877987da915Sopenharmony_ci			(long long)inode_number(&action->record),
878987da915Sopenharmony_ci			(long long)lcn, (int)target, (int)length);
879987da915Sopenharmony_ci	}
880987da915Sopenharmony_ci	attr = (ATTR_RECORD*)(buffer
881987da915Sopenharmony_ci			+ le16_to_cpu(action->record.record_offset));
882987da915Sopenharmony_ci	if (optv > 1) {
883987da915Sopenharmony_ci		printf("-> existing record :\n");
884987da915Sopenharmony_ci		dump(&buffer[target], length);
885987da915Sopenharmony_ci		printf("-> full MFT record :\n");
886987da915Sopenharmony_ci		dump(buffer,mftrecsz);
887987da915Sopenharmony_ci	}
888987da915Sopenharmony_ci	attrend = le16_to_cpu(action->record.record_offset)
889987da915Sopenharmony_ci			+ le32_to_cpu(attr->length);
890987da915Sopenharmony_ci	if ((target + length) > attrend) {
891987da915Sopenharmony_ci		printf("** Error : update overflows from attribute\n");
892987da915Sopenharmony_ci	}
893987da915Sopenharmony_ci	if (!(length & 7)
894987da915Sopenharmony_ci	    && ((target + length) <= attrend)
895987da915Sopenharmony_ci	    && (attrend <= mftrecsz)
896987da915Sopenharmony_ci	    && !sanity_mft(buffer)) {
897987da915Sopenharmony_ci		changed = memcmp(buffer + target, data, length);
898987da915Sopenharmony_ci		err = 0;
899987da915Sopenharmony_ci		if (changed) {
900987da915Sopenharmony_ci			memcpy(buffer + target, data, length);
901987da915Sopenharmony_ci			if (optv > 1) {
902987da915Sopenharmony_ci				printf("-> new record :\n");
903987da915Sopenharmony_ci				dump(buffer + target, length);
904987da915Sopenharmony_ci			}
905987da915Sopenharmony_ci			err = write_protected(vol, &action->record,
906987da915Sopenharmony_ci						buffer, mftrecsz);
907987da915Sopenharmony_ci		}
908987da915Sopenharmony_ci		if (optv > 1) {
909987da915Sopenharmony_ci			printf("-> MFT record %s\n",
910987da915Sopenharmony_ci				(changed ? "updated" : "unchanged"));
911987da915Sopenharmony_ci		}
912987da915Sopenharmony_ci	}
913987da915Sopenharmony_ci	return (err);
914987da915Sopenharmony_ci}
915987da915Sopenharmony_ci
916987da915Sopenharmony_cistatic int change_resident_expect(ntfs_volume *vol, const struct ACTION_RECORD *action,
917987da915Sopenharmony_ci		char *buffer, const char *data, const char *expected,
918987da915Sopenharmony_ci		u32 target, u32 length, ATTR_TYPES type)
919987da915Sopenharmony_ci{
920987da915Sopenharmony_ci	LCN lcn;
921987da915Sopenharmony_ci	ATTR_RECORD *attr;
922987da915Sopenharmony_ci	int err;
923987da915Sopenharmony_ci	BOOL found;
924987da915Sopenharmony_ci
925987da915Sopenharmony_ci	err = 1;
926987da915Sopenharmony_ci	if (action->record.undo_length != action->record.redo_length)
927987da915Sopenharmony_ci		printf("** Error size change in change_resident\n");
928987da915Sopenharmony_ci	if (optv > 1) {
929987da915Sopenharmony_ci		lcn = sle64_to_cpu(action->record.lcn_list[0]);
930987da915Sopenharmony_ci		printf("-> inode %lld lcn 0x%llx target 0x%x length %d\n",
931987da915Sopenharmony_ci			(long long)inode_number(&action->record),
932987da915Sopenharmony_ci			(long long)lcn, (int)target, (int)length);
933987da915Sopenharmony_ci	}
934987da915Sopenharmony_ci	attr = (ATTR_RECORD*)(buffer
935987da915Sopenharmony_ci			+ le16_to_cpu(action->record.record_offset));
936987da915Sopenharmony_ci	if (optv > 1) {
937987da915Sopenharmony_ci		printf("-> existing record :\n");
938987da915Sopenharmony_ci		dump(&buffer[target], length);
939987da915Sopenharmony_ci		printf("-> full record :\n");
940987da915Sopenharmony_ci		dump((char*)attr, le32_to_cpu(attr->length));
941987da915Sopenharmony_ci	}
942987da915Sopenharmony_ci	if ((attr->type == type)
943987da915Sopenharmony_ci	    && !(length & 7)
944987da915Sopenharmony_ci	    && ((target + length) <= mftrecsz)) {
945987da915Sopenharmony_ci		found = !memcmp(buffer + target, expected, length);
946987da915Sopenharmony_ci		err = 0;
947987da915Sopenharmony_ci		if (found) {
948987da915Sopenharmony_ci			memcpy(buffer + target, data, length);
949987da915Sopenharmony_ci			if (optv > 1) {
950987da915Sopenharmony_ci				printf("-> new record :\n");
951987da915Sopenharmony_ci				dump(buffer + target, length);
952987da915Sopenharmony_ci			}
953987da915Sopenharmony_ci			err = write_protected(vol, &action->record,
954987da915Sopenharmony_ci						buffer, mftrecsz);
955987da915Sopenharmony_ci		}
956987da915Sopenharmony_ci		if (optv > 1) {
957987da915Sopenharmony_ci			printf("-> MFT record %s\n",
958987da915Sopenharmony_ci				(found ? "updated" : "unchanged"));
959987da915Sopenharmony_ci		}
960987da915Sopenharmony_ci	}
961987da915Sopenharmony_ci	return (err);
962987da915Sopenharmony_ci}
963987da915Sopenharmony_ci
964987da915Sopenharmony_ci/*
965987da915Sopenharmony_ci *		Generic idempotent change to a an index value
966987da915Sopenharmony_ci *
967987da915Sopenharmony_ci */
968987da915Sopenharmony_ci
969987da915Sopenharmony_cistatic int change_index_value(ntfs_volume *vol, const struct ACTION_RECORD *action,
970987da915Sopenharmony_ci		char *buffer, const char *data, u32 target, u32 length)
971987da915Sopenharmony_ci{
972987da915Sopenharmony_ci	LCN lcn;
973987da915Sopenharmony_ci	u32 count;
974987da915Sopenharmony_ci	u32 xsize;
975987da915Sopenharmony_ci	int changed;
976987da915Sopenharmony_ci	int err;
977987da915Sopenharmony_ci
978987da915Sopenharmony_ci	err = 1;
979987da915Sopenharmony_ci	count = le16_to_cpu(action->record.lcns_to_follow);
980987da915Sopenharmony_ci	if (optv > 1) {
981987da915Sopenharmony_ci		lcn = sle64_to_cpu(action->record.lcn_list[0]);
982987da915Sopenharmony_ci		printf("-> lcn 0x%llx target 0x%x length %d\n",
983987da915Sopenharmony_ci			(long long)lcn, (int)target, (int)length);
984987da915Sopenharmony_ci	}
985987da915Sopenharmony_ci	xsize = vol->indx_record_size;
986987da915Sopenharmony_ci	if (optv > 1) {
987987da915Sopenharmony_ci		printf("-> existing record :\n");
988987da915Sopenharmony_ci		dump(&buffer[target], length);
989987da915Sopenharmony_ci	}
990987da915Sopenharmony_ci	if ((target + length) <= (count << clusterbits)) {
991987da915Sopenharmony_ci		changed = memcmp(buffer + target, data, length);
992987da915Sopenharmony_ci		err = 0;
993987da915Sopenharmony_ci		if (changed) {
994987da915Sopenharmony_ci			memcpy(buffer + target, data, length);
995987da915Sopenharmony_ci			if (optv > 1) {
996987da915Sopenharmony_ci				printf("-> new record :\n");
997987da915Sopenharmony_ci				dump(buffer + target, length);
998987da915Sopenharmony_ci			}
999987da915Sopenharmony_ci			err = write_protected(vol, &action->record,
1000987da915Sopenharmony_ci							buffer, xsize);
1001987da915Sopenharmony_ci		}
1002987da915Sopenharmony_ci		if (optv > 1) {
1003987da915Sopenharmony_ci			printf("-> data record %s\n",
1004987da915Sopenharmony_ci				(changed ? "updated" : "unchanged"));
1005987da915Sopenharmony_ci		}
1006987da915Sopenharmony_ci	}
1007987da915Sopenharmony_ci	return (err);
1008987da915Sopenharmony_ci}
1009987da915Sopenharmony_ci
1010987da915Sopenharmony_ci/*
1011987da915Sopenharmony_ci *		Add one or more resident attributes
1012987da915Sopenharmony_ci */
1013987da915Sopenharmony_ci
1014987da915Sopenharmony_cistatic int add_resident(ntfs_volume *vol, const struct ACTION_RECORD *action,
1015987da915Sopenharmony_ci			char *buffer, const char *data, u32 target,
1016987da915Sopenharmony_ci			u32 length, u32 oldlength)
1017987da915Sopenharmony_ci{
1018987da915Sopenharmony_ci	LCN lcn;
1019987da915Sopenharmony_ci	MFT_RECORD *entry;
1020987da915Sopenharmony_ci	int err;
1021987da915Sopenharmony_ci	BOOL found;
1022987da915Sopenharmony_ci	int resize;
1023987da915Sopenharmony_ci
1024987da915Sopenharmony_ci	err = 1;
1025987da915Sopenharmony_ci	if (optv > 1) {
1026987da915Sopenharmony_ci		lcn = sle64_to_cpu(action->record.lcn_list[0]);
1027987da915Sopenharmony_ci		printf("-> inode %lld lcn 0x%llx target 0x%x length %d\n",
1028987da915Sopenharmony_ci			(long long)inode_number(&action->record),
1029987da915Sopenharmony_ci			(long long)lcn, (int)target, (int)length);
1030987da915Sopenharmony_ci	}
1031987da915Sopenharmony_ci	entry = (MFT_RECORD*)buffer;
1032987da915Sopenharmony_ci	resize = length - oldlength;
1033987da915Sopenharmony_ci	if (optv > 1) {
1034987da915Sopenharmony_ci		printf("existing data :\n");
1035987da915Sopenharmony_ci		dump(buffer + target,length);
1036987da915Sopenharmony_ci	}
1037987da915Sopenharmony_ci	if (!(length & 7)
1038987da915Sopenharmony_ci	    && !(oldlength & 7)
1039987da915Sopenharmony_ci	    && ((target + length) <= mftrecsz)) {
1040987da915Sopenharmony_ci		/* This has to be an idempotent action */
1041987da915Sopenharmony_ci		err = 0;
1042987da915Sopenharmony_ci		if (data && length)
1043987da915Sopenharmony_ci			found = !memcmp(buffer + target,
1044987da915Sopenharmony_ci						data, length);
1045987da915Sopenharmony_ci		else {
1046987da915Sopenharmony_ci			found = TRUE;
1047987da915Sopenharmony_ci			err = 1;
1048987da915Sopenharmony_ci		}
1049987da915Sopenharmony_ci		if (!found && !err) {
1050987da915Sopenharmony_ci			/* Make space to insert the entry */
1051987da915Sopenharmony_ci			memmove(buffer + target + resize,
1052987da915Sopenharmony_ci				buffer + target,
1053987da915Sopenharmony_ci				mftrecsz - target - resize);
1054987da915Sopenharmony_ci			if (data)
1055987da915Sopenharmony_ci				memcpy(buffer + target, data, length);
1056987da915Sopenharmony_ci			else
1057987da915Sopenharmony_ci				memset(buffer + target, 0, length);
1058987da915Sopenharmony_ci			resize_attribute(entry, NULL, NULL,
1059987da915Sopenharmony_ci						resize, resize);
1060987da915Sopenharmony_ci			if (optv > 1) {
1061987da915Sopenharmony_ci				printf("new data at same location :\n");
1062987da915Sopenharmony_ci				dump(buffer + target, length);
1063987da915Sopenharmony_ci			}
1064987da915Sopenharmony_ci			err = write_protected(vol, &action->record,
1065987da915Sopenharmony_ci						buffer, mftrecsz);
1066987da915Sopenharmony_ci		}
1067987da915Sopenharmony_ci		if (optv > 1) {
1068987da915Sopenharmony_ci			printf("-> MFT record %s\n",
1069987da915Sopenharmony_ci				(found ? "unchanged" : "expanded"));
1070987da915Sopenharmony_ci		}
1071987da915Sopenharmony_ci	}
1072987da915Sopenharmony_ci	return (err);
1073987da915Sopenharmony_ci}
1074987da915Sopenharmony_ci
1075987da915Sopenharmony_ci/*
1076987da915Sopenharmony_ci *		Add one or more non-resident records
1077987da915Sopenharmony_ci */
1078987da915Sopenharmony_ci
1079987da915Sopenharmony_cistatic int delete_non_resident(void /*ntfs_volume *vol,
1080987da915Sopenharmony_ci		const struct ACTION_RECORD *action,
1081987da915Sopenharmony_ci		const char *data, u32 target, u32 length, u32 oldlength*/)
1082987da915Sopenharmony_ci{
1083987da915Sopenharmony_ci	int err;
1084987da915Sopenharmony_ci
1085987da915Sopenharmony_ci	err = 1;
1086987da915Sopenharmony_ci	printf("** delete_non_resident() not implemented\n");
1087987da915Sopenharmony_ci	return (err);
1088987da915Sopenharmony_ci}
1089987da915Sopenharmony_ci
1090987da915Sopenharmony_ci/*
1091987da915Sopenharmony_ci *		Expand a single resident attribute
1092987da915Sopenharmony_ci */
1093987da915Sopenharmony_ci
1094987da915Sopenharmony_cistatic int expand_resident(ntfs_volume *vol, const struct ACTION_RECORD *action,
1095987da915Sopenharmony_ci			char *buffer, const char *data, u32 target,
1096987da915Sopenharmony_ci			u32 length, u32 oldlength)
1097987da915Sopenharmony_ci{
1098987da915Sopenharmony_ci	LCN lcn;
1099987da915Sopenharmony_ci	ATTR_RECORD *attr;
1100987da915Sopenharmony_ci	MFT_RECORD *entry;
1101987da915Sopenharmony_ci	int err;
1102987da915Sopenharmony_ci	BOOL found;
1103987da915Sopenharmony_ci	int resize;
1104987da915Sopenharmony_ci	u16 base;
1105987da915Sopenharmony_ci
1106987da915Sopenharmony_ci	err = 1;
1107987da915Sopenharmony_ci	if (optv > 1) {
1108987da915Sopenharmony_ci		lcn = sle64_to_cpu(action->record.lcn_list[0]);
1109987da915Sopenharmony_ci		printf("-> inode %lld lcn 0x%llx target 0x%x length %d\n",
1110987da915Sopenharmony_ci			(long long)inode_number(&action->record),
1111987da915Sopenharmony_ci			(long long)lcn, (int)target, (int)length);
1112987da915Sopenharmony_ci	}
1113987da915Sopenharmony_ci	entry = (MFT_RECORD*)buffer;
1114987da915Sopenharmony_ci	attr = (ATTR_RECORD*)(buffer
1115987da915Sopenharmony_ci			+ le16_to_cpu(action->record.record_offset));
1116987da915Sopenharmony_ci	if (optv > 1) {
1117987da915Sopenharmony_ci		printf("existing data :\n");
1118987da915Sopenharmony_ci		dump(buffer + target,length);
1119987da915Sopenharmony_ci	}
1120987da915Sopenharmony_ci	base = 24 + 2*attr->name_length;
1121987da915Sopenharmony_ci	resize = ((base + length - 1) | 7)
1122987da915Sopenharmony_ci		- ((base + oldlength - 1) | 7);
1123987da915Sopenharmony_ci	if ((target + length) <= mftrecsz) {
1124987da915Sopenharmony_ci		/* This has to be an idempotent action */
1125987da915Sopenharmony_ci// TODO This test is wrong !
1126987da915Sopenharmony_ci		found = le32_to_cpu(attr->value_length) == length;
1127987da915Sopenharmony_ci		if (found && data && length)
1128987da915Sopenharmony_ci			found = !memcmp(buffer + target, data, length);
1129987da915Sopenharmony_ci		err = 0;
1130987da915Sopenharmony_ci		if (!found) {
1131987da915Sopenharmony_ci			/* Make space to insert the entry */
1132987da915Sopenharmony_ci			memmove(buffer + target + resize,
1133987da915Sopenharmony_ci				buffer + target,
1134987da915Sopenharmony_ci				mftrecsz - target - resize);
1135987da915Sopenharmony_ci// TODO what to do if length is not a multiple of 8 ?
1136987da915Sopenharmony_ci			if (data)
1137987da915Sopenharmony_ci				memcpy(buffer + target, data, length);
1138987da915Sopenharmony_ci			else
1139987da915Sopenharmony_ci				memset(buffer + target, 0, length);
1140987da915Sopenharmony_ci			resize_attribute(entry, attr, NULL,
1141987da915Sopenharmony_ci						length - oldlength, resize);
1142987da915Sopenharmony_ci			if (optv > 1) {
1143987da915Sopenharmony_ci				printf("new data at same location :\n");
1144987da915Sopenharmony_ci				dump(buffer + target, length);
1145987da915Sopenharmony_ci			}
1146987da915Sopenharmony_ci			err = write_protected(vol, &action->record,
1147987da915Sopenharmony_ci						buffer, mftrecsz);
1148987da915Sopenharmony_ci		}
1149987da915Sopenharmony_ci		if (optv > 1) {
1150987da915Sopenharmony_ci			printf("-> MFT record %s\n",
1151987da915Sopenharmony_ci					(found ? "unchanged" : "expanded"));
1152987da915Sopenharmony_ci		}
1153987da915Sopenharmony_ci	}
1154987da915Sopenharmony_ci	return (err);
1155987da915Sopenharmony_ci}
1156987da915Sopenharmony_ci
1157987da915Sopenharmony_ci/*
1158987da915Sopenharmony_ci *		Add one or more non-resident records
1159987da915Sopenharmony_ci */
1160987da915Sopenharmony_ci
1161987da915Sopenharmony_cistatic int add_non_resident(void /*ntfs_volume *vol,
1162987da915Sopenharmony_ci		const struct ACTION_RECORD *action,
1163987da915Sopenharmony_ci		const char *data, u32 target, u32 length, u32 oldlength*/)
1164987da915Sopenharmony_ci{
1165987da915Sopenharmony_ci	int err;
1166987da915Sopenharmony_ci
1167987da915Sopenharmony_ci	printf("** add_non_resident() not implemented\n");
1168987da915Sopenharmony_ci	err = 0;
1169987da915Sopenharmony_ci	return (err);
1170987da915Sopenharmony_ci}
1171987da915Sopenharmony_ci
1172987da915Sopenharmony_ci/*
1173987da915Sopenharmony_ci *		Generic insert a new resident attribute
1174987da915Sopenharmony_ci */
1175987da915Sopenharmony_ci
1176987da915Sopenharmony_cistatic int insert_resident(ntfs_volume *vol, const struct ACTION_RECORD *action,
1177987da915Sopenharmony_ci			char *buffer, const char *data, u32 target,
1178987da915Sopenharmony_ci			u32 length)
1179987da915Sopenharmony_ci{
1180987da915Sopenharmony_ci	LCN lcn;
1181987da915Sopenharmony_ci	ATTR_RECORD *attr;
1182987da915Sopenharmony_ci	const ATTR_RECORD *newattr;
1183987da915Sopenharmony_ci	MFT_RECORD *entry;
1184987da915Sopenharmony_ci	u32 newused;
1185987da915Sopenharmony_ci	u16 links;
1186987da915Sopenharmony_ci	int err;
1187987da915Sopenharmony_ci	BOOL found;
1188987da915Sopenharmony_ci
1189987da915Sopenharmony_ci	err = 1;
1190987da915Sopenharmony_ci	if (optv > 1) {
1191987da915Sopenharmony_ci		lcn = sle64_to_cpu(action->record.lcn_list[0]);
1192987da915Sopenharmony_ci		printf("-> inode %lld lcn 0x%llx target 0x%x length %d\n",
1193987da915Sopenharmony_ci			(long long)inode_number(&action->record),
1194987da915Sopenharmony_ci			(long long)lcn, (int)target, (int)length);
1195987da915Sopenharmony_ci	}
1196987da915Sopenharmony_ci	entry = (MFT_RECORD*)buffer;
1197987da915Sopenharmony_ci	attr = (ATTR_RECORD*)(buffer
1198987da915Sopenharmony_ci			+ le16_to_cpu(action->record.record_offset));
1199987da915Sopenharmony_ci	newattr = (const ATTR_RECORD*)data;
1200987da915Sopenharmony_ci	if (optv > 1) {
1201987da915Sopenharmony_ci		printf("existing record :\n");
1202987da915Sopenharmony_ci		dump(buffer + target,length);
1203987da915Sopenharmony_ci		if (le32_to_cpu(attr->type) < le32_to_cpu(newattr->type)) {
1204987da915Sopenharmony_ci			printf("** Bad attribute order, full record :\n");
1205987da915Sopenharmony_ci			dump(buffer, mftrecsz);
1206987da915Sopenharmony_ci		}
1207987da915Sopenharmony_ci	}
1208987da915Sopenharmony_ci	/* Types must be in ascending order */
1209987da915Sopenharmony_ci	if (valid_type(attr->type)
1210987da915Sopenharmony_ci	    && (le32_to_cpu(attr->type)
1211987da915Sopenharmony_ci		 >= le32_to_cpu(newattr->type))
1212987da915Sopenharmony_ci	    && !(length & 7)
1213987da915Sopenharmony_ci	    && ((target + length) <= mftrecsz)) {
1214987da915Sopenharmony_ci		/* This has to be an idempotent action */
1215987da915Sopenharmony_ci		found = !memcmp(buffer + target, data, length);
1216987da915Sopenharmony_ci		err = 0;
1217987da915Sopenharmony_ci		if (!found) {
1218987da915Sopenharmony_ci			/* Make space to insert the entry */
1219987da915Sopenharmony_ci			memmove(buffer + target + length,
1220987da915Sopenharmony_ci				buffer + target,
1221987da915Sopenharmony_ci				mftrecsz - target - length);
1222987da915Sopenharmony_ci			memcpy(buffer + target, data, length);
1223987da915Sopenharmony_ci			newused = le32_to_cpu(entry->bytes_in_use)
1224987da915Sopenharmony_ci						+ length;
1225987da915Sopenharmony_ci			entry->bytes_in_use = cpu_to_le32(newused);
1226987da915Sopenharmony_ci			if (action->record.redo_operation
1227987da915Sopenharmony_ci			    == const_cpu_to_le16(CreateAttribute)) {
1228987da915Sopenharmony_ci			/*
1229987da915Sopenharmony_ci			 * For a real create, may have to adjust
1230987da915Sopenharmony_ci			 * the next attribute instance
1231987da915Sopenharmony_ci			 */
1232987da915Sopenharmony_ci				adjust_instance(newattr, entry, 1);
1233987da915Sopenharmony_ci			}
1234987da915Sopenharmony_ci			if (newattr->type == AT_FILE_NAME) {
1235987da915Sopenharmony_ci				links = le16_to_cpu(entry->link_count) + 1;
1236987da915Sopenharmony_ci				entry->link_count = cpu_to_le16(links);
1237987da915Sopenharmony_ci			}
1238987da915Sopenharmony_ci			if (optv > 1) {
1239987da915Sopenharmony_ci				printf("expanded record (now 0x%x"
1240987da915Sopenharmony_ci					" bytes used) :\n",
1241987da915Sopenharmony_ci					(int)newused);
1242987da915Sopenharmony_ci				dump(buffer + target, 2*length);
1243987da915Sopenharmony_ci			}
1244987da915Sopenharmony_ci			err = write_protected(vol, &action->record,
1245987da915Sopenharmony_ci						buffer, mftrecsz);
1246987da915Sopenharmony_ci		}
1247987da915Sopenharmony_ci		if (optv > 1) {
1248987da915Sopenharmony_ci			printf("-> MFT record %s\n",
1249987da915Sopenharmony_ci				(found ? "unchanged" : "expanded"));
1250987da915Sopenharmony_ci		}
1251987da915Sopenharmony_ci	}
1252987da915Sopenharmony_ci	return (err);
1253987da915Sopenharmony_ci}
1254987da915Sopenharmony_ci
1255987da915Sopenharmony_ci/*
1256987da915Sopenharmony_ci *		Generic remove a single resident attribute
1257987da915Sopenharmony_ci */
1258987da915Sopenharmony_ci
1259987da915Sopenharmony_cistatic int remove_resident(ntfs_volume *vol, const struct ACTION_RECORD *action,
1260987da915Sopenharmony_ci			char *buffer, const char *data, u32 target,
1261987da915Sopenharmony_ci			u32 length)
1262987da915Sopenharmony_ci{
1263987da915Sopenharmony_ci	LCN lcn;
1264987da915Sopenharmony_ci	ATTR_RECORD *attr;
1265987da915Sopenharmony_ci	MFT_RECORD *entry;
1266987da915Sopenharmony_ci	u32 newused;
1267987da915Sopenharmony_ci	u16 links;
1268987da915Sopenharmony_ci	int err;
1269987da915Sopenharmony_ci	BOOL found;
1270987da915Sopenharmony_ci
1271987da915Sopenharmony_ci	err = 1;
1272987da915Sopenharmony_ci	if (optv > 1) {
1273987da915Sopenharmony_ci		lcn = sle64_to_cpu(action->record.lcn_list[0]);
1274987da915Sopenharmony_ci		printf("-> inode %lld lcn 0x%llx target 0x%x length %d\n",
1275987da915Sopenharmony_ci			(long long)inode_number(&action->record),
1276987da915Sopenharmony_ci			(long long)lcn, (int)target, (int)length);
1277987da915Sopenharmony_ci	}
1278987da915Sopenharmony_ci	entry = (MFT_RECORD*)buffer;
1279987da915Sopenharmony_ci	attr = (ATTR_RECORD*)(buffer
1280987da915Sopenharmony_ci			+ le16_to_cpu(action->record.record_offset));
1281987da915Sopenharmony_ci	if (optv > 1) {
1282987da915Sopenharmony_ci		printf("existing record :\n");
1283987da915Sopenharmony_ci		dump(buffer + target,length);
1284987da915Sopenharmony_ci	}
1285987da915Sopenharmony_ci	if (!(length & 7)
1286987da915Sopenharmony_ci	    && ((target + length) <= mftrecsz)) {
1287987da915Sopenharmony_ci		/* This has to be an idempotent action */
1288987da915Sopenharmony_ci	/* For AT_DATA the value is not always present */
1289987da915Sopenharmony_ci		if (attr->type == AT_DATA)
1290987da915Sopenharmony_ci			found = !memcmp(buffer + target, data,
1291987da915Sopenharmony_ci				le16_to_cpu(attr->value_offset));
1292987da915Sopenharmony_ci		else
1293987da915Sopenharmony_ci			found = !memcmp(buffer + target, data, length);
1294987da915Sopenharmony_ci		if (!found && optv) {
1295987da915Sopenharmony_ci			printf("data 0x%lx 0x%lx offset %d %ld\n",
1296987da915Sopenharmony_ci				(long)le32_to_cpu(attr->type),
1297987da915Sopenharmony_ci				(long)le32_to_cpu(AT_DATA),
1298987da915Sopenharmony_ci				(int)offsetof(ATTR_RECORD, resident_end),
1299987da915Sopenharmony_ci				(long)le16_to_cpu(attr->value_offset));
1300987da915Sopenharmony_ci			printf("The existing record does not match (%d/%d)\n",
1301987da915Sopenharmony_ci				(int)matchcount(buffer + target, data,
1302987da915Sopenharmony_ci				length),(int)length);
1303987da915Sopenharmony_ci			dump(data,length);
1304987da915Sopenharmony_ci			printf("full attr :\n");
1305987da915Sopenharmony_ci			dump((const char*)attr,mftrecsz
1306987da915Sopenharmony_ci				- le16_to_cpu(action->record.record_offset));
1307987da915Sopenharmony_ci		}
1308987da915Sopenharmony_ci		err = 0;
1309987da915Sopenharmony_ci		if (found) {
1310987da915Sopenharmony_ci			if (attr->type == AT_FILE_NAME) {
1311987da915Sopenharmony_ci				links = le16_to_cpu(entry->link_count) - 1;
1312987da915Sopenharmony_ci				entry->link_count = cpu_to_le16(links);
1313987da915Sopenharmony_ci			}
1314987da915Sopenharmony_ci			if (action->record.redo_operation
1315987da915Sopenharmony_ci			    == const_cpu_to_le16(CreateAttribute)) {
1316987da915Sopenharmony_ci				adjust_instance(attr, entry, -1);
1317987da915Sopenharmony_ci			}
1318987da915Sopenharmony_ci			/* Remove the entry */
1319987da915Sopenharmony_ci			memmove(buffer + target,
1320987da915Sopenharmony_ci				buffer + target + length,
1321987da915Sopenharmony_ci				mftrecsz - target - length);
1322987da915Sopenharmony_ci			newused = le32_to_cpu(entry->bytes_in_use) - length;
1323987da915Sopenharmony_ci			entry->bytes_in_use = cpu_to_le32(newused);
1324987da915Sopenharmony_ci			if (optv > 1) {
1325987da915Sopenharmony_ci				printf("new record at same location"
1326987da915Sopenharmony_ci					" (now 0x%x bytes used) :\n",
1327987da915Sopenharmony_ci					(int)newused);
1328987da915Sopenharmony_ci				dump(buffer + target, length);
1329987da915Sopenharmony_ci			}
1330987da915Sopenharmony_ci			err = write_protected(vol, &action->record,
1331987da915Sopenharmony_ci					buffer, mftrecsz);
1332987da915Sopenharmony_ci		}
1333987da915Sopenharmony_ci		if (optv > 1) {
1334987da915Sopenharmony_ci			printf("-> MFT record %s\n",
1335987da915Sopenharmony_ci				(found ? "shrinked" : "unchanged"));
1336987da915Sopenharmony_ci		}
1337987da915Sopenharmony_ci	}
1338987da915Sopenharmony_ci	return (err);
1339987da915Sopenharmony_ci}
1340987da915Sopenharmony_ci
1341987da915Sopenharmony_ci/*
1342987da915Sopenharmony_ci *		Delete one or more resident attributes
1343987da915Sopenharmony_ci */
1344987da915Sopenharmony_ci
1345987da915Sopenharmony_cistatic int delete_resident(ntfs_volume *vol, const struct ACTION_RECORD *action,
1346987da915Sopenharmony_ci			char *buffer, const char *data, u32 target,
1347987da915Sopenharmony_ci			u32 length, u32 oldlength)
1348987da915Sopenharmony_ci{
1349987da915Sopenharmony_ci	LCN lcn;
1350987da915Sopenharmony_ci	MFT_RECORD *entry;
1351987da915Sopenharmony_ci	int err;
1352987da915Sopenharmony_ci	BOOL found;
1353987da915Sopenharmony_ci	int resize;
1354987da915Sopenharmony_ci
1355987da915Sopenharmony_ci	if (optv > 1)
1356987da915Sopenharmony_ci		printf("-> %s()\n",__func__);
1357987da915Sopenharmony_ci	err = 1;
1358987da915Sopenharmony_ci	if (optv > 1) {
1359987da915Sopenharmony_ci		lcn = sle64_to_cpu(action->record.lcn_list[0]);
1360987da915Sopenharmony_ci		printf("-> inode %lld lcn 0x%llx target 0x%x length %d\n",
1361987da915Sopenharmony_ci			(long long)inode_number(&action->record),
1362987da915Sopenharmony_ci			(long long)lcn, (int)target, (int)length);
1363987da915Sopenharmony_ci	}
1364987da915Sopenharmony_ci	entry = (MFT_RECORD*)buffer;
1365987da915Sopenharmony_ci	if (optv > 1) {
1366987da915Sopenharmony_ci		printf("existing data :\n");
1367987da915Sopenharmony_ci		dump(buffer + target,length);
1368987da915Sopenharmony_ci	}
1369987da915Sopenharmony_ci	resize = length - oldlength;
1370987da915Sopenharmony_ci	if (!(length & 7)
1371987da915Sopenharmony_ci	    && !(oldlength & 7)
1372987da915Sopenharmony_ci	    && ((target + oldlength) <= mftrecsz)) {
1373987da915Sopenharmony_ci		/* This has to be an idempotent action */
1374987da915Sopenharmony_ci		err = 0;
1375987da915Sopenharmony_ci		if (data && length)
1376987da915Sopenharmony_ci			found = !memcmp(buffer + target, data, length);
1377987da915Sopenharmony_ci		else {
1378987da915Sopenharmony_ci			found = FALSE;
1379987da915Sopenharmony_ci			err = 1;
1380987da915Sopenharmony_ci		}
1381987da915Sopenharmony_ci		if (!found && !err) {
1382987da915Sopenharmony_ci			/* Remove the entry, if present */
1383987da915Sopenharmony_ci			memmove(buffer + target,
1384987da915Sopenharmony_ci				buffer + target - resize,
1385987da915Sopenharmony_ci				mftrecsz - target + resize);
1386987da915Sopenharmony_ci			resize_attribute(entry, NULL, NULL,
1387987da915Sopenharmony_ci					length - oldlength, resize);
1388987da915Sopenharmony_ci			if (optv > 1) {
1389987da915Sopenharmony_ci				printf("new data at same location :\n");
1390987da915Sopenharmony_ci				dump(buffer + target, length);
1391987da915Sopenharmony_ci			}
1392987da915Sopenharmony_ci			err = write_protected(vol, &action->record,
1393987da915Sopenharmony_ci							buffer, mftrecsz);
1394987da915Sopenharmony_ci		}
1395987da915Sopenharmony_ci		if (optv > 1) {
1396987da915Sopenharmony_ci			printf("-> MFT record %s\n",
1397987da915Sopenharmony_ci				(found ? "unchanged" : "shrinked"));
1398987da915Sopenharmony_ci		}
1399987da915Sopenharmony_ci	}
1400987da915Sopenharmony_ci	return (err);
1401987da915Sopenharmony_ci}
1402987da915Sopenharmony_ci
1403987da915Sopenharmony_cistatic int shrink_resident(ntfs_volume *vol, const struct ACTION_RECORD *action,
1404987da915Sopenharmony_ci			char *buffer, const char *data, u32 target,
1405987da915Sopenharmony_ci			u32 length, u32 oldlength)
1406987da915Sopenharmony_ci{
1407987da915Sopenharmony_ci	LCN lcn;
1408987da915Sopenharmony_ci	ATTR_RECORD *attr;
1409987da915Sopenharmony_ci	MFT_RECORD *entry;
1410987da915Sopenharmony_ci	int err;
1411987da915Sopenharmony_ci	BOOL found;
1412987da915Sopenharmony_ci	int resize;
1413987da915Sopenharmony_ci	u16 base;
1414987da915Sopenharmony_ci
1415987da915Sopenharmony_ci	if (optv > 1)
1416987da915Sopenharmony_ci		printf("-> %s()\n",__func__);
1417987da915Sopenharmony_ci	err = 1;
1418987da915Sopenharmony_ci	if (optv > 1) {
1419987da915Sopenharmony_ci		lcn = sle64_to_cpu(action->record.lcn_list[0]);
1420987da915Sopenharmony_ci		printf("-> inode %lld lcn 0x%llx target 0x%x length %d\n",
1421987da915Sopenharmony_ci			(long long)inode_number(&action->record),
1422987da915Sopenharmony_ci			(long long)lcn, (int)target, (int)length);
1423987da915Sopenharmony_ci	}
1424987da915Sopenharmony_ci	entry = (MFT_RECORD*)buffer;
1425987da915Sopenharmony_ci	attr = (ATTR_RECORD*)(buffer
1426987da915Sopenharmony_ci				+ le16_to_cpu(action->record.record_offset));
1427987da915Sopenharmony_ci	if (optv > 1) {
1428987da915Sopenharmony_ci		printf("existing data :\n");
1429987da915Sopenharmony_ci		dump(buffer + target,length);
1430987da915Sopenharmony_ci	}
1431987da915Sopenharmony_ci	base = 24 + 2*attr->name_length;
1432987da915Sopenharmony_ci	resize = ((base + length - 1) | 7)
1433987da915Sopenharmony_ci		- ((base + oldlength - 1) | 7);
1434987da915Sopenharmony_ci	if ((oldlength > length)
1435987da915Sopenharmony_ci// TODO limit to attr length
1436987da915Sopenharmony_ci	    && ((target + oldlength) <= mftrecsz)) {
1437987da915Sopenharmony_ci		/* This has to be an idempotent action */
1438987da915Sopenharmony_ci		if (data && length)
1439987da915Sopenharmony_ci			found = !memcmp(buffer + target, data, length);
1440987da915Sopenharmony_ci		else
1441987da915Sopenharmony_ci{
1442987da915Sopenharmony_ci// TODO wrong : need checking against the old data, but in known cases
1443987da915Sopenharmony_ci// redo data is not available either and existing data is not zero.
1444987da915Sopenharmony_ci			found = FALSE;
1445987da915Sopenharmony_ciprintf("* fake test, assuming not shrinked : value length %ld length %ld oldlength %ld\n",(long)le32_to_cpu(attr->value_length),(long)length,(long)oldlength);
1446987da915Sopenharmony_ci//dump(buffer + target, oldlength);
1447987da915Sopenharmony_ci}
1448987da915Sopenharmony_ci		err = 0;
1449987da915Sopenharmony_ci		if (!found) {
1450987da915Sopenharmony_ci			if (length) {
1451987da915Sopenharmony_ci				/* Relocate end of record */
1452987da915Sopenharmony_ci// TODO restrict to bytes_in_use
1453987da915Sopenharmony_ci				memmove(buffer + target + length,
1454987da915Sopenharmony_ci					buffer + target + oldlength,
1455987da915Sopenharmony_ci					mftrecsz - target - oldlength);
1456987da915Sopenharmony_ci				/* Insert new data or zeroes */
1457987da915Sopenharmony_ci				if (data)
1458987da915Sopenharmony_ci					memcpy(buffer + target, data, length);
1459987da915Sopenharmony_ci				else
1460987da915Sopenharmony_ci					memset(buffer + target, 0, length);
1461987da915Sopenharmony_ci			} else {
1462987da915Sopenharmony_ci				/* Remove the entry, unless targeted size */
1463987da915Sopenharmony_ci				memmove(buffer + target,
1464987da915Sopenharmony_ci					buffer + target - resize,
1465987da915Sopenharmony_ci					mftrecsz - target + resize);
1466987da915Sopenharmony_ci			}
1467987da915Sopenharmony_ci			resize_attribute(entry, attr, NULL,
1468987da915Sopenharmony_ci					length - oldlength, resize);
1469987da915Sopenharmony_ci			if (optv > 1) {
1470987da915Sopenharmony_ci				printf("new data at same location :\n");
1471987da915Sopenharmony_ci				dump(buffer + target, length);
1472987da915Sopenharmony_ci			}
1473987da915Sopenharmony_ci			err = write_protected(vol, &action->record,
1474987da915Sopenharmony_ci						buffer, mftrecsz);
1475987da915Sopenharmony_ci		}
1476987da915Sopenharmony_ci		if (optv > 1) {
1477987da915Sopenharmony_ci			printf("-> MFT record %s\n",
1478987da915Sopenharmony_ci				(found ? "unchanged" : "shrinked"));
1479987da915Sopenharmony_ci		}
1480987da915Sopenharmony_ci	}
1481987da915Sopenharmony_ci	return (err);
1482987da915Sopenharmony_ci}
1483987da915Sopenharmony_ci
1484987da915Sopenharmony_cistatic int update_index(ntfs_volume *vol, const struct ACTION_RECORD *action,
1485987da915Sopenharmony_ci		char *buffer, const char *data, u32 target, u32 length)
1486987da915Sopenharmony_ci{
1487987da915Sopenharmony_ci	LCN lcn;
1488987da915Sopenharmony_ci	INDEX_BLOCK *indx;
1489987da915Sopenharmony_ci	u32 xsize;
1490987da915Sopenharmony_ci	BOOL changed;
1491987da915Sopenharmony_ci	int err;
1492987da915Sopenharmony_ci
1493987da915Sopenharmony_ci	err = 1;
1494987da915Sopenharmony_ci	if (optv > 1) {
1495987da915Sopenharmony_ci		lcn = sle64_to_cpu(action->record.lcn_list[0]);
1496987da915Sopenharmony_ci		printf("-> lcn 0x%llx target 0x%x length %d\n",
1497987da915Sopenharmony_ci			(long long)lcn, (int)target, (int)length);
1498987da915Sopenharmony_ci	}
1499987da915Sopenharmony_ci	xsize = vol->indx_record_size;
1500987da915Sopenharmony_ci	indx = (INDEX_BLOCK*)(buffer
1501987da915Sopenharmony_ci			+ le16_to_cpu(action->record.record_offset));
1502987da915Sopenharmony_ci	if (optv > 1) {
1503987da915Sopenharmony_ci		printf("-> existing index :\n");
1504987da915Sopenharmony_ci		dump(&buffer[target], length);
1505987da915Sopenharmony_ci	}
1506987da915Sopenharmony_ci	if ((indx->magic == magic_INDX)
1507987da915Sopenharmony_ci	    && !(length & 7)
1508987da915Sopenharmony_ci	    && ((target + length) <= xsize)) {
1509987da915Sopenharmony_ci		/* This has to be an idempotent action */
1510987da915Sopenharmony_ci		changed = memcmp(buffer + target, data, length);
1511987da915Sopenharmony_ci		err = 0;
1512987da915Sopenharmony_ci		if (changed) {
1513987da915Sopenharmony_ci			/* Update the entry */
1514987da915Sopenharmony_ci			memcpy(buffer + target, data, length);
1515987da915Sopenharmony_ci			if (optv > 1) {
1516987da915Sopenharmony_ci				printf("-> new index :\n");
1517987da915Sopenharmony_ci				dump(&buffer[target], length);
1518987da915Sopenharmony_ci			}
1519987da915Sopenharmony_ci			err = write_protected(vol, &action->record,
1520987da915Sopenharmony_ci						buffer, xsize);
1521987da915Sopenharmony_ci		}
1522987da915Sopenharmony_ci		if (optv > 1) {
1523987da915Sopenharmony_ci			printf("-> INDX record %s\n",
1524987da915Sopenharmony_ci				(changed ? "updated" : "unchanged"));
1525987da915Sopenharmony_ci		}
1526987da915Sopenharmony_ci	}
1527987da915Sopenharmony_ci	return (err);
1528987da915Sopenharmony_ci}
1529987da915Sopenharmony_ci
1530987da915Sopenharmony_ci/*
1531987da915Sopenharmony_ci *		Controversial deletion of file names, see undo_delete_file()
1532987da915Sopenharmony_ci */
1533987da915Sopenharmony_ci
1534987da915Sopenharmony_cistatic int delete_names(char *buffer)
1535987da915Sopenharmony_ci{
1536987da915Sopenharmony_ci	MFT_RECORD *record;
1537987da915Sopenharmony_ci	ATTR_RECORD *attr;
1538987da915Sopenharmony_ci	u32 used;
1539987da915Sopenharmony_ci	u32 pos;
1540987da915Sopenharmony_ci	int length;
1541987da915Sopenharmony_ci	int cnt;
1542987da915Sopenharmony_ci
1543987da915Sopenharmony_ci	record = (MFT_RECORD*)buffer;
1544987da915Sopenharmony_ci	pos = le16_to_cpu(record->attrs_offset);
1545987da915Sopenharmony_ci	used = le32_to_cpu(record->bytes_in_use);
1546987da915Sopenharmony_ci	cnt = 0;
1547987da915Sopenharmony_ci	do {
1548987da915Sopenharmony_ci		attr = (ATTR_RECORD*)&buffer[pos];
1549987da915Sopenharmony_ci		length = le32_to_cpu(attr->length);
1550987da915Sopenharmony_ci		if (attr->type == AT_FILE_NAME) {
1551987da915Sopenharmony_ci			if (optv)
1552987da915Sopenharmony_ci				showname("Controversial deletion of ",
1553987da915Sopenharmony_ci					&buffer[pos+90], buffer[pos+88] & 255);
1554987da915Sopenharmony_ci			memmove(buffer + pos, buffer + pos + length,
1555987da915Sopenharmony_ci				mftrecsz - pos - length);
1556987da915Sopenharmony_ci			used -= length;
1557987da915Sopenharmony_ci			cnt++;
1558987da915Sopenharmony_ci		} else
1559987da915Sopenharmony_ci			pos += length;
1560987da915Sopenharmony_ci	} while ((pos < used)
1561987da915Sopenharmony_ci		&& (le32_to_cpu(attr->type) <= le32_to_cpu(AT_FILE_NAME)));
1562987da915Sopenharmony_ci	record->bytes_in_use = cpu_to_le32(used);
1563987da915Sopenharmony_ci	record->link_count = cpu_to_le16(0);
1564987da915Sopenharmony_ci	return (cnt ? 0 : 1);
1565987da915Sopenharmony_ci}
1566987da915Sopenharmony_ci
1567987da915Sopenharmony_cistatic int rebuildname(const INDEX_ENTRY *index)
1568987da915Sopenharmony_ci{
1569987da915Sopenharmony_ci	ATTR_RECORD *attr;
1570987da915Sopenharmony_ci	int headlth;
1571987da915Sopenharmony_ci	int datalth;
1572987da915Sopenharmony_ci
1573987da915Sopenharmony_ci	datalth = le16_to_cpu(index->length)
1574987da915Sopenharmony_ci				- offsetof(INDEX_ENTRY,key.file_name);
1575987da915Sopenharmony_ci	headlth = offsetof(ATTR_RECORD,resident_end);
1576987da915Sopenharmony_ci	attr = (ATTR_RECORD*)malloc(headlth + datalth);
1577987da915Sopenharmony_ci	if (attr) {
1578987da915Sopenharmony_ci		attr->type = AT_FILE_NAME;
1579987da915Sopenharmony_ci		attr->length = cpu_to_le32(headlth + datalth);
1580987da915Sopenharmony_ci		attr->non_resident = 0;
1581987da915Sopenharmony_ci		attr->name_length = 0;
1582987da915Sopenharmony_ci		attr->name_offset = const_cpu_to_le16(0);
1583987da915Sopenharmony_ci		attr->flags = const_cpu_to_le16(0);
1584987da915Sopenharmony_ci		attr->instance = const_cpu_to_le16(0);
1585987da915Sopenharmony_ci		attr->value_length = cpu_to_le32(
1586987da915Sopenharmony_ci			2*index->key.file_name.file_name_length
1587987da915Sopenharmony_ci			+ offsetof(FILE_NAME_ATTR, file_name));
1588987da915Sopenharmony_ci		attr->value_offset = cpu_to_le16(headlth);
1589987da915Sopenharmony_ci		attr->resident_flags = RESIDENT_ATTR_IS_INDEXED;
1590987da915Sopenharmony_ci		memcpy(attr->resident_end, &index->key.file_name, datalth);
1591987da915Sopenharmony_ci		free(attr);
1592987da915Sopenharmony_ci	}
1593987da915Sopenharmony_ci	return (0);
1594987da915Sopenharmony_ci}
1595987da915Sopenharmony_ci
1596987da915Sopenharmony_ci/*
1597987da915Sopenharmony_ci *		Controversial creation of an index allocation attribute
1598987da915Sopenharmony_ci *
1599987da915Sopenharmony_ci *	This is useful for turning the clock backward, but cannot
1600987da915Sopenharmony_ci *	work properly in the general case and must not be used for
1601987da915Sopenharmony_ci *	a real sync.
1602987da915Sopenharmony_ci *	The main problem is to synchronize the file names when an
1603987da915Sopenharmony_ci *	inode is reused with a different name.
1604987da915Sopenharmony_ci */
1605987da915Sopenharmony_ci
1606987da915Sopenharmony_cistatic int insert_index_allocation(ntfs_volume *vol, char *buffer, u32 offs)
1607987da915Sopenharmony_ci{
1608987da915Sopenharmony_ci	MFT_RECORD *record;
1609987da915Sopenharmony_ci	ATTR_RECORD *attr;
1610987da915Sopenharmony_ci	u32 used;
1611987da915Sopenharmony_ci	u32 pos;
1612987da915Sopenharmony_ci	u32 xsize;
1613987da915Sopenharmony_ci	u16 instance;
1614987da915Sopenharmony_ci	int length;
1615987da915Sopenharmony_ci	int addedlength;
1616987da915Sopenharmony_ci	int namelength;
1617987da915Sopenharmony_ci	int err;
1618987da915Sopenharmony_ci	static const unsigned char bitmap[] =
1619987da915Sopenharmony_ci			{ 1, 0, 0, 0, 0, 0, 0, 0 } ;
1620987da915Sopenharmony_ci
1621987da915Sopenharmony_ci	err = 1;
1622987da915Sopenharmony_ci	if (opts) {
1623987da915Sopenharmony_ci		printf("** Call to unsupported insert_index_allocation()\n");
1624987da915Sopenharmony_ci	} else {
1625987da915Sopenharmony_ci		record = (MFT_RECORD*)buffer;
1626987da915Sopenharmony_ci		pos = le16_to_cpu(record->attrs_offset);
1627987da915Sopenharmony_ci		used = le32_to_cpu(record->bytes_in_use);
1628987da915Sopenharmony_ci		attr = (ATTR_RECORD*)&buffer[pos];
1629987da915Sopenharmony_ci		while ((pos < used)
1630987da915Sopenharmony_ci		    && (le32_to_cpu(attr->type) < le32_to_cpu(AT_INDEX_ROOT))) {
1631987da915Sopenharmony_ci			pos += le32_to_cpu(attr->length);
1632987da915Sopenharmony_ci			attr = (ATTR_RECORD*)&buffer[pos];
1633987da915Sopenharmony_ci		}
1634987da915Sopenharmony_ci		length = le32_to_cpu(attr->length);
1635987da915Sopenharmony_ci		addedlength = length - 8 /* index allocation */
1636987da915Sopenharmony_ci                     + length - 48; /* bitmap */
1637987da915Sopenharmony_ci		if ((attr->type == AT_INDEX_ROOT)
1638987da915Sopenharmony_ci		    && ((pos + length) == offs)
1639987da915Sopenharmony_ci		    && ((used + addedlength) < mftrecsz)) {
1640987da915Sopenharmony_ci			/* Make space for the attribute */
1641987da915Sopenharmony_ci			memmove(buffer + offs + addedlength, buffer + offs,
1642987da915Sopenharmony_ci					mftrecsz - offs - addedlength);
1643987da915Sopenharmony_ci			record->bytes_in_use = cpu_to_le32(used + addedlength);
1644987da915Sopenharmony_ci			/*
1645987da915Sopenharmony_ci			 * Insert an AT_INDEX_ALLOCATION
1646987da915Sopenharmony_ci			 */
1647987da915Sopenharmony_ci			attr = (ATTR_RECORD*)&buffer[offs];
1648987da915Sopenharmony_ci			attr->type = AT_INDEX_ALLOCATION;
1649987da915Sopenharmony_ci			attr->length = cpu_to_le32(length - 8);
1650987da915Sopenharmony_ci			attr->non_resident = 1;
1651987da915Sopenharmony_ci			namelength = buffer[pos + 9] & 255;
1652987da915Sopenharmony_ci			attr->name_length = namelength;
1653987da915Sopenharmony_ci			attr->name_offset = const_cpu_to_le16(0x40);
1654987da915Sopenharmony_ci			memcpy(buffer + offs + 0x40, buffer + pos + 0x18,
1655987da915Sopenharmony_ci								2*namelength);
1656987da915Sopenharmony_ci			attr->flags = const_cpu_to_le16(0);
1657987da915Sopenharmony_ci			/* Should we really take a new instance ? */
1658987da915Sopenharmony_ci			attr->instance = record->next_attr_instance;
1659987da915Sopenharmony_ci			instance = le16_to_cpu(record->next_attr_instance) + 1;
1660987da915Sopenharmony_ci			record->next_attr_instance = cpu_to_le16(instance);
1661987da915Sopenharmony_ci			attr->lowest_vcn = const_cpu_to_sle64(0);
1662987da915Sopenharmony_ci			attr->highest_vcn = const_cpu_to_sle64(0);
1663987da915Sopenharmony_ci			attr->mapping_pairs_offset = cpu_to_le16(
1664987da915Sopenharmony_ci							2*namelength + 0x40);
1665987da915Sopenharmony_ci			attr->compression_unit = 0;
1666987da915Sopenharmony_ci			xsize = vol->indx_record_size;
1667987da915Sopenharmony_ci			attr->allocated_size = cpu_to_sle64(xsize);
1668987da915Sopenharmony_ci			attr->data_size = attr->allocated_size;
1669987da915Sopenharmony_ci			attr->initialized_size = attr->allocated_size;
1670987da915Sopenharmony_ci			/*
1671987da915Sopenharmony_ci			 * Insert an AT_INDEX_BITMAP
1672987da915Sopenharmony_ci			 */
1673987da915Sopenharmony_ci			attr = (ATTR_RECORD*)&buffer[offs + length - 8];
1674987da915Sopenharmony_ci			attr->type = AT_BITMAP;
1675987da915Sopenharmony_ci			attr->length = cpu_to_le32(length - 48);
1676987da915Sopenharmony_ci			attr->non_resident = 0;
1677987da915Sopenharmony_ci			namelength = buffer[pos + 9] & 255;
1678987da915Sopenharmony_ci			attr->name_length = namelength;
1679987da915Sopenharmony_ci			attr->name_offset = const_cpu_to_le16(0x18);
1680987da915Sopenharmony_ci			memcpy(buffer + offs + length - 8 + 0x18,
1681987da915Sopenharmony_ci					buffer + pos + 0x18, 2*namelength);
1682987da915Sopenharmony_ci			attr->flags = const_cpu_to_le16(0);
1683987da915Sopenharmony_ci			attr->value_length = const_cpu_to_le32(8);
1684987da915Sopenharmony_ci			attr->value_offset = cpu_to_le16(2*namelength + 24);
1685987da915Sopenharmony_ci			attr->resident_flags = 0;
1686987da915Sopenharmony_ci			memcpy((char*)attr->resident_end + 2*namelength,
1687987da915Sopenharmony_ci								bitmap, 8);
1688987da915Sopenharmony_ci			/* Should we really take a new instance ? */
1689987da915Sopenharmony_ci			attr->instance = record->next_attr_instance;
1690987da915Sopenharmony_ci			instance = le16_to_cpu(record->next_attr_instance) + 1;
1691987da915Sopenharmony_ci			record->next_attr_instance = cpu_to_le16(instance);
1692987da915Sopenharmony_ci			err = sanity_mft(buffer);
1693987da915Sopenharmony_ci		} else {
1694987da915Sopenharmony_ci			printf("** index root does not match\n");
1695987da915Sopenharmony_ci			err = 1;
1696987da915Sopenharmony_ci		}
1697987da915Sopenharmony_ci	}
1698987da915Sopenharmony_ci	return (err);
1699987da915Sopenharmony_ci}
1700987da915Sopenharmony_ci
1701987da915Sopenharmony_ci/*
1702987da915Sopenharmony_ci *		Check whether a full MFT record is fed by an action
1703987da915Sopenharmony_ci *
1704987da915Sopenharmony_ci *	If so, checking the validity of existing record is pointless
1705987da915Sopenharmony_ci */
1706987da915Sopenharmony_ci
1707987da915Sopenharmony_cistatic BOOL check_full_mft(const struct ACTION_RECORD *action, BOOL redoing)
1708987da915Sopenharmony_ci{
1709987da915Sopenharmony_ci	const MFT_RECORD *record;
1710987da915Sopenharmony_ci	const ATTR_RECORD *attr;
1711987da915Sopenharmony_ci	u32 length;
1712987da915Sopenharmony_ci	u32 k;
1713987da915Sopenharmony_ci	BOOL ok;
1714987da915Sopenharmony_ci
1715987da915Sopenharmony_ci	if (redoing) {
1716987da915Sopenharmony_ci		record = (const MFT_RECORD*)((const char*)&action->record
1717987da915Sopenharmony_ci				+ get_redo_offset(&action->record));
1718987da915Sopenharmony_ci		length = le16_to_cpu(action->record.redo_length);
1719987da915Sopenharmony_ci	} else {
1720987da915Sopenharmony_ci		record = (const MFT_RECORD*)((const char*)&action->record
1721987da915Sopenharmony_ci				+ get_undo_offset(&action->record));
1722987da915Sopenharmony_ci		length = le16_to_cpu(action->record.undo_length);
1723987da915Sopenharmony_ci	}
1724987da915Sopenharmony_ci		/* The length in use must be fed */
1725987da915Sopenharmony_ci	ok = !action->record.record_offset
1726987da915Sopenharmony_ci		&& !action->record.attribute_offset
1727987da915Sopenharmony_ci		&& (record->magic == magic_FILE)
1728987da915Sopenharmony_ci		&& (length <= mftrecsz)
1729987da915Sopenharmony_ci		&& (length >= (offsetof(MFT_RECORD, bytes_in_use)
1730987da915Sopenharmony_ci			 + sizeof(record->bytes_in_use)));
1731987da915Sopenharmony_ci	if (ok) {
1732987da915Sopenharmony_ci		k = le16_to_cpu(record->attrs_offset);
1733987da915Sopenharmony_ci		attr = (const ATTR_RECORD*)((const char*)record + k);
1734987da915Sopenharmony_ci		while (((k + sizeof(attr->type)) <= length)
1735987da915Sopenharmony_ci		    && (attr->type != AT_END)
1736987da915Sopenharmony_ci		    && valid_type(attr->type)) {
1737987da915Sopenharmony_ci			k += le32_to_cpu(attr->length);
1738987da915Sopenharmony_ci			attr = (const ATTR_RECORD*)((const char*)record + k);
1739987da915Sopenharmony_ci		}
1740987da915Sopenharmony_ci			/* AT_END must be present */
1741987da915Sopenharmony_ci		ok = ((k + sizeof(attr->type)) <= length)
1742987da915Sopenharmony_ci		    && (attr->type == AT_END);
1743987da915Sopenharmony_ci	}
1744987da915Sopenharmony_ci	return (ok);
1745987da915Sopenharmony_ci}
1746987da915Sopenharmony_ci
1747987da915Sopenharmony_ci/*
1748987da915Sopenharmony_ci *		Check whether a full index block is fed by the log record
1749987da915Sopenharmony_ci *
1750987da915Sopenharmony_ci *	If so, checking the validity of existing record is pointless
1751987da915Sopenharmony_ci */
1752987da915Sopenharmony_ci
1753987da915Sopenharmony_cistatic BOOL check_full_index(const struct ACTION_RECORD *action, BOOL redoing)
1754987da915Sopenharmony_ci{
1755987da915Sopenharmony_ci	const INDEX_BLOCK *indx;
1756987da915Sopenharmony_ci	u32 length;
1757987da915Sopenharmony_ci
1758987da915Sopenharmony_ci	if (redoing) {
1759987da915Sopenharmony_ci		indx = (const INDEX_BLOCK*)((const char*)&action->record
1760987da915Sopenharmony_ci				+ get_redo_offset(&action->record));
1761987da915Sopenharmony_ci		length = le16_to_cpu(action->record.redo_length);
1762987da915Sopenharmony_ci	} else {
1763987da915Sopenharmony_ci		indx = (const INDEX_BLOCK*)((const char*)&action->record
1764987da915Sopenharmony_ci				+ get_undo_offset(&action->record));
1765987da915Sopenharmony_ci		length = le16_to_cpu(action->record.undo_length);
1766987da915Sopenharmony_ci	}
1767987da915Sopenharmony_ci	/* the index length must be fed, so must be the full index block */
1768987da915Sopenharmony_ci	return (!action->record.record_offset
1769987da915Sopenharmony_ci		&& !action->record.attribute_offset
1770987da915Sopenharmony_ci		&& (indx->magic == magic_INDX)
1771987da915Sopenharmony_ci		&& (length >= (offsetof(INDEX_BLOCK, index.index_length) + 4))
1772987da915Sopenharmony_ci		&& (length >= (le32_to_cpu(indx->index.index_length) + 24)));
1773987da915Sopenharmony_ci}
1774987da915Sopenharmony_ci
1775987da915Sopenharmony_ci/*
1776987da915Sopenharmony_ci *		Create an index block for undoing its deletion
1777987da915Sopenharmony_ci *
1778987da915Sopenharmony_ci *	This is useful for turning the clock backward, but cannot
1779987da915Sopenharmony_ci *	work properly in the general case and must not be used for
1780987da915Sopenharmony_ci *	a real sync.
1781987da915Sopenharmony_ci */
1782987da915Sopenharmony_ci
1783987da915Sopenharmony_cistatic int create_indx(ntfs_volume *vol, const struct ACTION_RECORD *action,
1784987da915Sopenharmony_ci			char *buffer)
1785987da915Sopenharmony_ci{
1786987da915Sopenharmony_ci	INDEX_BLOCK *indx;
1787987da915Sopenharmony_ci	INDEX_ENTRY_HEADER *ixhead;
1788987da915Sopenharmony_ci	INDEX_ENTRY *ixentry;
1789987da915Sopenharmony_ci	VCN vcn;
1790987da915Sopenharmony_ci	int err;
1791987da915Sopenharmony_ci
1792987da915Sopenharmony_ci	if (opts) {
1793987da915Sopenharmony_ci		printf("** Call to unsupported create_indx()\n");
1794987da915Sopenharmony_ci		err = 1;
1795987da915Sopenharmony_ci	} else {
1796987da915Sopenharmony_ci		err = 0;
1797987da915Sopenharmony_ci		indx = (INDEX_BLOCK*)buffer;
1798987da915Sopenharmony_ci		indx->magic = magic_INDX;
1799987da915Sopenharmony_ci// TODO compute properly
1800987da915Sopenharmony_ci		indx->usa_ofs = const_cpu_to_le16(0x28);
1801987da915Sopenharmony_ci		indx->usa_count = const_cpu_to_le16(9);
1802987da915Sopenharmony_ci		indx->lsn = action->record.this_lsn;
1803987da915Sopenharmony_ci		vcn = sle64_to_cpu(action->record.target_vcn);
1804987da915Sopenharmony_ci			/* beware of size change on big-endian cpus */
1805987da915Sopenharmony_ci		indx->index_block_vcn = cpu_to_sle64(vcn);
1806987da915Sopenharmony_ci			/* INDEX_HEADER */
1807987da915Sopenharmony_ci		indx->index.entries_offset = const_cpu_to_le32(0x28);
1808987da915Sopenharmony_ci		indx->index.index_length = const_cpu_to_le32(0x38);
1809987da915Sopenharmony_ci		indx->index.allocated_size =
1810987da915Sopenharmony_ci				cpu_to_le32(vol->indx_record_size - 24);
1811987da915Sopenharmony_ci		indx->index.ih_flags = 0;
1812987da915Sopenharmony_ci			/* INDEX_ENTRY_HEADER */
1813987da915Sopenharmony_ci		ixhead = (INDEX_ENTRY_HEADER*)(buffer + 0x28);
1814987da915Sopenharmony_ci		ixhead->length = cpu_to_le16(vol->indx_record_size - 24);
1815987da915Sopenharmony_ci			/* terminating INDEX_ENTRY */
1816987da915Sopenharmony_ci		ixentry = (INDEX_ENTRY*)(buffer + 0x40);
1817987da915Sopenharmony_ci		ixentry->indexed_file = const_cpu_to_le64(0);
1818987da915Sopenharmony_ci		ixentry->length = const_cpu_to_le16(16);
1819987da915Sopenharmony_ci		ixentry->key_length = const_cpu_to_le16(0);
1820987da915Sopenharmony_ci		ixentry->ie_flags = INDEX_ENTRY_END;
1821987da915Sopenharmony_ci	}
1822987da915Sopenharmony_ci	return (err);
1823987da915Sopenharmony_ci}
1824987da915Sopenharmony_ci
1825987da915Sopenharmony_cistatic int redo_action37(ntfs_volume *vol, const struct ACTION_RECORD *action,
1826987da915Sopenharmony_ci			char *buffer)
1827987da915Sopenharmony_ci{
1828987da915Sopenharmony_ci	u32 target;
1829987da915Sopenharmony_ci	u32 length;
1830987da915Sopenharmony_ci	int err;
1831987da915Sopenharmony_ci
1832987da915Sopenharmony_ci	if (optv > 1)
1833987da915Sopenharmony_ci		printf("-> %s()\n",__func__);
1834987da915Sopenharmony_ci	err = 1;
1835987da915Sopenharmony_ci	length = le16_to_cpu(action->record.redo_length);
1836987da915Sopenharmony_ci	target = le16_to_cpu(action->record.record_offset)
1837987da915Sopenharmony_ci		+ le16_to_cpu(action->record.attribute_offset);
1838987da915Sopenharmony_ci	if (optv > 1) {
1839987da915Sopenharmony_ci		printf("existing data :\n");
1840987da915Sopenharmony_ci		dump(buffer + target,length);
1841987da915Sopenharmony_ci	}
1842987da915Sopenharmony_ci	if ((target + length) == mftrecsz) {
1843987da915Sopenharmony_ci		memset(buffer + target, 0, length);
1844987da915Sopenharmony_ci		err = write_protected(vol, &action->record,
1845987da915Sopenharmony_ci					buffer, mftrecsz);
1846987da915Sopenharmony_ci		if (optv > 1) {
1847987da915Sopenharmony_ci			printf("-> MFT record trimmed\n");
1848987da915Sopenharmony_ci		}
1849987da915Sopenharmony_ci	} else {
1850987da915Sopenharmony_ci		printf("** Bad action-37, inode %lld record :\n",
1851987da915Sopenharmony_ci			(long long)inode_number(&action->record));
1852987da915Sopenharmony_ci		printf("target %d length %d sum %d\n",
1853987da915Sopenharmony_ci			(int)target,(int)length,(int)(target + length));
1854987da915Sopenharmony_ci		dump(buffer,mftrecsz);
1855987da915Sopenharmony_ci	}
1856987da915Sopenharmony_ci	err = 0;
1857987da915Sopenharmony_ci	return (err);
1858987da915Sopenharmony_ci}
1859987da915Sopenharmony_ci
1860987da915Sopenharmony_cistatic int redo_add_index(ntfs_volume *vol, const struct ACTION_RECORD *action,
1861987da915Sopenharmony_ci			char *buffer)
1862987da915Sopenharmony_ci{
1863987da915Sopenharmony_ci	LCN lcn;
1864987da915Sopenharmony_ci	const char *data;
1865987da915Sopenharmony_ci	INDEX_BLOCK *indx;
1866987da915Sopenharmony_ci	u32 target;
1867987da915Sopenharmony_ci	u32 length;
1868987da915Sopenharmony_ci	u32 xsize;
1869987da915Sopenharmony_ci	u32 indexlth;
1870987da915Sopenharmony_ci	int err;
1871987da915Sopenharmony_ci	BOOL found;
1872987da915Sopenharmony_ci
1873987da915Sopenharmony_ci	if (optv > 1)
1874987da915Sopenharmony_ci		printf("-> %s()\n",__func__);
1875987da915Sopenharmony_ci	err = 1;
1876987da915Sopenharmony_ci	data = ((const char*)&action->record)
1877987da915Sopenharmony_ci			+ get_redo_offset(&action->record);
1878987da915Sopenharmony_ci	length = le16_to_cpu(action->record.redo_length);
1879987da915Sopenharmony_ci	target = le16_to_cpu(action->record.record_offset)
1880987da915Sopenharmony_ci		+ le16_to_cpu(action->record.attribute_offset);
1881987da915Sopenharmony_ci	if (optv > 1) {
1882987da915Sopenharmony_ci		lcn = sle64_to_cpu(action->record.lcn_list[0]);
1883987da915Sopenharmony_ci		printf("-> lcn 0x%llx target 0x%x length %d\n",
1884987da915Sopenharmony_ci			(long long)lcn, (int)target, (int)length);
1885987da915Sopenharmony_ci	}
1886987da915Sopenharmony_ci	xsize = vol->indx_record_size;
1887987da915Sopenharmony_ci	indx = (INDEX_BLOCK*)(buffer
1888987da915Sopenharmony_ci			+ le16_to_cpu(action->record.record_offset));
1889987da915Sopenharmony_ci	if (optv > 1) {
1890987da915Sopenharmony_ci		printf("-> existing record :\n");
1891987da915Sopenharmony_ci		dump(&buffer[target], length);
1892987da915Sopenharmony_ci	}
1893987da915Sopenharmony_ci	if ((indx->magic == magic_INDX)
1894987da915Sopenharmony_ci	    && !(length & 7)
1895987da915Sopenharmony_ci	    && ((target + length) <= xsize)) {
1896987da915Sopenharmony_ci		/* This has to be an idempotent action */
1897987da915Sopenharmony_ci		found = !memcmp(buffer + target, data, length);
1898987da915Sopenharmony_ci		err = 0;
1899987da915Sopenharmony_ci		if (!found) {
1900987da915Sopenharmony_ci			/* Make space to insert the entry */
1901987da915Sopenharmony_ci			memmove(buffer + target + length,
1902987da915Sopenharmony_ci				buffer + target,
1903987da915Sopenharmony_ci				xsize - target - length);
1904987da915Sopenharmony_ci			memcpy(buffer + target, data, length);
1905987da915Sopenharmony_ci			indexlth = le32_to_cpu(indx->index.index_length)
1906987da915Sopenharmony_ci						+ length;
1907987da915Sopenharmony_ci			indx->index.index_length = cpu_to_le32(indexlth);
1908987da915Sopenharmony_ci			if (optv > 1) {
1909987da915Sopenharmony_ci				printf("-> inserted record :\n");
1910987da915Sopenharmony_ci				dump(&buffer[target], length);
1911987da915Sopenharmony_ci			}
1912987da915Sopenharmony_ci			err = write_protected(vol, &action->record,
1913987da915Sopenharmony_ci						buffer, xsize);
1914987da915Sopenharmony_ci		}
1915987da915Sopenharmony_ci		if (optv > 1) {
1916987da915Sopenharmony_ci			printf("-> INDX record %s\n",
1917987da915Sopenharmony_ci				(found ? "unchanged" : "inserted"));
1918987da915Sopenharmony_ci		}
1919987da915Sopenharmony_ci	}
1920987da915Sopenharmony_ci	return (err);
1921987da915Sopenharmony_ci}
1922987da915Sopenharmony_ci
1923987da915Sopenharmony_cistatic int redo_add_root_index(ntfs_volume *vol,
1924987da915Sopenharmony_ci			const struct ACTION_RECORD *action, char *buffer)
1925987da915Sopenharmony_ci{
1926987da915Sopenharmony_ci	LCN lcn;
1927987da915Sopenharmony_ci	const char *data;
1928987da915Sopenharmony_ci	ATTR_RECORD *attr;
1929987da915Sopenharmony_ci	MFT_RECORD *entry;
1930987da915Sopenharmony_ci	INDEX_ROOT *index;
1931987da915Sopenharmony_ci	u32 target;
1932987da915Sopenharmony_ci	u32 length;
1933987da915Sopenharmony_ci	int err;
1934987da915Sopenharmony_ci	BOOL found;
1935987da915Sopenharmony_ci
1936987da915Sopenharmony_ci	if (optv > 1)
1937987da915Sopenharmony_ci		printf("-> %s()\n",__func__);
1938987da915Sopenharmony_ci	err = 1;
1939987da915Sopenharmony_ci	data = ((const char*)&action->record)
1940987da915Sopenharmony_ci			+ get_redo_offset(&action->record);
1941987da915Sopenharmony_ci	length = le16_to_cpu(action->record.redo_length);
1942987da915Sopenharmony_ci	target = le16_to_cpu(action->record.record_offset)
1943987da915Sopenharmony_ci		+ le16_to_cpu(action->record.attribute_offset);
1944987da915Sopenharmony_ci	if (optv > 1) {
1945987da915Sopenharmony_ci		lcn = sle64_to_cpu(action->record.lcn_list[0]);
1946987da915Sopenharmony_ci		printf("-> inode %lld lcn 0x%llx target 0x%x length %d\n",
1947987da915Sopenharmony_ci			(long long)inode_number(&action->record),
1948987da915Sopenharmony_ci			(long long)lcn, (int)target, (int)length);
1949987da915Sopenharmony_ci	}
1950987da915Sopenharmony_ci	entry = (MFT_RECORD*)buffer;
1951987da915Sopenharmony_ci	attr = (ATTR_RECORD*)(buffer
1952987da915Sopenharmony_ci			+ le16_to_cpu(action->record.record_offset));
1953987da915Sopenharmony_ci	index = (INDEX_ROOT*)(((char*)attr)
1954987da915Sopenharmony_ci			+ le16_to_cpu(attr->value_offset));
1955987da915Sopenharmony_ci	if (optv > 1) {
1956987da915Sopenharmony_ci		printf("existing index :\n");
1957987da915Sopenharmony_ci		dump(buffer + target,length);
1958987da915Sopenharmony_ci	}
1959987da915Sopenharmony_ci	if ((attr->type == AT_INDEX_ROOT)
1960987da915Sopenharmony_ci	    && !(length & 7)
1961987da915Sopenharmony_ci	    && ((target + length) <= mftrecsz)) {
1962987da915Sopenharmony_ci		/* This has to be an idempotent action */
1963987da915Sopenharmony_ci		found = !memcmp(buffer + target, data, length);
1964987da915Sopenharmony_ci		err = 0;
1965987da915Sopenharmony_ci		if (!found) {
1966987da915Sopenharmony_ci			/* Make space to insert the entry */
1967987da915Sopenharmony_ci			memmove(buffer + target + length,
1968987da915Sopenharmony_ci				buffer + target,
1969987da915Sopenharmony_ci				mftrecsz - target - length);
1970987da915Sopenharmony_ci			memcpy(buffer + target, data, length);
1971987da915Sopenharmony_ci			resize_attribute(entry, attr, index, length, length);
1972987da915Sopenharmony_ci			if (optv > 1) {
1973987da915Sopenharmony_ci				printf("new index at same location :\n");
1974987da915Sopenharmony_ci				dump(buffer + target, length);
1975987da915Sopenharmony_ci			}
1976987da915Sopenharmony_ci			err = write_protected(vol, &action->record,
1977987da915Sopenharmony_ci					buffer, mftrecsz);
1978987da915Sopenharmony_ci		}
1979987da915Sopenharmony_ci		if (optv > 1) {
1980987da915Sopenharmony_ci			printf("-> MFT record %s\n",
1981987da915Sopenharmony_ci				(found ? "unchanged" : "expanded"));
1982987da915Sopenharmony_ci		}
1983987da915Sopenharmony_ci	}
1984987da915Sopenharmony_ci	return (err);
1985987da915Sopenharmony_ci}
1986987da915Sopenharmony_ci
1987987da915Sopenharmony_cistatic int redo_compensate(ntfs_volume *vol __attribute__((unused)),
1988987da915Sopenharmony_ci			const struct ACTION_RECORD *action,
1989987da915Sopenharmony_ci			char *buffer __attribute__((unused)))
1990987da915Sopenharmony_ci{
1991987da915Sopenharmony_ci	u64 lsn;
1992987da915Sopenharmony_ci	s64 diff;
1993987da915Sopenharmony_ci
1994987da915Sopenharmony_ci	if (optv > 1)
1995987da915Sopenharmony_ci		printf("-> %s()\n",__func__);
1996987da915Sopenharmony_ci	lsn = sle64_to_cpu(action->record.this_lsn);
1997987da915Sopenharmony_ci	diff = lsn - restart_lsn;
1998987da915Sopenharmony_ci	if (diff > 0)
1999987da915Sopenharmony_ci		restart_lsn = lsn;
2000987da915Sopenharmony_ci	return (0);
2001987da915Sopenharmony_ci}
2002987da915Sopenharmony_ci
2003987da915Sopenharmony_cistatic int redo_create_file(ntfs_volume *vol,
2004987da915Sopenharmony_ci			const struct ACTION_RECORD *action, char *buffer)
2005987da915Sopenharmony_ci{
2006987da915Sopenharmony_ci	LCN lcn;
2007987da915Sopenharmony_ci	const char *data;
2008987da915Sopenharmony_ci	MFT_RECORD *record;
2009987da915Sopenharmony_ci	u32 target;
2010987da915Sopenharmony_ci	u32 length;
2011987da915Sopenharmony_ci	int err;
2012987da915Sopenharmony_ci	int changed;
2013987da915Sopenharmony_ci
2014987da915Sopenharmony_ci	if (optv > 1)
2015987da915Sopenharmony_ci		printf("-> %s()\n",__func__);
2016987da915Sopenharmony_ci	err = 1;
2017987da915Sopenharmony_ci	data = ((const char*)&action->record)
2018987da915Sopenharmony_ci			+ get_redo_offset(&action->record);
2019987da915Sopenharmony_ci	length = le16_to_cpu(action->record.redo_length);
2020987da915Sopenharmony_ci	target = le16_to_cpu(action->record.record_offset)
2021987da915Sopenharmony_ci		+ le16_to_cpu(action->record.attribute_offset);
2022987da915Sopenharmony_ci	if (optv > 1) {
2023987da915Sopenharmony_ci		lcn = sle64_to_cpu(action->record.lcn_list[0]);
2024987da915Sopenharmony_ci		printf("-> inode %lld lcn 0x%llx target 0x%x length %d\n",
2025987da915Sopenharmony_ci			(long long)inode_number(&action->record),
2026987da915Sopenharmony_ci			(long long)lcn, (int)target, (int)length);
2027987da915Sopenharmony_ci	}
2028987da915Sopenharmony_ci	record = (MFT_RECORD*)buffer;
2029987da915Sopenharmony_ci	if (optv > 1) {
2030987da915Sopenharmony_ci		printf("-> existing record :\n");
2031987da915Sopenharmony_ci		dump(buffer,mftrecsz);
2032987da915Sopenharmony_ci	}
2033987da915Sopenharmony_ci	if ((target + length) <= mftrecsz) {
2034987da915Sopenharmony_ci		changed = memcmp(buffer + target, data, length);
2035987da915Sopenharmony_ci		err = 0;
2036987da915Sopenharmony_ci		if (changed || !(record->flags & MFT_RECORD_IN_USE)) {
2037987da915Sopenharmony_ci			memcpy(buffer + target, data, length);
2038987da915Sopenharmony_ci			record->flags |= MFT_RECORD_IN_USE;
2039987da915Sopenharmony_ci			if (optv > 1) {
2040987da915Sopenharmony_ci				printf("-> new record :\n");
2041987da915Sopenharmony_ci				dump(buffer,mftrecsz);
2042987da915Sopenharmony_ci			}
2043987da915Sopenharmony_ci			err = write_protected(vol, &action->record,
2044987da915Sopenharmony_ci						buffer, mftrecsz);
2045987da915Sopenharmony_ci		}
2046987da915Sopenharmony_ci		if (optv > 1) {
2047987da915Sopenharmony_ci			printf("-> MFT record %s\n",
2048987da915Sopenharmony_ci				(changed ? "updated" : "unchanged"));
2049987da915Sopenharmony_ci		}
2050987da915Sopenharmony_ci	} else {
2051987da915Sopenharmony_ci		err = 1; /* record overflows */
2052987da915Sopenharmony_ci	}
2053987da915Sopenharmony_ci	return (err);
2054987da915Sopenharmony_ci}
2055987da915Sopenharmony_ci
2056987da915Sopenharmony_cistatic int redo_create_attribute(ntfs_volume *vol,
2057987da915Sopenharmony_ci			const struct ACTION_RECORD *action, char *buffer)
2058987da915Sopenharmony_ci{
2059987da915Sopenharmony_ci	const char *data;
2060987da915Sopenharmony_ci	u32 target;
2061987da915Sopenharmony_ci	u32 length;
2062987da915Sopenharmony_ci	int err;
2063987da915Sopenharmony_ci
2064987da915Sopenharmony_ci	if (optv > 1)
2065987da915Sopenharmony_ci		printf("-> %s()\n",__func__);
2066987da915Sopenharmony_ci	err = 1;
2067987da915Sopenharmony_ci	data = ((const char*)&action->record)
2068987da915Sopenharmony_ci			+ get_redo_offset(&action->record);
2069987da915Sopenharmony_ci	length = le16_to_cpu(action->record.redo_length);
2070987da915Sopenharmony_ci	target = le16_to_cpu(action->record.record_offset)
2071987da915Sopenharmony_ci		+ le16_to_cpu(action->record.attribute_offset);
2072987da915Sopenharmony_ci// Could also be AT_DATA or AT_INDEX_ALLOCATION
2073987da915Sopenharmony_ci	if (!action->record.undo_length)
2074987da915Sopenharmony_ci		err = insert_resident(vol, action, buffer, data,
2075987da915Sopenharmony_ci				target, length);
2076987da915Sopenharmony_ci	return (err);
2077987da915Sopenharmony_ci}
2078987da915Sopenharmony_ci
2079987da915Sopenharmony_cistatic int redo_delete_attribute(ntfs_volume *vol,
2080987da915Sopenharmony_ci			const struct ACTION_RECORD *action, char *buffer)
2081987da915Sopenharmony_ci{
2082987da915Sopenharmony_ci	const char *data;
2083987da915Sopenharmony_ci	u32 target;
2084987da915Sopenharmony_ci	u32 length;
2085987da915Sopenharmony_ci	int err;
2086987da915Sopenharmony_ci
2087987da915Sopenharmony_ci	if (optv > 1)
2088987da915Sopenharmony_ci		printf("-> %s()\n",__func__);
2089987da915Sopenharmony_ci	err = 1;
2090987da915Sopenharmony_ci	data = ((const char*)&action->record)
2091987da915Sopenharmony_ci			+ get_undo_offset(&action->record);
2092987da915Sopenharmony_ci	length = le16_to_cpu(action->record.undo_length);
2093987da915Sopenharmony_ci	target = le16_to_cpu(action->record.record_offset)
2094987da915Sopenharmony_ci		+ le16_to_cpu(action->record.attribute_offset);
2095987da915Sopenharmony_ci	if (!action->record.redo_length)
2096987da915Sopenharmony_ci		err = remove_resident(vol, action, buffer, data,
2097987da915Sopenharmony_ci				target, length);
2098987da915Sopenharmony_ci	return (err);
2099987da915Sopenharmony_ci}
2100987da915Sopenharmony_ci
2101987da915Sopenharmony_cistatic int redo_delete_file(ntfs_volume *vol,
2102987da915Sopenharmony_ci			const struct ACTION_RECORD *action, char *buffer)
2103987da915Sopenharmony_ci{
2104987da915Sopenharmony_ci	LCN lcn;
2105987da915Sopenharmony_ci	const char *data;
2106987da915Sopenharmony_ci	MFT_RECORD *record;
2107987da915Sopenharmony_ci	u32 target;
2108987da915Sopenharmony_ci	u32 length;
2109987da915Sopenharmony_ci	int err;
2110987da915Sopenharmony_ci	int changed;
2111987da915Sopenharmony_ci
2112987da915Sopenharmony_ci	if (optv > 1)
2113987da915Sopenharmony_ci		printf("-> %s()\n",__func__);
2114987da915Sopenharmony_ci	err = 1;
2115987da915Sopenharmony_ci	data = ((const char*)&action->record)
2116987da915Sopenharmony_ci			+ get_undo_offset(&action->record);
2117987da915Sopenharmony_ci	length = le16_to_cpu(action->record.undo_length);
2118987da915Sopenharmony_ci	target = le16_to_cpu(action->record.record_offset)
2119987da915Sopenharmony_ci		+ le16_to_cpu(action->record.attribute_offset);
2120987da915Sopenharmony_ci	if (optv > 1) {
2121987da915Sopenharmony_ci		lcn = sle64_to_cpu(action->record.lcn_list[0]);
2122987da915Sopenharmony_ci		printf("-> inode %lld lcn 0x%llx target 0x%x length %d\n",
2123987da915Sopenharmony_ci			(long long)inode_number(&action->record),
2124987da915Sopenharmony_ci			(long long)lcn, (int)target, (int)length);
2125987da915Sopenharmony_ci	}
2126987da915Sopenharmony_ci	if (optv > 1) {
2127987da915Sopenharmony_ci		printf("-> existing record :\n");
2128987da915Sopenharmony_ci		dump(buffer,mftrecsz);
2129987da915Sopenharmony_ci	}
2130987da915Sopenharmony_ci	record = (MFT_RECORD*)buffer;
2131987da915Sopenharmony_ci	if ((target + length) <= mftrecsz) {
2132987da915Sopenharmony_ci		/* write a void mft entry (needed ?) */
2133987da915Sopenharmony_ci		changed = (length && memcmp(buffer + target, data, length))
2134987da915Sopenharmony_ci			|| (record->flags & MFT_RECORD_IN_USE);
2135987da915Sopenharmony_ci		err = 0;
2136987da915Sopenharmony_ci		if (changed) {
2137987da915Sopenharmony_ci			memcpy(buffer + target, data, length);
2138987da915Sopenharmony_ci			record->flags &= ~MFT_RECORD_IN_USE;
2139987da915Sopenharmony_ci			if (optv > 1) {
2140987da915Sopenharmony_ci				printf("-> new record :\n");
2141987da915Sopenharmony_ci				dump(buffer,mftrecsz);
2142987da915Sopenharmony_ci			}
2143987da915Sopenharmony_ci			err = write_protected(vol, &action->record,
2144987da915Sopenharmony_ci						buffer, mftrecsz);
2145987da915Sopenharmony_ci		}
2146987da915Sopenharmony_ci		if (optv > 1) {
2147987da915Sopenharmony_ci			printf("-> MFT record %s\n",
2148987da915Sopenharmony_ci				(changed ? "updated" : "unchanged"));
2149987da915Sopenharmony_ci		}
2150987da915Sopenharmony_ci	}
2151987da915Sopenharmony_ci	return (err);
2152987da915Sopenharmony_ci}
2153987da915Sopenharmony_ci
2154987da915Sopenharmony_cistatic int redo_delete_index(ntfs_volume *vol,
2155987da915Sopenharmony_ci			const struct ACTION_RECORD *action, char *buffer)
2156987da915Sopenharmony_ci{
2157987da915Sopenharmony_ci	LCN lcn;
2158987da915Sopenharmony_ci	const char *data;
2159987da915Sopenharmony_ci	INDEX_BLOCK *indx;
2160987da915Sopenharmony_ci	u32 target;
2161987da915Sopenharmony_ci	u32 length;
2162987da915Sopenharmony_ci	u32 xsize;
2163987da915Sopenharmony_ci	u32 indexlth;
2164987da915Sopenharmony_ci	int err;
2165987da915Sopenharmony_ci	BOOL found;
2166987da915Sopenharmony_ci
2167987da915Sopenharmony_ci	if (optv > 1)
2168987da915Sopenharmony_ci		printf("-> %s()\n",__func__);
2169987da915Sopenharmony_ci	err = 1;
2170987da915Sopenharmony_ci	data = ((const char*)&action->record)
2171987da915Sopenharmony_ci			+ get_undo_offset(&action->record);
2172987da915Sopenharmony_ci	length = le16_to_cpu(action->record.undo_length);
2173987da915Sopenharmony_ci	target = le16_to_cpu(action->record.record_offset)
2174987da915Sopenharmony_ci		+ le16_to_cpu(action->record.attribute_offset);
2175987da915Sopenharmony_ci	if (optv > 1) {
2176987da915Sopenharmony_ci		lcn = sle64_to_cpu(action->record.lcn_list[0]);
2177987da915Sopenharmony_ci		printf("-> lcn 0x%llx target 0x%x length %d\n",
2178987da915Sopenharmony_ci			(long long)lcn, (int)target, (int)length);
2179987da915Sopenharmony_ci	}
2180987da915Sopenharmony_ci	xsize = vol->indx_record_size;
2181987da915Sopenharmony_ci	indx = (INDEX_BLOCK*)(buffer
2182987da915Sopenharmony_ci			+ le16_to_cpu(action->record.record_offset));
2183987da915Sopenharmony_ci	if (optv > 1) {
2184987da915Sopenharmony_ci		printf("-> existing record :\n");
2185987da915Sopenharmony_ci		dump(&buffer[target], length);
2186987da915Sopenharmony_ci	}
2187987da915Sopenharmony_ci	if ((indx->magic == magic_INDX)
2188987da915Sopenharmony_ci	    && !(length & 7)
2189987da915Sopenharmony_ci	    && ((target + length) <= xsize)) {
2190987da915Sopenharmony_ci		/* This has to be an idempotent action */
2191987da915Sopenharmony_ci		found = (action->record.undo_operation
2192987da915Sopenharmony_ci				== const_cpu_to_le16(CompensationlogRecord))
2193987da915Sopenharmony_ci		    || !memcmp(buffer + target, data, length);
2194987da915Sopenharmony_ci		err = 0;
2195987da915Sopenharmony_ci		if (found) {
2196987da915Sopenharmony_ci			/* Remove the entry */
2197987da915Sopenharmony_ci			memmove(buffer + target,
2198987da915Sopenharmony_ci				buffer + target + length,
2199987da915Sopenharmony_ci				xsize - target - length);
2200987da915Sopenharmony_ci			indexlth = le32_to_cpu(indx->index.index_length)
2201987da915Sopenharmony_ci						- length;
2202987da915Sopenharmony_ci			indx->index.index_length = cpu_to_le32(indexlth);
2203987da915Sopenharmony_ci			err = write_protected(vol, &action->record,
2204987da915Sopenharmony_ci						buffer, xsize);
2205987da915Sopenharmony_ci		}
2206987da915Sopenharmony_ci		if (optv > 1) {
2207987da915Sopenharmony_ci			printf("-> INDX record %s\n",
2208987da915Sopenharmony_ci				(found ? "removed" : "unchanged"));
2209987da915Sopenharmony_ci		}
2210987da915Sopenharmony_ci	}
2211987da915Sopenharmony_ci	return (err);
2212987da915Sopenharmony_ci}
2213987da915Sopenharmony_ci
2214987da915Sopenharmony_cistatic int redo_delete_root_index(ntfs_volume *vol,
2215987da915Sopenharmony_ci			const struct ACTION_RECORD *action, char *buffer)
2216987da915Sopenharmony_ci{
2217987da915Sopenharmony_ci	LCN lcn;
2218987da915Sopenharmony_ci	const char *data;
2219987da915Sopenharmony_ci	ATTR_RECORD *attr;
2220987da915Sopenharmony_ci	MFT_RECORD *entry;
2221987da915Sopenharmony_ci	INDEX_ROOT *index;
2222987da915Sopenharmony_ci	BOOL found;
2223987da915Sopenharmony_ci	u32 target;
2224987da915Sopenharmony_ci	u32 length;
2225987da915Sopenharmony_ci	int err;
2226987da915Sopenharmony_ci
2227987da915Sopenharmony_ci	if (optv > 1)
2228987da915Sopenharmony_ci		printf("-> %s()\n",__func__);
2229987da915Sopenharmony_ci	err = 1;
2230987da915Sopenharmony_ci	data = ((const char*)&action->record)
2231987da915Sopenharmony_ci			+ get_undo_offset(&action->record);
2232987da915Sopenharmony_ci	length = le16_to_cpu(action->record.undo_length);
2233987da915Sopenharmony_ci	target = le16_to_cpu(action->record.record_offset)
2234987da915Sopenharmony_ci		+ le16_to_cpu(action->record.attribute_offset);
2235987da915Sopenharmony_ci
2236987da915Sopenharmony_ci	if (optv > 1) {
2237987da915Sopenharmony_ci		lcn = sle64_to_cpu(action->record.lcn_list[0]);
2238987da915Sopenharmony_ci		printf("-> inode %lld lcn 0x%llx target 0x%x length %d\n",
2239987da915Sopenharmony_ci			(long long)inode_number(&action->record),
2240987da915Sopenharmony_ci			(long long)lcn, (int)target, (int)length);
2241987da915Sopenharmony_ci	}
2242987da915Sopenharmony_ci	entry = (MFT_RECORD*)buffer;
2243987da915Sopenharmony_ci	attr = (ATTR_RECORD*)(buffer
2244987da915Sopenharmony_ci			+ le16_to_cpu(action->record.record_offset));
2245987da915Sopenharmony_ci	index = (INDEX_ROOT*)(((char*)attr)
2246987da915Sopenharmony_ci			+ le16_to_cpu(attr->value_offset));
2247987da915Sopenharmony_ci	if (optv > 1) {
2248987da915Sopenharmony_ci		printf("existing index :\n");
2249987da915Sopenharmony_ci		dump(buffer + target,length);
2250987da915Sopenharmony_ci	}
2251987da915Sopenharmony_ci	if ((attr->type == AT_INDEX_ROOT)
2252987da915Sopenharmony_ci	    && !(length & 7)
2253987da915Sopenharmony_ci	    && ((target + length) <= mftrecsz)) {
2254987da915Sopenharmony_ci		/* This has to be an idempotent action */
2255987da915Sopenharmony_ci		found = (action->record.undo_operation
2256987da915Sopenharmony_ci				== const_cpu_to_le16(CompensationlogRecord))
2257987da915Sopenharmony_ci			|| !memcmp(buffer + target, data, length);
2258987da915Sopenharmony_ci		err = 0;
2259987da915Sopenharmony_ci		/* Only delete if present */
2260987da915Sopenharmony_ci		if (found) {
2261987da915Sopenharmony_ci			/* Remove the entry */
2262987da915Sopenharmony_ci			memmove(buffer + target,
2263987da915Sopenharmony_ci				buffer + target + length,
2264987da915Sopenharmony_ci				mftrecsz - target - length);
2265987da915Sopenharmony_ci			resize_attribute(entry, attr, index, -length, -length);
2266987da915Sopenharmony_ci			if (optv > 1) {
2267987da915Sopenharmony_ci				printf("new index at same location :\n");
2268987da915Sopenharmony_ci				dump(buffer + target, length);
2269987da915Sopenharmony_ci			}
2270987da915Sopenharmony_ci			err = write_protected(vol, &action->record,
2271987da915Sopenharmony_ci					buffer, mftrecsz);
2272987da915Sopenharmony_ci		}
2273987da915Sopenharmony_ci		if (optv > 1) {
2274987da915Sopenharmony_ci			printf("-> MFT record %s\n",
2275987da915Sopenharmony_ci				(found ? "shrinked" : "updated"));
2276987da915Sopenharmony_ci		}
2277987da915Sopenharmony_ci	}
2278987da915Sopenharmony_ci	return (err);
2279987da915Sopenharmony_ci}
2280987da915Sopenharmony_ci
2281987da915Sopenharmony_cistatic int redo_force_bits(ntfs_volume *vol,
2282987da915Sopenharmony_ci			const struct ACTION_RECORD *action, char *buffer)
2283987da915Sopenharmony_ci{
2284987da915Sopenharmony_ci	LCN lcn;
2285987da915Sopenharmony_ci	const struct BITMAP_ACTION *data;
2286987da915Sopenharmony_ci	u32 i;
2287987da915Sopenharmony_ci	int err;
2288987da915Sopenharmony_ci	int wanted;
2289987da915Sopenharmony_ci	u32 firstbit;
2290987da915Sopenharmony_ci	u32 count;
2291987da915Sopenharmony_ci
2292987da915Sopenharmony_ci	if (optv > 1)
2293987da915Sopenharmony_ci		printf("-> %s()\n",__func__);
2294987da915Sopenharmony_ci	err = 1;
2295987da915Sopenharmony_ci	data = (const struct BITMAP_ACTION*)
2296987da915Sopenharmony_ci			(((const char*)&action->record)
2297987da915Sopenharmony_ci				+ get_redo_offset(&action->record));
2298987da915Sopenharmony_ci	firstbit = le32_to_cpu(data->firstbit);
2299987da915Sopenharmony_ci	count = le32_to_cpu(data->count);
2300987da915Sopenharmony_ci	if (action->record.redo_operation
2301987da915Sopenharmony_ci			== const_cpu_to_le16(SetBitsInNonResidentBitMap))
2302987da915Sopenharmony_ci		wanted = 1;
2303987da915Sopenharmony_ci	else
2304987da915Sopenharmony_ci		wanted = 0;
2305987da915Sopenharmony_ci// TODO consistency undo_offset == redo_offset, etc.
2306987da915Sopenharmony_ci// firstbit + count < 8*clustersz (multiple clusters possible ?)
2307987da915Sopenharmony_ci	if (optv > 1) {
2308987da915Sopenharmony_ci		lcn = sle64_to_cpu(action->record.lcn_list[0]);
2309987da915Sopenharmony_ci		printf("-> lcn 0x%llx firstbit %d count %d wanted %d\n",
2310987da915Sopenharmony_ci			(long long)lcn,(int)firstbit,(int)count,(int)wanted);
2311987da915Sopenharmony_ci	}
2312987da915Sopenharmony_ci	for (i=0; i<count; i++)
2313987da915Sopenharmony_ci		ntfs_bit_set((u8*)buffer, firstbit + i, wanted);
2314987da915Sopenharmony_ci	if (!write_raw(vol, &action->record, buffer)) {
2315987da915Sopenharmony_ci		err = 0;
2316987da915Sopenharmony_ci		if (optv > 1)
2317987da915Sopenharmony_ci			printf("-> record updated\n");
2318987da915Sopenharmony_ci	}
2319987da915Sopenharmony_ci	if (err)
2320987da915Sopenharmony_ci		printf("** redo_clearbits failed\n");
2321987da915Sopenharmony_ci	return (err);
2322987da915Sopenharmony_ci}
2323987da915Sopenharmony_ci
2324987da915Sopenharmony_cistatic int redo_open_attribute(ntfs_volume *vol __attribute__((unused)),
2325987da915Sopenharmony_ci				const struct ACTION_RECORD *action)
2326987da915Sopenharmony_ci{
2327987da915Sopenharmony_ci	const char *data;
2328987da915Sopenharmony_ci	struct ATTR *pa;
2329987da915Sopenharmony_ci	const ATTR_OLD *attr_old;
2330987da915Sopenharmony_ci	const ATTR_NEW *attr_new;
2331987da915Sopenharmony_ci	const char *name;
2332987da915Sopenharmony_ci	le64 inode;
2333987da915Sopenharmony_ci	u32 namelen;
2334987da915Sopenharmony_ci	u32 length;
2335987da915Sopenharmony_ci	u32 extra;
2336987da915Sopenharmony_ci	int err;
2337987da915Sopenharmony_ci
2338987da915Sopenharmony_ci	if (optv > 1)
2339987da915Sopenharmony_ci		printf("-> %s()\n",__func__);
2340987da915Sopenharmony_ci	err = 1;
2341987da915Sopenharmony_ci	data = ((const char*)&action->record)
2342987da915Sopenharmony_ci			+ get_redo_offset(&action->record);
2343987da915Sopenharmony_ci	length = le16_to_cpu(action->record.redo_length);
2344987da915Sopenharmony_ci	extra = get_extra_offset(&action->record);
2345987da915Sopenharmony_ci	if (action->record.undo_length) {
2346987da915Sopenharmony_ci		name = ((const char*)&action->record) + extra;
2347987da915Sopenharmony_ci		namelen = le32_to_cpu(action->record.client_data_length)
2348987da915Sopenharmony_ci				+ LOG_RECORD_HEAD_SZ - extra;
2349987da915Sopenharmony_ci		/* fix namelen which was aligned modulo 8 */
2350987da915Sopenharmony_ci		namelen = fixnamelen(name, namelen);
2351987da915Sopenharmony_ci		if (optv > 1) {
2352987da915Sopenharmony_ci			printf("-> length %d namelen %d",(int)length,
2353987da915Sopenharmony_ci							(int)namelen);
2354987da915Sopenharmony_ci			showname(", ", name, namelen/2);
2355987da915Sopenharmony_ci		}
2356987da915Sopenharmony_ci	} else {
2357987da915Sopenharmony_ci		name = "";
2358987da915Sopenharmony_ci		namelen = 0;
2359987da915Sopenharmony_ci	}
2360987da915Sopenharmony_ci	pa = getattrentry(le16_to_cpu(action->record.target_attribute),0);
2361987da915Sopenharmony_ci	if (pa) {
2362987da915Sopenharmony_ci		if (optv) {
2363987da915Sopenharmony_ci			/*
2364987da915Sopenharmony_ci			 * If the actions have been displayed, the
2365987da915Sopenharmony_ci			 * attribute has already been fed. Check
2366987da915Sopenharmony_ci			 * whether it matches what we have in store.
2367987da915Sopenharmony_ci			 */
2368987da915Sopenharmony_ci			switch (length) {
2369987da915Sopenharmony_ci			case sizeof(ATTR_OLD) :
2370987da915Sopenharmony_ci				attr_old = (const ATTR_OLD*)data;
2371987da915Sopenharmony_ci					/* Badly aligned */
2372987da915Sopenharmony_ci				memcpy(&inode, &attr_old->inode, 8);
2373987da915Sopenharmony_ci				err = (MREF(le64_to_cpu(inode)) != pa->inode)
2374987da915Sopenharmony_ci				    || (attr_old->type != pa->type);
2375987da915Sopenharmony_ci				break;
2376987da915Sopenharmony_ci			case sizeof(ATTR_NEW) :
2377987da915Sopenharmony_ci				attr_new = (const ATTR_NEW*)data;
2378987da915Sopenharmony_ci				err = (MREF(le64_to_cpu(attr_new->inode))
2379987da915Sopenharmony_ci							!= pa->inode)
2380987da915Sopenharmony_ci				    || (attr_new->type != pa->type);
2381987da915Sopenharmony_ci				break;
2382987da915Sopenharmony_ci			default : err = 1;
2383987da915Sopenharmony_ci			}
2384987da915Sopenharmony_ci			if (!err) {
2385987da915Sopenharmony_ci				err = (namelen != pa->namelen)
2386987da915Sopenharmony_ci					|| (namelen
2387987da915Sopenharmony_ci				    	&& memcmp(name, pa->name, namelen));
2388987da915Sopenharmony_ci			}
2389987da915Sopenharmony_ci			if (optv > 1)
2390987da915Sopenharmony_ci				printf("-> attribute %s the recorded one\n",
2391987da915Sopenharmony_ci					(err ? "does not match" : "matches"));
2392987da915Sopenharmony_ci		} else {
2393987da915Sopenharmony_ci			copy_attribute(pa, data, length);
2394987da915Sopenharmony_ci			pa->namelen = namelen;
2395987da915Sopenharmony_ci			if (namelen)
2396987da915Sopenharmony_ci				memcpy(pa->name, data, namelen);
2397987da915Sopenharmony_ci			err = 0;
2398987da915Sopenharmony_ci		}
2399987da915Sopenharmony_ci	} else
2400987da915Sopenharmony_ci		if (optv)
2401987da915Sopenharmony_ci			printf("* Unrecorded attribute\n");
2402987da915Sopenharmony_ci	return (err);
2403987da915Sopenharmony_ci}
2404987da915Sopenharmony_ci
2405987da915Sopenharmony_cistatic int redo_sizes(ntfs_volume *vol, const struct ACTION_RECORD *action,
2406987da915Sopenharmony_ci			char *buffer)
2407987da915Sopenharmony_ci{
2408987da915Sopenharmony_ci	const char *data;
2409987da915Sopenharmony_ci	u32 target;
2410987da915Sopenharmony_ci	u32 length;
2411987da915Sopenharmony_ci	int err;
2412987da915Sopenharmony_ci
2413987da915Sopenharmony_ci	if (optv > 1)
2414987da915Sopenharmony_ci		printf("-> %s()\n",__func__);
2415987da915Sopenharmony_ci	err = 1;
2416987da915Sopenharmony_ci	data = ((const char*)&action->record)
2417987da915Sopenharmony_ci			+ get_redo_offset(&action->record);
2418987da915Sopenharmony_ci	length = le16_to_cpu(action->record.redo_length);
2419987da915Sopenharmony_ci	target = le16_to_cpu(action->record.record_offset)
2420987da915Sopenharmony_ci		+ le16_to_cpu(action->record.attribute_offset)
2421987da915Sopenharmony_ci		+ offsetof(ATTR_RECORD, allocated_size);
2422987da915Sopenharmony_ci	err = change_resident(vol, action, buffer,
2423987da915Sopenharmony_ci			data, target, length);
2424987da915Sopenharmony_ci	return (err);
2425987da915Sopenharmony_ci}
2426987da915Sopenharmony_ci
2427987da915Sopenharmony_cistatic int redo_update_index(ntfs_volume *vol,
2428987da915Sopenharmony_ci			const struct ACTION_RECORD *action, char *buffer)
2429987da915Sopenharmony_ci{
2430987da915Sopenharmony_ci	const char *data;
2431987da915Sopenharmony_ci	u32 target;
2432987da915Sopenharmony_ci	u32 length;
2433987da915Sopenharmony_ci	int err;
2434987da915Sopenharmony_ci
2435987da915Sopenharmony_ci	if (optv > 1)
2436987da915Sopenharmony_ci		printf("-> %s()\n",__func__);
2437987da915Sopenharmony_ci	err = 1;
2438987da915Sopenharmony_ci	data = ((const char*)&action->record)
2439987da915Sopenharmony_ci			+ get_redo_offset(&action->record);
2440987da915Sopenharmony_ci	length = le16_to_cpu(action->record.redo_length);
2441987da915Sopenharmony_ci			/* target is left-justified to creation time */
2442987da915Sopenharmony_ci	target = le16_to_cpu(action->record.record_offset)
2443987da915Sopenharmony_ci		+ le16_to_cpu(action->record.attribute_offset)
2444987da915Sopenharmony_ci		+ offsetof(INDEX_ENTRY, key.file_name.creation_time);
2445987da915Sopenharmony_ci	err = update_index(vol, action, buffer, data, target, length);
2446987da915Sopenharmony_ci	return (err);
2447987da915Sopenharmony_ci}
2448987da915Sopenharmony_ci
2449987da915Sopenharmony_cistatic int redo_update_index_value(ntfs_volume *vol,
2450987da915Sopenharmony_ci			const struct ACTION_RECORD *action, char *buffer)
2451987da915Sopenharmony_ci{
2452987da915Sopenharmony_ci	const char *data;
2453987da915Sopenharmony_ci	u32 length;
2454987da915Sopenharmony_ci	u32 target;
2455987da915Sopenharmony_ci	int err;
2456987da915Sopenharmony_ci
2457987da915Sopenharmony_ci	if (optv > 1)
2458987da915Sopenharmony_ci		printf("-> %s()\n",__func__);
2459987da915Sopenharmony_ci	err = 1;
2460987da915Sopenharmony_ci	data = ((const char*)&action->record)
2461987da915Sopenharmony_ci				+ get_redo_offset(&action->record);
2462987da915Sopenharmony_ci	length = le16_to_cpu(action->record.redo_length);
2463987da915Sopenharmony_ci	target = le16_to_cpu(action->record.record_offset)
2464987da915Sopenharmony_ci		+ le16_to_cpu(action->record.attribute_offset);
2465987da915Sopenharmony_ci	err = change_index_value(vol, action, buffer, data, target, length);
2466987da915Sopenharmony_ci	return (err);
2467987da915Sopenharmony_ci}
2468987da915Sopenharmony_ci
2469987da915Sopenharmony_cistatic int redo_update_mapping(ntfs_volume *vol,
2470987da915Sopenharmony_ci			const struct ACTION_RECORD *action, char *buffer)
2471987da915Sopenharmony_ci{
2472987da915Sopenharmony_ci	LCN lcn;
2473987da915Sopenharmony_ci	const char *data;
2474987da915Sopenharmony_ci	ATTR_RECORD *attr;
2475987da915Sopenharmony_ci	MFT_RECORD *entry;
2476987da915Sopenharmony_ci	u32 target;
2477987da915Sopenharmony_ci	u32 length;
2478987da915Sopenharmony_ci	u32 source;
2479987da915Sopenharmony_ci	u32 alen;
2480987da915Sopenharmony_ci	u32 newused;
2481987da915Sopenharmony_ci	int resize;
2482987da915Sopenharmony_ci	int err;
2483987da915Sopenharmony_ci	int changed;
2484987da915Sopenharmony_ci
2485987da915Sopenharmony_ci	if (optv > 1)
2486987da915Sopenharmony_ci		printf("-> %s()\n",__func__);
2487987da915Sopenharmony_ci	err = 1;
2488987da915Sopenharmony_ci	data = ((const char*)&action->record)
2489987da915Sopenharmony_ci			+ get_redo_offset(&action->record);
2490987da915Sopenharmony_ci	length = le16_to_cpu(action->record.redo_length);
2491987da915Sopenharmony_ci	resize = length - le16_to_cpu(action->record.undo_length);
2492987da915Sopenharmony_ci	target = le16_to_cpu(action->record.record_offset)
2493987da915Sopenharmony_ci		+ le16_to_cpu(action->record.attribute_offset);
2494987da915Sopenharmony_ci	if (optv > 1) {
2495987da915Sopenharmony_ci		lcn = sle64_to_cpu(action->record.lcn_list[0]);
2496987da915Sopenharmony_ci		printf("-> inode %lld lcn 0x%llx target 0x%x length %d\n",
2497987da915Sopenharmony_ci			(long long)inode_number(&action->record),
2498987da915Sopenharmony_ci			(long long)lcn, (int)target, (int)length);
2499987da915Sopenharmony_ci	}
2500987da915Sopenharmony_ci	if (optv > 1) {
2501987da915Sopenharmony_ci		printf("-> existing record :\n");
2502987da915Sopenharmony_ci		dump(&buffer[target], length);
2503987da915Sopenharmony_ci	}
2504987da915Sopenharmony_ci	entry = (MFT_RECORD*)buffer;
2505987da915Sopenharmony_ci	attr = (ATTR_RECORD*)(buffer
2506987da915Sopenharmony_ci			+ le16_to_cpu(action->record.record_offset));
2507987da915Sopenharmony_ci	if (!attr->non_resident) {
2508987da915Sopenharmony_ci		printf("** Error : update_mapping on resident attr\n");
2509987da915Sopenharmony_ci	}
2510987da915Sopenharmony_ci	if (valid_type(attr->type)
2511987da915Sopenharmony_ci	    && attr->non_resident
2512987da915Sopenharmony_ci	    && !(resize & 7)
2513987da915Sopenharmony_ci	    && ((target + length) <= mftrecsz)) {
2514987da915Sopenharmony_ci		changed = memcmp(buffer + target, data, length);
2515987da915Sopenharmony_ci		err = 0;
2516987da915Sopenharmony_ci		if (changed) {
2517987da915Sopenharmony_ci			/* Adjust space for new mapping pairs */
2518987da915Sopenharmony_ci			source = target - resize;
2519987da915Sopenharmony_ci			if (resize > 0) {
2520987da915Sopenharmony_ci				memmove(buffer + target + length,
2521987da915Sopenharmony_ci					buffer + source + length,
2522987da915Sopenharmony_ci					mftrecsz - target - length);
2523987da915Sopenharmony_ci			}
2524987da915Sopenharmony_ci			if (resize < 0) {
2525987da915Sopenharmony_ci				memmove(buffer + target + length,
2526987da915Sopenharmony_ci					buffer + source + length,
2527987da915Sopenharmony_ci					mftrecsz - source - length);
2528987da915Sopenharmony_ci			}
2529987da915Sopenharmony_ci			memcpy(buffer + target, data, length);
2530987da915Sopenharmony_ci				/* Resize the attribute */
2531987da915Sopenharmony_ci			alen = le32_to_cpu(attr->length) + resize;
2532987da915Sopenharmony_ci			attr->length = cpu_to_le32(alen);
2533987da915Sopenharmony_ci				/* Resize the mft record */
2534987da915Sopenharmony_ci			newused = le32_to_cpu(entry->bytes_in_use)
2535987da915Sopenharmony_ci					+ resize;
2536987da915Sopenharmony_ci			entry->bytes_in_use = cpu_to_le32(newused);
2537987da915Sopenharmony_ci				/* Compute the new highest_vcn */
2538987da915Sopenharmony_ci			err = adjust_high_vcn(vol, attr);
2539987da915Sopenharmony_ci			if (optv > 1) {
2540987da915Sopenharmony_ci				printf("-> new record :\n");
2541987da915Sopenharmony_ci				dump(buffer + target, length);
2542987da915Sopenharmony_ci			}
2543987da915Sopenharmony_ci			if (!err) {
2544987da915Sopenharmony_ci				err = write_protected(vol,
2545987da915Sopenharmony_ci					&action->record,
2546987da915Sopenharmony_ci					buffer, mftrecsz);
2547987da915Sopenharmony_ci			}
2548987da915Sopenharmony_ci		}
2549987da915Sopenharmony_ci		if (optv > 1) {
2550987da915Sopenharmony_ci			printf("-> MFT record %s\n",
2551987da915Sopenharmony_ci				(changed ? "updated" : "unchanged"));
2552987da915Sopenharmony_ci		}
2553987da915Sopenharmony_ci	}
2554987da915Sopenharmony_ci	return (err);
2555987da915Sopenharmony_ci}
2556987da915Sopenharmony_ci
2557987da915Sopenharmony_cistatic int redo_update_resident(ntfs_volume *vol,
2558987da915Sopenharmony_ci			const struct ACTION_RECORD *action, char *buffer)
2559987da915Sopenharmony_ci{
2560987da915Sopenharmony_ci	LCN lcn;
2561987da915Sopenharmony_ci	const char *data;
2562987da915Sopenharmony_ci	u32 target;
2563987da915Sopenharmony_ci	u32 length;
2564987da915Sopenharmony_ci	u32 oldlength;
2565987da915Sopenharmony_ci	u32 end;
2566987da915Sopenharmony_ci	u32 redo;
2567987da915Sopenharmony_ci	int err;
2568987da915Sopenharmony_ci	int changed;
2569987da915Sopenharmony_ci
2570987da915Sopenharmony_ci	if (optv > 1)
2571987da915Sopenharmony_ci		printf("-> %s()\n",__func__);
2572987da915Sopenharmony_ci	err = 1;
2573987da915Sopenharmony_ci	end = le32_to_cpu(action->record.client_data_length)
2574987da915Sopenharmony_ci			+ LOG_RECORD_HEAD_SZ;
2575987da915Sopenharmony_ci	length = le16_to_cpu(action->record.redo_length);
2576987da915Sopenharmony_ci	redo = get_redo_offset(&action->record);
2577987da915Sopenharmony_ci	if ((redo + length) > end)
2578987da915Sopenharmony_ci		data = (char*)NULL;
2579987da915Sopenharmony_ci	else
2580987da915Sopenharmony_ci		data = ((const char*)&action->record) + redo;
2581987da915Sopenharmony_ci	oldlength = le16_to_cpu(action->record.undo_length);
2582987da915Sopenharmony_ci	target = le16_to_cpu(action->record.record_offset)
2583987da915Sopenharmony_ci		+ le16_to_cpu(action->record.attribute_offset);
2584987da915Sopenharmony_ci	if (length == oldlength) {
2585987da915Sopenharmony_ci		if (optv > 1) {
2586987da915Sopenharmony_ci			lcn = sle64_to_cpu(action->record.lcn_list[0]);
2587987da915Sopenharmony_ci			printf("-> inode %lld lcn 0x%llx target 0x%x"
2588987da915Sopenharmony_ci				" length %d\n",
2589987da915Sopenharmony_ci				(long long)inode_number(&action->record),
2590987da915Sopenharmony_ci				(long long)lcn, (int)target, (int)length);
2591987da915Sopenharmony_ci		}
2592987da915Sopenharmony_ci		if (optv > 1) {
2593987da915Sopenharmony_ci			printf("-> existing record :\n");
2594987da915Sopenharmony_ci			dump(&buffer[target], length);
2595987da915Sopenharmony_ci		}
2596987da915Sopenharmony_ci		if ((target + length) <= mftrecsz) {
2597987da915Sopenharmony_ci			changed = (action->record.undo_operation
2598987da915Sopenharmony_ci				    == const_cpu_to_le16(CompensationlogRecord))
2599987da915Sopenharmony_ci				|| memcmp(buffer + target, data, length);
2600987da915Sopenharmony_ci			err = 0;
2601987da915Sopenharmony_ci			if (changed) {
2602987da915Sopenharmony_ci				memcpy(buffer + target, data, length);
2603987da915Sopenharmony_ci				if (optv > 1) {
2604987da915Sopenharmony_ci					printf("-> new record :\n");
2605987da915Sopenharmony_ci					dump(buffer + target, length);
2606987da915Sopenharmony_ci				}
2607987da915Sopenharmony_ci				err = write_protected(vol, &action->record,
2608987da915Sopenharmony_ci					buffer, mftrecsz);
2609987da915Sopenharmony_ci			}
2610987da915Sopenharmony_ci			if (optv > 1) {
2611987da915Sopenharmony_ci				printf("-> MFT record %s\n",
2612987da915Sopenharmony_ci					(changed ? "updated" : "unchanged"));
2613987da915Sopenharmony_ci			}
2614987da915Sopenharmony_ci		}
2615987da915Sopenharmony_ci	} else {
2616987da915Sopenharmony_ci		if (length > oldlength)
2617987da915Sopenharmony_ci			err = expand_resident(vol, action, buffer, data,
2618987da915Sopenharmony_ci					target, length, oldlength);
2619987da915Sopenharmony_ci		else
2620987da915Sopenharmony_ci			err = shrink_resident(vol, action, buffer, data,
2621987da915Sopenharmony_ci					target, length, oldlength);
2622987da915Sopenharmony_ci	}
2623987da915Sopenharmony_ci	return (err);
2624987da915Sopenharmony_ci}
2625987da915Sopenharmony_ci
2626987da915Sopenharmony_cistatic int redo_update_root_index(ntfs_volume *vol,
2627987da915Sopenharmony_ci			const struct ACTION_RECORD *action, char *buffer)
2628987da915Sopenharmony_ci{
2629987da915Sopenharmony_ci	const char *data;
2630987da915Sopenharmony_ci	const char *expected;
2631987da915Sopenharmony_ci	u32 target;
2632987da915Sopenharmony_ci	u32 length;
2633987da915Sopenharmony_ci	int err;
2634987da915Sopenharmony_ci
2635987da915Sopenharmony_ci	if (optv > 1)
2636987da915Sopenharmony_ci		printf("-> %s()\n",__func__);
2637987da915Sopenharmony_ci	err = 1;
2638987da915Sopenharmony_ci	data = ((const char*)&action->record)
2639987da915Sopenharmony_ci			+ get_redo_offset(&action->record);
2640987da915Sopenharmony_ci	expected = ((const char*)&action->record)
2641987da915Sopenharmony_ci			+ get_undo_offset(&action->record);
2642987da915Sopenharmony_ci	length = le16_to_cpu(action->record.redo_length);
2643987da915Sopenharmony_ci		/* the fixup is right-justified to the name length */
2644987da915Sopenharmony_ci	target = le16_to_cpu(action->record.record_offset)
2645987da915Sopenharmony_ci		+ le16_to_cpu(action->record.attribute_offset)
2646987da915Sopenharmony_ci		+ offsetof(INDEX_ENTRY, key.file_name.file_name_length)
2647987da915Sopenharmony_ci		- length;
2648987da915Sopenharmony_ci	if (action->record.undo_operation
2649987da915Sopenharmony_ci			== const_cpu_to_le16(CompensationlogRecord))
2650987da915Sopenharmony_ci		err = change_resident(vol, action, buffer, data,
2651987da915Sopenharmony_ci			target, length);
2652987da915Sopenharmony_ci	else
2653987da915Sopenharmony_ci		err = change_resident_expect(vol, action, buffer, data,
2654987da915Sopenharmony_ci			expected, target, length, AT_INDEX_ROOT);
2655987da915Sopenharmony_ci	return (err);
2656987da915Sopenharmony_ci}
2657987da915Sopenharmony_ci
2658987da915Sopenharmony_cistatic int redo_update_root_vcn(ntfs_volume *vol,
2659987da915Sopenharmony_ci			const struct ACTION_RECORD *action, char *buffer)
2660987da915Sopenharmony_ci{
2661987da915Sopenharmony_ci	const char *data;
2662987da915Sopenharmony_ci	const char *expected;
2663987da915Sopenharmony_ci	u32 target;
2664987da915Sopenharmony_ci	u32 length;
2665987da915Sopenharmony_ci	int err;
2666987da915Sopenharmony_ci
2667987da915Sopenharmony_ci	if (optv > 1)
2668987da915Sopenharmony_ci		printf("-> %s()\n",__func__);
2669987da915Sopenharmony_ci	err = 1;
2670987da915Sopenharmony_ci	data = ((const char*)&action->record)
2671987da915Sopenharmony_ci			+ get_redo_offset(&action->record);
2672987da915Sopenharmony_ci	expected = ((const char*)&action->record)
2673987da915Sopenharmony_ci			+ get_undo_offset(&action->record);
2674987da915Sopenharmony_ci	length = le16_to_cpu(action->record.redo_length);
2675987da915Sopenharmony_ci	if (length == 8) {
2676987da915Sopenharmony_ci		target = le16_to_cpu(action->record.record_offset)
2677987da915Sopenharmony_ci			+ le16_to_cpu(action->record.attribute_offset);
2678987da915Sopenharmony_ci		/* target is right-justified to end of attribute */
2679987da915Sopenharmony_ci		target += getle16(buffer, target + 8) - length;
2680987da915Sopenharmony_ci		err = change_resident_expect(vol, action, buffer, data,
2681987da915Sopenharmony_ci				expected, target, length, AT_INDEX_ROOT);
2682987da915Sopenharmony_ci	}
2683987da915Sopenharmony_ci	return (err);
2684987da915Sopenharmony_ci}
2685987da915Sopenharmony_ci
2686987da915Sopenharmony_cistatic int redo_update_value(ntfs_volume *vol,
2687987da915Sopenharmony_ci			const struct ACTION_RECORD *action, char *buffer)
2688987da915Sopenharmony_ci{
2689987da915Sopenharmony_ci	LCN lcn;
2690987da915Sopenharmony_ci	const char *data;
2691987da915Sopenharmony_ci	u32 length;
2692987da915Sopenharmony_ci	u32 target;
2693987da915Sopenharmony_ci	u32 count;
2694987da915Sopenharmony_ci	u32 redo;
2695987da915Sopenharmony_ci	u32 end;
2696987da915Sopenharmony_ci	u32 i;
2697987da915Sopenharmony_ci	int changed;
2698987da915Sopenharmony_ci	int err;
2699987da915Sopenharmony_ci
2700987da915Sopenharmony_ci	if (optv > 1)
2701987da915Sopenharmony_ci		printf("-> %s()\n",__func__);
2702987da915Sopenharmony_ci	err = 1;
2703987da915Sopenharmony_ci	length = le16_to_cpu(action->record.redo_length);
2704987da915Sopenharmony_ci	redo = get_redo_offset(&action->record);
2705987da915Sopenharmony_ci	end = le32_to_cpu(action->record.client_data_length)
2706987da915Sopenharmony_ci				+ LOG_RECORD_HEAD_SZ;
2707987da915Sopenharmony_ci		/* sometimes there is no redo data */
2708987da915Sopenharmony_ci	if ((redo + length) > end)
2709987da915Sopenharmony_ci		data = (char*)NULL;
2710987da915Sopenharmony_ci	else
2711987da915Sopenharmony_ci		data = ((const char*)&action->record) + redo;
2712987da915Sopenharmony_ci	target = le16_to_cpu(action->record.record_offset)
2713987da915Sopenharmony_ci		+ le16_to_cpu(action->record.attribute_offset);
2714987da915Sopenharmony_ci	count = le16_to_cpu(action->record.lcns_to_follow);
2715987da915Sopenharmony_ci	if (optv > 1) {
2716987da915Sopenharmony_ci		lcn = sle64_to_cpu(action->record.lcn_list[0]);
2717987da915Sopenharmony_ci		printf("-> lcn 0x%llx target 0x%x length %d\n",
2718987da915Sopenharmony_ci			(long long)lcn, (int)target, (int)length);
2719987da915Sopenharmony_ci	}
2720987da915Sopenharmony_ci	if (optv > 1) {
2721987da915Sopenharmony_ci		printf("-> existing record :\n");
2722987da915Sopenharmony_ci		dump(&buffer[target], length);
2723987da915Sopenharmony_ci	}
2724987da915Sopenharmony_ci	if ((target + length) <= (count << clusterbits)) {
2725987da915Sopenharmony_ci		if (data)
2726987da915Sopenharmony_ci			changed = memcmp(buffer + target, data, length);
2727987da915Sopenharmony_ci		else {
2728987da915Sopenharmony_ci			for (i=0; (i<length) && !buffer[target+i]; i++) { }
2729987da915Sopenharmony_ci			changed = length && (i < length);
2730987da915Sopenharmony_ci		}
2731987da915Sopenharmony_ci		err = 0;
2732987da915Sopenharmony_ci		if (changed) {
2733987da915Sopenharmony_ci			if (data)
2734987da915Sopenharmony_ci				memcpy(buffer + target, data, length);
2735987da915Sopenharmony_ci			else
2736987da915Sopenharmony_ci				memset(buffer + target, 0, length);
2737987da915Sopenharmony_ci			if (optv > 1) {
2738987da915Sopenharmony_ci				printf("-> new record :\n");
2739987da915Sopenharmony_ci				dump(buffer + target, length);
2740987da915Sopenharmony_ci			}
2741987da915Sopenharmony_ci			err = write_raw(vol, &action->record, buffer);
2742987da915Sopenharmony_ci		}
2743987da915Sopenharmony_ci		if (optv > 1) {
2744987da915Sopenharmony_ci			printf("-> data record %s\n",
2745987da915Sopenharmony_ci				(changed ? "updated" : "unchanged"));
2746987da915Sopenharmony_ci		}
2747987da915Sopenharmony_ci	}
2748987da915Sopenharmony_ci
2749987da915Sopenharmony_ci	return (err);
2750987da915Sopenharmony_ci}
2751987da915Sopenharmony_ci
2752987da915Sopenharmony_cistatic int redo_update_vcn(ntfs_volume *vol,
2753987da915Sopenharmony_ci			const struct ACTION_RECORD *action, char *buffer)
2754987da915Sopenharmony_ci{
2755987da915Sopenharmony_ci	const char *data;
2756987da915Sopenharmony_ci	u32 target;
2757987da915Sopenharmony_ci	u32 length;
2758987da915Sopenharmony_ci	int err;
2759987da915Sopenharmony_ci
2760987da915Sopenharmony_ci	if (optv > 1)
2761987da915Sopenharmony_ci		printf("-> %s()\n",__func__);
2762987da915Sopenharmony_ci	err = 1;
2763987da915Sopenharmony_ci	data = ((const char*)&action->record)
2764987da915Sopenharmony_ci			+ get_redo_offset(&action->record);
2765987da915Sopenharmony_ci	length = le16_to_cpu(action->record.redo_length);
2766987da915Sopenharmony_ci	if (length == 8) {
2767987da915Sopenharmony_ci		target = le16_to_cpu(action->record.record_offset)
2768987da915Sopenharmony_ci			+ le16_to_cpu(action->record.attribute_offset);
2769987da915Sopenharmony_ci		/* target is right-justified to end of attribute */
2770987da915Sopenharmony_ci		target += getle16(buffer, target + 8) - length;
2771987da915Sopenharmony_ci		err = update_index(vol, action, buffer, data, target, length);
2772987da915Sopenharmony_ci	}
2773987da915Sopenharmony_ci	return (err);
2774987da915Sopenharmony_ci}
2775987da915Sopenharmony_ci
2776987da915Sopenharmony_cistatic int redo_write_end(ntfs_volume *vol,
2777987da915Sopenharmony_ci			const struct ACTION_RECORD *action, char *buffer)
2778987da915Sopenharmony_ci{
2779987da915Sopenharmony_ci	LCN lcn;
2780987da915Sopenharmony_ci	const char *data;
2781987da915Sopenharmony_ci	u32 target;
2782987da915Sopenharmony_ci	u32 length;
2783987da915Sopenharmony_ci	u32 oldlength;
2784987da915Sopenharmony_ci	u32 end;
2785987da915Sopenharmony_ci	u32 redo;
2786987da915Sopenharmony_ci	int err;
2787987da915Sopenharmony_ci	int changed;
2788987da915Sopenharmony_ci
2789987da915Sopenharmony_ci	if (optv > 1)
2790987da915Sopenharmony_ci		printf("-> %s()\n",__func__);
2791987da915Sopenharmony_ci	err = 1;
2792987da915Sopenharmony_ci	end = le32_to_cpu(action->record.client_data_length)
2793987da915Sopenharmony_ci			+ LOG_RECORD_HEAD_SZ;
2794987da915Sopenharmony_ci	length = le16_to_cpu(action->record.redo_length);
2795987da915Sopenharmony_ci	redo = get_redo_offset(&action->record);
2796987da915Sopenharmony_ci	if ((redo + length) > end)
2797987da915Sopenharmony_ci		data = (char*)NULL;
2798987da915Sopenharmony_ci	else
2799987da915Sopenharmony_ci		data = ((const char*)&action->record) + redo;
2800987da915Sopenharmony_ci	oldlength = le16_to_cpu(action->record.undo_length);
2801987da915Sopenharmony_ci	target = le16_to_cpu(action->record.record_offset)
2802987da915Sopenharmony_ci		+ le16_to_cpu(action->record.attribute_offset);
2803987da915Sopenharmony_ci	if (length == oldlength) {
2804987da915Sopenharmony_ci		if (optv > 1) {
2805987da915Sopenharmony_ci			lcn = sle64_to_cpu(action->record.lcn_list[0]);
2806987da915Sopenharmony_ci			printf("-> inode %lld lcn 0x%llx target 0x%x"
2807987da915Sopenharmony_ci				" length %d\n",
2808987da915Sopenharmony_ci				(long long)inode_number(&action->record),
2809987da915Sopenharmony_ci				(long long)lcn, (int)target, (int)length);
2810987da915Sopenharmony_ci		}
2811987da915Sopenharmony_ci		if (optv > 1) {
2812987da915Sopenharmony_ci			printf("-> existing record :\n");
2813987da915Sopenharmony_ci			dump(&buffer[target], length);
2814987da915Sopenharmony_ci		}
2815987da915Sopenharmony_ci		if ((target + length) <= mftrecsz) {
2816987da915Sopenharmony_ci			changed = memcmp(buffer + target, data, length);
2817987da915Sopenharmony_ci			err = 0;
2818987da915Sopenharmony_ci			if (changed) {
2819987da915Sopenharmony_ci				memcpy(buffer + target, data, length);
2820987da915Sopenharmony_ci				if (optv > 1) {
2821987da915Sopenharmony_ci					printf("-> new record :\n");
2822987da915Sopenharmony_ci					dump(buffer + target, length);
2823987da915Sopenharmony_ci				}
2824987da915Sopenharmony_ci				err = write_protected(vol, &action->record,
2825987da915Sopenharmony_ci						buffer, mftrecsz);
2826987da915Sopenharmony_ci			}
2827987da915Sopenharmony_ci			if (optv > 1) {
2828987da915Sopenharmony_ci				printf("-> MFT record %s\n",
2829987da915Sopenharmony_ci					(changed ? "updated" : "unchanged"));
2830987da915Sopenharmony_ci			}
2831987da915Sopenharmony_ci		}
2832987da915Sopenharmony_ci	} else {
2833987da915Sopenharmony_ci		if (length > oldlength)
2834987da915Sopenharmony_ci			err = add_resident(vol, action, buffer, data,
2835987da915Sopenharmony_ci					target, length, oldlength);
2836987da915Sopenharmony_ci		else
2837987da915Sopenharmony_ci			err = delete_resident(vol, action, buffer, data,
2838987da915Sopenharmony_ci					target, length, oldlength);
2839987da915Sopenharmony_ci	}
2840987da915Sopenharmony_ci	return (err);
2841987da915Sopenharmony_ci}
2842987da915Sopenharmony_ci
2843987da915Sopenharmony_cistatic int redo_write_index(ntfs_volume *vol,
2844987da915Sopenharmony_ci			const struct ACTION_RECORD *action, char *buffer)
2845987da915Sopenharmony_ci{
2846987da915Sopenharmony_ci	LCN lcn;
2847987da915Sopenharmony_ci	const char *data;
2848987da915Sopenharmony_ci	INDEX_BLOCK *indx;
2849987da915Sopenharmony_ci	u32 target;
2850987da915Sopenharmony_ci	u32 length;
2851987da915Sopenharmony_ci	u32 xsize;
2852987da915Sopenharmony_ci	int err;
2853987da915Sopenharmony_ci	int changed;
2854987da915Sopenharmony_ci
2855987da915Sopenharmony_ci	if (optv > 1)
2856987da915Sopenharmony_ci		printf("-> %s()\n",__func__);
2857987da915Sopenharmony_ci	err = 1;
2858987da915Sopenharmony_ci	data = ((const char*)&action->record)
2859987da915Sopenharmony_ci			+ get_redo_offset(&action->record);
2860987da915Sopenharmony_ci	length = le16_to_cpu(action->record.redo_length);
2861987da915Sopenharmony_ci			/* target is left-justified to creation time */
2862987da915Sopenharmony_ci	target = le16_to_cpu(action->record.record_offset)
2863987da915Sopenharmony_ci		+ le16_to_cpu(action->record.attribute_offset);
2864987da915Sopenharmony_ci	if (optv > 1) {
2865987da915Sopenharmony_ci		lcn = sle64_to_cpu(action->record.lcn_list[0]);
2866987da915Sopenharmony_ci		printf("-> lcn 0x%llx target 0x%x length %d\n",
2867987da915Sopenharmony_ci			(long long)lcn, (int)target, (int)length);
2868987da915Sopenharmony_ci	}
2869987da915Sopenharmony_ci	xsize = vol->indx_record_size;
2870987da915Sopenharmony_ci	indx = (INDEX_BLOCK*)buffer;
2871987da915Sopenharmony_ci	if (action->record.record_offset) {
2872987da915Sopenharmony_ci		printf("** Non-null record_offset in redo_write_index()\n");
2873987da915Sopenharmony_ci	}
2874987da915Sopenharmony_ci	if (optv > 1) {
2875987da915Sopenharmony_ci		printf("-> existing index :\n");
2876987da915Sopenharmony_ci		dump(&buffer[target], length);
2877987da915Sopenharmony_ci	}
2878987da915Sopenharmony_ci	if ((indx->magic == magic_INDX)
2879987da915Sopenharmony_ci	    && !(length & 7)
2880987da915Sopenharmony_ci	    && ((target + length) <= xsize)) {
2881987da915Sopenharmony_ci		/* This has to be an idempotent action */
2882987da915Sopenharmony_ci		changed = memcmp(buffer + target, data, length);
2883987da915Sopenharmony_ci		err = 0;
2884987da915Sopenharmony_ci		if (changed) {
2885987da915Sopenharmony_ci			/* Update the entry */
2886987da915Sopenharmony_ci			memcpy(buffer + target, data, length);
2887987da915Sopenharmony_ci			/* If truncating, set the new size */
2888987da915Sopenharmony_ci			indx->index.index_length =
2889987da915Sopenharmony_ci					cpu_to_le32(target + length - 0x18);
2890987da915Sopenharmony_ci			if (optv > 1) {
2891987da915Sopenharmony_ci				printf("-> new index :\n");
2892987da915Sopenharmony_ci				dump(&buffer[target], length);
2893987da915Sopenharmony_ci			}
2894987da915Sopenharmony_ci			err = write_protected(vol, &action->record,
2895987da915Sopenharmony_ci					buffer, xsize);
2896987da915Sopenharmony_ci		}
2897987da915Sopenharmony_ci		if (optv > 1) {
2898987da915Sopenharmony_ci			printf("-> INDX record %s\n",
2899987da915Sopenharmony_ci				(changed ? "updated" : "unchanged"));
2900987da915Sopenharmony_ci		}
2901987da915Sopenharmony_ci	}
2902987da915Sopenharmony_ci	return (err);
2903987da915Sopenharmony_ci}
2904987da915Sopenharmony_ci
2905987da915Sopenharmony_cistatic int undo_action37(ntfs_volume *vol __attribute__((unused)),
2906987da915Sopenharmony_ci				const struct ACTION_RECORD *action,
2907987da915Sopenharmony_ci				char *buffer __attribute__((unused)))
2908987da915Sopenharmony_ci{
2909987da915Sopenharmony_ci/*
2910987da915Sopenharmony_ci	const char *data;
2911987da915Sopenharmony_ci	u32 target;
2912987da915Sopenharmony_ci	u32 length;
2913987da915Sopenharmony_ci*/
2914987da915Sopenharmony_ci	int err;
2915987da915Sopenharmony_ci
2916987da915Sopenharmony_ci	if (optv > 1)
2917987da915Sopenharmony_ci		printf("-> %s()\n",__func__);
2918987da915Sopenharmony_ci	err = 1;
2919987da915Sopenharmony_ci/*
2920987da915Sopenharmony_ci	data = ((const char*)&action->record)
2921987da915Sopenharmony_ci			+ get_redo_offset(&action->record);
2922987da915Sopenharmony_ci	length = le16_to_cpu(action->record.redo_length);
2923987da915Sopenharmony_ci	target = le16_to_cpu(action->record.record_offset)
2924987da915Sopenharmony_ci		+ le16_to_cpu(action->record.attribute_offset);
2925987da915Sopenharmony_ci*/
2926987da915Sopenharmony_ci	printf("* Ignored action-37, inode %lld record :\n",
2927987da915Sopenharmony_ci			(long long)inode_number(&action->record));
2928987da915Sopenharmony_ci	err = 0;
2929987da915Sopenharmony_ci	return (err);
2930987da915Sopenharmony_ci}
2931987da915Sopenharmony_ci
2932987da915Sopenharmony_cistatic int undo_add_index(ntfs_volume *vol,
2933987da915Sopenharmony_ci			const struct ACTION_RECORD *action, char *buffer)
2934987da915Sopenharmony_ci{
2935987da915Sopenharmony_ci	LCN lcn;
2936987da915Sopenharmony_ci	const char *data;
2937987da915Sopenharmony_ci	INDEX_BLOCK *indx;
2938987da915Sopenharmony_ci	u32 target;
2939987da915Sopenharmony_ci	u32 length;
2940987da915Sopenharmony_ci	u32 xsize;
2941987da915Sopenharmony_ci	u32 indexlth;
2942987da915Sopenharmony_ci	int err;
2943987da915Sopenharmony_ci	BOOL found;
2944987da915Sopenharmony_ci
2945987da915Sopenharmony_ci	if (optv > 1)
2946987da915Sopenharmony_ci		printf("-> %s()\n",__func__);
2947987da915Sopenharmony_ci	err = 1;
2948987da915Sopenharmony_ci	data = ((const char*)&action->record)
2949987da915Sopenharmony_ci			+ get_redo_offset(&action->record);
2950987da915Sopenharmony_ci	length = le16_to_cpu(action->record.redo_length);
2951987da915Sopenharmony_ci	target = le16_to_cpu(action->record.record_offset)
2952987da915Sopenharmony_ci		+ le16_to_cpu(action->record.attribute_offset);
2953987da915Sopenharmony_ci	if (optv > 1) {
2954987da915Sopenharmony_ci		lcn = sle64_to_cpu(action->record.lcn_list[0]);
2955987da915Sopenharmony_ci		printf("-> lcn 0x%llx target 0x%x length %d\n",
2956987da915Sopenharmony_ci			(long long)lcn, (int)target, (int)length);
2957987da915Sopenharmony_ci	}
2958987da915Sopenharmony_ci	xsize = vol->indx_record_size;
2959987da915Sopenharmony_ci	indx = (INDEX_BLOCK*)(buffer
2960987da915Sopenharmony_ci			+ le16_to_cpu(action->record.record_offset));
2961987da915Sopenharmony_ci	if (optv > 1) {
2962987da915Sopenharmony_ci		printf("-> existing record :\n");
2963987da915Sopenharmony_ci		dump(&buffer[target], length);
2964987da915Sopenharmony_ci	}
2965987da915Sopenharmony_ci	if ((indx->magic == magic_INDX)
2966987da915Sopenharmony_ci	    && !(length & 7)
2967987da915Sopenharmony_ci	    && ((target + length) <= xsize)) {
2968987da915Sopenharmony_ci		/* This has to be an idempotent action */
2969987da915Sopenharmony_ci		found = index_match_undo(buffer + target, data, length);
2970987da915Sopenharmony_ci		err = 0;
2971987da915Sopenharmony_ci		if (found) {
2972987da915Sopenharmony_ci			/* Remove the entry */
2973987da915Sopenharmony_ci			memmove(buffer + target,
2974987da915Sopenharmony_ci				buffer + target + length,
2975987da915Sopenharmony_ci				xsize - target - length);
2976987da915Sopenharmony_ci			indexlth = le32_to_cpu(indx->index.index_length)
2977987da915Sopenharmony_ci					- length;
2978987da915Sopenharmony_ci			indx->index.index_length = cpu_to_le32(indexlth);
2979987da915Sopenharmony_ci			err = write_protected(vol, &action->record,
2980987da915Sopenharmony_ci						buffer, xsize);
2981987da915Sopenharmony_ci		} else {
2982987da915Sopenharmony_ci			sanity_indx(vol,buffer);
2983987da915Sopenharmony_ci			printf("full record :\n");
2984987da915Sopenharmony_ci			dump(buffer,xsize);
2985987da915Sopenharmony_ci		}
2986987da915Sopenharmony_ci		if (optv > 1) {
2987987da915Sopenharmony_ci			printf("-> INDX record %s\n",
2988987da915Sopenharmony_ci				(found ? "removed" : "unchanged"));
2989987da915Sopenharmony_ci		}
2990987da915Sopenharmony_ci	}
2991987da915Sopenharmony_ci	return (err);
2992987da915Sopenharmony_ci}
2993987da915Sopenharmony_ci
2994987da915Sopenharmony_cistatic int undo_add_root_index(ntfs_volume *vol,
2995987da915Sopenharmony_ci			const struct ACTION_RECORD *action, char *buffer)
2996987da915Sopenharmony_ci{
2997987da915Sopenharmony_ci	LCN lcn;
2998987da915Sopenharmony_ci	const char *data;
2999987da915Sopenharmony_ci	ATTR_RECORD *attr;
3000987da915Sopenharmony_ci	MFT_RECORD *entry;
3001987da915Sopenharmony_ci	INDEX_ROOT *index;
3002987da915Sopenharmony_ci	BOOL found;
3003987da915Sopenharmony_ci	u32 target;
3004987da915Sopenharmony_ci	u32 length;
3005987da915Sopenharmony_ci	int err;
3006987da915Sopenharmony_ci
3007987da915Sopenharmony_ci	if (optv > 1)
3008987da915Sopenharmony_ci		printf("-> %s()\n",__func__);
3009987da915Sopenharmony_ci	err = 1;
3010987da915Sopenharmony_ci	data = ((const char*)&action->record)
3011987da915Sopenharmony_ci			+ get_redo_offset(&action->record);
3012987da915Sopenharmony_ci	length = le16_to_cpu(action->record.redo_length);
3013987da915Sopenharmony_ci	target = le16_to_cpu(action->record.record_offset)
3014987da915Sopenharmony_ci		+ le16_to_cpu(action->record.attribute_offset);
3015987da915Sopenharmony_ci	if (optv > 1) {
3016987da915Sopenharmony_ci		lcn = sle64_to_cpu(action->record.lcn_list[0]);
3017987da915Sopenharmony_ci		printf("-> inode %lld lcn 0x%llx target 0x%x length %d\n",
3018987da915Sopenharmony_ci			(long long)inode_number(&action->record),
3019987da915Sopenharmony_ci			(long long)lcn, (int)target, (int)length);
3020987da915Sopenharmony_ci	}
3021987da915Sopenharmony_ci	entry = (MFT_RECORD*)buffer;
3022987da915Sopenharmony_ci	attr = (ATTR_RECORD*)(buffer
3023987da915Sopenharmony_ci			+ le16_to_cpu(action->record.record_offset));
3024987da915Sopenharmony_ci	index = (INDEX_ROOT*)(((char*)attr)
3025987da915Sopenharmony_ci			+ le16_to_cpu(attr->value_offset));
3026987da915Sopenharmony_ci	if (optv > 1) {
3027987da915Sopenharmony_ci		printf("existing index :\n");
3028987da915Sopenharmony_ci		dump(buffer + target,length);
3029987da915Sopenharmony_ci	}
3030987da915Sopenharmony_ci	if ((attr->type == AT_INDEX_ROOT)
3031987da915Sopenharmony_ci	    && !(length & 7)
3032987da915Sopenharmony_ci	    && ((target + length) <= mftrecsz)) {
3033987da915Sopenharmony_ci		/* This has to be an idempotent action */
3034987da915Sopenharmony_ci		found = index_match_undo(buffer + target, data, length);
3035987da915Sopenharmony_ci		err = 0;
3036987da915Sopenharmony_ci		if (found && !older_record(entry, &action->record)) {
3037987da915Sopenharmony_ci			/* Remove the entry */
3038987da915Sopenharmony_ci			memmove(buffer + target,
3039987da915Sopenharmony_ci				buffer + target + length,
3040987da915Sopenharmony_ci				mftrecsz - target - length);
3041987da915Sopenharmony_ci			resize_attribute(entry, attr, index, -length, -length);
3042987da915Sopenharmony_ci			if (optv > 1) {
3043987da915Sopenharmony_ci				printf("new index at same location :\n");
3044987da915Sopenharmony_ci				dump(buffer + target, length);
3045987da915Sopenharmony_ci			}
3046987da915Sopenharmony_ci			err = write_protected(vol, &action->record,
3047987da915Sopenharmony_ci						buffer, mftrecsz);
3048987da915Sopenharmony_ci		}
3049987da915Sopenharmony_ci		if (optv > 1) {
3050987da915Sopenharmony_ci			printf("-> MFT record %s\n",
3051987da915Sopenharmony_ci				(found ? "shrinked" : "unchanged"));
3052987da915Sopenharmony_ci		}
3053987da915Sopenharmony_ci	}
3054987da915Sopenharmony_ci	return (err);
3055987da915Sopenharmony_ci}
3056987da915Sopenharmony_ci
3057987da915Sopenharmony_cistatic int undo_create_attribute(ntfs_volume *vol,
3058987da915Sopenharmony_ci			const struct ACTION_RECORD *action, char *buffer)
3059987da915Sopenharmony_ci{
3060987da915Sopenharmony_ci	const char *data;
3061987da915Sopenharmony_ci	u32 target;
3062987da915Sopenharmony_ci	u32 length;
3063987da915Sopenharmony_ci	int err;
3064987da915Sopenharmony_ci
3065987da915Sopenharmony_ci	if (optv > 1)
3066987da915Sopenharmony_ci		printf("-> %s()\n",__func__);
3067987da915Sopenharmony_ci	err = 1;
3068987da915Sopenharmony_ci	data = ((const char*)&action->record)
3069987da915Sopenharmony_ci			+ get_redo_offset(&action->record);
3070987da915Sopenharmony_ci	length = le16_to_cpu(action->record.redo_length);
3071987da915Sopenharmony_ci	target = le16_to_cpu(action->record.record_offset)
3072987da915Sopenharmony_ci		+ le16_to_cpu(action->record.attribute_offset);
3073987da915Sopenharmony_ci	if (!action->record.undo_length)
3074987da915Sopenharmony_ci		err = remove_resident(vol, action, buffer, data,
3075987da915Sopenharmony_ci				target, length);
3076987da915Sopenharmony_ci	return (err);
3077987da915Sopenharmony_ci}
3078987da915Sopenharmony_ci
3079987da915Sopenharmony_cistatic int undo_delete_attribute(ntfs_volume *vol,
3080987da915Sopenharmony_ci			const struct ACTION_RECORD *action, char *buffer)
3081987da915Sopenharmony_ci{
3082987da915Sopenharmony_ci	const char *data;
3083987da915Sopenharmony_ci	u32 target;
3084987da915Sopenharmony_ci	u32 length;
3085987da915Sopenharmony_ci	int err;
3086987da915Sopenharmony_ci
3087987da915Sopenharmony_ci	if (optv > 1)
3088987da915Sopenharmony_ci		printf("-> %s()\n",__func__);
3089987da915Sopenharmony_ci	err = 1;
3090987da915Sopenharmony_ci	data = ((const char*)&action->record)
3091987da915Sopenharmony_ci			+ get_undo_offset(&action->record);
3092987da915Sopenharmony_ci	length = le16_to_cpu(action->record.undo_length);
3093987da915Sopenharmony_ci	target = le16_to_cpu(action->record.record_offset)
3094987da915Sopenharmony_ci		+ le16_to_cpu(action->record.attribute_offset);
3095987da915Sopenharmony_ci	if (!action->record.redo_length)
3096987da915Sopenharmony_ci		err = insert_resident(vol, action, buffer, data,
3097987da915Sopenharmony_ci				target, length);
3098987da915Sopenharmony_ci	return (err);
3099987da915Sopenharmony_ci}
3100987da915Sopenharmony_ci
3101987da915Sopenharmony_cistatic int undo_delete_index(ntfs_volume *vol,
3102987da915Sopenharmony_ci			const struct ACTION_RECORD *action, char *buffer)
3103987da915Sopenharmony_ci{
3104987da915Sopenharmony_ci	LCN lcn;
3105987da915Sopenharmony_ci	const char *data;
3106987da915Sopenharmony_ci	INDEX_BLOCK *indx;
3107987da915Sopenharmony_ci	u32 target;
3108987da915Sopenharmony_ci	u32 length;
3109987da915Sopenharmony_ci	u32 xsize;
3110987da915Sopenharmony_ci	u32 indexlth;
3111987da915Sopenharmony_ci	int err;
3112987da915Sopenharmony_ci	BOOL found;
3113987da915Sopenharmony_ci
3114987da915Sopenharmony_ci// MERGE with redo_add_root_index() ?
3115987da915Sopenharmony_ci	if (optv > 1)
3116987da915Sopenharmony_ci		printf("-> %s()\n",__func__);
3117987da915Sopenharmony_ci	err = 1;
3118987da915Sopenharmony_ci	data = ((const char*)&action->record)
3119987da915Sopenharmony_ci			+ get_undo_offset(&action->record);
3120987da915Sopenharmony_ci	length = le16_to_cpu(action->record.undo_length);
3121987da915Sopenharmony_ci	target = le16_to_cpu(action->record.record_offset)
3122987da915Sopenharmony_ci		+ le16_to_cpu(action->record.attribute_offset);
3123987da915Sopenharmony_ci	if (optv > 1) {
3124987da915Sopenharmony_ci		lcn = sle64_to_cpu(action->record.lcn_list[0]);
3125987da915Sopenharmony_ci		printf("-> lcn 0x%llx target 0x%x length %d\n",
3126987da915Sopenharmony_ci			(long long)lcn, (int)target, (int)length);
3127987da915Sopenharmony_ci	}
3128987da915Sopenharmony_ci	xsize = vol->indx_record_size;
3129987da915Sopenharmony_ci	indx = (INDEX_BLOCK*)(buffer
3130987da915Sopenharmony_ci			+ le16_to_cpu(action->record.record_offset));
3131987da915Sopenharmony_ci	if (optv > 1) {
3132987da915Sopenharmony_ci		printf("-> existing record :\n");
3133987da915Sopenharmony_ci		dump(&buffer[target], length);
3134987da915Sopenharmony_ci	}
3135987da915Sopenharmony_ci	if ((indx->magic == magic_INDX)
3136987da915Sopenharmony_ci	    && !(length & 7)
3137987da915Sopenharmony_ci	    && ((target + length) <= xsize)
3138987da915Sopenharmony_ci	    && !sanity_indx(vol,buffer)) {
3139987da915Sopenharmony_ci		/* This has to be an idempotent action */
3140987da915Sopenharmony_ci		found = !memcmp(buffer + target, data, length);
3141987da915Sopenharmony_ci		err = 0;
3142987da915Sopenharmony_ci		if (!found) {
3143987da915Sopenharmony_ci			/* Make space to insert the entry */
3144987da915Sopenharmony_ci			memmove(buffer + target + length,
3145987da915Sopenharmony_ci				buffer + target,
3146987da915Sopenharmony_ci				xsize - target - length);
3147987da915Sopenharmony_ci			memcpy(buffer + target, data, length);
3148987da915Sopenharmony_ci			indexlth = le32_to_cpu(indx->index.index_length)
3149987da915Sopenharmony_ci					+ length;
3150987da915Sopenharmony_ci			indx->index.index_length = cpu_to_le32(indexlth);
3151987da915Sopenharmony_ci			if (optv > 1) {
3152987da915Sopenharmony_ci				printf("-> inserted record :\n");
3153987da915Sopenharmony_ci				dump(&buffer[target], length);
3154987da915Sopenharmony_ci			}
3155987da915Sopenharmony_ci			/* rebuildname() has no effect currently, should drop */
3156987da915Sopenharmony_ci			rebuildname((const INDEX_ENTRY*)data);
3157987da915Sopenharmony_ci			err = write_protected(vol, &action->record,
3158987da915Sopenharmony_ci						buffer, xsize);
3159987da915Sopenharmony_ci		}
3160987da915Sopenharmony_ci		if (optv > 1) {
3161987da915Sopenharmony_ci			printf("-> INDX record %s\n",
3162987da915Sopenharmony_ci				(found ? "unchanged" : "inserted"));
3163987da915Sopenharmony_ci		}
3164987da915Sopenharmony_ci	}
3165987da915Sopenharmony_ci	return (err);
3166987da915Sopenharmony_ci}
3167987da915Sopenharmony_ci
3168987da915Sopenharmony_cistatic int undo_delete_root_index(ntfs_volume *vol,
3169987da915Sopenharmony_ci			const struct ACTION_RECORD *action, char *buffer)
3170987da915Sopenharmony_ci{
3171987da915Sopenharmony_ci	LCN lcn;
3172987da915Sopenharmony_ci	const char *data;
3173987da915Sopenharmony_ci	ATTR_RECORD *attr;
3174987da915Sopenharmony_ci	MFT_RECORD *entry;
3175987da915Sopenharmony_ci	INDEX_ROOT *index;
3176987da915Sopenharmony_ci	u32 target;
3177987da915Sopenharmony_ci	u32 length;
3178987da915Sopenharmony_ci	int err;
3179987da915Sopenharmony_ci	BOOL found;
3180987da915Sopenharmony_ci
3181987da915Sopenharmony_ci	if (optv > 1)
3182987da915Sopenharmony_ci		printf("-> %s()\n",__func__);
3183987da915Sopenharmony_ci	err = 1;
3184987da915Sopenharmony_ci	data = ((const char*)&action->record)
3185987da915Sopenharmony_ci			+ get_undo_offset(&action->record);
3186987da915Sopenharmony_ci	length = le16_to_cpu(action->record.undo_length);
3187987da915Sopenharmony_ci	target = le16_to_cpu(action->record.record_offset)
3188987da915Sopenharmony_ci		+ le16_to_cpu(action->record.attribute_offset);
3189987da915Sopenharmony_ci	if (optv > 1) {
3190987da915Sopenharmony_ci		lcn = sle64_to_cpu(action->record.lcn_list[0]);
3191987da915Sopenharmony_ci		printf("-> inode %lld lcn 0x%llx target 0x%x length %d\n",
3192987da915Sopenharmony_ci			(long long)inode_number(&action->record),
3193987da915Sopenharmony_ci			(long long)lcn, (int)target, (int)length);
3194987da915Sopenharmony_ci	}
3195987da915Sopenharmony_ci	entry = (MFT_RECORD*)buffer;
3196987da915Sopenharmony_ci	attr = (ATTR_RECORD*)(buffer
3197987da915Sopenharmony_ci			+ le16_to_cpu(action->record.record_offset));
3198987da915Sopenharmony_ci	index = (INDEX_ROOT*)(((char*)attr)
3199987da915Sopenharmony_ci			+ le16_to_cpu(attr->value_offset));
3200987da915Sopenharmony_ci	if (attr->type != AT_INDEX_ROOT) {
3201987da915Sopenharmony_ci		printf("** Unexpected attr type 0x%lx\n",
3202987da915Sopenharmony_ci				(long)le32_to_cpu(attr->type));
3203987da915Sopenharmony_ci		printf("existing mft\n");
3204987da915Sopenharmony_ci		dump((char*)buffer,512);
3205987da915Sopenharmony_ci		printf("existing index\n");
3206987da915Sopenharmony_ci		dump(buffer + target,length);
3207987da915Sopenharmony_ci	}
3208987da915Sopenharmony_ci	if (optv > 1) {
3209987da915Sopenharmony_ci		printf("existing index :\n");
3210987da915Sopenharmony_ci		dump(buffer + target,length);
3211987da915Sopenharmony_ci	}
3212987da915Sopenharmony_ci	if ((attr->type == AT_INDEX_ROOT)
3213987da915Sopenharmony_ci	    && !(length & 7)
3214987da915Sopenharmony_ci	    && ((target + length) <= mftrecsz)) {
3215987da915Sopenharmony_ci		/* This has to be an idempotent action */
3216987da915Sopenharmony_ci		found = !memcmp(buffer + target, data, length);
3217987da915Sopenharmony_ci		err = 0;
3218987da915Sopenharmony_ci			/* Do not insert if present */
3219987da915Sopenharmony_ci		if (!found) {
3220987da915Sopenharmony_ci			/* Make space to insert the entry */
3221987da915Sopenharmony_ci			memmove(buffer + target + length,
3222987da915Sopenharmony_ci				buffer + target,
3223987da915Sopenharmony_ci				mftrecsz - target - length);
3224987da915Sopenharmony_ci			memcpy(buffer + target, data, length);
3225987da915Sopenharmony_ci			resize_attribute(entry, attr, index, length, length);
3226987da915Sopenharmony_ci			if (optv > 1) {
3227987da915Sopenharmony_ci				printf("new index :\n");
3228987da915Sopenharmony_ci				dump(buffer + target, length);
3229987da915Sopenharmony_ci			}
3230987da915Sopenharmony_ci			err = write_protected(vol, &action->record,
3231987da915Sopenharmony_ci						buffer, mftrecsz);
3232987da915Sopenharmony_ci		}
3233987da915Sopenharmony_ci		if (optv > 1) {
3234987da915Sopenharmony_ci			printf("-> MFT record %s\n",
3235987da915Sopenharmony_ci				(found ? "unchanged" : "expanded"));
3236987da915Sopenharmony_ci		}
3237987da915Sopenharmony_ci	}
3238987da915Sopenharmony_ci	return (err);
3239987da915Sopenharmony_ci}
3240987da915Sopenharmony_ci
3241987da915Sopenharmony_cistatic int undo_create_file(ntfs_volume *vol,
3242987da915Sopenharmony_ci			const struct ACTION_RECORD *action, char *buffer)
3243987da915Sopenharmony_ci{
3244987da915Sopenharmony_ci	LCN lcn;
3245987da915Sopenharmony_ci	const char *data;
3246987da915Sopenharmony_ci	MFT_RECORD *record;
3247987da915Sopenharmony_ci	u32 target;
3248987da915Sopenharmony_ci	u32 length;
3249987da915Sopenharmony_ci	int err;
3250987da915Sopenharmony_ci	int changed;
3251987da915Sopenharmony_ci
3252987da915Sopenharmony_ci	if (optv > 1)
3253987da915Sopenharmony_ci		printf("-> %s()\n",__func__);
3254987da915Sopenharmony_ci	err = 1;
3255987da915Sopenharmony_ci		/* redo initialize, clearing the in_use flag ? */
3256987da915Sopenharmony_ci	data = ((const char*)&action->record)
3257987da915Sopenharmony_ci			+ get_redo_offset(&action->record);
3258987da915Sopenharmony_ci	length = le16_to_cpu(action->record.redo_length);
3259987da915Sopenharmony_ci	target = le16_to_cpu(action->record.record_offset)
3260987da915Sopenharmony_ci		+ le16_to_cpu(action->record.attribute_offset);
3261987da915Sopenharmony_ci	if (optv > 1) {
3262987da915Sopenharmony_ci		lcn = sle64_to_cpu(action->record.lcn_list[0]);
3263987da915Sopenharmony_ci		printf("-> inode %lld lcn 0x%llx target 0x%x length %d\n",
3264987da915Sopenharmony_ci			(long long)inode_number(&action->record),
3265987da915Sopenharmony_ci			(long long)lcn, (int)target, (int)length);
3266987da915Sopenharmony_ci	}
3267987da915Sopenharmony_ci	record = (MFT_RECORD*)buffer;
3268987da915Sopenharmony_ci	if (optv > 1) {
3269987da915Sopenharmony_ci		printf("-> existing record :\n");
3270987da915Sopenharmony_ci		dump(buffer,mftrecsz);
3271987da915Sopenharmony_ci	}
3272987da915Sopenharmony_ci	if ((target + length) <= mftrecsz) {
3273987da915Sopenharmony_ci		changed = memcmp(buffer + target, data, length);
3274987da915Sopenharmony_ci		err = 0;
3275987da915Sopenharmony_ci		if (changed || (record->flags & MFT_RECORD_IN_USE)) {
3276987da915Sopenharmony_ci			memcpy(buffer + target, data, length);
3277987da915Sopenharmony_ci			record->flags &= ~MFT_RECORD_IN_USE;
3278987da915Sopenharmony_ci			if (optv > 1) {
3279987da915Sopenharmony_ci				printf("-> new record :\n");
3280987da915Sopenharmony_ci				dump(buffer,mftrecsz);
3281987da915Sopenharmony_ci			}
3282987da915Sopenharmony_ci			err = write_protected(vol, &action->record,
3283987da915Sopenharmony_ci						buffer, mftrecsz);
3284987da915Sopenharmony_ci		}
3285987da915Sopenharmony_ci		if (optv > 1) {
3286987da915Sopenharmony_ci			printf("-> MFT record %s\n",
3287987da915Sopenharmony_ci				(changed ? "updated" : "unchanged"));
3288987da915Sopenharmony_ci		}
3289987da915Sopenharmony_ci	}
3290987da915Sopenharmony_ci	return (err);
3291987da915Sopenharmony_ci}
3292987da915Sopenharmony_ci
3293987da915Sopenharmony_cistatic int undo_delete_file(ntfs_volume *vol,
3294987da915Sopenharmony_ci			const struct ACTION_RECORD *action, char *buffer)
3295987da915Sopenharmony_ci{
3296987da915Sopenharmony_ci	LCN lcn;
3297987da915Sopenharmony_ci	const char *data;
3298987da915Sopenharmony_ci	MFT_RECORD *record;
3299987da915Sopenharmony_ci	u32 target;
3300987da915Sopenharmony_ci	u32 length;
3301987da915Sopenharmony_ci	int err;
3302987da915Sopenharmony_ci	int changed;
3303987da915Sopenharmony_ci
3304987da915Sopenharmony_ci	if (optv > 1)
3305987da915Sopenharmony_ci		printf("-> %s()\n",__func__);
3306987da915Sopenharmony_ci	err = 1;
3307987da915Sopenharmony_ci	data = ((const char*)&action->record)
3308987da915Sopenharmony_ci			+ get_undo_offset(&action->record);
3309987da915Sopenharmony_ci	length = le16_to_cpu(action->record.undo_length);
3310987da915Sopenharmony_ci	target = le16_to_cpu(action->record.record_offset)
3311987da915Sopenharmony_ci		+ le16_to_cpu(action->record.attribute_offset);
3312987da915Sopenharmony_ci	if (optv > 1) {
3313987da915Sopenharmony_ci		lcn = sle64_to_cpu(action->record.lcn_list[0]);
3314987da915Sopenharmony_ci		printf("-> inode %lld lcn 0x%llx target 0x%x length %d\n",
3315987da915Sopenharmony_ci			(long long)inode_number(&action->record),
3316987da915Sopenharmony_ci			(long long)lcn, (int)target, (int)length);
3317987da915Sopenharmony_ci	}
3318987da915Sopenharmony_ci	if (optv > 1) {
3319987da915Sopenharmony_ci		printf("-> existing record :\n");
3320987da915Sopenharmony_ci		dump(buffer,mftrecsz);
3321987da915Sopenharmony_ci	}
3322987da915Sopenharmony_ci	record = (MFT_RECORD*)buffer;
3323987da915Sopenharmony_ci	if ((target + length) <= mftrecsz) {
3324987da915Sopenharmony_ci		changed = memcmp(buffer + target, data, length)
3325987da915Sopenharmony_ci			|| !(record->flags & MFT_RECORD_IN_USE);
3326987da915Sopenharmony_ci		err = 0;
3327987da915Sopenharmony_ci		if (changed) {
3328987da915Sopenharmony_ci			memcpy(buffer + target, data, length);
3329987da915Sopenharmony_ci		/*
3330987da915Sopenharmony_ci		 * Unclear what we should do for recreating a file.
3331987da915Sopenharmony_ci		 * Only 24 bytes are available, the used length is not known,
3332987da915Sopenharmony_ci		 * the number of links suggests we should keep the current
3333987da915Sopenharmony_ci		 * names... If so, when will they be deleted ?
3334987da915Sopenharmony_ci		 * We will have to make stamp changes in the standard
3335987da915Sopenharmony_ci		 * information attribute, so better not to delete it.
3336987da915Sopenharmony_ci		 * Should we create a data or index attribute ?
3337987da915Sopenharmony_ci		 * Here, we assume we should delete the file names when
3338987da915Sopenharmony_ci		 * the record now appears to not be in use and there are
3339987da915Sopenharmony_ci		 * links.
3340987da915Sopenharmony_ci		 */
3341987da915Sopenharmony_ci			if (record->link_count
3342987da915Sopenharmony_ci			    && !(record->flags & MFT_RECORD_IN_USE))
3343987da915Sopenharmony_ci				err = delete_names(buffer);
3344987da915Sopenharmony_ci			record->flags |= MFT_RECORD_IN_USE;
3345987da915Sopenharmony_ci			if (optv > 1) {
3346987da915Sopenharmony_ci				printf("-> new record :\n");
3347987da915Sopenharmony_ci				dump(buffer,mftrecsz);
3348987da915Sopenharmony_ci			}
3349987da915Sopenharmony_ci			if (!err)
3350987da915Sopenharmony_ci				err = write_protected(vol,
3351987da915Sopenharmony_ci					&action->record,
3352987da915Sopenharmony_ci					buffer, mftrecsz);
3353987da915Sopenharmony_ci		}
3354987da915Sopenharmony_ci		if (optv > 1) {
3355987da915Sopenharmony_ci			printf("-> MFT record %s\n",
3356987da915Sopenharmony_ci				(changed ? "updated" : "unchanged"));
3357987da915Sopenharmony_ci		}
3358987da915Sopenharmony_ci	}
3359987da915Sopenharmony_ci	return (err);
3360987da915Sopenharmony_ci}
3361987da915Sopenharmony_ci
3362987da915Sopenharmony_cistatic int undo_force_bits(ntfs_volume *vol,
3363987da915Sopenharmony_ci			const struct ACTION_RECORD *action, char *buffer)
3364987da915Sopenharmony_ci{
3365987da915Sopenharmony_ci	LCN lcn;
3366987da915Sopenharmony_ci	const struct BITMAP_ACTION *data;
3367987da915Sopenharmony_ci	u32 i;
3368987da915Sopenharmony_ci	int err;
3369987da915Sopenharmony_ci	int wanted;
3370987da915Sopenharmony_ci	u32 firstbit;
3371987da915Sopenharmony_ci	u32 count;
3372987da915Sopenharmony_ci
3373987da915Sopenharmony_ci	if (optv > 1)
3374987da915Sopenharmony_ci		printf("-> %s()\n",__func__);
3375987da915Sopenharmony_ci	err = 1;
3376987da915Sopenharmony_ci	data = (const struct BITMAP_ACTION*)
3377987da915Sopenharmony_ci			(((const char*)&action->record)
3378987da915Sopenharmony_ci				+ get_redo_offset(&action->record));
3379987da915Sopenharmony_ci	firstbit = le32_to_cpu(data->firstbit);
3380987da915Sopenharmony_ci	count = le32_to_cpu(data->count);
3381987da915Sopenharmony_ci	if (action->record.redo_operation
3382987da915Sopenharmony_ci			== const_cpu_to_le16(SetBitsInNonResidentBitMap))
3383987da915Sopenharmony_ci		wanted = 0;
3384987da915Sopenharmony_ci	else
3385987da915Sopenharmony_ci		wanted = 1;
3386987da915Sopenharmony_ci// TODO consistency undo_offset == redo_offset, etc.
3387987da915Sopenharmony_ci// firstbit + count < 8*clustersz (multiple clusters possible ?)
3388987da915Sopenharmony_ci	if (optv > 1) {
3389987da915Sopenharmony_ci		lcn = sle64_to_cpu(action->record.lcn_list[0]);
3390987da915Sopenharmony_ci		printf("-> lcn 0x%llx firstbit %d count %d wanted %d\n",
3391987da915Sopenharmony_ci			(long long)lcn,(int)firstbit,(int)count,(int)wanted);
3392987da915Sopenharmony_ci	}
3393987da915Sopenharmony_ci	for (i=0; i<count; i++)
3394987da915Sopenharmony_ci		ntfs_bit_set((u8*)buffer, firstbit + i, wanted);
3395987da915Sopenharmony_ci	if (!write_raw(vol, &action->record, buffer)) {
3396987da915Sopenharmony_ci		err = 0;
3397987da915Sopenharmony_ci		if (optv > 1)
3398987da915Sopenharmony_ci			printf("-> record updated\n");
3399987da915Sopenharmony_ci	}
3400987da915Sopenharmony_ci	if (err)
3401987da915Sopenharmony_ci		printf("** redo_clearbits failed\n");
3402987da915Sopenharmony_ci	return (err);
3403987da915Sopenharmony_ci}
3404987da915Sopenharmony_ci
3405987da915Sopenharmony_cistatic int undo_open_attribute(ntfs_volume *vol __attribute__((unused)),
3406987da915Sopenharmony_ci				const struct ACTION_RECORD *action)
3407987da915Sopenharmony_ci{
3408987da915Sopenharmony_ci	const char *data;
3409987da915Sopenharmony_ci	struct ATTR *pa;
3410987da915Sopenharmony_ci	const ATTR_OLD *attr_old;
3411987da915Sopenharmony_ci	const ATTR_NEW *attr_new;
3412987da915Sopenharmony_ci	const char *name;
3413987da915Sopenharmony_ci	le64 inode;
3414987da915Sopenharmony_ci	u32 namelen;
3415987da915Sopenharmony_ci	u32 length;
3416987da915Sopenharmony_ci	u32 extra;
3417987da915Sopenharmony_ci	int err;
3418987da915Sopenharmony_ci
3419987da915Sopenharmony_ci	if (optv > 1)
3420987da915Sopenharmony_ci		printf("-> %s()\n",__func__);
3421987da915Sopenharmony_ci	err = 1;
3422987da915Sopenharmony_ci	data = ((const char*)&action->record)
3423987da915Sopenharmony_ci			+ get_redo_offset(&action->record);
3424987da915Sopenharmony_ci	length = le16_to_cpu(action->record.redo_length);
3425987da915Sopenharmony_ci	extra = get_extra_offset(&action->record);
3426987da915Sopenharmony_ci	if (action->record.undo_length) {
3427987da915Sopenharmony_ci		name = ((const char*)&action->record) + extra;
3428987da915Sopenharmony_ci		namelen = le32_to_cpu(action->record.client_data_length)
3429987da915Sopenharmony_ci				+ LOG_RECORD_HEAD_SZ - extra;
3430987da915Sopenharmony_ci		/* fix namelen which was aligned modulo 8 */
3431987da915Sopenharmony_ci		namelen = fixnamelen(name, namelen);
3432987da915Sopenharmony_ci		if (optv > 1) {
3433987da915Sopenharmony_ci			printf("-> length %d namelen %d",(int)length,
3434987da915Sopenharmony_ci							(int)namelen);
3435987da915Sopenharmony_ci			showname(", ", name, namelen/2);
3436987da915Sopenharmony_ci		}
3437987da915Sopenharmony_ci	} else {
3438987da915Sopenharmony_ci		namelen = 0;
3439987da915Sopenharmony_ci		name = "";
3440987da915Sopenharmony_ci	}
3441987da915Sopenharmony_ci	pa = getattrentry(le16_to_cpu(action->record.target_attribute),0);
3442987da915Sopenharmony_ci// TODO Only process is attr is not older ?
3443987da915Sopenharmony_ci	if (pa) {
3444987da915Sopenharmony_ci		/* check whether the redo attr matches what we have in store */
3445987da915Sopenharmony_ci		switch (length) {
3446987da915Sopenharmony_ci		case sizeof(ATTR_OLD) :
3447987da915Sopenharmony_ci			attr_old = (const ATTR_OLD*)data;
3448987da915Sopenharmony_ci				/* Badly aligned */
3449987da915Sopenharmony_ci			memcpy(&inode, &attr_old->inode, 8);
3450987da915Sopenharmony_ci			err = (MREF(le64_to_cpu(inode)) != pa->inode)
3451987da915Sopenharmony_ci			    || (attr_old->type != pa->type);
3452987da915Sopenharmony_ci			break;
3453987da915Sopenharmony_ci		case sizeof(ATTR_NEW) :
3454987da915Sopenharmony_ci			attr_new = (const ATTR_NEW*)data;
3455987da915Sopenharmony_ci			err = (MREF(le64_to_cpu(attr_new->inode))!= pa->inode)
3456987da915Sopenharmony_ci			    || (attr_new->type != pa->type);
3457987da915Sopenharmony_ci			break;
3458987da915Sopenharmony_ci		default : err = 1;
3459987da915Sopenharmony_ci		}
3460987da915Sopenharmony_ci		if (!err) {
3461987da915Sopenharmony_ci			err = (namelen != pa->namelen)
3462987da915Sopenharmony_ci				|| (namelen
3463987da915Sopenharmony_ci				    && memcmp(name, pa->name, namelen));
3464987da915Sopenharmony_ci		}
3465987da915Sopenharmony_ci		if (optv > 1)
3466987da915Sopenharmony_ci			printf("-> attribute %s the recorded one\n",
3467987da915Sopenharmony_ci				(err ? "does not match" : "matches"));
3468987da915Sopenharmony_ci	} else
3469987da915Sopenharmony_ci		if (optv)
3470987da915Sopenharmony_ci			printf("* Unrecorded attribute\n");
3471987da915Sopenharmony_cierr = 0;
3472987da915Sopenharmony_ci	return (err);
3473987da915Sopenharmony_ci}
3474987da915Sopenharmony_ci
3475987da915Sopenharmony_cistatic int undo_sizes(ntfs_volume *vol,
3476987da915Sopenharmony_ci			const struct ACTION_RECORD *action, char *buffer)
3477987da915Sopenharmony_ci{
3478987da915Sopenharmony_ci	const char *data;
3479987da915Sopenharmony_ci	MFT_RECORD *entry;
3480987da915Sopenharmony_ci	ATTR_RECORD *attr;
3481987da915Sopenharmony_ci	u32 target;
3482987da915Sopenharmony_ci	u32 length;
3483987da915Sopenharmony_ci	u32 offs;
3484987da915Sopenharmony_ci	int err;
3485987da915Sopenharmony_ci
3486987da915Sopenharmony_ci	if (optv > 1)
3487987da915Sopenharmony_ci		printf("-> %s()\n",__func__);
3488987da915Sopenharmony_ci	err = 1;
3489987da915Sopenharmony_ci	data = ((const char*)&action->record)
3490987da915Sopenharmony_ci			+ get_undo_offset(&action->record);
3491987da915Sopenharmony_ci	length = le16_to_cpu(action->record.undo_length);
3492987da915Sopenharmony_ci	target = le16_to_cpu(action->record.record_offset)
3493987da915Sopenharmony_ci		+ le16_to_cpu(action->record.attribute_offset)
3494987da915Sopenharmony_ci		+ offsetof(ATTR_RECORD, allocated_size);
3495987da915Sopenharmony_ci	entry = (MFT_RECORD*)buffer;
3496987da915Sopenharmony_ci	if (!(entry->flags & MFT_RECORD_IS_DIRECTORY))
3497987da915Sopenharmony_ci		err = change_resident(vol, action, buffer,
3498987da915Sopenharmony_ci			data, target, length);
3499987da915Sopenharmony_ci	else {
3500987da915Sopenharmony_ci		/* On a directory, may have to build an index allocation */
3501987da915Sopenharmony_ci		offs = le16_to_cpu(action->record.record_offset);
3502987da915Sopenharmony_ci		attr = (ATTR_RECORD*)(buffer + offs);
3503987da915Sopenharmony_ci		if (attr->type != AT_INDEX_ALLOCATION) {
3504987da915Sopenharmony_ci			err = insert_index_allocation(vol, buffer, offs);
3505987da915Sopenharmony_ci			if (!err)
3506987da915Sopenharmony_ci				err = change_resident(vol, action, buffer,
3507987da915Sopenharmony_ci						data, target, length);
3508987da915Sopenharmony_ci		} else
3509987da915Sopenharmony_ci			err = change_resident(vol, action, buffer,
3510987da915Sopenharmony_ci					data, target, length);
3511987da915Sopenharmony_ci	}
3512987da915Sopenharmony_ci	return (err);
3513987da915Sopenharmony_ci}
3514987da915Sopenharmony_ci
3515987da915Sopenharmony_cistatic int undo_update_index(ntfs_volume *vol, const struct ACTION_RECORD *action,
3516987da915Sopenharmony_ci			char *buffer)
3517987da915Sopenharmony_ci{
3518987da915Sopenharmony_ci	const char *data;
3519987da915Sopenharmony_ci	u32 target;
3520987da915Sopenharmony_ci	u32 length;
3521987da915Sopenharmony_ci	int err;
3522987da915Sopenharmony_ci
3523987da915Sopenharmony_ci	if (optv > 1)
3524987da915Sopenharmony_ci		printf("-> %s()\n",__func__);
3525987da915Sopenharmony_ci	err = 1;
3526987da915Sopenharmony_ci	data = ((const char*)&action->record)
3527987da915Sopenharmony_ci			+ get_undo_offset(&action->record);
3528987da915Sopenharmony_ci	length = le16_to_cpu(action->record.undo_length);
3529987da915Sopenharmony_ci			/* target is left-justified to creation time */
3530987da915Sopenharmony_ci	target = le16_to_cpu(action->record.record_offset)
3531987da915Sopenharmony_ci		+ le16_to_cpu(action->record.attribute_offset)
3532987da915Sopenharmony_ci		+ offsetof(INDEX_ENTRY, key.file_name.creation_time);
3533987da915Sopenharmony_ci	err = update_index(vol, action, buffer, data, target, length);
3534987da915Sopenharmony_ci	return (err);
3535987da915Sopenharmony_ci}
3536987da915Sopenharmony_ci
3537987da915Sopenharmony_cistatic int undo_update_index_value(ntfs_volume *vol,
3538987da915Sopenharmony_ci			const struct ACTION_RECORD *action, char *buffer)
3539987da915Sopenharmony_ci{
3540987da915Sopenharmony_ci	LCN lcn;
3541987da915Sopenharmony_ci	const char *data;
3542987da915Sopenharmony_ci	u32 length;
3543987da915Sopenharmony_ci	u32 target;
3544987da915Sopenharmony_ci	int changed;
3545987da915Sopenharmony_ci	int err;
3546987da915Sopenharmony_ci
3547987da915Sopenharmony_ci	if (optv > 1)
3548987da915Sopenharmony_ci		printf("-> %s()\n",__func__);
3549987da915Sopenharmony_ci	err = 1;
3550987da915Sopenharmony_ci	data = ((const char*)&action->record)
3551987da915Sopenharmony_ci				+ get_undo_offset(&action->record);
3552987da915Sopenharmony_ci	length = le16_to_cpu(action->record.undo_length);
3553987da915Sopenharmony_ci	target = le16_to_cpu(action->record.record_offset)
3554987da915Sopenharmony_ci		+ le16_to_cpu(action->record.attribute_offset);
3555987da915Sopenharmony_ci	if (optv > 1) {
3556987da915Sopenharmony_ci		lcn = sle64_to_cpu(action->record.lcn_list[0]);
3557987da915Sopenharmony_ci		printf("-> lcn 0x%llx target 0x%x length %d\n",
3558987da915Sopenharmony_ci			(long long)lcn, (int)target, (int)length);
3559987da915Sopenharmony_ci	}
3560987da915Sopenharmony_ci	if (optv > 1) {
3561987da915Sopenharmony_ci		printf("-> existing record :\n");
3562987da915Sopenharmony_ci		dump(&buffer[target], length);
3563987da915Sopenharmony_ci	}
3564987da915Sopenharmony_ci	if ((target + length) <= vol->indx_record_size) {
3565987da915Sopenharmony_ci		changed = length && memcmp(buffer + target, data, length);
3566987da915Sopenharmony_ci		err = 0;
3567987da915Sopenharmony_ci		if (changed) {
3568987da915Sopenharmony_ci			memcpy(buffer + target, data, length);
3569987da915Sopenharmony_ci			if (optv > 1) {
3570987da915Sopenharmony_ci				printf("-> new record :\n");
3571987da915Sopenharmony_ci				dump(buffer + target, length);
3572987da915Sopenharmony_ci			}
3573987da915Sopenharmony_ci			err = write_protected(vol, &action->record, buffer,
3574987da915Sopenharmony_ci						vol->indx_record_size);
3575987da915Sopenharmony_ci		}
3576987da915Sopenharmony_ci		if (optv > 1) {
3577987da915Sopenharmony_ci			printf("-> data record %s\n",
3578987da915Sopenharmony_ci				(changed ? "updated" : "unchanged"));
3579987da915Sopenharmony_ci		}
3580987da915Sopenharmony_ci	}
3581987da915Sopenharmony_ci	return (err);
3582987da915Sopenharmony_ci}
3583987da915Sopenharmony_ci
3584987da915Sopenharmony_cistatic int undo_update_vcn(ntfs_volume *vol, const struct ACTION_RECORD *action,
3585987da915Sopenharmony_ci			char *buffer)
3586987da915Sopenharmony_ci{
3587987da915Sopenharmony_ci	const char *data;
3588987da915Sopenharmony_ci	u32 target;
3589987da915Sopenharmony_ci	u32 length;
3590987da915Sopenharmony_ci	int err;
3591987da915Sopenharmony_ci
3592987da915Sopenharmony_ci	if (optv > 1)
3593987da915Sopenharmony_ci		printf("-> %s()\n",__func__);
3594987da915Sopenharmony_ci	err = 1;
3595987da915Sopenharmony_ci	data = ((const char*)&action->record)
3596987da915Sopenharmony_ci			+ get_undo_offset(&action->record);
3597987da915Sopenharmony_ci	length = le16_to_cpu(action->record.undo_length);
3598987da915Sopenharmony_ci	if (length == 8) {
3599987da915Sopenharmony_ci		target = le16_to_cpu(action->record.record_offset)
3600987da915Sopenharmony_ci			+ le16_to_cpu(action->record.attribute_offset);
3601987da915Sopenharmony_ci		/* target is right-justified to end of attribute */
3602987da915Sopenharmony_ci		target += getle16(buffer, target + 8) - length;
3603987da915Sopenharmony_ci		err = update_index(vol, action, buffer, data, target, length);
3604987da915Sopenharmony_ci	}
3605987da915Sopenharmony_ci	return (err);
3606987da915Sopenharmony_ci}
3607987da915Sopenharmony_ci
3608987da915Sopenharmony_cistatic int undo_update_mapping(ntfs_volume *vol, const struct ACTION_RECORD *action,
3609987da915Sopenharmony_ci			char *buffer)
3610987da915Sopenharmony_ci{
3611987da915Sopenharmony_ci	LCN lcn;
3612987da915Sopenharmony_ci	const char *data;
3613987da915Sopenharmony_ci	ATTR_RECORD *attr;
3614987da915Sopenharmony_ci	MFT_RECORD *entry;
3615987da915Sopenharmony_ci	u32 target;
3616987da915Sopenharmony_ci	u32 length;
3617987da915Sopenharmony_ci	u32 source;
3618987da915Sopenharmony_ci	u32 alen;
3619987da915Sopenharmony_ci	u32 newused;
3620987da915Sopenharmony_ci	int err;
3621987da915Sopenharmony_ci	int changed;
3622987da915Sopenharmony_ci	int resize;
3623987da915Sopenharmony_ci
3624987da915Sopenharmony_ci	if (optv > 1)
3625987da915Sopenharmony_ci		printf("-> %s()\n",__func__);
3626987da915Sopenharmony_ci	err = 1;
3627987da915Sopenharmony_ci	data = ((const char*)&action->record)
3628987da915Sopenharmony_ci			+ get_undo_offset(&action->record);
3629987da915Sopenharmony_ci	length = le16_to_cpu(action->record.undo_length);
3630987da915Sopenharmony_ci	resize = length - le16_to_cpu(action->record.redo_length);
3631987da915Sopenharmony_ci	target = le16_to_cpu(action->record.record_offset)
3632987da915Sopenharmony_ci		+ le16_to_cpu(action->record.attribute_offset);
3633987da915Sopenharmony_ci	if (optv > 1) {
3634987da915Sopenharmony_ci		lcn = sle64_to_cpu(action->record.lcn_list[0]);
3635987da915Sopenharmony_ci		printf("-> inode %lld lcn 0x%llx target 0x%x new length %d resize %d\n",
3636987da915Sopenharmony_ci			(long long)inode_number(&action->record),
3637987da915Sopenharmony_ci			(long long)lcn, (int)target, (int)length, (int)resize);
3638987da915Sopenharmony_ci	}
3639987da915Sopenharmony_ci// TODO share with redo_update_mapping()
3640987da915Sopenharmony_ci	if (optv > 1) {
3641987da915Sopenharmony_ci		printf("-> existing record :\n");
3642987da915Sopenharmony_ci		dump(&buffer[target], length);
3643987da915Sopenharmony_ci	}
3644987da915Sopenharmony_ci	entry = (MFT_RECORD*)buffer;
3645987da915Sopenharmony_ci	attr = (ATTR_RECORD*)(buffer
3646987da915Sopenharmony_ci			+ le16_to_cpu(action->record.record_offset));
3647987da915Sopenharmony_ci	if (!attr->non_resident) {
3648987da915Sopenharmony_ci		printf("** Error : update_mapping on resident attr\n");
3649987da915Sopenharmony_ci	}
3650987da915Sopenharmony_ci	if (valid_type(attr->type)
3651987da915Sopenharmony_ci	    && attr->non_resident
3652987da915Sopenharmony_ci	    && !(resize & 7)
3653987da915Sopenharmony_ci	    && ((target + length) <= mftrecsz)) {
3654987da915Sopenharmony_ci		changed = memcmp(buffer + target, data, length);
3655987da915Sopenharmony_ci		err = 0;
3656987da915Sopenharmony_ci		if (changed) {
3657987da915Sopenharmony_ci			/* Adjust space for new mapping pairs */
3658987da915Sopenharmony_ci			source = target - resize;
3659987da915Sopenharmony_ci			if (resize > 0) {
3660987da915Sopenharmony_ci				memmove(buffer + target + length,
3661987da915Sopenharmony_ci					buffer + source + length,
3662987da915Sopenharmony_ci					mftrecsz - target - length);
3663987da915Sopenharmony_ci			}
3664987da915Sopenharmony_ci			if (resize < 0) {
3665987da915Sopenharmony_ci				memmove(buffer + target + length,
3666987da915Sopenharmony_ci					buffer + source + length,
3667987da915Sopenharmony_ci					mftrecsz - source - length);
3668987da915Sopenharmony_ci			}
3669987da915Sopenharmony_ci			memcpy(buffer + target, data, length);
3670987da915Sopenharmony_ci				/* Resize the attribute */
3671987da915Sopenharmony_ci			alen = le32_to_cpu(attr->length) + resize;
3672987da915Sopenharmony_ci			attr->length = cpu_to_le32(alen);
3673987da915Sopenharmony_ci				/* Resize the mft record */
3674987da915Sopenharmony_ci			newused = le32_to_cpu(entry->bytes_in_use)
3675987da915Sopenharmony_ci					+ resize;
3676987da915Sopenharmony_ci			entry->bytes_in_use = cpu_to_le32(newused);
3677987da915Sopenharmony_ci				/* Compute the new highest_vcn */
3678987da915Sopenharmony_ci			err = adjust_high_vcn(vol, attr);
3679987da915Sopenharmony_ci			if (optv > 1) {
3680987da915Sopenharmony_ci				printf("-> new record :\n");
3681987da915Sopenharmony_ci				dump(buffer + target, length);
3682987da915Sopenharmony_ci			}
3683987da915Sopenharmony_ci			if (!err) {
3684987da915Sopenharmony_ci				err = write_protected(vol,
3685987da915Sopenharmony_ci					&action->record, buffer,
3686987da915Sopenharmony_ci					mftrecsz);
3687987da915Sopenharmony_ci			}
3688987da915Sopenharmony_ci		}
3689987da915Sopenharmony_ci		if (optv > 1) {
3690987da915Sopenharmony_ci			printf("-> MFT record %s\n",
3691987da915Sopenharmony_ci				(changed ? "updated" : "unchanged"));
3692987da915Sopenharmony_ci		}
3693987da915Sopenharmony_ci	}
3694987da915Sopenharmony_ci	return (err);
3695987da915Sopenharmony_ci}
3696987da915Sopenharmony_ci
3697987da915Sopenharmony_cistatic int undo_update_resident(ntfs_volume *vol,
3698987da915Sopenharmony_ci			const struct ACTION_RECORD *action, char *buffer)
3699987da915Sopenharmony_ci{
3700987da915Sopenharmony_ci	LCN lcn;
3701987da915Sopenharmony_ci	const char *data;
3702987da915Sopenharmony_ci	u32 target;
3703987da915Sopenharmony_ci	u32 length;
3704987da915Sopenharmony_ci	u32 oldlength;
3705987da915Sopenharmony_ci	u32 end;
3706987da915Sopenharmony_ci	u32 undo;
3707987da915Sopenharmony_ci	int err;
3708987da915Sopenharmony_ci	int changed;
3709987da915Sopenharmony_ci
3710987da915Sopenharmony_ci	if (optv > 1)
3711987da915Sopenharmony_ci		printf("-> %s()\n",__func__);
3712987da915Sopenharmony_ci	err = 1;
3713987da915Sopenharmony_ci	end = le32_to_cpu(action->record.client_data_length)
3714987da915Sopenharmony_ci			+ LOG_RECORD_HEAD_SZ;
3715987da915Sopenharmony_ci	length = le16_to_cpu(action->record.undo_length);
3716987da915Sopenharmony_ci	undo = get_undo_offset(&action->record);
3717987da915Sopenharmony_ci	if ((undo + length) > end)
3718987da915Sopenharmony_ci		data = (char*)NULL;
3719987da915Sopenharmony_ci	else
3720987da915Sopenharmony_ci		data = ((const char*)&action->record) + undo;
3721987da915Sopenharmony_ci	oldlength = le16_to_cpu(action->record.redo_length);
3722987da915Sopenharmony_ci	target = le16_to_cpu(action->record.record_offset)
3723987da915Sopenharmony_ci		+ le16_to_cpu(action->record.attribute_offset);
3724987da915Sopenharmony_ci	if (length == oldlength) {
3725987da915Sopenharmony_ci		if (optv > 1) {
3726987da915Sopenharmony_ci			lcn = sle64_to_cpu(action->record.lcn_list[0]);
3727987da915Sopenharmony_ci			printf("-> inode %lld lcn 0x%llx target 0x%x length %d\n",
3728987da915Sopenharmony_ci				(long long)inode_number(&action->record),
3729987da915Sopenharmony_ci				(long long)lcn, (int)target, (int)length);
3730987da915Sopenharmony_ci		}
3731987da915Sopenharmony_ci		if (optv > 1) {
3732987da915Sopenharmony_ci			printf("-> existing record :\n");
3733987da915Sopenharmony_ci			dump(&buffer[target], length);
3734987da915Sopenharmony_ci		}
3735987da915Sopenharmony_ci		if ((target + length) <= mftrecsz) {
3736987da915Sopenharmony_ci			changed = memcmp(buffer + target, data, length);
3737987da915Sopenharmony_ci			err = 0;
3738987da915Sopenharmony_ci			if (changed) {
3739987da915Sopenharmony_ci				memcpy(buffer + target, data, length);
3740987da915Sopenharmony_ci				if (optv > 1) {
3741987da915Sopenharmony_ci					printf("-> new record :\n");
3742987da915Sopenharmony_ci					dump(buffer + target, length);
3743987da915Sopenharmony_ci				}
3744987da915Sopenharmony_ci				err = write_protected(vol, &action->record,
3745987da915Sopenharmony_ci						buffer, mftrecsz);
3746987da915Sopenharmony_ci			}
3747987da915Sopenharmony_ci			if (optv > 1) {
3748987da915Sopenharmony_ci				printf("-> MFT record %s\n",
3749987da915Sopenharmony_ci					(changed ? "updated" : "unchanged"));
3750987da915Sopenharmony_ci			}
3751987da915Sopenharmony_ci		}
3752987da915Sopenharmony_ci	} else {
3753987da915Sopenharmony_ci		if (length > oldlength)
3754987da915Sopenharmony_ci			err = expand_resident(vol, action, buffer, data,
3755987da915Sopenharmony_ci					target, length, oldlength);
3756987da915Sopenharmony_ci		else
3757987da915Sopenharmony_ci			err = shrink_resident(vol, action, buffer, data,
3758987da915Sopenharmony_ci					target, length, oldlength);
3759987da915Sopenharmony_ci	}
3760987da915Sopenharmony_ci	return (err);
3761987da915Sopenharmony_ci}
3762987da915Sopenharmony_ci
3763987da915Sopenharmony_cistatic int undo_update_root_index(ntfs_volume *vol,
3764987da915Sopenharmony_ci			const struct ACTION_RECORD *action, char *buffer)
3765987da915Sopenharmony_ci{
3766987da915Sopenharmony_ci	const char *data;
3767987da915Sopenharmony_ci	const char *expected;
3768987da915Sopenharmony_ci	u32 target;
3769987da915Sopenharmony_ci	u32 length;
3770987da915Sopenharmony_ci	int err;
3771987da915Sopenharmony_ci
3772987da915Sopenharmony_ci	if (optv > 1)
3773987da915Sopenharmony_ci		printf("-> %s()\n",__func__);
3774987da915Sopenharmony_ci	err = 1;
3775987da915Sopenharmony_ci	data = ((const char*)&action->record)
3776987da915Sopenharmony_ci			+ get_undo_offset(&action->record);
3777987da915Sopenharmony_ci	expected = ((const char*)&action->record)
3778987da915Sopenharmony_ci			+ get_redo_offset(&action->record);
3779987da915Sopenharmony_ci	length = le16_to_cpu(action->record.undo_length);
3780987da915Sopenharmony_ci		/* the fixup is right-justified to the name length */
3781987da915Sopenharmony_ci	target = le16_to_cpu(action->record.record_offset)
3782987da915Sopenharmony_ci		+ le16_to_cpu(action->record.attribute_offset)
3783987da915Sopenharmony_ci		+ offsetof(INDEX_ENTRY, key.file_name.file_name_length)
3784987da915Sopenharmony_ci		- length;
3785987da915Sopenharmony_ci	err = change_resident_expect(vol, action, buffer, data, expected,
3786987da915Sopenharmony_ci			target, length, AT_INDEX_ROOT);
3787987da915Sopenharmony_ci	return (err);
3788987da915Sopenharmony_ci}
3789987da915Sopenharmony_ci
3790987da915Sopenharmony_cistatic int undo_update_root_vcn(ntfs_volume *vol,
3791987da915Sopenharmony_ci			const struct ACTION_RECORD *action, char *buffer)
3792987da915Sopenharmony_ci{
3793987da915Sopenharmony_ci	const char *data;
3794987da915Sopenharmony_ci	const char *expected;
3795987da915Sopenharmony_ci	u32 target;
3796987da915Sopenharmony_ci	u32 length;
3797987da915Sopenharmony_ci	int err;
3798987da915Sopenharmony_ci
3799987da915Sopenharmony_ci	if (optv > 1)
3800987da915Sopenharmony_ci		printf("-> %s()\n",__func__);
3801987da915Sopenharmony_ci	err = 1;
3802987da915Sopenharmony_ci	data = ((const char*)&action->record)
3803987da915Sopenharmony_ci			+ get_undo_offset(&action->record);
3804987da915Sopenharmony_ci	expected = ((const char*)&action->record)
3805987da915Sopenharmony_ci			+ get_redo_offset(&action->record);
3806987da915Sopenharmony_ci	length = le16_to_cpu(action->record.undo_length);
3807987da915Sopenharmony_ci	if (length == 8) {
3808987da915Sopenharmony_ci		target = le16_to_cpu(action->record.record_offset)
3809987da915Sopenharmony_ci			+ le16_to_cpu(action->record.attribute_offset);
3810987da915Sopenharmony_ci		/* target is right-justified to end of attribute */
3811987da915Sopenharmony_ci		target += getle16(buffer, target + 8) - length;
3812987da915Sopenharmony_ci		err = change_resident_expect(vol, action, buffer, data,
3813987da915Sopenharmony_ci				expected, target, length, AT_INDEX_ROOT);
3814987da915Sopenharmony_ci	}
3815987da915Sopenharmony_ci	return (err);
3816987da915Sopenharmony_ci}
3817987da915Sopenharmony_ci
3818987da915Sopenharmony_cistatic int undo_update_value(ntfs_volume *vol,
3819987da915Sopenharmony_ci			const struct ACTION_RECORD *action, char *buffer)
3820987da915Sopenharmony_ci{
3821987da915Sopenharmony_ci	LCN lcn;
3822987da915Sopenharmony_ci	const char *data;
3823987da915Sopenharmony_ci	u32 length;
3824987da915Sopenharmony_ci	u32 target;
3825987da915Sopenharmony_ci	u32 count;
3826987da915Sopenharmony_ci	int changed;
3827987da915Sopenharmony_ci	int err;
3828987da915Sopenharmony_ci
3829987da915Sopenharmony_ci	if (optv > 1)
3830987da915Sopenharmony_ci		printf("-> %s()\n",__func__);
3831987da915Sopenharmony_ci	err = 1;
3832987da915Sopenharmony_ci	data = ((const char*)&action->record)
3833987da915Sopenharmony_ci				+ get_undo_offset(&action->record);
3834987da915Sopenharmony_ci	length = le16_to_cpu(action->record.undo_length);
3835987da915Sopenharmony_ci	target = le16_to_cpu(action->record.record_offset)
3836987da915Sopenharmony_ci		+ le16_to_cpu(action->record.attribute_offset);
3837987da915Sopenharmony_ci	count = le16_to_cpu(action->record.lcns_to_follow);
3838987da915Sopenharmony_ci	if (optv > 1) {
3839987da915Sopenharmony_ci		lcn = sle64_to_cpu(action->record.lcn_list[0]);
3840987da915Sopenharmony_ci		printf("-> lcn 0x%llx target 0x%x length %d\n",
3841987da915Sopenharmony_ci			(long long)lcn, (int)target, (int)length);
3842987da915Sopenharmony_ci	}
3843987da915Sopenharmony_ci	if (length) {
3844987da915Sopenharmony_ci		if (optv > 1) {
3845987da915Sopenharmony_ci			printf("-> existing record :\n");
3846987da915Sopenharmony_ci			dump(&buffer[target], length);
3847987da915Sopenharmony_ci		}
3848987da915Sopenharmony_ci		if ((target + length) <= (count << clusterbits)) {
3849987da915Sopenharmony_ci			changed = memcmp(buffer + target, data, length);
3850987da915Sopenharmony_ci			err = 0;
3851987da915Sopenharmony_ci			if (changed) {
3852987da915Sopenharmony_ci				memcpy(buffer + target, data, length);
3853987da915Sopenharmony_ci				if (optv > 1) {
3854987da915Sopenharmony_ci					printf("-> new record :\n");
3855987da915Sopenharmony_ci					dump(buffer + target, length);
3856987da915Sopenharmony_ci				}
3857987da915Sopenharmony_ci				err = write_raw(vol, &action->record, buffer);
3858987da915Sopenharmony_ci			}
3859987da915Sopenharmony_ci			if (optv > 1) {
3860987da915Sopenharmony_ci				printf("-> data record %s\n",
3861987da915Sopenharmony_ci					(changed ? "updated" : "unchanged"));
3862987da915Sopenharmony_ci			}
3863987da915Sopenharmony_ci		}
3864987da915Sopenharmony_ci	} else {
3865987da915Sopenharmony_ci		/*
3866987da915Sopenharmony_ci		 * No undo data, we cannot undo, sometimes the redo
3867987da915Sopenharmony_ci		 * data even overflows from record.
3868987da915Sopenharmony_ci		 * Just ignore for now.
3869987da915Sopenharmony_ci		 */
3870987da915Sopenharmony_ci		if (optv)
3871987da915Sopenharmony_ci			printf("Cannot undo, there is no undo data\n");
3872987da915Sopenharmony_ci		err = 0;
3873987da915Sopenharmony_ci	}
3874987da915Sopenharmony_ci
3875987da915Sopenharmony_ci	return (err);
3876987da915Sopenharmony_ci}
3877987da915Sopenharmony_ci
3878987da915Sopenharmony_cistatic int undo_write_end(ntfs_volume *vol,
3879987da915Sopenharmony_ci			const struct ACTION_RECORD *action, char *buffer)
3880987da915Sopenharmony_ci{
3881987da915Sopenharmony_ci	LCN lcn;
3882987da915Sopenharmony_ci	const char *data;
3883987da915Sopenharmony_ci	u32 target;
3884987da915Sopenharmony_ci	u32 length;
3885987da915Sopenharmony_ci	u32 oldlength;
3886987da915Sopenharmony_ci	u32 end;
3887987da915Sopenharmony_ci	u32 undo;
3888987da915Sopenharmony_ci	int err;
3889987da915Sopenharmony_ci	int changed;
3890987da915Sopenharmony_ci
3891987da915Sopenharmony_ci	if (optv > 1)
3892987da915Sopenharmony_ci		printf("-> %s()\n",__func__);
3893987da915Sopenharmony_ci	err = 1;
3894987da915Sopenharmony_ci	end = le32_to_cpu(action->record.client_data_length)
3895987da915Sopenharmony_ci			+ LOG_RECORD_HEAD_SZ;
3896987da915Sopenharmony_ci	length = le16_to_cpu(action->record.undo_length);
3897987da915Sopenharmony_ci	undo = get_undo_offset(&action->record);
3898987da915Sopenharmony_ci	if ((undo + length) > end)
3899987da915Sopenharmony_ci		data = (char*)NULL;
3900987da915Sopenharmony_ci	else
3901987da915Sopenharmony_ci		data = ((const char*)&action->record) + undo;
3902987da915Sopenharmony_ci	oldlength = le16_to_cpu(action->record.redo_length);
3903987da915Sopenharmony_ci	target = le16_to_cpu(action->record.record_offset)
3904987da915Sopenharmony_ci		+ le16_to_cpu(action->record.attribute_offset);
3905987da915Sopenharmony_ci	if (length == oldlength) {
3906987da915Sopenharmony_ci		if (optv > 1) {
3907987da915Sopenharmony_ci			lcn = sle64_to_cpu(action->record.lcn_list[0]);
3908987da915Sopenharmony_ci			printf("-> inode %lld lcn 0x%llx target 0x%x"
3909987da915Sopenharmony_ci				" length %d\n",
3910987da915Sopenharmony_ci				(long long)inode_number(&action->record),
3911987da915Sopenharmony_ci				(long long)lcn, (int)target, (int)length);
3912987da915Sopenharmony_ci		}
3913987da915Sopenharmony_ci		if (optv > 1) {
3914987da915Sopenharmony_ci			printf("-> existing record :\n");
3915987da915Sopenharmony_ci			dump(&buffer[target], length);
3916987da915Sopenharmony_ci		}
3917987da915Sopenharmony_ci		if ((target + length) <= mftrecsz) {
3918987da915Sopenharmony_ci			changed = memcmp(buffer + target, data, length);
3919987da915Sopenharmony_ci			err = 0;
3920987da915Sopenharmony_ci			if (changed) {
3921987da915Sopenharmony_ci				memcpy(buffer + target, data, length);
3922987da915Sopenharmony_ci				if (optv > 1) {
3923987da915Sopenharmony_ci					printf("-> new record :\n");
3924987da915Sopenharmony_ci					dump(buffer + target, length);
3925987da915Sopenharmony_ci				}
3926987da915Sopenharmony_ci				err = write_protected(vol, &action->record,
3927987da915Sopenharmony_ci						buffer, mftrecsz);
3928987da915Sopenharmony_ci			}
3929987da915Sopenharmony_ci			if (optv > 1) {
3930987da915Sopenharmony_ci				printf("-> MFT record %s\n",
3931987da915Sopenharmony_ci					(changed ? "updated" : "unchanged"));
3932987da915Sopenharmony_ci			}
3933987da915Sopenharmony_ci		}
3934987da915Sopenharmony_ci	} else {
3935987da915Sopenharmony_ci		if (length > oldlength)
3936987da915Sopenharmony_ci			err = add_resident(vol, action, buffer, data,
3937987da915Sopenharmony_ci					target, length, oldlength);
3938987da915Sopenharmony_ci		else
3939987da915Sopenharmony_ci			err = delete_resident(vol, action, buffer, data,
3940987da915Sopenharmony_ci					target, length, oldlength);
3941987da915Sopenharmony_ci	}
3942987da915Sopenharmony_ci	return (err);
3943987da915Sopenharmony_ci}
3944987da915Sopenharmony_ci
3945987da915Sopenharmony_cistatic int undo_write_index(ntfs_volume *vol,
3946987da915Sopenharmony_ci			const struct ACTION_RECORD *action, char *buffer)
3947987da915Sopenharmony_ci{
3948987da915Sopenharmony_ci	LCN lcn;
3949987da915Sopenharmony_ci	const char *data;
3950987da915Sopenharmony_ci	u32 target;
3951987da915Sopenharmony_ci	u32 length;
3952987da915Sopenharmony_ci	u32 oldlength;
3953987da915Sopenharmony_ci	u32 end;
3954987da915Sopenharmony_ci	u32 undo;
3955987da915Sopenharmony_ci	int err;
3956987da915Sopenharmony_ci	int changed;
3957987da915Sopenharmony_ci
3958987da915Sopenharmony_ci	if (optv > 1)
3959987da915Sopenharmony_ci		printf("-> %s()\n",__func__);
3960987da915Sopenharmony_ci	err = 1;
3961987da915Sopenharmony_ci	end = le32_to_cpu(action->record.client_data_length)
3962987da915Sopenharmony_ci			+ LOG_RECORD_HEAD_SZ;
3963987da915Sopenharmony_ci	length = le16_to_cpu(action->record.undo_length);
3964987da915Sopenharmony_ci	undo = get_undo_offset(&action->record);
3965987da915Sopenharmony_ci	if ((undo + length) > end)
3966987da915Sopenharmony_ci		data = (char*)NULL;
3967987da915Sopenharmony_ci	else
3968987da915Sopenharmony_ci		data = ((const char*)&action->record) + undo;
3969987da915Sopenharmony_ci	oldlength = le16_to_cpu(action->record.redo_length);
3970987da915Sopenharmony_ci	target = le16_to_cpu(action->record.record_offset)
3971987da915Sopenharmony_ci		+ le16_to_cpu(action->record.attribute_offset);
3972987da915Sopenharmony_ci	if (length == oldlength) {
3973987da915Sopenharmony_ci		if (optv > 1) {
3974987da915Sopenharmony_ci			lcn = sle64_to_cpu(action->record.lcn_list[0]);
3975987da915Sopenharmony_ci			printf("-> inode %lld lcn 0x%llx target 0x%x"
3976987da915Sopenharmony_ci				" length %d\n",
3977987da915Sopenharmony_ci				(long long)inode_number(&action->record),
3978987da915Sopenharmony_ci				(long long)lcn, (int)target, (int)length);
3979987da915Sopenharmony_ci		}
3980987da915Sopenharmony_ci		if (optv > 1) {
3981987da915Sopenharmony_ci			printf("-> existing record :\n");
3982987da915Sopenharmony_ci			dump(&buffer[target], length);
3983987da915Sopenharmony_ci		}
3984987da915Sopenharmony_ci		if ((target + length) <= mftrecsz) {
3985987da915Sopenharmony_ci			changed = memcmp(buffer + target, data, length);
3986987da915Sopenharmony_ci			err = 0;
3987987da915Sopenharmony_ci			if (changed) {
3988987da915Sopenharmony_ci				memcpy(buffer + target, data, length);
3989987da915Sopenharmony_ci				if (optv > 1) {
3990987da915Sopenharmony_ci					printf("-> new record :\n");
3991987da915Sopenharmony_ci					dump(buffer + target, length);
3992987da915Sopenharmony_ci				}
3993987da915Sopenharmony_ci				err = write_protected(vol, &action->record,
3994987da915Sopenharmony_ci						buffer, mftrecsz);
3995987da915Sopenharmony_ci			}
3996987da915Sopenharmony_ci			if (optv > 1) {
3997987da915Sopenharmony_ci				printf("-> MFT record %s\n",
3998987da915Sopenharmony_ci					(changed ? "updated" : "unchanged"));
3999987da915Sopenharmony_ci			}
4000987da915Sopenharmony_ci		}
4001987da915Sopenharmony_ci	} else {
4002987da915Sopenharmony_ci		if (length > oldlength)
4003987da915Sopenharmony_ci			err = add_non_resident(/*vol, action, data,
4004987da915Sopenharmony_ci					target, length, oldlength*/);
4005987da915Sopenharmony_ci		else
4006987da915Sopenharmony_ci			err = delete_non_resident(/*vol, action, data,
4007987da915Sopenharmony_ci					target, length, oldlength*/);
4008987da915Sopenharmony_ci	}
4009987da915Sopenharmony_ci	return (err);
4010987da915Sopenharmony_ci}
4011987da915Sopenharmony_ci
4012987da915Sopenharmony_cienum ACTION_KIND { ON_NONE, ON_MFT, ON_INDX, ON_RAW } ;
4013987da915Sopenharmony_ci
4014987da915Sopenharmony_cistatic enum ACTION_KIND get_action_kind(const struct ACTION_RECORD *action)
4015987da915Sopenharmony_ci{
4016987da915Sopenharmony_ci	struct ATTR *pa;
4017987da915Sopenharmony_ci	const char *data;
4018987da915Sopenharmony_ci	enum ACTION_KIND kind;
4019987da915Sopenharmony_ci		/*
4020987da915Sopenharmony_ci		 * If we are sure the action was defined by Vista
4021987da915Sopenharmony_ci		 * or subsequent, just use attribute_flags.
4022987da915Sopenharmony_ci		 * Unfortunately, only on some cases we can determine
4023987da915Sopenharmony_ci		 * the action was defined by Win10 (or subsequent).
4024987da915Sopenharmony_ci		 */
4025987da915Sopenharmony_ci	if (action->record.log_record_flags
4026987da915Sopenharmony_ci			& (LOG_RECORD_DELETING | LOG_RECORD_ADDING)) {
4027987da915Sopenharmony_ci		if (action->record.attribute_flags & ACTS_ON_INDX)
4028987da915Sopenharmony_ci			kind = ON_INDX;
4029987da915Sopenharmony_ci		else
4030987da915Sopenharmony_ci			if (action->record.attribute_flags & ACTS_ON_MFT)
4031987da915Sopenharmony_ci				kind = ON_MFT;
4032987da915Sopenharmony_ci			else
4033987da915Sopenharmony_ci				kind = ON_RAW;
4034987da915Sopenharmony_ci	} else {
4035987da915Sopenharmony_ci		/*
4036987da915Sopenharmony_ci		 * In other cases, we have to rely on the attribute
4037987da915Sopenharmony_ci		 * definition, but this has defects when undoing.
4038987da915Sopenharmony_ci		 */
4039987da915Sopenharmony_ci		pa = getattrentry(le16_to_cpu(
4040987da915Sopenharmony_ci					action->record.target_attribute),0);
4041987da915Sopenharmony_ci		if (!pa || !pa->type) {
4042987da915Sopenharmony_ci		/*
4043987da915Sopenharmony_ci		 * Even when the attribute has not been recorded,
4044987da915Sopenharmony_ci		 * we can sometimes tell the record does not apply
4045987da915Sopenharmony_ci		 * to MFT or INDX : such records always have a zero
4046987da915Sopenharmony_ci		 * record_offset, and if attribute_offset is zero, their
4047987da915Sopenharmony_ci		 * magic can be checked. If neither condition is true,
4048987da915Sopenharmony_ci		 * the action cannot apply to MFT or INDX.
4049987da915Sopenharmony_ci		 * (this is useful for undoing)
4050987da915Sopenharmony_ci		 */
4051987da915Sopenharmony_ci			data = (const char*)&action->record
4052987da915Sopenharmony_ci				+ get_redo_offset(&action->record);
4053987da915Sopenharmony_ci			if (action->record.record_offset
4054987da915Sopenharmony_ci			    || (!action->record.attribute_offset
4055987da915Sopenharmony_ci				&& (le16_to_cpu(action->record.redo_length)
4056987da915Sopenharmony_ci									>= 4)
4057987da915Sopenharmony_ci				&& memcmp(data,"FILE",4)
4058987da915Sopenharmony_ci				&& memcmp(data,"INDX",4))) {
4059987da915Sopenharmony_ci					kind = ON_RAW;
4060987da915Sopenharmony_ci			} else {
4061987da915Sopenharmony_ci				printf("** Error : attribute 0x%x"
4062987da915Sopenharmony_ci					" is not defined\n",
4063987da915Sopenharmony_ci					(int)le16_to_cpu(
4064987da915Sopenharmony_ci					action->record.target_attribute));
4065987da915Sopenharmony_ci				kind = ON_NONE;
4066987da915Sopenharmony_ci			}
4067987da915Sopenharmony_ci		} else {
4068987da915Sopenharmony_ci			if (pa->type == AT_INDEX_ALLOCATION)
4069987da915Sopenharmony_ci				kind = ON_INDX;
4070987da915Sopenharmony_ci			else
4071987da915Sopenharmony_ci				kind = ON_RAW;
4072987da915Sopenharmony_ci		}
4073987da915Sopenharmony_ci	}
4074987da915Sopenharmony_ci	return (kind);
4075987da915Sopenharmony_ci}
4076987da915Sopenharmony_ci
4077987da915Sopenharmony_ci
4078987da915Sopenharmony_ci/*
4079987da915Sopenharmony_ci *		Display the redo actions which were executed
4080987da915Sopenharmony_ci *
4081987da915Sopenharmony_ci *	Useful for getting indications on the coverage of a test
4082987da915Sopenharmony_ci */
4083987da915Sopenharmony_ci
4084987da915Sopenharmony_civoid show_redos(void)
4085987da915Sopenharmony_ci{
4086987da915Sopenharmony_ci	int i;
4087987da915Sopenharmony_ci
4088987da915Sopenharmony_ci	if (optv && redos_met) {
4089987da915Sopenharmony_ci		printf("Redo actions which were executed :\n");
4090987da915Sopenharmony_ci		for (i=0; i<64; i++)
4091987da915Sopenharmony_ci			if ((((u64)1) << i) & redos_met)
4092987da915Sopenharmony_ci				printf("%s\n", actionname(i));
4093987da915Sopenharmony_ci	}
4094987da915Sopenharmony_ci}
4095987da915Sopenharmony_ci
4096987da915Sopenharmony_cistatic int distribute_redos(ntfs_volume *vol,
4097987da915Sopenharmony_ci			const struct ACTION_RECORD *action, char *buffer)
4098987da915Sopenharmony_ci{
4099987da915Sopenharmony_ci	int rop, uop;
4100987da915Sopenharmony_ci	int err;
4101987da915Sopenharmony_ci
4102987da915Sopenharmony_ci	err = 0;
4103987da915Sopenharmony_ci	rop = le16_to_cpu(action->record.redo_operation);
4104987da915Sopenharmony_ci	uop = le16_to_cpu(action->record.undo_operation);
4105987da915Sopenharmony_ci	switch (rop) {
4106987da915Sopenharmony_ci	case AddIndexEntryAllocation :
4107987da915Sopenharmony_ci		if (action->record.undo_operation
4108987da915Sopenharmony_ci		    == const_cpu_to_le16(DeleteIndexEntryAllocation))
4109987da915Sopenharmony_ci			err = redo_add_index(vol, action, buffer);
4110987da915Sopenharmony_ci		break;
4111987da915Sopenharmony_ci	case AddIndexEntryRoot :
4112987da915Sopenharmony_ci		if (action->record.undo_operation
4113987da915Sopenharmony_ci		        == const_cpu_to_le16(DeleteIndexEntryRoot))
4114987da915Sopenharmony_ci			err = redo_add_root_index(vol, action, buffer);
4115987da915Sopenharmony_ci		break;
4116987da915Sopenharmony_ci	case ClearBitsInNonResidentBitMap :
4117987da915Sopenharmony_ci		if ((action->record.undo_operation
4118987da915Sopenharmony_ci			== const_cpu_to_le16(SetBitsInNonResidentBitMap))
4119987da915Sopenharmony_ci		    || (action->record.undo_operation
4120987da915Sopenharmony_ci			== const_cpu_to_le16(CompensationlogRecord)))
4121987da915Sopenharmony_ci			err = redo_force_bits(vol, action, buffer);
4122987da915Sopenharmony_ci		break;
4123987da915Sopenharmony_ci	case CompensationlogRecord :
4124987da915Sopenharmony_ci		if (action->record.undo_operation
4125987da915Sopenharmony_ci		    == const_cpu_to_le16(Noop))
4126987da915Sopenharmony_ci			err = redo_compensate(vol, action, buffer);
4127987da915Sopenharmony_ci		break;
4128987da915Sopenharmony_ci	case CreateAttribute :
4129987da915Sopenharmony_ci		if ((action->record.undo_operation
4130987da915Sopenharmony_ci			== const_cpu_to_le16(DeleteAttribute))
4131987da915Sopenharmony_ci		    || (action->record.undo_operation
4132987da915Sopenharmony_ci			== const_cpu_to_le16(CompensationlogRecord)))
4133987da915Sopenharmony_ci			err = redo_create_attribute(vol, action, buffer);
4134987da915Sopenharmony_ci		break;
4135987da915Sopenharmony_ci	case DeallocateFileRecordSegment :
4136987da915Sopenharmony_ci		if ((action->record.undo_operation
4137987da915Sopenharmony_ci			== const_cpu_to_le16(InitializeFileRecordSegment))
4138987da915Sopenharmony_ci		    || (action->record.undo_operation
4139987da915Sopenharmony_ci			== const_cpu_to_le16(CompensationlogRecord)))
4140987da915Sopenharmony_ci			err = redo_delete_file(vol, action, buffer);
4141987da915Sopenharmony_ci		break;
4142987da915Sopenharmony_ci	case DeleteAttribute :
4143987da915Sopenharmony_ci		if ((action->record.undo_operation
4144987da915Sopenharmony_ci			== const_cpu_to_le16(CreateAttribute))
4145987da915Sopenharmony_ci		    || (action->record.undo_operation
4146987da915Sopenharmony_ci			== const_cpu_to_le16(CompensationlogRecord)))
4147987da915Sopenharmony_ci			err = redo_delete_attribute(vol, action, buffer);
4148987da915Sopenharmony_ci		break;
4149987da915Sopenharmony_ci	case DeleteIndexEntryAllocation :
4150987da915Sopenharmony_ci		if ((action->record.undo_operation
4151987da915Sopenharmony_ci			== const_cpu_to_le16(AddIndexEntryAllocation))
4152987da915Sopenharmony_ci		    || (action->record.undo_operation
4153987da915Sopenharmony_ci			== const_cpu_to_le16(CompensationlogRecord)))
4154987da915Sopenharmony_ci			err = redo_delete_index(vol, action, buffer);
4155987da915Sopenharmony_ci		break;
4156987da915Sopenharmony_ci	case DeleteIndexEntryRoot :
4157987da915Sopenharmony_ci		if ((action->record.undo_operation
4158987da915Sopenharmony_ci			== const_cpu_to_le16(AddIndexEntryRoot))
4159987da915Sopenharmony_ci		    || (action->record.undo_operation
4160987da915Sopenharmony_ci			== const_cpu_to_le16(CompensationlogRecord)))
4161987da915Sopenharmony_ci			err = redo_delete_root_index(vol, action, buffer);
4162987da915Sopenharmony_ci		break;
4163987da915Sopenharmony_ci	case InitializeFileRecordSegment :
4164987da915Sopenharmony_ci		if (action->record.undo_operation
4165987da915Sopenharmony_ci		        == const_cpu_to_le16(Noop))
4166987da915Sopenharmony_ci			err = redo_create_file(vol, action, buffer);
4167987da915Sopenharmony_ci		break;
4168987da915Sopenharmony_ci	case OpenNonResidentAttribute :
4169987da915Sopenharmony_ci		if (action->record.undo_operation
4170987da915Sopenharmony_ci		        == const_cpu_to_le16(Noop))
4171987da915Sopenharmony_ci			err = redo_open_attribute(vol, action);
4172987da915Sopenharmony_ci		break;
4173987da915Sopenharmony_ci	case SetBitsInNonResidentBitMap :
4174987da915Sopenharmony_ci		if (action->record.undo_operation
4175987da915Sopenharmony_ci		    == const_cpu_to_le16(ClearBitsInNonResidentBitMap))
4176987da915Sopenharmony_ci			err = redo_force_bits(vol, action, buffer);
4177987da915Sopenharmony_ci		break;
4178987da915Sopenharmony_ci	case SetIndexEntryVcnAllocation :
4179987da915Sopenharmony_ci		if ((action->record.undo_operation
4180987da915Sopenharmony_ci			== const_cpu_to_le16(SetIndexEntryVcnAllocation))
4181987da915Sopenharmony_ci		    || (action->record.undo_operation
4182987da915Sopenharmony_ci			== const_cpu_to_le16(CompensationlogRecord)))
4183987da915Sopenharmony_ci			err = redo_update_vcn(vol, action, buffer);
4184987da915Sopenharmony_ci		break;
4185987da915Sopenharmony_ci	case SetIndexEntryVcnRoot :
4186987da915Sopenharmony_ci		if (action->record.undo_operation
4187987da915Sopenharmony_ci		        == const_cpu_to_le16(SetIndexEntryVcnRoot))
4188987da915Sopenharmony_ci			err = redo_update_root_vcn(vol, action, buffer);
4189987da915Sopenharmony_ci		break;
4190987da915Sopenharmony_ci	case SetNewAttributeSizes :
4191987da915Sopenharmony_ci		if ((action->record.undo_operation
4192987da915Sopenharmony_ci			== const_cpu_to_le16(SetNewAttributeSizes))
4193987da915Sopenharmony_ci		    || (action->record.undo_operation
4194987da915Sopenharmony_ci			== const_cpu_to_le16(CompensationlogRecord)))
4195987da915Sopenharmony_ci			err = redo_sizes(vol, action, buffer);
4196987da915Sopenharmony_ci		break;
4197987da915Sopenharmony_ci	case UpdateFileNameAllocation :
4198987da915Sopenharmony_ci		if ((action->record.undo_operation
4199987da915Sopenharmony_ci			== const_cpu_to_le16(UpdateFileNameAllocation))
4200987da915Sopenharmony_ci		    || (action->record.undo_operation
4201987da915Sopenharmony_ci			== const_cpu_to_le16(CompensationlogRecord)))
4202987da915Sopenharmony_ci			err = redo_update_index(vol, action, buffer);
4203987da915Sopenharmony_ci		break;
4204987da915Sopenharmony_ci	case UpdateFileNameRoot :
4205987da915Sopenharmony_ci		if ((action->record.undo_operation
4206987da915Sopenharmony_ci			== const_cpu_to_le16(UpdateFileNameRoot))
4207987da915Sopenharmony_ci		    || (action->record.undo_operation
4208987da915Sopenharmony_ci			== const_cpu_to_le16(CompensationlogRecord)))
4209987da915Sopenharmony_ci			err = redo_update_root_index(vol, action, buffer);
4210987da915Sopenharmony_ci		break;
4211987da915Sopenharmony_ci	case UpdateMappingPairs :
4212987da915Sopenharmony_ci		if (action->record.undo_operation
4213987da915Sopenharmony_ci		    == const_cpu_to_le16(UpdateMappingPairs))
4214987da915Sopenharmony_ci			err = redo_update_mapping(vol, action, buffer);
4215987da915Sopenharmony_ci		break;
4216987da915Sopenharmony_ci	case UpdateNonResidentValue :
4217987da915Sopenharmony_ci		switch (get_action_kind(action)) {
4218987da915Sopenharmony_ci		case ON_INDX :
4219987da915Sopenharmony_ci			err = redo_update_index_value(vol, action, buffer);
4220987da915Sopenharmony_ci			break;
4221987da915Sopenharmony_ci		case ON_RAW :
4222987da915Sopenharmony_ci			err = redo_update_value(vol, action, buffer);
4223987da915Sopenharmony_ci			break;
4224987da915Sopenharmony_ci		default :
4225987da915Sopenharmony_ci			printf("** Bad attribute type\n");
4226987da915Sopenharmony_ci			err = 1;
4227987da915Sopenharmony_ci		}
4228987da915Sopenharmony_ci		break;
4229987da915Sopenharmony_ci	case UpdateResidentValue :
4230987da915Sopenharmony_ci		if ((action->record.undo_operation
4231987da915Sopenharmony_ci			== const_cpu_to_le16(UpdateResidentValue))
4232987da915Sopenharmony_ci		    || (action->record.undo_operation
4233987da915Sopenharmony_ci			== const_cpu_to_le16(CompensationlogRecord)))
4234987da915Sopenharmony_ci			err = redo_update_resident(vol, action, buffer);
4235987da915Sopenharmony_ci		break;
4236987da915Sopenharmony_ci	case Win10Action37 :
4237987da915Sopenharmony_ci		if (action->record.undo_operation
4238987da915Sopenharmony_ci		    == const_cpu_to_le16(Noop))
4239987da915Sopenharmony_ci			err = redo_action37(vol, action, buffer);
4240987da915Sopenharmony_ci		break;
4241987da915Sopenharmony_ci	case WriteEndofFileRecordSegment :
4242987da915Sopenharmony_ci		if (action->record.undo_operation
4243987da915Sopenharmony_ci		    == const_cpu_to_le16(WriteEndofFileRecordSegment))
4244987da915Sopenharmony_ci			err = redo_write_end(vol, action, buffer);
4245987da915Sopenharmony_ci		break;
4246987da915Sopenharmony_ci	case WriteEndOfIndexBuffer :
4247987da915Sopenharmony_ci		if ((action->record.undo_operation
4248987da915Sopenharmony_ci			== const_cpu_to_le16(WriteEndOfIndexBuffer))
4249987da915Sopenharmony_ci		    || (action->record.undo_operation
4250987da915Sopenharmony_ci			== const_cpu_to_le16(CompensationlogRecord)))
4251987da915Sopenharmony_ci			err = redo_write_index(vol, action, buffer);
4252987da915Sopenharmony_ci		break;
4253987da915Sopenharmony_ci	case AttributeNamesDump :
4254987da915Sopenharmony_ci	case DirtyPageTableDump :
4255987da915Sopenharmony_ci	case ForgetTransaction :
4256987da915Sopenharmony_ci	case Noop :
4257987da915Sopenharmony_ci	case OpenAttributeTableDump :
4258987da915Sopenharmony_ci		break;
4259987da915Sopenharmony_ci	default :
4260987da915Sopenharmony_ci		printf("** Unsupported redo %s\n", actionname(rop));
4261987da915Sopenharmony_ci		err = 1;
4262987da915Sopenharmony_ci		break;
4263987da915Sopenharmony_ci	}
4264987da915Sopenharmony_ci	redos_met |= ((u64)1) << rop;
4265987da915Sopenharmony_ci	if (err)
4266987da915Sopenharmony_ci		printf("* Redoing action %d %s (%s) failed\n",
4267987da915Sopenharmony_ci			action->num,actionname(rop), actionname(uop));
4268987da915Sopenharmony_ci	return (err);
4269987da915Sopenharmony_ci}
4270987da915Sopenharmony_ci
4271987da915Sopenharmony_ci/*
4272987da915Sopenharmony_ci *		Redo a single action
4273987da915Sopenharmony_ci *
4274987da915Sopenharmony_ci *	The record the action acts on is read and, when it is an MFT or
4275987da915Sopenharmony_ci *	INDX one, the need for redoing is checked.
4276987da915Sopenharmony_ci *
4277987da915Sopenharmony_ci *	When this is an action which creates a new MFT or INDX record
4278987da915Sopenharmony_ci *	and the old one cannot be read (usually because it was not
4279987da915Sopenharmony_ci *	initialized), a zeroed buffer is allocated.
4280987da915Sopenharmony_ci */
4281987da915Sopenharmony_ci
4282987da915Sopenharmony_cistatic int play_one_redo(ntfs_volume *vol, const struct ACTION_RECORD *action)
4283987da915Sopenharmony_ci{
4284987da915Sopenharmony_ci	MFT_RECORD *entry;
4285987da915Sopenharmony_ci	INDEX_BLOCK *indx;
4286987da915Sopenharmony_ci	char *buffer;
4287987da915Sopenharmony_ci	s64 this_lsn;
4288987da915Sopenharmony_ci	s64 data_lsn;
4289987da915Sopenharmony_ci	u32 xsize;
4290987da915Sopenharmony_ci	int err;
4291987da915Sopenharmony_ci	BOOL warn;
4292987da915Sopenharmony_ci	BOOL executed;
4293987da915Sopenharmony_ci	enum ACTION_KIND kind;
4294987da915Sopenharmony_ci	u16 rop;
4295987da915Sopenharmony_ci	u16 uop;
4296987da915Sopenharmony_ci
4297987da915Sopenharmony_ci	err = 0;
4298987da915Sopenharmony_ci	rop = le16_to_cpu(action->record.redo_operation);
4299987da915Sopenharmony_ci	uop = le16_to_cpu(action->record.undo_operation);
4300987da915Sopenharmony_ci	this_lsn = sle64_to_cpu(action->record.this_lsn);
4301987da915Sopenharmony_ci	if (optv)
4302987da915Sopenharmony_ci		printf("Redo action %d %s (%s) 0x%llx\n",
4303987da915Sopenharmony_ci			action->num,
4304987da915Sopenharmony_ci			actionname(rop), actionname(uop),
4305987da915Sopenharmony_ci			(long long)sle64_to_cpu(
4306987da915Sopenharmony_ci				action->record.this_lsn));
4307987da915Sopenharmony_ci	buffer = (char*)NULL;
4308987da915Sopenharmony_ci	switch (rop) {
4309987da915Sopenharmony_ci			/* Actions always acting on MFT */
4310987da915Sopenharmony_ci	case AddIndexEntryRoot :
4311987da915Sopenharmony_ci	case CreateAttribute :
4312987da915Sopenharmony_ci	case DeallocateFileRecordSegment :
4313987da915Sopenharmony_ci	case DeleteAttribute :
4314987da915Sopenharmony_ci	case DeleteIndexEntryRoot :
4315987da915Sopenharmony_ci	case InitializeFileRecordSegment :
4316987da915Sopenharmony_ci	case SetIndexEntryVcnRoot :
4317987da915Sopenharmony_ci	case SetNewAttributeSizes :
4318987da915Sopenharmony_ci	case UpdateFileNameRoot :
4319987da915Sopenharmony_ci	case UpdateMappingPairs :
4320987da915Sopenharmony_ci	case UpdateResidentValue :
4321987da915Sopenharmony_ci	case Win10Action37 :
4322987da915Sopenharmony_ci	case WriteEndofFileRecordSegment :
4323987da915Sopenharmony_ci		kind = ON_MFT;
4324987da915Sopenharmony_ci		break;
4325987da915Sopenharmony_ci			/* Actions always acting on INDX */
4326987da915Sopenharmony_ci	case AddIndexEntryAllocation :
4327987da915Sopenharmony_ci	case DeleteIndexEntryAllocation :
4328987da915Sopenharmony_ci	case SetIndexEntryVcnAllocation :
4329987da915Sopenharmony_ci	case UpdateFileNameAllocation :
4330987da915Sopenharmony_ci	case WriteEndOfIndexBuffer :
4331987da915Sopenharmony_ci		kind = ON_INDX;
4332987da915Sopenharmony_ci		break;
4333987da915Sopenharmony_ci			/* Actions never acting on MFT or INDX */
4334987da915Sopenharmony_ci	case ClearBitsInNonResidentBitMap :
4335987da915Sopenharmony_ci	case SetBitsInNonResidentBitMap :
4336987da915Sopenharmony_ci		kind = ON_RAW;
4337987da915Sopenharmony_ci		break;
4338987da915Sopenharmony_ci			/* Actions which may act on MFT */
4339987da915Sopenharmony_ci	case Noop : /* on MFT if DeallocateFileRecordSegment */
4340987da915Sopenharmony_ci		kind = ON_NONE;
4341987da915Sopenharmony_ci		break;
4342987da915Sopenharmony_ci			/* Actions which may act on INDX */
4343987da915Sopenharmony_ci	case UpdateNonResidentValue :
4344987da915Sopenharmony_ci		/* Known cases : INDX, $SDS, ATTR_LIST */
4345987da915Sopenharmony_ci		kind = get_action_kind(action);
4346987da915Sopenharmony_ci		if (kind == ON_NONE)
4347987da915Sopenharmony_ci			err = 1;
4348987da915Sopenharmony_ci		break;
4349987da915Sopenharmony_ci	case CompensationlogRecord :
4350987da915Sopenharmony_ci	case OpenNonResidentAttribute :
4351987da915Sopenharmony_ci		/* probably not important */
4352987da915Sopenharmony_ci		kind = ON_NONE;
4353987da915Sopenharmony_ci		break;
4354987da915Sopenharmony_ci			/* Actions currently ignored */
4355987da915Sopenharmony_ci	case AttributeNamesDump :
4356987da915Sopenharmony_ci	case DirtyPageTableDump :
4357987da915Sopenharmony_ci	case ForgetTransaction :
4358987da915Sopenharmony_ci	case OpenAttributeTableDump :
4359987da915Sopenharmony_ci	case TransactionTableDump :
4360987da915Sopenharmony_ci		kind = ON_NONE;
4361987da915Sopenharmony_ci		break;
4362987da915Sopenharmony_ci			/* Actions with no known use case */
4363987da915Sopenharmony_ci	case CommitTransaction :
4364987da915Sopenharmony_ci	case DeleteDirtyClusters :
4365987da915Sopenharmony_ci	case EndTopLevelAction :
4366987da915Sopenharmony_ci	case HotFix :
4367987da915Sopenharmony_ci	case PrepareTransaction :
4368987da915Sopenharmony_ci	case UpdateRecordDataAllocation :
4369987da915Sopenharmony_ci	case UpdateRecordDataRoot :
4370987da915Sopenharmony_ci	case Win10Action35 :
4371987da915Sopenharmony_ci	case Win10Action36 :
4372987da915Sopenharmony_ci	default :
4373987da915Sopenharmony_ci		err = 1;
4374987da915Sopenharmony_ci		kind = ON_NONE;
4375987da915Sopenharmony_ci		break;
4376987da915Sopenharmony_ci	}
4377987da915Sopenharmony_ci	executed = FALSE;
4378987da915Sopenharmony_ci	switch (kind) {
4379987da915Sopenharmony_ci	case ON_MFT :
4380987da915Sopenharmony_ci/*
4381987da915Sopenharmony_ci the check below cannot be used on WinXP
4382987da915Sopenharmony_ciif (!(action->record.attribute_flags & ACTS_ON_MFT))
4383987da915Sopenharmony_ciprintf("** %s (action %d) not acting on MFT\n",actionname(rop),(int)action->num);
4384987da915Sopenharmony_ci*/
4385987da915Sopenharmony_ci		/* Check whether data is to be discarded */
4386987da915Sopenharmony_ci		warn = (rop != InitializeFileRecordSegment)
4387987da915Sopenharmony_ci			|| !check_full_mft(action,TRUE);
4388987da915Sopenharmony_ci		buffer = read_protected(vol, &action->record,
4389987da915Sopenharmony_ci					mftrecsz, warn);
4390987da915Sopenharmony_ci		entry = (MFT_RECORD*)buffer;
4391987da915Sopenharmony_ci		if (entry && (entry->magic == magic_FILE)) {
4392987da915Sopenharmony_ci			data_lsn = sle64_to_cpu(entry->lsn);
4393987da915Sopenharmony_ci			/*
4394987da915Sopenharmony_ci			 * Beware of records not updated
4395987da915Sopenharmony_ci			 * during the last session which may
4396987da915Sopenharmony_ci			 * have a stale lsn (consequence
4397987da915Sopenharmony_ci			 * of ntfs-3g resetting the log)
4398987da915Sopenharmony_ci			 */
4399987da915Sopenharmony_ci			executed = ((s64)(data_lsn - this_lsn) >= 0)
4400987da915Sopenharmony_ci			    && (((s64)(data_lsn - latest_lsn)) <= 0)
4401987da915Sopenharmony_ci			    && !exception(action->num);
4402987da915Sopenharmony_ci		} else {
4403987da915Sopenharmony_ci			if (!warn) {
4404987da915Sopenharmony_ci				/* Old record not needed */
4405987da915Sopenharmony_ci				if (!buffer)
4406987da915Sopenharmony_ci					buffer = (char*)calloc(1, mftrecsz);
4407987da915Sopenharmony_ci				if (buffer)
4408987da915Sopenharmony_ci					executed = FALSE;
4409987da915Sopenharmony_ci				else
4410987da915Sopenharmony_ci					err = 1;
4411987da915Sopenharmony_ci			} else {
4412987da915Sopenharmony_ci				printf("** %s (action %d) not"
4413987da915Sopenharmony_ci					" acting on MFT\n",
4414987da915Sopenharmony_ci					actionname(rop),
4415987da915Sopenharmony_ci					(int)action->num);
4416987da915Sopenharmony_ci				err = 1;
4417987da915Sopenharmony_ci			}
4418987da915Sopenharmony_ci		}
4419987da915Sopenharmony_ci		break;
4420987da915Sopenharmony_ci	case ON_INDX :
4421987da915Sopenharmony_ci/*
4422987da915Sopenharmony_ci the check below cannot be used on WinXP
4423987da915Sopenharmony_ciif (!(action->record.attribute_flags & ACTS_ON_INDX))
4424987da915Sopenharmony_ciprintf("** %s (action %d) not acting on INDX\n",actionname(rop),(int)action->num);
4425987da915Sopenharmony_ci*/
4426987da915Sopenharmony_ci		xsize = vol->indx_record_size;
4427987da915Sopenharmony_ci		/* Check whether data is to be discarded */
4428987da915Sopenharmony_ci		warn = (rop != UpdateNonResidentValue)
4429987da915Sopenharmony_ci			|| !check_full_index(action,TRUE);
4430987da915Sopenharmony_ci		buffer = read_protected(vol, &action->record,
4431987da915Sopenharmony_ci						xsize, warn);
4432987da915Sopenharmony_ci		indx = (INDEX_BLOCK*)buffer;
4433987da915Sopenharmony_ci		if (indx && (indx->magic == magic_INDX)) {
4434987da915Sopenharmony_ci			data_lsn = sle64_to_cpu(indx->lsn);
4435987da915Sopenharmony_ci			/*
4436987da915Sopenharmony_ci			 * Beware of records not updated
4437987da915Sopenharmony_ci			 * during the last session which may
4438987da915Sopenharmony_ci			 * have a stale lsn (consequence
4439987da915Sopenharmony_ci			 * of ntfs-3g resetting the log)
4440987da915Sopenharmony_ci			 */
4441987da915Sopenharmony_ci			executed = ((s64)(data_lsn - this_lsn) >= 0)
4442987da915Sopenharmony_ci			    && (((s64)(data_lsn - latest_lsn)) <= 0)
4443987da915Sopenharmony_ci			    && ! exception(action->num);
4444987da915Sopenharmony_ci		} else {
4445987da915Sopenharmony_ci			if (!warn) {
4446987da915Sopenharmony_ci				/* Old record not needed */
4447987da915Sopenharmony_ci				if (!buffer)
4448987da915Sopenharmony_ci					buffer = (char*)calloc(1, xsize);
4449987da915Sopenharmony_ci				if (buffer)
4450987da915Sopenharmony_ci					executed = FALSE;
4451987da915Sopenharmony_ci				else
4452987da915Sopenharmony_ci					err = 1;
4453987da915Sopenharmony_ci			} else {
4454987da915Sopenharmony_ci				printf("** %s (action %d) not"
4455987da915Sopenharmony_ci					" acting on INDX\n",
4456987da915Sopenharmony_ci					actionname(rop),
4457987da915Sopenharmony_ci					(int)action->num);
4458987da915Sopenharmony_ci				err = 1;
4459987da915Sopenharmony_ci			}
4460987da915Sopenharmony_ci		}
4461987da915Sopenharmony_ci		break;
4462987da915Sopenharmony_ci	case ON_RAW :
4463987da915Sopenharmony_ci		if (action->record.attribute_flags
4464987da915Sopenharmony_ci				& (ACTS_ON_INDX | ACTS_ON_MFT)) {
4465987da915Sopenharmony_ci			printf("** Error : action %s on MFT"
4466987da915Sopenharmony_ci				" or INDX\n",
4467987da915Sopenharmony_ci				actionname(rop));
4468987da915Sopenharmony_ci			err = 1;
4469987da915Sopenharmony_ci		} else {
4470987da915Sopenharmony_ci			buffer = read_raw(vol, &action->record);
4471987da915Sopenharmony_ci			if (!buffer)
4472987da915Sopenharmony_ci				err = 1;
4473987da915Sopenharmony_ci		}
4474987da915Sopenharmony_ci		break;
4475987da915Sopenharmony_ci	default :
4476987da915Sopenharmony_ci		buffer = (char*)NULL;
4477987da915Sopenharmony_ci		break;
4478987da915Sopenharmony_ci	}
4479987da915Sopenharmony_ci	if (!err && (!executed || !opts)) {
4480987da915Sopenharmony_ci		err = distribute_redos(vol, action, buffer);
4481987da915Sopenharmony_ci		redocount++;
4482987da915Sopenharmony_ci	} else {
4483987da915Sopenharmony_ci		if (optv)
4484987da915Sopenharmony_ci			printf("Action %d %s (%s) not redone\n",
4485987da915Sopenharmony_ci				action->num,
4486987da915Sopenharmony_ci				actionname(rop),
4487987da915Sopenharmony_ci				actionname(uop));
4488987da915Sopenharmony_ci	}
4489987da915Sopenharmony_ci	if (buffer)
4490987da915Sopenharmony_ci		free(buffer);
4491987da915Sopenharmony_ci	return (err);
4492987da915Sopenharmony_ci}
4493987da915Sopenharmony_ci
4494987da915Sopenharmony_ci
4495987da915Sopenharmony_ci/*
4496987da915Sopenharmony_ci *		Play the redo actions from earliest to latest
4497987da915Sopenharmony_ci *
4498987da915Sopenharmony_ci *	Currently we can only redo the last undone transaction,
4499987da915Sopenharmony_ci *	otherwise the attribute table would be out of phase.
4500987da915Sopenharmony_ci */
4501987da915Sopenharmony_ci
4502987da915Sopenharmony_ciint play_redos(ntfs_volume *vol, const struct ACTION_RECORD *firstaction)
4503987da915Sopenharmony_ci{
4504987da915Sopenharmony_ci	const struct ACTION_RECORD *action;
4505987da915Sopenharmony_ci	int err;
4506987da915Sopenharmony_ci
4507987da915Sopenharmony_ci	err = 0;
4508987da915Sopenharmony_ci	action = firstaction;
4509987da915Sopenharmony_ci	while (action && !err) {
4510987da915Sopenharmony_ci			/* Only committed actions should be redone */
4511987da915Sopenharmony_ci		if ((!optc || within_lcn_range(&action->record))
4512987da915Sopenharmony_ci		    && (action->flags & ACTION_TO_REDO))
4513987da915Sopenharmony_ci			err = play_one_redo(vol, action);
4514987da915Sopenharmony_ci		if (!err)
4515987da915Sopenharmony_ci			action = action->next;
4516987da915Sopenharmony_ci	}
4517987da915Sopenharmony_ci	return (err);
4518987da915Sopenharmony_ci}
4519987da915Sopenharmony_ci
4520987da915Sopenharmony_cistatic int distribute_undos(ntfs_volume *vol, const struct ACTION_RECORD *action,
4521987da915Sopenharmony_ci			char *buffer)
4522987da915Sopenharmony_ci{
4523987da915Sopenharmony_ci	int rop, uop;
4524987da915Sopenharmony_ci	int err;
4525987da915Sopenharmony_ci
4526987da915Sopenharmony_ci	err = 0;
4527987da915Sopenharmony_ci	rop = le16_to_cpu(action->record.redo_operation);
4528987da915Sopenharmony_ci	uop = le16_to_cpu(action->record.undo_operation);
4529987da915Sopenharmony_ci	switch (rop) {
4530987da915Sopenharmony_ci	case AddIndexEntryAllocation :
4531987da915Sopenharmony_ci		if (action->record.undo_operation
4532987da915Sopenharmony_ci		    == const_cpu_to_le16(DeleteIndexEntryAllocation))
4533987da915Sopenharmony_ci			err = undo_add_index(vol, action, buffer);
4534987da915Sopenharmony_ci		break;
4535987da915Sopenharmony_ci	case AddIndexEntryRoot :
4536987da915Sopenharmony_ci		if (action->record.undo_operation
4537987da915Sopenharmony_ci		        == const_cpu_to_le16(DeleteIndexEntryRoot))
4538987da915Sopenharmony_ci			err = undo_add_root_index(vol, action, buffer);
4539987da915Sopenharmony_ci		break;
4540987da915Sopenharmony_ci	case ClearBitsInNonResidentBitMap :
4541987da915Sopenharmony_ci		if (action->record.undo_operation
4542987da915Sopenharmony_ci		    == const_cpu_to_le16(SetBitsInNonResidentBitMap))
4543987da915Sopenharmony_ci			err = undo_force_bits(vol, action, buffer);
4544987da915Sopenharmony_ci		break;
4545987da915Sopenharmony_ci	case CreateAttribute :
4546987da915Sopenharmony_ci		if (action->record.undo_operation
4547987da915Sopenharmony_ci		    == const_cpu_to_le16(DeleteAttribute))
4548987da915Sopenharmony_ci			err = undo_create_attribute(vol, action, buffer);
4549987da915Sopenharmony_ci		break;
4550987da915Sopenharmony_ci	case DeallocateFileRecordSegment :
4551987da915Sopenharmony_ci		if (action->record.undo_operation
4552987da915Sopenharmony_ci		    == const_cpu_to_le16(InitializeFileRecordSegment))
4553987da915Sopenharmony_ci			err = undo_delete_file(vol, action, buffer);
4554987da915Sopenharmony_ci		break;
4555987da915Sopenharmony_ci	case DeleteAttribute :
4556987da915Sopenharmony_ci		if (action->record.undo_operation
4557987da915Sopenharmony_ci		    == const_cpu_to_le16(CreateAttribute))
4558987da915Sopenharmony_ci			err = undo_delete_attribute(vol, action, buffer);
4559987da915Sopenharmony_ci		break;
4560987da915Sopenharmony_ci	case DeleteIndexEntryAllocation :
4561987da915Sopenharmony_ci		if (action->record.undo_operation
4562987da915Sopenharmony_ci		    == const_cpu_to_le16(AddIndexEntryAllocation))
4563987da915Sopenharmony_ci			err = undo_delete_index(vol, action, buffer);
4564987da915Sopenharmony_ci		break;
4565987da915Sopenharmony_ci	case DeleteIndexEntryRoot :
4566987da915Sopenharmony_ci		if (action->record.undo_operation
4567987da915Sopenharmony_ci		        == const_cpu_to_le16(AddIndexEntryRoot))
4568987da915Sopenharmony_ci			err = undo_delete_root_index(vol, action, buffer);
4569987da915Sopenharmony_ci		break;
4570987da915Sopenharmony_ci	case InitializeFileRecordSegment :
4571987da915Sopenharmony_ci		if (action->record.undo_operation
4572987da915Sopenharmony_ci		        == const_cpu_to_le16(Noop))
4573987da915Sopenharmony_ci			err = undo_create_file(vol, action, buffer);
4574987da915Sopenharmony_ci		break;
4575987da915Sopenharmony_ci	case OpenNonResidentAttribute :
4576987da915Sopenharmony_ci		if (action->record.undo_operation
4577987da915Sopenharmony_ci		        == const_cpu_to_le16(Noop))
4578987da915Sopenharmony_ci			err = undo_open_attribute(vol, action);
4579987da915Sopenharmony_ci		break;
4580987da915Sopenharmony_ci	case SetBitsInNonResidentBitMap :
4581987da915Sopenharmony_ci		if (action->record.undo_operation
4582987da915Sopenharmony_ci		    == const_cpu_to_le16(ClearBitsInNonResidentBitMap))
4583987da915Sopenharmony_ci			err = undo_force_bits(vol, action, buffer);
4584987da915Sopenharmony_ci		break;
4585987da915Sopenharmony_ci	case SetIndexEntryVcnAllocation :
4586987da915Sopenharmony_ci		if (action->record.undo_operation
4587987da915Sopenharmony_ci		    == const_cpu_to_le16(SetIndexEntryVcnAllocation))
4588987da915Sopenharmony_ci			err = undo_update_vcn(vol, action, buffer);
4589987da915Sopenharmony_ci		break;
4590987da915Sopenharmony_ci	case SetIndexEntryVcnRoot :
4591987da915Sopenharmony_ci		if (action->record.undo_operation
4592987da915Sopenharmony_ci		        == const_cpu_to_le16(SetIndexEntryVcnRoot))
4593987da915Sopenharmony_ci			err = undo_update_root_vcn(vol, action, buffer);
4594987da915Sopenharmony_ci		break;
4595987da915Sopenharmony_ci	case SetNewAttributeSizes :
4596987da915Sopenharmony_ci		if (action->record.undo_operation
4597987da915Sopenharmony_ci		    == const_cpu_to_le16(SetNewAttributeSizes))
4598987da915Sopenharmony_ci			err = undo_sizes(vol, action, buffer);
4599987da915Sopenharmony_ci		break;
4600987da915Sopenharmony_ci	case UpdateFileNameAllocation :
4601987da915Sopenharmony_ci		if (action->record.undo_operation
4602987da915Sopenharmony_ci		    == const_cpu_to_le16(UpdateFileNameAllocation))
4603987da915Sopenharmony_ci			err = undo_update_index(vol, action, buffer);
4604987da915Sopenharmony_ci		break;
4605987da915Sopenharmony_ci	case UpdateFileNameRoot :
4606987da915Sopenharmony_ci		if (action->record.undo_operation
4607987da915Sopenharmony_ci		        == const_cpu_to_le16(UpdateFileNameRoot))
4608987da915Sopenharmony_ci			err = undo_update_root_index(vol, action, buffer);
4609987da915Sopenharmony_ci		break;
4610987da915Sopenharmony_ci	case UpdateMappingPairs :
4611987da915Sopenharmony_ci		if (action->record.undo_operation
4612987da915Sopenharmony_ci		    == const_cpu_to_le16(UpdateMappingPairs))
4613987da915Sopenharmony_ci			err = undo_update_mapping(vol, action, buffer);
4614987da915Sopenharmony_ci		break;
4615987da915Sopenharmony_ci	case UpdateNonResidentValue :
4616987da915Sopenharmony_ci		switch (get_action_kind(action)) {
4617987da915Sopenharmony_ci		case ON_INDX :
4618987da915Sopenharmony_ci			err = undo_update_index_value(vol, action, buffer);
4619987da915Sopenharmony_ci			break;
4620987da915Sopenharmony_ci		case ON_RAW :
4621987da915Sopenharmony_ci			err = undo_update_value(vol, action, buffer);
4622987da915Sopenharmony_ci			break;
4623987da915Sopenharmony_ci		default :
4624987da915Sopenharmony_ci			printf("** Bad attribute type\n");
4625987da915Sopenharmony_ci			err = 1;
4626987da915Sopenharmony_ci		}
4627987da915Sopenharmony_ci		break;
4628987da915Sopenharmony_ci	case UpdateResidentValue :
4629987da915Sopenharmony_ci		if (action->record.undo_operation
4630987da915Sopenharmony_ci		    == const_cpu_to_le16(UpdateResidentValue))
4631987da915Sopenharmony_ci			err = undo_update_resident(vol, action, buffer);
4632987da915Sopenharmony_ci		break;
4633987da915Sopenharmony_ci	case Win10Action37 :
4634987da915Sopenharmony_ci		if (action->record.undo_operation
4635987da915Sopenharmony_ci		    == const_cpu_to_le16(Noop))
4636987da915Sopenharmony_ci			err = undo_action37(vol, action, buffer);
4637987da915Sopenharmony_ci		break;
4638987da915Sopenharmony_ci	case WriteEndofFileRecordSegment :
4639987da915Sopenharmony_ci		if (action->record.undo_operation
4640987da915Sopenharmony_ci		    == const_cpu_to_le16(WriteEndofFileRecordSegment))
4641987da915Sopenharmony_ci			err = undo_write_end(vol, action, buffer);
4642987da915Sopenharmony_ci		break;
4643987da915Sopenharmony_ci	case WriteEndOfIndexBuffer :
4644987da915Sopenharmony_ci		if (action->record.undo_operation
4645987da915Sopenharmony_ci		    == const_cpu_to_le16(WriteEndOfIndexBuffer))
4646987da915Sopenharmony_ci			err = undo_write_index(vol, action, buffer);
4647987da915Sopenharmony_ci		break;
4648987da915Sopenharmony_ci	case AttributeNamesDump :
4649987da915Sopenharmony_ci	case CompensationlogRecord :
4650987da915Sopenharmony_ci	case DirtyPageTableDump :
4651987da915Sopenharmony_ci	case ForgetTransaction :
4652987da915Sopenharmony_ci	case Noop :
4653987da915Sopenharmony_ci	case OpenAttributeTableDump :
4654987da915Sopenharmony_ci		break;
4655987da915Sopenharmony_ci	default :
4656987da915Sopenharmony_ci		printf("** Unsupported undo %s\n", actionname(rop));
4657987da915Sopenharmony_ci		err = 1;
4658987da915Sopenharmony_ci		break;
4659987da915Sopenharmony_ci	}
4660987da915Sopenharmony_ci	if (err)
4661987da915Sopenharmony_ci		printf("* Undoing action %d %s (%s) failed\n",
4662987da915Sopenharmony_ci			action->num,actionname(rop), actionname(uop));
4663987da915Sopenharmony_ci	return (err);
4664987da915Sopenharmony_ci}
4665987da915Sopenharmony_ci
4666987da915Sopenharmony_ci/*
4667987da915Sopenharmony_ci *		Undo a single action
4668987da915Sopenharmony_ci *
4669987da915Sopenharmony_ci *	The record the action acts on is read and, when it is an MFT or
4670987da915Sopenharmony_ci *	INDX one, the need for undoing is checked.
4671987da915Sopenharmony_ci */
4672987da915Sopenharmony_ci
4673987da915Sopenharmony_cistatic int play_one_undo(ntfs_volume *vol, const struct ACTION_RECORD *action)
4674987da915Sopenharmony_ci{
4675987da915Sopenharmony_ci	MFT_RECORD *entry;
4676987da915Sopenharmony_ci	INDEX_BLOCK *indx;
4677987da915Sopenharmony_ci	char *buffer;
4678987da915Sopenharmony_ci	u32 xsize;
4679987da915Sopenharmony_ci	u16 rop;
4680987da915Sopenharmony_ci	u16 uop;
4681987da915Sopenharmony_ci	int err;
4682987da915Sopenharmony_ci	BOOL executed;
4683987da915Sopenharmony_ci	enum ACTION_KIND kind;
4684987da915Sopenharmony_ci
4685987da915Sopenharmony_ci	err = 0;
4686987da915Sopenharmony_ci	rop = le16_to_cpu(action->record.redo_operation);
4687987da915Sopenharmony_ci	uop = le16_to_cpu(action->record.undo_operation);
4688987da915Sopenharmony_ci	if (optv)
4689987da915Sopenharmony_ci		printf("Undo action %d %s (%s) lsn 0x%llx\n",
4690987da915Sopenharmony_ci			action->num,
4691987da915Sopenharmony_ci			actionname(rop), actionname(uop),
4692987da915Sopenharmony_ci			(long long)sle64_to_cpu(
4693987da915Sopenharmony_ci				action->record.this_lsn));
4694987da915Sopenharmony_ci	buffer = (char*)NULL;
4695987da915Sopenharmony_ci	executed = FALSE;
4696987da915Sopenharmony_ci	kind = ON_NONE;
4697987da915Sopenharmony_ci	switch (rop) {
4698987da915Sopenharmony_ci			/* Actions always acting on MFT */
4699987da915Sopenharmony_ci	case AddIndexEntryRoot :
4700987da915Sopenharmony_ci	case CreateAttribute :
4701987da915Sopenharmony_ci	case DeallocateFileRecordSegment :
4702987da915Sopenharmony_ci	case DeleteAttribute :
4703987da915Sopenharmony_ci	case DeleteIndexEntryRoot :
4704987da915Sopenharmony_ci	case InitializeFileRecordSegment :
4705987da915Sopenharmony_ci	case SetIndexEntryVcnRoot :
4706987da915Sopenharmony_ci	case SetNewAttributeSizes :
4707987da915Sopenharmony_ci	case UpdateFileNameRoot :
4708987da915Sopenharmony_ci	case UpdateMappingPairs :
4709987da915Sopenharmony_ci	case UpdateResidentValue :
4710987da915Sopenharmony_ci	case Win10Action37 :
4711987da915Sopenharmony_ci	case WriteEndofFileRecordSegment :
4712987da915Sopenharmony_ci		kind = ON_MFT;
4713987da915Sopenharmony_ci		break;
4714987da915Sopenharmony_ci			/* Actions always acting on INDX */
4715987da915Sopenharmony_ci	case AddIndexEntryAllocation :
4716987da915Sopenharmony_ci	case DeleteIndexEntryAllocation :
4717987da915Sopenharmony_ci	case SetIndexEntryVcnAllocation :
4718987da915Sopenharmony_ci	case UpdateFileNameAllocation :
4719987da915Sopenharmony_ci	case WriteEndOfIndexBuffer :
4720987da915Sopenharmony_ci		kind = ON_INDX;
4721987da915Sopenharmony_ci		break;
4722987da915Sopenharmony_ci			/* Actions never acting on MFT or INDX */
4723987da915Sopenharmony_ci	case ClearBitsInNonResidentBitMap :
4724987da915Sopenharmony_ci	case SetBitsInNonResidentBitMap :
4725987da915Sopenharmony_ci		kind = ON_RAW;
4726987da915Sopenharmony_ci		break;
4727987da915Sopenharmony_ci			/* Actions which may act on MFT */
4728987da915Sopenharmony_ci	case Noop : /* on MFT if DeallocateFileRecordSegment */
4729987da915Sopenharmony_ci		break;
4730987da915Sopenharmony_ci			/* Actions which may act on INDX */
4731987da915Sopenharmony_ci	case UpdateNonResidentValue :
4732987da915Sopenharmony_ci		/* Known cases : INDX, $SDS, ATTR_LIST */
4733987da915Sopenharmony_ci		kind = get_action_kind(action);
4734987da915Sopenharmony_ci		if (kind == ON_NONE)
4735987da915Sopenharmony_ci			err = 1;
4736987da915Sopenharmony_ci		break;
4737987da915Sopenharmony_ci	case OpenNonResidentAttribute :
4738987da915Sopenharmony_ci		/* probably not important */
4739987da915Sopenharmony_ci		kind = ON_NONE;
4740987da915Sopenharmony_ci		break;
4741987da915Sopenharmony_ci			/* Actions currently ignored */
4742987da915Sopenharmony_ci	case AttributeNamesDump :
4743987da915Sopenharmony_ci	case CommitTransaction :
4744987da915Sopenharmony_ci	case CompensationlogRecord :
4745987da915Sopenharmony_ci	case DeleteDirtyClusters :
4746987da915Sopenharmony_ci	case DirtyPageTableDump :
4747987da915Sopenharmony_ci	case EndTopLevelAction :
4748987da915Sopenharmony_ci	case ForgetTransaction :
4749987da915Sopenharmony_ci	case HotFix :
4750987da915Sopenharmony_ci	case OpenAttributeTableDump :
4751987da915Sopenharmony_ci	case PrepareTransaction :
4752987da915Sopenharmony_ci	case TransactionTableDump :
4753987da915Sopenharmony_ci	case UpdateRecordDataAllocation :
4754987da915Sopenharmony_ci	case UpdateRecordDataRoot :
4755987da915Sopenharmony_ci	case Win10Action35 :
4756987da915Sopenharmony_ci	case Win10Action36 :
4757987da915Sopenharmony_ci		kind = ON_NONE;
4758987da915Sopenharmony_ci		break;
4759987da915Sopenharmony_ci	}
4760987da915Sopenharmony_ci	switch (kind) {
4761987da915Sopenharmony_ci	case ON_MFT :
4762987da915Sopenharmony_ci/*
4763987da915Sopenharmony_ci the check below cannot be used on WinXP
4764987da915Sopenharmony_ciif (!(action->record.attribute_flags & ACTS_ON_MFT))
4765987da915Sopenharmony_ciprintf("** %s (action %d) not acting on MFT\n",actionname(rop),(int)action->num);
4766987da915Sopenharmony_ci*/
4767987da915Sopenharmony_ci		buffer = read_protected(vol, &action->record, mftrecsz, TRUE);
4768987da915Sopenharmony_ci		entry = (MFT_RECORD*)buffer;
4769987da915Sopenharmony_ci		if (entry) {
4770987da915Sopenharmony_ci			if (entry->magic == magic_FILE) {
4771987da915Sopenharmony_ci				executed = !older_record(entry,
4772987da915Sopenharmony_ci					&action->record);
4773987da915Sopenharmony_ci				if (!executed
4774987da915Sopenharmony_ci				    && exception(action->num))
4775987da915Sopenharmony_ci					executed = TRUE;
4776987da915Sopenharmony_ciif (optv > 1)
4777987da915Sopenharmony_ciprintf("record lsn 0x%llx is %s than action %d lsn 0x%llx\n",
4778987da915Sopenharmony_ci(long long)sle64_to_cpu(entry->lsn),
4779987da915Sopenharmony_ci(executed ? "not older" : "older"),
4780987da915Sopenharmony_ci(int)action->num,
4781987da915Sopenharmony_ci(long long)sle64_to_cpu(action->record.this_lsn));
4782987da915Sopenharmony_ci			} else {
4783987da915Sopenharmony_ci				printf("** %s (action %d) not acting on MFT\n",
4784987da915Sopenharmony_ci					actionname(rop), (int)action->num);
4785987da915Sopenharmony_ci				err = 1;
4786987da915Sopenharmony_ci			}
4787987da915Sopenharmony_ci		} else {
4788987da915Sopenharmony_ci			/*
4789987da915Sopenharmony_ci			 * Could not read the MFT record :
4790987da915Sopenharmony_ci			 * if this is undoing a record create (from scratch)
4791987da915Sopenharmony_ci			 * which did not take place, there is nothing to redo,
4792987da915Sopenharmony_ci			 * otherwise this is an error.
4793987da915Sopenharmony_ci			 */
4794987da915Sopenharmony_ci			if (check_full_mft(action,TRUE))
4795987da915Sopenharmony_ci				executed = FALSE;
4796987da915Sopenharmony_ci			else
4797987da915Sopenharmony_ci				err = 1;
4798987da915Sopenharmony_ci		}
4799987da915Sopenharmony_ci		break;
4800987da915Sopenharmony_ci	case ON_INDX :
4801987da915Sopenharmony_ci/*
4802987da915Sopenharmony_ci the check below cannot be used on WinXP
4803987da915Sopenharmony_ciif (!(action->record.attribute_flags & ACTS_ON_INDX))
4804987da915Sopenharmony_ciprintf("** %s (action %d) not acting on INDX\n",actionname(rop),(int)action->num);
4805987da915Sopenharmony_ci*/
4806987da915Sopenharmony_ci		xsize = vol->indx_record_size;
4807987da915Sopenharmony_ci		buffer = read_protected(vol, &action->record, xsize, TRUE);
4808987da915Sopenharmony_ci		indx = (INDEX_BLOCK*)buffer;
4809987da915Sopenharmony_ci		if (indx) {
4810987da915Sopenharmony_ci			if (indx->magic == magic_INDX) {
4811987da915Sopenharmony_ci				executed = !older_record(indx,
4812987da915Sopenharmony_ci					&action->record);
4813987da915Sopenharmony_ci				if (!executed
4814987da915Sopenharmony_ci				    && exception(action->num))
4815987da915Sopenharmony_ci					executed = TRUE;
4816987da915Sopenharmony_ciif (optv > 1)
4817987da915Sopenharmony_ciprintf("index lsn 0x%llx is %s than action %d lsn 0x%llx\n",
4818987da915Sopenharmony_ci(long long)sle64_to_cpu(indx->lsn),
4819987da915Sopenharmony_ci(executed ? "not older" : "older"),
4820987da915Sopenharmony_ci(int)action->num,
4821987da915Sopenharmony_ci(long long)sle64_to_cpu(action->record.this_lsn));
4822987da915Sopenharmony_ci			} else {
4823987da915Sopenharmony_ci				printf("** %s (action %d) not acting on INDX\n",
4824987da915Sopenharmony_ci					actionname(rop), (int)action->num);
4825987da915Sopenharmony_ci				err = 1;
4826987da915Sopenharmony_ci			}
4827987da915Sopenharmony_ci		} else {
4828987da915Sopenharmony_ci			/*
4829987da915Sopenharmony_ci			 * Could not read the INDX record :
4830987da915Sopenharmony_ci			 * if this is undoing a record create (from scratch)
4831987da915Sopenharmony_ci			 * which did not take place, there is nothing to redo,
4832987da915Sopenharmony_ci			 * otherwise this must be an error.
4833987da915Sopenharmony_ci			 * However, after deleting the last index allocation
4834987da915Sopenharmony_ci			 * in a block, the block is apparently zeroed
4835987da915Sopenharmony_ci			 * and cannot be read. In this case we have to
4836987da915Sopenharmony_ci			 * create an initial index block and apply the undo.
4837987da915Sopenharmony_ci			 */
4838987da915Sopenharmony_ci			if (check_full_index(action,TRUE))
4839987da915Sopenharmony_ci				executed = FALSE;
4840987da915Sopenharmony_ci			else {
4841987da915Sopenharmony_ci				err = 1;
4842987da915Sopenharmony_ci				if (uop == AddIndexEntryAllocation) {
4843987da915Sopenharmony_ci					executed = TRUE;
4844987da915Sopenharmony_ci					buffer = (char*)calloc(1, xsize);
4845987da915Sopenharmony_ci					if (buffer)
4846987da915Sopenharmony_ci						err = create_indx(vol,
4847987da915Sopenharmony_ci							action, buffer);
4848987da915Sopenharmony_ci				}
4849987da915Sopenharmony_ci			}
4850987da915Sopenharmony_ci		}
4851987da915Sopenharmony_ci		break;
4852987da915Sopenharmony_ci	case ON_RAW :
4853987da915Sopenharmony_ci		if (action->record.attribute_flags
4854987da915Sopenharmony_ci				& (ACTS_ON_INDX | ACTS_ON_MFT)) {
4855987da915Sopenharmony_ci			printf("** Error : action %s on MFT or INDX\n",
4856987da915Sopenharmony_ci				actionname(rop));
4857987da915Sopenharmony_ci			err = 1;
4858987da915Sopenharmony_ci		} else {
4859987da915Sopenharmony_ci			buffer = read_raw(vol, &action->record);
4860987da915Sopenharmony_ci			if (!buffer)
4861987da915Sopenharmony_ci				err = 1;
4862987da915Sopenharmony_ci		}
4863987da915Sopenharmony_ci		executed = TRUE;
4864987da915Sopenharmony_ci		break;
4865987da915Sopenharmony_ci	default :
4866987da915Sopenharmony_ci		executed = TRUE;
4867987da915Sopenharmony_ci		buffer = (char*)NULL;
4868987da915Sopenharmony_ci		break;
4869987da915Sopenharmony_ci	}
4870987da915Sopenharmony_ci	if (!err && executed) {
4871987da915Sopenharmony_ci		err = distribute_undos(vol, action, buffer);
4872987da915Sopenharmony_ci		undocount++;
4873987da915Sopenharmony_ci	}
4874987da915Sopenharmony_ci	if (buffer)
4875987da915Sopenharmony_ci		free(buffer);
4876987da915Sopenharmony_ci
4877987da915Sopenharmony_ci	return (err);
4878987da915Sopenharmony_ci}
4879987da915Sopenharmony_ci
4880987da915Sopenharmony_ci/*
4881987da915Sopenharmony_ci *		Play the undo actions from latest to earliest
4882987da915Sopenharmony_ci *
4883987da915Sopenharmony_ci *	For structured record, a check is made on the lsn to only
4884987da915Sopenharmony_ci *	try to undo the actions which were executed. This implies
4885987da915Sopenharmony_ci *	identifying actions on a structured record.
4886987da915Sopenharmony_ci *
4887987da915Sopenharmony_ci *	Returns 0 if successful
4888987da915Sopenharmony_ci */
4889987da915Sopenharmony_ci
4890987da915Sopenharmony_ciint play_undos(ntfs_volume *vol, const struct ACTION_RECORD *lastaction)
4891987da915Sopenharmony_ci{
4892987da915Sopenharmony_ci	const struct ACTION_RECORD *action;
4893987da915Sopenharmony_ci	int err;
4894987da915Sopenharmony_ci
4895987da915Sopenharmony_ci	err = 0;
4896987da915Sopenharmony_ci	action = lastaction;
4897987da915Sopenharmony_ci	while (action && !err) {
4898987da915Sopenharmony_ci		if (!optc || within_lcn_range(&action->record))
4899987da915Sopenharmony_ci			err = play_one_undo(vol, action);
4900987da915Sopenharmony_ci		if (!err)
4901987da915Sopenharmony_ci			action = action->prev;
4902987da915Sopenharmony_ci	}
4903987da915Sopenharmony_ci	return (err);
4904987da915Sopenharmony_ci}
4905