/** * ntfsmftalloc - Part of the Linux-NTFS project. * * Copyright (c) 2002-2005 Anton Altaparmakov * * This utility will allocate and initialize an mft record. * * 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 #endif #ifdef HAVE_STDLIB_H # include #endif #ifdef HAVE_STDIO_H # include #endif #ifdef HAVE_STDARG_H # include #endif #ifdef HAVE_STRING_H # include #endif #ifdef HAVE_ERRNO_H # include #endif #ifdef HAVE_TIME_H #include #endif #ifdef HAVE_GETOPT_H # include #else extern int optind; #endif #ifdef HAVE_LIMITS_H #include #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 "mft.h" #include "utils.h" /* #include "version.h" */ #include "logging.h" static const char *EXEC_NAME = "ntfsmftalloc"; /* Need these global so ntfsmftalloc_exit can access them. */ static BOOL success = FALSE; static char *dev_name; static ntfs_volume *vol; static ntfs_inode *ni = NULL; static ntfs_inode *base_ni = NULL; static s64 base_mft_no = -1; static 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 allocation. */ /* -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) { ntfs_log_info("Copyright (c) 2004-2005 Anton Altaparmakov\n" "Allocate and initialize a base or an extent mft " "record. If a base mft record\nis not specified, a " "base mft record is allocated and initialized. " "Otherwise,\nan extent mft record is allocated and " "initialized to point to the specified\nbase mft " "record.\n"); } /** * license - print license statement */ static void license(void) { ntfs_log_info("%s", ntfs_gpl); } /** * usage - print a list of the parameters to the program */ __attribute__((noreturn)) static void usage(void) { copyright(); ntfs_log_info("Usage: %s [options] device [base-mft-record]\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); ntfs_log_info("%s%s", ntfs_bugs, ntfs_home); exit(1); } /** * parse_options */ static void parse_options(int argc, char *argv[]) { long long ll; char *s; int c; if (argc && *argv) EXEC_NAME = *argv; ntfs_log_info("%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 (opts.verbose > 1) ntfs_log_set_levels(NTFS_LOG_LEVEL_DEBUG | NTFS_LOG_LEVEL_TRACE | NTFS_LOG_LEVEL_VERBOSE | NTFS_LOG_LEVEL_QUIET); if (optind == argc) usage(); /* Get the device. */ dev_name = argv[optind++]; ntfs_log_verbose("device name = %s\n", dev_name); if (optind != argc) { /* Get the base mft record number. */ ll = strtoll(argv[optind++], &s, 0); if (*s || !ll || (ll >= LLONG_MAX && errno == ERANGE)) err_exit("Invalid base mft record number: %s\n", argv[optind - 1]); base_mft_no = ll; ntfs_log_verbose("base mft record number = 0x%llx\n", (long long)ll); } if (optind != argc) usage(); } /** * dump_mft_record */ static void dump_mft_record(MFT_RECORD *m) { ATTR_RECORD *a; unsigned int u; MFT_REF r; ntfs_log_info("-- Beginning dump of mft record. --\n"); u = le32_to_cpu(m->magic); ntfs_log_info("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); ntfs_log_info("Update sequence array offset = %u (0x%x)\n", u, u); ntfs_log_info("Update sequence array size = %u\n", le16_to_cpu(m->usa_count)); ntfs_log_info("$LogFile sequence number (lsn) = %llu\n", (unsigned long long)le64_to_cpu(m->lsn)); ntfs_log_info("Sequence number = %u\n", le16_to_cpu(m->sequence_number)); ntfs_log_info("Reference (hard link) count = %u\n", le16_to_cpu(m->link_count)); u = le16_to_cpu(m->attrs_offset); ntfs_log_info("First attribute offset = %u (0x%x)\n", u, u); ntfs_log_info("Flags = %u: ", le16_to_cpu(m->flags)); if (m->flags & MFT_RECORD_IN_USE) ntfs_log_info("MFT_RECORD_IN_USE"); else ntfs_log_info("MFT_RECORD_NOT_IN_USE"); if (m->flags & MFT_RECORD_IS_DIRECTORY) ntfs_log_info(" | MFT_RECORD_IS_DIRECTORY"); ntfs_log_info("\n"); u = le32_to_cpu(m->bytes_in_use); ntfs_log_info("Bytes in use = %u (0x%x)\n", u, u); u = le32_to_cpu(m->bytes_allocated); ntfs_log_info("Bytes allocated = %u (0x%x)\n", u, u); r = le64_to_cpu(m->base_mft_record); ntfs_log_info("Base mft record reference:\n\tMft record number = %llu\n\t" "Sequence number = %u\n", (unsigned long long)MREF(r), MSEQNO(r)); ntfs_log_info("Next attribute instance = %u\n", le16_to_cpu(m->next_attr_instance)); a = (ATTR_RECORD*)((char*)m + le16_to_cpu(m->attrs_offset)); ntfs_log_info("-- Beginning dump of attributes within mft record. --\n"); while ((char*)a < (char*)m + le32_to_cpu(m->bytes_in_use)) { if (a->type == AT_END) break; a = (ATTR_RECORD*)((char*)a + le32_to_cpu(a->length)); }; ntfs_log_info("-- End of attributes. --\n"); } /** * ntfsmftalloc_exit */ static void ntfsmftalloc_exit(void) { if (success) return; /* If there is a base inode, close that instead of the extent inode. */ if (base_ni) ni = base_ni; /* Close the inode. */ if (ni && ntfs_inode_close(ni)) { ntfs_log_perror("Warning: Failed to close inode 0x%llx", (long long)ni->mft_no); } /* Unmount the volume. */ if (ntfs_umount(vol, 0) == -1) ntfs_log_perror("Warning: Could not umount %s", dev_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)); /* 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_error("Failed to determine whether %s is mounted: %s\n", dev_name, strerror(errno)); 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"); ntfs_log_error("ntfsmftalloc 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(&ntfsmftalloc_exit); if (err == -1) { ntfs_log_error("Could not set up exit() function because atexit() " "failed: %s Aborting...\n", strerror(errno)); ntfsmftalloc_exit(); exit(1); } if (base_mft_no != -1) { base_ni = ntfs_inode_open(vol, base_mft_no); if (!base_ni) err_exit("Failed to open base inode 0x%llx: %s\n", (long long)base_mft_no, strerror(errno)); } /* Open the specified inode. */ ni = ntfs_mft_record_alloc(vol, base_ni); if (!ni) err_exit("Failed to allocate mft record: %s\n", strerror(errno)); ntfs_log_info("Allocated %s mft record 0x%llx", base_ni ? "extent" : "base", (long long)ni->mft_no); if (base_ni) ntfs_log_info(" with base mft record 0x%llx", (long long)base_mft_no); ntfs_log_info(".\n"); if (!opts.quiet && opts.verbose > 1) { ntfs_log_verbose("Dumping allocated mft record 0x%llx:\n", (long long)ni->mft_no); dump_mft_record(ni->mrec); } /* Close the (base) inode. */ if (base_ni) ni = base_ni; err = ntfs_inode_close(ni); if (err) err_exit("Failed to close inode 0x%llx: %s\n", (long long)ni->mft_no, strerror(errno)); /* Unmount the volume. */ err = ntfs_umount(vol, 0); /* Disable our ntfsmftalloc_exit() handler. */ success = TRUE; if (err == -1) ntfs_log_perror("Warning: Failed to umount %s", dev_name); else ntfs_log_quiet("ntfsmftalloc completed successfully.\n"); return 0; }