1 /** 2 * ntfscluster - Part of the Linux-NTFS project. 3 * 4 * Copyright (c) 2002-2003 Richard Russon 5 * Copyright (c) 2005 Anton Altaparmakov 6 * Copyright (c) 2005-2006 Szabolcs Szakacsits 7 * 8 * This utility will locate the owner of any given sector or cluster. 9 * 10 * This program is free software; you can redistribute it and/or modify 11 * it under the terms of the GNU General Public License as published by 12 * the Free Software Foundation; either version 2 of the License, or 13 * (at your option) any later version. 14 * 15 * This program is distributed in the hope that it will be useful, 16 * but WITHOUT ANY WARRANTY; without even the implied warranty of 17 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 18 * GNU General Public License for more details. 19 * 20 * You should have received a copy of the GNU General Public License 21 * along with this program (in the main directory of the Linux-NTFS 22 * distribution in the file COPYING); if not, write to the Free Software 23 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA 24 */ 25 26 #include "config.h" 27 28 #ifdef HAVE_STDIO_H 29 #include <stdio.h> 30 #endif 31 #ifdef HAVE_GETOPT_H 32 #include <getopt.h> 33 #endif 34 #ifdef HAVE_STDLIB_H 35 #include <stdlib.h> 36 #endif 37 #ifdef HAVE_STRING_H 38 #include <string.h> 39 #endif 40 #ifdef HAVE_LIMITS_H 41 #include <limits.h> 42 #endif 43 44 #include "compat.h" 45 #include "ntfscluster.h" 46 #include "types.h" 47 #include "attrib.h" 48 #include "utils.h" 49 #include "volume.h" 50 #include "debug.h" 51 #include "dir.h" 52 #include "cluster.h" 53 #include "version.h" 54 #include "logging.h" 55 56 static const char *EXEC_NAME = "ntfscluster"; 57 static struct options opts; 58 59 /** 60 * version - Print version information about the program 61 * 62 * Print a copyright statement and a brief description of the program. 63 * 64 * Return: none 65 */ 66 static void version(void) 67 { 68 ntfs_log_info("\n%s v%s (libntfs %s) - Find the owner of any given sector or " 69 "cluster.\n\n", EXEC_NAME, VERSION, 70 ntfs_libntfs_version()); 71 ntfs_log_info("Copyright (c) 2002-2003 Richard Russon\n"); 72 ntfs_log_info("Copyright (c) 2005 Anton Altaparmakov\n"); 73 ntfs_log_info("Copyright (c) 2005-2006 Szabolcs Szakacsits\n"); 74 ntfs_log_info("\n%s\n%s%s\n", ntfs_gpl, ntfs_bugs, ntfs_home); 75 } 76 77 /** 78 * usage - Print a list of the parameters to the program 79 * 80 * Print a list of the parameters and options for the program. 81 * 82 * Return: none 83 */ 84 static void usage(void) 85 { 86 ntfs_log_info("\nUsage: %s [options] device\n" 87 " -i, --info Print information about the volume (default)\n" 88 "\n" 89 " -c, --cluster RANGE Look for objects in this range of clusters\n" 90 " -s, --sector RANGE Look for objects in this range of sectors\n" 91 " -I, --inode NUM Show information about this inode\n" 92 " -F, --filename NAME Show information about this file\n" 93 /* " -l, --last Find the last file on the volume\n" */ 94 "\n" 95 " -f, --force Use less caution\n" 96 " -q, --quiet Less output\n" 97 " -v, --verbose More output\n" 98 " -V, --version Version information\n" 99 " -h, --help Print this help\n\n", 100 EXEC_NAME); 101 ntfs_log_info("%s%s\n", ntfs_bugs, ntfs_home); 102 } 103 104 /** 105 * parse_options - Read and validate the programs command line 106 * 107 * Read the command line, verify the syntax and parse the options. 108 * This function is very long, but quite simple. 109 * 110 * Return: 1 Success 111 * 0 Error, one or more problems 112 */ 113 static int parse_options(int argc, char **argv) 114 { 115 static const char *sopt = "-c:F:fh?I:ilqs:vV"; 116 static const struct option lopt[] = { 117 { "cluster", required_argument, NULL, 'c' }, 118 { "filename", required_argument, NULL, 'F' }, 119 { "force", no_argument, NULL, 'f' }, 120 { "help", no_argument, NULL, 'h' }, 121 { "info", no_argument, NULL, 'i' }, 122 { "inode", required_argument, NULL, 'I' }, 123 { "last", no_argument, NULL, 'l' }, 124 { "quiet", no_argument, NULL, 'q' }, 125 { "sector", required_argument, NULL, 's' }, 126 { "verbose", no_argument, NULL, 'v' }, 127 { "version", no_argument, NULL, 'V' }, 128 { NULL, 0, NULL, 0 } 129 }; 130 131 int c = -1; 132 int err = 0; 133 int ver = 0; 134 int help = 0; 135 int levels = 0; 136 char *end = NULL; 137 138 opterr = 0; /* We'll handle the errors, thank you. */ 139 140 opts.action = act_none; 141 opts.range_begin = -1; 142 opts.range_end = -1; 143 144 while ((c = getopt_long(argc, argv, sopt, lopt, NULL)) != -1) { 145 switch (c) { 146 case 1: /* A non-option argument */ 147 if (!opts.device) { 148 opts.device = argv[optind-1]; 149 } else { 150 opts.device = NULL; 151 err++; 152 } 153 break; 154 155 case 'c': 156 if ((opts.action == act_none) && 157 (utils_parse_range(optarg, &opts.range_begin, &opts.range_end, FALSE))) 158 opts.action = act_cluster; 159 else 160 opts.action = act_error; 161 break; 162 case 'F': 163 if (opts.action == act_none) { 164 opts.action = act_file; 165 opts.filename = optarg; 166 } else { 167 opts.action = act_error; 168 } 169 break; 170 case 'f': 171 opts.force++; 172 break; 173 case 'h': 174 case '?': 175 if (strncmp (argv[optind-1], "--log-", 6) == 0) { 176 if (!ntfs_log_parse_option (argv[optind-1])) 177 err++; 178 break; 179 } 180 help++; 181 break; 182 case 'I': 183 if (opts.action == act_none) { 184 opts.action = act_inode; 185 opts.inode = strtol(optarg, &end, 0); 186 if (end && *end) 187 err++; 188 } else { 189 opts.action = act_error; 190 } 191 break; 192 case 'i': 193 if (opts.action == act_none) 194 opts.action = act_info; 195 else 196 opts.action = act_error; 197 break; 198 case 'l': 199 if (opts.action == act_none) 200 opts.action = act_last; 201 else 202 opts.action = act_error; 203 break; 204 case 'q': 205 opts.quiet++; 206 ntfs_log_clear_levels(NTFS_LOG_LEVEL_QUIET); 207 break; 208 case 's': 209 if ((opts.action == act_none) && 210 (utils_parse_range(optarg, &opts.range_begin, &opts.range_end, FALSE))) 211 opts.action = act_sector; 212 else 213 opts.action = act_error; 214 break; 215 case 'v': 216 opts.verbose++; 217 ntfs_log_set_levels(NTFS_LOG_LEVEL_VERBOSE); 218 break; 219 case 'V': 220 ver++; 221 break; 222 default: 223 if ((optopt == 'c') || (optopt == 's')) 224 ntfs_log_error("Option '%s' requires an argument.\n", argv[optind-1]); 225 else 226 ntfs_log_error("Unknown option '%s'.\n", argv[optind-1]); 227 err++; 228 break; 229 } 230 } 231 232 /* Make sure we're in sync with the log levels */ 233 levels = ntfs_log_get_levels(); 234 if (levels & NTFS_LOG_LEVEL_VERBOSE) 235 opts.verbose++; 236 if (!(levels & NTFS_LOG_LEVEL_QUIET)) 237 opts.quiet++; 238 239 if (help || ver) { 240 opts.quiet = 0; 241 } else { 242 if (opts.action == act_none) 243 opts.action = act_info; 244 if (opts.action == act_info) 245 opts.quiet = 0; 246 247 if (opts.device == NULL) { 248 if (argc > 1) 249 ntfs_log_error("You must specify exactly one device.\n"); 250 err++; 251 } 252 253 if (opts.quiet && opts.verbose) { 254 ntfs_log_error("You may not use --quiet and --verbose at the same time.\n"); 255 err++; 256 } 257 258 if (opts.action == act_error) { 259 ntfs_log_error("You may only specify one action: --info, --cluster, --sector or --last.\n"); 260 err++; 261 } else if (opts.range_begin > opts.range_end) { 262 ntfs_log_error("The range must be in ascending order.\n"); 263 err++; 264 } 265 } 266 267 if (ver) 268 version(); 269 if (help || err) 270 usage(); 271 272 return (!err && !help && !ver); 273 } 274 275 276 /** 277 * info 278 */ 279 static int info(ntfs_volume *vol) 280 { 281 u64 a, b, c, d, e, f, g, h, i, j, k, l, m, n, o, p, q, r, s, t, u; 282 int cb, sb, cps; 283 u64 uc = 0, mc = 0, fc = 0; 284 285 struct mft_search_ctx *m_ctx; 286 ntfs_attr_search_ctx *a_ctx; 287 runlist_element *rl; 288 ATTR_RECORD *rec; 289 int z; 290 int inuse = 0; 291 292 m_ctx = mft_get_search_ctx(vol); 293 m_ctx->flags_search = FEMR_IN_USE | FEMR_METADATA | FEMR_BASE_RECORD | FEMR_NOT_BASE_RECORD; 294 while (mft_next_record(m_ctx) == 0) { 295 296 if (!(m_ctx->flags_match & FEMR_IN_USE)) 297 continue; 298 299 inuse++; 300 301 a_ctx = ntfs_attr_get_search_ctx(m_ctx->inode, NULL); 302 303 while ((rec = find_attribute(AT_UNUSED, a_ctx))) { 304 305 if (!rec->non_resident) 306 continue; 307 308 rl = ntfs_mapping_pairs_decompress(vol, rec, NULL); 309 310 for (z = 0; rl[z].length > 0; z++) 311 { 312 if (rl[z].lcn >= 0) { 313 if (m_ctx->flags_match & FEMR_METADATA) 314 mc += rl[z].length; 315 else 316 uc += rl[z].length; 317 } 318 319 } 320 321 free(rl); 322 } 323 324 ntfs_attr_put_search_ctx(a_ctx); 325 } 326 mft_put_search_ctx(m_ctx); 327 328 cb = vol->cluster_size_bits; 329 sb = vol->sector_size_bits; 330 cps = cb - sb; 331 332 fc = vol->nr_clusters-mc-uc; 333 fc <<= cb; 334 mc <<= cb; 335 uc <<= cb; 336 337 a = vol->sector_size; 338 b = vol->cluster_size; 339 c = 1 << cps; 340 d = vol->nr_clusters << cb; 341 e = vol->nr_clusters; 342 f = vol->nr_clusters >> cps; 343 g = vol->mft_na->initialized_size >> vol->mft_record_size_bits; 344 h = inuse; 345 i = h * 100 / g; 346 j = fc; 347 k = fc >> sb; 348 l = fc >> cb; 349 m = fc * 100 / b / e; 350 n = uc; 351 o = uc >> sb; 352 p = uc >> cb; 353 q = uc * 100 / b / e; 354 r = mc; 355 s = mc >> sb; 356 t = mc >> cb; 357 u = mc * 100 / b / e; 358 359 ntfs_log_info("bytes per sector : %llu\n", (unsigned long long)a); 360 ntfs_log_info("bytes per cluster : %llu\n", (unsigned long long)b); 361 ntfs_log_info("sectors per cluster : %llu\n", (unsigned long long)c); 362 ntfs_log_info("bytes per volume : %llu\n", (unsigned long long)d); 363 ntfs_log_info("sectors per volume : %llu\n", (unsigned long long)e); 364 ntfs_log_info("clusters per volume : %llu\n", (unsigned long long)f); 365 ntfs_log_info("initialized mft records : %llu\n", (unsigned long long)g); 366 ntfs_log_info("mft records in use : %llu\n", (unsigned long long)h); 367 ntfs_log_info("mft records percentage : %llu\n", (unsigned long long)i); 368 ntfs_log_info("bytes of free space : %llu\n", (unsigned long long)j); 369 ntfs_log_info("sectors of free space : %llu\n", (unsigned long long)k); 370 ntfs_log_info("clusters of free space : %llu\n", (unsigned long long)l); 371 ntfs_log_info("percentage free space : %llu\n", (unsigned long long)m); 372 ntfs_log_info("bytes of user data : %llu\n", (unsigned long long)n); 373 ntfs_log_info("sectors of user data : %llu\n", (unsigned long long)o); 374 ntfs_log_info("clusters of user data : %llu\n", (unsigned long long)p); 375 ntfs_log_info("percentage user data : %llu\n", (unsigned long long)q); 376 ntfs_log_info("bytes of metadata : %llu\n", (unsigned long long)r); 377 ntfs_log_info("sectors of metadata : %llu\n", (unsigned long long)s); 378 ntfs_log_info("clusters of metadata : %llu\n", (unsigned long long)t); 379 ntfs_log_info("percentage metadata : %llu\n", (unsigned long long)u); 380 381 return 0; 382 } 383 384 /** 385 * dump_file 386 */ 387 static int dump_file(ntfs_volume *vol, ntfs_inode *ino) 388 { 389 char buffer[1024]; 390 ntfs_attr_search_ctx *ctx; 391 ATTR_RECORD *rec; 392 int i; 393 runlist *runs; 394 395 utils_inode_get_name(ino, buffer, sizeof(buffer)); 396 397 ntfs_log_info("Dump: %s\n", buffer); 398 399 ctx = ntfs_attr_get_search_ctx(ino, NULL); 400 401 while ((rec = find_attribute(AT_UNUSED, ctx))) { 402 ntfs_log_info(" 0x%02x - ", rec->type); 403 if (rec->non_resident) { 404 ntfs_log_info("non-resident\n"); 405 runs = ntfs_mapping_pairs_decompress(vol, rec, NULL); 406 if (runs) { 407 ntfs_log_info(" VCN LCN Length\n"); 408 for (i = 0; runs[i].length > 0; i++) { 409 ntfs_log_info(" %8lld %8lld %8lld\n", 410 (long long)runs[i].vcn, 411 (long long)runs[i].lcn, 412 (long long) 413 runs[i].length); 414 } 415 free(runs); 416 } 417 } else { 418 ntfs_log_info("resident\n"); 419 } 420 } 421 422 ntfs_attr_put_search_ctx(ctx); 423 return 0; 424 } 425 426 /** 427 * print_match 428 */ 429 static int print_match(ntfs_inode *ino, ATTR_RECORD *attr, 430 runlist_element *run, void *data __attribute__((unused))) 431 { 432 char *buffer; 433 434 if (!ino || !attr || !run) 435 return 1; 436 437 buffer = malloc(MAX_PATH); 438 if (!buffer) { 439 ntfs_log_error("!buffer\n"); 440 return 1; 441 } 442 443 utils_inode_get_name(ino, buffer, MAX_PATH); 444 ntfs_log_info("Inode %llu %s", (unsigned long long)ino->mft_no, buffer); 445 446 utils_attr_get_name(ino->vol, attr, buffer, MAX_PATH); 447 ntfs_log_info("/%s\n", buffer); 448 449 free(buffer); 450 return 0; 451 } 452 453 /** 454 * find_last 455 */ 456 static int find_last(ntfs_inode *ino, ATTR_RECORD *attr, runlist_element *run, 457 void *data) 458 { 459 struct match *m; 460 461 if (!ino || !attr || !run || !data) 462 return 1; 463 464 m = data; 465 466 if ((run->lcn + run->length) > m->lcn) { 467 m->inum = ino->mft_no; 468 m->lcn = run->lcn + run->length; 469 } 470 471 return 0; 472 } 473 474 /** 475 * main - Begin here 476 * 477 * Start from here. 478 * 479 * Return: 0 Success, the program worked 480 * 1 Error, something went wrong 481 */ 482 int main(int argc, char *argv[]) 483 { 484 ntfs_volume *vol; 485 ntfs_inode *ino = NULL; 486 struct match m; 487 int result = 1; 488 489 ntfs_log_set_handler(ntfs_log_handler_outerr); 490 491 if (!parse_options(argc, argv)) 492 return 1; 493 494 utils_set_locale(); 495 496 vol = utils_mount_volume(opts.device, NTFS_MNT_RDONLY | 497 (opts.force ? NTFS_MNT_FORCE : 0)); 498 if (!vol) 499 return 1; 500 501 switch (opts.action) { 502 case act_sector: 503 if (opts.range_begin == opts.range_end) 504 ntfs_log_quiet("Searching for sector %llu\n", 505 (unsigned long long)opts.range_begin); 506 else 507 ntfs_log_quiet("Searching for sector range %llu-%llu\n", (unsigned long long)opts.range_begin, (unsigned long long)opts.range_end); 508 /* Convert to clusters */ 509 opts.range_begin >>= (vol->cluster_size_bits - vol->sector_size_bits); 510 opts.range_end >>= (vol->cluster_size_bits - vol->sector_size_bits); 511 result = cluster_find(vol, opts.range_begin, opts.range_end, (cluster_cb*)&print_match, NULL); 512 break; 513 case act_cluster: 514 if (opts.range_begin == opts.range_end) 515 ntfs_log_quiet("Searching for cluster %llu\n", 516 (unsigned long long)opts.range_begin); 517 else 518 ntfs_log_quiet("Searching for cluster range %llu-%llu\n", (unsigned long long)opts.range_begin, (unsigned long long)opts.range_end); 519 result = cluster_find(vol, opts.range_begin, opts.range_end, (cluster_cb*)&print_match, NULL); 520 break; 521 case act_file: 522 ino = ntfs_pathname_to_inode(vol, NULL, opts.filename); 523 if (ino) 524 result = dump_file(vol, ino); 525 break; 526 case act_inode: 527 ino = ntfs_inode_open(vol, opts.inode); 528 if (ino) { 529 result = dump_file(vol, ino); 530 ntfs_inode_close(ino); 531 } else { 532 ntfs_log_error("Cannot open inode %llu\n", 533 (unsigned long long)opts.inode); 534 } 535 break; 536 case act_last: 537 memset(&m, 0, sizeof(m)); 538 m.lcn = -1; 539 result = cluster_find(vol, 0, LONG_MAX, (cluster_cb*)&find_last, &m); 540 if (m.lcn >= 0) { 541 ino = ntfs_inode_open(vol, m.inum); 542 if (ino) { 543 result = dump_file(vol, ino); 544 ntfs_inode_close(ino); 545 } else { 546 ntfs_log_error("Cannot open inode %llu\n", 547 (unsigned long long) 548 opts.inode); 549 } 550 result = 0; 551 } else { 552 result = 1; 553 } 554 break; 555 case act_info: 556 default: 557 result = info(vol); 558 break; 559 } 560 561 ntfs_umount(vol, FALSE); 562 return result; 563 } 564 565