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