Home | History | Annotate | Download | only in ext2fs
      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 <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 
     33 #include "ext2_fs.h"
     34 #include "ext2fs.h"
     35 
     36 #ifndef HAVE_TYPE_SSIZE_T
     37 typedef int ssize_t;
     38 #endif
     39 
     40 /*
     41  * This function returns 1 if the specified block is all zeros
     42  */
     43 static 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 
     61 errcode_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 	blk64_t		blk;
     66 	ssize_t		actual;
     67 	errcode_t	retval;
     68 	off_t		r;
     69 
     70 	buf = malloc(fs->blocksize * BUF_BLOCKS);
     71 	if (!buf)
     72 		return ENOMEM;
     73 
     74 	for (group = 0; group < fs->group_desc_count; group++) {
     75 		blk = ext2fs_inode_table_loc(fs, (unsigned)group);
     76 		if (!blk) {
     77 			retval = EXT2_ET_MISSING_INODE_TABLE;
     78 			goto errout;
     79 		}
     80 		left = fs->inode_blocks_per_group;
     81 		while (left) {
     82 			c = BUF_BLOCKS;
     83 			if (c > left)
     84 				c = left;
     85 			retval = io_channel_read_blk64(fs->io, blk, c, buf);
     86 			if (retval)
     87 				goto errout;
     88 			cp = buf;
     89 			while (c) {
     90 				if (!(flags & IMAGER_FLAG_SPARSEWRITE)) {
     91 					d = c;
     92 					goto skip_sparse;
     93 				}
     94 				/* Skip zero blocks */
     95 				if (check_zero_block(cp, fs->blocksize)) {
     96 					c--;
     97 					blk++;
     98 					left--;
     99 					cp += fs->blocksize;
    100 					r = lseek(fd, fs->blocksize, SEEK_CUR);
    101 					if (r < 0) {
    102 						retval = errno;
    103 						goto errout;
    104 					}
    105 					continue;
    106 				}
    107 				/* Find non-zero blocks */
    108 				for (d=1; d < c; d++) {
    109 					if (check_zero_block(cp + d*fs->blocksize, fs->blocksize))
    110 						break;
    111 				}
    112 			skip_sparse:
    113 				actual = write(fd, cp, fs->blocksize * d);
    114 				if (actual == -1) {
    115 					retval = errno;
    116 					goto errout;
    117 				}
    118 				if (actual != (ssize_t) (fs->blocksize * d)) {
    119 					retval = EXT2_ET_SHORT_WRITE;
    120 					goto errout;
    121 				}
    122 				blk += d;
    123 				left -= d;
    124 				cp += fs->blocksize * d;
    125 				c -= d;
    126 			}
    127 		}
    128 	}
    129 	retval = 0;
    130 
    131 errout:
    132 	free(buf);
    133 	return retval;
    134 }
    135 
    136 /*
    137  * Read in the inode table and stuff it into place
    138  */
    139 errcode_t ext2fs_image_inode_read(ext2_filsys fs, int fd,
    140 				  int flags EXT2FS_ATTR((unused)))
    141 {
    142 	unsigned int	group, c, left;
    143 	char		*buf;
    144 	blk64_t		blk;
    145 	ssize_t		actual;
    146 	errcode_t	retval;
    147 
    148 	buf = malloc(fs->blocksize * BUF_BLOCKS);
    149 	if (!buf)
    150 		return ENOMEM;
    151 
    152 	for (group = 0; group < fs->group_desc_count; group++) {
    153 		blk = ext2fs_inode_table_loc(fs, (unsigned)group);
    154 		if (!blk) {
    155 			retval = EXT2_ET_MISSING_INODE_TABLE;
    156 			goto errout;
    157 		}
    158 		left = fs->inode_blocks_per_group;
    159 		while (left) {
    160 			c = BUF_BLOCKS;
    161 			if (c > left)
    162 				c = left;
    163 			actual = read(fd, buf, fs->blocksize * c);
    164 			if (actual == -1) {
    165 				retval = errno;
    166 				goto errout;
    167 			}
    168 			if (actual != (ssize_t) (fs->blocksize * c)) {
    169 				retval = EXT2_ET_SHORT_READ;
    170 				goto errout;
    171 			}
    172 			retval = io_channel_write_blk64(fs->io, blk, c, buf);
    173 			if (retval)
    174 				goto errout;
    175 
    176 			blk += c;
    177 			left -= c;
    178 		}
    179 	}
    180 	retval = ext2fs_flush_icache(fs);
    181 
    182 errout:
    183 	free(buf);
    184 	return retval;
    185 }
    186 
    187 /*
    188  * Write out superblock and group descriptors
    189  */
    190 errcode_t ext2fs_image_super_write(ext2_filsys fs, int fd,
    191 				   int flags EXT2FS_ATTR((unused)))
    192 {
    193 	char		*buf, *cp;
    194 	ssize_t		actual;
    195 	errcode_t	retval;
    196 
    197 	buf = malloc(fs->blocksize);
    198 	if (!buf)
    199 		return ENOMEM;
    200 
    201 	/*
    202 	 * Write out the superblock
    203 	 */
    204 	memset(buf, 0, fs->blocksize);
    205 	memcpy(buf, fs->super, SUPERBLOCK_SIZE);
    206 	actual = write(fd, buf, fs->blocksize);
    207 	if (actual == -1) {
    208 		retval = errno;
    209 		goto errout;
    210 	}
    211 	if (actual != (ssize_t) fs->blocksize) {
    212 		retval = EXT2_ET_SHORT_WRITE;
    213 		goto errout;
    214 	}
    215 
    216 	/*
    217 	 * Now write out the block group descriptors
    218 	 */
    219 	cp = (char *) fs->group_desc;
    220 	actual = write(fd, cp, fs->blocksize * fs->desc_blocks);
    221 	if (actual == -1) {
    222 		retval = errno;
    223 		goto errout;
    224 	}
    225 	if (actual != (ssize_t) (fs->blocksize * fs->desc_blocks)) {
    226 		retval = EXT2_ET_SHORT_WRITE;
    227 		goto errout;
    228 	}
    229 
    230 	retval = 0;
    231 
    232 errout:
    233 	free(buf);
    234 	return retval;
    235 }
    236 
    237 /*
    238  * Read the superblock and group descriptors and overwrite them.
    239  */
    240 errcode_t ext2fs_image_super_read(ext2_filsys fs, int fd,
    241 				  int flags EXT2FS_ATTR((unused)))
    242 {
    243 	char		*buf;
    244 	ssize_t		actual, size;
    245 	errcode_t	retval;
    246 
    247 	size = fs->blocksize * (fs->group_desc_count + 1);
    248 	buf = malloc(size);
    249 	if (!buf)
    250 		return ENOMEM;
    251 
    252 	/*
    253 	 * Read it all in.
    254 	 */
    255 	actual = read(fd, buf, size);
    256 	if (actual == -1) {
    257 		retval = errno;
    258 		goto errout;
    259 	}
    260 	if (actual != size) {
    261 		retval = EXT2_ET_SHORT_READ;
    262 		goto errout;
    263 	}
    264 
    265 	/*
    266 	 * Now copy in the superblock and group descriptors
    267 	 */
    268 	memcpy(fs->super, buf, SUPERBLOCK_SIZE);
    269 
    270 	memcpy(fs->group_desc, buf + fs->blocksize,
    271 	       fs->blocksize * fs->group_desc_count);
    272 
    273 	retval = 0;
    274 
    275 errout:
    276 	free(buf);
    277 	return retval;
    278 }
    279 
    280 /*
    281  * Write the block/inode bitmaps.
    282  */
    283 errcode_t ext2fs_image_bitmap_write(ext2_filsys fs, int fd, int flags)
    284 {
    285 	ext2fs_generic_bitmap	bmap;
    286 	errcode_t		retval;
    287 	ssize_t			actual;
    288 	__u32			itr, cnt, size;
    289 	int			c, total_size;
    290 	char			buf[1024];
    291 
    292 	if (flags & IMAGER_FLAG_INODEMAP) {
    293 		if (!fs->inode_map) {
    294 			retval = ext2fs_read_inode_bitmap(fs);
    295 			if (retval)
    296 				return retval;
    297 		}
    298 		bmap = fs->inode_map;
    299 		itr = 1;
    300 		cnt = EXT2_INODES_PER_GROUP(fs->super) * fs->group_desc_count;
    301 		size = (EXT2_INODES_PER_GROUP(fs->super) / 8);
    302 	} else {
    303 		if (!fs->block_map) {
    304 			retval = ext2fs_read_block_bitmap(fs);
    305 			if (retval)
    306 				return retval;
    307 		}
    308 		bmap = fs->block_map;
    309 		itr = fs->super->s_first_data_block;
    310 		cnt = EXT2_BLOCKS_PER_GROUP(fs->super) * fs->group_desc_count;
    311 		size = EXT2_BLOCKS_PER_GROUP(fs->super) / 8;
    312 	}
    313 	total_size = size * fs->group_desc_count;
    314 
    315 	while (cnt > 0) {
    316 		size = sizeof(buf);
    317 		if (size > (cnt >> 3))
    318 			size = (cnt >> 3);
    319 
    320 		retval = ext2fs_get_generic_bmap_range(bmap, itr,
    321 						       size << 3, buf);
    322 		if (retval)
    323 			return retval;
    324 
    325 		actual = write(fd, buf, size);
    326 		if (actual == -1)
    327 			return errno;
    328 		if (actual != (int) size)
    329 			return EXT2_ET_SHORT_READ;
    330 
    331 		itr += size << 3;
    332 		cnt -= size << 3;
    333 	}
    334 
    335 	size = total_size % fs->blocksize;
    336 	memset(buf, 0, sizeof(buf));
    337 	if (size) {
    338 		size = fs->blocksize - size;
    339 		while (size) {
    340 			c = size;
    341 			if (c > (int) sizeof(buf))
    342 				c = sizeof(buf);
    343 			actual = write(fd, buf, c);
    344 			if (actual == -1)
    345 				return errno;
    346 			if (actual != c)
    347 				return EXT2_ET_SHORT_WRITE;
    348 			size -= c;
    349 		}
    350 	}
    351 	return 0;
    352 }
    353 
    354 
    355 /*
    356  * Read the block/inode bitmaps.
    357  */
    358 errcode_t ext2fs_image_bitmap_read(ext2_filsys fs, int fd, int flags)
    359 {
    360 	ext2fs_generic_bitmap	bmap;
    361 	errcode_t		retval;
    362 	__u32			itr, cnt;
    363 	char			buf[1024];
    364 	unsigned int		size;
    365 	ssize_t			actual;
    366 
    367 	if (flags & IMAGER_FLAG_INODEMAP) {
    368 		if (!fs->inode_map) {
    369 			retval = ext2fs_read_inode_bitmap(fs);
    370 			if (retval)
    371 				return retval;
    372 		}
    373 		bmap = fs->inode_map;
    374 		itr = 1;
    375 		cnt = EXT2_INODES_PER_GROUP(fs->super) * fs->group_desc_count;
    376 		size = (EXT2_INODES_PER_GROUP(fs->super) / 8);
    377 	} else {
    378 		if (!fs->block_map) {
    379 			retval = ext2fs_read_block_bitmap(fs);
    380 			if (retval)
    381 				return retval;
    382 		}
    383 		bmap = fs->block_map;
    384 		itr = fs->super->s_first_data_block;
    385 		cnt = EXT2_BLOCKS_PER_GROUP(fs->super) * fs->group_desc_count;
    386 		size = EXT2_BLOCKS_PER_GROUP(fs->super) / 8;
    387 	}
    388 
    389 	while (cnt > 0) {
    390 		size = sizeof(buf);
    391 		if (size > (cnt >> 3))
    392 			size = (cnt >> 3);
    393 
    394 		actual = read(fd, buf, size);
    395 		if (actual == -1)
    396 			return errno;
    397 		if (actual != (int) size)
    398 			return EXT2_ET_SHORT_READ;
    399 
    400 		retval = ext2fs_set_generic_bmap_range(bmap, itr,
    401 						       size << 3, buf);
    402 		if (retval)
    403 			return retval;
    404 
    405 		itr += size << 3;
    406 		cnt -= size << 3;
    407 	}
    408 	return 0;
    409 }
    410