1987da915Sopenharmony_ci/**
2987da915Sopenharmony_ci * ntfsmove - Part of the Linux-NTFS project.
3987da915Sopenharmony_ci *
4987da915Sopenharmony_ci * Copyright (c) 2003 Richard Russon
5987da915Sopenharmony_ci * Copyright (c) 2003-2005 Anton Altaparmakov
6987da915Sopenharmony_ci *
7987da915Sopenharmony_ci * This utility will move files on an NTFS volume.
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 Linux-NTFS
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_STDIO_H
28987da915Sopenharmony_ci#include <stdio.h>
29987da915Sopenharmony_ci#endif
30987da915Sopenharmony_ci#ifdef HAVE_GETOPT_H
31987da915Sopenharmony_ci#include <getopt.h>
32987da915Sopenharmony_ci#endif
33987da915Sopenharmony_ci#ifdef HAVE_STDLIB_H
34987da915Sopenharmony_ci#include <stdlib.h>
35987da915Sopenharmony_ci#endif
36987da915Sopenharmony_ci#ifdef HAVE_STRING_H
37987da915Sopenharmony_ci#include <string.h>
38987da915Sopenharmony_ci#endif
39987da915Sopenharmony_ci#ifdef HAVE_LIMITS_H
40987da915Sopenharmony_ci#include <limits.h>
41987da915Sopenharmony_ci#endif
42987da915Sopenharmony_ci
43987da915Sopenharmony_ci#include "types.h"
44987da915Sopenharmony_ci#include "attrib.h"
45987da915Sopenharmony_ci#include "utils.h"
46987da915Sopenharmony_ci#include "volume.h"
47987da915Sopenharmony_ci#include "debug.h"
48987da915Sopenharmony_ci#include "dir.h"
49987da915Sopenharmony_ci#include "bitmap.h"
50987da915Sopenharmony_ci#include "ntfsmove.h"
51987da915Sopenharmony_ci/* #include "version.h" */
52987da915Sopenharmony_ci#include "logging.h"
53987da915Sopenharmony_ci
54987da915Sopenharmony_cistatic const char *EXEC_NAME = "ntfsmove";
55987da915Sopenharmony_cistatic struct options opts;
56987da915Sopenharmony_ci
57987da915Sopenharmony_ci/**
58987da915Sopenharmony_ci * version - Print version information about the program
59987da915Sopenharmony_ci *
60987da915Sopenharmony_ci * Print a copyright statement and a brief description of the program.
61987da915Sopenharmony_ci *
62987da915Sopenharmony_ci * Return:  none
63987da915Sopenharmony_ci */
64987da915Sopenharmony_cistatic void version(void)
65987da915Sopenharmony_ci{
66987da915Sopenharmony_ci	ntfs_log_info("\n%s v%s (libntfs-3g) - Move files and directories on an "
67987da915Sopenharmony_ci			"NTFS volume.\n\n", EXEC_NAME, VERSION);
68987da915Sopenharmony_ci	ntfs_log_info("Copyright (c) 2003 Richard Russon\n");
69987da915Sopenharmony_ci	ntfs_log_info("\n%s\n%s%s\n", ntfs_gpl, ntfs_bugs, ntfs_home);
70987da915Sopenharmony_ci}
71987da915Sopenharmony_ci
72987da915Sopenharmony_ci/**
73987da915Sopenharmony_ci * usage - Print a list of the parameters to the program
74987da915Sopenharmony_ci *
75987da915Sopenharmony_ci * Print a list of the parameters and options for the program.
76987da915Sopenharmony_ci *
77987da915Sopenharmony_ci * Return:  none
78987da915Sopenharmony_ci */
79987da915Sopenharmony_cistatic void usage(void)
80987da915Sopenharmony_ci{
81987da915Sopenharmony_ci	ntfs_log_info("\nUsage: %s [options] device file\n"
82987da915Sopenharmony_ci		"\n"
83987da915Sopenharmony_ci		"    -S      --start        Move to the start of the volume\n"
84987da915Sopenharmony_ci		"    -B      --best         Move to the best place on the volume\n"
85987da915Sopenharmony_ci		"    -E      --end          Move to the end of the volume\n"
86987da915Sopenharmony_ci		"    -C num  --cluster num  Move to this cluster offset\n"
87987da915Sopenharmony_ci		"\n"
88987da915Sopenharmony_ci		"    -D      --no-dirty     Do not mark volume dirty (require chkdsk)\n"
89987da915Sopenharmony_ci		"    -n      --no-action    Do not write to disk\n"
90987da915Sopenharmony_ci		"    -f      --force        Use less caution\n"
91987da915Sopenharmony_ci		"    -h      --help         Print this help\n"
92987da915Sopenharmony_ci		"    -q      --quiet        Less output\n"
93987da915Sopenharmony_ci		"    -V      --version      Version information\n"
94987da915Sopenharmony_ci		"    -v      --verbose      More output\n\n",
95987da915Sopenharmony_ci		EXEC_NAME);
96987da915Sopenharmony_ci	ntfs_log_info("%s%s\n", ntfs_bugs, ntfs_home);
97987da915Sopenharmony_ci}
98987da915Sopenharmony_ci
99987da915Sopenharmony_ci/**
100987da915Sopenharmony_ci * parse_options - Read and validate the programs command line
101987da915Sopenharmony_ci *
102987da915Sopenharmony_ci * Read the command line, verify the syntax and parse the options.
103987da915Sopenharmony_ci * This function is very long, but quite simple.
104987da915Sopenharmony_ci *
105987da915Sopenharmony_ci * Return:  1 Success
106987da915Sopenharmony_ci *	    0 Error, one or more problems
107987da915Sopenharmony_ci */
108987da915Sopenharmony_cistatic int parse_options(int argc, char **argv)
109987da915Sopenharmony_ci{
110987da915Sopenharmony_ci	static const char *sopt = "-BC:DEfh?nqSVv";
111987da915Sopenharmony_ci	static const struct option lopt[] = {
112987da915Sopenharmony_ci		{ "best",	no_argument,		NULL, 'B' },
113987da915Sopenharmony_ci		{ "cluster",	required_argument,	NULL, 'C' },
114987da915Sopenharmony_ci		{ "end",	no_argument,		NULL, 'E' },
115987da915Sopenharmony_ci		{ "force",	no_argument,		NULL, 'f' },
116987da915Sopenharmony_ci		{ "help",	no_argument,		NULL, 'h' },
117987da915Sopenharmony_ci		{ "no-action",	no_argument,		NULL, 'n' },
118987da915Sopenharmony_ci		{ "no-dirty",	no_argument,		NULL, 'D' },
119987da915Sopenharmony_ci		{ "quiet",	no_argument,		NULL, 'q' },
120987da915Sopenharmony_ci		{ "start",	no_argument,		NULL, 'S' },
121987da915Sopenharmony_ci		{ "verbose",	no_argument,		NULL, 'v' },
122987da915Sopenharmony_ci		{ "version",	no_argument,		NULL, 'V' },
123987da915Sopenharmony_ci		{ NULL,		0,			NULL, 0   }
124987da915Sopenharmony_ci	};
125987da915Sopenharmony_ci
126987da915Sopenharmony_ci	int c = -1;
127987da915Sopenharmony_ci	int err  = 0;
128987da915Sopenharmony_ci	int ver  = 0;
129987da915Sopenharmony_ci	int help = 0;
130987da915Sopenharmony_ci	int levels = 0;
131987da915Sopenharmony_ci	char *end = NULL;
132987da915Sopenharmony_ci
133987da915Sopenharmony_ci	opterr = 0; /* We'll handle the errors, thank you. */
134987da915Sopenharmony_ci
135987da915Sopenharmony_ci	while ((c = getopt_long(argc, argv, sopt, lopt, NULL)) != -1) {
136987da915Sopenharmony_ci		switch (c) {
137987da915Sopenharmony_ci		case 1:	/* A non-option argument */
138987da915Sopenharmony_ci			if (!opts.device) {
139987da915Sopenharmony_ci				opts.device = argv[optind-1];
140987da915Sopenharmony_ci			} else if (!opts.file) {
141987da915Sopenharmony_ci				opts.file = argv[optind-1];
142987da915Sopenharmony_ci			} else {
143987da915Sopenharmony_ci				opts.device = NULL;
144987da915Sopenharmony_ci				opts.file   = NULL;
145987da915Sopenharmony_ci				err++;
146987da915Sopenharmony_ci			}
147987da915Sopenharmony_ci			break;
148987da915Sopenharmony_ci		case 'B':
149987da915Sopenharmony_ci			if (opts.location == 0)
150987da915Sopenharmony_ci				opts.location = NTFS_MOVE_LOC_BEST;
151987da915Sopenharmony_ci			else
152987da915Sopenharmony_ci				opts.location = -1;
153987da915Sopenharmony_ci			break;
154987da915Sopenharmony_ci		case 'C':
155987da915Sopenharmony_ci			if (opts.location == 0) {
156987da915Sopenharmony_ci				opts.location = strtoll(optarg, &end, 0);
157987da915Sopenharmony_ci				if (end && *end)
158987da915Sopenharmony_ci					err++;
159987da915Sopenharmony_ci			} else {
160987da915Sopenharmony_ci				opts.location = -1;
161987da915Sopenharmony_ci			}
162987da915Sopenharmony_ci			break;
163987da915Sopenharmony_ci		case 'D':
164987da915Sopenharmony_ci			opts.nodirty++;
165987da915Sopenharmony_ci			break;
166987da915Sopenharmony_ci		case 'E':
167987da915Sopenharmony_ci			if (opts.location == 0)
168987da915Sopenharmony_ci				opts.location = NTFS_MOVE_LOC_END;
169987da915Sopenharmony_ci			else
170987da915Sopenharmony_ci				opts.location = -1;
171987da915Sopenharmony_ci			break;
172987da915Sopenharmony_ci		case 'f':
173987da915Sopenharmony_ci			opts.force++;
174987da915Sopenharmony_ci			break;
175987da915Sopenharmony_ci		case 'h':
176987da915Sopenharmony_ci		case '?':
177987da915Sopenharmony_ci			if (strncmp (argv[optind-1], "--log-", 6) == 0) {
178987da915Sopenharmony_ci				if (!ntfs_log_parse_option (argv[optind-1]))
179987da915Sopenharmony_ci					err++;
180987da915Sopenharmony_ci				break;
181987da915Sopenharmony_ci			}
182987da915Sopenharmony_ci			help++;
183987da915Sopenharmony_ci			break;
184987da915Sopenharmony_ci		case 'n':
185987da915Sopenharmony_ci			opts.noaction++;
186987da915Sopenharmony_ci			break;
187987da915Sopenharmony_ci		case 'q':
188987da915Sopenharmony_ci			opts.quiet++;
189987da915Sopenharmony_ci			ntfs_log_clear_levels(NTFS_LOG_LEVEL_QUIET);
190987da915Sopenharmony_ci			break;
191987da915Sopenharmony_ci		case 'S':
192987da915Sopenharmony_ci			if (opts.location == 0)
193987da915Sopenharmony_ci				opts.location = NTFS_MOVE_LOC_START;
194987da915Sopenharmony_ci			else
195987da915Sopenharmony_ci				opts.location = -1;
196987da915Sopenharmony_ci			break;
197987da915Sopenharmony_ci		case 'V':
198987da915Sopenharmony_ci			ver++;
199987da915Sopenharmony_ci			break;
200987da915Sopenharmony_ci		case 'v':
201987da915Sopenharmony_ci			opts.verbose++;
202987da915Sopenharmony_ci			ntfs_log_set_levels(NTFS_LOG_LEVEL_VERBOSE);
203987da915Sopenharmony_ci			break;
204987da915Sopenharmony_ci		default:
205987da915Sopenharmony_ci			ntfs_log_error("Unknown option '%s'.\n", argv[optind-1]);
206987da915Sopenharmony_ci			err++;
207987da915Sopenharmony_ci			break;
208987da915Sopenharmony_ci		}
209987da915Sopenharmony_ci	}
210987da915Sopenharmony_ci
211987da915Sopenharmony_ci	/* Make sure we're in sync with the log levels */
212987da915Sopenharmony_ci	levels = ntfs_log_get_levels();
213987da915Sopenharmony_ci	if (levels & NTFS_LOG_LEVEL_VERBOSE)
214987da915Sopenharmony_ci		opts.verbose++;
215987da915Sopenharmony_ci	if (!(levels & NTFS_LOG_LEVEL_QUIET))
216987da915Sopenharmony_ci		opts.quiet++;
217987da915Sopenharmony_ci
218987da915Sopenharmony_ci	if (help || ver) {
219987da915Sopenharmony_ci		opts.quiet = 0;
220987da915Sopenharmony_ci	} else {
221987da915Sopenharmony_ci		if ((opts.device == NULL) ||
222987da915Sopenharmony_ci		    (opts.file   == NULL)) {
223987da915Sopenharmony_ci			if (argc > 1)
224987da915Sopenharmony_ci				ntfs_log_error("You must specify one device and one file.\n");
225987da915Sopenharmony_ci			err++;
226987da915Sopenharmony_ci		}
227987da915Sopenharmony_ci
228987da915Sopenharmony_ci		if (opts.quiet && opts.verbose) {
229987da915Sopenharmony_ci			ntfs_log_error("You may not use --quiet and --verbose at the "
230987da915Sopenharmony_ci					"same time.\n");
231987da915Sopenharmony_ci			err++;
232987da915Sopenharmony_ci		}
233987da915Sopenharmony_ci
234987da915Sopenharmony_ci		if (opts.location == -1) {
235987da915Sopenharmony_ci			ntfs_log_error("You may only specify one location option: "
236987da915Sopenharmony_ci				"--start, --best, --end or --cluster\n");
237987da915Sopenharmony_ci			err++;
238987da915Sopenharmony_ci		} else if (opts.location == 0) {
239987da915Sopenharmony_ci			opts.location = NTFS_MOVE_LOC_BEST;
240987da915Sopenharmony_ci		}
241987da915Sopenharmony_ci	}
242987da915Sopenharmony_ci
243987da915Sopenharmony_ci	if (ver)
244987da915Sopenharmony_ci		version();
245987da915Sopenharmony_ci	if (help || err)
246987da915Sopenharmony_ci		usage();
247987da915Sopenharmony_ci
248987da915Sopenharmony_ci	return (!err && !help && !ver);
249987da915Sopenharmony_ci}
250987da915Sopenharmony_ci
251987da915Sopenharmony_ci#if 0
252987da915Sopenharmony_ci
253987da915Sopenharmony_ci/**
254987da915Sopenharmony_ci * ntfs_debug_runlist_dump2 - Dump a runlist.
255987da915Sopenharmony_ci */
256987da915Sopenharmony_cistatic int ntfs_debug_runlist_dump2(const runlist *rl, int abbr, char *prefix)
257987da915Sopenharmony_ci{
258987da915Sopenharmony_ci	//int abbr = 3;	/* abbreviate long lists */
259987da915Sopenharmony_ci	int len = 0;
260987da915Sopenharmony_ci	int i;
261987da915Sopenharmony_ci	int res = 0;
262987da915Sopenharmony_ci	u64 total = 0;
263987da915Sopenharmony_ci	const char *lcn_str[5] = { "HOLE", "NOTMAP", "ENOENT", "EINVAL", "XXXX" };
264987da915Sopenharmony_ci
265987da915Sopenharmony_ci	if (!rl) {
266987da915Sopenharmony_ci		ntfs_log_info("    Run list not present.\n");
267987da915Sopenharmony_ci		return 0;
268987da915Sopenharmony_ci	}
269987da915Sopenharmony_ci
270987da915Sopenharmony_ci	if (!prefix)
271987da915Sopenharmony_ci		prefix = "";
272987da915Sopenharmony_ci
273987da915Sopenharmony_ci	if (abbr)
274987da915Sopenharmony_ci		for (len = 0; rl[len].length; len++) ;
275987da915Sopenharmony_ci
276987da915Sopenharmony_ci	ntfs_log_info("%s     VCN      LCN      len\n", prefix);
277987da915Sopenharmony_ci	for (i = 0; rl->length; i++, rl++) {
278987da915Sopenharmony_ci		LCN lcn = rl->lcn;
279987da915Sopenharmony_ci
280987da915Sopenharmony_ci		total += rl->length;
281987da915Sopenharmony_ci		if (abbr)
282987da915Sopenharmony_ci			if (len > 20) {
283987da915Sopenharmony_ci				if ((i == abbr) && (len > (abbr*2)))
284987da915Sopenharmony_ci					ntfs_log_info("%s     ...      ...      ...\n", prefix);
285987da915Sopenharmony_ci				if ((i > (abbr-1)) && (i < (len - (abbr-1))))
286987da915Sopenharmony_ci					continue;
287987da915Sopenharmony_ci			}
288987da915Sopenharmony_ci
289987da915Sopenharmony_ci		if (rl->vcn < -1)
290987da915Sopenharmony_ci			res = -1;
291987da915Sopenharmony_ci
292987da915Sopenharmony_ci		if (lcn < (LCN)0) {
293987da915Sopenharmony_ci			int j = -lcn - 1;
294987da915Sopenharmony_ci
295987da915Sopenharmony_ci			if ((j < 0) || (j > 4)) {
296987da915Sopenharmony_ci				j = 4;
297987da915Sopenharmony_ci				res = -1;
298987da915Sopenharmony_ci			}
299987da915Sopenharmony_ci			ntfs_log_info("%s%8lld %8s %8lld\n", prefix,
300987da915Sopenharmony_ci				rl->vcn, lcn_str[j], rl->length);
301987da915Sopenharmony_ci		} else
302987da915Sopenharmony_ci			ntfs_log_info("%s%8lld %8lld %8lld\n", prefix,
303987da915Sopenharmony_ci				rl->vcn, rl->lcn, rl->length);
304987da915Sopenharmony_ci	}
305987da915Sopenharmony_ci	ntfs_log_info("%s                  --------\n", prefix);
306987da915Sopenharmony_ci	ntfs_log_info("%s                  %8lld\n", prefix, total);
307987da915Sopenharmony_ci	ntfs_log_info("\n");
308987da915Sopenharmony_ci	return res;
309987da915Sopenharmony_ci}
310987da915Sopenharmony_ci
311987da915Sopenharmony_ci#endif /* if 0 */
312987da915Sopenharmony_ci
313987da915Sopenharmony_ci/**
314987da915Sopenharmony_ci * resize_nonres_attr
315987da915Sopenharmony_ci */
316987da915Sopenharmony_cistatic int resize_nonres_attr(MFT_RECORD *m, ATTR_RECORD *a, const u32 new_size)
317987da915Sopenharmony_ci{
318987da915Sopenharmony_ci	int this_attr;
319987da915Sopenharmony_ci	int next_attr;
320987da915Sopenharmony_ci	int tail_size;
321987da915Sopenharmony_ci	int file_size;
322987da915Sopenharmony_ci	int old_size;
323987da915Sopenharmony_ci	u8 *ptr;
324987da915Sopenharmony_ci
325987da915Sopenharmony_ci	old_size  = le32_to_cpu(a->length);
326987da915Sopenharmony_ci	file_size = le32_to_cpu(m->bytes_in_use);
327987da915Sopenharmony_ci	this_attr = p2n(a)-p2n(m);
328987da915Sopenharmony_ci	next_attr = this_attr + le32_to_cpu(a->length);
329987da915Sopenharmony_ci	tail_size = file_size - next_attr;
330987da915Sopenharmony_ci	ptr = (u8*) m;
331987da915Sopenharmony_ci
332987da915Sopenharmony_ci	/*
333987da915Sopenharmony_ci	ntfs_log_info("old_size  = %d\n", old_size);
334987da915Sopenharmony_ci	ntfs_log_info("new_size  = %d\n", new_size);
335987da915Sopenharmony_ci	ntfs_log_info("file_size = %d\n", file_size);
336987da915Sopenharmony_ci	ntfs_log_info("this_attr = %d\n", this_attr);
337987da915Sopenharmony_ci	ntfs_log_info("next_attr = %d\n", next_attr);
338987da915Sopenharmony_ci	ntfs_log_info("tail_size = %d\n", tail_size);
339987da915Sopenharmony_ci	*/
340987da915Sopenharmony_ci
341987da915Sopenharmony_ci	memmove(ptr + this_attr + new_size, ptr + next_attr, tail_size);
342987da915Sopenharmony_ci
343987da915Sopenharmony_ci	a->length = cpu_to_le32(new_size);
344987da915Sopenharmony_ci	m->bytes_in_use = cpu_to_le32(le32_to_cpu(m->bytes_in_use) + (new_size - old_size));
345987da915Sopenharmony_ci
346987da915Sopenharmony_ci	return 0;
347987da915Sopenharmony_ci}
348987da915Sopenharmony_ci
349987da915Sopenharmony_ci/**
350987da915Sopenharmony_ci * calc_attr_length
351987da915Sopenharmony_ci */
352987da915Sopenharmony_cistatic int calc_attr_length(ATTR_RECORD *rec, int runlength)
353987da915Sopenharmony_ci{
354987da915Sopenharmony_ci	int size;
355987da915Sopenharmony_ci
356987da915Sopenharmony_ci	if (!rec)
357987da915Sopenharmony_ci		return -1;
358987da915Sopenharmony_ci	if (!rec->non_resident)
359987da915Sopenharmony_ci		return -1;
360987da915Sopenharmony_ci
361987da915Sopenharmony_ci	size = le16_to_cpu(rec->mapping_pairs_offset) + runlength + 7;
362987da915Sopenharmony_ci	size &= 0xFFF8;
363987da915Sopenharmony_ci	return size;
364987da915Sopenharmony_ci}
365987da915Sopenharmony_ci
366987da915Sopenharmony_ci#if 0
367987da915Sopenharmony_ci
368987da915Sopenharmony_ci/**
369987da915Sopenharmony_ci * dump_runs
370987da915Sopenharmony_ci */
371987da915Sopenharmony_cistatic void dump_runs(u8 *buffer, int len)
372987da915Sopenharmony_ci{
373987da915Sopenharmony_ci	int i;
374987da915Sopenharmony_ci	ntfs_log_info("RUN: \e[01;31m");
375987da915Sopenharmony_ci
376987da915Sopenharmony_ci	for (i = 0; i < len; i++) {
377987da915Sopenharmony_ci		ntfs_log_info(" %02x", buffer[i]);
378987da915Sopenharmony_ci	}
379987da915Sopenharmony_ci	ntfs_log_info("\e[0m\n");
380987da915Sopenharmony_ci}
381987da915Sopenharmony_ci
382987da915Sopenharmony_ci#endif /* if 0 */
383987da915Sopenharmony_ci
384987da915Sopenharmony_ci/**
385987da915Sopenharmony_ci * find_unused
386987da915Sopenharmony_ci */
387987da915Sopenharmony_cistatic runlist * find_unused(ntfs_volume *vol, s64 size, u64 loc
388987da915Sopenharmony_ci	__attribute__((unused)), int flags __attribute__((unused)))
389987da915Sopenharmony_ci{
390987da915Sopenharmony_ci	const int bufsize = 8192;
391987da915Sopenharmony_ci	u8 *buffer;
392987da915Sopenharmony_ci	int clus;
393987da915Sopenharmony_ci	int i;
394987da915Sopenharmony_ci	int curr = 0;
395987da915Sopenharmony_ci	int count = 0;
396987da915Sopenharmony_ci	s64 start = 0;
397987da915Sopenharmony_ci	int bit = 0;
398987da915Sopenharmony_ci	runlist *res = NULL;
399987da915Sopenharmony_ci
400987da915Sopenharmony_ci	//ntfs_log_info("find_unused\n");
401987da915Sopenharmony_ci	buffer = malloc(bufsize);
402987da915Sopenharmony_ci	if (!buffer) {
403987da915Sopenharmony_ci		ntfs_log_info("!buffer\n");
404987da915Sopenharmony_ci		return NULL;
405987da915Sopenharmony_ci	}
406987da915Sopenharmony_ci
407987da915Sopenharmony_ci	//ntfs_log_info("looking for space for %lld clusters\n", size);
408987da915Sopenharmony_ci
409987da915Sopenharmony_ci	clus = vol->lcnbmp_na->allocated_size / bufsize;
410987da915Sopenharmony_ci	//ntfs_log_info("clus = %d\n", clus);
411987da915Sopenharmony_ci
412987da915Sopenharmony_ci	for (i = 0; i < clus; i++) {
413987da915Sopenharmony_ci		int bytes_read, j;
414987da915Sopenharmony_ci
415987da915Sopenharmony_ci		bytes_read = ntfs_attr_pread(vol->lcnbmp_na, i*bufsize,
416987da915Sopenharmony_ci				bufsize, buffer);
417987da915Sopenharmony_ci		if (bytes_read != bufsize) {
418987da915Sopenharmony_ci			ntfs_log_info("!read\n");
419987da915Sopenharmony_ci			return NULL;
420987da915Sopenharmony_ci		}
421987da915Sopenharmony_ci		for (j = 0; j < bufsize*8; j++) {
422987da915Sopenharmony_ci			bit = !!test_bit(j & 7, buffer[j>>3]);
423987da915Sopenharmony_ci			if (curr == bit) {
424987da915Sopenharmony_ci				count++;
425987da915Sopenharmony_ci				if ((!bit) && (count >= size)) {
426987da915Sopenharmony_ci					//res = calloc(2, sizeof(*res));
427987da915Sopenharmony_ci					res = calloc(1, 4096);
428987da915Sopenharmony_ci					if (res) {
429987da915Sopenharmony_ci						res[0].vcn    = 0;
430987da915Sopenharmony_ci						res[0].lcn    = start;
431987da915Sopenharmony_ci						res[0].length = size;
432987da915Sopenharmony_ci						res[1].lcn    = LCN_ENOENT;
433987da915Sopenharmony_ci					}
434987da915Sopenharmony_ci					goto done;
435987da915Sopenharmony_ci				}
436987da915Sopenharmony_ci			} else {
437987da915Sopenharmony_ci				//ntfs_log_info("%d * %d\n", curr, count);
438987da915Sopenharmony_ci				curr = bit;
439987da915Sopenharmony_ci				count = 1;
440987da915Sopenharmony_ci				start = i*bufsize*8 + j;
441987da915Sopenharmony_ci			}
442987da915Sopenharmony_ci		}
443987da915Sopenharmony_ci	}
444987da915Sopenharmony_cidone:
445987da915Sopenharmony_ci	//ntfs_log_info("%d * %d\n", curr, count);
446987da915Sopenharmony_ci
447987da915Sopenharmony_ci	free(buffer);
448987da915Sopenharmony_ci
449987da915Sopenharmony_ci	if (res) {
450987da915Sopenharmony_ci		for (i = 0; i < size; i++) {
451987da915Sopenharmony_ci			if (utils_cluster_in_use(vol, res->lcn + i)) {
452987da915Sopenharmony_ci				ntfs_log_info("ERROR cluster %lld in use\n",
453987da915Sopenharmony_ci				(long long)res->lcn + i);
454987da915Sopenharmony_ci			}
455987da915Sopenharmony_ci		}
456987da915Sopenharmony_ci	} else {
457987da915Sopenharmony_ci		ntfs_log_info("failed\n");
458987da915Sopenharmony_ci	}
459987da915Sopenharmony_ci
460987da915Sopenharmony_ci	return res;
461987da915Sopenharmony_ci}
462987da915Sopenharmony_ci
463987da915Sopenharmony_ci/**
464987da915Sopenharmony_ci * dont_move
465987da915Sopenharmony_ci *
466987da915Sopenharmony_ci * Don't let the user move:
467987da915Sopenharmony_ci *   ANY metadata
468987da915Sopenharmony_ci *   Any fragmented MFT records
469987da915Sopenharmony_ci *   The boot file 'ntldr'
470987da915Sopenharmony_ci */
471987da915Sopenharmony_cistatic int dont_move(ntfs_inode *ino)
472987da915Sopenharmony_ci{
473987da915Sopenharmony_ci	static const ntfschar ntldr[6] = {
474987da915Sopenharmony_ci		const_cpu_to_le16('n'), const_cpu_to_le16('t'), const_cpu_to_le16('l'),
475987da915Sopenharmony_ci		const_cpu_to_le16('d'), const_cpu_to_le16('r'), const_cpu_to_le16('\0')
476987da915Sopenharmony_ci	};
477987da915Sopenharmony_ci
478987da915Sopenharmony_ci	ATTR_RECORD *rec;
479987da915Sopenharmony_ci	FILE_NAME_ATTR *name;
480987da915Sopenharmony_ci
481987da915Sopenharmony_ci	if (utils_is_metadata(ino)) {
482987da915Sopenharmony_ci		ntfs_log_error("metadata\n");
483987da915Sopenharmony_ci		return 1;
484987da915Sopenharmony_ci	}
485987da915Sopenharmony_ci
486987da915Sopenharmony_ci	rec = find_first_attribute(AT_ATTRIBUTE_LIST, ino->mrec);
487987da915Sopenharmony_ci	if (rec) {
488987da915Sopenharmony_ci		ntfs_log_error("attribute list\n");
489987da915Sopenharmony_ci		return 1;
490987da915Sopenharmony_ci	}
491987da915Sopenharmony_ci
492987da915Sopenharmony_ci	rec = find_first_attribute(AT_FILE_NAME, ino->mrec);
493987da915Sopenharmony_ci	if (!rec) {
494987da915Sopenharmony_ci		ntfs_log_error("extend inode\n");
495987da915Sopenharmony_ci		return 1;
496987da915Sopenharmony_ci	}
497987da915Sopenharmony_ci
498987da915Sopenharmony_ci	name = (FILE_NAME_ATTR*) ((u8*)rec + le16_to_cpu(rec->value_offset));
499987da915Sopenharmony_ci	if (ntfs_names_are_equal(ntldr, 5, name->file_name, name->file_name_length,
500987da915Sopenharmony_ci		IGNORE_CASE, ino->vol->upcase, ino->vol->upcase_len)) {
501987da915Sopenharmony_ci		ntfs_log_error("ntldr\n");
502987da915Sopenharmony_ci		return 1;
503987da915Sopenharmony_ci	}
504987da915Sopenharmony_ci
505987da915Sopenharmony_ci	return 0;
506987da915Sopenharmony_ci}
507987da915Sopenharmony_ci
508987da915Sopenharmony_ci
509987da915Sopenharmony_ci/**
510987da915Sopenharmony_ci * bitmap_alloc
511987da915Sopenharmony_ci */
512987da915Sopenharmony_cistatic int bitmap_alloc(ntfs_volume *vol, runlist_element *rl)
513987da915Sopenharmony_ci{
514987da915Sopenharmony_ci	int res;
515987da915Sopenharmony_ci
516987da915Sopenharmony_ci	if (!rl)
517987da915Sopenharmony_ci		return -1;
518987da915Sopenharmony_ci
519987da915Sopenharmony_ci	res = ntfs_bitmap_set_run(vol->lcnbmp_na, rl->lcn, rl->length);
520987da915Sopenharmony_ci	if (res < 0) {
521987da915Sopenharmony_ci		ntfs_log_error("bitmap alloc returns %d\n", res);
522987da915Sopenharmony_ci	}
523987da915Sopenharmony_ci
524987da915Sopenharmony_ci	return res;
525987da915Sopenharmony_ci}
526987da915Sopenharmony_ci
527987da915Sopenharmony_ci/**
528987da915Sopenharmony_ci * bitmap_free
529987da915Sopenharmony_ci */
530987da915Sopenharmony_cistatic int bitmap_free(ntfs_volume *vol, runlist_element *rl)
531987da915Sopenharmony_ci{
532987da915Sopenharmony_ci	int res;
533987da915Sopenharmony_ci
534987da915Sopenharmony_ci	if (!rl)
535987da915Sopenharmony_ci		return -1;
536987da915Sopenharmony_ci
537987da915Sopenharmony_ci	res = ntfs_bitmap_clear_run(vol->lcnbmp_na, rl->lcn, rl->length);
538987da915Sopenharmony_ci	if (res < 0) {
539987da915Sopenharmony_ci		ntfs_log_error("bitmap free returns %d\n", res);
540987da915Sopenharmony_ci	}
541987da915Sopenharmony_ci
542987da915Sopenharmony_ci	return res;
543987da915Sopenharmony_ci}
544987da915Sopenharmony_ci
545987da915Sopenharmony_ci/**
546987da915Sopenharmony_ci * data_copy
547987da915Sopenharmony_ci */
548987da915Sopenharmony_cistatic int data_copy(ntfs_volume *vol, runlist_element *from, runlist_element *to)
549987da915Sopenharmony_ci{
550987da915Sopenharmony_ci	int i;
551987da915Sopenharmony_ci	u8 *buffer;
552987da915Sopenharmony_ci	s64 res = 0;
553987da915Sopenharmony_ci
554987da915Sopenharmony_ci	if (!vol || !from || !to)
555987da915Sopenharmony_ci		return -1;
556987da915Sopenharmony_ci	if ((from->length != to->length) || (from->lcn < 0) || (to->lcn < 0))
557987da915Sopenharmony_ci		return -1;
558987da915Sopenharmony_ci
559987da915Sopenharmony_ci	//ntfs_log_info("data_copy: from 0x%llx to 0x%llx\n", from->lcn, to->lcn);
560987da915Sopenharmony_ci	buffer = malloc(vol->cluster_size);
561987da915Sopenharmony_ci	if (!buffer) {
562987da915Sopenharmony_ci		ntfs_log_info("!buffer\n");
563987da915Sopenharmony_ci		return -1;
564987da915Sopenharmony_ci	}
565987da915Sopenharmony_ci
566987da915Sopenharmony_ci	for (i = 0; i < from->length; i++) {
567987da915Sopenharmony_ci		//ntfs_log_info("read  cluster at %8lld\n", from->lcn+i);
568987da915Sopenharmony_ci		res = ntfs_pread(vol->dev, (from->lcn+i) * vol->cluster_size,
569987da915Sopenharmony_ci				vol->cluster_size, buffer);
570987da915Sopenharmony_ci		if (res != vol->cluster_size) {
571987da915Sopenharmony_ci			ntfs_log_error("!read\n");
572987da915Sopenharmony_ci			res = -1;
573987da915Sopenharmony_ci			break;
574987da915Sopenharmony_ci		}
575987da915Sopenharmony_ci
576987da915Sopenharmony_ci		//ntfs_log_info("write cluster to %8lld\n", to->lcn+i);
577987da915Sopenharmony_ci		res = ntfs_pwrite(vol->dev, (to->lcn+i) * vol->cluster_size,
578987da915Sopenharmony_ci				vol->cluster_size, buffer);
579987da915Sopenharmony_ci		if (res != vol->cluster_size) {
580987da915Sopenharmony_ci			ntfs_log_error("!write %lld\n", (long long)res);
581987da915Sopenharmony_ci			res = -1;
582987da915Sopenharmony_ci			break;
583987da915Sopenharmony_ci		}
584987da915Sopenharmony_ci	}
585987da915Sopenharmony_ci
586987da915Sopenharmony_ci	free(buffer);
587987da915Sopenharmony_ci	return res;
588987da915Sopenharmony_ci}
589987da915Sopenharmony_ci
590987da915Sopenharmony_ci/**
591987da915Sopenharmony_ci * move_runlist
592987da915Sopenharmony_ci *
593987da915Sopenharmony_ci * validate:
594987da915Sopenharmony_ci *   runlists are the same size
595987da915Sopenharmony_ci *   from in use
596987da915Sopenharmony_ci *   to not in use
597987da915Sopenharmony_ci * allocate new space
598987da915Sopenharmony_ci * copy data
599987da915Sopenharmony_ci * deallocate old space
600987da915Sopenharmony_ci */
601987da915Sopenharmony_cistatic s64 move_runlist(ntfs_volume *vol, runlist_element *from,
602987da915Sopenharmony_ci	runlist_element *to)
603987da915Sopenharmony_ci{
604987da915Sopenharmony_ci	int i;
605987da915Sopenharmony_ci
606987da915Sopenharmony_ci	if (!vol || !from || !to)
607987da915Sopenharmony_ci		return -1;
608987da915Sopenharmony_ci	if (from->length != to->length) {
609987da915Sopenharmony_ci		ntfs_log_error("diffsizes\n");
610987da915Sopenharmony_ci		return -1;
611987da915Sopenharmony_ci	}
612987da915Sopenharmony_ci
613987da915Sopenharmony_ci	if ((from->lcn < 0) || (to->lcn < 0)) {
614987da915Sopenharmony_ci		ntfs_log_error("invalid runs\n");
615987da915Sopenharmony_ci		return -1;
616987da915Sopenharmony_ci	}
617987da915Sopenharmony_ci
618987da915Sopenharmony_ci	for (i = 0; i < from->length; i++) {
619987da915Sopenharmony_ci		if (!utils_cluster_in_use(vol, from->lcn+i)) {
620987da915Sopenharmony_ci			ntfs_log_error("from not in use\n");
621987da915Sopenharmony_ci			return -1;
622987da915Sopenharmony_ci		}
623987da915Sopenharmony_ci	}
624987da915Sopenharmony_ci
625987da915Sopenharmony_ci	for (i = 0; i < to->length; i++) {
626987da915Sopenharmony_ci		if (utils_cluster_in_use(vol, to->lcn+i)) {
627987da915Sopenharmony_ci			ntfs_log_error("to is in use\n");
628987da915Sopenharmony_ci			return -1;
629987da915Sopenharmony_ci		}
630987da915Sopenharmony_ci	}
631987da915Sopenharmony_ci
632987da915Sopenharmony_ci	if (bitmap_alloc(vol, to) < 0) {
633987da915Sopenharmony_ci		ntfs_log_error("cannot bitmap_alloc\n");
634987da915Sopenharmony_ci		return -1;
635987da915Sopenharmony_ci	}
636987da915Sopenharmony_ci
637987da915Sopenharmony_ci	if (data_copy(vol, from, to) < 0) {
638987da915Sopenharmony_ci		ntfs_log_error("cannot data_copy\n");
639987da915Sopenharmony_ci		return -1;
640987da915Sopenharmony_ci	}
641987da915Sopenharmony_ci
642987da915Sopenharmony_ci	if (bitmap_free(vol, from) < 0) {
643987da915Sopenharmony_ci		ntfs_log_error("cannot bitmap_free\n");
644987da915Sopenharmony_ci		return -1;
645987da915Sopenharmony_ci	}
646987da915Sopenharmony_ci
647987da915Sopenharmony_ci	return 0;
648987da915Sopenharmony_ci}
649987da915Sopenharmony_ci
650987da915Sopenharmony_ci
651987da915Sopenharmony_ci/**original
652987da915Sopenharmony_ci * move_datarun
653987da915Sopenharmony_ci * > 0  Bytes moved / size to be moved
654987da915Sopenharmony_ci * = 0  Nothing to do
655987da915Sopenharmony_ci * < 0  Error
656987da915Sopenharmony_ci */
657987da915Sopenharmony_ci
658987da915Sopenharmony_ci// get size of runlist
659987da915Sopenharmony_ci// find somewhere to put data
660987da915Sopenharmony_ci// backup original runlist
661987da915Sopenharmony_ci// move the data
662987da915Sopenharmony_ci
663987da915Sopenharmony_ci// got to get the runlist out of this function
664987da915Sopenharmony_ci//      requires a mrec arg, not an ino (ino->mrec will do for now)
665987da915Sopenharmony_ci// check size of new runlist before allocating / moving
666987da915Sopenharmony_ci// replace one datarun with another (by hand)
667987da915Sopenharmony_cistatic s64 move_datarun(ntfs_volume *vol, ntfs_inode *ino, ATTR_RECORD *rec,
668987da915Sopenharmony_ci	runlist_element *run, u64 loc, int flags)
669987da915Sopenharmony_ci{
670987da915Sopenharmony_ci	runlist *from;
671987da915Sopenharmony_ci	runlist *to;
672987da915Sopenharmony_ci	int need_from;
673987da915Sopenharmony_ci	int need_to;
674987da915Sopenharmony_ci	int i;
675987da915Sopenharmony_ci	s64 res = -1;
676987da915Sopenharmony_ci
677987da915Sopenharmony_ci	// find empty space
678987da915Sopenharmony_ci	to = find_unused(vol, run->length, loc, flags);
679987da915Sopenharmony_ci	if (!to) {
680987da915Sopenharmony_ci		ntfs_log_error("!to\n");
681987da915Sopenharmony_ci		return -1;
682987da915Sopenharmony_ci	}
683987da915Sopenharmony_ci
684987da915Sopenharmony_ci	to->vcn = run->vcn;
685987da915Sopenharmony_ci
686987da915Sopenharmony_ci	// copy original runlist
687987da915Sopenharmony_ci	from = ntfs_mapping_pairs_decompress(vol, rec, NULL);
688987da915Sopenharmony_ci	if (!from) {
689987da915Sopenharmony_ci		ntfs_log_info("!from\n");
690987da915Sopenharmony_ci		return -1;
691987da915Sopenharmony_ci	}
692987da915Sopenharmony_ci
693987da915Sopenharmony_ci	ntfs_log_info("move %lld,%lld,%lld to %lld,%lld,%lld\n",
694987da915Sopenharmony_ci		(long long)run->vcn, (long long)run->lcn, (long long)run->length,
695987da915Sopenharmony_ci		(long long)to->vcn, (long long)to->lcn, (long long)to->length);
696987da915Sopenharmony_ci
697987da915Sopenharmony_ci	need_from = ntfs_get_size_for_mapping_pairs(vol, from, 0, INT_MAX);
698987da915Sopenharmony_ci	ntfs_log_info("orig data run = %d bytes\n", need_from);
699987da915Sopenharmony_ci
700987da915Sopenharmony_ci	//ntfs_debug_runlist_dump2(from, 5, "\t");
701987da915Sopenharmony_ci
702987da915Sopenharmony_ci	for (i = 0; to[i].length > 0; i++) {
703987da915Sopenharmony_ci		if (from[i].vcn == run->vcn) {
704987da915Sopenharmony_ci			from[i].lcn = to->lcn;
705987da915Sopenharmony_ci			break;
706987da915Sopenharmony_ci		}
707987da915Sopenharmony_ci	}
708987da915Sopenharmony_ci
709987da915Sopenharmony_ci	//ntfs_debug_runlist_dump2(from, 5, "\t");
710987da915Sopenharmony_ci
711987da915Sopenharmony_ci	need_to = ntfs_get_size_for_mapping_pairs(vol, from, 0, INT_MAX);
712987da915Sopenharmony_ci	ntfs_log_info("new  data run = %d bytes\n", need_to);
713987da915Sopenharmony_ci
714987da915Sopenharmony_ci	need_from = calc_attr_length(rec, need_from);
715987da915Sopenharmony_ci	need_to   = calc_attr_length(rec, need_to);
716987da915Sopenharmony_ci
717987da915Sopenharmony_ci	ntfs_log_info("Before %d, after %d\n", need_from, need_to);
718987da915Sopenharmony_ci
719987da915Sopenharmony_ci	if (need_from != need_to) {
720987da915Sopenharmony_ci		if (resize_nonres_attr(ino->mrec, rec, need_to) < 0) {
721987da915Sopenharmony_ci			ntfs_log_info("!resize\n");
722987da915Sopenharmony_ci			return -1;
723987da915Sopenharmony_ci		}
724987da915Sopenharmony_ci	}
725987da915Sopenharmony_ci
726987da915Sopenharmony_ci	res = move_runlist(vol, run, to);
727987da915Sopenharmony_ci	if (res < 0) {
728987da915Sopenharmony_ci		ntfs_log_error("!move_runlist\n");
729987da915Sopenharmony_ci		return -1;
730987da915Sopenharmony_ci	}
731987da915Sopenharmony_ci
732987da915Sopenharmony_ci	// wipe orig runs
733987da915Sopenharmony_ci	memset(((u8*)rec) + le16_to_cpu(rec->mapping_pairs_offset), 0, need_to - le16_to_cpu(rec->mapping_pairs_offset));
734987da915Sopenharmony_ci
735987da915Sopenharmony_ci	// update data runs
736987da915Sopenharmony_ci	ntfs_mapping_pairs_build(vol, ((u8*)rec) + le16_to_cpu(rec->mapping_pairs_offset),
737987da915Sopenharmony_ci			need_to, from, 0, NULL);
738987da915Sopenharmony_ci
739987da915Sopenharmony_ci	// commit
740987da915Sopenharmony_ci	ntfs_inode_mark_dirty(ino);
741987da915Sopenharmony_ci
742987da915Sopenharmony_ci	if (ntfs_inode_sync(ino) < 0) {
743987da915Sopenharmony_ci		ntfs_log_info("!sync\n");
744987da915Sopenharmony_ci		return -1;
745987da915Sopenharmony_ci	}
746987da915Sopenharmony_ci
747987da915Sopenharmony_ci	free(from);
748987da915Sopenharmony_ci	free(to);
749987da915Sopenharmony_ci	return res;
750987da915Sopenharmony_ci}
751987da915Sopenharmony_ci
752987da915Sopenharmony_ci/**
753987da915Sopenharmony_ci * move_attribute -
754987da915Sopenharmony_ci *
755987da915Sopenharmony_ci * > 0  Bytes moved / size to be moved
756987da915Sopenharmony_ci * = 0  Nothing to do
757987da915Sopenharmony_ci * < 0  Error
758987da915Sopenharmony_ci */
759987da915Sopenharmony_cistatic s64 move_attribute(ntfs_volume *vol, ntfs_inode *ino, ATTR_RECORD *rec,
760987da915Sopenharmony_ci	u64 loc, int flags)
761987da915Sopenharmony_ci{
762987da915Sopenharmony_ci	int i;
763987da915Sopenharmony_ci	s64 res;
764987da915Sopenharmony_ci	s64 count = 0;
765987da915Sopenharmony_ci	runlist *runs;
766987da915Sopenharmony_ci
767987da915Sopenharmony_ci	// NTFS_MOVE_LOC_BEST : assess how much space this attribute will need,
768987da915Sopenharmony_ci	// find that space and pass the location to our children.
769987da915Sopenharmony_ci	// Anything else we pass directly to move_datarun.
770987da915Sopenharmony_ci
771987da915Sopenharmony_ci	runs = ntfs_mapping_pairs_decompress(vol, rec, NULL);
772987da915Sopenharmony_ci	if (!runs) {
773987da915Sopenharmony_ci		ntfs_log_error("!runs\n");
774987da915Sopenharmony_ci		return -1;
775987da915Sopenharmony_ci	}
776987da915Sopenharmony_ci
777987da915Sopenharmony_ci	//ntfs_debug_runlist_dump2(runs, 5, "\t");
778987da915Sopenharmony_ci
779987da915Sopenharmony_ci	//ntfs_log_info("             VCN     LCN     Length\n");
780987da915Sopenharmony_ci	for (i = 0; runs[i].length > 0; i++) {
781987da915Sopenharmony_ci		if (runs[i].lcn == LCN_RL_NOT_MAPPED) {
782987da915Sopenharmony_ci			continue;
783987da915Sopenharmony_ci		}
784987da915Sopenharmony_ci
785987da915Sopenharmony_ci		res = move_datarun(vol, ino, rec, runs+i, loc, flags);
786987da915Sopenharmony_ci		//ntfs_log_info("        %8lld %8lld %8lld\n", runs[i].vcn, runs[i].lcn, runs[i].length);
787987da915Sopenharmony_ci		if (res < 0) {
788987da915Sopenharmony_ci			ntfs_log_error("!move_datarun\n");
789987da915Sopenharmony_ci			count = res;
790987da915Sopenharmony_ci			break;
791987da915Sopenharmony_ci		}
792987da915Sopenharmony_ci		count += res;
793987da915Sopenharmony_ci	}
794987da915Sopenharmony_ci
795987da915Sopenharmony_ci	return count;
796987da915Sopenharmony_ci}
797987da915Sopenharmony_ci
798987da915Sopenharmony_ci/**
799987da915Sopenharmony_ci * move_file -
800987da915Sopenharmony_ci *
801987da915Sopenharmony_ci * > 0  Bytes moved / size to be moved
802987da915Sopenharmony_ci * = 0  Nothing to do
803987da915Sopenharmony_ci * < 0  Error
804987da915Sopenharmony_ci */
805987da915Sopenharmony_cistatic s64 move_file(ntfs_volume *vol, ntfs_inode *ino, u64 loc, int flags)
806987da915Sopenharmony_ci{
807987da915Sopenharmony_ci	char *buffer;
808987da915Sopenharmony_ci	ntfs_attr_search_ctx *ctx;
809987da915Sopenharmony_ci	ATTR_RECORD *rec;
810987da915Sopenharmony_ci	s64 res;
811987da915Sopenharmony_ci	s64 count = 0;
812987da915Sopenharmony_ci
813987da915Sopenharmony_ci	buffer = malloc(MAX_PATH);
814987da915Sopenharmony_ci	if (!buffer) {
815987da915Sopenharmony_ci		ntfs_log_error("Out of memory\n");
816987da915Sopenharmony_ci		return -1;
817987da915Sopenharmony_ci	}
818987da915Sopenharmony_ci
819987da915Sopenharmony_ci	utils_inode_get_name(ino, buffer, MAX_PATH);
820987da915Sopenharmony_ci
821987da915Sopenharmony_ci	if (dont_move(ino)) {
822987da915Sopenharmony_ci		ntfs_log_error("can't move\n");
823987da915Sopenharmony_ci		return -1;
824987da915Sopenharmony_ci	}
825987da915Sopenharmony_ci
826987da915Sopenharmony_ci	ntfs_log_info("Moving %s\n", buffer);
827987da915Sopenharmony_ci
828987da915Sopenharmony_ci	// NTFS_MOVE_LOC_BEST : assess how much space all the attributes will need,
829987da915Sopenharmony_ci	// find that space and pass the location to our children.
830987da915Sopenharmony_ci	// Anything else we pass directly to move_attribute.
831987da915Sopenharmony_ci
832987da915Sopenharmony_ci	ctx = ntfs_attr_get_search_ctx(ino, NULL);
833987da915Sopenharmony_ci
834987da915Sopenharmony_ci	while ((rec = find_attribute(AT_UNUSED, ctx))) {
835987da915Sopenharmony_ci		utils_attr_get_name(vol, rec, buffer, MAX_PATH);
836987da915Sopenharmony_ci		ntfs_log_info("\tAttribute 0x%02x %s is ", le32_to_cpu(rec->type), buffer);
837987da915Sopenharmony_ci
838987da915Sopenharmony_ci		if (rec->non_resident) {
839987da915Sopenharmony_ci			ntfs_log_info("non-resident.   Moving it.\n");
840987da915Sopenharmony_ci
841987da915Sopenharmony_ci			res = move_attribute(vol, ino, rec, loc, flags);
842987da915Sopenharmony_ci			if (res < 0) {
843987da915Sopenharmony_ci				count = res;
844987da915Sopenharmony_ci				break;
845987da915Sopenharmony_ci			}
846987da915Sopenharmony_ci			count += res;
847987da915Sopenharmony_ci		} else {
848987da915Sopenharmony_ci			ntfs_log_info("resident.\n\t\tSkipping it.\n");
849987da915Sopenharmony_ci		}
850987da915Sopenharmony_ci	}
851987da915Sopenharmony_ci
852987da915Sopenharmony_ci	ntfs_attr_put_search_ctx(ctx);
853987da915Sopenharmony_ci	free(buffer);
854987da915Sopenharmony_ci	return count;
855987da915Sopenharmony_ci}
856987da915Sopenharmony_ci
857987da915Sopenharmony_ci
858987da915Sopenharmony_ci/**
859987da915Sopenharmony_ci * main - Begin here
860987da915Sopenharmony_ci *
861987da915Sopenharmony_ci * Start from here.
862987da915Sopenharmony_ci *
863987da915Sopenharmony_ci * Return:  0  Success, the program worked
864987da915Sopenharmony_ci *	    1  Error, something went wrong
865987da915Sopenharmony_ci */
866987da915Sopenharmony_ciint main(int argc, char *argv[])
867987da915Sopenharmony_ci{
868987da915Sopenharmony_ci	ntfs_volume *vol;
869987da915Sopenharmony_ci	ntfs_inode *inode;
870987da915Sopenharmony_ci	int flags = 0;
871987da915Sopenharmony_ci	int result = 1;
872987da915Sopenharmony_ci	s64 count;
873987da915Sopenharmony_ci
874987da915Sopenharmony_ci	ntfs_log_set_handler(ntfs_log_handler_outerr);
875987da915Sopenharmony_ci
876987da915Sopenharmony_ci	if (!parse_options(argc, argv))
877987da915Sopenharmony_ci		return 1;
878987da915Sopenharmony_ci
879987da915Sopenharmony_ci	utils_set_locale();
880987da915Sopenharmony_ci
881987da915Sopenharmony_ci	if (opts.noaction)
882987da915Sopenharmony_ci		flags |= NTFS_MNT_RDONLY;
883987da915Sopenharmony_ci	if (opts.force)
884987da915Sopenharmony_ci		flags |= NTFS_MNT_RECOVER;
885987da915Sopenharmony_ci
886987da915Sopenharmony_ci	vol = utils_mount_volume(opts.device, flags);
887987da915Sopenharmony_ci	if (!vol) {
888987da915Sopenharmony_ci		ntfs_log_info("!vol\n");
889987da915Sopenharmony_ci		return 1;
890987da915Sopenharmony_ci	}
891987da915Sopenharmony_ci
892987da915Sopenharmony_ci	inode = ntfs_pathname_to_inode(vol, NULL, opts.file);
893987da915Sopenharmony_ci	if (!inode) {
894987da915Sopenharmony_ci		ntfs_log_info("!inode\n");
895987da915Sopenharmony_ci		return 1;
896987da915Sopenharmony_ci	}
897987da915Sopenharmony_ci
898987da915Sopenharmony_ci	count = move_file(vol, inode, opts.location, 0);
899987da915Sopenharmony_ci	if ((count > 0) && (!opts.nodirty)) {
900987da915Sopenharmony_ci
901987da915Sopenharmony_ci		/* Porting note: libntfs-3g does not automatically set or clear
902987da915Sopenharmony_ci		 * dirty flags on mount/unmount. It always preserves them until
903987da915Sopenharmony_ci		 * they are explicitly changed with ntfs_volume_write_flags.
904987da915Sopenharmony_ci		 * This means that the dirty flag is possibly not set, but
905987da915Sopenharmony_ci		 * should be set. So we explicitly set it with a call to
906987da915Sopenharmony_ci		 * ntfs_volume_write_flags. */
907987da915Sopenharmony_ci		if(!(vol->flags & VOLUME_IS_DIRTY) && ntfs_volume_write_flags(
908987da915Sopenharmony_ci			vol, vol->flags | VOLUME_IS_DIRTY)) {
909987da915Sopenharmony_ci			ntfs_log_error("Error: Failed to set volume dirty "
910987da915Sopenharmony_ci				"flag (%d (%s))!\n", errno, strerror(errno));
911987da915Sopenharmony_ci		}
912987da915Sopenharmony_ci
913987da915Sopenharmony_ci		ntfs_log_info("Relocated %lld bytes\n", (long long)count);
914987da915Sopenharmony_ci	}
915987da915Sopenharmony_ci	if (count >= 0)
916987da915Sopenharmony_ci		result = 0;
917987da915Sopenharmony_ci
918987da915Sopenharmony_ci	if (result)
919987da915Sopenharmony_ci		ntfs_log_info("failed\n");
920987da915Sopenharmony_ci	else
921987da915Sopenharmony_ci		ntfs_log_info("success\n");
922987da915Sopenharmony_ci
923987da915Sopenharmony_ci	ntfs_inode_close(inode);
924987da915Sopenharmony_ci	ntfs_umount(vol, FALSE);
925987da915Sopenharmony_ci	return result;
926987da915Sopenharmony_ci}
927