blob: c0c10f0285f2fefdc6e9f9f01c691548fd49e64f [file] [log] [blame]
Theodore Ts'o72ed1262000-11-12 19:32:20 +00001/*
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
Theodore Ts'oefc6f622008-08-27 23:07:54 -04008 * functions in this library. So sue me.
Theodore Ts'o72ed1262000-11-12 19:32:20 +00009 *
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
Theodore Ts'o72ed1262000-11-12 19:32:20 +000033#include "ext2_fs.h"
Theodore Ts'o72ed1262000-11-12 19:32:20 +000034#include "ext2fs.h"
35
Theodore Ts'offf45482003-04-13 00:44:19 -040036#ifndef HAVE_TYPE_SSIZE_T
37typedef int ssize_t;
38#endif
39
Theodore Ts'o72ed1262000-11-12 19:32:20 +000040/*
41 * This function returns 1 if the specified block is all zeros
42 */
43static 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
61errcode_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 = malloc(fs->blocksize * BUF_BLOCKS);
70 if (!buf)
71 return ENOMEM;
Theodore Ts'oefc6f622008-08-27 23:07:54 -040072
Theodore Ts'o72ed1262000-11-12 19:32:20 +000073 for (group = 0; group < fs->group_desc_count; group++) {
74 blk = fs->group_desc[(unsigned)group].bg_inode_table;
Brian Behlendorff93625b2007-03-21 17:43:37 -040075 if (!blk) {
76 retval = EXT2_ET_MISSING_INODE_TABLE;
77 goto errout;
78 }
Theodore Ts'o72ed1262000-11-12 19:32:20 +000079 left = fs->inode_blocks_per_group;
80 while (left) {
81 c = BUF_BLOCKS;
82 if (c > left)
83 c = left;
84 retval = io_channel_read_blk(fs->io, blk, c, buf);
85 if (retval)
86 goto errout;
87 cp = buf;
88 while (c) {
89 if (!(flags & IMAGER_FLAG_SPARSEWRITE)) {
90 d = c;
91 goto skip_sparse;
92 }
93 /* Skip zero blocks */
94 if (check_zero_block(cp, fs->blocksize)) {
95 c--;
96 blk++;
97 left--;
98 cp += fs->blocksize;
99 lseek(fd, fs->blocksize, SEEK_CUR);
100 continue;
101 }
102 /* Find non-zero blocks */
103 for (d=1; d < c; d++) {
104 if (check_zero_block(cp + d*fs->blocksize, fs->blocksize))
105 break;
106 }
107 skip_sparse:
108 actual = write(fd, cp, fs->blocksize * d);
109 if (actual == -1) {
Theodore Ts'ob94bd812001-01-12 17:26:05 +0000110 retval = errno;
Theodore Ts'o72ed1262000-11-12 19:32:20 +0000111 goto errout;
112 }
Theodore Ts'o54434922003-12-07 01:28:50 -0500113 if (actual != (ssize_t) (fs->blocksize * d)) {
Theodore Ts'o72ed1262000-11-12 19:32:20 +0000114 retval = EXT2_ET_SHORT_WRITE;
115 goto errout;
116 }
117 blk += d;
118 left -= d;
119 cp += fs->blocksize * d;
120 c -= d;
121 }
122 }
123 }
124 retval = 0;
125
126errout:
127 free(buf);
128 return retval;
129}
130
131/*
132 * Read in the inode table and stuff it into place
133 */
Theodore Ts'oefc6f622008-08-27 23:07:54 -0400134errcode_t ext2fs_image_inode_read(ext2_filsys fs, int fd,
Theodore Ts'o54434922003-12-07 01:28:50 -0500135 int flags EXT2FS_ATTR((unused)))
Theodore Ts'o72ed1262000-11-12 19:32:20 +0000136{
Theodore Ts'ob94bd812001-01-12 17:26:05 +0000137 unsigned int group, c, left;
Theodore Ts'o72ed1262000-11-12 19:32:20 +0000138 char *buf;
139 blk_t blk;
140 ssize_t actual;
141 errcode_t retval;
142
143 buf = malloc(fs->blocksize * BUF_BLOCKS);
144 if (!buf)
145 return ENOMEM;
Theodore Ts'oefc6f622008-08-27 23:07:54 -0400146
Theodore Ts'o72ed1262000-11-12 19:32:20 +0000147 for (group = 0; group < fs->group_desc_count; group++) {
148 blk = fs->group_desc[(unsigned)group].bg_inode_table;
Theodore Ts'ob94bd812001-01-12 17:26:05 +0000149 if (!blk) {
150 retval = EXT2_ET_MISSING_INODE_TABLE;
151 goto errout;
152 }
Theodore Ts'o72ed1262000-11-12 19:32:20 +0000153 left = fs->inode_blocks_per_group;
154 while (left) {
155 c = BUF_BLOCKS;
156 if (c > left)
157 c = left;
158 actual = read(fd, buf, fs->blocksize * c);
159 if (actual == -1) {
Theodore Ts'ob94bd812001-01-12 17:26:05 +0000160 retval = errno;
Theodore Ts'o72ed1262000-11-12 19:32:20 +0000161 goto errout;
162 }
Theodore Ts'o54434922003-12-07 01:28:50 -0500163 if (actual != (ssize_t) (fs->blocksize * c)) {
Theodore Ts'o72ed1262000-11-12 19:32:20 +0000164 retval = EXT2_ET_SHORT_READ;
165 goto errout;
166 }
167 retval = io_channel_write_blk(fs->io, blk, c, buf);
168 if (retval)
169 goto errout;
Theodore Ts'oefc6f622008-08-27 23:07:54 -0400170
Theodore Ts'o72ed1262000-11-12 19:32:20 +0000171 blk += c;
172 left -= c;
173 }
174 }
175 retval = ext2fs_flush_icache(fs);
176
177errout:
178 free(buf);
179 return retval;
180}
181
182/*
183 * Write out superblock and group descriptors
184 */
Theodore Ts'oefc6f622008-08-27 23:07:54 -0400185errcode_t ext2fs_image_super_write(ext2_filsys fs, int fd,
Theodore Ts'o54434922003-12-07 01:28:50 -0500186 int flags EXT2FS_ATTR((unused)))
Theodore Ts'o72ed1262000-11-12 19:32:20 +0000187{
Theodore Ts'o72ed1262000-11-12 19:32:20 +0000188 char *buf, *cp;
Theodore Ts'o72ed1262000-11-12 19:32:20 +0000189 ssize_t actual;
190 errcode_t retval;
191
192 buf = malloc(fs->blocksize);
193 if (!buf)
194 return ENOMEM;
195
196 /*
197 * Write out the superblock
198 */
199 memset(buf, 0, fs->blocksize);
200 memcpy(buf, fs->super, SUPERBLOCK_SIZE);
201 actual = write(fd, buf, fs->blocksize);
202 if (actual == -1) {
Theodore Ts'ob94bd812001-01-12 17:26:05 +0000203 retval = errno;
Theodore Ts'o72ed1262000-11-12 19:32:20 +0000204 goto errout;
205 }
Theodore Ts'o54434922003-12-07 01:28:50 -0500206 if (actual != (ssize_t) fs->blocksize) {
Theodore Ts'o72ed1262000-11-12 19:32:20 +0000207 retval = EXT2_ET_SHORT_WRITE;
208 goto errout;
209 }
210
211 /*
212 * Now write out the block group descriptors
213 */
214 cp = (char *) fs->group_desc;
215 actual = write(fd, cp, fs->blocksize * fs->desc_blocks);
216 if (actual == -1) {
Theodore Ts'ob94bd812001-01-12 17:26:05 +0000217 retval = errno;
Theodore Ts'o72ed1262000-11-12 19:32:20 +0000218 goto errout;
219 }
Theodore Ts'o54434922003-12-07 01:28:50 -0500220 if (actual != (ssize_t) (fs->blocksize * fs->desc_blocks)) {
Theodore Ts'o72ed1262000-11-12 19:32:20 +0000221 retval = EXT2_ET_SHORT_WRITE;
222 goto errout;
223 }
Theodore Ts'oefc6f622008-08-27 23:07:54 -0400224
Theodore Ts'o72ed1262000-11-12 19:32:20 +0000225 retval = 0;
226
227errout:
228 free(buf);
229 return retval;
230}
231
232/*
233 * Read the superblock and group descriptors and overwrite them.
234 */
Theodore Ts'oefc6f622008-08-27 23:07:54 -0400235errcode_t ext2fs_image_super_read(ext2_filsys fs, int fd,
Theodore Ts'o54434922003-12-07 01:28:50 -0500236 int flags EXT2FS_ATTR((unused)))
Theodore Ts'o72ed1262000-11-12 19:32:20 +0000237{
Theodore Ts'ob94bd812001-01-12 17:26:05 +0000238 char *buf;
Theodore Ts'o72ed1262000-11-12 19:32:20 +0000239 ssize_t actual, size;
240 errcode_t retval;
241
242 size = fs->blocksize * (fs->group_desc_count + 1);
243 buf = malloc(size);
244 if (!buf)
245 return ENOMEM;
246
247 /*
248 * Read it all in.
249 */
250 actual = read(fd, buf, size);
251 if (actual == -1) {
Theodore Ts'ob94bd812001-01-12 17:26:05 +0000252 retval = errno;
Theodore Ts'o72ed1262000-11-12 19:32:20 +0000253 goto errout;
254 }
255 if (actual != size) {
256 retval = EXT2_ET_SHORT_READ;
257 goto errout;
258 }
259
260 /*
261 * Now copy in the superblock and group descriptors
262 */
263 memcpy(fs->super, buf, SUPERBLOCK_SIZE);
264
265 memcpy(fs->group_desc, buf + fs->blocksize,
266 fs->blocksize * fs->group_desc_count);
267
268 retval = 0;
269
270errout:
271 free(buf);
272 return retval;
273}
274
275/*
276 * Write the block/inode bitmaps.
277 */
278errcode_t ext2fs_image_bitmap_write(ext2_filsys fs, int fd, int flags)
279{
Theodore Ts'of1f115a2007-07-23 04:32:48 -0400280 ext2fs_generic_bitmap bmap;
281 errcode_t err, retval;
282 ssize_t actual;
283 __u32 itr, cnt, size;
284 int c, total_size;
285 char buf[1024];
Theodore Ts'o72ed1262000-11-12 19:32:20 +0000286
287 if (flags & IMAGER_FLAG_INODEMAP) {
288 if (!fs->inode_map) {
289 retval = ext2fs_read_inode_bitmap(fs);
290 if (retval)
291 return retval;
292 }
Theodore Ts'of1f115a2007-07-23 04:32:48 -0400293 bmap = fs->inode_map;
294 err = EXT2_ET_MAGIC_INODE_BITMAP;
295 itr = 1;
296 cnt = EXT2_INODES_PER_GROUP(fs->super) * fs->group_desc_count;
Theodore Ts'oa78926e2001-05-03 04:02:29 +0000297 size = (EXT2_INODES_PER_GROUP(fs->super) / 8);
Theodore Ts'o72ed1262000-11-12 19:32:20 +0000298 } else {
299 if (!fs->block_map) {
300 retval = ext2fs_read_block_bitmap(fs);
301 if (retval)
302 return retval;
303 }
Theodore Ts'of1f115a2007-07-23 04:32:48 -0400304 bmap = fs->block_map;
305 err = EXT2_ET_MAGIC_BLOCK_BITMAP;
306 itr = fs->super->s_first_data_block;
307 cnt = EXT2_BLOCKS_PER_GROUP(fs->super) * fs->group_desc_count;
Theodore Ts'o72ed1262000-11-12 19:32:20 +0000308 size = EXT2_BLOCKS_PER_GROUP(fs->super) / 8;
309 }
Theodore Ts'of1f115a2007-07-23 04:32:48 -0400310 total_size = size * fs->group_desc_count;
Theodore Ts'o72ed1262000-11-12 19:32:20 +0000311
Theodore Ts'of1f115a2007-07-23 04:32:48 -0400312 while (cnt > 0) {
313 size = sizeof(buf);
314 if (size > (cnt >> 3))
315 size = (cnt >> 3);
316
Valerie Aurora Henson8f82ef92009-08-05 00:27:10 -0400317 retval = ext2fs_get_generic_bmap_range(bmap, itr,
318 size << 3, buf);
Theodore Ts'of1f115a2007-07-23 04:32:48 -0400319 if (retval)
320 return retval;
321
322 actual = write(fd, buf, size);
323 if (actual == -1)
324 return errno;
325 if (actual != (int) size)
326 return EXT2_ET_SHORT_READ;
Theodore Ts'oefc6f622008-08-27 23:07:54 -0400327
Theodore Ts'of1f115a2007-07-23 04:32:48 -0400328 itr += size << 3;
329 cnt -= size << 3;
Theodore Ts'o72ed1262000-11-12 19:32:20 +0000330 }
Theodore Ts'of1f115a2007-07-23 04:32:48 -0400331
332 size = total_size % fs->blocksize;
333 memset(buf, 0, sizeof(buf));
Theodore Ts'o72ed1262000-11-12 19:32:20 +0000334 if (size) {
335 size = fs->blocksize - size;
336 while (size) {
337 c = size;
Theodore Ts'of1f115a2007-07-23 04:32:48 -0400338 if (c > (int) sizeof(buf))
339 c = sizeof(buf);
340 actual = write(fd, buf, c);
341 if (actual == -1)
342 return errno;
343 if (actual != c)
344 return EXT2_ET_SHORT_WRITE;
Theodore Ts'o72ed1262000-11-12 19:32:20 +0000345 size -= c;
346 }
347 }
Theodore Ts'of1f115a2007-07-23 04:32:48 -0400348 return 0;
Theodore Ts'o72ed1262000-11-12 19:32:20 +0000349}
350
351
352/*
353 * Read the block/inode bitmaps.
354 */
355errcode_t ext2fs_image_bitmap_read(ext2_filsys fs, int fd, int flags)
356{
Theodore Ts'of1f115a2007-07-23 04:32:48 -0400357 ext2fs_generic_bitmap bmap;
358 errcode_t err, retval;
359 __u32 itr, cnt;
360 char buf[1024];
361 unsigned int size;
362 ssize_t actual;
Theodore Ts'o72ed1262000-11-12 19:32:20 +0000363
364 if (flags & IMAGER_FLAG_INODEMAP) {
365 if (!fs->inode_map) {
366 retval = ext2fs_read_inode_bitmap(fs);
367 if (retval)
368 return retval;
369 }
Theodore Ts'of1f115a2007-07-23 04:32:48 -0400370 bmap = fs->inode_map;
371 err = EXT2_ET_MAGIC_INODE_BITMAP;
372 itr = 1;
373 cnt = EXT2_INODES_PER_GROUP(fs->super) * fs->group_desc_count;
Theodore Ts'oa78926e2001-05-03 04:02:29 +0000374 size = (EXT2_INODES_PER_GROUP(fs->super) / 8);
Theodore Ts'o72ed1262000-11-12 19:32:20 +0000375 } else {
376 if (!fs->block_map) {
377 retval = ext2fs_read_block_bitmap(fs);
378 if (retval)
379 return retval;
380 }
Theodore Ts'of1f115a2007-07-23 04:32:48 -0400381 bmap = fs->block_map;
382 err = EXT2_ET_MAGIC_BLOCK_BITMAP;
383 itr = fs->super->s_first_data_block;
384 cnt = EXT2_BLOCKS_PER_GROUP(fs->super) * fs->group_desc_count;
Theodore Ts'o72ed1262000-11-12 19:32:20 +0000385 size = EXT2_BLOCKS_PER_GROUP(fs->super) / 8;
386 }
387
Theodore Ts'of1f115a2007-07-23 04:32:48 -0400388 while (cnt > 0) {
389 size = sizeof(buf);
390 if (size > (cnt >> 3))
391 size = (cnt >> 3);
Theodore Ts'o72ed1262000-11-12 19:32:20 +0000392
Theodore Ts'of1f115a2007-07-23 04:32:48 -0400393 actual = read(fd, buf, size);
394 if (actual == -1)
395 return errno;
396 if (actual != (int) size)
397 return EXT2_ET_SHORT_READ;
398
Valerie Aurora Henson8f82ef92009-08-05 00:27:10 -0400399 retval = ext2fs_set_generic_bmap_range(bmap, itr,
400 size << 3, buf);
Theodore Ts'of1f115a2007-07-23 04:32:48 -0400401 if (retval)
402 return retval;
403
404 itr += size << 3;
405 cnt -= size << 3;
Theodore Ts'o72ed1262000-11-12 19:32:20 +0000406 }
Theodore Ts'of1f115a2007-07-23 04:32:48 -0400407 return 0;
Theodore Ts'o72ed1262000-11-12 19:32:20 +0000408}