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