http://linux-ntfs.sourceforge.net/snapshots/ntfsprogs-200309071734.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 (default)\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                         Eprintf ("You may only specify one action: --info, --cluster or --sector.\n");
209                         err++;
210                 } else if (opts.range_begin > opts.range_end) {
211                         Eprintf ("The range must be in ascending order.\n");
212                         err++;
213                 }
214         }
215
216         if (ver)
217                 version();
218         if (help || err)
219                 usage();
220
221         return (!err && !help && !ver);
222 }
223
224
225 /**
226  * free_space - Calculate the amount of space which isn't in use
227  */
228 u64 free_space (ntfs_volume *vol)
229 {
230         return 0;
231 }
232
233 /**
234  * user_space - Calculate the amount of space of the user's files
235  */
236 u64 user_space (ntfs_volume *vol)
237 {
238         return 0;
239 }
240
241 /**
242  * meta_space - Calculate the amount of space used by the filesystem structures
243  */
244 u64 meta_space (ntfs_volume *vol)
245 {
246         return 0;
247 }
248
249 /**
250  * info - Display information about the volume
251  */
252 int info (ntfs_volume *vol)
253 {
254         u64 a, b, c, d, e, f, g, h, i, j, k, l, m, n;
255         int cps;
256         u64 fs, us, ms;
257
258         cps = vol->cluster_size_bits - vol->sector_size_bits;
259         fs  = free_space (vol);
260         ms  = meta_space (vol);
261         us  = user_space (vol);
262
263         a = vol->sector_size;
264         b = vol->cluster_size;
265         c = 1 << cps;
266         d = vol->nr_clusters >> cps;
267         e = vol->nr_clusters;
268         f = fs / a;
269         g = fs / b;
270         h = fs * 100 / a / d;
271         i = us / a;
272         j = us / b;
273         k = us * 100 / a / d;
274         l = ms / a;
275         m = ms / b;
276         n = ms * 100 / a / d;
277
278         printf ("bytes per sector       : %lld\n", a);
279         printf ("bytes per cluster      : %lld\n", b);
280         printf ("sectors per cluster    : %lld\n", c);
281         printf ("sectors per volume     : %lld\n", d);
282         printf ("clusters per volume    : %lld\n", e);
283         printf ("sectors of free space  : %lld\n", f);
284         printf ("clusters of free space : %lld\n", g);
285         printf ("percentage free space  : %lld\n", h);
286         printf ("sectors of user data   : %lld\n", i);
287         printf ("clusters of user data  : %lld\n", j);
288         printf ("percentage user data   : %lld\n", k);
289         printf ("sectors of metadata    : %lld\n", l);
290         printf ("clusters of metadata   : %lld\n", m);
291         printf ("percentage metadata    : %lld\n", n);
292
293         return 0;
294 }
295
296
297 /**
298  * cluster_find
299  */
300 int cluster_find (ntfs_volume *vol, LCN s_begin, LCN s_end)
301 {
302         u64 i;
303         int in_use = 0, result = 1;
304         u8 *buffer;
305
306         if (!vol)
307                 return 1;
308
309         buffer = malloc (vol->mft_record_size);
310         if (!buffer) {
311                 Eprintf ("Couldn't allocate memory.\n");
312                 return 1;
313         }
314
315         for (i = s_begin; i < s_end; i++) {
316                 if (utils_cluster_in_use (vol, i) == 1) {
317                         in_use = 1;
318                         break;
319                 }
320         }
321
322         if (!in_use) {
323                 if (s_begin == s_end)
324                         printf ("cluster isn't in use\n");
325                 else
326                         printf ("clusters aren't in use\n");
327                 result = 0;
328                 goto free;
329         }
330
331         // first, is the cluster in use in $Bitmap?
332
333         for (i = 0; i < vol->nr_mft_records; i++) {
334                 ntfs_inode *inode;
335                 ntfs_attr_search_ctx *ctx;
336
337                 if (!utils_mftrec_in_use (vol, i)) {
338                         //printf ("%lld skipped\n", i);
339                         continue;
340                 }
341
342                 inode = ntfs_inode_open (vol, i);
343                 if (!inode) {
344                         Eprintf ("Can't read inode %lld\n", i);
345                         goto free;
346                 }
347
348                 if (inode->nr_extents == -1) {
349                         printf ("inode %lld is an extent record\n", i);
350                         goto close;
351                 }
352
353                 Vprintf ("Inode: %lld\n", i);
354                 ctx = ntfs_attr_get_search_ctx (inode, NULL);
355
356                 if (ntfs_attr_lookup (AT_STANDARD_INFORMATION, NULL, 0, IGNORE_CASE, 0, NULL, 0, ctx) < 0) {
357                         //printf ("extent inode\n");
358                         continue;
359                 }
360                 ntfs_attr_reinit_search_ctx (ctx);
361
362                 //printf ("Searching for cluster range %lld-%lld\n", s_begin, s_end);
363                 while (ntfs_attr_lookup (AT_UNUSED, NULL, 0, IGNORE_CASE, 0, NULL, 0, ctx) >= 0) {
364                         runlist_element *runs;
365                         int j;
366
367                         if (!ctx->attr->non_resident) {
368                                 //printf ("0x%02X ", ctx->attr->type);
369                                 continue;
370                         }
371
372                         runs = ntfs_mapping_pairs_decompress (vol, ctx->attr, NULL);
373                         if (!runs) {
374                                 Eprintf ("Couldn't read the data runs.\n");
375                                 ntfs_inode_close (inode);
376                                 goto free;
377                         }
378
379                         Vprintf ("\t[0x%02X]\n", ctx->attr->type);
380
381                         Vprintf ("\t\tVCN\tLCN\tLength\n");
382                         for (j = 0; runs[j].length > 0; j++) {
383                                 LCN a_begin = runs[j].lcn;
384                                 LCN a_end   = a_begin + runs[j].length - 1;
385
386                                 if (a_begin < 0)
387                                         continue;       // sparse, discontiguous, etc
388
389                                 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);
390                                 //Vprintf ("\t\t%lld\t%lld\t%lld\n", runs[j].vcn, runs[j].lcn, runs[j].length);
391                                 //dprint list
392
393                                 if ((a_begin > s_end) || (a_end < s_begin))
394                                         continue;       // before or after search range
395
396                                 {
397                                         char buffer[256];
398                                         utils_inode_get_name (inode, buffer, sizeof (buffer));
399                                         //XXX distinguish between file/dir
400                                         printf ("inode %lld %s", i, buffer);
401                                         utils_attr_get_name (vol, ctx->attr, buffer, sizeof (buffer));
402                                         printf ("%c%s\n", PATH_SEP, buffer);
403                                 }
404                                 break;  // XXX if verbose, we should list all matching runs
405                         }
406                 }
407
408                 ntfs_attr_put_search_ctx (ctx);
409                 ctx = NULL;
410 close:
411                 //printf ("\n");
412                 ntfs_inode_close (inode);
413         }
414 free:
415         free (buffer);
416         result = 0;
417         return result;
418 }
419
420
421 /**
422  * main - Begin here
423  *
424  * Start from here.
425  *
426  * Return:  0  Success, the program worked
427  *          1  Error, something went wrong
428  */
429 int main (int argc, char *argv[])
430 {
431         ntfs_volume *vol;
432         int result = 1;
433
434         if (!parse_options (argc, argv))
435                 return 1;
436
437         utils_set_locale();
438
439         vol = utils_mount_volume (opts.device, MS_RDONLY, opts.force);
440         if (!vol)
441                 return 1;
442
443         switch (opts.action) {
444                 case act_sector:
445                         if (opts.range_begin == opts.range_end)
446                                 Qprintf ("Searching for sector %lld\n", opts.range_begin);
447                         else
448                                 Qprintf ("Searching for sector range %lld-%lld\n", opts.range_begin, opts.range_end);
449                         /* Convert to clusters */
450                         opts.range_begin >>= (vol->cluster_size_bits - vol->sector_size_bits);
451                         opts.range_end   >>= (vol->cluster_size_bits - vol->sector_size_bits);
452                         result = cluster_find (vol, opts.range_begin, opts.range_end);
453                         break;
454                 case act_cluster:
455                         if (opts.range_begin == opts.range_end)
456                                 Qprintf ("Searching for cluster %lld\n", opts.range_begin);
457                         else
458                                 Qprintf ("Searching for cluster range %lld-%lld\n", opts.range_begin, opts.range_end);
459                         result = cluster_find (vol, opts.range_begin, opts.range_end);
460                         break;
461                 /*
462                 case act_last:
463                         printf ("Last\n");
464                         break;
465                 */
466                 case act_info:
467                 default:
468                         info (vol);
469                         break;
470         }
471
472         ntfs_umount (vol, FALSE);
473         return result;
474 }
475