1 /**
   2  * ntfscat - Part of the Linux-NTFS project.
   3  *
   4  * Copyright (c) 2003-2005 Richard Russon
   5  * Copyright (c) 2003-2005 Anton Altaparmakov
   6  * Copyright (c) 2003-2005 Szabolcs Szakacsits
   7  * Copyright (c) 2007      Yura Pakhuchiy
   8  *
   9  * This utility will concatenate files and print on the standard output.
  10  *
  11  * This program is free software; you can redistribute it and/or modify
  12  * it under the terms of the GNU General Public License as published by
  13  * the Free Software Foundation; either version 2 of the License, or
  14  * (at your option) any later version.
  15  *
  16  * This program is distributed in the hope that it will be useful,
  17  * but WITHOUT ANY WARRANTY; without even the implied warranty of
  18  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  19  * GNU General Public License for more details.
  20  *
  21  * You should have received a copy of the GNU General Public License
  22  * along with this program (in the main directory of the Linux-NTFS
  23  * distribution in the file COPYING); if not, write to the Free Software
  24  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
  25  */
  26 
  27 #include "config.h"
  28 
  29 #ifdef HAVE_STDIO_H
  30 #include <stdio.h>
  31 #endif
  32 #ifdef HAVE_GETOPT_H
  33 #include <getopt.h>
  34 #endif
  35 #ifdef HAVE_STDLIB_H
  36 #include <stdlib.h>
  37 #endif
  38 #ifdef HAVE_STRING_H
  39 #include <string.h>
  40 #endif
  41 
  42 #include "compat.h"
  43 #include "types.h"
  44 #include "attrib.h"
  45 #include "utils.h"
  46 #include "volume.h"
  47 #include "debug.h"
  48 #include "dir.h"
  49 #include "ntfscat.h"
  50 #include "version.h"
  51 
  52 static const char *EXEC_NAME = "ntfscat";
  53 static struct options opts;
  54 
  55 /**
  56  * version - Print version information about the program
  57  *
  58  * Print a copyright statement and a brief description of the program.
  59  *
  60  * Return:  none
  61  */
  62 static void version(void)
  63 {
  64         ntfs_log_info("\n%s v%s (libntfs %s) - Concatenate files and print "
  65                         "on the standard output.\n\n", EXEC_NAME, VERSION,
  66                         ntfs_libntfs_version());
  67         ntfs_log_info("Copyright (c) 2003-2005 Richard Russon\n");
  68         ntfs_log_info("Copyright (c) 2003-2005 Anton Altaparmakov\n");
  69         ntfs_log_info("Copyright (c) 2003-2005 Szabolcs Szakacsits\n");
  70         ntfs_log_info("Copyright (c) 2007      Yura Pakhuchiy\n");
  71         ntfs_log_info("\n%s\n%s%s\n", ntfs_gpl, ntfs_bugs, ntfs_home);
  72 }
  73 
  74 /**
  75  * usage - Print a list of the parameters to the program
  76  *
  77  * Print a list of the parameters and options for the program.
  78  *
  79  * Return:  none
  80  */
  81 static void usage(void)
  82 {
  83         ntfs_log_info("\nUsage: %s [options] device [file]\n\n"
  84                 "    -a, --attribute TYPE       Display this attribute type\n"
  85                 "    -n, --attribute-name NAME  Display this attribute name\n"
  86                 "    -i, --inode NUM            Display this inode\n\n"
  87                 "    -f, --force                Use less caution\n"
  88                 "    -h, --help                 Print this help\n"
  89                 "    -q, --quiet                Less output\n"
  90                 "    -V, --version              Version information\n"
  91                 "    -v, --verbose              More output\n\n",
  92 // Does not work for compressed files at present so leave undocumented...
  93 //              "    -r  --raw                  Display the raw data (e.g. for compressed or encrypted file)",
  94                 EXEC_NAME);
  95         ntfs_log_info("%s%s\n", ntfs_bugs, ntfs_home);
  96 }
  97 
  98 /**
  99  * parse_attribute - Read an attribute name, or number
 100  * @value:   String to be parsed
 101  * @attr:    Resulting attribute id (on success)
 102  *
 103  * Read a string representing an attribute.  It may be a decimal, octal or
 104  * hexadecimal number, or the attribute name in full.  The leading $ sign is
 105  * optional.
 106  *
 107  * Return:  1  Success, a valid attribute name or number
 108  *          0  Error, not an attribute name or number
 109  */
 110 static int parse_attribute(const char *value, ATTR_TYPES *attr)
 111 {
 112         static const char *attr_name[] = {
 113                 "$STANDARD_INFORMATION",
 114                 "$ATTRIBUTE_LIST",
 115                 "$FILE_NAME",
 116                 "$OBJECT_ID",
 117                 "$SECURITY_DESCRIPTOR",
 118                 "$VOLUME_NAME",
 119                 "$VOLUME_INFORMATION",
 120                 "$DATA",
 121                 "$INDEX_ROOT",
 122                 "$INDEX_ALLOCATION",
 123                 "$BITMAP",
 124                 "$REPARSE_POINT",
 125                 "$EA_INFORMATION",
 126                 "$EA",
 127                 "$PROPERTY_SET",
 128                 "$LOGGED_UTILITY_STREAM",
 129                 NULL
 130         };
 131 
 132         int i;
 133         long num;
 134 
 135         for (i = 0; attr_name[i]; i++) {
 136                 if ((strcmp(value, attr_name[i]) == 0) ||
 137                     (strcmp(value, attr_name[i] + 1) == 0)) {
 138                         *attr = (ATTR_TYPES)cpu_to_le32((i + 1) * 16);
 139                         return 1;
 140                 }
 141         }
 142 
 143         num = strtol(value, NULL, 0);
 144         if ((num > 0) && (num < 257)) {
 145                 *attr = (ATTR_TYPES)cpu_to_le32(num);
 146                 return 1;
 147         }
 148 
 149         return 0;
 150 }
 151 
 152 /**
 153  * parse_options - Read and validate the programs command line
 154  *
 155  * Read the command line, verify the syntax and parse the options.
 156  * This function is very long, but quite simple.
 157  *
 158  * Return:  1 Success
 159  *          0 Error, one or more problems
 160  */
 161 static int parse_options(int argc, char **argv)
 162 {
 163         static const char *sopt = "-a:fh?i:n:qVvr";
 164         static const struct option lopt[] = {
 165                 { "attribute",      required_argument,  NULL, 'a' },
 166                 { "attribute-name", required_argument,  NULL, 'n' },
 167                 { "force",          no_argument,        NULL, 'f' },
 168                 { "help",           no_argument,        NULL, 'h' },
 169                 { "inode",          required_argument,  NULL, 'i' },
 170                 { "quiet",          no_argument,        NULL, 'q' },
 171                 { "version",        no_argument,        NULL, 'V' },
 172                 { "verbose",        no_argument,        NULL, 'v' },
 173                 { "raw",            no_argument,        NULL, 'r' },
 174                 { NULL,             0,                  NULL, 0   }
 175         };
 176 
 177         int c = -1;
 178         int err  = 0;
 179         int ver  = 0;
 180         int help = 0;
 181         int levels = 0;
 182         ATTR_TYPES attr = AT_UNUSED;
 183 
 184         opterr = 0; /* We'll handle the errors, thank you. */
 185 
 186         opts.inode = -1;
 187         opts.attr = cpu_to_le32(-1);
 188         opts.attr_name = NULL;
 189         opts.attr_name_len = 0;
 190 
 191         while ((c = getopt_long(argc, argv, sopt, lopt, NULL)) != -1) {
 192                 switch (c) {
 193                 case 1: /* A non-option argument */
 194                         if (!opts.device) {
 195                                 opts.device = argv[optind - 1];
 196                         } else if (!opts.file) {
 197                                 opts.file = argv[optind - 1];
 198                         } else {
 199                                 ntfs_log_error("You must specify exactly one "
 200                                                 "file.\n");
 201                                 err++;
 202                         }
 203                         break;
 204                 case 'a':
 205                         if (opts.attr != cpu_to_le32(-1)) {
 206                                 ntfs_log_error("You must specify exactly one "
 207                                                 "attribute.\n");
 208                         } else if (parse_attribute(optarg, &attr) > 0) {
 209                                 opts.attr = attr;
 210                                 break;
 211                         } else {
 212                                 ntfs_log_error("Couldn't parse attribute.\n");
 213                         }
 214                         err++;
 215                         break;
 216                 case 'f':
 217                         opts.force++;
 218                         break;
 219                 case 'h':
 220                 case '?':
 221                         if (strncmp (argv[optind-1], "--log-", 6) == 0) {
 222                                 if (!ntfs_log_parse_option (argv[optind-1]))
 223                                         err++;
 224                                 break;
 225                         }
 226                         help++;
 227                         break;
 228                 case 'i':
 229                         if (opts.inode != -1)
 230                                 ntfs_log_error("You must specify exactly one inode.\n");
 231                         else if (utils_parse_size(optarg, &opts.inode, FALSE))
 232                                 break;
 233                         else
 234                                 ntfs_log_error("Couldn't parse inode number.\n");
 235                         err++;
 236                         break;
 237 
 238                 case 'n':
 239                         opts.attr_name_len = ntfs_mbstoucs(optarg,
 240                                                            &opts.attr_name, 0);
 241                         if (opts.attr_name_len < 0) {
 242                                 ntfs_log_perror("Invalid attribute name '%s'",
 243                                                 optarg);
 244                                 usage();
 245                         }
 246 
 247                 case 'q':
 248                         opts.quiet++;
 249                         ntfs_log_clear_levels(NTFS_LOG_LEVEL_QUIET);
 250                         break;
 251                 case 'V':
 252                         ver++;
 253                         break;
 254                 case 'v':
 255                         opts.verbose++;
 256                         ntfs_log_set_levels(NTFS_LOG_LEVEL_VERBOSE);
 257                         break;
 258                 case 'r':
 259                         opts.raw = TRUE;
 260                         break;
 261                 default:
 262                         ntfs_log_error("Unknown option '%s'.\n", argv[optind-1]);
 263                         err++;
 264                         break;
 265                 }
 266         }
 267 
 268         /* Make sure we're in sync with the log levels */
 269         levels = ntfs_log_get_levels();
 270         if (levels & NTFS_LOG_LEVEL_VERBOSE)
 271                 opts.verbose++;
 272         if (!(levels & NTFS_LOG_LEVEL_QUIET))
 273                 opts.quiet++;
 274 
 275         if (help || ver) {
 276                 opts.quiet = 0;
 277         } else {
 278                 if (opts.device == NULL) {
 279                         ntfs_log_error("You must specify a device.\n");
 280                         err++;
 281 
 282                 } else if (opts.file == NULL && opts.inode == -1) {
 283                         ntfs_log_error("You must specify a file or inode "
 284                                  "with the -i option.\n");
 285                         err++;
 286 
 287                 } else if (opts.file != NULL && opts.inode != -1) {
 288                         ntfs_log_error("You can't specify both a file and inode.\n");
 289                         err++;
 290                 }
 291 
 292                 if (opts.quiet && opts.verbose) {
 293                         ntfs_log_error("You may not use --quiet and --verbose at the "
 294                                         "same time.\n");
 295                         err++;
 296                 }
 297         }
 298 
 299         if (ver)
 300                 version();
 301         if (help || err)
 302                 usage();
 303 
 304         return (!err && !help && !ver);
 305 }
 306 
 307 /**
 308  * index_get_size - Find the INDX block size from the index root
 309  * @inode:  Inode of the directory to be checked
 310  *
 311  * Find the size of a directory's INDX block from the INDEX_ROOT attribute.
 312  *
 313  * Return:  n  Success, the INDX blocks are n bytes in size
 314  *          0  Error, not a directory
 315  */
 316 static int index_get_size(ntfs_inode *inode)
 317 {
 318         ATTR_RECORD *attr90;
 319         INDEX_ROOT *iroot;
 320 
 321         attr90 = find_first_attribute(AT_INDEX_ROOT, inode->mrec);
 322         if (!attr90)
 323                 return 0;       // not a directory
 324 
 325         iroot = (INDEX_ROOT*)((u8*)attr90 + le16_to_cpu(attr90->u.res.value_offset));
 326         return le32_to_cpu(iroot->index_block_size);
 327 }
 328 
 329 /**
 330  * cat
 331  */
 332 static int cat(ntfs_volume *vol, ntfs_inode *inode, ATTR_TYPES type,
 333                 ntfschar *name, int namelen)
 334 {
 335         const int bufsize = 4096;
 336         char *buffer;
 337         ntfs_attr *attr;
 338         s64 bytes_read, written;
 339         s64 offset;
 340         u32 block_size;
 341 
 342         buffer = malloc(bufsize);
 343         if (!buffer)
 344                 return 1;
 345 
 346         attr = ntfs_attr_open(inode, type, name, namelen);
 347         if (!attr) {
 348                 ntfs_log_error("Cannot find attribute type 0x%x.\n",
 349                                 le32_to_cpu(type));
 350                 free(buffer);
 351                 return 1;
 352         }
 353 
 354         if ((inode->mft_no < 2) && (attr->type == AT_DATA))
 355                 block_size = vol->mft_record_size;
 356         else if (attr->type == AT_INDEX_ALLOCATION)
 357                 block_size = index_get_size(inode);
 358         else
 359                 block_size = 0;
 360 
 361         offset = 0;
 362         for (;;) {
 363                 if (!opts.raw && block_size > 0) {
 364                         // These types have fixup
 365                         bytes_read = ntfs_attr_mst_pread(attr, offset, 1, block_size, buffer);
 366                         if (bytes_read > 0)
 367                                 bytes_read *= block_size;
 368                 } else {
 369                         bytes_read = ntfs_attr_pread(attr, offset, bufsize, buffer);
 370                 }
 371                 //ntfs_log_info("read %lld bytes\n", bytes_read);
 372                 if (bytes_read == -1) {
 373                         ntfs_log_perror("ERROR: Couldn't read file");
 374                         break;
 375                 }
 376                 if (!bytes_read)
 377                         break;
 378 
 379                 written = fwrite(buffer, 1, bytes_read, stdout);
 380                 if (written != bytes_read) {
 381                         ntfs_log_perror("ERROR: Couldn't output all data!");
 382                         break;
 383                 }
 384                 offset += bytes_read;
 385         }
 386 
 387         ntfs_attr_close(attr);
 388         free(buffer);
 389         return 0;
 390 }
 391 
 392 /**
 393  * main - Begin here
 394  *
 395  * Start from here.
 396  *
 397  * Return:  0  Success, the program worked
 398  *          1  Error, something went wrong
 399  */
 400 int main(int argc, char *argv[])
 401 {
 402         ntfs_volume *vol;
 403         ntfs_inode *inode;
 404         ATTR_TYPES attr;
 405         int result = 1;
 406 
 407         ntfs_log_set_handler(ntfs_log_handler_stderr);
 408 
 409         if (!parse_options(argc, argv))
 410                 return 1;
 411 
 412         utils_set_locale();
 413 
 414         vol = utils_mount_volume(opts.device, NTFS_MNT_RDONLY |
 415                         (opts.force ? NTFS_MNT_FORCE : 0));
 416         if (!vol) {
 417                 ntfs_log_perror("ERROR: couldn't mount volume");
 418                 return 1;
 419         }
 420 
 421         if (opts.inode != -1)
 422                 inode = ntfs_inode_open(vol, opts.inode);
 423         else
 424                 inode = ntfs_pathname_to_inode(vol, NULL, opts.file);
 425 
 426         if (!inode) {
 427                 ntfs_log_perror("ERROR: Couldn't open inode");
 428                 return 1;
 429         }
 430 
 431         attr = AT_DATA;
 432         if (opts.attr != cpu_to_le32(-1))
 433                 attr = opts.attr;
 434 
 435         result = cat(vol, inode, attr, opts.attr_name, opts.attr_name_len);
 436 
 437         ntfs_inode_close(inode);
 438         ntfs_umount(vol, FALSE);
 439 
 440         return result;
 441 }