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.
+2000-05-23 Aaron Crane <aaronc@pobox.com>
+
+ * 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 <tytso@valinux.com>
* debugfs.c (dump_inode): Remove #ifdef for i_version
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;
.SH SYNOPSIS
.B debugfs
[
+.B \-b
+blocksize
+]
+[
+.B \-s
+superblock
+]
+[
.B \-f
cmd_file
]
.B \-V
]
[
+[
.B \-w
+]
+[
+.B \-c
+]
[
device
]
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
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.
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
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;
void do_open_filesys(int argc, char **argv)
{
- const char *usage = "Usage: open [-w] <device>";
+ const char *usage = "Usage: open [-s superblock] [-b blocksize] [-c] [-w] <device>";
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;
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;
}
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 <native dir>";
+
+ 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)
{
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;
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",
}
}
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);
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)
{
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;
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 <directory> <native directory>");
+ 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;
}
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);
+2000-05-27 Theodore Ts'o <tytso@valinux.com>
+
+ * 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 <tytso@snap.thunk.org>
* getsize.c (ext2fs_get_device_size): Use open64() instead of
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;
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));
* 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;
}
*/
#define E2FSPROGS_VERSION "1.19-WIP"
-#define E2FSPROGS_DATE "6-Apr-2000"
+#define E2FSPROGS_DATE "27-May-2000"