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