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 */
check_zero_block(char * buf,int blocksize)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
ext2fs_image_inode_write(ext2_filsys fs,int fd,int flags)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 */
ext2fs_image_inode_read(ext2_filsys fs,int fd,int flags EXT2FS_ATTR ((unused)))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 */
ext2fs_image_super_write(ext2_filsys fs,int fd,int flags EXT2FS_ATTR ((unused)))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 */
ext2fs_image_super_read(ext2_filsys fs,int fd,int flags EXT2FS_ATTR ((unused)))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 */
ext2fs_image_bitmap_write(ext2_filsys fs,int fd,int flags)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 */
ext2fs_image_bitmap_read(ext2_filsys fs,int fd,int flags)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 }
453