2 * ntfstruncate - Part of the Linux-NTFS project.
4 * Copyright (c) 2002-2005 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
58 # define LLONG_MAX 9223372036854775807LL
68 /* #include "version.h" */
71 const char *EXEC_NAME
= "ntfstruncate";
73 /* Need these global so ntfstruncate_exit can access them. */
79 ntfschar
*attr_name
= NULL
;
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. */
101 * err_exit - error output and terminate; ignores quiet (-q)
103 __attribute__((noreturn
))
104 __attribute__((format(printf
, 1, 2)))
105 static void err_exit(const char *fmt
, ...)
109 fprintf(stderr
, "ERROR: ");
111 vfprintf(stderr
, fmt
, ap
);
113 fprintf(stderr
, "Aborting...\n");
118 * copyright - print copyright statements
120 static void copyright(void)
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 "
129 * license - print license statement
131 static void license(void)
133 fprintf(stderr
, "%s", ntfs_gpl
);
137 * usage - print a list of the parameters to the program
139 __attribute__((noreturn
))
140 static void usage(int ret
)
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) "
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
);
164 static void parse_options(int argc
, char *argv
[])
172 fprintf(stderr
, "%s v%s (libntfs-3g)\n", EXEC_NAME
, VERSION
);
173 while ((c
= getopt(argc
, argv
, "fh?nqvVl")) != EOF
)
183 ntfs_log_clear_levels(NTFS_LOG_LEVEL_QUIET
);
187 ntfs_log_set_levels(NTFS_LOG_LEVEL_VERBOSE
);
190 /* Version number already printed, so just exit. */
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
);
209 /* Get the device. */
210 dev_name
= argv
[optind
++];
211 ntfs_log_verbose("device name = %s\n", dev_name
);
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]);
221 ntfs_log_verbose("inode = %lli\n", (long long)inode
);
226 /* Get the attribute type, if specified. */
228 if (optind
== argc
) {
230 attr_name
= AT_UNNAMED
;
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
,
241 /* Get the attribute name, if specified. */
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",
250 /* Keep hold of the original string. */
257 attr_name
= AT_UNNAMED
;
261 ntfs_log_verbose("attribute type = 0x%x\n", (unsigned int)attr_type
);
262 if (attr_name
== AT_UNNAMED
)
263 ntfs_log_verbose("attribute name = \"\" (UNNAMED)\n");
265 ntfs_log_verbose("attribute name = \"%s\" (length %u Unicode "
267 (unsigned int)attr_name_len
);
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
);
274 ntfs_log_verbose("new length = %lli\n", (long long)new_len
);
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
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.
287 static int ucstos(char *dest
, const ntfschar
*src
, int maxlen
)
292 /* Need one byte for null terminator. */
294 for (i
= 0; i
< maxlen
; i
++) {
295 u
= le16_to_cpu(src
[i
]);
307 * dump_resident_attr_val
309 static void dump_resident_attr_val(ATTR_TYPES type
, char *val
, u32 val_len
)
311 const char *don_t_know
= "Don't know what to do with this attribute "
313 const char *skip
= "Skipping display of $%s attribute value.\n";
314 const char *todo
= "This is still work in progress.";
320 case AT_STANDARD_INFORMATION
:
322 printf("%s\n", todo
);
324 case AT_ATTRIBUTE_LIST
:
326 printf("%s\n", todo
);
330 printf("%s\n", todo
);
334 printf("%s\n", todo
);
336 case AT_SECURITY_DESCRIPTOR
:
338 printf("%s\n", todo
);
341 printf("Volume name length = %u\n", (unsigned int)val_len
);
343 buf
= calloc(1, val_len
);
345 err_exit("Failed to allocate internal buffer: "
346 "%s\n", strerror(errno
));
347 i
= ucstos(buf
, (ntfschar
*)val
, val_len
);
349 printf("Volume name contains non-displayable "
350 "Unicode characters.\n");
351 printf("Volume name = %s\n", buf
);
355 case AT_VOLUME_INFORMATION
:
356 #define VOL_INF(x) ((VOLUME_INFORMATION *)(x))
357 printf("NTFS version %i.%i\n", VOL_INF(val
)->major_ver
,
358 VOL_INF(val
)->minor_ver
);
359 i
= VOL_INF(val
)->flags
;
361 printf("Volume flags = 0x%x: ", le16_to_cpu(i
));
367 if (i
& VOLUME_MODIFIED_BY_CHKDSK
) {
369 printf("VOLUME_MODIFIED_BY_CHKDSK");
371 if (i
& VOLUME_REPAIR_OBJECT_ID
) {
376 printf("VOLUME_REPAIR_OBJECT_ID");
378 if (i
& VOLUME_DELETE_USN_UNDERWAY
) {
383 printf("VOLUME_DELETE_USN_UNDERWAY");
385 if (i
& VOLUME_MOUNTED_ON_NT4
) {
390 printf("VOLUME_MOUNTED_ON_NT4");
392 if (i
& VOLUME_UPGRADE_ON_MOUNT
) {
397 printf("VOLUME_UPGRADE_ON_MOUNT");
399 if (i
& VOLUME_RESIZE_LOG_FILE
) {
404 printf("VOLUME_RESIZE_LOG_FILE");
406 if (i
& VOLUME_IS_DIRTY
) {
411 printf("VOLUME_IS_DIRTY");
416 printf(skip
, "DATA");
420 printf("%s\n", todo
);
422 case AT_INDEX_ALLOCATION
:
424 printf("%s\n", todo
);
427 printf(skip
, "BITMAP");
429 case AT_REPARSE_POINT
:
431 printf("%s\n", todo
);
433 case AT_EA_INFORMATION
:
435 printf("%s\n", don_t_know
);
439 printf("%s\n", don_t_know
);
441 case AT_LOGGED_UTILITY_STREAM
:
443 printf("%s\n", don_t_know
);
446 u
= le32_to_cpu(type
);
447 printf("Cannot display unknown %s defined attribute type 0x%x"
449 le32_to_cpu(AT_FIRST_USER_DEFINED_ATTRIBUTE
) ?
450 "user" : "system", (unsigned int)u
);
457 static void dump_resident_attr(ATTR_RECORD
*a
)
461 i
= le32_to_cpu(a
->value_length
);
462 printf("Attribute value length = %u (0x%x)\n", i
, i
);
463 i
= le16_to_cpu(a
->value_offset
);
464 printf("Attribute value offset = %u (0x%x)\n", i
, i
);
465 i
= a
->resident_flags
;
466 printf("Resident flags = 0x%x: ", i
);
469 else if (i
& ~RESIDENT_ATTR_IS_INDEXED
)
470 printf("UNKNOWN FLAG(S)\n");
472 printf("RESIDENT_ATTR_IS_INDEXED\n");
473 dump_resident_attr_val(a
->type
, (char*)a
+ le16_to_cpu(a
->value_offset
),
474 le32_to_cpu(a
->value_length
));
478 * dump_mapping_pairs_array
480 static void dump_mapping_pairs_array(char *b
__attribute__((unused
)),
481 unsigned int max_len
__attribute__((unused
)))
488 * dump_non_resident_attr
490 static void dump_non_resident_attr(ATTR_RECORD
*a
)
495 l
= sle64_to_cpu(a
->lowest_vcn
);
496 printf("Lowest VCN = %lli (0x%llx)\n", (long long)l
,
497 (unsigned long long)l
);
498 l
= sle64_to_cpu(a
->highest_vcn
);
499 printf("Highest VCN = %lli (0x%llx)\n", (long long)l
,
500 (unsigned long long)l
);
501 printf("Mapping pairs array offset = 0x%x\n",
502 le16_to_cpu(a
->mapping_pairs_offset
));
503 printf("Compression unit = 0x%x: %sCOMPRESSED\n", a
->compression_unit
,
504 a
->compression_unit
? "" : "NOT ");
505 if (sle64_to_cpu(a
->lowest_vcn
))
506 printf("Attribute is not the first extent. The following "
507 "sizes are meaningless:\n");
508 l
= sle64_to_cpu(a
->allocated_size
);
509 printf("Allocated size = %lli (0x%llx)\n", (long long)l
,
510 (unsigned long long)l
);
511 l
= sle64_to_cpu(a
->data_size
);
512 printf("Data size = %lli (0x%llx)\n", (long long)l
,
513 (unsigned long long)l
);
514 l
= sle64_to_cpu(a
->initialized_size
);
515 printf("Initialized size = %lli (0x%llx)\n", (long long)l
,
516 (unsigned long long)l
);
517 if (a
->flags
& ATTR_COMPRESSION_MASK
) {
518 l
= sle64_to_cpu(a
->compressed_size
);
519 printf("Compressed size = %lli (0x%llx)\n", (long long)l
,
520 (unsigned long long)l
);
522 i
= le16_to_cpu(a
->mapping_pairs_offset
);
523 dump_mapping_pairs_array((char*)a
+ i
, le32_to_cpu(a
->length
) - i
);
529 static void dump_attr_record(MFT_RECORD
*m
, ATTR_RECORD
*a
)
535 printf("-- Beginning dump of attribute record at offset 0x%x. --\n",
536 (unsigned)((u8
*)a
- (u8
*)m
));
537 if (a
->type
== AT_END
) {
538 printf("Attribute type = 0x%x ($END)\n",
539 (unsigned int)le32_to_cpu(AT_END
));
540 u
= le32_to_cpu(a
->length
);
541 printf("Length of resident part = %u (0x%x)\n", u
, u
);
544 u
= le32_to_cpu(a
->type
);
545 for (i
= 0; attr_defs
[i
].type
; i
++)
546 if (le32_to_cpu(attr_defs
[i
].type
) >= u
)
548 if (attr_defs
[i
].type
) {
549 // printf("type = 0x%x\n", le32_to_cpu(attr_defs[i].type));
550 // { char *p = (char*)attr_defs[i].name;
551 // printf("name = %c%c%c%c%c\n", *p, p[1], p[2], p[3], p[4]);
553 if (ucstos(s
, attr_defs
[i
].name
, sizeof(s
)) == -1) {
554 ntfs_log_error("Could not convert Unicode string to single "
555 "byte string in current locale.\n");
556 strncpy(s
, "Error converting Unicode string",
560 strncpy(s
, "UNKNOWN_TYPE", sizeof(s
));
561 printf("Attribute type = 0x%x (%s)\n", u
, s
);
562 u
= le32_to_cpu(a
->length
);
563 printf("Length of resident part = %u (0x%x)\n", u
, u
);
564 printf("Attribute is %sresident\n", a
->non_resident
? "non-" : "");
565 printf("Name length = %u unicode characters\n", a
->name_length
);
566 printf("Name offset = %u (0x%x)\n", le16_to_cpu(a
->name_offset
),
567 le16_to_cpu(a
->name_offset
));
569 if (a
->name_length
) {
570 if (ucstos(s
, (ntfschar
*)((char*)a
+
571 le16_to_cpu(a
->name_offset
)),
573 a
->name_length
+ 1)) == -1) {
574 ntfs_log_error("Could not convert Unicode string to single "
575 "byte string in current locale.\n");
576 strncpy(s
, "Error converting Unicode string",
580 printf("Name = %s\n", s
);
582 printf("Attribute flags = 0x%x: ", le16_to_cpu(u
));
587 if (u
& ATTR_COMPRESSION_MASK
) {
588 if (u
& ATTR_IS_COMPRESSED
) {
589 printf("ATTR_IS_COMPRESSED");
592 if ((u
& ATTR_COMPRESSION_MASK
) & ~ATTR_IS_COMPRESSED
) {
597 printf("ATTR_UNKNOWN_COMPRESSION");
600 if (u
& ATTR_IS_ENCRYPTED
) {
605 printf("ATTR_IS_ENCRYPTED");
607 if (u
& ATTR_IS_SPARSE
) {
612 printf("ATTR_IS_SPARSE");
616 printf("Attribute instance = %u\n", le16_to_cpu(a
->instance
));
617 if (a
->non_resident
) {
618 dump_non_resident_attr(a
);
620 dump_resident_attr(a
);
627 static void dump_mft_record(MFT_RECORD
*m
)
633 printf("-- Beginning dump of mft record. --\n");
634 u
= le32_to_cpu(m
->magic
);
635 printf("Mft record signature (magic) = %c%c%c%c\n", u
& 0xff,
636 u
>> 8 & 0xff, u
>> 16 & 0xff, u
>> 24 & 0xff);
637 u
= le16_to_cpu(m
->usa_ofs
);
638 printf("Update sequence array offset = %u (0x%x)\n", u
, u
);
639 printf("Update sequence array size = %u\n", le16_to_cpu(m
->usa_count
));
640 printf("$LogFile sequence number (lsn) = %llu\n",
641 (unsigned long long)sle64_to_cpu(m
->lsn
));
642 printf("Sequence number = %u\n", le16_to_cpu(m
->sequence_number
));
643 printf("Reference (hard link) count = %u\n",
644 le16_to_cpu(m
->link_count
));
645 u
= le16_to_cpu(m
->attrs_offset
);
646 printf("First attribute offset = %u (0x%x)\n", u
, u
);
647 printf("Flags = %u: ", le16_to_cpu(m
->flags
));
648 if (m
->flags
& MFT_RECORD_IN_USE
)
649 printf("MFT_RECORD_IN_USE");
651 printf("MFT_RECORD_NOT_IN_USE");
652 if (m
->flags
& MFT_RECORD_IS_DIRECTORY
)
653 printf(" | MFT_RECORD_IS_DIRECTORY");
655 u
= le32_to_cpu(m
->bytes_in_use
);
656 printf("Bytes in use = %u (0x%x)\n", u
, u
);
657 u
= le32_to_cpu(m
->bytes_allocated
);
658 printf("Bytes allocated = %u (0x%x)\n", u
, u
);
659 r
= le64_to_cpu(m
->base_mft_record
);
660 printf("Base mft record reference:\n\tMft record number = %llu\n\t"
661 "Sequence number = %u\n",
662 (unsigned long long)MREF(r
), MSEQNO(r
));
663 printf("Next attribute instance = %u\n",
664 le16_to_cpu(m
->next_attr_instance
));
665 a
= (ATTR_RECORD
*)((char*)m
+ le16_to_cpu(m
->attrs_offset
));
666 printf("-- Beginning dump of attributes within mft record. --\n");
667 while ((char*)a
< (char*)m
+ le32_to_cpu(m
->bytes_in_use
)) {
668 if (a
->type
== cpu_to_le32(attr_type
))
669 dump_attr_record(m
, a
);
670 if (a
->type
== AT_END
)
672 a
= (ATTR_RECORD
*)((char*)a
+ le32_to_cpu(a
->length
));
674 printf("-- End of attributes. --\n");
680 static void ntfstruncate_exit(void)
686 /* Close the attribute. */
689 /* Close the inode. */
690 if (ni
&& ntfs_inode_close(ni
)) {
691 ntfs_log_perror("Warning: Failed to close inode %lli",
694 /* Unmount the volume. */
695 err
= ntfs_umount(vol
, 0);
697 ntfs_log_perror("Warning: Could not umount %s", dev_name
);
698 /* Free the attribute name if it exists. */
699 ntfs_ucsfree(attr_name
);
705 int main(int argc
, char **argv
)
707 unsigned long mnt_flags
, ul
;
710 ntfs_log_set_handler(ntfs_log_handler_outerr
);
712 /* Initialize opts to zero / required values. */
713 memset(&opts
, 0, sizeof(opts
));
716 * Setup a default $AttrDef. FIXME: Should be reading this from the
717 * volume itself, at ntfs_mount() time.
719 attr_defs
= (ATTR_DEF
*)&attrdef_ntfs3x_array
;
721 /* Parse command line options. */
722 parse_options(argc
, argv
);
726 /* Make sure the file system is not mounted. */
727 if (ntfs_check_if_mounted(dev_name
, &mnt_flags
))
728 ntfs_log_perror("Failed to determine whether %s is mounted",
730 else if (mnt_flags
& NTFS_MF_MOUNTED
) {
731 ntfs_log_error("%s is mounted.\n", dev_name
);
733 err_exit("Refusing to run!\n");
734 fprintf(stderr
, "ntfstruncate forced anyway. Hope /etc/mtab "
738 /* Mount the device. */
739 if (opts
.no_action
) {
740 ntfs_log_quiet("Running in READ-ONLY mode!\n");
741 ul
= NTFS_MNT_RDONLY
;
744 vol
= ntfs_mount(dev_name
, ul
);
746 err_exit("Failed to mount %s: %s\n", dev_name
, strerror(errno
));
748 /* Register our exit function which will unlock and close the device. */
749 err
= atexit(&ntfstruncate_exit
);
751 ntfs_log_error("Could not set up exit() function because atexit() "
752 "failed: %s Aborting...\n", strerror(errno
));
757 /* Open the specified inode. */
758 ni
= ntfs_inode_open(vol
, inode
);
760 err_exit("Failed to open inode %lli: %s\n", (long long)inode
,
763 /* Open the specified attribute. */
764 na
= ntfs_attr_open(ni
, attr_type
, attr_name
, attr_name_len
);
766 err_exit("Failed to open attribute 0x%x: %s\n",
767 (unsigned int)attr_type
, strerror(errno
));
769 if (!opts
.quiet
&& opts
.verbose
> 1) {
770 ntfs_log_verbose("Dumping mft record before calling "
771 "ntfs_attr_truncate():\n");
772 dump_mft_record(ni
->mrec
);
775 /* Truncate the attribute. */
776 err
= ntfs_attr_truncate(na
, new_len
);
778 err_exit("Failed to truncate attribute 0x%x: %s\n",
779 (unsigned int)le32_to_cpu(attr_type
), strerror(errno
));
781 if (!opts
.quiet
&& opts
.verbose
> 1) {
782 ntfs_log_verbose("Dumping mft record after calling "
783 "ntfs_attr_truncate():\n");
784 dump_mft_record(ni
->mrec
);
787 /* Close the attribute. */
791 /* Close the inode. */
792 err
= ntfs_inode_close(ni
);
794 err_exit("Failed to close inode %lli: %s\n", (long long)inode
,
797 /* Unmount the volume. */
798 err
= ntfs_umount(vol
, 0);
800 ntfs_log_perror("Warning: Failed to umount %s", dev_name
);
802 /* Free the attribute name if it exists. */
803 ntfs_ucsfree(attr_name
);
805 /* Finally, disable our ntfstruncate_exit() handler. */
808 ntfs_log_quiet("ntfstruncate completed successfully. Have a nice day.\n");