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