• 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 		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