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.
diff --git a/debugfs/ChangeLog b/debugfs/ChangeLog
index 8cdee82..82149ea 100644
--- a/debugfs/ChangeLog
+++ b/debugfs/ChangeLog
@@ -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
diff --git a/debugfs/debug_cmds.ct b/debugfs/debug_cmds.ct
index 4cd034f..63f9c0a 100644
--- a/debugfs/debug_cmds.ct
+++ b/debugfs/debug_cmds.ct
@@ -103,6 +103,9 @@
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;
diff --git a/debugfs/debugfs.8.in b/debugfs/debugfs.8.in
index 77a893a..231a3e3 100644
--- a/debugfs/debugfs.8.in
+++ b/debugfs/debugfs.8.in
@@ -185,8 +185,10 @@
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 @@
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 @@
.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 @@
.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 @@
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
diff --git a/debugfs/debugfs.c b/debugfs/debugfs.c
index ebd02f2..df08610 100644
--- a/debugfs/debugfs.c
+++ b/debugfs/debugfs.c
@@ -95,11 +95,10 @@
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 @@
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_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 @@
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(¶m, 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, ¶m,
unix_io_manager, ¤t_fs);
if (retval) {
@@ -491,29 +474,17 @@
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 @@
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 @@
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 @@
{
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 @@
{
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 @@
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 @@
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 @@
&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 @@
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 @@
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 @@
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 @@
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_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 @@
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 @@
}
}
retval = ext2fs_file_close(e2_file);
-
return retval;
fail:
@@ -1146,17 +1083,13 @@
struct ext2_inode inode;
dgrp_t group;
- if (check_fs_open(argv[0]))
+ if (common_args_process(argc, argv, 3, 3, "write",
+ "<native file> <new file>", CHECK_FS_RW))
return;
- if (argc != 3) {
- com_err(argv[0], 0, "Usage: write <nativefile> <newfile>");
- return;
- }
- if (check_fs_read_write(argv[0]))
- 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 @@
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 @@
}
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
{
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 @@
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 @@
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 @@
}
--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 @@
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 @@
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 @@
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;
diff --git a/debugfs/debugfs.h b/debugfs/debugfs.h
index 1cc308a..1634875 100644
--- a/debugfs/debugfs.h
+++ b/debugfs/debugfs.h
@@ -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_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 */
diff --git a/debugfs/dump.c b/debugfs/dump.c
index e290c14..abe8ea6 100644
--- a/debugfs/dump.c
+++ b/debugfs/dump.c
@@ -104,12 +104,8 @@
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 @@
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 @@
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 @@
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 @@
{
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);
diff --git a/debugfs/icheck.c b/debugfs/icheck.c
index fa065c6..941c749 100644
--- a/debugfs/icheck.c
+++ b/debugfs/icheck.c
@@ -61,7 +61,6 @@
ext2_ino_t ino;
struct ext2_inode inode;
errcode_t retval;
- char *tmp;
char *block_buf;
if (argc < 2) {
@@ -86,11 +85,8 @@
}
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;
diff --git a/debugfs/logdump.c b/debugfs/logdump.c
index 34552d0..c7a5ca9 100644
--- a/debugfs/logdump.c
+++ b/debugfs/logdump.c
@@ -203,13 +203,8 @@
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);
diff --git a/debugfs/ls.c b/debugfs/ls.c
index e24bdb3..4459f30 100644
--- a/debugfs/ls.c
+++ b/debugfs/ls.c
@@ -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 @@
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 @@
{
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);
diff --git a/debugfs/lsdel.c b/debugfs/lsdel.c
index 6100434..251e06e 100644
--- a/debugfs/lsdel.c
+++ b/debugfs/lsdel.c
@@ -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 @@
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 @@
}
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 @@
}
}
- 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 @@
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);
diff --git a/debugfs/setsuper.c b/debugfs/setsuper.c
index d48bd04..ce574d0 100644
--- a/debugfs/setsuper.c
+++ b/debugfs/setsuper.c
@@ -169,7 +169,7 @@
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 @@
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 @@
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;
diff --git a/debugfs/util.c b/debugfs/util.c
index 6fcbac2..183a5f6 100644
--- a/debugfs/util.c
+++ b/debugfs/util.c
@@ -132,6 +132,135 @@
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;
+}