Allow undelete to recover a set / range of inodes
(Logical change 1.408)edge.strict_endians
parent
24ac354062
commit
6995b610d3
|
@ -264,12 +264,14 @@ option. Only match files that have been altered since this time. The time must
|
|||
be given as number using a suffix of d, w, m, y for days, weeks, months or years
|
||||
ago.
|
||||
.TP
|
||||
.BI "\-u " num
|
||||
.BI "\-u " nums
|
||||
.br
|
||||
.ns
|
||||
.TP
|
||||
.BI "\-\-undelete " num
|
||||
Recover the file with this inode number. This option can be combined with
|
||||
.BI "\-\-undelete " nums
|
||||
Recover the files with these inode numbers. You can specify more than
|
||||
one inode by separating with "," or a range of inodes by using
|
||||
"-". This option can be combined with
|
||||
.BR \-\-output ,
|
||||
.BR \-\-destination ,
|
||||
and
|
||||
|
@ -321,6 +323,12 @@ Look for deleted files altered in the last two days
|
|||
.B ntfsundelete /dev/hda1 \-t 2d
|
||||
.sp
|
||||
.RE
|
||||
Undelete inodes 2, 5 and 100 to 131 of device /dev/sda1
|
||||
.RS
|
||||
.sp
|
||||
.B ntfsundelete /dev/sda1 -u 2,5,100-131
|
||||
.sp
|
||||
.RE
|
||||
Undelete inode number 3689, call the file 'work.doc' and put it in the user's
|
||||
home directory.
|
||||
.RS
|
||||
|
@ -339,9 +347,9 @@ If you find one, please send an email to
|
|||
.nh
|
||||
<linux-ntfs-dev@lists.sourceforge.net>
|
||||
.hy
|
||||
.SH AUTHOR
|
||||
.SH AUTHORS
|
||||
.B ntfsundelete
|
||||
was written by Richard Russon (FlatCap)
|
||||
was written by Richard Russon (FlatCap) and Holger Ohmacht.
|
||||
.SH AVAILABILITY
|
||||
.B ntfsundelete
|
||||
is part of the ntfsprogs package and is available from
|
||||
|
|
|
@ -1,7 +1,8 @@
|
|||
/**
|
||||
* ntfsundelete - Part of the Linux-NTFS project.
|
||||
*
|
||||
* Copyright (c) 2002-2003 Richard Russon
|
||||
* Copyright (c) 2002-2004 Richard Russon
|
||||
* Copyright (c) 2004 Holger Ohmacht
|
||||
*
|
||||
* This utility will recover deleted files from an NTFS volume.
|
||||
*
|
||||
|
@ -55,16 +56,136 @@
|
|||
static const char *EXEC_NAME = "ntfsundelete";
|
||||
static const char *MFTFILE = "mft";
|
||||
static const char *UNNAMED = "<unnamed>";
|
||||
static char *NONE = "<none>";
|
||||
static char *UNKNOWN = "unknown";
|
||||
static const char *NONE = "<none>";
|
||||
static const char *UNKNOWN = "unknown";
|
||||
static struct options opts;
|
||||
|
||||
typedef struct
|
||||
{
|
||||
u32 begin;
|
||||
u32 end;
|
||||
} range;
|
||||
|
||||
static range *ranges;
|
||||
static long nr_entries;
|
||||
|
||||
GEN_PRINTF (Eprintf, stderr, NULL, FALSE)
|
||||
GEN_PRINTF (Vprintf, stdout, &opts.verbose, TRUE)
|
||||
GEN_PRINTF (Qprintf, stdout, &opts.quiet, FALSE)
|
||||
|
||||
#define _(S) gettext(S)
|
||||
|
||||
/**
|
||||
* parse_inode_arg - parses the inode expression
|
||||
*
|
||||
* Parses the optarg after parameter -u for valid ranges
|
||||
*
|
||||
* Return: Number of correct inode specifications or -1 for error
|
||||
*/
|
||||
static int parse_inode_arg (void)
|
||||
{
|
||||
int p;
|
||||
u32 imax;
|
||||
u32 range_begin;
|
||||
u32 range_end;
|
||||
u32 range_temp;
|
||||
u32 inode;
|
||||
char *opt_arg_ptr;
|
||||
char *opt_arg_temp;
|
||||
char *opt_arg_end1;
|
||||
char *opt_arg_end2;
|
||||
|
||||
/* init variables */
|
||||
p = strlen (optarg);
|
||||
imax = p;
|
||||
opt_arg_ptr = optarg;
|
||||
opt_arg_end1 = optarg;
|
||||
opt_arg_end2 = &(optarg[p]);
|
||||
nr_entries = 0;
|
||||
|
||||
/* alloc mem for range table */
|
||||
ranges = (range *) malloc ((p + 1) * sizeof (range));
|
||||
if (ranges == NULL)
|
||||
{
|
||||
printf ("ERROR: Couldn't alloc mem for parsing inodes!\n");
|
||||
return (-1);
|
||||
}
|
||||
|
||||
/* loop */
|
||||
while ((opt_arg_end1 != opt_arg_end2) && (p > 0))
|
||||
{
|
||||
inode = strtoul (opt_arg_ptr, &opt_arg_end1, 0);
|
||||
p--;
|
||||
|
||||
/* invalid char at begin */
|
||||
if ((opt_arg_ptr == opt_arg_end1) || (opt_arg_ptr == opt_arg_end2))
|
||||
{
|
||||
printf ("ERROR: Invalid Number: %s\n", opt_arg_ptr);
|
||||
return (-1);
|
||||
}
|
||||
|
||||
/* RANGE - Check for range */
|
||||
if (opt_arg_end1[0] == '-')
|
||||
{
|
||||
/* get range end */
|
||||
opt_arg_temp = opt_arg_end1;
|
||||
opt_arg_end1 = & (opt_arg_temp[1]);
|
||||
if (opt_arg_temp >= opt_arg_end2)
|
||||
{
|
||||
printf ("ERROR: Missing range end!\n");
|
||||
return (-1);
|
||||
}
|
||||
range_begin = inode;
|
||||
|
||||
/* get count */
|
||||
range_end = strtoul (opt_arg_end1, &opt_arg_temp, 0);
|
||||
if (opt_arg_temp == opt_arg_end1)
|
||||
{
|
||||
printf ("ERROR: Invalid Number: %s\n", opt_arg_temp);
|
||||
return (-1);
|
||||
}
|
||||
|
||||
/* check for correct values */
|
||||
if (range_begin > range_end)
|
||||
{
|
||||
range_temp = range_end;
|
||||
range_end = range_begin;
|
||||
range_begin = range_temp;
|
||||
}
|
||||
|
||||
/* put into struct */
|
||||
ranges[nr_entries].begin = range_begin;
|
||||
ranges[nr_entries].end = range_end;
|
||||
nr_entries++;
|
||||
|
||||
/* Last check */
|
||||
opt_arg_ptr = & (opt_arg_temp[1]);
|
||||
if (opt_arg_ptr >= opt_arg_end2)
|
||||
break;
|
||||
} else if (opt_arg_end1[0] == ',') {
|
||||
/* SINGLE VALUE, BUT CONTINUING */
|
||||
/* put inode into range list */
|
||||
ranges[nr_entries].begin = inode;
|
||||
ranges[nr_entries].end = inode;
|
||||
nr_entries++;
|
||||
|
||||
/* Next inode */
|
||||
opt_arg_ptr = & (opt_arg_end1[1]);
|
||||
if (opt_arg_ptr >= opt_arg_end2)
|
||||
{
|
||||
printf ("ERROR: Missing new value at end of input!\n");
|
||||
return (-1);
|
||||
}
|
||||
continue;
|
||||
} else { /* SINGLE VALUE, END */
|
||||
ranges[nr_entries].begin = inode;
|
||||
ranges[nr_entries].end = inode;
|
||||
nr_entries++;
|
||||
}
|
||||
}
|
||||
return (nr_entries);
|
||||
}
|
||||
|
||||
/**
|
||||
* version - Print version information about the program
|
||||
*
|
||||
|
@ -98,7 +219,7 @@ static void usage (void)
|
|||
" -S range --size range Match files of this size\n"
|
||||
" -t since --time since Last referenced since this time\n"
|
||||
"\n"
|
||||
" -u num --undelete num Undelete inode\n"
|
||||
" -u nums --undelete nums Undelete inodes\n"
|
||||
" -o file --output file Save with this filename\n"
|
||||
" -d dir --destination dir Destination directory\n"
|
||||
" -b num --byte num Fill missing parts with this byte\n"
|
||||
|
@ -398,10 +519,13 @@ static int parse_options (int argc, char *argv[])
|
|||
}
|
||||
break;
|
||||
case 'u':
|
||||
if (opts.mode == MODE_NONE) {
|
||||
if (opts.mode == MODE_NONE)
|
||||
{
|
||||
end = NULL;
|
||||
opts.mode = MODE_UNDELETE;
|
||||
opts.uinode = strtol (optarg, &end, 0);
|
||||
/* parse inodes */
|
||||
if (parse_inode_arg() == -1)
|
||||
err++;
|
||||
if (end && *end)
|
||||
err++;
|
||||
} else {
|
||||
|
@ -894,7 +1018,7 @@ static int calc_percentage (struct ufile *file, ntfs_volume *vol)
|
|||
clusters_free++;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
if ((clusters_inuse + clusters_free) == 0) {
|
||||
Eprintf ("Unexpected error whilst calculating percentage for inode %lld\n", file->inode);
|
||||
continue;
|
||||
|
@ -928,7 +1052,7 @@ static int calc_percentage (struct ufile *file, ntfs_volume *vol)
|
|||
static void dump_record (struct ufile *file)
|
||||
{
|
||||
char buffer[20];
|
||||
char *name;
|
||||
const char *name;
|
||||
struct list_head *item;
|
||||
int i;
|
||||
|
||||
|
@ -1040,7 +1164,7 @@ static void list_record (struct ufile *file)
|
|||
{
|
||||
char buffer[20];
|
||||
struct list_head *item;
|
||||
char *name = NULL;
|
||||
const char *name = NULL;
|
||||
long long size = 0;
|
||||
int percent = 0;
|
||||
|
||||
|
@ -1179,7 +1303,7 @@ static unsigned int write_data (int fd, const char *buffer,
|
|||
*
|
||||
* Return: n Length of the allocated name
|
||||
*/
|
||||
static int create_pathname (const char *dir, const char *name,
|
||||
static int create_pathname (const char *dir, const char *name,
|
||||
const char *stream, char *buffer, int bufsize)
|
||||
{
|
||||
if (!name)
|
||||
|
@ -1672,6 +1796,8 @@ int main (int argc, char *argv[])
|
|||
{
|
||||
ntfs_volume *vol;
|
||||
int result = 1;
|
||||
int i;
|
||||
u32 inode;
|
||||
|
||||
if (!parse_options (argc, argv))
|
||||
goto free;
|
||||
|
@ -1689,17 +1815,27 @@ int main (int argc, char *argv[])
|
|||
Vprintf ("Failed to scan device '%s'.\n", opts.device);
|
||||
break;
|
||||
case MODE_UNDELETE:
|
||||
result = !undelete_file (vol, opts.uinode);
|
||||
if (result)
|
||||
Vprintf ("Failed to undelete inode %d.\n", opts.uinode);
|
||||
if (nr_entries == 0)
|
||||
{
|
||||
printf ("ERROR: No inode(s) specified!\n");
|
||||
break;
|
||||
}
|
||||
|
||||
for (i = 0; i < nr_entries; i++)
|
||||
{
|
||||
for (inode = ranges[i].begin; inode <= ranges[i].end; inode ++)
|
||||
{
|
||||
result = !undelete_file (vol, inode);
|
||||
if (result)
|
||||
Vprintf ("ERROR: Failed to undelete inode %d\n!", inode);
|
||||
}
|
||||
}
|
||||
break;
|
||||
case MODE_COPY:
|
||||
result = !copy_mft (vol, opts.mft_begin, opts.mft_end);
|
||||
if (result)
|
||||
Vprintf ("Failed to read MFT blocks %lld-%lld.\n",
|
||||
(long long)opts.mft_begin,
|
||||
(long long)min(vol->nr_mft_records,
|
||||
opts.mft_end));
|
||||
opts.mft_begin, min(vol->nr_mft_records, opts.mft_end));
|
||||
break;
|
||||
default:
|
||||
; /* Cannot happen */
|
||||
|
|
Loading…
Reference in New Issue