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