• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /**
2  * ntfsfallocate
3  *
4  * Copyright (c) 2013-2020 Jean-Pierre Andre
5  *
6  * This utility will allocate clusters to a specified attribute belonging
7  * to a specified file or directory, to a specified length.
8  *
9  * WARNING : this can lead to configurations not supported by Windows
10  * and Windows may crash (BSOD) when writing to preallocated clusters
11  * which were not written to.
12  *
13  * This program is free software; you can redistribute it and/or modify
14  * it under the terms of the GNU General Public License as published by
15  * the Free Software Foundation; either version 2 of the License, or
16  * (at your option) any later version.
17  *
18  * This program is distributed in the hope that it will be useful,
19  * but WITHOUT ANY WARRANTY; without even the implied warranty of
20  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
21  * GNU General Public License for more details.
22  *
23  * You should have received a copy of the GNU General Public License
24  * along with this program (in the main directory of the Linux-NTFS source
25  * in the file COPYING); if not, write to the Free Software Foundation,
26  * Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
27  */
28 
29 #include "config.h"
30 
31 #ifdef HAVE_STDLIB_H
32 #include <stdlib.h>
33 #endif
34 #ifdef HAVE_STDIO_H
35 #include <stdio.h>
36 #endif
37 #ifdef HAVE_UNISTD_H
38 #include <unistd.h>
39 #endif
40 #ifdef HAVE_STDARG_H
41 #include <stdarg.h>
42 #endif
43 #ifdef HAVE_STRING_H
44 #include <string.h>
45 #endif
46 #ifdef HAVE_ERRNO_H
47 #include <errno.h>
48 #endif
49 #ifdef HAVE_TIME_H
50 #include <time.h>
51 #endif
52 #ifdef HAVE_GETOPT_H
53 #include <getopt.h>
54 #else
55 extern char *optarg;
56 extern int optind;
57 #endif
58 #ifdef HAVE_LIMITS_H
59 #include <limits.h>
60 #endif
61 
62 #ifndef LLONG_MAX
63 #define LLONG_MAX 9223372036854775807LL
64 #endif
65 
66 #include "types.h"
67 #include "attrib.h"
68 #include "inode.h"
69 #include "layout.h"
70 #include "volume.h"
71 #include "logging.h"
72 #include "runlist.h"
73 #include "dir.h"
74 #include "bitmap.h"
75 #include "lcnalloc.h"
76 #include "utils.h"
77 #include "misc.h"
78 
79 const char *EXEC_NAME = "ntfsfallocate";
80 
81 char *dev_name;
82 const char *file_name;
83 le32 attr_type;
84 ntfschar *attr_name = NULL;
85 u32 attr_name_len;
86 s64 opt_alloc_offs;
87 s64 opt_alloc_len;
88 
89 ATTR_DEF *attr_defs;
90 
91 static struct {
92 				/* -h, print usage and exit. */
93 	int no_action;		/*     do not write to device, only display
94 				       what would be done. */
95 	int no_size_change;	/* -n, do not change the apparent size */
96 	int quiet;		/* -q, quiet execution. */
97 	int verbose;		/* -v, verbose execution, given twice, really
98 				       verbose execution (debug mode). */
99 	int force;		/* -f, force allocation. */
100 				/* -V, print version and exit. */
101 } opts;
102 
103 static const struct option lopt[] = {
104 	{ "offset",	required_argument,	NULL, 'o' },
105 	{ "length",	required_argument,	NULL, 'l' },
106 	{ "force",	no_argument,		NULL, 'f' },
107 	{ "help",	no_argument,		NULL, 'h' },
108 	{ "no-action",	no_argument,		NULL, 'N' },
109 	{ "no-size-change",	no_argument,	NULL, 'n' },
110 	{ "quiet",	no_argument,		NULL, 'q' },
111 	{ "version",	no_argument,		NULL, 'V' },
112 	{ "verbose",	no_argument,		NULL, 'v' },
113 	{ NULL,		0,			NULL, 0   }
114 };
115 
116 /**
117  * err_exit - error output and terminate; ignores quiet (-q)
118  *
119  *	DO NOT USE when allocations are not in initial state
120  */
121 __attribute__((noreturn))
122 __attribute__((format(printf, 2, 3)))
err_exit(ntfs_volume * vol,const char * fmt,...)123 static void err_exit(ntfs_volume *vol, const char *fmt, ...)
124 {
125 	va_list ap;
126 
127 	fprintf(stderr, "ERROR: ");
128 	va_start(ap, fmt);
129 	vfprintf(stderr, fmt, ap);
130 	va_end(ap);
131 	fprintf(stderr, "Aborting...\n");
132 	if (vol && ntfs_umount(vol, 0))
133 		fprintf(stderr, "Warning: Could not umount %s\n", dev_name);
134 	exit(1);
135 }
136 
137 /**
138  * copyright - print copyright statements
139  */
copyright(void)140 static void copyright(void)
141 {
142 	fprintf(stderr, "Copyright (c) 2013-2014 Jean-Pierre Andre\n"
143 			"Allocate clusters to a specified attribute of "
144 			"a specified file.\n");
145 }
146 
147 /**
148  * license - print license statement
149  */
license(void)150 static void license(void)
151 {
152 	fprintf(stderr, "%s", ntfs_gpl);
153 }
154 
155 /**
156  * usage - print a list of the parameters to the program
157  */
158 __attribute__((noreturn))
usage(int ret)159 static void usage(int ret)
160 {
161 	copyright();
162 	fprintf(stderr, "Usage: %s [options] -l length device file [attr-type "
163 			"[attr-name]]\n"
164 			"    If attr-type is not specified, 0x80 (i.e. $DATA) "
165 			"is assumed.\n"
166 			"    If attr-name is not specified, an unnamed "
167 			"attribute is assumed.\n"
168 			"    -f         Force execution despite errors\n"
169 			"    -n         Do not change the apparent size of file\n"
170 			"    -l length  Allocate length bytes\n"
171 			"    -o offset  Start allocating at offset\n"
172 			"    -v         Verbose execution\n"
173 			"    -vv        Very verbose execution\n"
174 			"    -V         Display version information\n"
175 			"    -h         Display this help\n", EXEC_NAME);
176 	fprintf(stderr, "%s%s", ntfs_bugs, ntfs_home);
177 	exit(ret);
178 }
179 
180 /**
181  * err_exit - error output, display usage and exit
182  */
183 __attribute__((noreturn))
184 __attribute__((format(printf, 1, 2)))
err_usage(const char * fmt,...)185 static void err_usage(const char *fmt, ...)
186 {
187 	va_list ap;
188 
189 	fprintf(stderr, "ERROR: ");
190 	va_start(ap, fmt);
191 	vfprintf(stderr, fmt, ap);
192 	va_end(ap);
193 	fprintf(stderr, "\n");
194 	usage(1);
195 }
196 
197 /*
198  *		Get a value option with a possible multiple suffix
199  */
200 
option_value(const char * arg)201 static s64 option_value(const char *arg)
202 {
203 	s64 ll;
204 	char *s;
205 	s64 fact;
206 	int count;
207 	BOOL err;
208 
209 	err = FALSE;
210 	ll = strtoll(arg, &s, 0);
211 	if ((ll >= LLONG_MAX) && (errno == ERANGE))
212 		err_exit((ntfs_volume*)NULL, "Too big value : %s\n",arg);
213 	if (*s) {
214 		count = 0;
215 		switch (*s++) {
216 		case 'E' : count++;
217 			/* FALLTHRU */
218 		case 'P' : count++;
219 			/* FALLTHRU */
220 		case 'T' : count++;
221 			/* FALLTHRU */
222 		case 'G' : count++;
223 			/* FALLTHRU */
224 		case 'M' : count++;
225 			/* FALLTHRU */
226 		case 'K' : count++;
227 			switch (*s++) {
228 			case 'i' :
229 				fact = 1024;
230 				if (*s++ != 'B')
231 					err = TRUE;
232 				break;
233 			case 'B' :
234 				fact = 1000;
235 				break;
236 			case '\0' :
237 				fact = 1024;
238 				s--;
239 				break;
240 			default :
241 				err = TRUE;
242 				fact = 1;
243 				break;
244 			}
245 			if (*s)
246 				err = TRUE;
247 			break;
248 		default :
249 			err = TRUE;
250 			break;
251 		}
252 		if (err)
253 			err_exit((ntfs_volume*)NULL,
254 					"Invalid suffix in : %s\n",arg);
255 		else
256 			while (count-- > 0) {
257 				if (ll > LLONG_MAX/1024)
258 					err_exit((ntfs_volume*)NULL,
259 						"Too big value : %s\n",arg);
260 				ll *= fact;
261 			}
262 	}
263 	return (ll);
264 }
265 
266 
267 /**
268  * parse_options
269  */
parse_options(int argc,char * argv[])270 static void parse_options(int argc, char *argv[])
271 {
272 	long long ll;
273 	char *s, *s2;
274 	int c;
275 
276 	opt_alloc_len = 0;
277 	opt_alloc_offs = 0;
278 	if (argc && *argv)
279 		EXEC_NAME = *argv;
280 	fprintf(stderr, "%s v%s (libntfs-3g)\n", EXEC_NAME, VERSION);
281 	while ((c = getopt_long(argc, argv, "fh?no:qvVl:", lopt, NULL)) != EOF) {
282 		switch (c) {
283 		case 'f':
284 			opts.force = 1;
285 			break;
286 		case 'n':
287 			opts.no_size_change = 1;
288 			break;
289 		case 'N':		/* Not proposed as a short option */
290 			opts.no_action = 1;
291 			break;
292 		case 'q':
293 			opts.quiet = 1;
294 			ntfs_log_clear_levels(NTFS_LOG_LEVEL_QUIET);
295 			break;
296 		case 'v':
297 			opts.verbose++;
298 			ntfs_log_set_levels(NTFS_LOG_LEVEL_VERBOSE);
299 			break;
300 		case 'V':
301 			/* Version number already printed */
302 			license();
303 			exit(0);
304 		case 'l':
305 			ll = option_value(argv[optind - 1]);
306 			if ((ll <= 0)
307 			    || (ll >= LLONG_MAX && errno == ERANGE))
308 				err_usage("Invalid length : %s\n",
309 					argv[optind - 1]);
310 			opt_alloc_len = ll;
311 			break;
312 		case 'o':
313 			ll = option_value(argv[optind - 1]);
314 			if ((ll < 0)
315 			    || (ll >= LLONG_MAX && errno == ERANGE))
316 				err_usage("Invalid offset : %s\n",
317 					argv[optind - 1]);
318 			opt_alloc_offs = ll;
319 			break;
320 		case 'h':
321 			usage(0);
322 		case '?':
323 		default:
324 			usage(1);
325 		}
326 	}
327 	if (!opt_alloc_len) {
328 		err_usage("Missing allocation length\n");
329 	}
330 
331 	ntfs_log_verbose("length = %lli = 0x%llx\n",
332 			(long long)opt_alloc_len, (long long)opt_alloc_len);
333 	ntfs_log_verbose("offset = %lli = 0x%llx\n",
334 			(long long)opt_alloc_offs, (long long)opt_alloc_offs);
335 
336 	if (optind == argc)
337 		usage(1);
338 
339 	if (opts.verbose > 1)
340 		ntfs_log_set_levels(NTFS_LOG_LEVEL_DEBUG | NTFS_LOG_LEVEL_TRACE |
341 			NTFS_LOG_LEVEL_VERBOSE | NTFS_LOG_LEVEL_QUIET);
342 
343 	/* Get the device. */
344 	dev_name = argv[optind++];
345 	ntfs_log_verbose("device name = %s\n", dev_name);
346 
347 	if (optind == argc)
348 		usage(1);
349 
350 	/* Get the file name. */
351 	file_name = argv[optind++];
352 	ntfs_log_verbose("file name = \"%s\"\n", file_name);
353 
354 	/* Get the attribute type, if specified. */
355 	if (optind == argc) {
356 		attr_type = AT_DATA;
357 		attr_name = AT_UNNAMED;
358 		attr_name_len = 0;
359 	} else {
360 		unsigned long ul;
361 
362 		s = argv[optind++];
363 		ul = strtoul(s, &s2, 0);
364 		if (*s2 || !ul || (ul >= ULONG_MAX && errno == ERANGE))
365 			err_usage("Invalid attribute type %s: %s\n", s,
366 					strerror(errno));
367 		attr_type = cpu_to_le32(ul);
368 
369 		/* Get the attribute name, if specified. */
370 		if (optind != argc) {
371 			s = argv[optind++];
372 			/* Convert the string to little endian Unicode. */
373 			attr_name_len = ntfs_mbstoucs(s, &attr_name);
374 			if ((int)attr_name_len < 0)
375 				err_usage("Invalid attribute name "
376 						"\"%s\": %s\n",
377 						s, strerror(errno));
378 
379 			/* Keep hold of the original string. */
380 			s2 = s;
381 
382 			s = argv[optind++];
383 			if (optind != argc)
384 				usage(1);
385 		} else {
386 			attr_name = AT_UNNAMED;
387 			attr_name_len = 0;
388 		}
389 	}
390 	ntfs_log_verbose("attribute type = 0x%lx\n",
391 					(unsigned long)le32_to_cpu(attr_type));
392 	if (attr_name == AT_UNNAMED)
393 		ntfs_log_verbose("attribute name = \"\" (UNNAMED)\n");
394 	else
395 		ntfs_log_verbose("attribute name = \"%s\" (length %u "
396 				"Unicode characters)\n", s2,
397 				(unsigned int)attr_name_len);
398 }
399 
400 /*
401  *		Save the initial runlist, to be restored on error
402  */
403 
ntfs_save_rl(runlist_element * rl)404 static runlist_element *ntfs_save_rl(runlist_element *rl)
405 {
406 	runlist_element *save;
407 	int n;
408 
409 	n = 0;
410 	save = (runlist_element*)NULL;
411 	if (rl) {
412 		while (rl[n].length)
413 			n++;
414 		save = (runlist_element*)malloc((n + 1)*sizeof(runlist_element));
415 		if (save) {
416 			memcpy(save, rl, (n + 1)*sizeof(runlist_element));
417 		}
418 	}
419 	return (save);
420 }
421 
422 /*
423  *		Free the common part of two runs
424  */
425 
free_common(ntfs_volume * vol,runlist_element * brl,s64 blth,runlist_element * grl,s64 glth)426 static void free_common(ntfs_volume *vol, runlist_element *brl, s64 blth,
427 				runlist_element *grl, s64 glth)
428 {
429 	VCN begin_common;
430 	VCN end_common;
431 
432 	begin_common = max(grl->vcn, brl->vcn);
433 	end_common = min(grl->vcn + glth, brl->vcn + blth);
434 	if (end_common > begin_common) {
435 		if (ntfs_bitmap_clear_run(vol->lcnbmp_na,
436 			brl->lcn + begin_common - brl->vcn,
437 					end_common - begin_common))
438 			ntfs_log_error("Failed to free %lld clusters "
439 				"from 0x%llx\n",
440 				(long long)end_common - begin_common,
441 				(long long)(brl->lcn + begin_common
442 							- brl->vcn));
443 	}
444 }
445 
446 /*
447  *		Restore the cluster allocations to initial state
448  *
449  *	If a new error occurs, only output a message
450  */
451 
ntfs_restore_rl(ntfs_attr * na,runlist_element * oldrl)452 static void ntfs_restore_rl(ntfs_attr *na, runlist_element *oldrl)
453 {
454 	runlist_element *brl; /* Pointer to bad runlist */
455 	runlist_element *grl; /* Pointer to good runlist */
456 	ntfs_volume *vol;
457 
458 	vol = na->ni->vol;
459 		/* Examine allocated entries from the bad runlist */
460 	for (brl=na->rl; brl->length; brl++) {
461 		if (brl->lcn != LCN_HOLE) {
462 // TODO improve by examining both list in parallel
463 		/* Find the holes in the good runlist which overlap */
464 			for (grl=oldrl; grl->length
465 			   && (grl->vcn<=(brl->vcn+brl->length)); grl++) {
466 				if (grl->lcn == LCN_HOLE) {
467 					free_common(vol, brl, brl->length, grl,
468 						grl->length);
469 				}
470 			}
471 			/* Free allocations beyond the end of good runlist */
472 			if (grl && !grl->length
473 			    && ((brl->vcn + brl->length) > grl->vcn)) {
474 				free_common(vol, brl, brl->length, grl,
475 					brl->vcn + brl->length - grl->vcn);
476 			}
477 		}
478 	}
479 	free(na->rl);
480 	na->rl = oldrl;
481 	if (ntfs_attr_update_mapping_pairs(na, 0)) {
482 		ntfs_log_error("Failed to restore the original runlist\n");
483 	}
484 }
485 
486 /*
487  *		Zero newly allocated runs up to initialized_size
488  */
489 
ntfs_inner_zero(ntfs_attr * na,runlist_element * rl)490 static int ntfs_inner_zero(ntfs_attr *na, runlist_element *rl)
491 {
492 	ntfs_volume *vol;
493 	char *buf;
494 	runlist_element *zrl;
495 	s64 cofs;
496 	s64 pos;
497 	s64 zeroed;
498 	int err;
499 
500 	err = 0;
501 	vol = na->ni->vol;
502 	buf = (char*)malloc(vol->cluster_size);
503 	if (buf) {
504 		memset(buf, 0, vol->cluster_size);
505 		zrl = rl;
506 		pos = zrl->vcn << vol->cluster_size_bits;
507 		while (zrl->length
508 	 	    && !err
509 	    	    && (pos < na->initialized_size)) {
510 			for (cofs=0; cofs<zrl->length && !err; cofs++) {
511 				zeroed = ntfs_pwrite(vol->dev,
512 					(rl->lcn + cofs)
513 						<< vol->cluster_size_bits,
514 					vol->cluster_size, buf);
515 				if (zeroed != vol->cluster_size) {
516 					ntfs_log_error("Failed to zero at "
517 						"offset %lld\n",
518 						(long long)pos);
519 					errno = EIO;
520 					err = -1;
521 				}
522 				pos += vol->cluster_size;
523 			}
524 			zrl++;
525 			pos = zrl->vcn << vol->cluster_size_bits;
526 		}
527 		free(buf);
528 	} else {
529 		ntfs_log_error("Failed to allocate memory\n");
530 		errno = ENOSPC;
531 		err = -1;
532 	}
533 	return (err);
534 }
535 
536 /*
537  *		Merge newly allocated runs into runlist
538  */
539 
ntfs_merge_allocation(ntfs_attr * na,runlist_element * rl,s64 size)540 static int ntfs_merge_allocation(ntfs_attr *na, runlist_element *rl,
541 				s64 size)
542 {
543 	ntfs_volume *vol;
544 	int err;
545 
546 	err = 0;
547 	vol = na->ni->vol;
548 	/* Newly allocated clusters before initialized size need be zeroed */
549 	if ((rl->vcn << vol->cluster_size_bits) < na->initialized_size) {
550 		err = ntfs_inner_zero(na, rl);
551 	}
552 	if (!err) {
553 		if (na->data_flags & ATTR_IS_SPARSE) {
554 			na->compressed_size += size;
555 			if (na->compressed_size >= na->allocated_size) {
556 				na->data_flags &= ~ATTR_IS_SPARSE;
557 				if (na->compressed_size > na->allocated_size) {
558 					ntfs_log_error("File size error : "
559 						"apparent %lld, "
560 						"compressed %lld > "
561 						"allocated %lld",
562 						(long long)na->data_size,
563 						(long long)na->compressed_size,
564 						(long long)na->allocated_size);
565 					errno = EIO;
566 					err = -1;
567 				}
568 			}
569 		}
570 	}
571 	if (!err) {
572 		rl = ntfs_runlists_merge(na->rl, rl);
573 		if (!rl) {
574 			ntfs_log_error("Failed to merge the new allocation\n");
575 			err = -1;
576 		} else {
577 			na->rl = rl;
578 				/* Update the runlist */
579 			if (ntfs_attr_update_mapping_pairs(na, 0)) {
580 				ntfs_log_error(
581 					"Failed to update the runlist\n");
582 				err = -1;
583 			}
584 		}
585 	}
586 	return (err);
587 }
588 
ntfs_inner_allocation(ntfs_attr * na,s64 alloc_offs,s64 alloc_len)589 static int ntfs_inner_allocation(ntfs_attr *na, s64 alloc_offs, s64 alloc_len)
590 {
591 	ntfs_volume *vol;
592 	runlist_element *rl;
593 	runlist_element *prl;
594 	runlist_element *rlc;
595 	VCN from_vcn;
596 	VCN end_vcn;
597 	LCN lcn_seek_from;
598 	VCN from_hole;
599 	VCN end_hole;
600 	s64 need;
601 	int err;
602 	BOOL done;
603 
604 	err = 0;
605 	vol = na->ni->vol;
606 		/* Find holes which overlap the requested allocation */
607 	from_vcn = alloc_offs >> vol->cluster_size_bits;
608 	end_vcn = (alloc_offs + alloc_len + vol->cluster_size - 1)
609 			>> vol->cluster_size_bits;
610 	do {
611 		done = FALSE;
612 		rl = na->rl;
613 		while (rl->length
614 		    && ((rl->lcn >= 0)
615 		    	|| ((rl->vcn + rl->length) <= from_vcn)
616 			|| (rl->vcn >= end_vcn)))
617 				rl++;
618 		if (!rl->length)
619 			done = TRUE;
620 		else {
621 			from_hole = max(from_vcn, rl->vcn);
622 			end_hole = min(end_vcn, rl->vcn + rl->length);
623 			need = end_hole - from_hole;
624 			lcn_seek_from = -1;
625 			if (rl->vcn) {
626 					/* Avoid fragmentation when possible */
627 				prl = rl;
628 				if ((--prl)->lcn >= 0) {
629 					lcn_seek_from = prl->lcn
630 						+ from_hole - prl->vcn;
631 				}
632 			}
633 			if (need <= 0) {
634 				ntfs_log_error("Wrong hole size %lld\n",
635 							(long long)need);
636 				errno = EIO;
637 				err = -1;
638 			} else {
639 				rlc = ntfs_cluster_alloc(vol, from_hole, need,
640 					 lcn_seek_from, DATA_ZONE);
641 				if (!rlc)
642 					err = -1;
643 				else
644 					err = ntfs_merge_allocation(na, rlc,
645 						need << vol->cluster_size_bits);
646 			}
647 		}
648 	} while (!err && !done);
649 	return (err);
650 }
651 
ntfs_full_allocation(ntfs_attr * na,ntfs_attr_search_ctx * ctx,s64 alloc_offs,s64 alloc_len)652 static int ntfs_full_allocation(ntfs_attr *na, ntfs_attr_search_ctx *ctx,
653 				s64 alloc_offs, s64 alloc_len)
654 {
655 	ATTR_RECORD *attr;
656 	ntfs_inode *ni;
657 	s64 initialized_size;
658 	s64 data_size;
659 	int err;
660 
661 	err = 0;
662 	initialized_size = na->initialized_size;
663 	data_size = na->data_size;
664 
665 	if (na->allocated_size <= alloc_offs) {
666 		/*
667 		 * Request is fully beyond what was already allocated :
668 		 * only need to expand the attribute
669 		 */
670 		err = ntfs_attr_truncate(na, alloc_offs);
671 		if (!err)
672 			err = ntfs_attr_truncate_solid(na,
673 						alloc_offs + alloc_len);
674 	} else {
675 		/*
676 		 * Request overlaps what was already allocated :
677 		 * We may have to fill existing holes, and force zeroes
678 		 * into clusters which are visible.
679 		 */
680 		if ((alloc_offs + alloc_len) > na->allocated_size)
681 			err = ntfs_attr_truncate(na, alloc_offs + alloc_len);
682 		if (!err)
683 			err = ntfs_inner_allocation(na, alloc_offs, alloc_len);
684 	}
685 		/* Set the sizes, even after an error, to keep consistency */
686 	na->initialized_size = initialized_size;
687 		/* Restore the original apparent size if requested or error */
688 	if (err || opts.no_size_change
689 	    || ((alloc_offs + alloc_len) < data_size))
690 		na->data_size = data_size;
691 	else {
692 		/*
693 		 * "man 1 fallocate" does not define the new apparent size
694 		 * when size change is allowed (no --keep-size).
695 		 * Assuming the same as no FALLOC_FL_KEEP_SIZE in fallocate(2) :
696 		 * "the file size will be changed if offset + len is greater
697 		 * than the  file  size"
698 // TODO check the behavior of another file system
699 		 */
700 		na->data_size = alloc_offs + alloc_len;
701 	}
702 
703 	if (!err) {
704 	/* Find the attribute, which may have been relocated for allocations */
705 		if (ntfs_attr_lookup(attr_type, attr_name, attr_name_len,
706 					CASE_SENSITIVE, 0, NULL, 0, ctx)) {
707 			err = -1;
708 			ntfs_log_error("Failed to locate the attribute\n");
709 		} else {
710 				/* Feed the sizes into the attribute */
711 			attr = ctx->attr;
712 			attr->data_size = cpu_to_sle64(na->data_size);
713 			attr->initialized_size
714 				= cpu_to_sle64(na->initialized_size);
715 			attr->allocated_size
716 				= cpu_to_sle64(na->allocated_size);
717 			if (na->data_flags & ATTR_IS_SPARSE)
718 				attr->compressed_size
719 					= cpu_to_sle64(na->compressed_size);
720 			/* Copy the unnamed data attribute sizes to inode */
721 			if ((attr_type == AT_DATA) && !attr_name_len) {
722 				ni = na->ni;
723 				ni->data_size = na->data_size;
724 				if (na->data_flags & ATTR_IS_SPARSE) {
725 					ni->allocated_size
726 						= na->compressed_size;
727 					ni->flags |= FILE_ATTR_SPARSE_FILE;
728 				} else
729 					ni->allocated_size
730 						= na->allocated_size;
731 			}
732 		}
733 	}
734 	return (err);
735 }
736 
737 
738 /*
739  *		Do the actual allocations
740  */
741 
ntfs_fallocate(ntfs_inode * ni,s64 alloc_offs,s64 alloc_len)742 static int ntfs_fallocate(ntfs_inode *ni, s64 alloc_offs, s64 alloc_len)
743 {
744 	s64 allocated_size;
745 	s64 data_size;
746 	ntfs_attr_search_ctx *ctx;
747 	ntfs_attr *na;
748 	runlist_element *oldrl;
749 	const char *errmess;
750 	int save_errno;
751 	int err;
752 
753 	err = 0;
754 	/* Open the specified attribute. */
755 	na = ntfs_attr_open(ni, attr_type, attr_name, attr_name_len);
756 	if (!na) {
757 		ntfs_log_perror("Failed to open attribute 0x%lx: ",
758 				(unsigned long)le32_to_cpu(attr_type));
759 		err = -1;
760 	} else {
761 		errmess = (const char*)NULL;
762 		if (na->data_flags & ATTR_IS_COMPRESSED) {
763 			errmess= "Cannot fallocate a compressed file";
764 		}
765 
766 		/* Locate the attribute record, needed for updating sizes */
767 		ctx = ntfs_attr_get_search_ctx(ni, NULL);
768 		if (!ctx) {
769 			errmess = "Failed to allocate a search context";
770 		}
771 		if (errmess) {
772 			ntfs_log_error("%s\n",errmess);
773 			err = -1;
774 		} else {
775 			/* Get and save the initial allocations */
776 			allocated_size = na->allocated_size;
777 			data_size = ni->data_size;
778 			if (na->rl)
779 				err = ntfs_attr_map_whole_runlist(na);
780 			if (!err) {
781 				if (na->rl)
782 					oldrl = ntfs_save_rl(na->rl);
783 				else
784 					oldrl = (runlist_element*)NULL;
785 				if (!na->rl || oldrl) {
786 					err = ntfs_full_allocation(na, ctx,
787 							alloc_offs, alloc_len);
788 					if (err) {
789 						save_errno = errno;
790 						ni->allocated_size
791 							= allocated_size;
792 						ni->data_size = data_size;
793 						ntfs_restore_rl(na, oldrl);
794 						errno = save_errno;
795 					} else {
796 						free(oldrl);
797 	/* Mark file name dirty, to update the sizes in directories */
798 						NInoFileNameSetDirty(ni);
799 						NInoSetDirty(ni);
800 					}
801 				} else
802 					err = -1;
803 			}
804 			ntfs_attr_put_search_ctx(ctx);
805 		}
806 		/* Close the attribute. */
807 		ntfs_attr_close(na);
808 	}
809 	return (err);
810 }
811 
812 /**
813  * main
814  */
main(int argc,char ** argv)815 int main(int argc, char **argv)
816 {
817 	unsigned long mnt_flags, ul;
818 	int err;
819 	ntfs_inode *ni;
820 	ntfs_volume *vol;
821 #ifdef HAVE_WINDOWS_H
822 	char *unix_name;
823 #endif
824 
825 	vol = (ntfs_volume*)NULL;
826 	ntfs_log_set_handler(ntfs_log_handler_outerr);
827 
828 	/* Initialize opts to zero / required values. */
829 	memset(&opts, 0, sizeof(opts));
830 
831 	/* Parse command line options. */
832 	parse_options(argc, argv);
833 
834 	utils_set_locale();
835 
836 	/* Make sure the file system is not mounted. */
837 	if (ntfs_check_if_mounted(dev_name, &mnt_flags))
838 		ntfs_log_perror("Failed to determine whether %s is mounted",
839 				dev_name);
840 	else if (mnt_flags & NTFS_MF_MOUNTED) {
841 		ntfs_log_error("%s is mounted.\n", dev_name);
842 		if (!opts.force)
843 			err_exit((ntfs_volume*)NULL, "Refusing to run!\n");
844 		fprintf(stderr, "ntfsfallocate forced anyway. Hope /etc/mtab "
845 				"is incorrect.\n");
846 	}
847 
848 	/* Mount the device. */
849 	if (opts.no_action) {
850 		ntfs_log_quiet("Running in READ-ONLY mode!\n");
851 		ul = NTFS_MNT_RDONLY;
852 	} else
853 		if (opts.force)
854 			ul = NTFS_MNT_RECOVER;
855 		else
856 			ul = 0;
857 	vol = ntfs_mount(dev_name, ul);
858 	if (!vol)
859 		err_exit(vol, "Failed to mount %s: %s\n", dev_name,
860 			strerror(errno));
861 
862 	if ((vol->flags & VOLUME_IS_DIRTY) && !opts.force)
863 		err_exit(vol, "Volume is dirty, please run chkdsk.\n");
864 
865 	if (ntfs_volume_get_free_space(vol))
866 		err_exit(vol, "Failed to get free clusters %s: %s\n",
867 					dev_name, strerror(errno));
868 
869 	/* Open the specified inode. */
870 #ifdef HAVE_WINDOWS_H
871 	unix_name = ntfs_utils_unix_path(file_name);
872 	if (unix_name) {
873 		ni = ntfs_pathname_to_inode(vol, NULL, unix_name);
874 		free(unix_name);
875 	} else
876 		ni = (ntfs_inode*)NULL;
877 #else
878 	ni = ntfs_pathname_to_inode(vol, NULL, file_name);
879 #endif
880 	if (!ni)
881 		err_exit(vol, "Failed to open file \"%s\": %s\n", file_name,
882 				strerror(errno));
883 	if (!opts.no_action)
884 		err = ntfs_fallocate(ni, opt_alloc_offs, opt_alloc_len);
885 
886 	/* Close the inode. */
887 	if (ntfs_inode_close(ni)) {
888 		err = -1;
889 		err_exit(vol, "Failed to close inode \"%s\" : %s\n", file_name,
890 				strerror(errno));
891 	}
892 
893 	/* Unmount the volume. */
894 	err = ntfs_umount(vol, 0);
895 	vol = (ntfs_volume*)NULL;
896 	if (err)
897 		ntfs_log_perror("Warning: Failed to umount %s", dev_name);
898 
899 	/* Free the attribute name if it exists. */
900 	if (attr_name_len)
901 		ntfs_ucsfree(attr_name);
902 
903 	ntfs_log_quiet("ntfsfallocate completed successfully. Have a nice day.\n");
904 	return 0;
905 }
906