From 2e8d40d562ec93d68505800a46c5b9dcc229264e Mon Sep 17 00:00:00 2001 From: Theodore Ts'o Date: Sat, 27 May 2000 15:15:40 +0000 Subject: [PATCH] ChangeLog, debug_cmds.ct, debugfs.8.in, debugfs.c, dump.c, ls.c: debugfs.8.in: Documented new behaviour. ls.c (ls_l_file): Fix Y2K bug -- was printing 22-May-100 for recent files. Switched to 4-digit years. dump.c, debug_cmds.ct (do_rdump): Add new debugfs command "rdump", which recursively dumps a directory and its contents. (fix_perms): New function. Break permission-fixing code out of dump_file() so it can be called by rdump code as well. (dump_file): Call fix_perms(). debugfs.c, debug_cmds.ct (do_lcd): Add new debugfs command "lcd", which changes the cwd on the native filesystem. debugfs.c (open_filesystem): Extra args for superblock, blocksize, and catastrophic mode. Changed callers. (do_open_filesys, main): Accept new -b, -s, -c options for open_filesystem. ChangeLog, mkdir.c: mkdir.c (ext2fs_mkdir): Read the parent directory's inode earlier, so that if there's an error reading it, we can more cleanly back out of the operation. version.h: Update version file for WIP release. --- debugfs/ChangeLog | 23 +++++ debugfs/debug_cmds.ct | 6 ++ debugfs/debugfs.8.in | 54 +++++++++++- debugfs/debugfs.c | 117 +++++++++++++++++++++---- debugfs/dump.c | 233 +++++++++++++++++++++++++++++++++++++++++++++----- debugfs/ls.c | 4 +- lib/ext2fs/ChangeLog | 6 ++ lib/ext2fs/mkdir.c | 19 ++-- version.h | 2 +- 9 files changed, 415 insertions(+), 49 deletions(-) diff --git a/debugfs/ChangeLog b/debugfs/ChangeLog index 8026e87b..ae9e3b78 100644 --- a/debugfs/ChangeLog +++ b/debugfs/ChangeLog @@ -1,3 +1,26 @@ +2000-05-23 Aaron Crane + + * debugfs.8.in: Documented new behaviour. + + * ls.c (ls_l_file): Fix Y2K bug -- was printing 22-May-100 for + recent files. Switched to 4-digit years. + + * dump.c, debug_cmds.ct (do_rdump): Add new debugfs command + "rdump", which recursively dumps a directory and its + contents. + (fix_perms): New function. Break permission-fixing + code out of dump_file() so it can be called by rdump + code as well. + (dump_file): Call fix_perms(). + + * debugfs.c, debug_cmds.ct (do_lcd): Add new debugfs command + "lcd", which changes the cwd on the native filesystem. + + * debugfs.c (open_filesystem): Extra args for superblock, + blocksize, and catastrophic mode. Changed callers. + (do_open_filesys, main): Accept new -b, -s, -c options + for open_filesystem. + 2000-02-02 Theodore Ts'o * debugfs.c (dump_inode): Remove #ifdef for i_version diff --git a/debugfs/debug_cmds.ct b/debugfs/debug_cmds.ct index 983c3420..9b2f5928 100644 --- a/debugfs/debug_cmds.ct +++ b/debugfs/debug_cmds.ct @@ -112,5 +112,11 @@ request do_dump, "Dump an inode out to a file", request do_cat, "Dump an inode out to stdout", cat; +request do_lcd, "Change the current directory on your native filesystem", + lcd; + +request do_rdump, "Recursively dump a directory to the native filesystem", + rdump; + end; diff --git a/debugfs/debugfs.8.in b/debugfs/debugfs.8.in index c56a6bd0..7ac45525 100644 --- a/debugfs/debugfs.8.in +++ b/debugfs/debugfs.8.in @@ -8,6 +8,14 @@ debugfs \- ext2 file system debugger .SH SYNOPSIS .B debugfs [ +.B \-b +blocksize +] +[ +.B \-s +superblock +] +[ .B \-f cmd_file ] @@ -19,7 +27,12 @@ request .B \-V ] [ +[ .B \-w +] +[ +.B \-c +] [ device ] @@ -40,6 +53,24 @@ file system (e.g /dev/hdXX). Specifies that the file system should be opened in read-write mode. Without this option, the file system is opened in read-only mode. .TP +.I -c +Specifies that the file system should be opened in catastrophic mode, in +which the inode and group bitmaps are not read initially. This can be +useful for filesystems with significant corruption, but because of this, +catastrophic mode forces the filesystem to be opened read-only. +.TP +.I -b blocksize +Forces the use of the given block size for the file system, rather than +detecting the correct block size as normal. +.TP +.I -s superblock +Causes the file system superblock to be read from the given block number, +rather than the default (1). If you give a +.I -s +option, you must also give a +.I -b +option. +.TP .I -f cmd_file Causes .B debugfs @@ -186,14 +217,18 @@ device numbers must be specified. Take the requested list of inode numbers, and print a listing of pathnames to those inodes. .TP -.I open [-w] [-f] device +.I open [-w] [-f] [-c] [-b blocksize] [-s superblock] device Open a filesystem for editing. The .I -w flag causes the filesystem to be opened for writing. The .I -f flag forces the filesystem to be opened even if there are some unknown or incompatible filesystem features which would normally -prevent the filesystem from being opened. +prevent the filesystem from being opened. The +.IR -c ", " -b ", and " -s +options behave the same as those to +.B debugfs +itself. .TP .I pwd Print the current working directory. @@ -256,6 +291,21 @@ Create a file in the filesystem named and copy the contents of .I source_file into the destination file. +.TP +.I lcd directory +Change the current working directory of the +.B debugfs +process to +.I directory +on the native filesystem. +.TP +.I rdump directory destination +Recursively dump +.I directory +and all its contents (including regular files, symbolic links, and other +directories) into the named +.I destination +which should be an existing directory on the native filesystem. .SH SPECIFYING FILES Many .B debugfs diff --git a/debugfs/debugfs.c b/debugfs/debugfs.c index ad3abf93..1a93d583 100644 --- a/debugfs/debugfs.c +++ b/debugfs/debugfs.c @@ -43,26 +43,44 @@ extern ss_request_table debug_cmds; ext2_filsys current_fs = NULL; ino_t root, cwd; -static void open_filesystem(char *device, int open_flags) +static void open_filesystem(char *device, int open_flags, blk_t superblock, + blk_t blocksize, int catastrophic) { int retval; + + if (superblock != 0 && blocksize == 0) { + com_err(device, 0, "if you specify the superblock, you must also specify the block size"); + current_fs = NULL; + return; + } + + if (catastrophic && (open_flags & EXT2_FLAG_RW)) { + com_err(device, 0, + "opening read-only because of catastrophic mode"); + open_flags &= ~EXT2_FLAG_RW; + } - retval = ext2fs_open(device, open_flags, 0, 0, + retval = ext2fs_open(device, open_flags, superblock, blocksize, unix_io_manager, ¤t_fs); if (retval) { com_err(device, retval, "while opening filesystem"); current_fs = NULL; return; } - retval = ext2fs_read_inode_bitmap(current_fs); - if (retval) { - com_err(device, retval, "while reading inode bitmap"); - goto errout; - } - retval = ext2fs_read_block_bitmap(current_fs); - if (retval) { - com_err(device, retval, "while reading block bitmap"); - goto errout; + + if (catastrophic) + com_err(device, 0, "catastrophic mode - not reading inode or group bitmaps"); + else { + retval = ext2fs_read_inode_bitmap(current_fs); + if (retval) { + com_err(device, retval, "while reading inode bitmap"); + goto errout; + } + retval = ext2fs_read_block_bitmap(current_fs); + if (retval) { + com_err(device, retval, "while reading block bitmap"); + goto errout; + } } root = cwd = EXT2_ROOT_INO; return; @@ -76,15 +94,19 @@ errout: void do_open_filesys(int argc, char **argv) { - const char *usage = "Usage: open [-w] "; + const char *usage = "Usage: open [-s superblock] [-b blocksize] [-c] [-w] "; int c; + int catastrophic = 0; + blk_t superblock = 0; + blk_t blocksize = 0; + char *tmp; int open_flags = 0; optind = 0; #ifdef HAVE_OPTRESET optreset = 1; /* Makes BSD getopt happy */ #endif - while ((c = getopt (argc, argv, "wf")) != EOF) { + while ((c = getopt (argc, argv, "wfcb:s:")) != EOF) { switch (c) { case 'w': open_flags |= EXT2_FLAG_RW; @@ -92,6 +114,25 @@ void do_open_filesys(int argc, char **argv) case 'f': open_flags |= EXT2_FLAG_FORCE; break; + case 'c': + catastrophic = 1; + break; + case 'b': + blocksize = strtoul(optarg, &tmp, 0); + if (*tmp) { + com_err(argv[0], 0, + "Bad block size - %s", optarg); + return; + } + break; + case 's': + superblock = strtoul(optarg, &tmp, 0); + if (*tmp) { + com_err(argv[0], 0, + "Bad superblock number - %s", optarg); + return; + } + break; default: com_err(argv[0], 0, usage); return; @@ -103,7 +144,25 @@ void do_open_filesys(int argc, char **argv) } if (check_fs_not_open(argv[0])) return; - open_filesystem(argv[optind], open_flags); + open_filesystem(argv[optind], open_flags, + superblock, blocksize, catastrophic); +} + +void do_lcd(int argc, char **argv) +{ + const char *usage = "Usage: lcd "; + + if (argc != 2) { + com_err(argv[0], 0, usage); + return; + } + + if (chdir(argv[1]) == -1) { + com_err(argv[0], errno, + "while trying to change native directory to %s", + argv[1]); + return; + } } static void close_filesystem(NOARGS) @@ -1438,19 +1497,23 @@ int main(int argc, char **argv) { int retval; int sci_idx; - const char *usage = "Usage: debugfs [-f cmd_file] [-R request] [-V] [[-w] device]"; + const char *usage = "Usage: debugfs [-b blocksize] [-s superblock] [-f cmd_file] [-R request] [-V] [[-w] [-c] device]"; int c; int open_flags = 0; char *request = 0; int exit_status = 0; char *cmd_file = 0; + blk_t superblock = 0; + blk_t blocksize = 0; + int catastrophic = 0; + char *tmp; initialize_ext2_error_table(); fprintf (stderr, "debugfs %s, %s for EXT2 FS %s, %s\n", E2FSPROGS_VERSION, E2FSPROGS_DATE, EXT2FS_VERSION, EXT2FS_DATE); - while ((c = getopt (argc, argv, "wR:f:V")) != EOF) { + while ((c = getopt (argc, argv, "wcR:f:b:s:V")) != EOF) { switch (c) { case 'R': request = optarg; @@ -1461,6 +1524,25 @@ int main(int argc, char **argv) case 'w': 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; + } + break; + case 's': + superblock = strtoul(optarg, &tmp, 0); + if (*tmp) { + com_err(argv[0], 0, + "Bad superblock number - %s", optarg); + return 1; + } + break; + case 'c': + catastrophic = 1; + break; case 'V': /* Print version number and exit */ fprintf(stderr, "\tUsing %s\n", @@ -1472,7 +1554,8 @@ int main(int argc, char **argv) } } if (optind < argc) - open_filesystem(argv[optind], open_flags); + open_filesystem(argv[optind], open_flags, + superblock, blocksize, catastrophic); sci_idx = ss_create_invocation("debugfs", "0.0", (char *) NULL, &debug_cmds, &retval); diff --git a/debugfs/dump.c b/debugfs/dump.c index d3919dd1..55aa4ac7 100644 --- a/debugfs/dump.c +++ b/debugfs/dump.c @@ -61,6 +61,39 @@ static mode_t mode_xlate(__u16 lmode) return mode; } +static void fix_perms(const char *cmd, const struct ext2_inode *inode, + int fd, const char *name) +{ + struct utimbuf ut; + int i; + + if (fd != -1) + i = fchmod(fd, mode_xlate(inode->i_mode)); + else + i = chmod(name, mode_xlate(inode->i_mode)); + if (i == -1) + com_err(cmd, errno, "while setting permissions of %s", name); + +#ifndef HAVE_FCHOWN + i = chmod(name, inode->i_uid, inode->i_gid); +#else + if (fd != -1) + i = fchown(fd, inode->i_uid, inode->i_gid); + else + i = chown(name, inode->i_uid, inode->i_gid); +#endif + if (i == -1) + com_err(cmd, errno, "while changing ownership of %s", name); + + if (fd != -1) + close(fd); + + ut.actime = inode->i_atime; + ut.modtime = inode->i_mtime; + if (utime(name, &ut) == -1) + com_err(cmd, errno, "while setting times of %s", name); +} + static void dump_file(char *cmdname, ino_t ino, int fd, int preserve, char *outname) { @@ -100,27 +133,9 @@ static void dump_file(char *cmdname, ino_t ino, int fd, int preserve, return; } - if (preserve) { -#ifdef HAVE_FCHOWN - if (fchown(fd, inode.i_uid, inode.i_gid) < 0) - com_err("dump_file", errno, - "while changing ownership of %s", outname); -#else - if (chown(outname, inode.i_uid, inode.i_gid) < 0) - com_err("dump_file", errno, - "while changing ownership of %s", outname); - -#endif - if (fchmod(fd, mode_xlate(inode.i_mode)) < 0) - com_err("dump_file", errno, - "while setting permissions of %s", outname); - ut.actime = inode.i_atime; - ut.modtime = inode.i_mtime; - close(fd); - if (utime(outname, &ut) < 0) - com_err("dump_file", errno, - "while setting times on %s", outname); - } else if (fd != 1) + if (preserve) + fix_perms("dump_file", &inode, fd, outname); + else if (fd != 1) close(fd); return; @@ -176,6 +191,182 @@ void do_dump(int argc, char **argv) return; } +static void rdump_symlink(ino_t ino, struct ext2_inode *inode, + const char *fullname) +{ + ext2_file_t e2_file; + char *buf; + errcode_t retval; + + buf = malloc(inode->i_size + 1); + if (!buf) { + com_err("rdump", errno, "while allocating for symlink"); + goto errout; + } + + /* Apparently, this is the right way to detect and handle fast + * symlinks; see do_stat() in debugfs.c. */ + if (inode->i_blocks == 0) + strcpy(buf, (char *) inode->i_block); + else { + unsigned bytes = inode->i_size; + char *p = buf; + retval = ext2fs_file_open(current_fs, ino, 0, &e2_file); + if (retval) { + com_err("rdump", retval, "while opening symlink"); + goto errout; + } + for (;;) { + unsigned int got; + retval = ext2fs_file_read(e2_file, p, bytes, &got); + if (retval) { + com_err("rdump", retval, "while reading symlink"); + goto errout; + } + bytes -= got; + p += got; + if (got == 0 || bytes == 0) + break; + } + buf[inode->i_size] = 0; + retval = ext2fs_file_close(e2_file); + if (retval) + com_err("rdump", retval, "while closing symlink"); + } + + if (symlink(buf, fullname) == -1) { + com_err("rdump", errno, "while creating symlink %s -> %s", buf, fullname); + goto errout; + } + +errout: + free(buf); +} + +static int rdump_dirent(struct ext2_dir_entry *, int, int, char *, void *); + +static void rdump_inode(ino_t ino, struct ext2_inode *inode, + const char *name, const char *dumproot) +{ + char *fullname; + struct utimbuf ut; + + /* There are more efficient ways to do this, but this method + * requires only minimal debugging. */ + fullname = malloc(strlen(dumproot) + strlen(name) + 2); + if (!fullname) { + com_err("rdump", errno, "while allocating memory"); + return; + } + sprintf(fullname, "%s/%s", dumproot, name); + + if (LINUX_S_ISLNK(inode->i_mode)) + rdump_symlink(ino, inode, fullname); + else if (LINUX_S_ISREG(inode->i_mode)) { + int fd; + fd = open(fullname, O_WRONLY | O_CREAT | O_TRUNC, S_IRWXU); + if (fd == -1) { + com_err("rdump", errno, "while dumping %s", fullname); + goto errout; + } + dump_file("rdump", ino, fd, 1, fullname); + } + else if (LINUX_S_ISDIR(inode->i_mode) && strcmp(name, ".") && strcmp(name, "..")) { + errcode_t retval; + + /* Create the directory with 0700 permissions, because we + * expect to have to create entries it. Then fix its perms + * once we've done the traversal. */ + if (mkdir(fullname, S_IRWXU) == -1) { + com_err("rdump", errno, "while making directory %s", fullname); + goto errout; + } + + retval = ext2fs_dir_iterate(current_fs, ino, 0, 0, + rdump_dirent, (void *) fullname); + if (retval) + com_err("rdump", retval, "while dumping %s", fullname); + + fix_perms("rdump", inode, -1, fullname); + } + /* else do nothing (don't dump device files, sockets, fifos, etc.) */ + +errout: + free(fullname); +} + +static int rdump_dirent(struct ext2_dir_entry *dirent, int offset, + int blocksize, char *buf, void *private) +{ + char name[EXT2_NAME_LEN]; + int thislen; + const char *dumproot = private; + struct ext2_inode inode; + errcode_t retval; + + thislen = ((dirent->name_len & 0xFF) < EXT2_NAME_LEN + ? (dirent->name_len & 0xFF) : EXT2_NAME_LEN); + 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); + return 0; + } + + rdump_inode(dirent->inode, &inode, name, dumproot); + + return 0; +} + +void do_rdump(int argc, char **argv) +{ + ino_t ino; + struct ext2_inode inode; + errcode_t retval; + struct stat st; + int i; + char *p; + + if (argc != 3) { + com_err(argv[0], 0, "Usage: rdump "); + return; + } + + if (check_fs_open(argv[0])) + return; + + ino = string_to_inode(argv[1]); + if (!ino) + return; + + /* Ensure ARGV[2] is a directory. */ + i = stat(argv[2], &st); + if (i == -1) { + com_err("rdump", errno, "while statting %s", argv[2]); + return; + } + if (!S_ISDIR(st.st_mode)) { + com_err("rdump", 0, "%s is not a directory", argv[2]); + return; + } + + retval = ext2fs_read_inode(current_fs, ino, &inode); + if (retval) { + com_err("rdump", retval, "while dumping %s", argv[1]); + return; + } + + p = strrchr(argv[1], '/'); + if (p) + p++; + else + p = argv[1]; + + rdump_inode(ino, &inode, p, argv[2]); +} + void do_cat(int argc, char **argv) { ino_t inode; diff --git a/debugfs/ls.c b/debugfs/ls.c index aadf75d1..4c96db46 100644 --- a/debugfs/ls.c +++ b/debugfs/ls.c @@ -48,8 +48,8 @@ static void ls_l_file(struct list_dir_struct *ls, char *name, ino_t ino) } modtime = inode.i_mtime; tm_p = localtime(&modtime); - sprintf(datestr, "%2d-%s-%2d %02d:%02d", - tm_p->tm_mday, monstr[tm_p->tm_mon], tm_p->tm_year, + 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, "%6ld %6o %5d %5d ", ino, inode.i_mode, inode.i_uid, inode.i_gid); diff --git a/lib/ext2fs/ChangeLog b/lib/ext2fs/ChangeLog index 2107c19c..9141e845 100644 --- a/lib/ext2fs/ChangeLog +++ b/lib/ext2fs/ChangeLog @@ -1,3 +1,9 @@ +2000-05-27 Theodore Ts'o + + * mkdir.c (ext2fs_mkdir): Read the parent directory's inode + earlier, so that if there's an error reading it, we can + more cleanly back out of the operation. + 2000-05-25 * getsize.c (ext2fs_get_device_size): Use open64() instead of diff --git a/lib/ext2fs/mkdir.c b/lib/ext2fs/mkdir.c index eca9a1d1..98c5ccf2 100644 --- a/lib/ext2fs/mkdir.c +++ b/lib/ext2fs/mkdir.c @@ -39,7 +39,7 @@ errcode_t ext2fs_mkdir(ext2_filsys fs, ino_t parent, ino_t inum, const char *name) { errcode_t retval; - struct ext2_inode inode; + struct ext2_inode parent_inode, inode; ino_t ino = inum; ino_t scratch_ino; blk_t blk; @@ -73,6 +73,16 @@ errcode_t ext2fs_mkdir(ext2_filsys fs, ino_t parent, ino_t inum, goto cleanup; /* + * Get the parent's inode, if necessary + */ + if (parent != ino) { + retval = ext2fs_read_inode(fs, parent, &parent_inode); + if (retval) + goto cleanup; + } else + memset(&parent_inode, 0, sizeof(parent_inode)); + + /* * Create the inode structure.... */ memset(&inode, 0, sizeof(struct ext2_inode)); @@ -116,11 +126,8 @@ errcode_t ext2fs_mkdir(ext2_filsys fs, ino_t parent, ino_t inum, * Update parent inode's counts */ if (parent != ino) { - retval = ext2fs_read_inode(fs, parent, &inode); - if (retval) - goto cleanup; - inode.i_links_count++; - retval = ext2fs_write_inode(fs, parent, &inode); + parent_inode.i_links_count++; + retval = ext2fs_write_inode(fs, parent, &parent_inode); if (retval) goto cleanup; } diff --git a/version.h b/version.h index 4ec26e43..a1bb8056 100644 --- a/version.h +++ b/version.h @@ -7,4 +7,4 @@ */ #define E2FSPROGS_VERSION "1.19-WIP" -#define E2FSPROGS_DATE "6-Apr-2000" +#define E2FSPROGS_DATE "27-May-2000" -- 2.11.0