| /** |
| * 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; |
| ATTR_TYPES 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(int ret) |
| { |
| 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(ret); |
| } |
| |
| /** |
| * 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': |
| usage(0); |
| case '?': |
| default: |
| usage(1); |
| } |
| if (optind == argc) |
| usage(1); |
| |
| 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(1); |
| |
| /* 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(1); |
| |
| /* 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 = cpu_to_le32(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(1); |
| } else { |
| attr_name = AT_UNNAMED; |
| attr_name_len = 0; |
| } |
| } |
| ntfs_log_verbose("attribute type = 0x%x\n", (unsigned int)le32_to_cpu(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) |
| { |
| u16 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; |
| VOLUME_FLAGS flags; |
| 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); |
| flags = VOL_INF(val)->flags; |
| #undef VOL_INF |
| printf("Volume flags = 0x%x: ", le16_to_cpu(flags)); |
| if (!flags) { |
| printf("NONE\n"); |
| return; |
| } |
| j = 0; |
| if (flags & VOLUME_MODIFIED_BY_CHKDSK) { |
| j = 1; |
| printf("VOLUME_MODIFIED_BY_CHKDSK"); |
| } |
| if (flags & VOLUME_REPAIR_OBJECT_ID) { |
| if (j) |
| printf(" | "); |
| else |
| j = 0; |
| printf("VOLUME_REPAIR_OBJECT_ID"); |
| } |
| if (flags & VOLUME_DELETE_USN_UNDERWAY) { |
| if (j) |
| printf(" | "); |
| else |
| j = 0; |
| printf("VOLUME_DELETE_USN_UNDERWAY"); |
| } |
| if (flags & VOLUME_MOUNTED_ON_NT4) { |
| if (j) |
| printf(" | "); |
| else |
| j = 0; |
| printf("VOLUME_MOUNTED_ON_NT4"); |
| } |
| if (flags & VOLUME_UPGRADE_ON_MOUNT) { |
| if (j) |
| printf(" | "); |
| else |
| j = 0; |
| printf("VOLUME_UPGRADE_ON_MOUNT"); |
| } |
| if (flags & VOLUME_RESIZE_LOG_FILE) { |
| if (j) |
| printf(" | "); |
| else |
| j = 0; |
| printf("VOLUME_RESIZE_LOG_FILE"); |
| } |
| if (flags & 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; |
| ATTR_FLAGS flags; |
| |
| 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", le16_to_cpu(a->name_offset), |
| le16_to_cpu(a->name_offset)); |
| flags = a->flags; |
| if (a->name_length) { |
| if (ucstos(s, (ntfschar*)((char*)a + |
| le16_to_cpu(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(flags)); |
| if (!flags) |
| printf("NONE"); |
| else { |
| int first = TRUE; |
| if (flags & ATTR_COMPRESSION_MASK) { |
| if (flags & ATTR_IS_COMPRESSED) { |
| printf("ATTR_IS_COMPRESSED"); |
| first = FALSE; |
| } |
| if ((flags & ATTR_COMPRESSION_MASK) & ~ATTR_IS_COMPRESSED) { |
| if (!first) |
| printf(" | "); |
| else |
| first = FALSE; |
| printf("ATTR_UNKNOWN_COMPRESSION"); |
| } |
| } |
| if (flags & ATTR_IS_ENCRYPTED) { |
| if (!first) |
| printf(" | "); |
| else |
| first = FALSE; |
| printf("ATTR_IS_ENCRYPTED"); |
| } |
| if (flags & 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)sle64_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 == 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)le32_to_cpu(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)le32_to_cpu(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; |
| } |