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