mirror of
https://git.kernel.org/pub/scm/fs/ext2/e2fsprogs.git
synced 2024-11-24 10:33:35 +08:00
a25fffae5c
In response to reviewer comments, commit fe56188b07
included changes
that modified some of the code used to output error messages when
checking user-supplied block numbers. These changes converted calls
to parse_ulonglong() to calls to strtoblk(). Because strtoblk() calls
parse_ulonglong(), and both output error messages, two redundant and
relatively generic messages were output on each error.
Fix this by removing the error message output from strtoblk(), and
extending it to accept an optional error message argument that it
supplies in lieu of a default to parse_ulonglong(). Also, revert to
the more descriptive original error messages with mods per reviewer
comments, and fix an error message in do_replace_node().
Signed-off-by: Eric Whitney <enwlinux@gmail.com>
Signed-off-by: "Theodore Ts'o" <tytso@mit.edu>
538 lines
11 KiB
C
538 lines
11 KiB
C
/*
|
|
* extent_inode.c --- direct extent tree manipulation
|
|
*
|
|
* Copyright (C) 2012 Theodore Ts'o. This file may be redistributed
|
|
* under the terms of the GNU Public License.
|
|
*/
|
|
|
|
#include "config.h"
|
|
#include <stdio.h>
|
|
#include <unistd.h>
|
|
#include <stdlib.h>
|
|
#include <ctype.h>
|
|
#include <string.h>
|
|
#include <time.h>
|
|
#ifdef HAVE_ERRNO_H
|
|
#include <errno.h>
|
|
#endif
|
|
#include <sys/types.h>
|
|
#ifdef HAVE_GETOPT_H
|
|
#include <getopt.h>
|
|
#else
|
|
extern int optind;
|
|
extern char *optarg;
|
|
#endif
|
|
|
|
#include "debugfs.h"
|
|
|
|
static ext2_ino_t current_ino;
|
|
static ext2_extent_handle_t current_handle;
|
|
|
|
static void dbg_print_extent(char *desc, struct ext2fs_extent *extent)
|
|
{
|
|
if (desc)
|
|
printf("%s: ", desc);
|
|
printf("extent: lblk %llu--%llu, len %u, pblk %llu, flags: ",
|
|
extent->e_lblk, extent->e_lblk + extent->e_len - 1,
|
|
extent->e_len, extent->e_pblk);
|
|
if (extent->e_flags & EXT2_EXTENT_FLAGS_LEAF)
|
|
fputs("LEAF ", stdout);
|
|
if (extent->e_flags & EXT2_EXTENT_FLAGS_UNINIT)
|
|
fputs("UNINIT ", stdout);
|
|
if (extent->e_flags & EXT2_EXTENT_FLAGS_SECOND_VISIT)
|
|
fputs("2ND_VISIT ", stdout);
|
|
if (!extent->e_flags)
|
|
fputs("(none)", stdout);
|
|
fputc('\n', stdout);
|
|
|
|
}
|
|
|
|
static int common_extent_args_process(int argc, char *argv[], int min_argc,
|
|
int max_argc, const char *cmd,
|
|
const char *usage, int flags)
|
|
{
|
|
if (common_args_process(argc, argv, min_argc, max_argc, cmd,
|
|
usage, flags))
|
|
return 1;
|
|
|
|
if (!current_handle) {
|
|
com_err(cmd, 0, "Extent handle not open");
|
|
return 1;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
static char *orig_prompt, *extent_prompt;
|
|
|
|
void do_extent_open(int argc, char *argv[])
|
|
{
|
|
ext2_ino_t inode;
|
|
int ret;
|
|
errcode_t retval;
|
|
char *cp;
|
|
|
|
if (check_fs_open(argv[0]))
|
|
return;
|
|
|
|
if (argc == 1) {
|
|
if (current_ino)
|
|
printf("Current inode is %d\n", current_ino);
|
|
else
|
|
printf("No current inode\n");
|
|
return;
|
|
}
|
|
|
|
if (common_inode_args_process(argc, argv, &inode, 0))
|
|
return;
|
|
|
|
current_ino = 0;
|
|
|
|
retval = ext2fs_extent_open(current_fs, inode, ¤t_handle);
|
|
if (retval) {
|
|
com_err(argv[1], retval, "while opening extent handle");
|
|
return;
|
|
}
|
|
|
|
current_ino = inode;
|
|
|
|
orig_prompt = ss_get_prompt(sci_idx);
|
|
extent_prompt = malloc(strlen(orig_prompt) + 32);
|
|
strcpy(extent_prompt, orig_prompt);
|
|
cp = strchr(extent_prompt, ':');
|
|
if (cp)
|
|
*cp = 0;
|
|
sprintf(extent_prompt + strlen(extent_prompt), " (extent ino %d): ",
|
|
current_ino);
|
|
ss_add_request_table(sci_idx, &extent_cmds, 1, &ret);
|
|
ss_set_prompt(sci_idx, extent_prompt);
|
|
return;
|
|
}
|
|
|
|
void do_extent_close(int argc, char *argv[])
|
|
{
|
|
int ret;
|
|
|
|
if (common_args_process(argc, argv, 1, 1,
|
|
"extent_close", "", 0))
|
|
return;
|
|
|
|
if (!current_handle) {
|
|
com_err(argv[0], 0, "Extent handle not open");
|
|
return;
|
|
}
|
|
|
|
ext2fs_extent_free(current_handle);
|
|
current_handle = NULL;
|
|
current_ino = 0;
|
|
ss_delete_request_table(sci_idx, &extent_cmds, &ret);
|
|
ss_set_prompt(sci_idx, orig_prompt);
|
|
free(extent_prompt);
|
|
extent_prompt = NULL;
|
|
}
|
|
|
|
static void generic_goto_node(const char *my_name, int argc,
|
|
char **argv, int op)
|
|
{
|
|
struct ext2fs_extent extent;
|
|
errcode_t retval;
|
|
|
|
if (my_name && common_args_process(argc, argv, 1, 1,
|
|
my_name, "", 0))
|
|
return;
|
|
|
|
if (!current_handle) {
|
|
com_err(argv[0], 0, "Extent handle not open");
|
|
return;
|
|
}
|
|
|
|
retval = ext2fs_extent_get(current_handle, op, &extent);
|
|
if (retval) {
|
|
com_err(argv[0], retval, 0);
|
|
return;
|
|
}
|
|
dbg_print_extent(0, &extent);
|
|
}
|
|
|
|
void do_current_node(int argc, char *argv[])
|
|
{
|
|
generic_goto_node("current_node", argc, argv, EXT2_EXTENT_CURRENT);
|
|
}
|
|
|
|
void do_root_node(int argc, char *argv[])
|
|
{
|
|
generic_goto_node("root_node", argc, argv, EXT2_EXTENT_ROOT);
|
|
}
|
|
|
|
void do_last_leaf(int argc, char *argv[])
|
|
{
|
|
generic_goto_node("last_leaf", argc, argv, EXT2_EXTENT_LAST_LEAF);
|
|
}
|
|
|
|
void do_first_sib(int argc, char *argv[])
|
|
{
|
|
generic_goto_node("first_sib", argc, argv, EXT2_EXTENT_FIRST_SIB);
|
|
}
|
|
|
|
void do_last_sib(int argc, char *argv[])
|
|
{
|
|
generic_goto_node("next_sib", argc, argv, EXT2_EXTENT_LAST_SIB);
|
|
}
|
|
|
|
void do_next_sib(int argc, char *argv[])
|
|
{
|
|
generic_goto_node("next_sib", argc, argv, EXT2_EXTENT_NEXT_SIB);
|
|
}
|
|
|
|
void do_prev_sib(int argc, char *argv[])
|
|
{
|
|
generic_goto_node("prev_sib", argc, argv, EXT2_EXTENT_PREV_SIB);
|
|
}
|
|
|
|
void do_next_leaf(int argc, char *argv[])
|
|
{
|
|
generic_goto_node("next_leaf", argc, argv, EXT2_EXTENT_NEXT_LEAF);
|
|
}
|
|
|
|
void do_prev_leaf(int argc, char *argv[])
|
|
{
|
|
generic_goto_node("prev_leaf", argc, argv, EXT2_EXTENT_PREV_LEAF);
|
|
}
|
|
|
|
void do_next(int argc, char *argv[])
|
|
{
|
|
generic_goto_node("next", argc, argv, EXT2_EXTENT_NEXT);
|
|
}
|
|
|
|
void do_prev(int argc, char *argv[])
|
|
{
|
|
generic_goto_node("prev", argc, argv, EXT2_EXTENT_PREV);
|
|
}
|
|
|
|
void do_up(int argc, char *argv[])
|
|
{
|
|
generic_goto_node("up", argc, argv, EXT2_EXTENT_UP);
|
|
}
|
|
|
|
void do_down(int argc, char *argv[])
|
|
{
|
|
generic_goto_node("down", argc, argv, EXT2_EXTENT_DOWN);
|
|
}
|
|
|
|
void do_delete_node(int argc, char *argv[])
|
|
{
|
|
struct ext2fs_extent extent;
|
|
errcode_t retval;
|
|
|
|
if (common_extent_args_process(argc, argv, 1, 1, "delete_node",
|
|
"", CHECK_FS_RW | CHECK_FS_BITMAPS))
|
|
return;
|
|
|
|
retval = ext2fs_extent_delete(current_handle, 0);
|
|
if (retval) {
|
|
com_err(argv[0], retval, 0);
|
|
return;
|
|
}
|
|
|
|
retval = ext2fs_extent_get(current_handle, EXT2_EXTENT_CURRENT,
|
|
&extent);
|
|
if (retval)
|
|
return;
|
|
dbg_print_extent(0, &extent);
|
|
}
|
|
|
|
void do_replace_node(int argc, char *argv[])
|
|
{
|
|
const char *usage = "[--uninit] <lblk> <len> <pblk>";
|
|
errcode_t retval;
|
|
struct ext2fs_extent extent;
|
|
int err;
|
|
|
|
if (common_extent_args_process(argc, argv, 3, 5, "replace_node",
|
|
usage, CHECK_FS_RW | CHECK_FS_BITMAPS))
|
|
return;
|
|
|
|
extent.e_flags = 0;
|
|
|
|
if (!strcmp(argv[1], "--uninit")) {
|
|
argc--;
|
|
argv++;
|
|
extent.e_flags |= EXT2_EXTENT_FLAGS_UNINIT;
|
|
}
|
|
|
|
if (argc != 4) {
|
|
fprintf(stderr, "Usage: %s %s\n", argv[0], usage);
|
|
return;
|
|
}
|
|
|
|
err = strtoblk(argv[0], argv[1], "logical block", &extent.e_lblk);
|
|
if (err)
|
|
return;
|
|
|
|
extent.e_len = parse_ulong(argv[2], argv[0], "length", &err);
|
|
if (err)
|
|
return;
|
|
|
|
err = strtoblk(argv[0], argv[3], "physical block", &extent.e_pblk);
|
|
if (err)
|
|
return;
|
|
|
|
retval = ext2fs_extent_replace(current_handle, 0, &extent);
|
|
if (retval) {
|
|
com_err(argv[0], retval, 0);
|
|
return;
|
|
}
|
|
generic_goto_node(NULL, argc, argv, EXT2_EXTENT_CURRENT);
|
|
}
|
|
|
|
void do_split_node(int argc, char *argv[])
|
|
{
|
|
errcode_t retval;
|
|
|
|
if (common_extent_args_process(argc, argv, 1, 1, "split_node",
|
|
"", CHECK_FS_RW | CHECK_FS_BITMAPS))
|
|
return;
|
|
|
|
retval = ext2fs_extent_node_split(current_handle);
|
|
if (retval) {
|
|
com_err(argv[0], retval, 0);
|
|
return;
|
|
}
|
|
generic_goto_node(NULL, argc, argv, EXT2_EXTENT_CURRENT);
|
|
}
|
|
|
|
void do_insert_node(int argc, char *argv[])
|
|
{
|
|
const char *usage = "[--after] [--uninit] <lblk> <len> <pblk>";
|
|
errcode_t retval;
|
|
struct ext2fs_extent extent;
|
|
char *cmd;
|
|
int err;
|
|
int flags = 0;
|
|
|
|
if (common_extent_args_process(argc, argv, 3, 6, "insert_node",
|
|
usage, CHECK_FS_RW | CHECK_FS_BITMAPS))
|
|
return;
|
|
|
|
cmd = argv[0];
|
|
|
|
extent.e_flags = 0;
|
|
|
|
while (argc > 2) {
|
|
if (!strcmp(argv[1], "--after")) {
|
|
argc--;
|
|
argv++;
|
|
flags |= EXT2_EXTENT_INSERT_AFTER;
|
|
continue;
|
|
}
|
|
if (!strcmp(argv[1], "--uninit")) {
|
|
argc--;
|
|
argv++;
|
|
extent.e_flags |= EXT2_EXTENT_FLAGS_UNINIT;
|
|
continue;
|
|
}
|
|
break;
|
|
}
|
|
|
|
if (argc != 4) {
|
|
fprintf(stderr, "usage: %s %s\n", cmd, usage);
|
|
return;
|
|
}
|
|
|
|
err = strtoblk(cmd, argv[1], "logical block", &extent.e_lblk);
|
|
if (err)
|
|
return;
|
|
|
|
extent.e_len = parse_ulong(argv[2], cmd, "length", &err);
|
|
if (err)
|
|
return;
|
|
|
|
err = strtoblk(cmd, argv[3], "physical block", &extent.e_pblk);
|
|
if (err)
|
|
return;
|
|
|
|
retval = ext2fs_extent_insert(current_handle, flags, &extent);
|
|
if (retval) {
|
|
com_err(cmd, retval, 0);
|
|
return;
|
|
}
|
|
generic_goto_node(NULL, argc, argv, EXT2_EXTENT_CURRENT);
|
|
}
|
|
|
|
void do_set_bmap(int argc, char **argv)
|
|
{
|
|
const char *usage = "[--uninit] <lblk> <pblk>";
|
|
struct ext2fs_extent extent;
|
|
errcode_t retval;
|
|
blk64_t logical;
|
|
blk64_t physical;
|
|
char *cmd = argv[0];
|
|
int flags = 0;
|
|
int err;
|
|
|
|
if (common_extent_args_process(argc, argv, 3, 5, "set_bmap",
|
|
usage, CHECK_FS_RW | CHECK_FS_BITMAPS))
|
|
return;
|
|
|
|
if (argc > 2 && !strcmp(argv[1], "--uninit")) {
|
|
argc--;
|
|
argv++;
|
|
flags |= EXT2_EXTENT_SET_BMAP_UNINIT;
|
|
}
|
|
|
|
if (argc != 3) {
|
|
fprintf(stderr, "Usage: %s %s\n", cmd, usage);
|
|
return;
|
|
}
|
|
|
|
err = strtoblk(cmd, argv[1], "logical block", &logical);
|
|
if (err)
|
|
return;
|
|
|
|
err = strtoblk(cmd, argv[2], "physical block", &physical);
|
|
if (err)
|
|
return;
|
|
|
|
retval = ext2fs_extent_set_bmap(current_handle, logical,
|
|
physical, flags);
|
|
if (retval) {
|
|
com_err(cmd, retval, 0);
|
|
return;
|
|
}
|
|
|
|
retval = ext2fs_extent_get(current_handle, EXT2_EXTENT_CURRENT,
|
|
&extent);
|
|
if (retval)
|
|
return;
|
|
dbg_print_extent(0, &extent);
|
|
}
|
|
|
|
void do_print_all(int argc, char **argv)
|
|
{
|
|
const char *usage = "[--leaf-only|--reverse|--reverse-leaf]";
|
|
struct ext2fs_extent extent;
|
|
errcode_t retval;
|
|
errcode_t end_err = EXT2_ET_EXTENT_NO_NEXT;
|
|
int op = EXT2_EXTENT_NEXT;
|
|
int first_op = EXT2_EXTENT_ROOT;
|
|
|
|
|
|
if (common_extent_args_process(argc, argv, 1, 2, "print_all",
|
|
usage, 0))
|
|
return;
|
|
|
|
if (argc == 2) {
|
|
if (!strcmp(argv[1], "--leaf-only"))
|
|
op = EXT2_EXTENT_NEXT_LEAF;
|
|
else if (!strcmp(argv[1], "--reverse")) {
|
|
op = EXT2_EXTENT_PREV;
|
|
first_op = EXT2_EXTENT_LAST_LEAF;
|
|
end_err = EXT2_ET_EXTENT_NO_PREV;
|
|
} else if (!strcmp(argv[1], "--reverse-leaf")) {
|
|
op = EXT2_EXTENT_PREV_LEAF;
|
|
first_op = EXT2_EXTENT_LAST_LEAF;
|
|
end_err = EXT2_ET_EXTENT_NO_PREV;
|
|
} else {
|
|
fprintf(stderr, "Usage: %s %s\n", argv[0], usage);
|
|
return;
|
|
}
|
|
}
|
|
|
|
retval = ext2fs_extent_get(current_handle, first_op, &extent);
|
|
if (retval) {
|
|
com_err(argv[0], retval, 0);
|
|
return;
|
|
}
|
|
dbg_print_extent(0, &extent);
|
|
|
|
while (1) {
|
|
retval = ext2fs_extent_get(current_handle, op, &extent);
|
|
if (retval == end_err)
|
|
break;
|
|
|
|
if (retval) {
|
|
com_err(argv[0], retval, 0);
|
|
return;
|
|
}
|
|
dbg_print_extent(0, &extent);
|
|
}
|
|
}
|
|
|
|
void do_fix_parents(int argc, char **argv)
|
|
{
|
|
errcode_t retval;
|
|
|
|
if (common_extent_args_process(argc, argv, 1, 1, "fix_parents", "",
|
|
CHECK_FS_RW))
|
|
return;
|
|
|
|
retval = ext2fs_extent_fix_parents(current_handle);
|
|
if (retval) {
|
|
com_err(argv[0], retval, 0);
|
|
return;
|
|
}
|
|
}
|
|
|
|
void do_info(int argc, char **argv)
|
|
{
|
|
struct ext2fs_extent extent;
|
|
struct ext2_extent_info info;
|
|
errcode_t retval;
|
|
|
|
if (common_extent_args_process(argc, argv, 1, 1, "info", "", 0))
|
|
return;
|
|
|
|
retval = ext2fs_extent_get_info(current_handle, &info);
|
|
if (retval) {
|
|
com_err(argv[0], retval, 0);
|
|
return;
|
|
}
|
|
|
|
retval = ext2fs_extent_get(current_handle,
|
|
EXT2_EXTENT_CURRENT, &extent);
|
|
if (retval) {
|
|
com_err(argv[0], retval, 0);
|
|
return;
|
|
}
|
|
|
|
dbg_print_extent(0, &extent);
|
|
|
|
printf("Current handle location: %d/%d (max: %d, bytes %d), level %d/%d\n",
|
|
info.curr_entry, info.num_entries, info.max_entries,
|
|
info.bytes_avail, info.curr_level, info.max_depth);
|
|
printf("\tmax lblk: %llu, max pblk: %llu\n", info.max_lblk,
|
|
info.max_pblk);
|
|
printf("\tmax_len: %u, max_uninit_len: %u\n", info.max_len,
|
|
info.max_uninit_len);
|
|
}
|
|
|
|
void do_goto_block(int argc, char **argv)
|
|
{
|
|
errcode_t retval;
|
|
blk64_t blk;
|
|
int level = 0, err;
|
|
|
|
if (common_extent_args_process(argc, argv, 2, 3, "goto_block",
|
|
"block [level]", 0))
|
|
return;
|
|
|
|
if (strtoblk(argv[0], argv[1], NULL, &blk))
|
|
return;
|
|
|
|
if (argc == 3) {
|
|
level = parse_ulong(argv[2], argv[0], "level", &err);
|
|
if (err)
|
|
return;
|
|
}
|
|
|
|
retval = ext2fs_extent_goto2(current_handle, level, (blk64_t) blk);
|
|
|
|
if (retval) {
|
|
com_err(argv[0], retval,
|
|
"while trying to go to block %llu, level %d",
|
|
(unsigned long long) blk, level);
|
|
return;
|
|
}
|
|
|
|
generic_goto_node(NULL, argc, argv, EXT2_EXTENT_CURRENT);
|
|
}
|