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