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