http://linux-ntfs.sourceforge.net/snapshots/ntfsprogs-200307311516.tar.bz2
[ntfsprogs.git] / ntfsprogs / ntfscluster.c
1 /**
2  * ntfscluster - Part of the Linux-NTFS project.
3  *
4  * Copyright (c) 2002-2003 Richard Russon
5  *
6  * This utility will locate the owner of any given sector or cluster.
7  *
8  * This program is free software; you can redistribute it and/or modify
9  * it under the terms of the GNU General Public License as published by
10  * the Free Software Foundation; either version 2 of the License, or
11  * (at your option) any later version.
12  *
13  * This program is distributed in the hope that it will be useful,
14  * but WITHOUT ANY WARRANTY; without even the implied warranty of
15  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16  * GNU General Public License for more details.
17  *
18  * You should have received a copy of the GNU General Public License
19  * along with this program (in the main directory of the Linux-NTFS
20  * distribution in the file COPYING); if not, write to the Free Software
21  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
22  */
23
24 #include "config.h"
25
26 #include <stdio.h>
27 #include <getopt.h>
28 #include <stdlib.h>
29
30 #include "ntfscluster.h"
31 #include "types.h"
32 #include "attrib.h"
33 #include "utils.h"
34 #include "volume.h"
35 #include "debug.h"
36
37 static const char *EXEC_NAME = "ntfscluster";
38 static struct options opts;
39
40 GEN_PRINTF (Eprintf, stderr, NULL,          FALSE)
41 GEN_PRINTF (Vprintf, stdout, &opts.verbose, TRUE)
42 GEN_PRINTF (Qprintf, stdout, &opts.quiet,   FALSE)
43
44 /**
45  * version - Print version information about the program
46  *
47  * Print a copyright statement and a brief description of the program.
48  *
49  * Return:  none
50  */
51 void version (void)
52 {
53         printf ("\n%s v%s - Find the owner of any given sector or cluster.\n\n",
54                 EXEC_NAME, VERSION);
55         printf ("Copyright (c)\n");
56         printf ("    2002-2003 Richard Russon\n");
57         printf ("\n%s\n%s%s\n", ntfs_gpl, ntfs_bugs, ntfs_home);
58 }
59
60 /**
61  * usage - Print a list of the parameters to the program
62  *
63  * Print a list of the parameters and options for the program.
64  *
65  * Return:  none
66  */
67 void usage (void)
68 {
69         printf ("\nUsage: %s [options] device\n"
70                 "    -i        --info           Print information about the volume\n"
71                 "    -c range  --cluster range  Look for objects in this range of clusters\n"
72                 "    -s range  --sector range   Look for objects in this range of sectors\n"
73         /*      "    -l        --last           Find the last file on the volume\n" */
74                 "\n"
75                 "    -f        --force          Use less caution\n"
76                 "    -q        --quiet          Less output\n"
77                 "    -v        --verbose        More output\n"
78                 "    -V        --version        Version information\n"
79                 "    -h        --help           Print this help\n\n",
80                 EXEC_NAME);
81         printf ("%s%s\n", ntfs_bugs, ntfs_home);
82 }
83
84 /**
85  * parse_options - Read and validate the programs command line
86  *
87  * Read the command line, verify the syntax and parse the options.
88  * This function is very long, but quite simple.
89  *
90  * Return:  1 Success
91  *          0 Error, one or more problems
92  */
93 int parse_options (int argc, char **argv)
94 {
95         static const char *sopt = "-c:fh?iqs:vV"; // l
96         static const struct option lopt[] = {
97                 { "cluster",    required_argument,      NULL, 'c' },
98                 { "force",      no_argument,            NULL, 'f' },
99                 { "help",       no_argument,            NULL, 'h' },
100                 { "info",       no_argument,            NULL, 'i' },
101                 //{ "last",     no_argument,            NULL, 'l' },
102                 { "quiet",      no_argument,            NULL, 'q' },
103                 { "sector",     required_argument,      NULL, 's' },
104                 { "verbose",    no_argument,            NULL, 'v' },
105                 { "version",    no_argument,            NULL, 'V' },
106                 { NULL,         0,                      NULL, 0   }
107         };
108
109         char c = -1;
110         int err  = 0;
111         int ver  = 0;
112         int help = 0;
113
114         opterr = 0; /* We'll handle the errors, thank you. */
115
116         opts.action      = act_none;
117         opts.range_begin = -1;
118         opts.range_end   = -1;
119
120         while ((c = getopt_long (argc, argv, sopt, lopt, NULL)) != -1) {
121                 switch (c) {
122                 case 1: /* A non-option argument */
123                         if (!opts.device) {
124                                 opts.device = argv[optind-1];
125                         } else {
126                                 opts.device = NULL;
127                                 err++;
128                         }
129                         break;
130
131                 case 'c':
132                         if ((opts.action == act_none) &&
133                             (utils_parse_range (optarg, &opts.range_begin, &opts.range_end, FALSE)))
134                                 opts.action = act_cluster;
135                         else
136                                 opts.action = act_error;
137                         break;
138                 case 'f':
139                         opts.force++;
140                         break;
141                 case 'h':
142                 case '?':
143                         help++;
144                         break;
145                 case 'i':
146                         if (opts.action == act_none) {
147                                 opts.action = act_info;
148                         } else {
149                                 opts.action = act_error;
150                                 err++;
151                         }
152                         break;
153                 /*
154                 case 'l':
155                         if (opts.action == act_none)
156                                 opts.action = act_last;
157                         else
158                                 opts.action = act_error;
159                         break;
160                 */
161                 case 'q':
162                         opts.quiet++;
163                         break;
164                 case 's':
165                         if ((opts.action == act_none) &&
166                             (utils_parse_range (optarg, &opts.range_begin, &opts.range_end, FALSE)))
167                                 opts.action = act_sector;
168                         else
169                                 opts.action = act_error;
170                         break;
171                 case 'v':
172                         opts.verbose++;
173                         break;
174                 case 'V':
175                         ver++;
176                         break;
177                 default:
178                         if ((optopt == 'c') || (optopt == 's'))
179                                 Eprintf ("Option '%s' requires an argument.\n", argv[optind-1]);
180                         else
181                                 Eprintf ("Unknown option '%s'.\n", argv[optind-1]);
182                         err++;
183                         break;
184                 }
185         }
186
187         if (help || ver) {
188                 opts.quiet = 0;
189         } else {
190                 if (opts.action == act_none)
191                         opts.action = act_info;
192                 if (opts.action == act_info)
193                         opts.quiet = 0;
194
195                 if (opts.device == NULL) {
196                         if (argc > 1)
197                                 Eprintf ("You must specify exactly one device.\n");
198                         err++;
199                 }
200
201                 if (opts.quiet && opts.verbose) {
202                         Eprintf ("You may not use --quiet and --verbose at the same time.\n");
203                         err++;
204                 }
205
206                 if (opts.action == act_error) {
207                         Eprintf ("You may only specify one action: --info, --cluster, --sector or --last.\n");
208                         err++;
209                 } else if (opts.range_begin > opts.range_end) {
210                         Eprintf ("The range must be in ascending order.\n");
211                         err++;
212                 }
213         }
214
215         if (ver)
216                 version();
217         if (help || err)
218                 usage();
219
220         return (!err && !help && !ver);
221 }
222
223 /**
224  * cluster_find
225  */
226 int cluster_find (ntfs_volume *vol, LCN s_begin, LCN s_end)
227 {
228         u64 i;
229         int in_use = 0, result = 1;
230         u8 *buffer;
231
232         if (!vol)
233                 return 1;
234
235         buffer = malloc (vol->mft_record_size);
236         if (!buffer) {
237                 Eprintf ("Couldn't allocate memory.\n");
238                 return 1;
239         }
240
241         for (i = s_begin; i < s_end; i++) {
242                 if (utils_cluster_in_use (vol, i) == 1) {
243                         in_use = 1;
244                         break;
245                 }
246         }
247
248         if (!in_use) {
249                 if (s_begin == s_end)
250                         printf ("clusters isn't in use\n");
251                 else
252                         printf ("clusters aren't in use\n");
253                 return 0;
254         }
255
256         // first, is the cluster in use in $Bitmap?
257
258         for (i = 0; i < vol->nr_mft_records; i++) {
259                 ntfs_inode *inode;
260                 ntfs_attr_search_ctx *ctx;
261
262                 if (!utils_mftrec_in_use (vol, i)) {
263                         //printf ("%d skipped\n", i);
264                         continue;
265                 }
266
267                 inode = ntfs_inode_open (vol, i);
268                 if (!inode) {
269                         Eprintf ("Can't read inode %lld\n", i);
270                         goto free;
271                 }
272
273                 if (inode->nr_extents == -1) {
274                         printf ("inode %lld is an extent record\n", i);
275                         goto close;
276                 }
277
278                 Vprintf ("Inode: %lld\n", i);
279                 ctx = ntfs_attr_get_search_ctx (inode, NULL);
280
281                 if (ntfs_attr_lookup (AT_STANDARD_INFORMATION, NULL, 0, IGNORE_CASE, 0, NULL, 0, ctx) < 0) {
282                         //printf ("extent inode\n");
283                         continue;
284                 }
285                 ntfs_attr_reinit_search_ctx (ctx);
286
287                 //printf ("Searching for cluster range %lld-%lld\n", s_begin, s_end);
288                 while (ntfs_attr_lookup (AT_UNUSED, NULL, 0, IGNORE_CASE, 0, NULL, 0, ctx) >= 0) {
289                         runlist_element *runs;
290                         int j;
291
292                         if (!ctx->attr->non_resident) {
293                                 //printf ("0x%02X ", ctx->attr->type);
294                                 continue;
295                         }
296
297                         runs = ntfs_mapping_pairs_decompress (vol, ctx->attr, NULL);
298                         if (!runs) {
299                                 Eprintf ("Couldn't read the data runs.\n");
300                                 ntfs_inode_close (inode);
301                                 goto free;
302                         }
303
304                         Vprintf ("\t[0x%02X]\n", ctx->attr->type);
305
306                         Vprintf ("\t\tVCN\tLCN\tLength\n");
307                         for (j = 0; runs[j].length > 0; j++) {
308                                 LCN a_begin = runs[j].lcn;
309                                 LCN a_end   = a_begin + runs[j].length - 1;
310
311                                 if (a_begin < 0)
312                                         continue;       // sparse, discontiguous, etc
313
314                                 Vprintf ("\t\t%lld\t%lld-%lld (%lld)\n", runs[j].vcn, runs[j].lcn, runs[j].lcn + runs[j].length - 1, runs[j].length);
315                                 //Vprintf ("\t\t%lld\t%lld\t%lld\n", runs[j].vcn, runs[j].lcn, runs[j].length);
316                                 //dprint list
317
318                                 if ((a_begin > s_end) || (a_end < s_begin))
319                                         continue;       // before or after search range
320
321                                 {
322                                         char buffer[256];
323                                         utils_inode_get_name (inode, buffer, sizeof (buffer));
324                                         //XXX distinguish between file/dir
325                                         printf ("inode %lld %s", i, buffer);
326                                         utils_attr_get_name (vol, ctx->attr, buffer, sizeof (buffer));
327                                         printf ("%c%s\n", PATH_SEP, buffer);
328                                         //printf ("\n");
329                                 }
330                                 break;
331                         }
332                 }
333
334                 ntfs_attr_put_search_ctx (ctx);
335                 ctx = NULL;
336 close:
337                 //printf ("\n");
338                 ntfs_inode_close (inode);
339         }
340 free:
341         free (buffer);
342         result = 0;
343         return result;
344 }
345
346 /**
347  * main - Begin here
348  *
349  * Start from here.
350  *
351  * Return:  0  Success, the program worked
352  *          1  Error, something went wrong
353  */
354 int main (int argc, char *argv[])
355 {
356         ntfs_volume *vol;
357         int result = 1;
358
359         if (!parse_options (argc, argv))
360                 return 1;
361
362         utils_set_locale();
363
364         vol = utils_mount_volume (opts.device, MS_RDONLY, opts.force);
365         if (!vol)
366                 return 1;
367
368         switch (opts.action) {
369                 case act_sector:
370                         if (opts.range_begin == opts.range_end)
371                                 Qprintf ("Searching for sector %lld\n", opts.range_begin);
372                         else
373                                 Qprintf ("Searching for sector range %lld-%lld\n", opts.range_begin, opts.range_end);
374                         /* Convert to clusters */
375                         opts.range_begin <<= (vol->cluster_size_bits - vol->sector_size_bits);
376                         opts.range_end   <<= (vol->cluster_size_bits - vol->sector_size_bits);
377                         result = cluster_find (vol, opts.range_begin, opts.range_end);
378                         break;
379                 case act_cluster:
380                         if (opts.range_begin == opts.range_end)
381                                 Qprintf ("Searching for cluster %lld\n", opts.range_begin);
382                         else
383                                 Qprintf ("Searching for cluster range %lld-%lld\n", opts.range_begin, opts.range_end);
384                         result = cluster_find (vol, opts.range_begin, opts.range_end);
385                         break;
386                 /*
387                 case act_last:
388                         printf ("Last\n");
389                         break;
390                 */
391                 case act_info:
392                 default:
393                         printf ("Info\n");
394                         break;
395         }
396
397         ntfs_umount (vol, FALSE);
398         return result;
399 }
400