Whamcloud - gitweb
Many files:
[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 #include <stdio.h>
9 #include <unistd.h>
10 #include <stdlib.h>
11 #include <ctype.h>
12 #include <string.h>
13 #include <time.h>
14 #ifdef HAVE_ERRNO_H
15 #include <errno.h>
16 #endif
17 #include <sys/types.h>
18 #include <sys/stat.h>
19 #include <fcntl.h>
20 #include <utime.h>
21 #ifdef HAVE_GETOPT_H
22 #include <getopt.h>
23 #else 
24 extern int optind;
25 extern char *optarg;
26 #endif
27 #ifdef HAVE_OPTRESET
28 extern int optreset;            /* defined by BSD, but not others */
29 #endif
30
31 #include "debugfs.h"
32
33 /*
34  * The mode_xlate function translates a linux mode into a native-OS mode_t.
35  */
36 static struct {
37         __u16 lmask;
38         mode_t mask;
39 } mode_table[] = {
40         { LINUX_S_IRUSR, S_IRUSR },
41         { LINUX_S_IWUSR, S_IWUSR },
42         { LINUX_S_IXUSR, S_IXUSR },
43         { LINUX_S_IRGRP, S_IRGRP },
44         { LINUX_S_IWGRP, S_IWGRP },
45         { LINUX_S_IXGRP, S_IXGRP },
46         { LINUX_S_IROTH, S_IROTH },
47         { LINUX_S_IWOTH, S_IWOTH },
48         { LINUX_S_IXOTH, S_IXOTH },
49         { 0, 0 }
50 };
51  
52 static mode_t mode_xlate(__u16 lmode)
53 {
54         mode_t  mode = 0;
55         int     i;
56
57         for (i=0; mode_table[i].lmask; i++) {
58                 if (lmode & mode_table[i].lmask)
59                         mode |= mode_table[i].mask;
60         }
61         return mode;
62 }
63
64 struct dump_block_struct {
65         int             fd;
66         char            *buf;
67         int             left;
68         errcode_t       errcode;
69 };
70
71 static int dump_block(ext2_filsys fs, blk_t *blocknr, int blockcnt,
72                       void *private)
73 {
74         int nbytes, left;
75         off_t   ret_off;
76         
77         struct dump_block_struct *rec = (struct dump_block_struct *) private;
78         
79         if (blockcnt < 0)
80                 return 0;
81
82         if (*blocknr) {
83                 rec->errcode = io_channel_read_blk(fs->io, *blocknr,
84                                                    1, rec->buf);
85                 if (rec->errcode)
86                         return BLOCK_ABORT;
87         } else {
88                 /*
89                  * OK, the file has a hole.  Let's try to seek past
90                  * the hole in the destination file, so that the
91                  * destination file has a hole too.
92                  */
93                 ret_off = lseek(rec->fd, fs->blocksize, SEEK_CUR);
94                 if (ret_off >= 0)
95                         return 0;
96                 memset(rec->buf, 0, fs->blocksize);
97         }
98
99         left = (rec->left > fs->blocksize) ? fs->blocksize : rec->left;
100         rec->left -= left;
101         
102         while (left > 0) {
103                 nbytes = write(rec->fd, rec->buf, left);
104                 if (nbytes == -1) {
105                         if (errno == EINTR)
106                                 continue;
107                         rec->errcode = errno;
108                         return BLOCK_ABORT;
109                 }
110                 left -= nbytes;
111         }
112         if (rec->left <= 0)
113                 return BLOCK_ABORT;
114         return 0;
115 }
116
117 static void dump_file(char *cmdname, ino_t ino, int fd, int preserve,
118                       char *outname)
119 {
120         errcode_t retval;
121         struct dump_block_struct rec;
122         struct ext2_inode       inode;
123         struct utimbuf  ut;
124
125         retval = ext2fs_read_inode(current_fs, ino, &inode);
126         if (retval) {
127                 com_err(cmdname, retval,
128                         "while reading inode %u in dump_file", ino);
129                 return;
130         }
131
132         rec.fd = fd;
133         rec.errcode = 0;
134         rec.buf = malloc(current_fs->blocksize);
135         rec.left = inode.i_size;
136
137         if (rec.buf == 0) {
138                 com_err(cmdname, ENOMEM,
139                         "while allocating block buffer for dump_inode");
140                 return;
141         }
142         
143         retval = ext2fs_block_iterate(current_fs, ino,
144                                       BLOCK_FLAG_HOLE|BLOCK_FLAG_DATA_ONLY,
145                                       NULL, dump_block, &rec);
146         if (retval) {
147                 com_err(cmdname, retval, "while iterating over blocks in %s",
148                         outname);
149                 goto cleanup;
150         }
151         if (rec.errcode) {
152                 com_err(cmdname, retval, "in dump_block while dumping %s",
153                         outname);
154                 goto cleanup;
155         }
156         
157 cleanup:
158         if (preserve) {
159 #ifdef HAVE_FCHOWN
160                 if (fchown(fd, inode.i_uid, inode.i_gid) < 0)
161                         com_err("dump_file", errno,
162                                 "while changing ownership of %s", outname);
163 #else
164                 if (chown(outname, inode.i_uid, inode.i_gid) < 0)
165                         com_err("dump_file", errno,
166                                 "while changing ownership of %s", outname);
167                         
168 #endif
169                 if (fchmod(fd, mode_xlate(inode.i_mode)) < 0)
170                         com_err("dump_file", errno,
171                                 "while setting permissions of %s", outname);
172                 ut.actime = inode.i_atime;
173                 ut.modtime = inode.i_mtime;
174                 close(fd);
175                 if (utime(outname, &ut) < 0)
176                         com_err("dump_file", errno,
177                                 "while setting times on %s", outname);
178         } else if (fd != 1)
179                 close(fd);
180                                     
181         free(rec.buf);
182         return;
183 }
184
185 void do_dump(int argc, char **argv)
186 {
187         ino_t   inode;
188         int     fd;
189         char    c;
190         int     preserve = 0;
191         const char *dump_usage = "Usage: dump_inode [-p] <file> <output_file>";
192         char    *in_fn, *out_fn;
193         
194         optind = 0;
195 #ifdef HAVE_OPTRESET
196         optreset = 1;           /* Makes BSD getopt happy */
197 #endif
198         while ((c = getopt (argc, argv, "p")) != EOF) {
199                 switch (c) {
200                 case 'p':
201                         preserve++;
202                         break;
203                 default:
204                         com_err(argv[0], 0, dump_usage);
205                         return;
206                 }
207         }
208         if (optind != argc-2) {
209                 com_err(argv[0], 0, dump_usage);
210                 return;
211         }
212
213         if (check_fs_open(argv[0]))
214                 return;
215
216         in_fn = argv[optind];
217         out_fn = argv[optind+1];
218
219         inode = string_to_inode(in_fn);
220         if (!inode) 
221                 return;
222
223         fd = open(out_fn, O_CREAT | O_WRONLY | O_TRUNC, 0666);
224         if (fd < 0) {
225                 com_err(argv[0], errno, "while opening %s for dump_inode",
226                         out_fn);
227                 return;
228         }
229
230         dump_file(argv[0], inode, fd, preserve, out_fn);
231
232         return;
233 }
234
235 void do_cat(int argc, char **argv)
236 {
237         ino_t   inode;
238
239         if (argc != 2) {
240                 com_err(argv[0], 0, "Usage: cat <file>");
241                 return;
242         }
243
244         if (check_fs_open(argv[0]))
245                 return;
246
247         inode = string_to_inode(argv[1]);
248         if (!inode) 
249                 return;
250
251         fflush(stdout);
252         fflush(stderr);
253         dump_file(argv[0], inode, 1, 0, argv[2]); 
254
255         return;
256 }
257