/* * image.c --- writes out the critical parts of the filesystem as a * flat file. * * Copyright (C) 2000 Theodore Ts'o. * * Note: this uses the POSIX IO interfaces, unlike most of the other * functions in this library. So sue me. * * %Begin-Header% * This file may be redistributed under the terms of the GNU Library * General Public License, version 2. * %End-Header% */ #include "config.h" #include <stdio.h> #include <string.h> #if HAVE_UNISTD_H #include <unistd.h> #endif #if HAVE_ERRNO_H #include <errno.h> #endif #include <fcntl.h> #include <time.h> #if HAVE_SYS_STAT_H #include <sys/stat.h> #endif #if HAVE_SYS_TYPES_H #include <sys/types.h> #endif #include "ext2_fs.h" #include "ext2fs.h" #ifndef HAVE_TYPE_SSIZE_T typedef int ssize_t; #endif /* * This function returns 1 if the specified block is all zeros */ static int check_zero_block(char *buf, int blocksize) { char *cp = buf; int left = blocksize; while (left > 0) { if (*cp++) return 0; left--; } return 1; } /* * Write the inode table out as a single block. */ #define BUF_BLOCKS 32 errcode_t ext2fs_image_inode_write(ext2_filsys fs, int fd, int flags) { unsigned int group, left, c, d; char *buf, *cp; blk64_t blk; ssize_t actual; errcode_t retval; off_t r; buf = malloc(fs->blocksize * BUF_BLOCKS); if (!buf) return ENOMEM; for (group = 0; group < fs->group_desc_count; group++) { blk = ext2fs_inode_table_loc(fs, (unsigned)group); if (!blk) { retval = EXT2_ET_MISSING_INODE_TABLE; goto errout; } left = fs->inode_blocks_per_group; while (left) { c = BUF_BLOCKS; if (c > left) c = left; retval = io_channel_read_blk64(fs->io, blk, c, buf); if (retval) goto errout; cp = buf; while (c) { if (!(flags & IMAGER_FLAG_SPARSEWRITE)) { d = c; goto skip_sparse; } /* Skip zero blocks */ if (check_zero_block(cp, fs->blocksize)) { c--; blk++; left--; cp += fs->blocksize; r = ext2fs_llseek(fd, fs->blocksize, SEEK_CUR); if (r < 0) { retval = errno; goto errout; } continue; } /* Find non-zero blocks */ for (d=1; d < c; d++) { if (check_zero_block(cp + d*fs->blocksize, fs->blocksize)) break; } skip_sparse: actual = write(fd, cp, fs->blocksize * d); if (actual == -1) { retval = errno; goto errout; } if (actual != (ssize_t) (fs->blocksize * d)) { retval = EXT2_ET_SHORT_WRITE; goto errout; } blk += d; left -= d; cp += fs->blocksize * d; c -= d; } } } retval = 0; errout: free(buf); return retval; } /* * Read in the inode table and stuff it into place */ errcode_t ext2fs_image_inode_read(ext2_filsys fs, int fd, int flags EXT2FS_ATTR((unused))) { unsigned int group, c, left; char *buf; blk64_t blk; ssize_t actual; errcode_t retval; buf = malloc(fs->blocksize * BUF_BLOCKS); if (!buf) return ENOMEM; for (group = 0; group < fs->group_desc_count; group++) { blk = ext2fs_inode_table_loc(fs, (unsigned)group); if (!blk) { retval = EXT2_ET_MISSING_INODE_TABLE; goto errout; } left = fs->inode_blocks_per_group; while (left) { c = BUF_BLOCKS; if (c > left) c = left; actual = read(fd, buf, fs->blocksize * c); if (actual == -1) { retval = errno; goto errout; } if (actual != (ssize_t) (fs->blocksize * c)) { retval = EXT2_ET_SHORT_READ; goto errout; } retval = io_channel_write_blk64(fs->io, blk, c, buf); if (retval) goto errout; blk += c; left -= c; } } retval = ext2fs_flush_icache(fs); errout: free(buf); return retval; } /* * Write out superblock and group descriptors */ errcode_t ext2fs_image_super_write(ext2_filsys fs, int fd, int flags EXT2FS_ATTR((unused))) { char *buf, *cp; ssize_t actual; errcode_t retval; #ifdef WORDS_BIGENDIAN unsigned int groups_per_block; struct ext2_group_desc *gdp; int j; #endif buf = malloc(fs->blocksize); if (!buf) return ENOMEM; /* * Write out the superblock */ memset(buf, 0, fs->blocksize); #ifdef WORDS_BIGENDIAN /* * We're writing out superblock so let's convert * it to little endian and then back if needed */ ext2fs_swap_super(fs->super); memcpy(buf, fs->super, SUPERBLOCK_SIZE); ext2fs_swap_super(fs->super); #else memcpy(buf, fs->super, SUPERBLOCK_SIZE); #endif actual = write(fd, buf, fs->blocksize); if (actual == -1) { retval = errno; goto errout; } if (actual != (ssize_t) fs->blocksize) { retval = EXT2_ET_SHORT_WRITE; goto errout; } /* * Now write out the block group descriptors */ cp = (char *) fs->group_desc; #ifdef WORDS_BIGENDIAN /* * Convert group descriptors to little endian and back * if needed */ groups_per_block = EXT2_DESC_PER_BLOCK(fs->super); gdp = (struct ext2_group_desc *) cp; for (j=0; j < groups_per_block*fs->desc_blocks; j++) { gdp = ext2fs_group_desc(fs, fs->group_desc, j); ext2fs_swap_group_desc2(fs, gdp); } #endif actual = write(fd, cp, fs->blocksize * fs->desc_blocks); #ifdef WORDS_BIGENDIAN groups_per_block = EXT2_DESC_PER_BLOCK(fs->super); gdp = (struct ext2_group_desc *) cp; for (j=0; j < groups_per_block*fs->desc_blocks; j++) { gdp = ext2fs_group_desc(fs, fs->group_desc, j); ext2fs_swap_group_desc2(fs, gdp); } #endif if (actual == -1) { retval = errno; goto errout; } if (actual != (ssize_t) (fs->blocksize * fs->desc_blocks)) { retval = EXT2_ET_SHORT_WRITE; goto errout; } retval = 0; errout: free(buf); return retval; } /* * Read the superblock and group descriptors and overwrite them. */ errcode_t ext2fs_image_super_read(ext2_filsys fs, int fd, int flags EXT2FS_ATTR((unused))) { char *buf; ssize_t actual, size; errcode_t retval; size = fs->blocksize * (fs->group_desc_count + 1); buf = malloc(size); if (!buf) return ENOMEM; /* * Read it all in. */ actual = read(fd, buf, size); if (actual == -1) { retval = errno; goto errout; } if (actual != size) { retval = EXT2_ET_SHORT_READ; goto errout; } /* * Now copy in the superblock and group descriptors */ memcpy(fs->super, buf, SUPERBLOCK_SIZE); memcpy(fs->group_desc, buf + fs->blocksize, fs->blocksize * fs->group_desc_count); retval = 0; errout: free(buf); return retval; } /* * Write the block/inode bitmaps. */ errcode_t ext2fs_image_bitmap_write(ext2_filsys fs, int fd, int flags) { ext2fs_generic_bitmap bmap; errcode_t retval; ssize_t actual; size_t c; __u64 itr, cnt, size, total_size; char buf[1024]; if (flags & IMAGER_FLAG_INODEMAP) { if (!fs->inode_map) { retval = ext2fs_read_inode_bitmap(fs); if (retval) return retval; } bmap = fs->inode_map; itr = 1; cnt = EXT2_INODES_PER_GROUP(fs->super) * fs->group_desc_count; size = (EXT2_INODES_PER_GROUP(fs->super) / 8); } else { if (!fs->block_map) { retval = ext2fs_read_block_bitmap(fs); if (retval) return retval; } bmap = fs->block_map; itr = fs->super->s_first_data_block; cnt = EXT2_GROUPS_TO_CLUSTERS(fs->super, fs->group_desc_count); size = EXT2_CLUSTERS_PER_GROUP(fs->super) / 8; } total_size = size * fs->group_desc_count; while (cnt > 0) { size = sizeof(buf); if (size > (cnt >> 3)) size = (cnt >> 3); retval = ext2fs_get_generic_bmap_range(bmap, itr, size << 3, buf); if (retval) return retval; actual = write(fd, buf, size); if (actual == -1) return errno; if (actual != (int) size) return EXT2_ET_SHORT_READ; itr += size << 3; cnt -= size << 3; } size = total_size % fs->blocksize; memset(buf, 0, sizeof(buf)); if (size) { size = fs->blocksize - size; while (size) { c = size; if (c > (int) sizeof(buf)) c = sizeof(buf); actual = write(fd, buf, c); if (actual < 0) return errno; if ((size_t) actual != c) return EXT2_ET_SHORT_WRITE; size -= c; } } return 0; } /* * Read the block/inode bitmaps. */ errcode_t ext2fs_image_bitmap_read(ext2_filsys fs, int fd, int flags) { ext2fs_generic_bitmap bmap; errcode_t retval; __u64 itr, cnt; char buf[1024]; unsigned int size; ssize_t actual; if (flags & IMAGER_FLAG_INODEMAP) { if (!fs->inode_map) { retval = ext2fs_read_inode_bitmap(fs); if (retval) return retval; } bmap = fs->inode_map; itr = 1; cnt = EXT2_INODES_PER_GROUP(fs->super) * fs->group_desc_count; size = (EXT2_INODES_PER_GROUP(fs->super) / 8); } else { if (!fs->block_map) { retval = ext2fs_read_block_bitmap(fs); if (retval) return retval; } bmap = fs->block_map; itr = fs->super->s_first_data_block; cnt = EXT2_GROUPS_TO_BLOCKS(fs->super, fs->group_desc_count); size = EXT2_BLOCKS_PER_GROUP(fs->super) / 8; } while (cnt > 0) { size = sizeof(buf); if (size > (cnt >> 3)) size = (cnt >> 3); actual = read(fd, buf, size); if (actual == -1) return errno; if (actual != (int) size) return EXT2_ET_SHORT_READ; retval = ext2fs_set_generic_bmap_range(bmap, itr, size << 3, buf); if (retval) return retval; itr += size << 3; cnt -= size << 3; } return 0; }