Whamcloud - gitweb
afa9a21640582845ace7a9cef6a2880c27db645d
[tools/e2fsprogs.git] / lib / ext2fs / imager.c
1 /*
2  * image.c --- writes out the critical parts of the filesystem as a
3  *      flat file.
4  *
5  * Copyright (C) 2000 Theodore Ts'o.
6  *
7  * Note: this uses the POSIX IO interfaces, unlike most of the other
8  * functions in this library.  So sue me.
9  *
10  * %Begin-Header%
11  * This file may be redistributed under the terms of the GNU Library
12  * General Public License, version 2.
13  * %End-Header%
14  */
15
16 #include <stdio.h>
17 #include <string.h>
18 #if HAVE_UNISTD_H
19 #include <unistd.h>
20 #endif
21 #if HAVE_ERRNO_H
22 #include <errno.h>
23 #endif
24 #include <fcntl.h>
25 #include <time.h>
26 #if HAVE_SYS_STAT_H
27 #include <sys/stat.h>
28 #endif
29 #if HAVE_SYS_TYPES_H
30 #include <sys/types.h>
31 #endif
32
33 #include "ext2_fs.h"
34 #include "ext2fs.h"
35
36 #ifndef HAVE_TYPE_SSIZE_T
37 typedef int ssize_t;
38 #endif
39
40 /*
41  * This function returns 1 if the specified block is all zeros
42  */
43 static int check_zero_block(char *buf, int blocksize)
44 {
45         char    *cp = buf;
46         int     left = blocksize;
47
48         while (left > 0) {
49                 if (*cp++)
50                         return 0;
51                 left--;
52         }
53         return 1;
54 }
55
56 /*
57  * Write the inode table out as a single block.
58  */
59 #define BUF_BLOCKS      32
60
61 errcode_t ext2fs_image_inode_write(ext2_filsys fs, int fd, int flags)
62 {
63         unsigned int    group, left, c, d;
64         char            *buf, *cp;
65         blk_t           blk;
66         ssize_t         actual;
67         errcode_t       retval;
68
69         buf = malloc(fs->blocksize * BUF_BLOCKS);
70         if (!buf)
71                 return ENOMEM;
72
73         for (group = 0; group < fs->group_desc_count; group++) {
74                 blk = fs->group_desc[(unsigned)group].bg_inode_table;
75                 if (!blk) {
76                         retval = EXT2_ET_MISSING_INODE_TABLE;
77                         goto errout;
78                 }
79                 left = fs->inode_blocks_per_group;
80                 while (left) {
81                         c = BUF_BLOCKS;
82                         if (c > left)
83                                 c = left;
84                         retval = io_channel_read_blk(fs->io, blk, c, buf);
85                         if (retval)
86                                 goto errout;
87                         cp = buf;
88                         while (c) {
89                                 if (!(flags & IMAGER_FLAG_SPARSEWRITE)) {
90                                         d = c;
91                                         goto skip_sparse;
92                                 }
93                                 /* Skip zero blocks */
94                                 if (check_zero_block(cp, fs->blocksize)) {
95                                         c--;
96                                         blk++;
97                                         left--;
98                                         cp += fs->blocksize;
99                                         lseek(fd, fs->blocksize, SEEK_CUR);
100                                         continue;
101                                 }
102                                 /* Find non-zero blocks */
103                                 for (d=1; d < c; d++) {
104                                         if (check_zero_block(cp + d*fs->blocksize, fs->blocksize))
105                                                 break;
106                                 }
107                         skip_sparse:
108                                 actual = write(fd, cp, fs->blocksize * d);
109                                 if (actual == -1) {
110                                         retval = errno;
111                                         goto errout;
112                                 }
113                                 if (actual != (ssize_t) (fs->blocksize * d)) {
114                                         retval = EXT2_ET_SHORT_WRITE;
115                                         goto errout;
116                                 }
117                                 blk += d;
118                                 left -= d;
119                                 cp += fs->blocksize * d;
120                                 c -= d;
121                         }
122                 }
123         }
124         retval = 0;
125
126 errout:
127         free(buf);
128         return retval;
129 }
130
131 /*
132  * Read in the inode table and stuff it into place
133  */
134 errcode_t ext2fs_image_inode_read(ext2_filsys fs, int fd,
135                                   int flags EXT2FS_ATTR((unused)))
136 {
137         unsigned int    group, c, left;
138         char            *buf;
139         blk_t           blk;
140         ssize_t         actual;
141         errcode_t       retval;
142
143         buf = malloc(fs->blocksize * BUF_BLOCKS);
144         if (!buf)
145                 return ENOMEM;
146
147         for (group = 0; group < fs->group_desc_count; group++) {
148                 blk = fs->group_desc[(unsigned)group].bg_inode_table;
149                 if (!blk) {
150                         retval = EXT2_ET_MISSING_INODE_TABLE;
151                         goto errout;
152                 }
153                 left = fs->inode_blocks_per_group;
154                 while (left) {
155                         c = BUF_BLOCKS;
156                         if (c > left)
157                                 c = left;
158                         actual = read(fd, buf, fs->blocksize * c);
159                         if (actual == -1) {
160                                 retval = errno;
161                                 goto errout;
162                         }
163                         if (actual != (ssize_t) (fs->blocksize * c)) {
164                                 retval = EXT2_ET_SHORT_READ;
165                                 goto errout;
166                         }
167                         retval = io_channel_write_blk(fs->io, blk, c, buf);
168                         if (retval)
169                                 goto errout;
170
171                         blk += c;
172                         left -= c;
173                 }
174         }
175         retval = ext2fs_flush_icache(fs);
176
177 errout:
178         free(buf);
179         return retval;
180 }
181
182 /*
183  * Write out superblock and group descriptors
184  */
185 errcode_t ext2fs_image_super_write(ext2_filsys fs, int fd,
186                                    int flags EXT2FS_ATTR((unused)))
187 {
188         char            *buf, *cp;
189         ssize_t         actual;
190         errcode_t       retval;
191
192         buf = malloc(fs->blocksize);
193         if (!buf)
194                 return ENOMEM;
195
196         /*
197          * Write out the superblock
198          */
199         memset(buf, 0, fs->blocksize);
200         memcpy(buf, fs->super, SUPERBLOCK_SIZE);
201         actual = write(fd, buf, fs->blocksize);
202         if (actual == -1) {
203                 retval = errno;
204                 goto errout;
205         }
206         if (actual != (ssize_t) fs->blocksize) {
207                 retval = EXT2_ET_SHORT_WRITE;
208                 goto errout;
209         }
210
211         /*
212          * Now write out the block group descriptors
213          */
214         cp = (char *) fs->group_desc;
215         actual = write(fd, cp, fs->blocksize * fs->desc_blocks);
216         if (actual == -1) {
217                 retval = errno;
218                 goto errout;
219         }
220         if (actual != (ssize_t) (fs->blocksize * fs->desc_blocks)) {
221                 retval = EXT2_ET_SHORT_WRITE;
222                 goto errout;
223         }
224
225         retval = 0;
226
227 errout:
228         free(buf);
229         return retval;
230 }
231
232 /*
233  * Read the superblock and group descriptors and overwrite them.
234  */
235 errcode_t ext2fs_image_super_read(ext2_filsys fs, int fd,
236                                   int flags EXT2FS_ATTR((unused)))
237 {
238         char            *buf;
239         ssize_t         actual, size;
240         errcode_t       retval;
241
242         size = fs->blocksize * (fs->group_desc_count + 1);
243         buf = malloc(size);
244         if (!buf)
245                 return ENOMEM;
246
247         /*
248          * Read it all in.
249          */
250         actual = read(fd, buf, size);
251         if (actual == -1) {
252                 retval = errno;
253                 goto errout;
254         }
255         if (actual != size) {
256                 retval = EXT2_ET_SHORT_READ;
257                 goto errout;
258         }
259
260         /*
261          * Now copy in the superblock and group descriptors
262          */
263         memcpy(fs->super, buf, SUPERBLOCK_SIZE);
264
265         memcpy(fs->group_desc, buf + fs->blocksize,
266                fs->blocksize * fs->group_desc_count);
267
268         retval = 0;
269
270 errout:
271         free(buf);
272         return retval;
273 }
274
275 /*
276  * Write the block/inode bitmaps.
277  */
278 errcode_t ext2fs_image_bitmap_write(ext2_filsys fs, int fd, int flags)
279 {
280         ext2fs_generic_bitmap   bmap;
281         errcode_t               err, retval;
282         ssize_t                 actual;
283         __u32                   itr, cnt, size;
284         int                     c, total_size;
285         char                    buf[1024];
286
287         if (flags & IMAGER_FLAG_INODEMAP) {
288                 if (!fs->inode_map) {
289                         retval = ext2fs_read_inode_bitmap(fs);
290                         if (retval)
291                                 return retval;
292                 }
293                 bmap = fs->inode_map;
294                 err = EXT2_ET_MAGIC_INODE_BITMAP;
295                 itr = 1;
296                 cnt = EXT2_INODES_PER_GROUP(fs->super) * fs->group_desc_count;
297                 size = (EXT2_INODES_PER_GROUP(fs->super) / 8);
298         } else {
299                 if (!fs->block_map) {
300                         retval = ext2fs_read_block_bitmap(fs);
301                         if (retval)
302                                 return retval;
303                 }
304                 bmap = fs->block_map;
305                 err = EXT2_ET_MAGIC_BLOCK_BITMAP;
306                 itr = fs->super->s_first_data_block;
307                 cnt = EXT2_BLOCKS_PER_GROUP(fs->super) * fs->group_desc_count;
308                 size = EXT2_BLOCKS_PER_GROUP(fs->super) / 8;
309         }
310         total_size = size * fs->group_desc_count;
311
312         while (cnt > 0) {
313                 size = sizeof(buf);
314                 if (size > (cnt >> 3))
315                         size = (cnt >> 3);
316
317                 retval = ext2fs_get_generic_bitmap_range(bmap,
318                                  err, itr, size << 3, buf);
319                 if (retval)
320                         return retval;
321
322                 actual = write(fd, buf, size);
323                 if (actual == -1)
324                         return errno;
325                 if (actual != (int) size)
326                         return EXT2_ET_SHORT_READ;
327
328                 itr += size << 3;
329                 cnt -= size << 3;
330         }
331
332         size = total_size % fs->blocksize;
333         memset(buf, 0, sizeof(buf));
334         if (size) {
335                 size = fs->blocksize - size;
336                 while (size) {
337                         c = size;
338                         if (c > (int) sizeof(buf))
339                                 c = sizeof(buf);
340                         actual = write(fd, buf, c);
341                         if (actual == -1)
342                                 return errno;
343                         if (actual != c)
344                                 return EXT2_ET_SHORT_WRITE;
345                         size -= c;
346                 }
347         }
348         return 0;
349 }
350
351
352 /*
353  * Read the block/inode bitmaps.
354  */
355 errcode_t ext2fs_image_bitmap_read(ext2_filsys fs, int fd, int flags)
356 {
357         ext2fs_generic_bitmap   bmap;
358         errcode_t               err, retval;
359         __u32                   itr, cnt;
360         char                    buf[1024];
361         unsigned int            size;
362         ssize_t                 actual;
363
364         if (flags & IMAGER_FLAG_INODEMAP) {
365                 if (!fs->inode_map) {
366                         retval = ext2fs_read_inode_bitmap(fs);
367                         if (retval)
368                                 return retval;
369                 }
370                 bmap = fs->inode_map;
371                 err = EXT2_ET_MAGIC_INODE_BITMAP;
372                 itr = 1;
373                 cnt = EXT2_INODES_PER_GROUP(fs->super) * fs->group_desc_count;
374                 size = (EXT2_INODES_PER_GROUP(fs->super) / 8);
375         } else {
376                 if (!fs->block_map) {
377                         retval = ext2fs_read_block_bitmap(fs);
378                         if (retval)
379                                 return retval;
380                 }
381                 bmap = fs->block_map;
382                 err = EXT2_ET_MAGIC_BLOCK_BITMAP;
383                 itr = fs->super->s_first_data_block;
384                 cnt = EXT2_BLOCKS_PER_GROUP(fs->super) * fs->group_desc_count;
385                 size = EXT2_BLOCKS_PER_GROUP(fs->super) / 8;
386         }
387
388         while (cnt > 0) {
389                 size = sizeof(buf);
390                 if (size > (cnt >> 3))
391                         size = (cnt >> 3);
392
393                 actual = read(fd, buf, size);
394                 if (actual == -1)
395                         return errno;
396                 if (actual != (int) size)
397                         return EXT2_ET_SHORT_READ;
398
399                 retval = ext2fs_set_generic_bitmap_range(bmap,
400                                  err, itr, size << 3, buf);
401                 if (retval)
402                         return retval;
403
404                 itr += size << 3;
405                 cnt -= size << 3;
406         }
407         return 0;
408 }