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