Whamcloud - gitweb
Fix clang warnings on architectures with a 64-bit long
[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         dgrp_t          group;
65         ssize_t         left, c, d;
66         char            *buf, *cp;
67         blk64_t         blk;
68         ssize_t         actual;
69         errcode_t       retval;
70         ext2_loff_t     r;
71
72         buf = malloc(fs->blocksize * BUF_BLOCKS);
73         if (!buf)
74                 return ENOMEM;
75
76         for (group = 0; group < fs->group_desc_count; group++) {
77                 blk = ext2fs_inode_table_loc(fs, group);
78                 if (!blk) {
79                         retval = EXT2_ET_MISSING_INODE_TABLE;
80                         goto errout;
81                 }
82                 left = fs->inode_blocks_per_group;
83                 while (left) {
84                         c = BUF_BLOCKS;
85                         if (c > left)
86                                 c = left;
87                         retval = io_channel_read_blk64(fs->io, blk, c, buf);
88                         if (retval)
89                                 goto errout;
90                         cp = buf;
91                         while (c) {
92                                 if (!(flags & IMAGER_FLAG_SPARSEWRITE)) {
93                                         d = c;
94                                         goto skip_sparse;
95                                 }
96                                 /* Skip zero blocks */
97                                 if (check_zero_block(cp, fs->blocksize)) {
98                                         c--;
99                                         blk++;
100                                         left--;
101                                         cp += fs->blocksize;
102                                         r = ext2fs_llseek(fd, fs->blocksize,
103                                                           SEEK_CUR);
104                                         if (r < 0) {
105                                                 retval = errno;
106                                                 goto errout;
107                                         }
108                                         continue;
109                                 }
110                                 /* Find non-zero blocks */
111                                 for (d = 1; d < c; d++) {
112                                         if (check_zero_block(cp +
113                                                              d * fs->blocksize,
114                                                              fs->blocksize))
115                                                 break;
116                                 }
117                         skip_sparse:
118                                 actual = write(fd, cp, d * fs->blocksize);
119                                 if (actual == -1) {
120                                         retval = errno;
121                                         goto errout;
122                                 }
123                                 if (actual != d * fs->blocksize) {
124                                         retval = EXT2_ET_SHORT_WRITE;
125                                         goto errout;
126                                 }
127                                 blk += d;
128                                 left -= d;
129                                 cp += d * fs->blocksize;
130                                 c -= d;
131                         }
132                 }
133         }
134         retval = 0;
135
136 errout:
137         free(buf);
138         return retval;
139 }
140
141 /*
142  * Read in the inode table and stuff it into place
143  */
144 errcode_t ext2fs_image_inode_read(ext2_filsys fs, int fd,
145                                   int flags EXT2FS_ATTR((unused)))
146 {
147         dgrp_t          group;
148         ssize_t         c, left;
149         char            *buf;
150         blk64_t         blk;
151         ssize_t         actual;
152         errcode_t       retval;
153
154         buf = malloc(fs->blocksize * BUF_BLOCKS);
155         if (!buf)
156                 return ENOMEM;
157
158         for (group = 0; group < fs->group_desc_count; group++) {
159                 blk = ext2fs_inode_table_loc(fs, group);
160                 if (!blk) {
161                         retval = EXT2_ET_MISSING_INODE_TABLE;
162                         goto errout;
163                 }
164                 left = fs->inode_blocks_per_group;
165                 while (left) {
166                         c = BUF_BLOCKS;
167                         if (c > left)
168                                 c = left;
169                         actual = read(fd, buf, fs->blocksize * c);
170                         if (actual == -1) {
171                                 retval = errno;
172                                 goto errout;
173                         }
174                         if (actual != fs->blocksize * c) {
175                                 retval = EXT2_ET_SHORT_READ;
176                                 goto errout;
177                         }
178                         retval = io_channel_write_blk64(fs->io, blk, c, buf);
179                         if (retval)
180                                 goto errout;
181
182                         blk += c;
183                         left -= c;
184                 }
185         }
186         retval = ext2fs_flush_icache(fs);
187
188 errout:
189         free(buf);
190         return retval;
191 }
192
193 /*
194  * Write out superblock and group descriptors
195  */
196 errcode_t ext2fs_image_super_write(ext2_filsys fs, int fd,
197                                    int flags EXT2FS_ATTR((unused)))
198 {
199         char            *buf, *cp;
200         ssize_t         actual;
201         errcode_t       retval;
202 #ifdef WORDS_BIGENDIAN
203         unsigned int    groups_per_block;
204         struct          ext2_group_desc *gdp;
205         int             j;
206 #endif
207
208         if (fs->group_desc == NULL)
209                 return EXT2_ET_NO_GDESC;
210
211         buf = malloc(fs->blocksize);
212         if (!buf)
213                 return ENOMEM;
214
215         /*
216          * Write out the superblock
217          */
218         memset(buf, 0, fs->blocksize);
219 #ifdef WORDS_BIGENDIAN
220         /*
221          * We're writing out superblock so let's convert
222          * it to little endian and then back if needed
223          */
224         ext2fs_swap_super(fs->super);
225         memcpy(buf, fs->super, SUPERBLOCK_SIZE);
226         ext2fs_swap_super(fs->super);
227 #else
228         memcpy(buf, fs->super, SUPERBLOCK_SIZE);
229 #endif
230         actual = write(fd, buf, fs->blocksize);
231         if (actual == -1) {
232                 retval = errno;
233                 goto errout;
234         }
235         if (actual != (ssize_t) fs->blocksize) {
236                 retval = EXT2_ET_SHORT_WRITE;
237                 goto errout;
238         }
239
240         /*
241          * Now write out the block group descriptors
242          */
243
244         cp = (char *) fs->group_desc;
245
246 #ifdef WORDS_BIGENDIAN
247         /*
248          * Convert group descriptors to little endian and back
249          * if needed
250          */
251         groups_per_block = EXT2_DESC_PER_BLOCK(fs->super);
252         for (j=0; j < groups_per_block*fs->desc_blocks; j++) {
253                 gdp = ext2fs_group_desc(fs, fs->group_desc, j);
254                 if (gdp)
255                         ext2fs_swap_group_desc2(fs, gdp);
256         }
257 #endif
258
259         actual = write(fd, cp, (ssize_t)fs->blocksize * fs->desc_blocks);
260
261
262 #ifdef WORDS_BIGENDIAN
263         groups_per_block = EXT2_DESC_PER_BLOCK(fs->super);
264         for (j=0; j < groups_per_block*fs->desc_blocks; j++) {
265                 gdp = ext2fs_group_desc(fs, fs->group_desc, j);
266                 if (gdp)
267                         ext2fs_swap_group_desc2(fs, gdp);
268         }
269 #endif
270
271         if (actual == -1) {
272                 retval = errno;
273                 goto errout;
274         }
275         if (actual != (ssize_t)fs->blocksize * fs->desc_blocks) {
276                 retval = EXT2_ET_SHORT_WRITE;
277                 goto errout;
278         }
279
280         retval = 0;
281
282 errout:
283         free(buf);
284         return retval;
285 }
286
287 /*
288  * Read the superblock and group descriptors and overwrite them.
289  */
290 errcode_t ext2fs_image_super_read(ext2_filsys fs, int fd,
291                                   int flags EXT2FS_ATTR((unused)))
292 {
293         char            *buf;
294         ssize_t         actual, size;
295         errcode_t       retval;
296
297         size = (ssize_t)fs->blocksize * (fs->group_desc_count + 1);
298         buf = malloc(size);
299         if (!buf)
300                 return ENOMEM;
301
302         /*
303          * Read it all in.
304          */
305         actual = read(fd, buf, size);
306         if (actual == -1) {
307                 retval = errno;
308                 goto errout;
309         }
310         if (actual != size) {
311                 retval = EXT2_ET_SHORT_READ;
312                 goto errout;
313         }
314
315         /*
316          * Now copy in the superblock and group descriptors
317          */
318         memcpy(fs->super, buf, SUPERBLOCK_SIZE);
319
320         memcpy(fs->group_desc, buf + fs->blocksize,
321                (ssize_t)fs->blocksize * fs->group_desc_count);
322
323         retval = 0;
324
325 errout:
326         free(buf);
327         return retval;
328 }
329
330 /*
331  * Write the block/inode bitmaps.
332  */
333 errcode_t ext2fs_image_bitmap_write(ext2_filsys fs, int fd, int flags)
334 {
335         ext2fs_generic_bitmap   bmap;
336         errcode_t               retval;
337         ssize_t                 actual;
338         size_t                  c;
339         __u64                   itr, cnt, size, total_size;
340         char                    buf[1024];
341
342         if (flags & IMAGER_FLAG_INODEMAP) {
343                 if (!fs->inode_map) {
344                         retval = ext2fs_read_inode_bitmap(fs);
345                         if (retval)
346                                 return retval;
347                 }
348                 bmap = fs->inode_map;
349                 itr = 1;
350                 cnt = (__u64)EXT2_INODES_PER_GROUP(fs->super) *
351                         fs->group_desc_count;
352                 size = (EXT2_INODES_PER_GROUP(fs->super) / 8);
353         } else {
354                 if (!fs->block_map) {
355                         retval = ext2fs_read_block_bitmap(fs);
356                         if (retval)
357                                 return retval;
358                 }
359                 bmap = fs->block_map;
360                 itr = fs->super->s_first_data_block;
361                 cnt = EXT2_GROUPS_TO_CLUSTERS(fs->super, fs->group_desc_count);
362                 size = EXT2_CLUSTERS_PER_GROUP(fs->super) / 8;
363         }
364         total_size = size * fs->group_desc_count;
365
366         while (cnt > 0) {
367                 size = sizeof(buf);
368                 if (size > (cnt >> 3))
369                         size = (cnt >> 3);
370
371                 retval = ext2fs_get_generic_bmap_range(bmap, itr,
372                                                        size << 3, buf);
373                 if (retval)
374                         return retval;
375
376                 actual = write(fd, buf, size);
377                 if (actual == -1)
378                         return errno;
379                 if (actual != (int) size)
380                         return EXT2_ET_SHORT_READ;
381
382                 itr += size << 3;
383                 cnt -= size << 3;
384         }
385
386         size = total_size % fs->blocksize;
387         memset(buf, 0, sizeof(buf));
388         if (size) {
389                 size = fs->blocksize - size;
390                 while (size) {
391                         c = size;
392                         if (c > (int) sizeof(buf))
393                                 c = sizeof(buf);
394                         actual = write(fd, buf, c);
395                         if (actual < 0)
396                                 return errno;
397                         if ((size_t) actual != c)
398                                 return EXT2_ET_SHORT_WRITE;
399                         size -= c;
400                 }
401         }
402         return 0;
403 }
404
405
406 /*
407  * Read the block/inode bitmaps.
408  */
409 errcode_t ext2fs_image_bitmap_read(ext2_filsys fs, int fd, int flags)
410 {
411         ext2fs_generic_bitmap   bmap;
412         errcode_t               retval;
413         __u64                   itr, cnt;
414         char                    buf[1024];
415         unsigned int            size;
416         ssize_t                 actual;
417
418         if (flags & IMAGER_FLAG_INODEMAP) {
419                 if (!fs->inode_map) {
420                         retval = ext2fs_read_inode_bitmap(fs);
421                         if (retval)
422                                 return retval;
423                 }
424                 bmap = fs->inode_map;
425                 itr = 1;
426                 cnt = (__u64)EXT2_INODES_PER_GROUP(fs->super) *
427                         fs->group_desc_count;
428                 size = (EXT2_INODES_PER_GROUP(fs->super) / 8);
429         } else {
430                 if (!fs->block_map) {
431                         retval = ext2fs_read_block_bitmap(fs);
432                         if (retval)
433                                 return retval;
434                 }
435                 bmap = fs->block_map;
436                 itr = fs->super->s_first_data_block;
437                 cnt = EXT2_GROUPS_TO_BLOCKS(fs->super, fs->group_desc_count);
438                 size = EXT2_BLOCKS_PER_GROUP(fs->super) / 8;
439         }
440
441         while (cnt > 0) {
442                 size = sizeof(buf);
443                 if (size > (cnt >> 3))
444                         size = (cnt >> 3);
445
446                 actual = read(fd, buf, size);
447                 if (actual == -1)
448                         return errno;
449                 if (actual != (int) size)
450                         return EXT2_ET_SHORT_READ;
451
452                 retval = ext2fs_set_generic_bmap_range(bmap, itr,
453                                                        size << 3, buf);
454                 if (retval)
455                         return retval;
456
457                 itr += size << 3;
458                 cnt -= size << 3;
459         }
460         return 0;
461 }