1/* 2 * dump.c --- dump the contents of an inode out to a file 3 * 4 * Copyright (C) 1994 Theodore Ts'o. This file may be redistributed 5 * under the terms of the GNU Public License. 6 */ 7 8#define _GNU_SOURCE /* for O_LARGEFILE */ 9 10#include <stdio.h> 11#include <unistd.h> 12#include <stdlib.h> 13#include <ctype.h> 14#include <string.h> 15#include <time.h> 16#ifdef HAVE_ERRNO_H 17#include <errno.h> 18#endif 19#include <sys/types.h> 20#include <sys/stat.h> 21#include <fcntl.h> 22#include <utime.h> 23#ifdef HAVE_GETOPT_H 24#include <getopt.h> 25#else 26extern int optind; 27extern char *optarg; 28#endif 29 30#include "debugfs.h" 31 32#ifndef O_LARGEFILE 33#define O_LARGEFILE 0 34#endif 35 36/* 37 * The mode_xlate function translates a linux mode into a native-OS mode_t. 38 */ 39static struct { 40 __u16 lmask; 41 mode_t mask; 42} mode_table[] = { 43 { LINUX_S_IRUSR, S_IRUSR }, 44 { LINUX_S_IWUSR, S_IWUSR }, 45 { LINUX_S_IXUSR, S_IXUSR }, 46 { LINUX_S_IRGRP, S_IRGRP }, 47 { LINUX_S_IWGRP, S_IWGRP }, 48 { LINUX_S_IXGRP, S_IXGRP }, 49 { LINUX_S_IROTH, S_IROTH }, 50 { LINUX_S_IWOTH, S_IWOTH }, 51 { LINUX_S_IXOTH, S_IXOTH }, 52 { 0, 0 } 53}; 54 55static mode_t mode_xlate(__u16 lmode) 56{ 57 mode_t mode = 0; 58 int i; 59 60 for (i=0; mode_table[i].lmask; i++) { 61 if (lmode & mode_table[i].lmask) 62 mode |= mode_table[i].mask; 63 } 64 return mode; 65} 66 67static void fix_perms(const char *cmd, const struct ext2_inode *inode, 68 int fd, const char *name) 69{ 70 struct utimbuf ut; 71 int i; 72 73 if (fd != -1) 74 i = fchmod(fd, mode_xlate(inode->i_mode)); 75 else 76 i = chmod(name, mode_xlate(inode->i_mode)); 77 if (i == -1) 78 com_err(cmd, errno, "while setting permissions of %s", name); 79 80#ifndef HAVE_FCHOWN 81 i = chown(name, inode->i_uid, inode->i_gid); 82#else 83 if (fd != -1) 84 i = fchown(fd, inode->i_uid, inode->i_gid); 85 else 86 i = chown(name, inode->i_uid, inode->i_gid); 87#endif 88 if (i == -1) 89 com_err(cmd, errno, "while changing ownership of %s", name); 90 91 if (fd != -1) 92 close(fd); 93 94 ut.actime = inode->i_atime; 95 ut.modtime = inode->i_mtime; 96 if (utime(name, &ut) == -1) 97 com_err(cmd, errno, "while setting times of %s", name); 98} 99 100static void dump_file(const char *cmdname, ext2_ino_t ino, int fd, 101 int preserve, char *outname) 102{ 103 errcode_t retval; 104 struct ext2_inode inode; 105 char buf[8192]; 106 ext2_file_t e2_file; 107 int nbytes; 108 unsigned int got; 109 110 if (debugfs_read_inode(ino, &inode, cmdname)) 111 return; 112 113 retval = ext2fs_file_open(current_fs, ino, 0, &e2_file); 114 if (retval) { 115 com_err(cmdname, retval, "while opening ext2 file"); 116 return; 117 } 118 while (1) { 119 retval = ext2fs_file_read(e2_file, buf, sizeof(buf), &got); 120 if (retval) 121 com_err(cmdname, retval, "while reading ext2 file"); 122 if (got == 0) 123 break; 124 nbytes = write(fd, buf, got); 125 if ((unsigned) nbytes != got) 126 com_err(cmdname, errno, "while writing file"); 127 } 128 retval = ext2fs_file_close(e2_file); 129 if (retval) { 130 com_err(cmdname, retval, "while closing ext2 file"); 131 return; 132 } 133 134 if (preserve) 135 fix_perms("dump_file", &inode, fd, outname); 136 else if (fd != 1) 137 close(fd); 138 139 return; 140} 141 142void do_dump(int argc, char **argv) 143{ 144 ext2_ino_t inode; 145 int fd; 146 int c; 147 int preserve = 0; 148 char *in_fn, *out_fn; 149 150 reset_getopt(); 151 while ((c = getopt (argc, argv, "p")) != EOF) { 152 switch (c) { 153 case 'p': 154 preserve++; 155 break; 156 default: 157 print_usage: 158 com_err(argv[0], 0, "Usage: dump_inode [-p] " 159 "<file> <output_file>"); 160 return; 161 } 162 } 163 if (optind != argc-2) 164 goto print_usage; 165 166 if (check_fs_open(argv[0])) 167 return; 168 169 in_fn = argv[optind]; 170 out_fn = argv[optind+1]; 171 172 inode = string_to_inode(in_fn); 173 if (!inode) 174 return; 175 176 fd = open(out_fn, O_CREAT | O_WRONLY | O_TRUNC | O_LARGEFILE, 0666); 177 if (fd < 0) { 178 com_err(argv[0], errno, "while opening %s for dump_inode", 179 out_fn); 180 return; 181 } 182 183 dump_file(argv[0], inode, fd, preserve, out_fn); 184 185 return; 186} 187 188static void rdump_symlink(ext2_ino_t ino, struct ext2_inode *inode, 189 const char *fullname) 190{ 191 ext2_file_t e2_file; 192 char *buf; 193 errcode_t retval; 194 195 buf = malloc(inode->i_size + 1); 196 if (!buf) { 197 com_err("rdump", errno, "while allocating for symlink"); 198 goto errout; 199 } 200 201 /* Apparently, this is the right way to detect and handle fast 202 * symlinks; see do_stat() in debugfs.c. */ 203 if (inode->i_blocks == 0) 204 strcpy(buf, (char *) inode->i_block); 205 else { 206 unsigned bytes = inode->i_size; 207 char *p = buf; 208 retval = ext2fs_file_open(current_fs, ino, 0, &e2_file); 209 if (retval) { 210 com_err("rdump", retval, "while opening symlink"); 211 goto errout; 212 } 213 for (;;) { 214 unsigned int got; 215 retval = ext2fs_file_read(e2_file, p, bytes, &got); 216 if (retval) { 217 com_err("rdump", retval, "while reading symlink"); 218 goto errout; 219 } 220 bytes -= got; 221 p += got; 222 if (got == 0 || bytes == 0) 223 break; 224 } 225 buf[inode->i_size] = 0; 226 retval = ext2fs_file_close(e2_file); 227 if (retval) 228 com_err("rdump", retval, "while closing symlink"); 229 } 230 231 if (symlink(buf, fullname) == -1) { 232 com_err("rdump", errno, "while creating symlink %s -> %s", buf, fullname); 233 goto errout; 234 } 235 236errout: 237 free(buf); 238} 239 240static int rdump_dirent(struct ext2_dir_entry *, int, int, char *, void *); 241 242static void rdump_inode(ext2_ino_t ino, struct ext2_inode *inode, 243 const char *name, const char *dumproot) 244{ 245 char *fullname; 246 247 /* There are more efficient ways to do this, but this method 248 * requires only minimal debugging. */ 249 fullname = malloc(strlen(dumproot) + strlen(name) + 2); 250 if (!fullname) { 251 com_err("rdump", errno, "while allocating memory"); 252 return; 253 } 254 sprintf(fullname, "%s/%s", dumproot, name); 255 256 if (LINUX_S_ISLNK(inode->i_mode)) 257 rdump_symlink(ino, inode, fullname); 258 else if (LINUX_S_ISREG(inode->i_mode)) { 259 int fd; 260 fd = open(fullname, O_WRONLY | O_CREAT | O_TRUNC | O_LARGEFILE, S_IRWXU); 261 if (fd == -1) { 262 com_err("rdump", errno, "while dumping %s", fullname); 263 goto errout; 264 } 265 dump_file("rdump", ino, fd, 1, fullname); 266 } 267 else if (LINUX_S_ISDIR(inode->i_mode) && strcmp(name, ".") && strcmp(name, "..")) { 268 errcode_t retval; 269 270 /* Create the directory with 0700 permissions, because we 271 * expect to have to create entries it. Then fix its perms 272 * once we've done the traversal. */ 273 if (mkdir(fullname, S_IRWXU) == -1) { 274 com_err("rdump", errno, "while making directory %s", fullname); 275 goto errout; 276 } 277 278 retval = ext2fs_dir_iterate(current_fs, ino, 0, 0, 279 rdump_dirent, (void *) fullname); 280 if (retval) 281 com_err("rdump", retval, "while dumping %s", fullname); 282 283 fix_perms("rdump", inode, -1, fullname); 284 } 285 /* else do nothing (don't dump device files, sockets, fifos, etc.) */ 286 287errout: 288 free(fullname); 289} 290 291static int rdump_dirent(struct ext2_dir_entry *dirent, 292 int offset EXT2FS_ATTR((unused)), 293 int blocksize EXT2FS_ATTR((unused)), 294 char *buf EXT2FS_ATTR((unused)), void *private) 295{ 296 char name[EXT2_NAME_LEN + 1]; 297 int thislen; 298 const char *dumproot = private; 299 struct ext2_inode inode; 300 301 thislen = ((dirent->name_len & 0xFF) < EXT2_NAME_LEN 302 ? (dirent->name_len & 0xFF) : EXT2_NAME_LEN); 303 strncpy(name, dirent->name, thislen); 304 name[thislen] = 0; 305 306 if (debugfs_read_inode(dirent->inode, &inode, name)) 307 return 0; 308 309 rdump_inode(dirent->inode, &inode, name, dumproot); 310 311 return 0; 312} 313 314void do_rdump(int argc, char **argv) 315{ 316 ext2_ino_t ino; 317 struct ext2_inode inode; 318 struct stat st; 319 int i; 320 char *p; 321 322 if (common_args_process(argc, argv, 3, 3, "rdump", 323 "<directory> <native directory>", 0)) 324 return; 325 326 ino = string_to_inode(argv[1]); 327 if (!ino) 328 return; 329 330 /* Ensure ARGV[2] is a directory. */ 331 i = stat(argv[2], &st); 332 if (i == -1) { 333 com_err("rdump", errno, "while statting %s", argv[2]); 334 return; 335 } 336 if (!S_ISDIR(st.st_mode)) { 337 com_err("rdump", 0, "%s is not a directory", argv[2]); 338 return; 339 } 340 341 if (debugfs_read_inode(ino, &inode, argv[1])) 342 return; 343 344 p = strrchr(argv[1], '/'); 345 if (p) 346 p++; 347 else 348 p = argv[1]; 349 350 rdump_inode(ino, &inode, p, argv[2]); 351} 352 353void do_cat(int argc, char **argv) 354{ 355 ext2_ino_t inode; 356 357 if (common_inode_args_process(argc, argv, &inode, 0)) 358 return; 359 360 fflush(stdout); 361 fflush(stderr); 362 dump_file(argv[0], inode, 1, 0, argv[2]); 363 364 return; 365} 366 367