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) 148 { 149 ext2_ino_t inode; 150 int fd; 151 int c; 152 int preserve = 0; 153 char *in_fn, *out_fn; 154 155 reset_getopt(); 156 while ((c = getopt (argc, argv, "p")) != EOF) { 157 switch (c) { 158 case 'p': 159 preserve++; 160 break; 161 default: 162 print_usage: 163 com_err(argv[0], 0, "Usage: dump_inode [-p] " 164 "<file> <output_file>"); 165 return; 166 } 167 } 168 if (optind != argc-2) 169 goto print_usage; 170 171 if (check_fs_open(argv[0])) 172 return; 173 174 in_fn = argv[optind]; 175 out_fn = argv[optind+1]; 176 177 inode = string_to_inode(in_fn); 178 if (!inode) 179 return; 180 181 fd = open(out_fn, O_CREAT | O_WRONLY | O_TRUNC | O_LARGEFILE, 0666); 182 if (fd < 0) { 183 com_err(argv[0], errno, "while opening %s for dump_inode", 184 out_fn); 185 return; 186 } 187 188 dump_file(argv[0], inode, fd, preserve, out_fn); 189 if (close(fd) != 0) { 190 com_err(argv[0], errno, "while closing %s for dump_inode", 191 out_fn); 192 return; 193 } 194 195 return; 196 } 197 198 static void rdump_symlink(ext2_ino_t ino, struct ext2_inode *inode, 199 const char *fullname) 200 { 201 ext2_file_t e2_file; 202 char *buf; 203 errcode_t retval; 204 205 buf = malloc(inode->i_size + 1); 206 if (!buf) { 207 com_err("rdump", errno, "while allocating for symlink"); 208 goto errout; 209 } 210 211 /* Apparently, this is the right way to detect and handle fast 212 * symlinks; see do_stat() in debugfs.c. */ 213 if (ext2fs_inode_data_blocks2(current_fs, inode) == 0) 214 strcpy(buf, (char *) inode->i_block); 215 else { 216 unsigned bytes = inode->i_size; 217 char *p = buf; 218 retval = ext2fs_file_open(current_fs, ino, 0, &e2_file); 219 if (retval) { 220 com_err("rdump", retval, "while opening symlink"); 221 goto errout; 222 } 223 for (;;) { 224 unsigned int got; 225 retval = ext2fs_file_read(e2_file, p, bytes, &got); 226 if (retval) { 227 com_err("rdump", retval, "while reading symlink"); 228 goto errout; 229 } 230 bytes -= got; 231 p += got; 232 if (got == 0 || bytes == 0) 233 break; 234 } 235 buf[inode->i_size] = 0; 236 retval = ext2fs_file_close(e2_file); 237 if (retval) 238 com_err("rdump", retval, "while closing symlink"); 239 } 240 241 if (symlink(buf, fullname) == -1) { 242 com_err("rdump", errno, "while creating symlink %s -> %s", buf, fullname); 243 goto errout; 244 } 245 246 errout: 247 free(buf); 248 } 249 250 static int rdump_dirent(struct ext2_dir_entry *, int, int, char *, void *); 251 252 static void rdump_inode(ext2_ino_t ino, struct ext2_inode *inode, 253 const char *name, const char *dumproot) 254 { 255 char *fullname; 256 257 /* There are more efficient ways to do this, but this method 258 * requires only minimal debugging. */ 259 fullname = malloc(strlen(dumproot) + strlen(name) + 2); 260 if (!fullname) { 261 com_err("rdump", errno, "while allocating memory"); 262 return; 263 } 264 sprintf(fullname, "%s/%s", dumproot, name); 265 266 if (LINUX_S_ISLNK(inode->i_mode)) 267 rdump_symlink(ino, inode, fullname); 268 else if (LINUX_S_ISREG(inode->i_mode)) { 269 int fd; 270 fd = open(fullname, O_WRONLY | O_CREAT | O_TRUNC | O_LARGEFILE, S_IRWXU); 271 if (fd == -1) { 272 com_err("rdump", errno, "while opening %s", fullname); 273 goto errout; 274 } 275 dump_file("rdump", ino, fd, 1, fullname); 276 if (close(fd) != 0) { 277 com_err("rdump", errno, "while closing %s", fullname); 278 goto errout; 279 } 280 } 281 else if (LINUX_S_ISDIR(inode->i_mode) && strcmp(name, ".") && strcmp(name, "..")) { 282 errcode_t retval; 283 284 /* Create the directory with 0700 permissions, because we 285 * expect to have to create entries it. Then fix its perms 286 * once we've done the traversal. */ 287 if (name[0] && mkdir(fullname, S_IRWXU) == -1) { 288 com_err("rdump", errno, "while making directory %s", fullname); 289 goto errout; 290 } 291 292 retval = ext2fs_dir_iterate(current_fs, ino, 0, 0, 293 rdump_dirent, (void *) fullname); 294 if (retval) 295 com_err("rdump", retval, "while dumping %s", fullname); 296 297 fix_perms("rdump", inode, -1, fullname); 298 } 299 /* else do nothing (don't dump device files, sockets, fifos, etc.) */ 300 301 errout: 302 free(fullname); 303 } 304 305 static int rdump_dirent(struct ext2_dir_entry *dirent, 306 int offset EXT2FS_ATTR((unused)), 307 int blocksize EXT2FS_ATTR((unused)), 308 char *buf EXT2FS_ATTR((unused)), void *private) 309 { 310 char name[EXT2_NAME_LEN + 1]; 311 int thislen; 312 const char *dumproot = private; 313 struct ext2_inode inode; 314 315 thislen = ext2fs_dirent_name_len(dirent); 316 strncpy(name, dirent->name, thislen); 317 name[thislen] = 0; 318 319 if (debugfs_read_inode(dirent->inode, &inode, name)) 320 return 0; 321 322 rdump_inode(dirent->inode, &inode, name, dumproot); 323 324 return 0; 325 } 326 327 void do_rdump(int argc, char **argv) 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) 372 { 373 ext2_ino_t inode; 374 375 if (common_inode_args_process(argc, argv, &inode, 0)) 376 return; 377 378 fflush(stdout); 379 fflush(stderr); 380 dump_file(argv[0], inode, 1, 0, argv[2]); 381 382 return; 383 } 384 385