• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 
71 const char *EXEC_NAME = "ntfstruncate";
72 
73 /* Need these global so ntfstruncate_exit can access them. */
74 BOOL success = FALSE;
75 
76 char *dev_name;
77 s64 inode;
78 ATTR_TYPES attr_type;
79 ntfschar *attr_name = NULL;
80 u32 attr_name_len;
81 s64 new_len;
82 
83 ntfs_volume *vol;
84 ntfs_inode *ni;
85 ntfs_attr *na = NULL;
86 
87 ATTR_DEF *attr_defs;
88 
89 struct {
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)))
err_exit(const char * fmt,...)105 static 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  */
copyright(void)120 static 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  */
license(void)131 static 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))
usage(int ret)140 static void usage(int ret)
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(ret);
159 }
160 
161 /**
162  * parse_options
163  */
parse_options(int argc,char * argv[])164 static 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 			usage(0);
198 		case '?':
199 		default:
200 			usage(1);
201 		}
202 	if (optind == argc)
203 		usage(1);
204 
205 	if (opts.verbose > 1)
206 		ntfs_log_set_levels(NTFS_LOG_LEVEL_DEBUG | NTFS_LOG_LEVEL_TRACE |
207 			NTFS_LOG_LEVEL_VERBOSE | NTFS_LOG_LEVEL_QUIET);
208 
209 	/* Get the device. */
210 	dev_name = argv[optind++];
211 	ntfs_log_verbose("device name = %s\n", dev_name);
212 
213 	if (optind == argc)
214 		usage(1);
215 
216 	/* Get the inode. */
217 	ll = strtoll(argv[optind++], &s, 0);
218 	if (*s || !ll || (ll >= LLONG_MAX && errno == ERANGE))
219 		err_exit("Invalid inode number: %s\n", argv[optind - 1]);
220 	inode = ll;
221 	ntfs_log_verbose("inode = %lli\n", (long long)inode);
222 
223 	if (optind == argc)
224 		usage(1);
225 
226 	/* Get the attribute type, if specified. */
227 	s = argv[optind++];
228 	if (optind == argc) {
229 		attr_type = AT_DATA;
230 		attr_name = AT_UNNAMED;
231 		attr_name_len = 0;
232 	} else {
233 		unsigned long ul;
234 
235 		ul = strtoul(s, &s2, 0);
236 		if (*s2 || !ul || (ul >= ULONG_MAX && errno == ERANGE))
237 			err_exit("Invalid attribute type %s: %s\n", s,
238 					strerror(errno));
239 		attr_type = cpu_to_le32(ul);
240 
241 		/* Get the attribute name, if specified. */
242 		s = argv[optind++];
243 		if (optind != argc) {
244 			/* Convert the string to little endian Unicode. */
245 			attr_name_len = ntfs_mbstoucs(s, &attr_name);
246 			if ((int)attr_name_len < 0)
247 				err_exit("Invalid attribute name \"%s\": %s\n",
248 						s, strerror(errno));
249 
250 			/* Keep hold of the original string. */
251 			s2 = s;
252 
253 			s = argv[optind++];
254 			if (optind != argc)
255 				usage(1);
256 		} else {
257 			attr_name = AT_UNNAMED;
258 			attr_name_len = 0;
259 		}
260 	}
261 	ntfs_log_verbose("attribute type = 0x%x\n", (unsigned int)le32_to_cpu(attr_type));
262 	if (attr_name == AT_UNNAMED)
263 		ntfs_log_verbose("attribute name = \"\" (UNNAMED)\n");
264 	else
265 		ntfs_log_verbose("attribute name = \"%s\" (length %u Unicode "
266 				"characters)\n", s2,
267 				(unsigned int)attr_name_len);
268 
269 	/* Get the new length. */
270 	ll = strtoll(s, &s2, 0);
271 	if (*s2 || ll < 0 || (ll >= LLONG_MAX && errno == ERANGE))
272 		err_exit("Invalid new length: %s\n", s);
273 	new_len = ll;
274 	ntfs_log_verbose("new length = %lli\n", (long long)new_len);
275 }
276 
277 /**
278  * ucstos - convert unicode-character string to ASCII
279  * @dest:	points to buffer to receive the converted string
280  * @src:	points to string to convert
281  * @maxlen:	size of @dest buffer in bytes
282  *
283  * Return the number of characters written to @dest, not including the
284  * terminating null byte. If a unicode character was encountered which could
285  * not be converted -1 is returned.
286  */
ucstos(char * dest,const ntfschar * src,int maxlen)287 static int ucstos(char *dest, const ntfschar *src, int maxlen)
288 {
289 	u16 u;
290 	int i;
291 
292 	/* Need one byte for null terminator. */
293 	maxlen--;
294 	for (i = 0; i < maxlen; i++) {
295 		u = le16_to_cpu(src[i]);
296 		if (!u)
297 			break;
298 		if (u & 0xff00)
299 			return -1;
300 		dest[i] = u & 0xff;
301 	}
302 	dest[i] = 0;
303 	return i;
304 }
305 
306 /**
307  * dump_resident_attr_val
308  */
dump_resident_attr_val(ATTR_TYPES type,char * val,u32 val_len)309 static void dump_resident_attr_val(ATTR_TYPES type, char *val, u32 val_len)
310 {
311 	const char *don_t_know = "Don't know what to do with this attribute "
312 			"type yet.";
313 	const char *skip = "Skipping display of $%s attribute value.\n";
314 	const char *todo = "This is still work in progress.";
315 	char *buf;
316 	int i, j;
317 	VOLUME_FLAGS flags;
318 	u32 u;
319 
320 	switch (type) {
321 	case AT_STANDARD_INFORMATION:
322 		// TODO
323 		printf("%s\n", todo);
324 		return;
325 	case AT_ATTRIBUTE_LIST:
326 		// TODO
327 		printf("%s\n", todo);
328 		return;
329 	case AT_FILE_NAME:
330 		// TODO
331 		printf("%s\n", todo);
332 		return;
333 	case AT_OBJECT_ID:
334 		// TODO
335 		printf("%s\n", todo);
336 		return;
337 	case AT_SECURITY_DESCRIPTOR:
338 		// TODO
339 		printf("%s\n", todo);
340 		return;
341 	case AT_VOLUME_NAME:
342 		printf("Volume name length = %u\n", (unsigned int)val_len);
343 		if (val_len) {
344 			buf = calloc(1, val_len);
345 			if (!buf)
346 				err_exit("Failed to allocate internal buffer: "
347 						"%s\n", strerror(errno));
348 			i = ucstos(buf, (ntfschar*)val, val_len);
349 			if (i == -1)
350 				printf("Volume name contains non-displayable "
351 						"Unicode characters.\n");
352 			printf("Volume name = %s\n", buf);
353 			free(buf);
354 		}
355 		return;
356 	case AT_VOLUME_INFORMATION:
357 #define VOL_INF(x) ((VOLUME_INFORMATION *)(x))
358 		printf("NTFS version %i.%i\n", VOL_INF(val)->major_ver,
359 				VOL_INF(val)->minor_ver);
360 		flags = VOL_INF(val)->flags;
361 #undef VOL_INF
362 		printf("Volume flags = 0x%x: ", le16_to_cpu(flags));
363 		if (!flags) {
364 			printf("NONE\n");
365 			return;
366 		}
367 		j = 0;
368 		if (flags & VOLUME_MODIFIED_BY_CHKDSK) {
369 			j = 1;
370 			printf("VOLUME_MODIFIED_BY_CHKDSK");
371 		}
372 		if (flags & VOLUME_REPAIR_OBJECT_ID) {
373 			if (j)
374 				printf(" | ");
375 			else
376 				j = 0;
377 			printf("VOLUME_REPAIR_OBJECT_ID");
378 		}
379 		if (flags & VOLUME_DELETE_USN_UNDERWAY) {
380 			if (j)
381 				printf(" | ");
382 			else
383 				j = 0;
384 			printf("VOLUME_DELETE_USN_UNDERWAY");
385 		}
386 		if (flags & VOLUME_MOUNTED_ON_NT4) {
387 			if (j)
388 				printf(" | ");
389 			else
390 				j = 0;
391 			printf("VOLUME_MOUNTED_ON_NT4");
392 		}
393 		if (flags & VOLUME_UPGRADE_ON_MOUNT) {
394 			if (j)
395 				printf(" | ");
396 			else
397 				j = 0;
398 			printf("VOLUME_UPGRADE_ON_MOUNT");
399 		}
400 		if (flags & VOLUME_RESIZE_LOG_FILE) {
401 			if (j)
402 				printf(" | ");
403 			else
404 				j = 0;
405 			printf("VOLUME_RESIZE_LOG_FILE");
406 		}
407 		if (flags & VOLUME_IS_DIRTY) {
408 			if (j)
409 				printf(" | ");
410 			else
411 				j = 0;
412 			printf("VOLUME_IS_DIRTY");
413 		}
414 		printf("\n");
415 		return;
416 	case AT_DATA:
417 		printf(skip, "DATA");
418 		return;
419 	case AT_INDEX_ROOT:
420 		// TODO
421 		printf("%s\n", todo);
422 		return;
423 	case AT_INDEX_ALLOCATION:
424 		// TODO
425 		printf("%s\n", todo);
426 		return;
427 	case AT_BITMAP:
428 		printf(skip, "BITMAP");
429 		return;
430 	case AT_REPARSE_POINT:
431 		// TODO
432 		printf("%s\n", todo);
433 		return;
434 	case AT_EA_INFORMATION:
435 		// TODO
436 		printf("%s\n", don_t_know);
437 		return;
438 	case AT_EA:
439 		// TODO
440 		printf("%s\n", don_t_know);
441 		return;
442 	case AT_LOGGED_UTILITY_STREAM:
443 		// TODO
444 		printf("%s\n", don_t_know);
445 		return;
446 	default:
447 		u = le32_to_cpu(type);
448 		printf("Cannot display unknown %s defined attribute type 0x%x"
449 				".\n", u >=
450 				le32_to_cpu(AT_FIRST_USER_DEFINED_ATTRIBUTE) ?
451 				"user" : "system", (unsigned int)u);
452 	}
453 }
454 
455 /**
456  * dump_resident_attr
457  */
dump_resident_attr(ATTR_RECORD * a)458 static void dump_resident_attr(ATTR_RECORD *a)
459 {
460 	int i;
461 
462 	i = le32_to_cpu(a->value_length);
463 	printf("Attribute value length = %u (0x%x)\n", i, i);
464 	i = le16_to_cpu(a->value_offset);
465 	printf("Attribute value offset = %u (0x%x)\n", i, i);
466 	i = a->resident_flags;
467 	printf("Resident flags = 0x%x: ", i);
468 	if (!i)
469 		printf("NONE\n");
470 	else if (i & ~RESIDENT_ATTR_IS_INDEXED)
471 		printf("UNKNOWN FLAG(S)\n");
472 	else
473 		printf("RESIDENT_ATTR_IS_INDEXED\n");
474 	dump_resident_attr_val(a->type, (char*)a + le16_to_cpu(a->value_offset),
475 			le32_to_cpu(a->value_length));
476 }
477 
478 /**
479  * dump_mapping_pairs_array
480  */
dump_mapping_pairs_array(char * b,unsigned int max_len)481 static void dump_mapping_pairs_array(char *b __attribute__((unused)),
482 	unsigned int max_len __attribute__((unused)))
483 {
484 	// TODO
485 	return;
486 }
487 
488 /**
489  * dump_non_resident_attr
490  */
dump_non_resident_attr(ATTR_RECORD * a)491 static void dump_non_resident_attr(ATTR_RECORD *a)
492 {
493 	s64 l;
494 	int i;
495 
496 	l = sle64_to_cpu(a->lowest_vcn);
497 	printf("Lowest VCN = %lli (0x%llx)\n", (long long)l,
498 			(unsigned long long)l);
499 	l = sle64_to_cpu(a->highest_vcn);
500 	printf("Highest VCN = %lli (0x%llx)\n", (long long)l,
501 			(unsigned long long)l);
502 	printf("Mapping pairs array offset = 0x%x\n",
503 			le16_to_cpu(a->mapping_pairs_offset));
504 	printf("Compression unit = 0x%x: %sCOMPRESSED\n", a->compression_unit,
505 			a->compression_unit ? "" : "NOT ");
506 	if (sle64_to_cpu(a->lowest_vcn))
507 		printf("Attribute is not the first extent. The following "
508 				"sizes are meaningless:\n");
509 	l = sle64_to_cpu(a->allocated_size);
510 	printf("Allocated size = %lli (0x%llx)\n", (long long)l,
511 			(unsigned long long)l);
512 	l = sle64_to_cpu(a->data_size);
513 	printf("Data size = %lli (0x%llx)\n", (long long)l,
514 			(unsigned long long)l);
515 	l = sle64_to_cpu(a->initialized_size);
516 	printf("Initialized size = %lli (0x%llx)\n", (long long)l,
517 			(unsigned long long)l);
518 	if (a->flags & ATTR_COMPRESSION_MASK) {
519 		l = sle64_to_cpu(a->compressed_size);
520 		printf("Compressed size = %lli (0x%llx)\n", (long long)l,
521 				(unsigned long long)l);
522 	}
523 	i = le16_to_cpu(a->mapping_pairs_offset);
524 	dump_mapping_pairs_array((char*)a + i, le32_to_cpu(a->length) - i);
525 }
526 
527 /**
528  * dump_attr_record
529  */
dump_attr_record(MFT_RECORD * m,ATTR_RECORD * a)530 static void dump_attr_record(MFT_RECORD *m, ATTR_RECORD *a)
531 {
532 	unsigned int u;
533 	char s[0x200];
534 	int i;
535 	ATTR_FLAGS flags;
536 
537 	printf("-- Beginning dump of attribute record at offset 0x%x. --\n",
538 			(unsigned)((u8*)a - (u8*)m));
539 	if (a->type == AT_END) {
540 		printf("Attribute type = 0x%x ($END)\n",
541 				(unsigned int)le32_to_cpu(AT_END));
542 		u = le32_to_cpu(a->length);
543 		printf("Length of resident part = %u (0x%x)\n", u, u);
544 		return;
545 	}
546 	u = le32_to_cpu(a->type);
547 	for (i = 0; attr_defs[i].type; i++)
548 		if (le32_to_cpu(attr_defs[i].type) >= u)
549 			break;
550 	if (attr_defs[i].type) {
551 //		printf("type = 0x%x\n", le32_to_cpu(attr_defs[i].type));
552 //		{ char *p = (char*)attr_defs[i].name;
553 //		printf("name = %c%c%c%c%c\n", *p, p[1], p[2], p[3], p[4]);
554 //		}
555 		if (ucstos(s, attr_defs[i].name, sizeof(s)) == -1) {
556 			ntfs_log_error("Could not convert Unicode string to single "
557 				"byte string in current locale.\n");
558 			strncpy(s, "Error converting Unicode string",
559 					sizeof(s));
560 		}
561 	} else
562 		strncpy(s, "UNKNOWN_TYPE", sizeof(s));
563 	printf("Attribute type = 0x%x (%s)\n", u, s);
564 	u = le32_to_cpu(a->length);
565 	printf("Length of resident part = %u (0x%x)\n", u, u);
566 	printf("Attribute is %sresident\n", a->non_resident ? "non-" : "");
567 	printf("Name length = %u unicode characters\n", a->name_length);
568 	printf("Name offset = %u (0x%x)\n", le16_to_cpu(a->name_offset),
569 			le16_to_cpu(a->name_offset));
570 	flags = a->flags;
571 	if (a->name_length) {
572 		if (ucstos(s, (ntfschar*)((char*)a +
573 				le16_to_cpu(a->name_offset)),
574 				min((int)sizeof(s),
575 						a->name_length + 1)) == -1) {
576 			ntfs_log_error("Could not convert Unicode string to single "
577 				"byte string in current locale.\n");
578 			strncpy(s, "Error converting Unicode string",
579 					sizeof(s));
580 
581 		}
582 		printf("Name = %s\n", s);
583 	}
584 	printf("Attribute flags = 0x%x: ", le16_to_cpu(flags));
585 	if (!flags)
586 		printf("NONE");
587 	else {
588 		int first = TRUE;
589 		if (flags & ATTR_COMPRESSION_MASK) {
590 			if (flags & ATTR_IS_COMPRESSED) {
591 				printf("ATTR_IS_COMPRESSED");
592 				first = FALSE;
593 			}
594 			if ((flags & ATTR_COMPRESSION_MASK) & ~ATTR_IS_COMPRESSED) {
595 				if (!first)
596 					printf(" | ");
597 				else
598 					first = FALSE;
599 				printf("ATTR_UNKNOWN_COMPRESSION");
600 			}
601 		}
602 		if (flags & ATTR_IS_ENCRYPTED) {
603 			if (!first)
604 				printf(" | ");
605 			else
606 				first = FALSE;
607 			printf("ATTR_IS_ENCRYPTED");
608 		}
609 		if (flags & ATTR_IS_SPARSE) {
610 			if (!first)
611 				printf(" | ");
612 			else
613 				first = FALSE;
614 			printf("ATTR_IS_SPARSE");
615 		}
616 	}
617 	printf("\n");
618 	printf("Attribute instance = %u\n", le16_to_cpu(a->instance));
619 	if (a->non_resident) {
620 		dump_non_resident_attr(a);
621 	} else {
622 		dump_resident_attr(a);
623 	}
624 }
625 
626 /**
627  * dump_mft_record
628  */
dump_mft_record(MFT_RECORD * m)629 static void dump_mft_record(MFT_RECORD *m)
630 {
631 	ATTR_RECORD *a;
632 	unsigned int u;
633 	MFT_REF r;
634 
635 	printf("-- Beginning dump of mft record. --\n");
636 	u = le32_to_cpu(m->magic);
637 	printf("Mft record signature (magic) = %c%c%c%c\n", u & 0xff,
638 			u >> 8 & 0xff, u >> 16 & 0xff, u >> 24 & 0xff);
639 	u = le16_to_cpu(m->usa_ofs);
640 	printf("Update sequence array offset = %u (0x%x)\n", u, u);
641 	printf("Update sequence array size = %u\n", le16_to_cpu(m->usa_count));
642 	printf("$LogFile sequence number (lsn) = %llu\n",
643 			(unsigned long long)sle64_to_cpu(m->lsn));
644 	printf("Sequence number = %u\n", le16_to_cpu(m->sequence_number));
645 	printf("Reference (hard link) count = %u\n",
646 						le16_to_cpu(m->link_count));
647 	u = le16_to_cpu(m->attrs_offset);
648 	printf("First attribute offset = %u (0x%x)\n", u, u);
649 	printf("Flags = %u: ", le16_to_cpu(m->flags));
650 	if (m->flags & MFT_RECORD_IN_USE)
651 		printf("MFT_RECORD_IN_USE");
652 	else
653 		printf("MFT_RECORD_NOT_IN_USE");
654 	if (m->flags & MFT_RECORD_IS_DIRECTORY)
655 		printf(" | MFT_RECORD_IS_DIRECTORY");
656 	printf("\n");
657 	u = le32_to_cpu(m->bytes_in_use);
658 	printf("Bytes in use = %u (0x%x)\n", u, u);
659 	u = le32_to_cpu(m->bytes_allocated);
660 	printf("Bytes allocated = %u (0x%x)\n", u, u);
661 	r = le64_to_cpu(m->base_mft_record);
662 	printf("Base mft record reference:\n\tMft record number = %llu\n\t"
663 			"Sequence number = %u\n",
664 			(unsigned long long)MREF(r), MSEQNO(r));
665 	printf("Next attribute instance = %u\n",
666 			le16_to_cpu(m->next_attr_instance));
667 	a = (ATTR_RECORD*)((char*)m + le16_to_cpu(m->attrs_offset));
668 	printf("-- Beginning dump of attributes within mft record. --\n");
669 	while ((char*)a < (char*)m + le32_to_cpu(m->bytes_in_use)) {
670 		if (a->type == attr_type)
671 			dump_attr_record(m, a);
672 		if (a->type == AT_END)
673 			break;
674 		a = (ATTR_RECORD*)((char*)a + le32_to_cpu(a->length));
675 	};
676 	printf("-- End of attributes. --\n");
677 }
678 
679 /**
680  * ntfstruncate_exit
681  */
ntfstruncate_exit(void)682 static void ntfstruncate_exit(void)
683 {
684 	int err;
685 
686 	if (success)
687 		return;
688 	/* Close the attribute. */
689 	if (na)
690 		ntfs_attr_close(na);
691 	/* Close the inode. */
692 	if (ni && ntfs_inode_close(ni)) {
693 		ntfs_log_perror("Warning: Failed to close inode %lli",
694 				(long long)inode);
695 	}
696 	/* Unmount the volume. */
697 	err = ntfs_umount(vol, 0);
698 	if (err == -1)
699 		ntfs_log_perror("Warning: Could not umount %s", dev_name);
700 	/* Free the attribute name if it exists. */
701 	ntfs_ucsfree(attr_name);
702 }
703 
704 /**
705  * main
706  */
main(int argc,char ** argv)707 int main(int argc, char **argv)
708 {
709 	unsigned long mnt_flags, ul;
710 	int err;
711 
712 	ntfs_log_set_handler(ntfs_log_handler_outerr);
713 
714 	/* Initialize opts to zero / required values. */
715 	memset(&opts, 0, sizeof(opts));
716 
717 	/*
718 	 * Setup a default $AttrDef. FIXME: Should be reading this from the
719 	 * volume itself, at ntfs_mount() time.
720 	 */
721 	attr_defs = (ATTR_DEF*)&attrdef_ntfs3x_array;
722 
723 	/* Parse command line options. */
724 	parse_options(argc, argv);
725 
726 	utils_set_locale();
727 
728 	/* Make sure the file system is not mounted. */
729 	if (ntfs_check_if_mounted(dev_name, &mnt_flags))
730 		ntfs_log_perror("Failed to determine whether %s is mounted",
731 				dev_name);
732 	else if (mnt_flags & NTFS_MF_MOUNTED) {
733 		ntfs_log_error("%s is mounted.\n", dev_name);
734 		if (!opts.force)
735 			err_exit("Refusing to run!\n");
736 		fprintf(stderr, "ntfstruncate forced anyway. Hope /etc/mtab "
737 				"is incorrect.\n");
738 	}
739 
740 	/* Mount the device. */
741 	if (opts.no_action) {
742 		ntfs_log_quiet("Running in READ-ONLY mode!\n");
743 		ul = NTFS_MNT_RDONLY;
744 	} else
745 		ul = 0;
746 	vol = ntfs_mount(dev_name, ul);
747 	if (!vol)
748 		err_exit("Failed to mount %s: %s\n", dev_name, strerror(errno));
749 
750 	/* Register our exit function which will unlock and close the device. */
751 	err = atexit(&ntfstruncate_exit);
752 	if (err == -1) {
753 		ntfs_log_error("Could not set up exit() function because atexit() "
754 				"failed: %s Aborting...\n", strerror(errno));
755 		ntfstruncate_exit();
756 		exit(1);
757 	}
758 
759 	/* Open the specified inode. */
760 	ni = ntfs_inode_open(vol, inode);
761 	if (!ni)
762 		err_exit("Failed to open inode %lli: %s\n", (long long)inode,
763 				strerror(errno));
764 
765 	/* Open the specified attribute. */
766 	na = ntfs_attr_open(ni, attr_type, attr_name, attr_name_len);
767 	if (!na)
768 		err_exit("Failed to open attribute 0x%x: %s\n",
769 				(unsigned int)le32_to_cpu(attr_type), strerror(errno));
770 
771 	if (!opts.quiet && opts.verbose > 1) {
772 		ntfs_log_verbose("Dumping mft record before calling "
773 				"ntfs_attr_truncate():\n");
774 		dump_mft_record(ni->mrec);
775 	}
776 
777 	/* Truncate the attribute. */
778 	err = ntfs_attr_truncate(na, new_len);
779 	if (err)
780 		err_exit("Failed to truncate attribute 0x%x: %s\n",
781 				(unsigned int)le32_to_cpu(attr_type), strerror(errno));
782 
783 	if (!opts.quiet && opts.verbose > 1) {
784 		ntfs_log_verbose("Dumping mft record after calling "
785 				"ntfs_attr_truncate():\n");
786 		dump_mft_record(ni->mrec);
787 	}
788 
789 	/* Close the attribute. */
790 	ntfs_attr_close(na);
791 	na = NULL;
792 
793 	/* Close the inode. */
794 	err = ntfs_inode_close(ni);
795 	if (err)
796 		err_exit("Failed to close inode %lli: %s\n", (long long)inode,
797 				strerror(errno));
798 
799 	/* Unmount the volume. */
800 	err = ntfs_umount(vol, 0);
801 	if (err == -1)
802 		ntfs_log_perror("Warning: Failed to umount %s", dev_name);
803 
804 	/* Free the attribute name if it exists. */
805 	ntfs_ucsfree(attr_name);
806 
807 	/* Finally, disable our ntfstruncate_exit() handler. */
808 	success = TRUE;
809 
810 	ntfs_log_quiet("ntfstruncate completed successfully. Have a nice day.\n");
811 	return 0;
812 }
813