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