summaryrefslogtreecommitdiff
path: root/ntfsprogs/ntfstruncate.c (plain)
blob: 7ac5ae4fe0fd3852b6094ea13cefde1cdce86033
1/**
2 * ntfstruncate - Part of the Linux-NTFS project.
3 *
4 * Copyright (c) 2002-2005 Anton Altaparmakov
5 *
6 * This utility will truncate a specified attribute belonging to a
7 * specified inode, i.e. file or directory, to a specified length.
8 *
9 * This program is free software; you can redistribute it and/or modify
10 * it under the terms of the GNU General Public License as published by
11 * the Free Software Foundation; either version 2 of the License, or
12 * (at your option) any later version.
13 *
14 * This program is distributed in the hope that it will be useful,
15 * but WITHOUT ANY WARRANTY; without even the implied warranty of
16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 * GNU General Public License for more details.
18 *
19 * You should have received a copy of the GNU General Public License
20 * along with this program (in the main directory of the Linux-NTFS source
21 * in the file COPYING); if not, write to the Free Software Foundation,
22 * Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
23 */
24
25#include "config.h"
26
27#ifdef HAVE_UNISTD_H
28# include <unistd.h>
29#endif
30#ifdef HAVE_STDLIB_H
31# include <stdlib.h>
32#endif
33#ifdef HAVE_STDIO_H
34# include <stdio.h>
35#endif
36#ifdef HAVE_STDARG_H
37# include <stdarg.h>
38#endif
39#ifdef HAVE_STRING_H
40# include <string.h>
41#endif
42#ifdef HAVE_ERRNO_H
43# include <errno.h>
44#endif
45#ifdef HAVE_TIME_H
46#include <time.h>
47#endif
48#ifdef HAVE_GETOPT_H
49# include <getopt.h>
50#else
51 extern char *optarg;
52 extern int optind;
53#endif
54#ifdef HAVE_LIMITS_H
55#include <limits.h>
56#endif
57#ifndef LLONG_MAX
58# define LLONG_MAX 9223372036854775807LL
59#endif
60
61#include "types.h"
62#include "attrib.h"
63#include "inode.h"
64#include "layout.h"
65#include "volume.h"
66#include "utils.h"
67#include "attrdef.h"
68/* #include "version.h" */
69#include "logging.h"
70
71const char *EXEC_NAME = "ntfstruncate";
72
73/* Need these global so ntfstruncate_exit can access them. */
74BOOL success = FALSE;
75
76char *dev_name;
77s64 inode;
78u32 attr_type;
79ntfschar *attr_name = NULL;
80u32 attr_name_len;
81s64 new_len;
82
83ntfs_volume *vol;
84ntfs_inode *ni;
85ntfs_attr *na = NULL;
86
87ATTR_DEF *attr_defs;
88
89struct {
90 /* -h, print usage and exit. */
91 int no_action; /* -n, do not write to device, only display
92 what would be done. */
93 int quiet; /* -q, quiet execution. */
94 int verbose; /* -v, verbose execution, given twice, really
95 verbose execution (debug mode). */
96 int force; /* -f, force truncation. */
97 /* -V, print version and exit. */
98} opts;
99
100/**
101 * err_exit - error output and terminate; ignores quiet (-q)
102 */
103__attribute__((noreturn))
104__attribute__((format(printf, 1, 2)))
105static void err_exit(const char *fmt, ...)
106{
107 va_list ap;
108
109 fprintf(stderr, "ERROR: ");
110 va_start(ap, fmt);
111 vfprintf(stderr, fmt, ap);
112 va_end(ap);
113 fprintf(stderr, "Aborting...\n");
114 exit(1);
115}
116
117/**
118 * copyright - print copyright statements
119 */
120static void copyright(void)
121{
122 fprintf(stderr, "Copyright (c) 2002-2005 Anton Altaparmakov\n"
123 "Copyright (c) 2003 Richard Russon\n"
124 "Truncate a specified attribute of a specified "
125 "inode.\n");
126}
127
128/**
129 * license - print license statement
130 */
131static void license(void)
132{
133 fprintf(stderr, "%s", ntfs_gpl);
134}
135
136/**
137 * usage - print a list of the parameters to the program
138 */
139__attribute__((noreturn))
140static void usage(void)
141{
142 copyright();
143 fprintf(stderr, "Usage: %s [options] device inode [attr-type "
144 "[attr-name]] new-length\n"
145 " If attr-type is not specified, 0x80 (i.e. $DATA) "
146 "is assumed.\n"
147 " If attr-name is not specified, an unnamed "
148 "attribute is assumed.\n"
149 " -n Do not write to disk\n"
150 " -f Force execution despite errors\n"
151 " -q Quiet execution\n"
152 " -v Verbose execution\n"
153 " -vv Very verbose execution\n"
154 " -V Display version information\n"
155 " -l Display licensing information\n"
156 " -h Display this help\n", EXEC_NAME);
157 fprintf(stderr, "%s%s", ntfs_bugs, ntfs_home);
158 exit(1);
159}
160
161/**
162 * parse_options
163 */
164static void parse_options(int argc, char *argv[])
165{
166 long long ll;
167 char *s, *s2;
168 int c;
169
170 if (argc && *argv)
171 EXEC_NAME = *argv;
172 fprintf(stderr, "%s v%s (libntfs-3g)\n", EXEC_NAME, VERSION);
173 while ((c = getopt(argc, argv, "fh?nqvVl")) != EOF)
174 switch (c) {
175 case 'f':
176 opts.force = 1;
177 break;
178 case 'n':
179 opts.no_action = 1;
180 break;
181 case 'q':
182 opts.quiet = 1;
183 ntfs_log_clear_levels(NTFS_LOG_LEVEL_QUIET);
184 break;
185 case 'v':
186 opts.verbose++;
187 ntfs_log_set_levels(NTFS_LOG_LEVEL_VERBOSE);
188 break;
189 case 'V':
190 /* Version number already printed, so just exit. */
191 exit(0);
192 case 'l':
193 copyright();
194 license();
195 exit(0);
196 case 'h':
197 case '?':
198 default:
199 usage();
200 }
201 if (optind == argc)
202 usage();
203
204 if (opts.verbose > 1)
205 ntfs_log_set_levels(NTFS_LOG_LEVEL_DEBUG | NTFS_LOG_LEVEL_TRACE |
206 NTFS_LOG_LEVEL_VERBOSE | NTFS_LOG_LEVEL_QUIET);
207
208 /* Get the device. */
209 dev_name = argv[optind++];
210 ntfs_log_verbose("device name = %s\n", dev_name);
211
212 if (optind == argc)
213 usage();
214
215 /* Get the inode. */
216 ll = strtoll(argv[optind++], &s, 0);
217 if (*s || !ll || (ll >= LLONG_MAX && errno == ERANGE))
218 err_exit("Invalid inode number: %s\n", argv[optind - 1]);
219 inode = ll;
220 ntfs_log_verbose("inode = %lli\n", (long long)inode);
221
222 if (optind == argc)
223 usage();
224
225 /* Get the attribute type, if specified. */
226 s = argv[optind++];
227 if (optind == argc) {
228 attr_type = AT_DATA;
229 attr_name = AT_UNNAMED;
230 attr_name_len = 0;
231 } else {
232 unsigned long ul;
233
234 ul = strtoul(s, &s2, 0);
235 if (*s2 || !ul || (ul >= ULONG_MAX && errno == ERANGE))
236 err_exit("Invalid attribute type %s: %s\n", s,
237 strerror(errno));
238 attr_type = ul;
239
240 /* Get the attribute name, if specified. */
241 s = argv[optind++];
242 if (optind != argc) {
243 /* Convert the string to little endian Unicode. */
244 attr_name_len = ntfs_mbstoucs(s, &attr_name);
245 if ((int)attr_name_len < 0)
246 err_exit("Invalid attribute name \"%s\": %s\n",
247 s, strerror(errno));
248
249 /* Keep hold of the original string. */
250 s2 = s;
251
252 s = argv[optind++];
253 if (optind != argc)
254 usage();
255 } else {
256 attr_name = AT_UNNAMED;
257 attr_name_len = 0;
258 }
259 }
260 ntfs_log_verbose("attribute type = 0x%x\n", (unsigned int)attr_type);
261 if (attr_name == AT_UNNAMED)
262 ntfs_log_verbose("attribute name = \"\" (UNNAMED)\n");
263 else
264 ntfs_log_verbose("attribute name = \"%s\" (length %u Unicode "
265 "characters)\n", s2,
266 (unsigned int)attr_name_len);
267
268 /* Get the new length. */
269 ll = strtoll(s, &s2, 0);
270 if (*s2 || ll < 0 || (ll >= LLONG_MAX && errno == ERANGE))
271 err_exit("Invalid new length: %s\n", s);
272 new_len = ll;
273 ntfs_log_verbose("new length = %lli\n", (long long)new_len);
274}
275
276/**
277 * ucstos - convert unicode-character string to ASCII
278 * @dest: points to buffer to receive the converted string
279 * @src: points to string to convert
280 * @maxlen: size of @dest buffer in bytes
281 *
282 * Return the number of characters written to @dest, not including the
283 * terminating null byte. If a unicode character was encountered which could
284 * not be converted -1 is returned.
285 */
286static int ucstos(char *dest, const ntfschar *src, int maxlen)
287{
288 ntfschar u;
289 int i;
290
291 /* Need one byte for null terminator. */
292 maxlen--;
293 for (i = 0; i < maxlen; i++) {
294 u = le16_to_cpu(src[i]);
295 if (!u)
296 break;
297 if (u & 0xff00)
298 return -1;
299 dest[i] = u & 0xff;
300 }
301 dest[i] = 0;
302 return i;
303}
304
305/**
306 * dump_resident_attr_val
307 */
308static void dump_resident_attr_val(ATTR_TYPES type, char *val, u32 val_len)
309{
310 const char *don_t_know = "Don't know what to do with this attribute "
311 "type yet.";
312 const char *skip = "Skipping display of $%s attribute value.\n";
313 const char *todo = "This is still work in progress.";
314 char *buf;
315 int i, j;
316 u32 u;
317
318 switch (type) {
319 case AT_STANDARD_INFORMATION:
320 // TODO
321 printf("%s\n", todo);
322 return;
323 case AT_ATTRIBUTE_LIST:
324 // TODO
325 printf("%s\n", todo);
326 return;
327 case AT_FILE_NAME:
328 // TODO
329 printf("%s\n", todo);
330 return;
331 case AT_OBJECT_ID:
332 // TODO
333 printf("%s\n", todo);
334 return;
335 case AT_SECURITY_DESCRIPTOR:
336 // TODO
337 printf("%s\n", todo);
338 return;
339 case AT_VOLUME_NAME:
340 printf("Volume name length = %u\n", (unsigned int)val_len);
341 if (val_len) {
342 buf = calloc(1, val_len);
343 if (!buf)
344 err_exit("Failed to allocate internal buffer: "
345 "%s\n", strerror(errno));
346 i = ucstos(buf, (ntfschar*)val, val_len);
347 if (i == -1)
348 printf("Volume name contains non-displayable "
349 "Unicode characters.\n");
350 printf("Volume name = %s\n", buf);
351 free(buf);
352 }
353 return;
354 case AT_VOLUME_INFORMATION:
355#define VOL_INF(x) ((VOLUME_INFORMATION *)(x))
356 printf("NTFS version %i.%i\n", VOL_INF(val)->major_ver,
357 VOL_INF(val)->minor_ver);
358 i = VOL_INF(val)->flags;
359#undef VOL_INF
360 printf("Volume flags = 0x%x: ", i);
361 if (!i) {
362 printf("NONE\n");
363 return;
364 }
365 j = 0;
366 if (i & VOLUME_MODIFIED_BY_CHKDSK) {
367 j = 1;
368 printf("VOLUME_MODIFIED_BY_CHKDSK");
369 }
370 if (i & VOLUME_REPAIR_OBJECT_ID) {
371 if (j)
372 printf(" | ");
373 else
374 j = 0;
375 printf("VOLUME_REPAIR_OBJECT_ID");
376 }
377 if (i & VOLUME_DELETE_USN_UNDERWAY) {
378 if (j)
379 printf(" | ");
380 else
381 j = 0;
382 printf("VOLUME_DELETE_USN_UNDERWAY");
383 }
384 if (i & VOLUME_MOUNTED_ON_NT4) {
385 if (j)
386 printf(" | ");
387 else
388 j = 0;
389 printf("VOLUME_MOUNTED_ON_NT4");
390 }
391 if (i & VOLUME_UPGRADE_ON_MOUNT) {
392 if (j)
393 printf(" | ");
394 else
395 j = 0;
396 printf("VOLUME_UPGRADE_ON_MOUNT");
397 }
398 if (i & VOLUME_RESIZE_LOG_FILE) {
399 if (j)
400 printf(" | ");
401 else
402 j = 0;
403 printf("VOLUME_RESIZE_LOG_FILE");
404 }
405 if (i & VOLUME_IS_DIRTY) {
406 if (j)
407 printf(" | ");
408 else
409 j = 0;
410 printf("VOLUME_IS_DIRTY");
411 }
412 printf("\n");
413 return;
414 case AT_DATA:
415 printf(skip, "DATA");
416 return;
417 case AT_INDEX_ROOT:
418 // TODO
419 printf("%s\n", todo);
420 return;
421 case AT_INDEX_ALLOCATION:
422 // TODO
423 printf("%s\n", todo);
424 return;
425 case AT_BITMAP:
426 printf(skip, "BITMAP");
427 return;
428 case AT_REPARSE_POINT:
429 // TODO
430 printf("%s\n", todo);
431 return;
432 case AT_EA_INFORMATION:
433 // TODO
434 printf("%s\n", don_t_know);
435 return;
436 case AT_EA:
437 // TODO
438 printf("%s\n", don_t_know);
439 return;
440 case AT_LOGGED_UTILITY_STREAM:
441 // TODO
442 printf("%s\n", don_t_know);
443 return;
444 default:
445 u = le32_to_cpu(type);
446 printf("Cannot display unknown %s defined attribute type 0x%x"
447 ".\n", u >=
448 le32_to_cpu(AT_FIRST_USER_DEFINED_ATTRIBUTE) ?
449 "user" : "system", (unsigned int)u);
450 }
451}
452
453/**
454 * dump_resident_attr
455 */
456static void dump_resident_attr(ATTR_RECORD *a)
457{
458 int i;
459
460 i = le32_to_cpu(a->value_length);
461 printf("Attribute value length = %u (0x%x)\n", i, i);
462 i = le16_to_cpu(a->value_offset);
463 printf("Attribute value offset = %u (0x%x)\n", i, i);
464 i = a->resident_flags;
465 printf("Resident flags = 0x%x: ", i);
466 if (!i)
467 printf("NONE\n");
468 else if (i & ~RESIDENT_ATTR_IS_INDEXED)
469 printf("UNKNOWN FLAG(S)\n");
470 else
471 printf("RESIDENT_ATTR_IS_INDEXED\n");
472 dump_resident_attr_val(a->type, (char*)a + le16_to_cpu(a->value_offset),
473 le32_to_cpu(a->value_length));
474}
475
476/**
477 * dump_mapping_pairs_array
478 */
479static void dump_mapping_pairs_array(char *b __attribute__((unused)),
480 unsigned int max_len __attribute__((unused)))
481{
482 // TODO
483 return;
484}
485
486/**
487 * dump_non_resident_attr
488 */
489static void dump_non_resident_attr(ATTR_RECORD *a)
490{
491 s64 l;
492 int i;
493
494 l = sle64_to_cpu(a->lowest_vcn);
495 printf("Lowest VCN = %lli (0x%llx)\n", (long long)l,
496 (unsigned long long)l);
497 l = sle64_to_cpu(a->highest_vcn);
498 printf("Highest VCN = %lli (0x%llx)\n", (long long)l,
499 (unsigned long long)l);
500 printf("Mapping pairs array offset = 0x%x\n",
501 le16_to_cpu(a->mapping_pairs_offset));
502 printf("Compression unit = 0x%x: %sCOMPRESSED\n", a->compression_unit,
503 a->compression_unit ? "" : "NOT ");
504 if (sle64_to_cpu(a->lowest_vcn))
505 printf("Attribute is not the first extent. The following "
506 "sizes are meaningless:\n");
507 l = sle64_to_cpu(a->allocated_size);
508 printf("Allocated size = %lli (0x%llx)\n", (long long)l,
509 (unsigned long long)l);
510 l = sle64_to_cpu(a->data_size);
511 printf("Data size = %lli (0x%llx)\n", (long long)l,
512 (unsigned long long)l);
513 l = sle64_to_cpu(a->initialized_size);
514 printf("Initialized size = %lli (0x%llx)\n", (long long)l,
515 (unsigned long long)l);
516 if (a->flags & ATTR_COMPRESSION_MASK) {
517 l = sle64_to_cpu(a->compressed_size);
518 printf("Compressed size = %lli (0x%llx)\n", (long long)l,
519 (unsigned long long)l);
520 }
521 i = le16_to_cpu(a->mapping_pairs_offset);
522 dump_mapping_pairs_array((char*)a + i, le32_to_cpu(a->length) - i);
523}
524
525/**
526 * dump_attr_record
527 */
528static void dump_attr_record(MFT_RECORD *m, ATTR_RECORD *a)
529{
530 unsigned int u;
531 char s[0x200];
532 int i;
533
534 printf("-- Beginning dump of attribute record at offset 0x%x. --\n",
535 (unsigned)((u8*)a - (u8*)m));
536 if (a->type == AT_END) {
537 printf("Attribute type = 0x%x ($END)\n",
538 (unsigned int)le32_to_cpu(AT_END));
539 u = le32_to_cpu(a->length);
540 printf("Length of resident part = %u (0x%x)\n", u, u);
541 return;
542 }
543 u = le32_to_cpu(a->type);
544 for (i = 0; attr_defs[i].type; i++)
545 if (le32_to_cpu(attr_defs[i].type) >= u)
546 break;
547 if (attr_defs[i].type) {
548// printf("type = 0x%x\n", le32_to_cpu(attr_defs[i].type));
549// { char *p = (char*)attr_defs[i].name;
550// printf("name = %c%c%c%c%c\n", *p, p[1], p[2], p[3], p[4]);
551// }
552 if (ucstos(s, attr_defs[i].name, sizeof(s)) == -1) {
553 ntfs_log_error("Could not convert Unicode string to single "
554 "byte string in current locale.\n");
555 strncpy(s, "Error converting Unicode string",
556 sizeof(s));
557 }
558 } else
559 strncpy(s, "UNKNOWN_TYPE", sizeof(s));
560 printf("Attribute type = 0x%x (%s)\n", u, s);
561 u = le32_to_cpu(a->length);
562 printf("Length of resident part = %u (0x%x)\n", u, u);
563 printf("Attribute is %sresident\n", a->non_resident ? "non-" : "");
564 printf("Name length = %u unicode characters\n", a->name_length);
565 printf("Name offset = %u (0x%x)\n", cpu_to_le16(a->name_offset),
566 cpu_to_le16(a->name_offset));
567 u = a->flags;
568 if (a->name_length) {
569 if (ucstos(s, (ntfschar*)((char*)a +
570 cpu_to_le16(a->name_offset)),
571 min((int)sizeof(s),
572 a->name_length + 1)) == -1) {
573 ntfs_log_error("Could not convert Unicode string to single "
574 "byte string in current locale.\n");
575 strncpy(s, "Error converting Unicode string",
576 sizeof(s));
577
578 }
579 printf("Name = %s\n", s);
580 }
581 printf("Attribute flags = 0x%x: ", le16_to_cpu(u));
582 if (!u)
583 printf("NONE");
584 else {
585 int first = TRUE;
586 if (u & ATTR_COMPRESSION_MASK) {
587 if (u & ATTR_IS_COMPRESSED) {
588 printf("ATTR_IS_COMPRESSED");
589 first = FALSE;
590 }
591 if ((u & ATTR_COMPRESSION_MASK) & ~ATTR_IS_COMPRESSED) {
592 if (!first)
593 printf(" | ");
594 else
595 first = FALSE;
596 printf("ATTR_UNKNOWN_COMPRESSION");
597 }
598 }
599 if (u & ATTR_IS_ENCRYPTED) {
600 if (!first)
601 printf(" | ");
602 else
603 first = FALSE;
604 printf("ATTR_IS_ENCRYPTED");
605 }
606 if (u & ATTR_IS_SPARSE) {
607 if (!first)
608 printf(" | ");
609 else
610 first = FALSE;
611 printf("ATTR_IS_SPARSE");
612 }
613 }
614 printf("\n");
615 printf("Attribute instance = %u\n", le16_to_cpu(a->instance));
616 if (a->non_resident) {
617 dump_non_resident_attr(a);
618 } else {
619 dump_resident_attr(a);
620 }
621}
622
623/**
624 * dump_mft_record
625 */
626static void dump_mft_record(MFT_RECORD *m)
627{
628 ATTR_RECORD *a;
629 unsigned int u;
630 MFT_REF r;
631
632 printf("-- Beginning dump of mft record. --\n");
633 u = le32_to_cpu(m->magic);
634 printf("Mft record signature (magic) = %c%c%c%c\n", u & 0xff,
635 u >> 8 & 0xff, u >> 16 & 0xff, u >> 24 & 0xff);
636 u = le16_to_cpu(m->usa_ofs);
637 printf("Update sequence array offset = %u (0x%x)\n", u, u);
638 printf("Update sequence array size = %u\n", le16_to_cpu(m->usa_count));
639 printf("$LogFile sequence number (lsn) = %llu\n",
640 (unsigned long long)le64_to_cpu(m->lsn));
641 printf("Sequence number = %u\n", le16_to_cpu(m->sequence_number));
642 printf("Reference (hard link) count = %u\n",
643 le16_to_cpu(m->link_count));
644 u = le16_to_cpu(m->attrs_offset);
645 printf("First attribute offset = %u (0x%x)\n", u, u);
646 printf("Flags = %u: ", le16_to_cpu(m->flags));
647 if (m->flags & MFT_RECORD_IN_USE)
648 printf("MFT_RECORD_IN_USE");
649 else
650 printf("MFT_RECORD_NOT_IN_USE");
651 if (m->flags & MFT_RECORD_IS_DIRECTORY)
652 printf(" | MFT_RECORD_IS_DIRECTORY");
653 printf("\n");
654 u = le32_to_cpu(m->bytes_in_use);
655 printf("Bytes in use = %u (0x%x)\n", u, u);
656 u = le32_to_cpu(m->bytes_allocated);
657 printf("Bytes allocated = %u (0x%x)\n", u, u);
658 r = le64_to_cpu(m->base_mft_record);
659 printf("Base mft record reference:\n\tMft record number = %llu\n\t"
660 "Sequence number = %u\n",
661 (unsigned long long)MREF(r), MSEQNO(r));
662 printf("Next attribute instance = %u\n",
663 le16_to_cpu(m->next_attr_instance));
664 a = (ATTR_RECORD*)((char*)m + le16_to_cpu(m->attrs_offset));
665 printf("-- Beginning dump of attributes within mft record. --\n");
666 while ((char*)a < (char*)m + le32_to_cpu(m->bytes_in_use)) {
667 if (a->type == cpu_to_le32(attr_type))
668 dump_attr_record(m, a);
669 if (a->type == AT_END)
670 break;
671 a = (ATTR_RECORD*)((char*)a + le32_to_cpu(a->length));
672 };
673 printf("-- End of attributes. --\n");
674}
675
676/**
677 * ntfstruncate_exit
678 */
679static void ntfstruncate_exit(void)
680{
681 int err;
682
683 if (success)
684 return;
685 /* Close the attribute. */
686 if (na)
687 ntfs_attr_close(na);
688 /* Close the inode. */
689 if (ni && ntfs_inode_close(ni)) {
690 ntfs_log_perror("Warning: Failed to close inode %lli",
691 (long long)inode);
692 }
693 /* Unmount the volume. */
694 err = ntfs_umount(vol, 0);
695 if (err == -1)
696 ntfs_log_perror("Warning: Could not umount %s", dev_name);
697 /* Free the attribute name if it exists. */
698 ntfs_ucsfree(attr_name);
699}
700
701/**
702 * main
703 */
704int main(int argc, char **argv)
705{
706 unsigned long mnt_flags, ul;
707 int err;
708
709 ntfs_log_set_handler(ntfs_log_handler_outerr);
710
711 /* Initialize opts to zero / required values. */
712 memset(&opts, 0, sizeof(opts));
713
714 /*
715 * Setup a default $AttrDef. FIXME: Should be reading this from the
716 * volume itself, at ntfs_mount() time.
717 */
718 attr_defs = (ATTR_DEF*)&attrdef_ntfs3x_array;
719
720 /* Parse command line options. */
721 parse_options(argc, argv);
722
723 utils_set_locale();
724
725 /* Make sure the file system is not mounted. */
726 if (ntfs_check_if_mounted(dev_name, &mnt_flags))
727 ntfs_log_perror("Failed to determine whether %s is mounted",
728 dev_name);
729 else if (mnt_flags & NTFS_MF_MOUNTED) {
730 ntfs_log_error("%s is mounted.\n", dev_name);
731 if (!opts.force)
732 err_exit("Refusing to run!\n");
733 fprintf(stderr, "ntfstruncate forced anyway. Hope /etc/mtab "
734 "is incorrect.\n");
735 }
736
737 /* Mount the device. */
738 if (opts.no_action) {
739 ntfs_log_quiet("Running in READ-ONLY mode!\n");
740 ul = NTFS_MNT_RDONLY;
741 } else
742 ul = 0;
743 vol = ntfs_mount(dev_name, ul);
744 if (!vol)
745 err_exit("Failed to mount %s: %s\n", dev_name, strerror(errno));
746
747 /* Register our exit function which will unlock and close the device. */
748 err = atexit(&ntfstruncate_exit);
749 if (err == -1) {
750 ntfs_log_error("Could not set up exit() function because atexit() "
751 "failed: %s Aborting...\n", strerror(errno));
752 ntfstruncate_exit();
753 exit(1);
754 }
755
756 /* Open the specified inode. */
757 ni = ntfs_inode_open(vol, inode);
758 if (!ni)
759 err_exit("Failed to open inode %lli: %s\n", (long long)inode,
760 strerror(errno));
761
762 /* Open the specified attribute. */
763 na = ntfs_attr_open(ni, attr_type, attr_name, attr_name_len);
764 if (!na)
765 err_exit("Failed to open attribute 0x%x: %s\n",
766 (unsigned int)attr_type, strerror(errno));
767
768 if (!opts.quiet && opts.verbose > 1) {
769 ntfs_log_verbose("Dumping mft record before calling "
770 "ntfs_attr_truncate():\n");
771 dump_mft_record(ni->mrec);
772 }
773
774 /* Truncate the attribute. */
775 err = ntfs_attr_truncate(na, new_len);
776 if (err)
777 err_exit("Failed to truncate attribute 0x%x: %s\n",
778 (unsigned int)attr_type, strerror(errno));
779
780 if (!opts.quiet && opts.verbose > 1) {
781 ntfs_log_verbose("Dumping mft record after calling "
782 "ntfs_attr_truncate():\n");
783 dump_mft_record(ni->mrec);
784 }
785
786 /* Close the attribute. */
787 ntfs_attr_close(na);
788 na = NULL;
789
790 /* Close the inode. */
791 err = ntfs_inode_close(ni);
792 if (err)
793 err_exit("Failed to close inode %lli: %s\n", (long long)inode,
794 strerror(errno));
795
796 /* Unmount the volume. */
797 err = ntfs_umount(vol, 0);
798 if (err == -1)
799 ntfs_log_perror("Warning: Failed to umount %s", dev_name);
800
801 /* Free the attribute name if it exists. */
802 ntfs_ucsfree(attr_name);
803
804 /* Finally, disable our ntfstruncate_exit() handler. */
805 success = TRUE;
806
807 ntfs_log_quiet("ntfstruncate completed successfully. Have a nice day.\n");
808 return 0;
809}
810