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