2 * ntfstruncate - Part of the Linux-NTFS project.
4 * Copyright (c) 2002-2003 Anton Altaparmakov
6 * This utility will truncate a specified attribute belonging to a
7 * specified inode, i.e. file or directory, to a specified length.
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.
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.
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
54 # define LLONG_MAX 9223372036854775807LL
64 extern const unsigned char attrdef_ntfs12_array[2400];
66 const char *EXEC_NAME = "ntfstruncate";
68 /* Need these global so ntfstruncate_exit can access them. */
74 uchar_t *attr_name = NULL;
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. */
96 * Dprintf - debugging output (-vv); overriden by quiet (-q)
98 void Dprintf(const char *fmt, ...)
102 if (!opts.quiet && opts.verbose > 1) {
111 * Eprintf - error output; ignores quiet (-q)
113 void Eprintf(const char *fmt, ...)
117 fprintf(stderr, "ERROR: ");
119 vfprintf(stderr, fmt, ap);
123 /* Generate code for Vprintf() function: Verbose output (-v). */
124 GEN_PRINTF(Vprintf, stdout, &opts.verbose, TRUE)
126 /* Generate code for Qprintf() function: Quietable output (if not -q). */
127 GEN_PRINTF(Qprintf, stdout, &opts.quiet, FALSE)
130 * err_exit - error output and terminate; ignores quiet (-q)
132 void err_exit(const char *fmt, ...)
136 fprintf(stderr, "ERROR: ");
138 vfprintf(stderr, fmt, ap);
140 fprintf(stderr, "Aborting...\n");
145 * copyright - print copyright statements
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 "
156 * license - print licese statement
160 fprintf(stderr, "%s", ntfs_gpl);
164 * usage - print a list of the parameters to the program
166 void usage(void) __attribute__ ((noreturn));
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) "
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);
191 void parse_options(int argc, char *argv[])
199 fprintf(stderr, "%s v%s\n", EXEC_NAME, VERSION);
200 while ((c = getopt(argc, argv, "fh?nqvVl")) != EOF)
215 /* Version number already printed, so just exit. */
229 /* Get the device. */
230 dev_name = argv[optind++];
231 Dprintf("device name = %s\n", dev_name);
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]);
241 Dprintf("inode = %Li\n", (long long)inode);
246 /* Get the attribute type, if specified. */
248 if (optind == argc) {
250 attr_name = AT_UNNAMED;
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,
261 /* Get the attribute name, if specified. */
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",
270 /* Keep hold of the original string. */
277 attr_name = AT_UNNAMED;
281 Dprintf("attribute type = 0x%x\n", attr_type);
282 if (attr_name == AT_UNNAMED)
283 Dprintf("attribute name = \"\" (UNNAMED)\n");
285 Dprintf("attribute name = \"%s\" (length %i Unicode "
286 "characters)\n", s2, attr_name_len);
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);
293 Dprintf("new length = %Li\n", new_len);
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
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.
306 int ucstos(char *dest, const uchar_t *src, int maxlen)
311 /* Need one byte for null terminator. */
313 for (i = 0; i < maxlen; i++) {
314 u = le16_to_cpu(src[i]);
326 * dump_resident_attr_val
328 void dump_resident_attr_val(ATTR_TYPES type, char *val, u32 val_len)
330 const char *don_t_know = "Don't know what to do with this attribute "
332 const char *skip = "Skipping display of $%s attribute value.\n";
333 const char *todo = "This is still work in progress.";
338 case AT_STANDARD_INFORMATION:
340 printf("%s\n", todo);
342 case AT_ATTRIBUTE_LIST:
344 printf("%s\n", todo);
348 printf("%s\n", todo);
352 printf("%s\n", todo);
354 case AT_SECURITY_DESCRIPTOR:
356 printf("%s\n", todo);
359 printf("Volume name length = %i\n", val_len);
361 buf = calloc(1, val_len);
363 err_exit("Failed to allocate internal buffer: "
364 "%s\n", strerror(errno));
365 i = ucstos(buf, (uchar_t*)val, val_len);
367 printf("Volume name contains non-displayable "
368 "Unicode characters.\n");
369 printf("Volume name = %s\n", buf);
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;
379 printf("Volume flags = 0x%x: ", i);
385 if (i & VOLUME_MODIFIED_BY_CHKDSK) {
387 printf("VOLUME_MODIFIED_BY_CHKDSK");
389 if (i & VOLUME_REPAIR_OBJECT_ID) {
394 printf("VOLUME_REPAIR_OBJECT_ID");
396 if (i & VOLUME_DELETE_USN_UNDERWAY) {
401 printf("VOLUME_DELETE_USN_UNDERWAY");
403 if (i & VOLUME_MOUNTED_ON_NT4) {
408 printf("VOLUME_MOUNTED_ON_NT4");
410 if (i & VOLUME_UPGRADE_ON_MOUNT) {
415 printf("VOLUME_UPGRADE_ON_MOUNT");
417 if (i & VOLUME_RESIZE_LOG_FILE) {
422 printf("VOLUME_RESIZE_LOG_FILE");
424 if (i & VOLUME_IS_DIRTY) {
429 printf("VOLUME_IS_DIRTY");
434 printf(skip, "DATA");
438 printf("%s\n", todo);
440 case AT_INDEX_ALLOCATION:
442 printf("%s\n", todo);
445 printf(skip, "BITMAP");
447 case AT_REPARSE_POINT:
449 printf("%s\n", todo);
451 case AT_EA_INFORMATION:
453 printf("%s\n", don_t_know);
457 printf("%s\n", don_t_know);
459 case AT_LOGGED_UTILITY_STREAM:
461 printf("%s\n", don_t_know);
464 i = le32_to_cpu(type);
465 printf("Cannot display unknown %s defined attribute type 0x%x"
467 le32_to_cpu(AT_FIRST_USER_DEFINED_ATTRIBUTE) ?
468 "user" : "system", i);
475 void dump_resident_attr(ATTR_RECORD *a)
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);
487 else if (i & ~RESIDENT_ATTR_IS_INDEXED)
488 printf("UNKNOWN FLAG(S)\n");
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));
496 * dump_mapping_pairs_array
498 void dump_mapping_pairs_array(char *b, unsigned int max_len)
505 * dump_non_resident_attr
507 void dump_non_resident_attr(ATTR_RECORD *a)
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);
533 i = le16_to_cpu(a->mapping_pairs_offset);
534 dump_mapping_pairs_array((char*)a + i, le32_to_cpu(a->length) - i);
540 void dump_attr_record(MFT_RECORD *m, ATTR_RECORD *a)
546 printf("-- Beginning dump of attribute record at offset 0x%x. --\n",
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);
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)
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]);
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",
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));
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",
589 printf("Name = %s\n", s);
591 printf("Attribute flags = 0x%x: ", le16_to_cpu(u));
596 if (u & ATTR_COMPRESSION_MASK) {
597 if (u & ATTR_IS_COMPRESSED) {
598 printf("ATTR_IS_COMPRESSED");
601 if ((u & ATTR_COMPRESSION_MASK) & ~ATTR_IS_COMPRESSED) {
606 printf("ATTR_UNKNOWN_COMPRESSION");
609 if (u & ATTR_IS_ENCRYPTED) {
614 printf("ATTR_IS_ENCRYPTED");
616 if (u & ATTR_IS_SPARSE) {
621 printf("ATTR_IS_SPARSE");
625 printf("Attribute instance = %u\n", le16_to_cpu(a->instance));
626 if (a->non_resident) {
627 dump_non_resident_attr(a);
629 dump_resident_attr(a);
636 void dump_mft_record(MFT_RECORD *m)
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");
659 printf("MFT_RECORD_NOT_IN_USE");
660 if (m->flags & MFT_RECORD_IS_DIRECTORY)
661 printf(" | MFT_RECORD_IS_DIRECTORY");
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)
679 a = (ATTR_RECORD*)((char*)a + le32_to_cpu(a->length));
681 printf("-- End of attributes. --\n");
687 void ntfstruncate_exit(void)
693 /* Close the attribute. */
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));
701 /* Unmount the volume. */
702 err = ntfs_umount(vol, 0);
704 fprintf(stderr, "Warning: Could not umount %s: %s\n", dev_name,
706 /* Free the attribute name if it exists. */
707 if (attr_name && attr_name != AT_UNNAMED)
714 int main(int argc, char **argv)
716 unsigned long mnt_flags, ul;
719 /* Initialize opts to zero / required values. */
720 memset(&opts, 0, sizeof(opts));
723 * Setup a default $AttrDef. FIXME: Should be reading this from the
724 * volume itself, at ntfs_mount() time.
726 attr_defs = (ATTR_DEF*)&attrdef_ntfs12_array;
728 /* Parse command line options. */
729 parse_options(argc, argv);
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);
740 err_exit("Refusing to run!\n");
741 fprintf(stderr, "ntfstruncate forced anyway. Hope /etc/mtab "
745 /* Mount the device. */
746 if (opts.no_action) {
747 Qprintf("Running in READ-ONLY mode!\n");
751 vol = ntfs_mount(dev_name, ul);
753 err_exit("Failed to mount %s: %s\n", dev_name, strerror(errno));
755 /* Register our exit function which will unlock and close the device. */
756 err = atexit(&ntfstruncate_exit);
758 Eprintf("Could not set up exit() function because atexit() "
759 "failed: %s Aborting...\n", strerror(errno));
764 /* Open the specified inode. */
765 ni = ntfs_inode_open(vol, inode);
767 err_exit("Failed to open inode %Li: %s\n", (long long)inode,
770 /* Open the specified attribute. */
771 na = ntfs_attr_open(ni, attr_type, attr_name, attr_name_len);
773 err_exit("Failed to open attribute 0x%x: %s\n", attr_type,
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);
782 /* Truncate the attribute. */
783 err = ntfs_attr_truncate(na, new_len);
785 err_exit("Failed to truncate attribute 0x%x: %s\n", attr_type,
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);
794 /* Close the attribute. */
798 /* Close the inode. */
799 err = ntfs_inode_close(ni);
801 err_exit("Failed to close inode %Li: %s\n", (long long)inode,
804 /* Unmount the volume. */
805 err = ntfs_umount(vol, 0);
807 fprintf(stderr, "Warning: Failed to umount %s: %s\n", dev_name,
810 /* Free the attribute name if it exists. */
811 if (attr_name && attr_name != AT_UNNAMED)
814 /* Finally, disable our ntfstruncate_exit() handler. */
817 Qprintf("ntfstruncate completed successfully. Have a nice day.\n");