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