2 * ntfscluster - Part of the Linux-NTFS project.
4 * Copyright (c) 2002-2003 Richard Russon
5 * Copyright (c) 2005 Anton Altaparmakov
6 * Copyright (c) 2005-2006 Szabolcs Szakacsits
8 * This utility will locate the owner of any given sector or cluster.
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.
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.
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
44 #include "ntfscluster.h"
52 /* #include "version.h" */
55 static const char *EXEC_NAME
= "ntfscluster";
56 static struct options opts
;
59 * version - Print version information about the program
61 * Print a copyright statement and a brief description of the program.
65 static void version(void)
67 ntfs_log_info("\n%s v%s (libntfs-3g) - Find the owner of any given sector or "
68 "cluster.\n\n", EXEC_NAME
, VERSION
);
69 ntfs_log_info("Copyright (c) 2002-2003 Richard Russon\n");
70 ntfs_log_info("Copyright (c) 2005 Anton Altaparmakov\n");
71 ntfs_log_info("Copyright (c) 2005-2006 Szabolcs Szakacsits\n");
72 ntfs_log_info("\n%s\n%s%s\n", ntfs_gpl
, ntfs_bugs
, ntfs_home
);
76 * usage - Print a list of the parameters to the program
78 * Print a list of the parameters and options for the program.
82 static void usage(void)
84 ntfs_log_info("\nUsage: %s [options] device\n"
85 " -i, --info Print information about the volume (default)\n"
87 " -c, --cluster RANGE Look for objects in this range of clusters\n"
88 " -s, --sector RANGE Look for objects in this range of sectors\n"
89 " -I, --inode NUM Show information about this inode\n"
90 " -F, --filename NAME Show information about this file\n"
91 /* " -l, --last Find the last file on the volume\n" */
93 " -f, --force Use less caution\n"
94 " -q, --quiet Less output\n"
95 " -v, --verbose More output\n"
96 " -V, --version Version information\n"
97 " -h, --help Print this help\n\n",
99 ntfs_log_info("%s%s\n", ntfs_bugs
, ntfs_home
);
103 * parse_options - Read and validate the programs command line
105 * Read the command line, verify the syntax and parse the options.
106 * This function is very long, but quite simple.
109 * 0 Error, one or more problems
111 static int parse_options(int argc
, char **argv
)
113 static const char *sopt
= "-c:F:fh?I:ilqs:vV";
114 static const struct option lopt
[] = {
115 { "cluster", required_argument
, NULL
, 'c' },
116 { "filename", required_argument
, NULL
, 'F' },
117 { "force", no_argument
, NULL
, 'f' },
118 { "help", no_argument
, NULL
, 'h' },
119 { "info", no_argument
, NULL
, 'i' },
120 { "inode", required_argument
, NULL
, 'I' },
121 { "last", no_argument
, NULL
, 'l' },
122 { "quiet", no_argument
, NULL
, 'q' },
123 { "sector", required_argument
, NULL
, 's' },
124 { "verbose", no_argument
, NULL
, 'v' },
125 { "version", no_argument
, NULL
, 'V' },
136 opterr
= 0; /* We'll handle the errors, thank you. */
138 opts
.action
= act_none
;
139 opts
.range_begin
= -1;
142 while ((c
= getopt_long(argc
, argv
, sopt
, lopt
, NULL
)) != -1) {
144 case 1: /* A non-option argument */
146 opts
.device
= argv
[optind
-1];
154 if ((opts
.action
== act_none
) &&
155 (utils_parse_range(optarg
, &opts
.range_begin
, &opts
.range_end
, FALSE
)))
156 opts
.action
= act_cluster
;
158 opts
.action
= act_error
;
161 if (opts
.action
== act_none
) {
162 opts
.action
= act_file
;
163 opts
.filename
= optarg
;
165 opts
.action
= act_error
;
173 if (strncmp (argv
[optind
-1], "--log-", 6) == 0) {
174 if (!ntfs_log_parse_option (argv
[optind
-1]))
181 if (opts
.action
== act_none
) {
182 opts
.action
= act_inode
;
183 opts
.inode
= strtol(optarg
, &end
, 0);
187 opts
.action
= act_error
;
191 if (opts
.action
== act_none
)
192 opts
.action
= act_info
;
194 opts
.action
= act_error
;
197 if (opts
.action
== act_none
)
198 opts
.action
= act_last
;
200 opts
.action
= act_error
;
204 ntfs_log_clear_levels(NTFS_LOG_LEVEL_QUIET
);
207 if ((opts
.action
== act_none
) &&
208 (utils_parse_range(optarg
, &opts
.range_begin
, &opts
.range_end
, FALSE
)))
209 opts
.action
= act_sector
;
211 opts
.action
= act_error
;
215 ntfs_log_set_levels(NTFS_LOG_LEVEL_VERBOSE
);
221 if ((optopt
== 'c') || (optopt
== 's'))
222 ntfs_log_error("Option '%s' requires an argument.\n", argv
[optind
-1]);
224 ntfs_log_error("Unknown option '%s'.\n", argv
[optind
-1]);
230 /* Make sure we're in sync with the log levels */
231 levels
= ntfs_log_get_levels();
232 if (levels
& NTFS_LOG_LEVEL_VERBOSE
)
234 if (!(levels
& NTFS_LOG_LEVEL_QUIET
))
240 if (opts
.action
== act_none
)
241 opts
.action
= act_info
;
242 if (opts
.action
== act_info
)
245 if (opts
.device
== NULL
) {
247 ntfs_log_error("You must specify exactly one device.\n");
251 if (opts
.quiet
&& opts
.verbose
) {
252 ntfs_log_error("You may not use --quiet and --verbose at the same time.\n");
256 if (opts
.action
== act_error
) {
257 ntfs_log_error("You may only specify one action: --info, --cluster, --sector or --last.\n");
259 } else if (opts
.range_begin
> opts
.range_end
) {
260 ntfs_log_error("The range must be in ascending order.\n");
270 return (!err
&& !help
&& !ver
);
277 static int info(ntfs_volume
*vol
)
279 u64 a
, b
, c
, d
, e
, f
, g
, h
, i
, j
, k
, l
, m
, n
, o
, p
, q
, r
, s
, t
, u
;
281 u64 uc
= 0, mc
= 0, fc
= 0;
283 struct mft_search_ctx
*m_ctx
;
284 ntfs_attr_search_ctx
*a_ctx
;
290 m_ctx
= mft_get_search_ctx(vol
);
291 m_ctx
->flags_search
= FEMR_IN_USE
| FEMR_METADATA
| FEMR_BASE_RECORD
| FEMR_NOT_BASE_RECORD
;
292 while (mft_next_record(m_ctx
) == 0) {
294 if (!(m_ctx
->flags_match
& FEMR_IN_USE
))
299 a_ctx
= ntfs_attr_get_search_ctx(m_ctx
->inode
, NULL
);
301 while ((rec
= find_attribute(AT_UNUSED
, a_ctx
))) {
303 if (!rec
->non_resident
)
306 rl
= ntfs_mapping_pairs_decompress(vol
, rec
, NULL
);
308 for (z
= 0; rl
[z
].length
> 0; z
++)
310 if (rl
[z
].lcn
>= 0) {
311 if (m_ctx
->flags_match
& FEMR_METADATA
)
322 ntfs_attr_put_search_ctx(a_ctx
);
324 mft_put_search_ctx(m_ctx
);
326 cb
= vol
->cluster_size_bits
;
327 sb
= vol
->sector_size_bits
;
330 fc
= vol
->nr_clusters
-mc
-uc
;
335 a
= vol
->sector_size
;
336 b
= vol
->cluster_size
;
338 d
= vol
->nr_clusters
<< cb
;
339 e
= vol
->nr_clusters
;
340 f
= vol
->nr_clusters
>> cps
;
341 g
= vol
->mft_na
->initialized_size
>> vol
->mft_record_size_bits
;
347 m
= fc
* 100 / b
/ e
;
351 q
= uc
* 100 / b
/ e
;
355 u
= mc
* 100 / b
/ e
;
357 ntfs_log_info("bytes per sector : %llu\n", (unsigned long long)a
);
358 ntfs_log_info("bytes per cluster : %llu\n", (unsigned long long)b
);
359 ntfs_log_info("sectors per cluster : %llu\n", (unsigned long long)c
);
360 ntfs_log_info("bytes per volume : %llu\n", (unsigned long long)d
);
361 ntfs_log_info("sectors per volume : %llu\n", (unsigned long long)e
);
362 ntfs_log_info("clusters per volume : %llu\n", (unsigned long long)f
);
363 ntfs_log_info("initialized mft records : %llu\n", (unsigned long long)g
);
364 ntfs_log_info("mft records in use : %llu\n", (unsigned long long)h
);
365 ntfs_log_info("mft records percentage : %llu\n", (unsigned long long)i
);
366 ntfs_log_info("bytes of free space : %llu\n", (unsigned long long)j
);
367 ntfs_log_info("sectors of free space : %llu\n", (unsigned long long)k
);
368 ntfs_log_info("clusters of free space : %llu\n", (unsigned long long)l
);
369 ntfs_log_info("percentage free space : %llu\n", (unsigned long long)m
);
370 ntfs_log_info("bytes of user data : %llu\n", (unsigned long long)n
);
371 ntfs_log_info("sectors of user data : %llu\n", (unsigned long long)o
);
372 ntfs_log_info("clusters of user data : %llu\n", (unsigned long long)p
);
373 ntfs_log_info("percentage user data : %llu\n", (unsigned long long)q
);
374 ntfs_log_info("bytes of metadata : %llu\n", (unsigned long long)r
);
375 ntfs_log_info("sectors of metadata : %llu\n", (unsigned long long)s
);
376 ntfs_log_info("clusters of metadata : %llu\n", (unsigned long long)t
);
377 ntfs_log_info("percentage metadata : %llu\n", (unsigned long long)u
);
385 static int dump_file(ntfs_volume
*vol
, ntfs_inode
*ino
)
388 ntfs_attr_search_ctx
*ctx
;
393 utils_inode_get_name(ino
, buffer
, sizeof(buffer
));
395 ntfs_log_info("Dump: %s\n", buffer
);
397 ctx
= ntfs_attr_get_search_ctx(ino
, NULL
);
399 while ((rec
= find_attribute(AT_UNUSED
, ctx
))) {
400 ntfs_log_info(" 0x%02x - ", rec
->type
);
401 if (rec
->non_resident
) {
402 ntfs_log_info("non-resident\n");
403 runs
= ntfs_mapping_pairs_decompress(vol
, rec
, NULL
);
405 ntfs_log_info(" VCN LCN Length\n");
406 for (i
= 0; runs
[i
].length
> 0; i
++) {
407 ntfs_log_info(" %8lld %8lld %8lld\n",
408 (long long)runs
[i
].vcn
,
409 (long long)runs
[i
].lcn
,
416 ntfs_log_info("resident\n");
420 ntfs_attr_put_search_ctx(ctx
);
427 static int print_match(ntfs_inode
*ino
, ATTR_RECORD
*attr
,
428 runlist_element
*run
, void *data
__attribute__((unused
)))
432 if (!ino
|| !attr
|| !run
)
435 buffer
= malloc(MAX_PATH
);
437 ntfs_log_error("!buffer\n");
441 utils_inode_get_name(ino
, buffer
, MAX_PATH
);
442 ntfs_log_info("Inode %llu %s", (unsigned long long)ino
->mft_no
, buffer
);
444 utils_attr_get_name(ino
->vol
, attr
, buffer
, MAX_PATH
);
445 ntfs_log_info("/%s\n", buffer
);
454 static int find_last(ntfs_inode
*ino
, ATTR_RECORD
*attr
, runlist_element
*run
,
459 if (!ino
|| !attr
|| !run
|| !data
)
464 if ((run
->lcn
+ run
->length
) > m
->lcn
) {
465 m
->inum
= ino
->mft_no
;
466 m
->lcn
= run
->lcn
+ run
->length
;
477 * Return: 0 Success, the program worked
478 * 1 Error, something went wrong
480 int main(int argc
, char *argv
[])
483 ntfs_inode
*ino
= NULL
;
487 ntfs_log_set_handler(ntfs_log_handler_outerr
);
489 if (!parse_options(argc
, argv
))
494 vol
= utils_mount_volume(opts
.device
, NTFS_MNT_RDONLY
|
495 (opts
.force
? NTFS_MNT_RECOVER
: 0));
499 switch (opts
.action
) {
501 if (opts
.range_begin
== opts
.range_end
)
502 ntfs_log_quiet("Searching for sector %llu\n",
503 (unsigned long long)opts
.range_begin
);
505 ntfs_log_quiet("Searching for sector range %llu-%llu\n", (unsigned long long)opts
.range_begin
, (unsigned long long)opts
.range_end
);
506 /* Convert to clusters */
507 opts
.range_begin
>>= (vol
->cluster_size_bits
- vol
->sector_size_bits
);
508 opts
.range_end
>>= (vol
->cluster_size_bits
- vol
->sector_size_bits
);
509 result
= cluster_find(vol
, opts
.range_begin
, opts
.range_end
, (cluster_cb
*)&print_match
, NULL
);
512 if (opts
.range_begin
== opts
.range_end
)
513 ntfs_log_quiet("Searching for cluster %llu\n",
514 (unsigned long long)opts
.range_begin
);
516 ntfs_log_quiet("Searching for cluster range %llu-%llu\n", (unsigned long long)opts
.range_begin
, (unsigned long long)opts
.range_end
);
517 result
= cluster_find(vol
, opts
.range_begin
, opts
.range_end
, (cluster_cb
*)&print_match
, NULL
);
520 ino
= ntfs_pathname_to_inode(vol
, NULL
, opts
.filename
);
522 result
= dump_file(vol
, ino
);
525 ino
= ntfs_inode_open(vol
, opts
.inode
);
527 result
= dump_file(vol
, ino
);
528 ntfs_inode_close(ino
);
530 ntfs_log_error("Cannot open inode %llu\n",
531 (unsigned long long)opts
.inode
);
535 memset(&m
, 0, sizeof(m
));
537 result
= cluster_find(vol
, 0, LONG_MAX
, (cluster_cb
*)&find_last
, &m
);
539 ino
= ntfs_inode_open(vol
, m
.inum
);
541 result
= dump_file(vol
, ino
);
542 ntfs_inode_close(ino
);
544 ntfs_log_error("Cannot open inode %llu\n",
559 ntfs_umount(vol
, FALSE
);