blob: b0a7c4ecb609cf191f616c48d62eaea64f14dfa7 [file] [log] [blame]
Steve Kondik79165c32015-11-09 19:43:00 -08001/**
2 * ntfsfallocate
3 *
4 * Copyright (c) 2013-2014 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
55extern char *optarg;
56extern 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
79const char *EXEC_NAME = "ntfsfallocate";
80
81char *dev_name;
82const char *file_name;
83le32 attr_type;
84ntfschar *attr_name = NULL;
85u32 attr_name_len;
86s64 opt_alloc_offs;
87s64 opt_alloc_len;
88
89ATTR_DEF *attr_defs;
90
91static 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
103static 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)))
123static 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 */
140static 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 */
150static 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))
159static 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)))
185static 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
201static 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 case 'P' : count++;
218 case 'T' : count++;
219 case 'G' : count++;
220 case 'M' : count++;
221 case 'K' : count++;
222 switch (*s++) {
223 case 'i' :
224 fact = 1024;
225 if (*s++ != 'B')
226 err = TRUE;
227 break;
228 case 'B' :
229 fact = 1000;
230 break;
231 case '\0' :
232 fact = 1024;
233 s--;
234 break;
235 default :
236 err = TRUE;
237 fact = 1;
238 break;
239 }
240 if (*s)
241 err = TRUE;
242 break;
243 default :
244 err = TRUE;
245 break;
246 }
247 if (err)
248 err_exit((ntfs_volume*)NULL,
249 "Invalid suffix in : %s\n",arg);
250 else
251 while (count-- > 0) {
252 if (ll > LLONG_MAX/1024)
253 err_exit((ntfs_volume*)NULL,
254 "Too big value : %s\n",arg);
255 ll *= fact;
256 }
257 }
258 return (ll);
259}
260
261
262/**
263 * parse_options
264 */
265static void parse_options(int argc, char *argv[])
266{
267 long long ll;
268 char *s, *s2;
269 int c;
270
271 opt_alloc_len = 0;
272 opt_alloc_offs = 0;
273 if (argc && *argv)
274 EXEC_NAME = *argv;
275 fprintf(stderr, "%s v%s (libntfs-3g)\n", EXEC_NAME, VERSION);
276 while ((c = getopt_long(argc, argv, "fh?no:qvVl:", lopt, NULL)) != EOF) {
277 switch (c) {
278 case 'f':
279 opts.force = 1;
280 break;
281 case 'n':
282 opts.no_size_change = 1;
283 break;
284 case 'N': /* Not proposed as a short option */
285 opts.no_action = 1;
286 break;
287 case 'q':
288 opts.quiet = 1;
289 ntfs_log_clear_levels(NTFS_LOG_LEVEL_QUIET);
290 break;
291 case 'v':
292 opts.verbose++;
293 ntfs_log_set_levels(NTFS_LOG_LEVEL_VERBOSE);
294 break;
295 case 'V':
296 /* Version number already printed */
297 license();
298 exit(0);
299 case 'l':
300 ll = option_value(argv[optind - 1]);
301 if ((ll <= 0)
302 || (ll >= LLONG_MAX && errno == ERANGE))
303 err_usage("Invalid length : %s\n",
304 argv[optind - 1]);
305 opt_alloc_len = ll;
306 break;
307 case 'o':
308 ll = option_value(argv[optind - 1]);
309 if ((ll < 0)
310 || (ll >= LLONG_MAX && errno == ERANGE))
311 err_usage("Invalid offset : %s\n",
312 argv[optind - 1]);
313 opt_alloc_offs = ll;
314 break;
315 case 'h':
316 usage(0);
317 case '?':
318 default:
319 usage(1);
320 }
321 }
322 if (!opt_alloc_len) {
323 err_usage("Missing allocation length\n");
324 }
325
326 ntfs_log_verbose("length = %lli = 0x%llx\n",
327 (long long)opt_alloc_len, (long long)opt_alloc_len);
328 ntfs_log_verbose("offset = %lli = 0x%llx\n",
329 (long long)opt_alloc_offs, (long long)opt_alloc_offs);
330
331 if (optind == argc)
332 usage(1);
333
334 if (opts.verbose > 1)
335 ntfs_log_set_levels(NTFS_LOG_LEVEL_DEBUG | NTFS_LOG_LEVEL_TRACE |
336 NTFS_LOG_LEVEL_VERBOSE | NTFS_LOG_LEVEL_QUIET);
337
338 /* Get the device. */
339 dev_name = argv[optind++];
340 ntfs_log_verbose("device name = %s\n", dev_name);
341
342 if (optind == argc)
343 usage(1);
344
345 /* Get the file name. */
346 file_name = argv[optind++];
347 ntfs_log_verbose("file name = \"%s\"\n", file_name);
348
349 /* Get the attribute type, if specified. */
350 if (optind == argc) {
351 attr_type = AT_DATA;
352 attr_name = AT_UNNAMED;
353 attr_name_len = 0;
354 } else {
355 unsigned long ul;
356
357 s = argv[optind++];
358 ul = strtoul(s, &s2, 0);
359 if (*s2 || !ul || (ul >= ULONG_MAX && errno == ERANGE))
360 err_usage("Invalid attribute type %s: %s\n", s,
361 strerror(errno));
362 attr_type = cpu_to_le32(ul);
363
364 /* Get the attribute name, if specified. */
365 if (optind != argc) {
366 s = argv[optind++];
367 /* Convert the string to little endian Unicode. */
368 attr_name_len = ntfs_mbstoucs(s, &attr_name);
369 if ((int)attr_name_len < 0)
370 err_usage("Invalid attribute name "
371 "\"%s\": %s\n",
372 s, strerror(errno));
373
374 /* Keep hold of the original string. */
375 s2 = s;
376
377 s = argv[optind++];
378 if (optind != argc)
379 usage(1);
380 } else {
381 attr_name = AT_UNNAMED;
382 attr_name_len = 0;
383 }
384 }
385 ntfs_log_verbose("attribute type = 0x%lx\n",
386 (unsigned long)le32_to_cpu(attr_type));
387 if (attr_name == AT_UNNAMED)
388 ntfs_log_verbose("attribute name = \"\" (UNNAMED)\n");
389 else
390 ntfs_log_verbose("attribute name = \"%s\" (length %u "
391 "Unicode characters)\n", s2,
392 (unsigned int)attr_name_len);
393}
394
395/*
396 * Save the initial runlist, to be restored on error
397 */
398
399static runlist_element *ntfs_save_rl(runlist_element *rl)
400{
401 runlist_element *save;
402 int n;
403
404 n = 0;
405 save = (runlist_element*)NULL;
406 if (rl) {
407 while (rl[n].length)
408 n++;
409 save = (runlist_element*)malloc((n + 1)*sizeof(runlist_element));
410 if (save) {
411 memcpy(save, rl, (n + 1)*sizeof(runlist_element));
412 }
413 }
414 return (save);
415}
416
417/*
418 * Free the common part of two runs
419 */
420
421static void free_common(ntfs_volume *vol, runlist_element *brl, s64 blth,
422 runlist_element *grl, s64 glth)
423{
424 VCN begin_common;
425 VCN end_common;
426
427 begin_common = max(grl->vcn, brl->vcn);
428 end_common = min(grl->vcn + glth, brl->vcn + blth);
429 if (end_common > begin_common) {
430 if (ntfs_bitmap_clear_run(vol->lcnbmp_na,
431 brl->lcn + begin_common - brl->vcn,
432 end_common - begin_common))
433 ntfs_log_error("Failed to free %lld clusters "
434 "from 0x%llx\n",
435 (long long)end_common - begin_common,
436 (long long)(brl->lcn + begin_common
437 - brl->vcn));
438 }
439}
440
441/*
442 * Restore the cluster allocations to initial state
443 *
444 * If a new error occurs, only output a message
445 */
446
447static void ntfs_restore_rl(ntfs_attr *na, runlist_element *oldrl)
448{
449 runlist_element *brl; /* Pointer to bad runlist */
450 runlist_element *grl; /* Pointer to good runlist */
451 ntfs_volume *vol;
452
453 vol = na->ni->vol;
454 /* Examine allocated entries from the bad runlist */
455 for (brl=na->rl; brl->length; brl++) {
456 if (brl->lcn != LCN_HOLE) {
457// TODO improve by examining both list in parallel
458 /* Find the holes in the good runlist which overlap */
459 for (grl=oldrl; grl->length
460 && (grl->vcn<=(brl->vcn+brl->length)); grl++) {
461 if (grl->lcn == LCN_HOLE) {
462 free_common(vol, brl, brl->length, grl,
463 grl->length);
464 }
465 }
466 /* Free allocations beyond the end of good runlist */
467 if (grl && !grl->length
468 && ((brl->vcn + brl->length) > grl->vcn)) {
469 free_common(vol, brl, brl->length, grl,
470 brl->vcn + brl->length - grl->vcn);
471 }
472 }
473 }
474 free(na->rl);
475 na->rl = oldrl;
476 if (ntfs_attr_update_mapping_pairs(na, 0)) {
477 ntfs_log_error("Failed to restore the original runlist\n");
478 }
479}
480
481/*
482 * Zero newly allocated runs up to initialized_size
483 */
484
485static int ntfs_inner_zero(ntfs_attr *na, runlist_element *rl)
486{
487 ntfs_volume *vol;
488 char *buf;
489 runlist_element *zrl;
490 s64 cofs;
491 s64 pos;
492 s64 zeroed;
493 int err;
494
495 err = 0;
496 vol = na->ni->vol;
497 buf = (char*)malloc(vol->cluster_size);
498 if (buf) {
499 memset(buf, 0, vol->cluster_size);
500 zrl = rl;
501 pos = zrl->vcn << vol->cluster_size_bits;
502 while (zrl->length
503 && !err
504 && (pos < na->initialized_size)) {
505 for (cofs=0; cofs<zrl->length && !err; cofs++) {
506 zeroed = ntfs_pwrite(vol->dev,
507 (rl->lcn + cofs)
508 << vol->cluster_size_bits,
509 vol->cluster_size, buf);
510 if (zeroed != vol->cluster_size) {
511 ntfs_log_error("Failed to zero at "
512 "offset %lld\n",
513 (long long)pos);
514 errno = EIO;
515 err = -1;
516 }
517 pos += vol->cluster_size;
518 }
519 zrl++;
520 pos = zrl->vcn << vol->cluster_size_bits;
521 }
522 free(buf);
523 } else {
524 ntfs_log_error("Failed to allocate memory\n");
525 errno = ENOSPC;
526 err = -1;
527 }
528 return (err);
529}
530
531/*
532 * Merge newly allocated runs into runlist
533 */
534
535static int ntfs_merge_allocation(ntfs_attr *na, runlist_element *rl,
536 s64 size)
537{
538 ntfs_volume *vol;
539 int err;
540
541 err = 0;
542 vol = na->ni->vol;
543 /* Newly allocated clusters before initialized size need be zeroed */
544 if ((rl->vcn << vol->cluster_size_bits) < na->initialized_size) {
545 err = ntfs_inner_zero(na, rl);
546 }
547 if (!err) {
548 if (na->data_flags & ATTR_IS_SPARSE) {
549 na->compressed_size += size;
550 if (na->compressed_size >= na->allocated_size) {
551 na->data_flags &= ~ATTR_IS_SPARSE;
552 if (na->compressed_size > na->allocated_size) {
553 ntfs_log_error("File size error : "
554 "apparent %lld, "
555 "compressed %lld > "
556 "allocated %lld",
557 (long long)na->data_size,
558 (long long)na->compressed_size,
559 (long long)na->allocated_size);
560 errno = EIO;
561 err = -1;
562 }
563 }
564 }
565 }
566 if (!err) {
567 rl = ntfs_runlists_merge(na->rl, rl);
568 if (!rl) {
569 ntfs_log_error("Failed to merge the new allocation\n");
570 err = -1;
571 } else {
572 na->rl = rl;
573 /* Update the runlist */
574 if (ntfs_attr_update_mapping_pairs(na, 0)) {
575 ntfs_log_error(
576 "Failed to update the runlist\n");
577 err = -1;
578 }
579 }
580 }
581 return (err);
582}
583
584static int ntfs_inner_allocation(ntfs_attr *na, s64 alloc_offs, s64 alloc_len)
585{
586 ntfs_volume *vol;
587 runlist_element *rl;
588 runlist_element *prl;
589 runlist_element *rlc;
590 VCN from_vcn;
591 VCN end_vcn;
592 LCN lcn_seek_from;
593 VCN from_hole;
594 VCN end_hole;
595 s64 need;
596 int err;
597 BOOL done;
598
599 err = 0;
600 vol = na->ni->vol;
601 /* Find holes which overlap the requested allocation */
602 from_vcn = alloc_offs >> vol->cluster_size_bits;
603 end_vcn = (alloc_offs + alloc_len + vol->cluster_size - 1)
604 >> vol->cluster_size_bits;
605 do {
606 done = FALSE;
607 rl = na->rl;
608 while (rl->length
609 && ((rl->lcn >= 0)
610 || ((rl->vcn + rl->length) <= from_vcn)
611 || (rl->vcn >= end_vcn)))
612 rl++;
613 if (!rl->length)
614 done = TRUE;
615 else {
616 from_hole = max(from_vcn, rl->vcn);
617 end_hole = min(end_vcn, rl->vcn + rl->length);
618 need = end_hole - from_hole;
619 lcn_seek_from = -1;
620 if (rl->vcn) {
621 /* Avoid fragmentation when possible */
622 prl = rl;
623 if ((--prl)->lcn >= 0) {
624 lcn_seek_from = prl->lcn
625 + from_hole - prl->vcn;
626 }
627 }
628 if (need <= 0) {
629 ntfs_log_error("Wrong hole size %lld\n",
630 (long long)need);
631 errno = EIO;
632 err = -1;
633 } else {
634 rlc = ntfs_cluster_alloc(vol, from_hole, need,
635 lcn_seek_from, DATA_ZONE);
636 if (!rlc)
637 err = -1;
638 else
639 err = ntfs_merge_allocation(na, rlc,
640 need << vol->cluster_size_bits);
641 }
642 }
643 } while (!err && !done);
644 return (err);
645}
646
647static int ntfs_full_allocation(ntfs_attr *na, ntfs_attr_search_ctx *ctx,
648 s64 alloc_offs, s64 alloc_len)
649{
650 ATTR_RECORD *attr;
651 ntfs_inode *ni;
652 s64 initialized_size;
653 s64 data_size;
654 int err;
655
656 err = 0;
657 initialized_size = na->initialized_size;
658 data_size = na->data_size;
659
660 if (na->allocated_size <= alloc_offs) {
661 /*
662 * Request is fully beyond what was already allocated :
663 * only need to expand the attribute
664 */
665 err = ntfs_attr_truncate(na, alloc_offs);
666 if (!err)
667 err = ntfs_attr_truncate_solid(na,
668 alloc_offs + alloc_len);
669 } else {
670 /*
671 * Request overlaps what was already allocated :
672 * We may have to fill existing holes, and force zeroes
673 * into clusters which are visible.
674 */
675 if ((alloc_offs + alloc_len) > na->allocated_size)
676 err = ntfs_attr_truncate(na, alloc_offs + alloc_len);
677 if (!err)
678 err = ntfs_inner_allocation(na, alloc_offs, alloc_len);
679 }
680 /* Set the sizes, even after an error, to keep consistency */
681 na->initialized_size = initialized_size;
682 /* Restore the original apparent size if requested or error */
683 if (err || opts.no_size_change
684 || ((alloc_offs + alloc_len) < data_size))
685 na->data_size = data_size;
686 else {
687 /*
688 * "man 1 fallocate" does not define the new apparent size
689 * when size change is allowed (no --keep-size).
690 * Assuming the same as no FALLOC_FL_KEEP_SIZE in fallocate(2) :
691 * "the file size will be changed if offset + len is greater
692 * than the file size"
693// TODO check the behavior of another file system
694 */
695 na->data_size = alloc_offs + alloc_len;
696 }
697
698 if (!err) {
699 /* Find the attribute, which may have been relocated for allocations */
700 if (ntfs_attr_lookup(attr_type, attr_name, attr_name_len,
701 CASE_SENSITIVE, 0, NULL, 0, ctx)) {
702 err = -1;
703 ntfs_log_error("Failed to locate the attribute\n");
704 } else {
705 /* Feed the sizes into the attribute */
706 attr = ctx->attr;
Steve Kondike68cb602016-08-28 00:45:36 -0700707 attr->data_size = cpu_to_sle64(na->data_size);
Steve Kondik79165c32015-11-09 19:43:00 -0800708 attr->initialized_size
Steve Kondike68cb602016-08-28 00:45:36 -0700709 = cpu_to_sle64(na->initialized_size);
Steve Kondik79165c32015-11-09 19:43:00 -0800710 attr->allocated_size
Steve Kondike68cb602016-08-28 00:45:36 -0700711 = cpu_to_sle64(na->allocated_size);
Steve Kondik79165c32015-11-09 19:43:00 -0800712 if (na->data_flags & ATTR_IS_SPARSE)
713 attr->compressed_size
Steve Kondike68cb602016-08-28 00:45:36 -0700714 = cpu_to_sle64(na->compressed_size);
Steve Kondik79165c32015-11-09 19:43:00 -0800715 /* Copy the unnamed data attribute sizes to inode */
716 if ((attr_type == AT_DATA) && !attr_name_len) {
717 ni = na->ni;
718 ni->data_size = na->data_size;
719 if (na->data_flags & ATTR_IS_SPARSE) {
720 ni->allocated_size
721 = na->compressed_size;
722 ni->flags |= FILE_ATTR_SPARSE_FILE;
723 } else
724 ni->allocated_size
725 = na->allocated_size;
726 }
727 }
728 }
729 return (err);
730}
731
732
733/*
734 * Do the actual allocations
735 */
736
737static int ntfs_fallocate(ntfs_inode *ni, s64 alloc_offs, s64 alloc_len)
738{
739 s64 allocated_size;
740 s64 data_size;
741 ntfs_attr_search_ctx *ctx;
742 ntfs_attr *na;
743 runlist_element *oldrl;
744 const char *errmess;
745 int save_errno;
746 int err;
747
748 err = 0;
749 /* Open the specified attribute. */
750 na = ntfs_attr_open(ni, attr_type, attr_name, attr_name_len);
751 if (!na) {
752 ntfs_log_perror("Failed to open attribute 0x%lx: ",
753 (unsigned long)le32_to_cpu(attr_type));
754 err = -1;
755 } else {
756 errmess = (const char*)NULL;
757 if (na->data_flags & ATTR_IS_COMPRESSED) {
758 errmess= "Cannot fallocate a compressed file";
759 }
760
761 /* Locate the attribute record, needed for updating sizes */
762 ctx = ntfs_attr_get_search_ctx(ni, NULL);
763 if (!ctx) {
764 errmess = "Failed to allocate a search context";
765 }
766 if (errmess) {
767 ntfs_log_error("%s\n",errmess);
768 err = -1;
769 } else {
770 /* Get and save the initial allocations */
771 allocated_size = na->allocated_size;
772 data_size = ni->data_size;
773 err = ntfs_attr_map_whole_runlist(na);
774 if (!err) {
775 oldrl = ntfs_save_rl(na->rl);
776 if (oldrl) {
777 err = ntfs_full_allocation(na, ctx,
778 alloc_offs, alloc_len);
779 if (err) {
780 save_errno = errno;
781 ni->allocated_size
782 = allocated_size;
783 ni->data_size = data_size;
784 ntfs_restore_rl(na, oldrl);
785 errno = save_errno;
786 } else {
787 free(oldrl);
788 /* Mark file name dirty, to update the sizes in directories */
789 NInoFileNameSetDirty(ni);
790 NInoSetDirty(ni);
791 }
792 } else
793 err = -1;
794 }
795 ntfs_attr_put_search_ctx(ctx);
796 }
797 /* Close the attribute. */
798 ntfs_attr_close(na);
799 }
800 return (err);
801}
802
803/**
804 * main
805 */
806int main(int argc, char **argv)
807{
808 unsigned long mnt_flags, ul;
809 int err;
810 ntfs_inode *ni;
811 ntfs_volume *vol;
812#ifdef HAVE_WINDOWS_H
813 char *unix_name;
814#endif
815
816 vol = (ntfs_volume*)NULL;
817 ntfs_log_set_handler(ntfs_log_handler_outerr);
818
819 /* Initialize opts to zero / required values. */
820 memset(&opts, 0, sizeof(opts));
821
822 /* Parse command line options. */
823 parse_options(argc, argv);
824
825 utils_set_locale();
826
827 /* Make sure the file system is not mounted. */
828 if (ntfs_check_if_mounted(dev_name, &mnt_flags))
829 ntfs_log_perror("Failed to determine whether %s is mounted",
830 dev_name);
831 else if (mnt_flags & NTFS_MF_MOUNTED) {
832 ntfs_log_error("%s is mounted.\n", dev_name);
833 if (!opts.force)
834 err_exit((ntfs_volume*)NULL, "Refusing to run!\n");
835 fprintf(stderr, "ntfsfallocate forced anyway. Hope /etc/mtab "
836 "is incorrect.\n");
837 }
838
839 /* Mount the device. */
840 if (opts.no_action) {
841 ntfs_log_quiet("Running in READ-ONLY mode!\n");
842 ul = NTFS_MNT_RDONLY;
843 } else
844 if (opts.force)
845 ul = NTFS_MNT_RECOVER;
846 else
847 ul = 0;
848 vol = ntfs_mount(dev_name, ul);
849 if (!vol)
850 err_exit(vol, "Failed to mount %s: %s\n", dev_name,
851 strerror(errno));
852
853 if ((vol->flags & VOLUME_IS_DIRTY) && !opts.force)
854 err_exit(vol, "Volume is dirty, please run chkdsk.\n");
855
856 if (ntfs_volume_get_free_space(vol))
857 err_exit(vol, "Failed to get free clusters %s: %s\n",
858 dev_name, strerror(errno));
859
860 /* Open the specified inode. */
861#ifdef HAVE_WINDOWS_H
Steve Kondike68cb602016-08-28 00:45:36 -0700862 unix_name = ntfs_utils_unix_path(file_name);
Steve Kondik79165c32015-11-09 19:43:00 -0800863 if (unix_name) {
Steve Kondik79165c32015-11-09 19:43:00 -0800864 ni = ntfs_pathname_to_inode(vol, NULL, unix_name);
865 free(unix_name);
866 } else
867 ni = (ntfs_inode*)NULL;
868#else
869 ni = ntfs_pathname_to_inode(vol, NULL, file_name);
870#endif
871 if (!ni)
872 err_exit(vol, "Failed to open file \"%s\": %s\n", file_name,
873 strerror(errno));
874 if (!opts.no_action)
875 err = ntfs_fallocate(ni, opt_alloc_offs, opt_alloc_len);
876
877 /* Close the inode. */
878 if (ntfs_inode_close(ni)) {
879 err = -1;
880 err_exit(vol, "Failed to close inode \"%s\" : %s\n", file_name,
881 strerror(errno));
882 }
883
884 /* Unmount the volume. */
885 err = ntfs_umount(vol, 0);
886 vol = (ntfs_volume*)NULL;
887 if (err)
888 ntfs_log_perror("Warning: Failed to umount %s", dev_name);
889
890 /* Free the attribute name if it exists. */
891 if (attr_name_len)
892 ntfs_ucsfree(attr_name);
893
894 ntfs_log_quiet("ntfsfallocate completed successfully. Have a nice day.\n");
895 return 0;
896}