/*
* 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 = lseek(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;
buf = malloc(fs->blocksize);
if (!buf)
return ENOMEM;
/*
* Write out the superblock
*/
memset(buf, 0, fs->blocksize);
memcpy(buf, fs->super, SUPERBLOCK_SIZE);
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;
actual = write(fd, cp, fs->blocksize * fs->desc_blocks);
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_BLOCKS(fs->super, fs->group_desc_count);
size = EXT2_BLOCKS_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;
}