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