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