1/** 2 * ntfsmftalloc - Part of the Linux-NTFS project. 3 * 4 * Copyright (c) 2002-2005 Anton Altaparmakov 5 * 6 * This utility will allocate and initialize an mft record. 7 * 8 * This program is free software; you can redistribute it and/or modify 9 * it under the terms of the GNU General Public License as published by 10 * the Free Software Foundation; either version 2 of the License, or 11 * (at your option) any later version. 12 * 13 * This program is distributed in the hope that it will be useful, 14 * but WITHOUT ANY WARRANTY; without even the implied warranty of 15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 16 * GNU General Public License for more details. 17 * 18 * You should have received a copy of the GNU General Public License 19 * along with this program (in the main directory of the Linux-NTFS source 20 * in the file COPYING); if not, write to the Free Software Foundation, 21 * Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA 22 */ 23 24#include "config.h" 25 26#ifdef HAVE_UNISTD_H 27# include <unistd.h> 28#endif 29#ifdef HAVE_STDLIB_H 30# include <stdlib.h> 31#endif 32#ifdef HAVE_STDIO_H 33# include <stdio.h> 34#endif 35#ifdef HAVE_STDARG_H 36# include <stdarg.h> 37#endif 38#ifdef HAVE_STRING_H 39# include <string.h> 40#endif 41#ifdef HAVE_ERRNO_H 42# include <errno.h> 43#endif 44#ifdef HAVE_TIME_H 45#include <time.h> 46#endif 47#ifdef HAVE_GETOPT_H 48# include <getopt.h> 49#else 50 extern int optind; 51#endif 52#ifdef HAVE_LIMITS_H 53#include <limits.h> 54#endif 55#ifndef LLONG_MAX 56# define LLONG_MAX 9223372036854775807LL 57#endif 58 59#include "types.h" 60#include "attrib.h" 61#include "inode.h" 62#include "layout.h" 63#include "volume.h" 64#include "mft.h" 65#include "utils.h" 66/* #include "version.h" */ 67#include "logging.h" 68 69static const char *EXEC_NAME = "ntfsmftalloc"; 70 71/* Need these global so ntfsmftalloc_exit can access them. */ 72static BOOL success = FALSE; 73 74static char *dev_name; 75 76static ntfs_volume *vol; 77static ntfs_inode *ni = NULL; 78static ntfs_inode *base_ni = NULL; 79static s64 base_mft_no = -1; 80 81static struct { 82 /* -h, print usage and exit. */ 83 int no_action; /* -n, do not write to device, only display 84 what would be done. */ 85 int quiet; /* -q, quiet execution. */ 86 int verbose; /* -v, verbose execution, given twice, really 87 verbose execution (debug mode). */ 88 int force; /* -f, force allocation. */ 89 /* -V, print version and exit. */ 90} opts; 91 92/** 93 * err_exit - error output and terminate; ignores quiet (-q) 94 */ 95__attribute__((noreturn)) 96__attribute__((format(printf, 1, 2))) 97static void err_exit(const char *fmt, ...) 98{ 99 va_list ap; 100 101 fprintf(stderr, "ERROR: "); 102 va_start(ap, fmt); 103 vfprintf(stderr, fmt, ap); 104 va_end(ap); 105 fprintf(stderr, "Aborting...\n"); 106 exit(1); 107} 108 109/** 110 * copyright - print copyright statements 111 */ 112static void copyright(void) 113{ 114 ntfs_log_info("Copyright (c) 2004-2005 Anton Altaparmakov\n" 115 "Allocate and initialize a base or an extent mft " 116 "record. If a base mft record\nis not specified, a " 117 "base mft record is allocated and initialized. " 118 "Otherwise,\nan extent mft record is allocated and " 119 "initialized to point to the specified\nbase mft " 120 "record.\n"); 121} 122 123/** 124 * license - print license statement 125 */ 126static void license(void) 127{ 128 ntfs_log_info("%s", ntfs_gpl); 129} 130 131/** 132 * usage - print a list of the parameters to the program 133 */ 134__attribute__((noreturn)) 135static void usage(void) 136{ 137 copyright(); 138 ntfs_log_info("Usage: %s [options] device [base-mft-record]\n" 139 " -n Do not write to disk\n" 140 " -f Force execution despite errors\n" 141 " -q Quiet execution\n" 142 " -v Verbose execution\n" 143 " -vv Very verbose execution\n" 144 " -V Display version information\n" 145 " -l Display licensing information\n" 146 " -h Display this help\n", EXEC_NAME); 147 ntfs_log_info("%s%s", ntfs_bugs, ntfs_home); 148 exit(1); 149} 150 151/** 152 * parse_options 153 */ 154static void parse_options(int argc, char *argv[]) 155{ 156 long long ll; 157 char *s; 158 int c; 159 160 if (argc && *argv) 161 EXEC_NAME = *argv; 162 ntfs_log_info("%s v%s (libntfs-3g)\n", EXEC_NAME, VERSION); 163 while ((c = getopt(argc, argv, "fh?nqvVl")) != EOF) { 164 switch (c) { 165 case 'f': 166 opts.force = 1; 167 break; 168 case 'n': 169 opts.no_action = 1; 170 break; 171 case 'q': 172 opts.quiet = 1; 173 ntfs_log_clear_levels(NTFS_LOG_LEVEL_QUIET); 174 break; 175 case 'v': 176 opts.verbose++; 177 ntfs_log_set_levels(NTFS_LOG_LEVEL_VERBOSE); 178 break; 179 case 'V': 180 /* Version number already printed, so just exit. */ 181 exit(0); 182 case 'l': 183 copyright(); 184 license(); 185 exit(0); 186 case 'h': 187 case '?': 188 default: 189 usage(); 190 } 191 } 192 193 if (opts.verbose > 1) 194 ntfs_log_set_levels(NTFS_LOG_LEVEL_DEBUG | NTFS_LOG_LEVEL_TRACE | 195 NTFS_LOG_LEVEL_VERBOSE | NTFS_LOG_LEVEL_QUIET); 196 197 if (optind == argc) 198 usage(); 199 /* Get the device. */ 200 dev_name = argv[optind++]; 201 ntfs_log_verbose("device name = %s\n", dev_name); 202 if (optind != argc) { 203 /* Get the base mft record number. */ 204 ll = strtoll(argv[optind++], &s, 0); 205 if (*s || !ll || (ll >= LLONG_MAX && errno == ERANGE)) 206 err_exit("Invalid base mft record number: %s\n", 207 argv[optind - 1]); 208 base_mft_no = ll; 209 ntfs_log_verbose("base mft record number = 0x%llx\n", (long long)ll); 210 } 211 if (optind != argc) 212 usage(); 213} 214 215/** 216 * dump_mft_record 217 */ 218static void dump_mft_record(MFT_RECORD *m) 219{ 220 ATTR_RECORD *a; 221 unsigned int u; 222 MFT_REF r; 223 224 ntfs_log_info("-- Beginning dump of mft record. --\n"); 225 u = le32_to_cpu(m->magic); 226 ntfs_log_info("Mft record signature (magic) = %c%c%c%c\n", u & 0xff, 227 u >> 8 & 0xff, u >> 16 & 0xff, u >> 24 & 0xff); 228 u = le16_to_cpu(m->usa_ofs); 229 ntfs_log_info("Update sequence array offset = %u (0x%x)\n", u, u); 230 ntfs_log_info("Update sequence array size = %u\n", le16_to_cpu(m->usa_count)); 231 ntfs_log_info("$LogFile sequence number (lsn) = %llu\n", 232 (unsigned long long)sle64_to_cpu(m->lsn)); 233 ntfs_log_info("Sequence number = %u\n", le16_to_cpu(m->sequence_number)); 234 ntfs_log_info("Reference (hard link) count = %u\n", 235 le16_to_cpu(m->link_count)); 236 u = le16_to_cpu(m->attrs_offset); 237 ntfs_log_info("First attribute offset = %u (0x%x)\n", u, u); 238 ntfs_log_info("Flags = %u: ", le16_to_cpu(m->flags)); 239 if (m->flags & MFT_RECORD_IN_USE) 240 ntfs_log_info("MFT_RECORD_IN_USE"); 241 else 242 ntfs_log_info("MFT_RECORD_NOT_IN_USE"); 243 if (m->flags & MFT_RECORD_IS_DIRECTORY) 244 ntfs_log_info(" | MFT_RECORD_IS_DIRECTORY"); 245 ntfs_log_info("\n"); 246 u = le32_to_cpu(m->bytes_in_use); 247 ntfs_log_info("Bytes in use = %u (0x%x)\n", u, u); 248 u = le32_to_cpu(m->bytes_allocated); 249 ntfs_log_info("Bytes allocated = %u (0x%x)\n", u, u); 250 r = le64_to_cpu(m->base_mft_record); 251 ntfs_log_info("Base mft record reference:\n\tMft record number = %llu\n\t" 252 "Sequence number = %u\n", 253 (unsigned long long)MREF(r), MSEQNO(r)); 254 ntfs_log_info("Next attribute instance = %u\n", 255 le16_to_cpu(m->next_attr_instance)); 256 a = (ATTR_RECORD*)((char*)m + le16_to_cpu(m->attrs_offset)); 257 ntfs_log_info("-- Beginning dump of attributes within mft record. --\n"); 258 while ((char*)a < (char*)m + le32_to_cpu(m->bytes_in_use)) { 259 if (a->type == AT_END) 260 break; 261 a = (ATTR_RECORD*)((char*)a + le32_to_cpu(a->length)); 262 }; 263 ntfs_log_info("-- End of attributes. --\n"); 264} 265 266/** 267 * ntfsmftalloc_exit 268 */ 269static void ntfsmftalloc_exit(void) 270{ 271 if (success) 272 return; 273 /* If there is a base inode, close that instead of the extent inode. */ 274 if (base_ni) 275 ni = base_ni; 276 /* Close the inode. */ 277 if (ni && ntfs_inode_close(ni)) { 278 ntfs_log_perror("Warning: Failed to close inode 0x%llx", 279 (long long)ni->mft_no); 280 } 281 /* Unmount the volume. */ 282 if (ntfs_umount(vol, 0) == -1) 283 ntfs_log_perror("Warning: Could not umount %s", dev_name); 284} 285 286/** 287 * main 288 */ 289int main(int argc, char **argv) 290{ 291 unsigned long mnt_flags, ul; 292 int err; 293 294 ntfs_log_set_handler(ntfs_log_handler_outerr); 295 296 /* Initialize opts to zero / required values. */ 297 memset(&opts, 0, sizeof(opts)); 298 /* Parse command line options. */ 299 parse_options(argc, argv); 300 utils_set_locale(); 301 /* Make sure the file system is not mounted. */ 302 if (ntfs_check_if_mounted(dev_name, &mnt_flags)) 303 ntfs_log_error("Failed to determine whether %s is mounted: %s\n", 304 dev_name, strerror(errno)); 305 else if (mnt_flags & NTFS_MF_MOUNTED) { 306 ntfs_log_error("%s is mounted.\n", dev_name); 307 if (!opts.force) 308 err_exit("Refusing to run!\n"); 309 ntfs_log_error("ntfsmftalloc forced anyway. Hope /etc/mtab " 310 "is incorrect.\n"); 311 } 312 /* Mount the device. */ 313 if (opts.no_action) { 314 ntfs_log_quiet("Running in READ-ONLY mode!\n"); 315 ul = NTFS_MNT_RDONLY; 316 } else 317 ul = 0; 318 vol = ntfs_mount(dev_name, ul); 319 if (!vol) 320 err_exit("Failed to mount %s: %s\n", dev_name, strerror(errno)); 321 /* Register our exit function which will unlock and close the device. */ 322 err = atexit(&ntfsmftalloc_exit); 323 if (err == -1) { 324 ntfs_log_error("Could not set up exit() function because atexit() " 325 "failed: %s Aborting...\n", strerror(errno)); 326 ntfsmftalloc_exit(); 327 exit(1); 328 } 329 if (base_mft_no != -1) { 330 base_ni = ntfs_inode_open(vol, base_mft_no); 331 if (!base_ni) 332 err_exit("Failed to open base inode 0x%llx: %s\n", 333 (long long)base_mft_no, 334 strerror(errno)); 335 } 336 /* Open the specified inode. */ 337 ni = ntfs_mft_record_alloc(vol, base_ni); 338 if (!ni) 339 err_exit("Failed to allocate mft record: %s\n", 340 strerror(errno)); 341 ntfs_log_info("Allocated %s mft record 0x%llx", base_ni ? "extent" : "base", 342 (long long)ni->mft_no); 343 if (base_ni) 344 ntfs_log_info(" with base mft record 0x%llx", 345 (long long)base_mft_no); 346 ntfs_log_info(".\n"); 347 if (!opts.quiet && opts.verbose > 1) { 348 ntfs_log_verbose("Dumping allocated mft record 0x%llx:\n", 349 (long long)ni->mft_no); 350 dump_mft_record(ni->mrec); 351 } 352 /* Close the (base) inode. */ 353 if (base_ni) 354 ni = base_ni; 355 err = ntfs_inode_close(ni); 356 if (err) 357 err_exit("Failed to close inode 0x%llx: %s\n", 358 (long long)ni->mft_no, strerror(errno)); 359 /* Unmount the volume. */ 360 err = ntfs_umount(vol, 0); 361 /* Disable our ntfsmftalloc_exit() handler. */ 362 success = TRUE; 363 if (err == -1) 364 ntfs_log_perror("Warning: Failed to umount %s", dev_name); 365 else 366 ntfs_log_quiet("ntfsmftalloc completed successfully.\n"); 367 return 0; 368} 369