blob: 8d9788641ebcc80f1b72db30106b1f6b7dc8644a [file] [log] [blame]
Theodore Ts'of3db3561997-04-26 13:34:30 +00001/*
2 * dump.c --- dump the contents of an inode out to a file
Theodore Ts'oefc6f622008-08-27 23:07:54 -04003 *
Theodore Ts'of3db3561997-04-26 13:34:30 +00004 * Copyright (C) 1994 Theodore Ts'o. This file may be redistributed
5 * under the terms of the GNU Public License.
6 */
7
Andreas Dilgercf5301d2011-06-11 10:58:25 -04008#ifndef _GNU_SOURCE
Theodore Ts'oefac5a72007-04-02 18:30:46 -04009#define _GNU_SOURCE /* for O_LARGEFILE */
Andreas Dilgercf5301d2011-06-11 10:58:25 -040010#endif
Theodore Ts'oefac5a72007-04-02 18:30:46 -040011
Theodore Ts'od1154eb2011-09-18 17:34:37 -040012#include "config.h"
Theodore Ts'of3db3561997-04-26 13:34:30 +000013#include <stdio.h>
14#include <unistd.h>
15#include <stdlib.h>
16#include <ctype.h>
17#include <string.h>
18#include <time.h>
Theodore Ts'o50e1e101997-04-26 13:58:21 +000019#ifdef HAVE_ERRNO_H
20#include <errno.h>
21#endif
Theodore Ts'of3db3561997-04-26 13:34:30 +000022#include <sys/types.h>
23#include <sys/stat.h>
24#include <fcntl.h>
Theodore Ts'ofc6d9d51997-04-29 14:51:31 +000025#include <utime.h>
26#ifdef HAVE_GETOPT_H
27#include <getopt.h>
Theodore Ts'oefc6f622008-08-27 23:07:54 -040028#else
Theodore Ts'ofc6d9d51997-04-29 14:51:31 +000029extern int optind;
30extern char *optarg;
31#endif
Theodore Ts'of3db3561997-04-26 13:34:30 +000032
33#include "debugfs.h"
34
Theodore Ts'o819157d2003-01-22 18:25:39 -050035#ifndef O_LARGEFILE
36#define O_LARGEFILE 0
37#endif
38
Theodore Ts'ofc6d9d51997-04-29 14:51:31 +000039/*
40 * The mode_xlate function translates a linux mode into a native-OS mode_t.
41 */
42static 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};
Theodore Ts'oefc6f622008-08-27 23:07:54 -040057
Theodore Ts'ofc6d9d51997-04-29 14:51:31 +000058static 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
Theodore Ts'o2e8d40d2000-05-27 15:15:40 +000070static 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
Theodore Ts'oc5de1d42000-12-31 01:39:54 +000084 i = chown(name, inode->i_uid, inode->i_gid);
Theodore Ts'o2e8d40d2000-05-27 15:15:40 +000085#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
Theodore Ts'o2e8d40d2000-05-27 15:15:40 +000094 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
Theodore Ts'ob044c2e2001-01-11 15:26:39 +0000100static void dump_file(const char *cmdname, ext2_ino_t ino, int fd,
101 int preserve, char *outname)
Theodore Ts'of3db3561997-04-26 13:34:30 +0000102{
103 errcode_t retval;
Theodore Ts'ofc6d9d51997-04-29 14:51:31 +0000104 struct ext2_inode inode;
Zheng Liud3b4de42013-01-01 20:30:14 +0800105 char *buf = 0;
Theodore Ts'o5a513841997-10-25 22:41:14 +0000106 ext2_file_t e2_file;
Theodore Ts'o4a31c481998-03-30 01:27:25 +0000107 int nbytes;
Zheng Liud3b4de42013-01-01 20:30:14 +0800108 unsigned int got, blocksize = current_fs->blocksize;
Theodore Ts'oefc6f622008-08-27 23:07:54 -0400109
Theodore Ts'oe1018ee2002-01-03 04:55:25 -0500110 if (debugfs_read_inode(ino, &inode, cmdname))
Theodore Ts'ofc6d9d51997-04-29 14:51:31 +0000111 return;
Theodore Ts'of3db3561997-04-26 13:34:30 +0000112
Theodore Ts'o5a513841997-10-25 22:41:14 +0000113 retval = ext2fs_file_open(current_fs, ino, 0, &e2_file);
114 if (retval) {
115 com_err(cmdname, retval, "while opening ext2 file");
Theodore Ts'of3db3561997-04-26 13:34:30 +0000116 return;
117 }
Zheng Liud3b4de42013-01-01 20:30:14 +0800118 retval = ext2fs_get_mem(blocksize, &buf);
119 if (retval) {
120 com_err(cmdname, retval, "while allocating memory");
121 return;
122 }
Theodore Ts'o5a513841997-10-25 22:41:14 +0000123 while (1) {
Zheng Liud3b4de42013-01-01 20:30:14 +0800124 retval = ext2fs_file_read(e2_file, buf, blocksize, &got);
Theodore Ts'oefc6f622008-08-27 23:07:54 -0400125 if (retval)
Theodore Ts'o5a513841997-10-25 22:41:14 +0000126 com_err(cmdname, retval, "while reading ext2 file");
127 if (got == 0)
128 break;
129 nbytes = write(fd, buf, got);
Theodore Ts'o54434922003-12-07 01:28:50 -0500130 if ((unsigned) nbytes != got)
Theodore Ts'o5a513841997-10-25 22:41:14 +0000131 com_err(cmdname, errno, "while writing file");
132 }
Zheng Liud3b4de42013-01-01 20:30:14 +0800133 if (buf)
134 ext2fs_free_mem(&buf);
Theodore Ts'o5a513841997-10-25 22:41:14 +0000135 retval = ext2fs_file_close(e2_file);
Theodore Ts'of3db3561997-04-26 13:34:30 +0000136 if (retval) {
Theodore Ts'o5a513841997-10-25 22:41:14 +0000137 com_err(cmdname, retval, "while closing ext2 file");
138 return;
Theodore Ts'of3db3561997-04-26 13:34:30 +0000139 }
Theodore Ts'oefc6f622008-08-27 23:07:54 -0400140
Theodore Ts'o2e8d40d2000-05-27 15:15:40 +0000141 if (preserve)
142 fix_perms("dump_file", &inode, fd, outname);
Theodore Ts'oefc6f622008-08-27 23:07:54 -0400143
Theodore Ts'of3db3561997-04-26 13:34:30 +0000144 return;
145}
146
147void do_dump(int argc, char **argv)
148{
Theodore Ts'ob044c2e2001-01-11 15:26:39 +0000149 ext2_ino_t inode;
150 int fd;
151 int c;
152 int preserve = 0;
Theodore Ts'ob044c2e2001-01-11 15:26:39 +0000153 char *in_fn, *out_fn;
Theodore Ts'oefc6f622008-08-27 23:07:54 -0400154
Theodore Ts'o88494bb2003-05-13 23:03:43 -0400155 reset_getopt();
Theodore Ts'ofc6d9d51997-04-29 14:51:31 +0000156 while ((c = getopt (argc, argv, "p")) != EOF) {
157 switch (c) {
158 case 'p':
159 preserve++;
160 break;
161 default:
Theodore Ts'o52995802009-01-20 13:05:25 -0500162 print_usage:
163 com_err(argv[0], 0, "Usage: dump_inode [-p] "
164 "<file> <output_file>");
Theodore Ts'ofc6d9d51997-04-29 14:51:31 +0000165 return;
166 }
167 }
Theodore Ts'o52995802009-01-20 13:05:25 -0500168 if (optind != argc-2)
169 goto print_usage;
Theodore Ts'of3db3561997-04-26 13:34:30 +0000170
171 if (check_fs_open(argv[0]))
172 return;
173
Theodore Ts'ofc6d9d51997-04-29 14:51:31 +0000174 in_fn = argv[optind];
175 out_fn = argv[optind+1];
176
177 inode = string_to_inode(in_fn);
Theodore Ts'oefc6f622008-08-27 23:07:54 -0400178 if (!inode)
Theodore Ts'of3db3561997-04-26 13:34:30 +0000179 return;
180
Theodore Ts'o819157d2003-01-22 18:25:39 -0500181 fd = open(out_fn, O_CREAT | O_WRONLY | O_TRUNC | O_LARGEFILE, 0666);
Theodore Ts'of3db3561997-04-26 13:34:30 +0000182 if (fd < 0) {
183 com_err(argv[0], errno, "while opening %s for dump_inode",
Theodore Ts'ofc6d9d51997-04-29 14:51:31 +0000184 out_fn);
Theodore Ts'of3db3561997-04-26 13:34:30 +0000185 return;
186 }
187
Theodore Ts'ofc6d9d51997-04-29 14:51:31 +0000188 dump_file(argv[0], inode, fd, preserve, out_fn);
Darrick J. Wongddbe7852013-12-12 12:50:58 -0500189 if (close(fd) != 0) {
190 com_err(argv[0], errno, "while closing %s for dump_inode",
191 out_fn);
192 return;
193 }
Theodore Ts'of3db3561997-04-26 13:34:30 +0000194
Theodore Ts'of3db3561997-04-26 13:34:30 +0000195 return;
196}
197
Theodore Ts'ob044c2e2001-01-11 15:26:39 +0000198static void rdump_symlink(ext2_ino_t ino, struct ext2_inode *inode,
Theodore Ts'o2e8d40d2000-05-27 15:15:40 +0000199 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 (inode->i_blocks == 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
246errout:
247 free(buf);
248}
249
250static int rdump_dirent(struct ext2_dir_entry *, int, int, char *, void *);
251
Theodore Ts'ob044c2e2001-01-11 15:26:39 +0000252static void rdump_inode(ext2_ino_t ino, struct ext2_inode *inode,
Theodore Ts'o2e8d40d2000-05-27 15:15:40 +0000253 const char *name, const char *dumproot)
254{
255 char *fullname;
Theodore Ts'o2e8d40d2000-05-27 15:15:40 +0000256
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;
Theodore Ts'oefac5a72007-04-02 18:30:46 -0400270 fd = open(fullname, O_WRONLY | O_CREAT | O_TRUNC | O_LARGEFILE, S_IRWXU);
Theodore Ts'o2e8d40d2000-05-27 15:15:40 +0000271 if (fd == -1) {
Aaron Crane09727a02014-08-03 21:51:04 -0400272 com_err("rdump", errno, "while opening %s", fullname);
Theodore Ts'o2e8d40d2000-05-27 15:15:40 +0000273 goto errout;
274 }
275 dump_file("rdump", ino, fd, 1, fullname);
Darrick J. Wongddbe7852013-12-12 12:50:58 -0500276 if (close(fd) != 0) {
Aaron Crane09727a02014-08-03 21:51:04 -0400277 com_err("rdump", errno, "while closing %s", fullname);
Darrick J. Wongddbe7852013-12-12 12:50:58 -0500278 goto errout;
279 }
Theodore Ts'o2e8d40d2000-05-27 15:15:40 +0000280 }
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 (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
301errout:
302 free(fullname);
303}
304
Theodore Ts'oefc6f622008-08-27 23:07:54 -0400305static int rdump_dirent(struct ext2_dir_entry *dirent,
Theodore Ts'o54434922003-12-07 01:28:50 -0500306 int offset EXT2FS_ATTR((unused)),
307 int blocksize EXT2FS_ATTR((unused)),
308 char *buf EXT2FS_ATTR((unused)), void *private)
Theodore Ts'o2e8d40d2000-05-27 15:15:40 +0000309{
Brian Behlendorfb7729002007-03-21 15:09:15 -0400310 char name[EXT2_NAME_LEN + 1];
Theodore Ts'o2e8d40d2000-05-27 15:15:40 +0000311 int thislen;
312 const char *dumproot = private;
313 struct ext2_inode inode;
Theodore Ts'o2e8d40d2000-05-27 15:15:40 +0000314
Eric Sandeen11ba79b2011-09-16 15:49:17 -0500315 thislen = dirent->name_len & 0xFF;
Theodore Ts'o2e8d40d2000-05-27 15:15:40 +0000316 strncpy(name, dirent->name, thislen);
317 name[thislen] = 0;
318
Theodore Ts'oe1018ee2002-01-03 04:55:25 -0500319 if (debugfs_read_inode(dirent->inode, &inode, name))
Theodore Ts'o2e8d40d2000-05-27 15:15:40 +0000320 return 0;
Theodore Ts'o2e8d40d2000-05-27 15:15:40 +0000321
322 rdump_inode(dirent->inode, &inode, name, dumproot);
323
324 return 0;
325}
326
327void do_rdump(int argc, char **argv)
328{
Theodore Ts'o2e8d40d2000-05-27 15:15:40 +0000329 struct stat st;
Aaron Craneb6edbf62014-08-03 21:54:14 -0400330 char *dest_dir;
331 int i;
Theodore Ts'o2e8d40d2000-05-27 15:15:40 +0000332
Aaron Craneb6edbf62014-08-03 21:54:14 -0400333 if (common_args_process(argc, argv, 3, INT_MAX, "rdump",
334 "<directory>... <native directory>", 0))
Theodore Ts'o2e8d40d2000-05-27 15:15:40 +0000335 return;
336
Aaron Crane850fe1a2014-08-03 21:53:24 -0400337 /* 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);
Theodore Ts'o2e8d40d2000-05-27 15:15:40 +0000344 return;
345 }
346 if (!S_ISDIR(st.st_mode)) {
Aaron Crane850fe1a2014-08-03 21:53:24 -0400347 com_err("rdump", 0, "%s is not a directory", dest_dir);
Theodore Ts'o2e8d40d2000-05-27 15:15:40 +0000348 return;
349 }
350
Aaron Craneb6edbf62014-08-03 21:54:14 -0400351 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;
Theodore Ts'o2e8d40d2000-05-27 15:15:40 +0000357
Aaron Craneb6edbf62014-08-03 21:54:14 -0400358 if (debugfs_read_inode(ino, &inode, arg))
359 continue;
Theodore Ts'o2e8d40d2000-05-27 15:15:40 +0000360
Aaron Craneb6edbf62014-08-03 21:54:14 -0400361 basename = strrchr(arg, '/');
362 if (basename)
363 basename++;
364 else
365 basename = arg;
366
367 rdump_inode(ino, &inode, basename, dest_dir);
368 }
Theodore Ts'o2e8d40d2000-05-27 15:15:40 +0000369}
370
Theodore Ts'of3db3561997-04-26 13:34:30 +0000371void do_cat(int argc, char **argv)
372{
Theodore Ts'ob044c2e2001-01-11 15:26:39 +0000373 ext2_ino_t inode;
Theodore Ts'of3db3561997-04-26 13:34:30 +0000374
Theodore Ts'oe1018ee2002-01-03 04:55:25 -0500375 if (common_inode_args_process(argc, argv, &inode, 0))
Theodore Ts'of3db3561997-04-26 13:34:30 +0000376 return;
377
Theodore Ts'ofc6d9d51997-04-29 14:51:31 +0000378 fflush(stdout);
379 fflush(stderr);
Theodore Ts'oefc6f622008-08-27 23:07:54 -0400380 dump_file(argv[0], inode, 1, 0, argv[2]);
Theodore Ts'of3db3561997-04-26 13:34:30 +0000381
382 return;
383}
384