193 files changed, 80411 insertions, 44563 deletions
diff --git a/ntfsprogs/ntfstruncate.c b/ntfsprogs/ntfstruncate.c new file mode 100755 index 0000000..7ac5ae4 --- a/dev/null +++ b/ntfsprogs/ntfstruncate.c @@ -0,0 +1,809 @@ +/** + * ntfstruncate - Part of the Linux-NTFS project. + * + * Copyright (c) 2002-2005 Anton Altaparmakov + * + * This utility will truncate a specified attribute belonging to a + * specified inode, i.e. file or directory, to a specified length. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program (in the main directory of the Linux-NTFS source + * in the file COPYING); if not, write to the Free Software Foundation, + * Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include "config.h" + +#ifdef HAVE_UNISTD_H +# include <unistd.h> +#endif +#ifdef HAVE_STDLIB_H +# include <stdlib.h> +#endif +#ifdef HAVE_STDIO_H +# include <stdio.h> +#endif +#ifdef HAVE_STDARG_H +# include <stdarg.h> +#endif +#ifdef HAVE_STRING_H +# include <string.h> +#endif +#ifdef HAVE_ERRNO_H +# include <errno.h> +#endif +#ifdef HAVE_TIME_H +#include <time.h> +#endif +#ifdef HAVE_GETOPT_H +# include <getopt.h> +#else + extern char *optarg; + extern int optind; +#endif +#ifdef HAVE_LIMITS_H +#include <limits.h> +#endif +#ifndef LLONG_MAX +# define LLONG_MAX 9223372036854775807LL +#endif + +#include "types.h" +#include "attrib.h" +#include "inode.h" +#include "layout.h" +#include "volume.h" +#include "utils.h" +#include "attrdef.h" +/* #include "version.h" */ +#include "logging.h" + +const char *EXEC_NAME = "ntfstruncate"; + +/* Need these global so ntfstruncate_exit can access them. */ +BOOL success = FALSE; + +char *dev_name; +s64 inode; +u32 attr_type; +ntfschar *attr_name = NULL; +u32 attr_name_len; +s64 new_len; + +ntfs_volume *vol; +ntfs_inode *ni; +ntfs_attr *na = NULL; + +ATTR_DEF *attr_defs; + +struct { + /* -h, print usage and exit. */ + int no_action; /* -n, do not write to device, only display + what would be done. */ + int quiet; /* -q, quiet execution. */ + int verbose; /* -v, verbose execution, given twice, really + verbose execution (debug mode). */ + int force; /* -f, force truncation. */ + /* -V, print version and exit. */ +} opts; + +/** + * err_exit - error output and terminate; ignores quiet (-q) + */ +__attribute__((noreturn)) +__attribute__((format(printf, 1, 2))) +static void err_exit(const char *fmt, ...) +{ + va_list ap; + + fprintf(stderr, "ERROR: "); + va_start(ap, fmt); + vfprintf(stderr, fmt, ap); + va_end(ap); + fprintf(stderr, "Aborting...\n"); + exit(1); +} + +/** + * copyright - print copyright statements + */ +static void copyright(void) +{ + fprintf(stderr, "Copyright (c) 2002-2005 Anton Altaparmakov\n" + "Copyright (c) 2003 Richard Russon\n" + "Truncate a specified attribute of a specified " + "inode.\n"); +} + +/** + * license - print license statement + */ +static void license(void) +{ + fprintf(stderr, "%s", ntfs_gpl); +} + +/** + * usage - print a list of the parameters to the program + */ +__attribute__((noreturn)) +static void usage(void) +{ + copyright(); + fprintf(stderr, "Usage: %s [options] device inode [attr-type " + "[attr-name]] new-length\n" + " If attr-type is not specified, 0x80 (i.e. $DATA) " + "is assumed.\n" + " If attr-name is not specified, an unnamed " + "attribute is assumed.\n" + " -n Do not write to disk\n" + " -f Force execution despite errors\n" + " -q Quiet execution\n" + " -v Verbose execution\n" + " -vv Very verbose execution\n" + " -V Display version information\n" + " -l Display licensing information\n" + " -h Display this help\n", EXEC_NAME); + fprintf(stderr, "%s%s", ntfs_bugs, ntfs_home); + exit(1); +} + +/** + * parse_options + */ +static void parse_options(int argc, char *argv[]) +{ + long long ll; + char *s, *s2; + int c; + + if (argc && *argv) + EXEC_NAME = *argv; + fprintf(stderr, "%s v%s (libntfs-3g)\n", EXEC_NAME, VERSION); + while ((c = getopt(argc, argv, "fh?nqvVl")) != EOF) + switch (c) { + case 'f': + opts.force = 1; + break; + case 'n': + opts.no_action = 1; + break; + case 'q': + opts.quiet = 1; + ntfs_log_clear_levels(NTFS_LOG_LEVEL_QUIET); + break; + case 'v': + opts.verbose++; + ntfs_log_set_levels(NTFS_LOG_LEVEL_VERBOSE); + break; + case 'V': + /* Version number already printed, so just exit. */ + exit(0); + case 'l': + copyright(); + license(); + exit(0); + case 'h': + case '?': + default: + usage(); + } + if (optind == argc) + usage(); + + if (opts.verbose > 1) + ntfs_log_set_levels(NTFS_LOG_LEVEL_DEBUG | NTFS_LOG_LEVEL_TRACE | + NTFS_LOG_LEVEL_VERBOSE | NTFS_LOG_LEVEL_QUIET); + + /* Get the device. */ + dev_name = argv[optind++]; + ntfs_log_verbose("device name = %s\n", dev_name); + + if (optind == argc) + usage(); + + /* Get the inode. */ + ll = strtoll(argv[optind++], &s, 0); + if (*s || !ll || (ll >= LLONG_MAX && errno == ERANGE)) + err_exit("Invalid inode number: %s\n", argv[optind - 1]); + inode = ll; + ntfs_log_verbose("inode = %lli\n", (long long)inode); + + if (optind == argc) + usage(); + + /* Get the attribute type, if specified. */ + s = argv[optind++]; + if (optind == argc) { + attr_type = AT_DATA; + attr_name = AT_UNNAMED; + attr_name_len = 0; + } else { + unsigned long ul; + + ul = strtoul(s, &s2, 0); + if (*s2 || !ul || (ul >= ULONG_MAX && errno == ERANGE)) + err_exit("Invalid attribute type %s: %s\n", s, + strerror(errno)); + attr_type = ul; + + /* Get the attribute name, if specified. */ + s = argv[optind++]; + if (optind != argc) { + /* Convert the string to little endian Unicode. */ + attr_name_len = ntfs_mbstoucs(s, &attr_name); + if ((int)attr_name_len < 0) + err_exit("Invalid attribute name \"%s\": %s\n", + s, strerror(errno)); + + /* Keep hold of the original string. */ + s2 = s; + + s = argv[optind++]; + if (optind != argc) + usage(); + } else { + attr_name = AT_UNNAMED; + attr_name_len = 0; + } + } + ntfs_log_verbose("attribute type = 0x%x\n", (unsigned int)attr_type); + if (attr_name == AT_UNNAMED) + ntfs_log_verbose("attribute name = \"\" (UNNAMED)\n"); + else + ntfs_log_verbose("attribute name = \"%s\" (length %u Unicode " + "characters)\n", s2, + (unsigned int)attr_name_len); + + /* Get the new length. */ + ll = strtoll(s, &s2, 0); + if (*s2 || ll < 0 || (ll >= LLONG_MAX && errno == ERANGE)) + err_exit("Invalid new length: %s\n", s); + new_len = ll; + ntfs_log_verbose("new length = %lli\n", (long long)new_len); +} + +/** + * ucstos - convert unicode-character string to ASCII + * @dest: points to buffer to receive the converted string + * @src: points to string to convert + * @maxlen: size of @dest buffer in bytes + * + * Return the number of characters written to @dest, not including the + * terminating null byte. If a unicode character was encountered which could + * not be converted -1 is returned. + */ +static int ucstos(char *dest, const ntfschar *src, int maxlen) +{ + ntfschar u; + int i; + + /* Need one byte for null terminator. */ + maxlen--; + for (i = 0; i < maxlen; i++) { + u = le16_to_cpu(src[i]); + if (!u) + break; + if (u & 0xff00) + return -1; + dest[i] = u & 0xff; + } + dest[i] = 0; + return i; +} + +/** + * dump_resident_attr_val + */ +static void dump_resident_attr_val(ATTR_TYPES type, char *val, u32 val_len) +{ + const char *don_t_know = "Don't know what to do with this attribute " + "type yet."; + const char *skip = "Skipping display of $%s attribute value.\n"; + const char *todo = "This is still work in progress."; + char *buf; + int i, j; + u32 u; + + switch (type) { + case AT_STANDARD_INFORMATION: + // TODO + printf("%s\n", todo); + return; + case AT_ATTRIBUTE_LIST: + // TODO + printf("%s\n", todo); + return; + case AT_FILE_NAME: + // TODO + printf("%s\n", todo); + return; + case AT_OBJECT_ID: + // TODO + printf("%s\n", todo); + return; + case AT_SECURITY_DESCRIPTOR: + // TODO + printf("%s\n", todo); + return; + case AT_VOLUME_NAME: + printf("Volume name length = %u\n", (unsigned int)val_len); + if (val_len) { + buf = calloc(1, val_len); + if (!buf) + err_exit("Failed to allocate internal buffer: " + "%s\n", strerror(errno)); + i = ucstos(buf, (ntfschar*)val, val_len); + if (i == -1) + printf("Volume name contains non-displayable " + "Unicode characters.\n"); + printf("Volume name = %s\n", buf); + free(buf); + } + return; + case AT_VOLUME_INFORMATION: +#define VOL_INF(x) ((VOLUME_INFORMATION *)(x)) + printf("NTFS version %i.%i\n", VOL_INF(val)->major_ver, + VOL_INF(val)->minor_ver); + i = VOL_INF(val)->flags; +#undef VOL_INF + printf("Volume flags = 0x%x: ", i); + if (!i) { + printf("NONE\n"); + return; + } + j = 0; + if (i & VOLUME_MODIFIED_BY_CHKDSK) { + j = 1; + printf("VOLUME_MODIFIED_BY_CHKDSK"); + } + if (i & VOLUME_REPAIR_OBJECT_ID) { + if (j) + printf(" | "); + else + j = 0; + printf("VOLUME_REPAIR_OBJECT_ID"); + } + if (i & VOLUME_DELETE_USN_UNDERWAY) { + if (j) + printf(" | "); + else + j = 0; + printf("VOLUME_DELETE_USN_UNDERWAY"); + } + if (i & VOLUME_MOUNTED_ON_NT4) { + if (j) + printf(" | "); + else + j = 0; + printf("VOLUME_MOUNTED_ON_NT4"); + } + if (i & VOLUME_UPGRADE_ON_MOUNT) { + if (j) + printf(" | "); + else + j = 0; + printf("VOLUME_UPGRADE_ON_MOUNT"); + } + if (i & VOLUME_RESIZE_LOG_FILE) { + if (j) + printf(" | "); + else + j = 0; + printf("VOLUME_RESIZE_LOG_FILE"); + } + if (i & VOLUME_IS_DIRTY) { + if (j) + printf(" | "); + else + j = 0; + printf("VOLUME_IS_DIRTY"); + } + printf("\n"); + return; + case AT_DATA: + printf(skip, "DATA"); + return; + case AT_INDEX_ROOT: + // TODO + printf("%s\n", todo); + return; + case AT_INDEX_ALLOCATION: + // TODO + printf("%s\n", todo); + return; + case AT_BITMAP: + printf(skip, "BITMAP"); + return; + case AT_REPARSE_POINT: + // TODO + printf("%s\n", todo); + return; + case AT_EA_INFORMATION: + // TODO + printf("%s\n", don_t_know); + return; + case AT_EA: + // TODO + printf("%s\n", don_t_know); + return; + case AT_LOGGED_UTILITY_STREAM: + // TODO + printf("%s\n", don_t_know); + return; + default: + u = le32_to_cpu(type); + printf("Cannot display unknown %s defined attribute type 0x%x" + ".\n", u >= + le32_to_cpu(AT_FIRST_USER_DEFINED_ATTRIBUTE) ? + "user" : "system", (unsigned int)u); + } +} + +/** + * dump_resident_attr + */ +static void dump_resident_attr(ATTR_RECORD *a) +{ + int i; + + i = le32_to_cpu(a->value_length); + printf("Attribute value length = %u (0x%x)\n", i, i); + i = le16_to_cpu(a->value_offset); + printf("Attribute value offset = %u (0x%x)\n", i, i); + i = a->resident_flags; + printf("Resident flags = 0x%x: ", i); + if (!i) + printf("NONE\n"); + else if (i & ~RESIDENT_ATTR_IS_INDEXED) + printf("UNKNOWN FLAG(S)\n"); + else + printf("RESIDENT_ATTR_IS_INDEXED\n"); + dump_resident_attr_val(a->type, (char*)a + le16_to_cpu(a->value_offset), + le32_to_cpu(a->value_length)); +} + +/** + * dump_mapping_pairs_array + */ +static void dump_mapping_pairs_array(char *b __attribute__((unused)), + unsigned int max_len __attribute__((unused))) +{ + // TODO + return; +} + +/** + * dump_non_resident_attr + */ +static void dump_non_resident_attr(ATTR_RECORD *a) +{ + s64 l; + int i; + + l = sle64_to_cpu(a->lowest_vcn); + printf("Lowest VCN = %lli (0x%llx)\n", (long long)l, + (unsigned long long)l); + l = sle64_to_cpu(a->highest_vcn); + printf("Highest VCN = %lli (0x%llx)\n", (long long)l, + (unsigned long long)l); + printf("Mapping pairs array offset = 0x%x\n", + le16_to_cpu(a->mapping_pairs_offset)); + printf("Compression unit = 0x%x: %sCOMPRESSED\n", a->compression_unit, + a->compression_unit ? "" : "NOT "); + if (sle64_to_cpu(a->lowest_vcn)) + printf("Attribute is not the first extent. The following " + "sizes are meaningless:\n"); + l = sle64_to_cpu(a->allocated_size); + printf("Allocated size = %lli (0x%llx)\n", (long long)l, + (unsigned long long)l); + l = sle64_to_cpu(a->data_size); + printf("Data size = %lli (0x%llx)\n", (long long)l, + (unsigned long long)l); + l = sle64_to_cpu(a->initialized_size); + printf("Initialized size = %lli (0x%llx)\n", (long long)l, + (unsigned long long)l); + if (a->flags & ATTR_COMPRESSION_MASK) { + l = sle64_to_cpu(a->compressed_size); + printf("Compressed size = %lli (0x%llx)\n", (long long)l, + (unsigned long long)l); + } + i = le16_to_cpu(a->mapping_pairs_offset); + dump_mapping_pairs_array((char*)a + i, le32_to_cpu(a->length) - i); +} + +/** + * dump_attr_record + */ +static void dump_attr_record(MFT_RECORD *m, ATTR_RECORD *a) +{ + unsigned int u; + char s[0x200]; + int i; + + printf("-- Beginning dump of attribute record at offset 0x%x. --\n", + (unsigned)((u8*)a - (u8*)m)); + if (a->type == AT_END) { + printf("Attribute type = 0x%x ($END)\n", + (unsigned int)le32_to_cpu(AT_END)); + u = le32_to_cpu(a->length); + printf("Length of resident part = %u (0x%x)\n", u, u); + return; + } + u = le32_to_cpu(a->type); + for (i = 0; attr_defs[i].type; i++) + if (le32_to_cpu(attr_defs[i].type) >= u) + break; + if (attr_defs[i].type) { +// printf("type = 0x%x\n", le32_to_cpu(attr_defs[i].type)); +// { char *p = (char*)attr_defs[i].name; +// printf("name = %c%c%c%c%c\n", *p, p[1], p[2], p[3], p[4]); +// } + if (ucstos(s, attr_defs[i].name, sizeof(s)) == -1) { + ntfs_log_error("Could not convert Unicode string to single " + "byte string in current locale.\n"); + strncpy(s, "Error converting Unicode string", + sizeof(s)); + } + } else + strncpy(s, "UNKNOWN_TYPE", sizeof(s)); + printf("Attribute type = 0x%x (%s)\n", u, s); + u = le32_to_cpu(a->length); + printf("Length of resident part = %u (0x%x)\n", u, u); + printf("Attribute is %sresident\n", a->non_resident ? "non-" : ""); + printf("Name length = %u unicode characters\n", a->name_length); + printf("Name offset = %u (0x%x)\n", cpu_to_le16(a->name_offset), + cpu_to_le16(a->name_offset)); + u = a->flags; + if (a->name_length) { + if (ucstos(s, (ntfschar*)((char*)a + + cpu_to_le16(a->name_offset)), + min((int)sizeof(s), + a->name_length + 1)) == -1) { + ntfs_log_error("Could not convert Unicode string to single " + "byte string in current locale.\n"); + strncpy(s, "Error converting Unicode string", + sizeof(s)); + + } + printf("Name = %s\n", s); + } + printf("Attribute flags = 0x%x: ", le16_to_cpu(u)); + if (!u) + printf("NONE"); + else { + int first = TRUE; + if (u & ATTR_COMPRESSION_MASK) { + if (u & ATTR_IS_COMPRESSED) { + printf("ATTR_IS_COMPRESSED"); + first = FALSE; + } + if ((u & ATTR_COMPRESSION_MASK) & ~ATTR_IS_COMPRESSED) { + if (!first) + printf(" | "); + else + first = FALSE; + printf("ATTR_UNKNOWN_COMPRESSION"); + } + } + if (u & ATTR_IS_ENCRYPTED) { + if (!first) + printf(" | "); + else + first = FALSE; + printf("ATTR_IS_ENCRYPTED"); + } + if (u & ATTR_IS_SPARSE) { + if (!first) + printf(" | "); + else + first = FALSE; + printf("ATTR_IS_SPARSE"); + } + } + printf("\n"); + printf("Attribute instance = %u\n", le16_to_cpu(a->instance)); + if (a->non_resident) { + dump_non_resident_attr(a); + } else { + dump_resident_attr(a); + } +} + +/** + * dump_mft_record + */ +static void dump_mft_record(MFT_RECORD *m) +{ + ATTR_RECORD *a; + unsigned int u; + MFT_REF r; + + printf("-- Beginning dump of mft record. --\n"); + u = le32_to_cpu(m->magic); + printf("Mft record signature (magic) = %c%c%c%c\n", u & 0xff, + u >> 8 & 0xff, u >> 16 & 0xff, u >> 24 & 0xff); + u = le16_to_cpu(m->usa_ofs); + printf("Update sequence array offset = %u (0x%x)\n", u, u); + printf("Update sequence array size = %u\n", le16_to_cpu(m->usa_count)); + printf("$LogFile sequence number (lsn) = %llu\n", + (unsigned long long)le64_to_cpu(m->lsn)); + printf("Sequence number = %u\n", le16_to_cpu(m->sequence_number)); + printf("Reference (hard link) count = %u\n", + le16_to_cpu(m->link_count)); + u = le16_to_cpu(m->attrs_offset); + printf("First attribute offset = %u (0x%x)\n", u, u); + printf("Flags = %u: ", le16_to_cpu(m->flags)); + if (m->flags & MFT_RECORD_IN_USE) + printf("MFT_RECORD_IN_USE"); + else + printf("MFT_RECORD_NOT_IN_USE"); + if (m->flags & MFT_RECORD_IS_DIRECTORY) + printf(" | MFT_RECORD_IS_DIRECTORY"); + printf("\n"); + u = le32_to_cpu(m->bytes_in_use); + printf("Bytes in use = %u (0x%x)\n", u, u); + u = le32_to_cpu(m->bytes_allocated); + printf("Bytes allocated = %u (0x%x)\n", u, u); + r = le64_to_cpu(m->base_mft_record); + printf("Base mft record reference:\n\tMft record number = %llu\n\t" + "Sequence number = %u\n", + (unsigned long long)MREF(r), MSEQNO(r)); + printf("Next attribute instance = %u\n", + le16_to_cpu(m->next_attr_instance)); + a = (ATTR_RECORD*)((char*)m + le16_to_cpu(m->attrs_offset)); + printf("-- Beginning dump of attributes within mft record. --\n"); + while ((char*)a < (char*)m + le32_to_cpu(m->bytes_in_use)) { + if (a->type == cpu_to_le32(attr_type)) + dump_attr_record(m, a); + if (a->type == AT_END) + break; + a = (ATTR_RECORD*)((char*)a + le32_to_cpu(a->length)); + }; + printf("-- End of attributes. --\n"); +} + +/** + * ntfstruncate_exit + */ +static void ntfstruncate_exit(void) +{ + int err; + + if (success) + return; + /* Close the attribute. */ + if (na) + ntfs_attr_close(na); + /* Close the inode. */ + if (ni && ntfs_inode_close(ni)) { + ntfs_log_perror("Warning: Failed to close inode %lli", + (long long)inode); + } + /* Unmount the volume. */ + err = ntfs_umount(vol, 0); + if (err == -1) + ntfs_log_perror("Warning: Could not umount %s", dev_name); + /* Free the attribute name if it exists. */ + ntfs_ucsfree(attr_name); +} + +/** + * main + */ +int main(int argc, char **argv) +{ + unsigned long mnt_flags, ul; + int err; + + ntfs_log_set_handler(ntfs_log_handler_outerr); + + /* Initialize opts to zero / required values. */ + memset(&opts, 0, sizeof(opts)); + + /* + * Setup a default $AttrDef. FIXME: Should be reading this from the + * volume itself, at ntfs_mount() time. + */ + attr_defs = (ATTR_DEF*)&attrdef_ntfs3x_array; + + /* Parse command line options. */ + parse_options(argc, argv); + + utils_set_locale(); + + /* Make sure the file system is not mounted. */ + if (ntfs_check_if_mounted(dev_name, &mnt_flags)) + ntfs_log_perror("Failed to determine whether %s is mounted", + dev_name); + else if (mnt_flags & NTFS_MF_MOUNTED) { + ntfs_log_error("%s is mounted.\n", dev_name); + if (!opts.force) + err_exit("Refusing to run!\n"); + fprintf(stderr, "ntfstruncate forced anyway. Hope /etc/mtab " + "is incorrect.\n"); + } + + /* Mount the device. */ + if (opts.no_action) { + ntfs_log_quiet("Running in READ-ONLY mode!\n"); + ul = NTFS_MNT_RDONLY; + } else + ul = 0; + vol = ntfs_mount(dev_name, ul); + if (!vol) + err_exit("Failed to mount %s: %s\n", dev_name, strerror(errno)); + + /* Register our exit function which will unlock and close the device. */ + err = atexit(&ntfstruncate_exit); + if (err == -1) { + ntfs_log_error("Could not set up exit() function because atexit() " + "failed: %s Aborting...\n", strerror(errno)); + ntfstruncate_exit(); + exit(1); + } + + /* Open the specified inode. */ + ni = ntfs_inode_open(vol, inode); + if (!ni) + err_exit("Failed to open inode %lli: %s\n", (long long)inode, + strerror(errno)); + + /* Open the specified attribute. */ + na = ntfs_attr_open(ni, attr_type, attr_name, attr_name_len); + if (!na) + err_exit("Failed to open attribute 0x%x: %s\n", + (unsigned int)attr_type, strerror(errno)); + + if (!opts.quiet && opts.verbose > 1) { + ntfs_log_verbose("Dumping mft record before calling " + "ntfs_attr_truncate():\n"); + dump_mft_record(ni->mrec); + } + + /* Truncate the attribute. */ + err = ntfs_attr_truncate(na, new_len); + if (err) + err_exit("Failed to truncate attribute 0x%x: %s\n", + (unsigned int)attr_type, strerror(errno)); + + if (!opts.quiet && opts.verbose > 1) { + ntfs_log_verbose("Dumping mft record after calling " + "ntfs_attr_truncate():\n"); + dump_mft_record(ni->mrec); + } + + /* Close the attribute. */ + ntfs_attr_close(na); + na = NULL; + + /* Close the inode. */ + err = ntfs_inode_close(ni); + if (err) + err_exit("Failed to close inode %lli: %s\n", (long long)inode, + strerror(errno)); + + /* Unmount the volume. */ + err = ntfs_umount(vol, 0); + if (err == -1) + ntfs_log_perror("Warning: Failed to umount %s", dev_name); + + /* Free the attribute name if it exists. */ + ntfs_ucsfree(attr_name); + + /* Finally, disable our ntfstruncate_exit() handler. */ + success = TRUE; + + ntfs_log_quiet("ntfstruncate completed successfully. Have a nice day.\n"); + return 0; +} |