2 * ntfscat - Part of the Linux-NTFS project.
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
9 * This utility will concatenate files and print on the standard output.
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.
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.
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
52 static const char *EXEC_NAME
= "ntfscat";
53 static struct options opts
;
56 * version - Print version information about the program
58 * Print a copyright statement and a brief description of the program.
62 static void version(void)
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
);
75 * usage - Print a list of the parameters to the program
77 * Print a list of the parameters and options for the program.
81 static void usage(void)
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)",
95 ntfs_log_info("%s%s\n", ntfs_bugs
, ntfs_home
);
99 * parse_attribute - Read an attribute name, or number
100 * @value: String to be parsed
101 * @attr: Resulting attribute id (on success)
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
107 * Return: 1 Success, a valid attribute name or number
108 * 0 Error, not an attribute name or number
110 static int parse_attribute(const char *value
, ATTR_TYPES
*attr
)
112 static const char *attr_name
[] = {
113 "$STANDARD_INFORMATION",
117 "$SECURITY_DESCRIPTOR",
119 "$VOLUME_INFORMATION",
128 "$LOGGED_UTILITY_STREAM",
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);
143 num
= strtol(value
, NULL
, 0);
144 if ((num
> 0) && (num
< 257)) {
145 *attr
= (ATTR_TYPES
)cpu_to_le32(num
);
153 * parse_options - Read and validate the programs command line
155 * Read the command line, verify the syntax and parse the options.
156 * This function is very long, but quite simple.
159 * 0 Error, one or more problems
161 static int parse_options(int argc
, char **argv
)
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' },
182 ATTR_TYPES attr
= AT_UNUSED
;
184 opterr
= 0; /* We'll handle the errors, thank you. */
187 opts
.attr
= cpu_to_le32(-1);
188 opts
.attr_name
= NULL
;
189 opts
.attr_name_len
= 0;
191 while ((c
= getopt_long(argc
, argv
, sopt
, lopt
, NULL
)) != -1) {
193 case 1: /* A non-option argument */
195 opts
.device
= argv
[optind
- 1];
196 } else if (!opts
.file
) {
197 opts
.file
= argv
[optind
- 1];
199 ntfs_log_error("You must specify exactly one "
205 if (opts
.attr
!= cpu_to_le32(-1)) {
206 ntfs_log_error("You must specify exactly one "
208 } else if (parse_attribute(optarg
, &attr
) > 0) {
212 ntfs_log_error("Couldn't parse attribute.\n");
221 if (strncmp (argv
[optind
-1], "--log-", 6) == 0) {
222 if (!ntfs_log_parse_option (argv
[optind
-1]))
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
))
234 ntfs_log_error("Couldn't parse inode number.\n");
239 opts
.attr_name_len
= ntfs_mbstoucs(optarg
,
241 if (opts
.attr_name_len
< 0) {
242 ntfs_log_perror("Invalid attribute name '%s'",
249 ntfs_log_clear_levels(NTFS_LOG_LEVEL_QUIET
);
256 ntfs_log_set_levels(NTFS_LOG_LEVEL_VERBOSE
);
262 ntfs_log_error("Unknown option '%s'.\n", argv
[optind
-1]);
268 /* Make sure we're in sync with the log levels */
269 levels
= ntfs_log_get_levels();
270 if (levels
& NTFS_LOG_LEVEL_VERBOSE
)
272 if (!(levels
& NTFS_LOG_LEVEL_QUIET
))
278 if (opts
.device
== NULL
) {
279 ntfs_log_error("You must specify a device.\n");
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");
287 } else if (opts
.file
!= NULL
&& opts
.inode
!= -1) {
288 ntfs_log_error("You can't specify both a file and inode.\n");
292 if (opts
.quiet
&& opts
.verbose
) {
293 ntfs_log_error("You may not use --quiet and --verbose at the "
304 return (!err
&& !help
&& !ver
);
308 * index_get_size - Find the INDX block size from the index root
309 * @inode: Inode of the directory to be checked
311 * Find the size of a directory's INDX block from the INDEX_ROOT attribute.
313 * Return: n Success, the INDX blocks are n bytes in size
314 * 0 Error, not a directory
316 static int index_get_size(ntfs_inode
*inode
)
321 attr90
= find_first_attribute(AT_INDEX_ROOT
, inode
->mrec
);
323 return 0; // not a directory
325 iroot
= (INDEX_ROOT
*)((u8
*)attr90
+ le16_to_cpu(attr90
->u
.res
.value_offset
));
326 return le32_to_cpu(iroot
->index_block_size
);
332 static int cat(ntfs_volume
*vol
, ntfs_inode
*inode
, ATTR_TYPES type
,
333 ntfschar
*name
, int namelen
)
335 const int bufsize
= 4096;
338 s64 bytes_read
, written
;
342 buffer
= malloc(bufsize
);
346 attr
= ntfs_attr_open(inode
, type
, name
, namelen
);
348 ntfs_log_error("Cannot find attribute type 0x%x.\n",
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
);
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
);
367 bytes_read
*= block_size
;
369 bytes_read
= ntfs_attr_pread(attr
, offset
, bufsize
, buffer
);
371 //ntfs_log_info("read %lld bytes\n", bytes_read);
372 if (bytes_read
== -1) {
373 ntfs_log_perror("ERROR: Couldn't read file");
379 written
= fwrite(buffer
, 1, bytes_read
, stdout
);
380 if (written
!= bytes_read
) {
381 ntfs_log_perror("ERROR: Couldn't output all data!");
384 offset
+= bytes_read
;
387 ntfs_attr_close(attr
);
397 * Return: 0 Success, the program worked
398 * 1 Error, something went wrong
400 int main(int argc
, char *argv
[])
407 ntfs_log_set_handler(ntfs_log_handler_stderr
);
409 if (!parse_options(argc
, argv
))
414 vol
= utils_mount_volume(opts
.device
, NTFS_MNT_RDONLY
|
415 (opts
.force
? NTFS_MNT_FORCE
: 0));
417 ntfs_log_perror("ERROR: couldn't mount volume");
421 if (opts
.inode
!= -1)
422 inode
= ntfs_inode_open(vol
, opts
.inode
);
424 inode
= ntfs_pathname_to_inode(vol
, NULL
, opts
.file
);
427 ntfs_log_perror("ERROR: Couldn't open inode");
432 if (opts
.attr
!= cpu_to_le32(-1))
435 result
= cat(vol
, inode
, attr
, opts
.attr_name
, opts
.attr_name_len
);
437 ntfs_inode_close(inode
);
438 ntfs_umount(vol
, FALSE
);