Add new debugfs commands and arguments which make it easier to

recover deleted files.  The lsdel command now takes an optional
argument which allows the user to only see the most recently
deleted files.  Also added a new command, undel, which automates
undeleting a deleted inode and linking it back to a directory.
Also added an optional count argument to the testb, freeb, setb,
and find_free_block commands.  The ls command now takes a new
option, -d, which lists deleted directory entries.

Factored out out commonly used code into utility subroutines
for ease of maintenance and to make the executable size smaller.
This commit is contained in:
Theodore Ts'o 2002-01-03 04:55:25 -05:00
parent 8bd0c95908
commit e1018eeaa3
12 changed files with 508 additions and 436 deletions

View File

@ -1,3 +1,29 @@
2002-01-03 Theodore Tso <tytso@valinux.com>
* lsdel.c (do_lsdel): New optional argument which allows the user
to only see the most recently deleted files.
* debugfs.c (do_undel, do_testb, do_freeb, do_setb, do_ffb): Add
new command, undelete, which automates undeleting a
deleted inode and linking it back to a directory. Add a
count argument to the testb, freeb, setb, and ffb commands.
* ls.c (list_dir_proc, do_list_dir): Add support for -d option
which lists deleted directory entries.
* debug_cmds.ct: Add new command, undelete.
* dump.c, icheck.c, logdump.c, ls.c, lsdel.c, setsuper.c,
debugfs.c: Use new utility functions which factor out
commonly used code.
* util.c (debugfs_read_inode, debugfs_write_inode,
common_block_args_process, common_inode_args_process,
common_args_process, strtoblk, parse_ulong): New
functions which factor out commonly used code into
subroutines for ease of maintenance and to make the
executable size smaller.
2001-12-23 Theodore Tso <tytso@valinux.com>
* Makefile.in, jfs_user.h: Move linux/jbd.h to

View File

@ -103,6 +103,9 @@ request do_mknod, "Create a special file",
request do_lsdel, "List deleted inodes",
list_deleted_inodes, lsdel;
request do_undel, "Undelete file",
undelete, undel;
request do_write, "Copy a file from your native filesystem",
write;

View File

@ -185,8 +185,10 @@ Set or clear various filesystem features in the superblock. After setting
or clearing any filesystem features that were requested, print the current
state of the filesystem feature set.
.TP
.I find_free_block [goal]
Find the first free block, starting from
.I find_free_block [count [goal]]
Find the first
.I count
free blocks, starting from
.I goal
and allocate it.
.TP
@ -200,10 +202,17 @@ optional argument
specifies the permissions of the new inode. (If the directory bit is set
on the mode, the allocation routine will function differently.)
.TP
.I freeb block
.I freeb block [count]
Mark the block number
.I block
as not allocated.
If the optional argument
.I count
is present, then
.I count
blocks starting at block number
.I block
will be marked as not allocated.
.TP
.I freei filespec
Free the inode specified by
@ -253,9 +262,15 @@ Note this does not adjust the inode reference counts.
.I logdump [-ac] [-b<block>] [-i<inode>] [-f<journal_file>] [output_file]
Dump the contents of the ext3 journal.
.TP
.I ls [-l] filespec
.I ls [-l] [-d] filespec
Print a listing of the files in the directory
.IR filespec .
The
.I \-l
flag will list files using a more verbose format.
The
.I \-d
flag will list deleted entries in the directory.
.TP
.I modify_inode filespec
Modify the contents of the inode structure in the inode
@ -318,10 +333,17 @@ Remove the directory
.IR filespec .
This function is currently not implemented.
.TP
.I setb block
.I setb block [count]
Mark the block number
.I block
as allocated.
If the optional argument
.I count
is present, then
.I count
blocks starting at block number
.I block
will be marked as allocated.
.TP
.I seti filespec
Mark inode
@ -346,10 +368,17 @@ flag is given, only print out the superblock contents.
Display the contents of the inode structure of the inode
.IR filespec .
.TP
.I testb block
.I testb block [count]
Test if the block number
.I block
is marked as allocated in the block bitmap.
If the optional argument
.I count
is present, then
.I count
blocks starting at block number
.I block
will be tested.
.TP
.I testi filespec
Test if the inode

View File

@ -95,11 +95,10 @@ errout:
void do_open_filesys(int argc, char **argv)
{
const char *usage = "Usage: open [-s superblock] [-b blocksize] [-c] [-w] <device>";
int c;
int c, err;
int catastrophic = 0;
blk_t superblock = 0;
blk_t blocksize = 0;
char *tmp;
int open_flags = 0;
optind = 0;
@ -121,20 +120,16 @@ void do_open_filesys(int argc, char **argv)
catastrophic = 1;
break;
case 'b':
blocksize = strtoul(optarg, &tmp, 0);
if (*tmp) {
com_err(argv[0], 0,
"Bad block size - %s", optarg);
blocksize = parse_ulong(optarg, argv[0],
"block size", &err);
if (err)
return;
}
break;
case 's':
superblock = strtoul(optarg, &tmp, 0);
if (*tmp) {
com_err(argv[0], 0,
"Bad superblock number - %s", optarg);
superblock = parse_ulong(optarg, argv[0],
"superblock number", &err);
if (err)
return;
}
break;
default:
com_err(argv[0], 0, usage);
@ -153,12 +148,9 @@ void do_open_filesys(int argc, char **argv)
void do_lcd(int argc, char **argv)
{
const char *usage = "Usage: lcd <native dir>";
if (argc != 2) {
com_err(argv[0], 0, usage);
if (common_args_process(argc, argv, 2, 2, "lcd",
"<native dir>", 0))
return;
}
if (chdir(argv[1]) == -1) {
com_err(argv[0], errno,
@ -191,35 +183,26 @@ static void close_filesystem(NOARGS)
void do_close_filesys(int argc, char **argv)
{
if (argc > 1) {
com_err(argv[0], 0, "Usage: close_filesys");
return;
}
if (check_fs_open(argv[0]))
if (common_args_process(argc, argv, 1, 1, "close_filesys", "", 0))
return;
close_filesystem();
}
void do_init_filesys(int argc, char **argv)
{
const char *usage = "Usage: initialize <device> <blocksize>";
struct ext2_super_block param;
errcode_t retval;
char *tmp;
int err;
if (argc != 3) {
com_err(argv[0], 0, usage);
return;
}
if (check_fs_not_open(argv[0]))
if (common_args_process(argc, argv, 3, 3, "initialize",
"<device> <blocksize>", CHECK_FS_NOTOPEN))
return;
memset(&param, 0, sizeof(struct ext2_super_block));
param.s_blocks_count = strtoul(argv[2], &tmp, 0);
if (*tmp) {
com_err(argv[0], 0, "Bad blocks count - %s", argv[2]);
param.s_blocks_count = parse_ulong(argv[0], argv[2],
"blocks count", &err);
if (err)
return;
}
retval = ext2fs_initialize(argv[1], 0, &param,
unix_io_manager, &current_fs);
if (retval) {
@ -491,29 +474,17 @@ static void dump_inode(ext2_ino_t inode_num, struct ext2_inode inode)
close_pager(out);
}
void do_stat(int argc, char *argv[])
{
ext2_ino_t inode;
struct ext2_inode inode_buf;
int retval;
if (argc != 2) {
com_err(argv[0], 0, "Usage: stat <file>");
return;
}
if (check_fs_open(argv[0]))
return;
inode = string_to_inode(argv[1]);
if (!inode)
if (common_inode_args_process(argc, argv, &inode, 0))
return;
retval = ext2fs_read_inode(current_fs, inode, &inode_buf);
if (retval)
{
com_err(argv[0], retval, "while trying to read inode %d", inode);
return;
}
if (debugfs_read_inode(inode, &inode_buf, argv[0]))
return;
dump_inode(inode,inode_buf);
return;
@ -524,14 +495,7 @@ void do_chroot(int argc, char *argv[])
ext2_ino_t inode;
int retval;
if (argc != 2) {
com_err(argv[0], 0, "Usage: chroot <file>");
return;
}
if (check_fs_open(argv[0]))
return;
inode = string_to_inode(argv[1]);
if (!inode)
if (common_inode_args_process(argc, argv, &inode, 0))
return;
retval = ext2fs_check_directory(current_fs, inode);
@ -548,47 +512,22 @@ void do_clri(int argc, char *argv[])
int retval;
struct ext2_inode inode_buf;
if (argc != 2) {
com_err(argv[0], 0, "Usage: clri <file>");
return;
}
if (check_fs_open(argv[0]))
return;
if (check_fs_read_write(argv[0]))
return;
inode = string_to_inode(argv[1]);
if (!inode)
if (common_inode_args_process(argc, argv, &inode, CHECK_FS_RW))
return;
retval = ext2fs_read_inode(current_fs, inode, &inode_buf);
if (retval) {
com_err(argv[0], retval, "while trying to read inode %d",
inode);
if (debugfs_read_inode(inode, &inode_buf, argv[0]))
return;
}
memset(&inode_buf, 0, sizeof(inode_buf));
retval = ext2fs_write_inode(current_fs, inode, &inode_buf);
if (retval) {
com_err(argv[0], retval, "while trying to write inode %d",
inode);
if (debugfs_write_inode(inode, &inode_buf, argv[0]))
return;
}
}
void do_freei(int argc, char *argv[])
{
ext2_ino_t inode;
if (argc != 2) {
com_err(argv[0], 0, "Usage: freei <file>");
return;
}
if (check_fs_open(argv[0]))
return;
if (check_fs_read_write(argv[0]))
return;
inode = string_to_inode(argv[1]);
if (!inode)
if (common_inode_args_process(argc, argv, &inode,
CHECK_FS_RW | CHECK_FS_BITMAPS))
return;
if (!ext2fs_test_inode_bitmap(current_fs->inode_map,inode))
@ -601,16 +540,8 @@ void do_seti(int argc, char *argv[])
{
ext2_ino_t inode;
if (argc != 2) {
com_err(argv[0], 0, "Usage: seti <file>");
return;
}
if (check_fs_open(argv[0]))
return;
if (check_fs_read_write(argv[0]))
return;
inode = string_to_inode(argv[1]);
if (!inode)
if (common_inode_args_process(argc, argv, &inode,
CHECK_FS_RW | CHECK_FS_BITMAPS))
return;
if (ext2fs_test_inode_bitmap(current_fs->inode_map,inode))
@ -623,16 +554,7 @@ void do_testi(int argc, char *argv[])
{
ext2_ino_t inode;
if (argc != 2) {
com_err(argv[0], 0, "Usage: testi <file>");
return;
}
if (check_fs_open(argv[0]))
return;
if (check_fs_bitmaps(argv[0]))
return;
inode = string_to_inode(argv[1]);
if (!inode)
if (common_inode_args_process(argc, argv, &inode, CHECK_FS_BITMAPS))
return;
if (ext2fs_test_inode_bitmap(current_fs->inode_map,inode))
@ -641,76 +563,58 @@ void do_testi(int argc, char *argv[])
printf("Inode %u is not in use\n", inode);
}
void do_freeb(int argc, char *argv[])
{
blk_t block;
char *tmp;
int count = 1;
if (argc != 2) {
com_err(argv[0], 0, "Usage: freeb <block>");
return;
}
if (check_fs_open(argv[0]))
if (common_block_args_process(argc, argv, &block, &count))
return;
if (check_fs_read_write(argv[0]))
return;
block = strtoul(argv[1], &tmp, 0);
if (!block || *tmp) {
com_err(argv[0], 0, "No block 0");
return;
}
if (!ext2fs_test_block_bitmap(current_fs->block_map,block))
com_err(argv[0], 0, "Warning: block already clear");
ext2fs_unmark_block_bitmap(current_fs->block_map,block);
while (count-- > 0) {
if (!ext2fs_test_block_bitmap(current_fs->block_map,block))
com_err(argv[0], 0, "Warning: block %d already clear",
block);
ext2fs_unmark_block_bitmap(current_fs->block_map,block);
block++;
}
ext2fs_mark_bb_dirty(current_fs);
}
void do_setb(int argc, char *argv[])
{
blk_t block;
char *tmp;
int count = 1;
if (argc != 2) {
com_err(argv[0], 0, "Usage: setb <block>");
return;
}
if (check_fs_open(argv[0]))
if (common_block_args_process(argc, argv, &block, &count))
return;
if (check_fs_read_write(argv[0]))
return;
block = strtoul(argv[1], &tmp, 0);
if (!block || *tmp) {
com_err(argv[0], 0, "No block 0");
return;
}
if (ext2fs_test_block_bitmap(current_fs->block_map,block))
com_err(argv[0], 0, "Warning: block already set");
ext2fs_mark_block_bitmap(current_fs->block_map,block);
while (count-- > 0) {
if (ext2fs_test_block_bitmap(current_fs->block_map,block))
com_err(argv[0], 0, "Warning: block %d already set",
block);
ext2fs_mark_block_bitmap(current_fs->block_map,block);
block++;
}
ext2fs_mark_bb_dirty(current_fs);
}
void do_testb(int argc, char *argv[])
{
blk_t block;
char *tmp;
int count = 1;
if (argc != 2) {
com_err(argv[0], 0, "Usage: testb <block>");
if (common_block_args_process(argc, argv, &block, &count))
return;
while (count-- > 0) {
if (ext2fs_test_block_bitmap(current_fs->block_map,block))
printf("Block %d marked in use\n", block);
else
printf("Block %d not in use\n", block);
block++;
}
if (check_fs_open(argv[0]))
return;
if (check_fs_bitmaps(argv[0]))
return;
block = strtoul(argv[1], &tmp, 0);
if (!block || *tmp) {
com_err(argv[0], 0, "No block 0");
return;
}
if (ext2fs_test_block_bitmap(current_fs->block_map,block))
printf("Block %d marked in use\n", block);
else printf("Block %d not in use\n", block);
}
static void modify_u8(char *com, const char *prompt,
@ -790,25 +694,11 @@ void do_modify_inode(int argc, char *argv[])
const char *octal_format = "0%o";
const char *decimal_format = "%d";
if (argc != 2) {
com_err(argv[0], 0, "Usage: modify_inode <file>");
return;
}
if (check_fs_open(argv[0]))
return;
if (check_fs_read_write(argv[0]))
if (common_inode_args_process(argc, argv, &inode_num, CHECK_FS_RW))
return;
inode_num = string_to_inode(argv[1]);
if (!inode_num)
if (debugfs_read_inode(inode_num, &inode, argv[1]))
return;
retval = ext2fs_read_inode(current_fs, inode_num, &inode);
if (retval) {
com_err(argv[1], retval, "while trying to read inode %d",
inode_num);
return;
}
modify_u16(argv[0], "Mode", octal_format, &inode.i_mode);
modify_u16(argv[0], "User ID", decimal_format, &inode.i_uid);
@ -867,12 +757,8 @@ void do_modify_inode(int argc, char *argv[])
&inode.i_block[EXT2_DIND_BLOCK]);
modify_u32(argv[0], "Triple Indirect Block", decimal_format,
&inode.i_block[EXT2_TIND_BLOCK]);
retval = ext2fs_write_inode(current_fs, inode_num, &inode);
if (retval) {
com_err(argv[1], retval, "while trying to write inode %d",
inode_num);
if (debugfs_write_inode(inode_num, &inode, argv[1]))
return;
}
}
void do_change_working_dir(int argc, char *argv[])
@ -880,15 +766,7 @@ void do_change_working_dir(int argc, char *argv[])
ext2_ino_t inode;
int retval;
if (argc != 2) {
com_err(argv[0], 0, "Usage: cd <file>");
return;
}
if (check_fs_open(argv[0]))
return;
inode = string_to_inode(argv[1]);
if (!inode)
if (common_inode_args_process(argc, argv, &inode, 0))
return;
retval = ext2fs_check_directory(current_fs, inode);
@ -905,11 +783,8 @@ void do_print_working_directory(int argc, char *argv[])
int retval;
char *pathname = NULL;
if (argc > 1) {
com_err(argv[0], 0, "Usage: print_working_directory");
return;
}
if (check_fs_open(argv[0]))
if (common_args_process(argc, argv, 1, 1,
"print_working_directory", "", 0))
return;
retval = ext2fs_get_pathname(current_fs, cwd, 0, &pathname);
@ -980,13 +855,63 @@ static void make_link(char *sourcename, char *destname)
void do_link(int argc, char *argv[])
{
if (argc != 3) {
com_err(argv[0], 0, "Usage: link <source_file> <dest_name>");
if (common_args_process(argc, argv, 3, 3, "link",
"<source file> <dest_name>", CHECK_FS_RW))
return;
make_link(argv[1], argv[2]);
}
static int mark_blocks_proc(ext2_filsys fs, blk_t *blocknr,
int blockcnt, void *private)
{
blk_t block;
dgrp_t group;
block = *blocknr;
ext2fs_block_alloc_stats(fs, block, +1);
return 0;
}
void do_undel(int argc, char *argv[])
{
ext2_ino_t ino;
struct ext2_inode inode;
int retval;
dgrp_t group;
if (common_args_process(argc, argv, 3, 3, "undelete",
"<inode_num> <dest_name>",
CHECK_FS_RW | CHECK_FS_BITMAPS))
return;
ino = string_to_inode(argv[1]);
if (!ino)
return;
if (debugfs_read_inode(ino, &inode, argv[1]))
return;
if (ext2fs_test_inode_bitmap(current_fs->inode_map, ino)) {
com_err(argv[1], 0, "Inode is not marked as deleted");
return;
}
if (check_fs_open(argv[0]))
/*
* XXX this function doesn't handle changing the links count on the
* parent directory when undeleting a directory.
*/
inode.i_links_count = LINUX_S_ISDIR(inode.i_mode) ? 2 : 1;
inode.i_dtime = 0;
if (debugfs_write_inode(ino, &inode, argv[0]))
return;
ext2fs_block_iterate(current_fs, ino, 0, NULL,
mark_blocks_proc, NULL);
ext2fs_inode_alloc_stats(current_fs, ino, +1);
make_link(argv[1], argv[2]);
}
@ -1014,11 +939,8 @@ static void unlink_file_by_name(char *filename)
void do_unlink(int argc, char *argv[])
{
if (argc != 2) {
com_err(argv[0], 0, "Usage: unlink <pathname>");
return;
}
if (check_fs_open(argv[0]))
if (common_args_process(argc, argv, 2, 2, "link",
"<pathname>", CHECK_FS_RW))
return;
unlink_file_by_name(argv[1]);
@ -1027,18 +949,28 @@ void do_unlink(int argc, char *argv[])
void do_find_free_block(int argc, char *argv[])
{
blk_t free_blk, goal;
int count;
errcode_t retval;
char *tmp;
if ((argc > 2) || (argc==2 && *argv[1] == '?')) {
com_err(argv[0], 0, "Usage: find_free_block [goal]");
if ((argc > 3) || (argc==2 && *argv[1] == '?')) {
com_err(argv[0], 0, "Usage: find_free_block [count [goal]]");
return;
}
if (check_fs_open(argv[0]))
return;
if (argc > 1) {
goal = strtol(argv[1], &tmp, 0);
count = strtol(argv[1],&tmp,0);
if (*tmp) {
com_err(argv[0], 0, "Bad count - %s", argv[1]);
return;
}
} else
count = 1;
if (argc > 2) {
goal = strtol(argv[2], &tmp, 0);
if (*tmp) {
com_err(argv[0], 0, "Bad goal - %s", argv[1]);
return;
@ -1047,12 +979,18 @@ void do_find_free_block(int argc, char *argv[])
else
goal = current_fs->super->s_first_data_block;
retval = ext2fs_new_block(current_fs, goal, 0, &free_blk);
if (retval)
com_err("ext2fs_new_block", retval, "");
else
printf("Free block found: %d\n", free_blk);
printf("Free blocks found: ");
free_blk = goal - 1;
while (count-- > 0) {
retval = ext2fs_new_block(current_fs, free_blk + 1, 0,
&free_blk);
if (retval) {
com_err("ext2fs_new_block", retval, "");
return;
} else
printf("%d ", free_blk);
}
printf("\n");
}
void do_find_free_inode(int argc, char *argv[])
@ -1128,7 +1066,6 @@ static errcode_t copy_file(int fd, ext2_ino_t newfile)
}
}
retval = ext2fs_file_close(e2_file);
return retval;
fail:
@ -1146,17 +1083,13 @@ void do_write(int argc, char *argv[])
struct ext2_inode inode;
dgrp_t group;
if (check_fs_open(argv[0]))
return;
if (argc != 3) {
com_err(argv[0], 0, "Usage: write <nativefile> <newfile>");
return;
}
if (check_fs_read_write(argv[0]))
if (common_args_process(argc, argv, 3, 3, "write",
"<native file> <new file>", CHECK_FS_RW))
return;
fd = open(argv[1], O_RDONLY);
if (fd < 0) {
com_err(argv[1], fd, "");
com_err(argv[1], errno, "");
return;
}
if (fstat(fd, &statbuf) < 0) {
@ -1171,10 +1104,6 @@ void do_write(int argc, char *argv[])
close(fd);
return;
}
group = ext2fs_group_of_ino(current_fs, newfile);
current_fs->group_desc[group].bg_free_inodes_count--;
current_fs->super->s_free_inodes_count--;
ext2fs_mark_super_dirty(current_fs);
printf("Allocated inode: %u\n", newfile);
retval = ext2fs_link(current_fs, cwd, argv[2], newfile,
EXT2_FT_REG_FILE);
@ -1185,17 +1114,13 @@ void do_write(int argc, char *argv[])
}
if (ext2fs_test_inode_bitmap(current_fs->inode_map,newfile))
com_err(argv[0], 0, "Warning: inode already set");
ext2fs_mark_inode_bitmap(current_fs->inode_map,newfile);
ext2fs_mark_ib_dirty(current_fs);
ext2fs_inode_alloc_stats(current_fs, newfile, +1);
memset(&inode, 0, sizeof(inode));
inode.i_mode = statbuf.st_mode;
inode.i_atime = inode.i_ctime = inode.i_mtime = time(NULL);
inode.i_links_count = 1;
inode.i_size = statbuf.st_size;
retval = ext2fs_write_inode(current_fs, newfile, &inode);
if (retval) {
com_err(argv[0], retval, "while trying to write inode %d",
inode);
if (debugfs_write_inode(newfile, &inode, argv[0])) {
close(fd);
return;
}
@ -1218,6 +1143,7 @@ void do_mknod(int argc, char *argv[])
if (check_fs_open(argv[0]))
return;
if (argc < 3 || argv[2][1]) {
usage:
com_err(argv[0], 0, "Usage: mknod <name> [p| [c|b] <major> <minor>]");
return;
}
@ -1248,10 +1174,8 @@ void do_mknod(int argc, char *argv[])
if (major > 255 || minor > 255 || argv[3][0] || argv[4][0])
nr = 0;
}
if (argc != nr) {
com_err(argv[0], 0, "Usage: mknod <name> [p| [c|b] <major> <minor>]");
return;
}
if (argc != nr)
goto usage;
if (check_fs_read_write(argv[0]))
return;
retval = ext2fs_new_inode(current_fs, cwd, 010755, 0, &newfile);
@ -1283,11 +1207,8 @@ void do_mknod(int argc, char *argv[])
inode.i_atime = inode.i_ctime = inode.i_mtime = time(NULL);
inode.i_block[0] = major*256+minor;
inode.i_links_count = 1;
retval = ext2fs_write_inode(current_fs, newfile, &inode);
if (retval) {
com_err(argv[0], retval, "while trying to write inode %d", inode);
if (debugfs_write_inode(newfile, &inode, argv[0]))
return;
}
}
void do_mkdir(int argc, char *argv[])
@ -1297,15 +1218,8 @@ void do_mkdir(int argc, char *argv[])
char *name;
errcode_t retval;
if (check_fs_open(argv[0]))
return;
if (argc != 2) {
com_err(argv[0], 0, "Usage: mkdir <file>");
return;
}
if (check_fs_read_write(argv[0]))
if (common_args_process(argc, argv, 2, 2, "mkdir",
"<filename>", CHECK_FS_RW))
return;
cp = strrchr(argv[1], '/');
@ -1341,14 +1255,10 @@ static int release_blocks_proc(ext2_filsys fs, blk_t *blocknr,
int blockcnt, void *private)
{
blk_t block;
int group;
dgrp_t group;
block = *blocknr;
printf("%d ", block);
ext2fs_unmark_block_bitmap(fs->block_map,block);
group = ext2fs_group_of_blk(fs, block);
fs->group_desc[group].bg_free_blocks_count++;
fs->super->s_free_blocks_count++;
ext2fs_block_alloc_stats(fs, block, -1);
return 0;
}
@ -1357,22 +1267,17 @@ static void kill_file_by_inode(ext2_ino_t inode)
struct ext2_inode inode_buf;
int group;
ext2fs_read_inode(current_fs, inode, &inode_buf);
if (debugfs_read_inode(inode, &inode_buf, 0))
return;
inode_buf.i_dtime = time(NULL);
ext2fs_write_inode(current_fs, inode, &inode_buf);
if (debugfs_write_inode(inode, &inode_buf, 0))
return;
printf("Kill file by inode %u\n", inode);
ext2fs_block_iterate(current_fs, inode, 0, NULL,
release_blocks_proc, NULL);
printf("\n");
ext2fs_unmark_inode_bitmap(current_fs->inode_map, inode);
group = ext2fs_group_of_ino(current_fs, inode);
current_fs->group_desc[group].bg_free_inodes_count++;
current_fs->super->s_free_inodes_count++;
ext2fs_mark_super_dirty(current_fs);
ext2fs_mark_bb_dirty(current_fs);
ext2fs_mark_ib_dirty(current_fs);
ext2fs_inode_alloc_stats(current_fs, inode, -1);
}
@ -1380,21 +1285,9 @@ void do_kill_file(int argc, char *argv[])
{
ext2_ino_t inode_num;
if (argc != 2) {
com_err(argv[0], 0, "Usage: kill_file <file>");
return;
}
if (check_fs_open(argv[0]))
if (common_inode_args_process(argc, argv, &inode_num, CHECK_FS_RW))
return;
if (check_fs_read_write(argv[0]))
return;
inode_num = string_to_inode(argv[1]);
if (!inode_num) {
com_err(argv[0], 0, "Cannot find file");
return;
}
kill_file_by_inode(inode_num);
}
@ -1404,14 +1297,8 @@ void do_rm(int argc, char *argv[])
ext2_ino_t inode_num;
struct ext2_inode inode;
if (argc != 2) {
com_err(argv[0], 0, "Usage: rm <filename>");
return;
}
if (check_fs_open(argv[0]))
return;
if (check_fs_read_write(argv[0]))
if (common_args_process(argc, argv, 2, 2, "rm",
"<filename>", CHECK_FS_RW))
return;
retval = ext2fs_namei(current_fs, root, cwd, argv[1], &inode_num);
@ -1420,11 +1307,8 @@ void do_rm(int argc, char *argv[])
return;
}
retval = ext2fs_read_inode(current_fs,inode_num,&inode);
if (retval) {
com_err(argv[0], retval, "while reading file's inode");
if (debugfs_read_inode(inode_num, &inode, argv[0]))
return;
}
if (LINUX_S_ISDIR(inode.i_mode)) {
com_err(argv[0], 0, "file is a directory");
@ -1432,11 +1316,8 @@ void do_rm(int argc, char *argv[])
}
--inode.i_links_count;
retval = ext2fs_write_inode(current_fs,inode_num,&inode);
if (retval) {
com_err(argv[0], retval, "while writing inode");
if (debugfs_write_inode(inode_num, &inode, argv[0]))
return;
}
unlink_file_by_name(argv[1]);
if (inode.i_links_count == 0)
@ -1459,14 +1340,7 @@ void do_expand_dir(int argc, char *argv[])
ext2_ino_t inode;
int retval;
if (argc != 2) {
com_err(argv[0], 0, "Usage: expand_dir <file>");
return;
}
if (check_fs_open(argv[0]))
return;
inode = string_to_inode(argv[1]);
if (!inode)
if (common_inode_args_process(argc, argv, &inode, CHECK_FS_RW))
return;
retval = ext2fs_expand_dir(current_fs, inode);
@ -1546,7 +1420,6 @@ int main(int argc, char **argv)
blk_t superblock = 0;
blk_t blocksize = 0;
int catastrophic = 0;
char *tmp;
initialize_ext2_error_table();
fprintf (stderr, "debugfs %s (%s)\n", E2FSPROGS_VERSION,
@ -1567,20 +1440,12 @@ int main(int argc, char **argv)
open_flags |= EXT2_FLAG_RW;
break;
case 'b':
blocksize = strtoul(optarg, &tmp, 0);
if (*tmp) {
com_err(argv[0], 0,
"Bad block size - %s", optarg);
return 1;
}
blocksize = parse_ulong(argv[0], optarg,
"block size", 0);
break;
case 's':
superblock = strtoul(optarg, &tmp, 0);
if (*tmp) {
com_err(argv[0], 0,
"Bad superblock number - %s", optarg);
return 1;
}
superblock = parse_ulong(argv[0], optarg,
"superblock number", 0);
break;
case 'c':
catastrophic = 1;

View File

@ -12,6 +12,13 @@
#define const
#endif
/*
* Flags used by the common argument processing functions
*/
#define CHECK_FS_RW 0x0001
#define CHECK_FS_BITMAPS 0x0002
#define CHECK_FS_NOTOPEN 0x0004
extern ext2_filsys current_fs;
extern ext2_ino_t root, cwd;
@ -23,6 +30,20 @@ extern int check_fs_read_write(char *name);
extern int check_fs_bitmaps(char *name);
extern ext2_ino_t string_to_inode(char *str);
extern char *time_to_string(__u32);
extern unsigned long parse_ulong(const char *str, const char *cmd,
const char *descr, int *err);
extern int strtoblk(const char *cmd, const char *str, blk_t *ret);
extern int common_args_process(int argc, char *argv[], int min_argc,
int max_argc, const char *cmd,
const char *usage, int flags);
extern int common_inode_args_process(int argc, char *argv[],
ext2_ino_t *inode, int flags);
extern int common_block_args_process(int argc, char *argv[],
blk_t *block, int *count);
extern int debugfs_read_inode(ext2_ino_t ino, struct ext2_inode * inode,
const char *cmd);
extern int debugfs_write_inode(ext2_ino_t ino, struct ext2_inode * inode,
const char *cmd);
/* ss command functions */

View File

@ -104,12 +104,8 @@ static void dump_file(const char *cmdname, ext2_ino_t ino, int fd,
int nbytes;
unsigned int got;
retval = ext2fs_read_inode(current_fs, ino, &inode);
if (retval) {
com_err(cmdname, retval,
"while reading inode %u in dump_file", ino);
if (debugfs_read_inode(ino, &inode, cmdname))
return;
}
retval = ext2fs_file_open(current_fs, ino, 0, &e2_file);
if (retval) {
@ -307,11 +303,8 @@ static int rdump_dirent(struct ext2_dir_entry *dirent, int offset,
strncpy(name, dirent->name, thislen);
name[thislen] = 0;
retval = ext2fs_read_inode(current_fs, dirent->inode, &inode);
if (retval) {
com_err("rdump", retval, "while dumping %s/%s", dumproot, name);
if (debugfs_read_inode(dirent->inode, &inode, name))
return 0;
}
rdump_inode(dirent->inode, &inode, name, dumproot);
@ -327,12 +320,8 @@ void do_rdump(int argc, char **argv)
int i;
char *p;
if (argc != 3) {
com_err(argv[0], 0, "Usage: rdump <directory> <native directory>");
return;
}
if (check_fs_open(argv[0]))
if (common_args_process(argc, argv, 3, 3, "rdump",
"<directory> <native directory>", 0))
return;
ino = string_to_inode(argv[1]);
@ -350,11 +339,8 @@ void do_rdump(int argc, char **argv)
return;
}
retval = ext2fs_read_inode(current_fs, ino, &inode);
if (retval) {
com_err("rdump", retval, "while dumping %s", argv[1]);
if (debugfs_read_inode(ino, &inode, argv[1]))
return;
}
p = strrchr(argv[1], '/');
if (p)
@ -369,16 +355,7 @@ void do_cat(int argc, char **argv)
{
ext2_ino_t inode;
if (argc != 2) {
com_err(argv[0], 0, "Usage: cat <file>");
return;
}
if (check_fs_open(argv[0]))
return;
inode = string_to_inode(argv[1]);
if (!inode)
if (common_inode_args_process(argc, argv, &inode, 0))
return;
fflush(stdout);

View File

@ -61,7 +61,6 @@ void do_icheck(int argc, char **argv)
ext2_ino_t ino;
struct ext2_inode inode;
errcode_t retval;
char *tmp;
char *block_buf;
if (argc < 2) {
@ -86,11 +85,8 @@ void do_icheck(int argc, char **argv)
}
for (i=1; i < argc; i++) {
bw.barray[i-1].blk = strtoul(argv[i], &tmp, 0);
if (*tmp) {
com_err(argv[0], 0, "Bad block - %s", argv[i]);
if (strtoblk(argv[0], argv[i], &bw.barray[i-1].blk))
return;
}
}
bw.num_blocks = bw.blocks_left = argc-1;

View File

@ -203,13 +203,8 @@ void do_logdump(int argc, char **argv)
journal_source.where = JOURNAL_IS_EXTERNAL;
journal_source.fd = journal_fd;
} else if ((journal_inum = current_fs->super->s_journal_inum)) {
retval = ext2fs_read_inode(current_fs, journal_inum,
&journal_inode);
if (retval) {
com_err(argv[0], retval,
"while reading inode %u", journal_inum);
if (debugfs_read_inode(journal_inum, &journal_inode, argv[0]))
return;
}
retval = ext2fs_file_open(current_fs, journal_inum,
0, &journal_file);

View File

@ -15,6 +15,15 @@
#include <errno.h>
#endif
#include <sys/types.h>
#ifdef HAVE_GETOPT_H
#include <getopt.h>
#else
extern int optind;
extern char *optarg;
#endif
#ifdef HAVE_OPTRESET
extern int optreset; /* defined by BSD, but not others */
#endif
#include "debugfs.h"
@ -23,6 +32,7 @@
*/
#define LONG_OPT 0x0001
#define DELETED_OPT 0x0002
struct list_dir_struct {
FILE *f;
@ -33,73 +43,73 @@ struct list_dir_struct {
static const char *monstr[] = { "Jan", "Feb", "Mar", "Apr", "May", "Jun",
"Jul", "Aug", "Sep", "Oct", "Nov", "Dec"};
static void ls_l_file(struct list_dir_struct *ls, char *name, ext2_ino_t ino)
{
struct ext2_inode inode;
errcode_t retval;
struct tm *tm_p;
time_t modtime;
char datestr[80];
retval = ext2fs_read_inode(current_fs, ino, &inode);
if (retval) {
fprintf(ls->f, "%5ld --- error --- %s\n", retval, name);
return;
}
modtime = inode.i_mtime;
tm_p = localtime(&modtime);
sprintf(datestr, "%2d-%s-%4d %02d:%02d",
tm_p->tm_mday, monstr[tm_p->tm_mon], 1900 + tm_p->tm_year,
tm_p->tm_hour, tm_p->tm_min);
fprintf(ls->f, "%6u %6o %5d %5d ", ino, inode.i_mode,
inode.i_uid, inode.i_gid);
if (LINUX_S_ISDIR(inode.i_mode))
fprintf(ls->f, "%5d", inode.i_size);
else
fprintf(ls->f, "%5lld", inode.i_size |
((__u64)inode.i_size_high << 32));
fprintf (ls->f, " %s %s\n", datestr, name);
}
static void ls_file(struct list_dir_struct *ls, char *name,
ext2_ino_t ino, int rec_len)
{
char tmp[EXT2_NAME_LEN + 16];
int thislen;
sprintf(tmp, "%u (%d) %s ", ino, rec_len, name);
thislen = strlen(tmp);
if (ls->col + thislen > 80) {
fprintf(ls->f, "\n");
ls->col = 0;
}
fprintf(ls->f, "%s", tmp);
ls->col += thislen;
}
static int list_dir_proc(struct ext2_dir_entry *dirent,
static int list_dir_proc(ext2_ino_t dir,
int entry,
struct ext2_dir_entry *dirent,
int offset,
int blocksize,
char *buf,
void *private)
{
char name[EXT2_NAME_LEN];
struct ext2_inode inode;
ext2_ino_t ino;
errcode_t retval;
struct tm *tm_p;
time_t modtime;
char name[EXT2_NAME_LEN];
char tmp[EXT2_NAME_LEN + 16];
char datestr[80];
char lbr, rbr;
int thislen;
struct list_dir_struct *ls = (struct list_dir_struct *) private;
int thislen;
thislen = ((dirent->name_len & 0xFF) < EXT2_NAME_LEN) ?
(dirent->name_len & 0xFF) : EXT2_NAME_LEN;
strncpy(name, dirent->name, thislen);
name[thislen] = '\0';
ino = dirent->inode;
if (ls->options & LONG_OPT)
ls_l_file(ls, name, dirent->inode);
else
ls_file(ls, name, dirent->inode, dirent->rec_len);
if (entry == DIRENT_DELETED_FILE) {
lbr = '<';
rbr = '>';
ino = 0;
} else {
lbr = rbr = ' ';
}
if (ls->options & LONG_OPT) {
if (ino) {
if (debugfs_read_inode(ino, &inode, name))
return;
modtime = inode.i_mtime;
tm_p = localtime(&modtime);
sprintf(datestr, "%2d-%s-%4d %02d:%02d",
tm_p->tm_mday, monstr[tm_p->tm_mon],
1900 + tm_p->tm_year, tm_p->tm_hour,
tm_p->tm_min);
} else {
strcpy(datestr, " ");
memset(&inode, 0, sizeof(struct ext2_inode));
}
fprintf(ls->f, "%c%6u%c %6o %5d %5d ", lbr, ino, rbr,
inode.i_mode, inode.i_uid, inode.i_gid);
if (LINUX_S_ISDIR(inode.i_mode))
fprintf(ls->f, "%5d", inode.i_size);
else
fprintf(ls->f, "%5lld", inode.i_size |
((__u64)inode.i_size_high << 32));
fprintf (ls->f, " %s %s\n", datestr, name);
} else {
sprintf(tmp, "%c%u%c (%d) %s ", lbr, dirent->inode, rbr,
dirent->rec_len, name);
thislen = strlen(tmp);
if (ls->col + thislen > 80) {
fprintf(ls->f, "\n");
ls->col = 0;
}
fprintf(ls->f, "%s", tmp);
ls->col += thislen;
}
return 0;
}
@ -107,29 +117,48 @@ void do_list_dir(int argc, char *argv[])
{
ext2_ino_t inode;
int retval;
int c;
int flags;
struct list_dir_struct ls;
int argptr = 1;
ls.options = 0;
if (check_fs_open(argv[0]))
return;
if ((argc > argptr) && (argv[argptr][0] == '-')) {
argptr++;
ls.options = LONG_OPT;
optind = 0;
#ifdef HAVE_OPTRESET
optreset = 1; /* Makes BSD getopt happy */
#endif
while ((c = getopt (argc, argv, "dl")) != EOF) {
switch (c) {
case 'l':
ls.options |= LONG_OPT;
break;
case 'd':
ls.options |= DELETED_OPT;
break;
}
}
if (argc <= argptr)
if (argc > optind+1) {
com_err(0, 0, "Usage: ls [-l] [-d] file");
return;
}
if (argc == optind)
inode = cwd;
else
inode = string_to_inode(argv[argptr]);
inode = string_to_inode(argv[optind]);
if (!inode)
return;
ls.f = open_pager();
ls.col = 0;
retval = ext2fs_dir_iterate(current_fs, inode,
DIRENT_FLAG_INCLUDE_EMPTY,
flags = DIRENT_FLAG_INCLUDE_EMPTY;
if (ls.options & DELETED_OPT)
flags |= DIRENT_FLAG_INCLUDE_REMOVED;
retval = ext2fs_dir_iterate2(current_fs, inode, flags,
0, list_dir_proc, &ls);
fprintf(ls.f, "\n");
close_pager(ls.f);

View File

@ -1,8 +1,9 @@
/*
* undelete.c --- routines to try to help a user recover a deleted file.
* lsdel.c --- routines to try to help a user recover a deleted file.
*
* Copyright (C) 1994 Theodore Ts'o. This file may be redistributed
* under the terms of the GNU Public License.
* Copyright (C) 1994, 1995, 1996, 1997, 1998, 1999, 2000, 2001
* Theodore Ts'o. This file may be redistributed under the terms of
* the GNU Public License.
*/
#include <stdio.h>
@ -78,13 +79,20 @@ void do_lsdel(int argc, char **argv)
errcode_t retval;
char *block_buf;
int i;
long secs = 0;
char *tmp;
time_t now = time(0);
if (common_args_process(argc, argv, 1, 2, "ls_deleted_inodes",
"[secs]", 0))
return;
if (argc > 1) {
com_err(argv[0], 0, "Usage: ls_deleted_inodes\n");
return;
secs = strtol(argv[1],&tmp,0);
if (*tmp) {
com_err(argv[0], 0, "Bad time - %s",argv[1]);
return;
}
}
if (check_fs_open(argv[0]))
return;
max_delarray = 100;
num_delarray = 0;
@ -118,7 +126,8 @@ void do_lsdel(int argc, char **argv)
}
while (ino) {
if (inode.i_dtime == 0)
if ((inode.i_dtime == 0) ||
(secs && ((now - secs) > inode.i_dtime)))
goto next;
lsd.inode = ino;
@ -170,7 +179,6 @@ void do_lsdel(int argc, char **argv)
}
}
printf("%d deleted inodes found.\n", num_delarray);
printf(" Inode Owner Mode Size Blocks Time deleted\n");
qsort(delarray, num_delarray, sizeof(struct deleted_info),
@ -182,6 +190,7 @@ void do_lsdel(int argc, char **argv)
delarray[i].free_blocks, delarray[i].num_blocks,
time_to_string(delarray[i].dtime));
}
printf("%d deleted inodes found.\n", num_delarray);
error_out:
free(block_buf);

View File

@ -169,7 +169,7 @@ static errcode_t parse_string(struct super_set_info *info, char *arg)
return 0;
}
static void print_possible_fields()
static void print_possible_fields(void)
{
struct super_set_info *ss;
const char *type;
@ -190,9 +190,9 @@ static void print_possible_fields()
void do_set_super(int argc, char *argv[])
{
const char *usage = "Usage: set_super <field> <value>\n"
"\t\"set_super -l\" will list the names of superblock fields "
"which\n\tcan be set.";
const char *usage = "<field> <value>\n"
"\t\"set_super_value -l\" will list the names of "
"superblock fields\n\twhich can be set.";
static struct super_set_info *ss;
if ((argc == 2) && !strcmp(argv[1], "-l")) {
@ -200,17 +200,10 @@ void do_set_super(int argc, char *argv[])
return;
}
if (check_fs_open(argv[0]))
if (common_args_process(argc, argv, 3, 3, "set_super_value",
usage, CHECK_FS_RW))
return;
if (argc != 3) {
com_err(argv[0], 0, usage);
return;
}
if (check_fs_read_write(argv[0]))
return;
if ((ss = find_field(argv[1])) == 0) {
com_err(argv[0], 0, "invalid field specifier: %s", argv[1]);
return;

View File

@ -132,6 +132,135 @@ char *time_to_string(__u32 cl)
return ctime(&t);
}
/*
* This function will convert a string to an unsigned long, printing
* an error message if it fails, and returning success or failure in err.
*/
unsigned long parse_ulong(const char *str, const char *cmd,
const char *descr, int *err)
{
char *tmp;
unsigned long ret;
ret = strtoul(str, &tmp, 0);
if (*tmp == 0) {
if (*err)
*err = 0;
return ret;
}
com_err(cmd, 0, "Bad %s - %s", descr, str);
if (*err)
*err = 1;
else
exit(1);
return 0;
}
/*
* This function will convert a string to a block number. It returns
* 0 on success, 1 on failure.
*/
int strtoblk(const char *cmd, const char *str, blk_t *ret)
{
blk_t blk;
int err;
blk = parse_ulong(str, cmd, "block number", &err);
*ret = blk;
if (err == 0 && blk == 0) {
com_err(cmd, 0, "Invalid block number 0");
err = 1;
}
return err;
}
/*
* This is a common helper function used by the command processing
* routines
*/
int common_args_process(int argc, char *argv[], int min_argc, int max_argc,
const char *cmd, const char *usage, int flags)
{
if (argc < min_argc || argc > max_argc) {
com_err(argv[0], 0, "Usage: %s %s", cmd, usage);
return 1;
}
if (flags & CHECK_FS_NOTOPEN) {
if (check_fs_not_open(argv[0]))
return 1;
} else {
if (check_fs_open(argv[0]))
return 1;
}
if ((flags & CHECK_FS_RW) && check_fs_read_write(argv[0]))
return 1;
if ((flags & CHECK_FS_BITMAPS) && check_fs_bitmaps(argv[0]))
return 1;
return 0;
}
/*
* This is a helper function used by do_stat, do_freei, do_seti, and
* do_testi, etc. Basically, any command which takes a single
* argument which is a file/inode number specifier.
*/
int common_inode_args_process(int argc, char *argv[],
ext2_ino_t *inode, int flags)
{
if (common_args_process(argc, argv, 2, 2, argv[0], "<file>", flags))
return 1;
*inode = string_to_inode(argv[1]);
if (!*inode)
return 1;
return 0;
}
/*
* This is a helper function used by do_freeb, do_setb, and do_testb
*/
int common_block_args_process(int argc, char *argv[],
blk_t *block, int *count)
{
int err;
if (common_args_process(argc, argv, 2, 3, argv[0],
"<block> [count]", CHECK_FS_BITMAPS))
return 1;
if (strtoblk(argv[0], argv[1], block))
return 1;
if (argc > 2) {
*count = parse_ulong(argv[0], argv[2], "count", &err);
if (err)
return 1;
}
return 0;
}
int debugfs_read_inode(ext2_ino_t ino, struct ext2_inode * inode,
const char *cmd)
{
int retval;
retval = ext2fs_read_inode(current_fs, ino, inode);
if (retval) {
com_err(cmd, retval, "while reading inode %u", ino);
return 1;
}
return 0;
}
int debugfs_write_inode(ext2_ino_t ino, struct ext2_inode * inode,
const char *cmd)
{
int retval;
retval = ext2fs_write_inode(current_fs, ino, inode);
if (retval) {
com_err(cmd, retval, "while writing inode %u", ino);
return 1;
}
return 0;
}