From a21fa980de1b6eb6c7e2f02638bb1e7159f8fa96 Mon Sep 17 00:00:00 2001 From: Quentin Kaiser Date: Fri, 9 Feb 2024 10:31:29 +0100 Subject: [PATCH] debugfs: expose a 'preserve' command line switch in rdump command rdump assumed the user wanted to preserve permissions and ownership when dumping a filesystem directory recursively with 'rdump'. This is in opposition with the way the 'dump' or 'dump_inode' command has been designed, since it expose a '-p' command line switch to allow the end users to explicitly opt-in for permission and ownership preservation. The inability to explicitly ask for permission and ownership preservation would get rdump to default to preservation, which is a problem when faced with filesystems having directories with the read flag but not the execute flag, since it would only allow to enumerate the directory content, but not see the inode details. Therefore getting debugfs in all kinds of issues trying to set ownership and permissions of files it can't see. This fix introduce a 'preserve' ('-p') flag in rdump command so that users can explicitly opt-in for it, and debugfs will default to a safer way of operation (no preserve). --- debugfs/dump.c | 137 ++++++++++++++++++++++++++++++------------------- 1 file changed, 83 insertions(+), 54 deletions(-) diff --git a/debugfs/dump.c b/debugfs/dump.c index b8a46eacc..78c1809af 100644 --- a/debugfs/dump.c +++ b/debugfs/dump.c @@ -256,12 +256,16 @@ static void rdump_symlink(ext2_ino_t ino, struct ext2_inode *inode, free(buf); } +typedef struct rdump_dirent_private { + char* fullname; + int preserve; +} rdump_dirent_private_t; + static int rdump_dirent(struct ext2_dir_entry *, int, int, char *, void *); static void rdump_inode(ext2_ino_t ino, struct ext2_inode *inode, - const char *name, const char *dumproot) -{ - char *fullname; + const char *name, const char *dumproot, int preserve) { + char *fullname; /* There are more efficient ways to do this, but this method * requires only minimal debugging. */ @@ -272,16 +276,16 @@ static void rdump_inode(ext2_ino_t ino, struct ext2_inode *inode, } 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 | O_LARGEFILE, S_IRWXU); - if (fd == -1) { - com_err("rdump", errno, "while opening %s", fullname); - goto errout; - } - if (dump_file("rdump", ino, fd, 1, fullname) != 0) { + 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 | O_LARGEFILE, S_IRWXU); + if (fd == -1) { + com_err("rdump", errno, "while opening %s", fullname); + goto errout; + } + if (dump_file("rdump", ino, fd, preserve, fullname) != 0) { com_err("rdump", errno, "while dumping %s", fullname); free(fullname); exit(1); @@ -302,28 +306,36 @@ static void rdump_inode(ext2_ino_t ino, struct ext2_inode *inode, 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.) */ + rdump_dirent_private_t* entry = malloc(sizeof(rdump_dirent_private_t)); + if (entry == NULL) { + com_err("rdump", errno, "while allocating entry for %s", fullname); + } + entry->fullname = fullname; + entry->preserve = preserve; + + retval = ext2fs_dir_iterate(current_fs, ino, 0, 0, rdump_dirent, + (void *)entry); + free(entry); + if (retval) + com_err("rdump", retval, "while dumping %s", fullname); + if (preserve) + 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 EXT2FS_ATTR((unused)), - int blocksize EXT2FS_ATTR((unused)), - char *buf EXT2FS_ATTR((unused)), void *private) -{ - char name[EXT2_NAME_LEN + 1]; - int thislen; - const char *dumproot = private; - struct ext2_inode inode; + int offset EXT2FS_ATTR((unused)), + int blocksize EXT2FS_ATTR((unused)), + char *buf EXT2FS_ATTR((unused)), void *private) { + char name[EXT2_NAME_LEN + 1]; + int thislen; + struct ext2_inode inode; thislen = ext2fs_dirent_name_len(dirent); strncpy(name, dirent->name, thislen); @@ -332,42 +344,59 @@ static int rdump_dirent(struct ext2_dir_entry *dirent, if (debugfs_read_inode(dirent->inode, &inode, name)) return 0; - rdump_inode(dirent->inode, &inode, name, dumproot); + rdump_inode(dirent->inode, &inode, name, ((rdump_dirent_private_t*)private)->fullname, ((rdump_dirent_private_t*)private)->preserve); return 0; } void do_rdump(int argc, char **argv, int sci_idx EXT2FS_ATTR((unused)), - void *infop EXT2FS_ATTR((unused))) -{ - struct stat st; - char *dest_dir; - int i; + void *infop EXT2FS_ATTR((unused))) { + + int i, c; + int preserve = 0; + char *dest_dir; + struct stat st; + ext2_ino_t ino; + struct ext2_inode inode; + + reset_getopt(); + while ((c = getopt(argc, argv, "p")) != EOF) { + switch (c) { + case 'p': + preserve++; + break; + default: + print_usage: + com_err(argv[0], 0, + "Usage: rdump [-p] " + "[ ...] "); + exit(1); + } + } - if (common_args_process(argc, argv, 3, INT_MAX, "rdump", - "... ", 0)) - return; + if (optind > argc - 2) + goto print_usage; /* Pull out last argument */ dest_dir = argv[argc - 1]; argc--; - /* Ensure last arg is a directory. */ - if (stat(dest_dir, &st) == -1) { - com_err("rdump", errno, "while statting %s", dest_dir); - return; - } - if (!S_ISDIR(st.st_mode)) { - com_err("rdump", 0, "%s is not a directory", dest_dir); - return; - } + /* Ensure last arg is a directory. */ + if (stat(dest_dir, &st) == -1) { + com_err("rdump", errno, "while statting %s", dest_dir); + return; + } - for (i = 1; i < argc; i++) { - char *arg = argv[i], *basename; - struct ext2_inode inode; - ext2_ino_t ino = string_to_inode(arg); - if (!ino) - continue; + if (!S_ISDIR(st.st_mode)) { + com_err("rdump", 0, "%s is not a directory", dest_dir); + return; + } + + for (i = optind; i < argc; i++) { + char *arg = argv[i], *basename; + ino = string_to_inode(arg); + if (!ino) + continue; if (debugfs_read_inode(ino, &inode, arg)) continue; @@ -378,7 +407,7 @@ void do_rdump(int argc, char **argv, int sci_idx EXT2FS_ATTR((unused)), else basename = arg; - rdump_inode(ino, &inode, basename, dest_dir); + rdump_inode(ino, &inode, basename, dest_dir, preserve); } }