Whamcloud - gitweb
e2fsck: allow to fix some superblock corruptions
[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         buf = malloc(fs->blocksize);
205         if (!buf)
206                 return ENOMEM;
207
208         /*
209          * Write out the superblock
210          */
211         memset(buf, 0, fs->blocksize);
212 #ifdef WORDS_BIGENDIAN
213         /*
214          * We're writing out superblock so let's convert
215          * it to little endian and then back if needed
216          */
217         ext2fs_swap_super(fs->super);
218         memcpy(buf, fs->super, SUPERBLOCK_SIZE);
219         ext2fs_swap_super(fs->super);
220 #else
221         memcpy(buf, fs->super, SUPERBLOCK_SIZE);
222 #endif
223         actual = write(fd, buf, fs->blocksize);
224         if (actual == -1) {
225                 retval = errno;
226                 goto errout;
227         }
228         if (actual != (ssize_t) fs->blocksize) {
229                 retval = EXT2_ET_SHORT_WRITE;
230                 goto errout;
231         }
232
233         /*
234          * Now write out the block group descriptors
235          */
236
237         cp = (char *) fs->group_desc;
238
239 #ifdef WORDS_BIGENDIAN
240         /*
241          * Convert group descriptors to little endian and back
242          * if needed
243          */
244         groups_per_block = EXT2_DESC_PER_BLOCK(fs->super);
245         gdp = (struct ext2_group_desc *) cp;
246         for (j=0; j < groups_per_block*fs->desc_blocks; j++) {
247                 gdp = ext2fs_group_desc(fs, fs->group_desc, j);
248                 ext2fs_swap_group_desc2(fs, gdp);
249         }
250 #endif
251
252         actual = write(fd, cp, fs->blocksize * fs->desc_blocks);
253
254
255 #ifdef WORDS_BIGENDIAN
256         groups_per_block = EXT2_DESC_PER_BLOCK(fs->super);
257         gdp = (struct ext2_group_desc *) cp;
258         for (j=0; j < groups_per_block*fs->desc_blocks; j++) {
259                 gdp = ext2fs_group_desc(fs, fs->group_desc, j);
260                 ext2fs_swap_group_desc2(fs, gdp);
261         }
262 #endif
263
264         if (actual == -1) {
265                 retval = errno;
266                 goto errout;
267         }
268         if (actual != (ssize_t) (fs->blocksize * fs->desc_blocks)) {
269                 retval = EXT2_ET_SHORT_WRITE;
270                 goto errout;
271         }
272
273         retval = 0;
274
275 errout:
276         free(buf);
277         return retval;
278 }
279
280 /*
281  * Read the superblock and group descriptors and overwrite them.
282  */
283 errcode_t ext2fs_image_super_read(ext2_filsys fs, int fd,
284                                   int flags EXT2FS_ATTR((unused)))
285 {
286         char            *buf;
287         ssize_t         actual, size;
288         errcode_t       retval;
289
290         size = fs->blocksize * (fs->group_desc_count + 1);
291         buf = malloc(size);
292         if (!buf)
293                 return ENOMEM;
294
295         /*
296          * Read it all in.
297          */
298         actual = read(fd, buf, size);
299         if (actual == -1) {
300                 retval = errno;
301                 goto errout;
302         }
303         if (actual != size) {
304                 retval = EXT2_ET_SHORT_READ;
305                 goto errout;
306         }
307
308         /*
309          * Now copy in the superblock and group descriptors
310          */
311         memcpy(fs->super, buf, SUPERBLOCK_SIZE);
312
313         memcpy(fs->group_desc, buf + fs->blocksize,
314                fs->blocksize * fs->group_desc_count);
315
316         retval = 0;
317
318 errout:
319         free(buf);
320         return retval;
321 }
322
323 /*
324  * Write the block/inode bitmaps.
325  */
326 errcode_t ext2fs_image_bitmap_write(ext2_filsys fs, int fd, int flags)
327 {
328         ext2fs_generic_bitmap   bmap;
329         errcode_t               retval;
330         ssize_t                 actual;
331         size_t                  c;
332         __u64                   itr, cnt, size, total_size;
333         char                    buf[1024];
334
335         if (flags & IMAGER_FLAG_INODEMAP) {
336                 if (!fs->inode_map) {
337                         retval = ext2fs_read_inode_bitmap(fs);
338                         if (retval)
339                                 return retval;
340                 }
341                 bmap = fs->inode_map;
342                 itr = 1;
343                 cnt = EXT2_INODES_PER_GROUP(fs->super) * fs->group_desc_count;
344                 size = (EXT2_INODES_PER_GROUP(fs->super) / 8);
345         } else {
346                 if (!fs->block_map) {
347                         retval = ext2fs_read_block_bitmap(fs);
348                         if (retval)
349                                 return retval;
350                 }
351                 bmap = fs->block_map;
352                 itr = fs->super->s_first_data_block;
353                 cnt = EXT2_GROUPS_TO_CLUSTERS(fs->super, fs->group_desc_count);
354                 size = EXT2_CLUSTERS_PER_GROUP(fs->super) / 8;
355         }
356         total_size = size * fs->group_desc_count;
357
358         while (cnt > 0) {
359                 size = sizeof(buf);
360                 if (size > (cnt >> 3))
361                         size = (cnt >> 3);
362
363                 retval = ext2fs_get_generic_bmap_range(bmap, itr,
364                                                        size << 3, buf);
365                 if (retval)
366                         return retval;
367
368                 actual = write(fd, buf, size);
369                 if (actual == -1)
370                         return errno;
371                 if (actual != (int) size)
372                         return EXT2_ET_SHORT_READ;
373
374                 itr += size << 3;
375                 cnt -= size << 3;
376         }
377
378         size = total_size % fs->blocksize;
379         memset(buf, 0, sizeof(buf));
380         if (size) {
381                 size = fs->blocksize - size;
382                 while (size) {
383                         c = size;
384                         if (c > (int) sizeof(buf))
385                                 c = sizeof(buf);
386                         actual = write(fd, buf, c);
387                         if (actual < 0)
388                                 return errno;
389                         if ((size_t) actual != c)
390                                 return EXT2_ET_SHORT_WRITE;
391                         size -= c;
392                 }
393         }
394         return 0;
395 }
396
397
398 /*
399  * Read the block/inode bitmaps.
400  */
401 errcode_t ext2fs_image_bitmap_read(ext2_filsys fs, int fd, int flags)
402 {
403         ext2fs_generic_bitmap   bmap;
404         errcode_t               retval;
405         __u64                   itr, cnt;
406         char                    buf[1024];
407         unsigned int            size;
408         ssize_t                 actual;
409
410         if (flags & IMAGER_FLAG_INODEMAP) {
411                 if (!fs->inode_map) {
412                         retval = ext2fs_read_inode_bitmap(fs);
413                         if (retval)
414                                 return retval;
415                 }
416                 bmap = fs->inode_map;
417                 itr = 1;
418                 cnt = EXT2_INODES_PER_GROUP(fs->super) * fs->group_desc_count;
419                 size = (EXT2_INODES_PER_GROUP(fs->super) / 8);
420         } else {
421                 if (!fs->block_map) {
422                         retval = ext2fs_read_block_bitmap(fs);
423                         if (retval)
424                                 return retval;
425                 }
426                 bmap = fs->block_map;
427                 itr = fs->super->s_first_data_block;
428                 cnt = EXT2_GROUPS_TO_BLOCKS(fs->super, fs->group_desc_count);
429                 size = EXT2_BLOCKS_PER_GROUP(fs->super) / 8;
430         }
431
432         while (cnt > 0) {
433                 size = sizeof(buf);
434                 if (size > (cnt >> 3))
435                         size = (cnt >> 3);
436
437                 actual = read(fd, buf, size);
438                 if (actual == -1)
439                         return errno;
440                 if (actual != (int) size)
441                         return EXT2_ET_SHORT_READ;
442
443                 retval = ext2fs_set_generic_bmap_range(bmap, itr,
444                                                        size << 3, buf);
445                 if (retval)
446                         return retval;
447
448                 itr += size << 3;
449                 cnt -= size << 3;
450         }
451         return 0;
452 }