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