/* Iterates over the queued data blocks, calling data_func for each contiguous
data block, and file_func for each contiguous file block */
void for_each_data_block(data_block_callback_t data_func,
- data_block_file_callback_t file_func, struct output_file *out)
+ data_block_file_callback_t file_func, void *priv)
{
struct data_block *db;
u32 last_block = 0;
last_block = db->block + DIV_ROUND_UP(db->len, info.block_size) - 1;
if (db->filename)
- file_func(out, (u64)db->block * info.block_size, db->filename, db->offset, db->len);
+ file_func(priv, (u64)db->block * info.block_size, db->filename, db->offset, db->len);
else
- data_func(out, (u64)db->block * info.block_size, db->data, db->len);
+ data_func(priv, (u64)db->block * info.block_size, db->data, db->len);
}
}
return 0;
}
+struct count_chunks {
+ u32 chunks;
+ u64 cur_ptr;
+};
+
+void count_data_block(void *priv, u64 off, u8 *data, int len)
+{
+ struct count_chunks *count_chunks = priv;
+ if (off > count_chunks->cur_ptr)
+ count_chunks->chunks++;
+ count_chunks->cur_ptr = off + ALIGN(len, info.block_size);
+ count_chunks->chunks++;
+}
+
+void count_file_block(void *priv, u64 off, const char *file,
+ off64_t offset, int len)
+{
+ struct count_chunks *count_chunks = priv;
+ if (off > count_chunks->cur_ptr)
+ count_chunks->chunks++;
+ count_chunks->cur_ptr = off + ALIGN(len, info.block_size);
+ count_chunks->chunks++;
+}
+
+int count_sparse_chunks()
+{
+ struct count_chunks count_chunks = {0, 0};
+
+ for_each_data_block(count_data_block, count_file_block, &count_chunks);
+
+ if (count_chunks.cur_ptr != info.len)
+ count_chunks.chunks++;
+
+ return count_chunks.chunks;
+}
+
+static void ext4_write_data_block(void *priv, u64 off, u8 *data, int len)
+{
+ write_data_block(priv, off, data, len);
+}
+static void ext4_write_data_file(void *priv, u64 off, const char *file,
+ off64_t offset, int len)
+{
+ write_data_file(priv, off, file, offset, len);
+}
+
/* Write the filesystem image to a file */
void write_ext4_image(const char *filename, int gz, int sparse)
{
int ret = 0;
- struct output_file *out = open_output_file(filename, gz, sparse);
+ struct output_file *out = open_output_file(filename, gz, sparse,
+ count_sparse_chunks());
if (!out)
return;
- /* The write_data* functions expect only block aligned calls.
- * This is not an issue, except when we write out the super
- * block on a system with a block size > 1K. So, we need to
- * deal with that here.
- */
- if (info.block_size > 1024) {
- u8 buf[4096] = { 0 }; /* The larget supported ext4 block size */
- memcpy(buf + 1024, (u8*)aux_info.sb, 1024);
- write_data_block(out, 0, buf, info.block_size);
-
- } else {
- write_data_block(out, 1024, (u8*)aux_info.sb, 1024);
- }
-
- write_data_block(out, (u64)(aux_info.first_data_block + 1) * info.block_size,
- (u8*)aux_info.bg_desc,
- aux_info.bg_desc_blocks * info.block_size);
-
- for_each_data_block(write_data_block, write_data_file, out);
+ for_each_data_block(ext4_write_data_block, ext4_write_data_file, out);
pad_output_file(out, info.len);
info.blocks_per_group;
u32 header_size = 0;
if (ext4_bg_has_super_block(i)) {
- if (i != 0) {
+ if (i != 0)
queue_data_block((u8 *)sb, info.block_size, group_start_block);
- queue_data_block((u8 *)aux_info.bg_desc,
- aux_info.bg_desc_blocks * info.block_size,
- group_start_block + 1);
- }
+ queue_data_block((u8 *)aux_info.bg_desc,
+ aux_info.bg_desc_blocks * info.block_size,
+ group_start_block + 1);
header_size = 1 + aux_info.bg_desc_blocks + info.bg_desc_reserve_blocks;
}
}
}
+void ext4_queue_sb(void)
+{
+ /* The write_data* functions expect only block aligned calls.
+ * This is not an issue, except when we write out the super
+ * block on a system with a block size > 1K. So, we need to
+ * deal with that here.
+ */
+ if (info.block_size > 1024) {
+ u8 *buf = calloc(info.block_size, 1);
+ memcpy(buf + 1024, (u8*)aux_info.sb, 1024);
+ queue_data_block(buf, info.block_size, 0);
+ } else {
+ queue_data_block((u8*)aux_info.sb, 1024, 1);
+ }
+}
+
void ext4_create_resize_inode()
{
struct block_allocation *reserve_inode_alloc = create_allocation();
gzFile gz_fd;
int sparse;
u64 cur_out_ptr;
- int chunk_cnt;
+ u32 chunk_cnt;
u32 crc32;
struct output_file_ops *ops;
};
void close_output_file(struct output_file *out)
{
int ret;
+ chunk_header_t chunk_header;
if (out->sparse) {
- /* we need to seek back to the beginning and update the file header */
- sparse_header.total_chunks = out->chunk_cnt;
- sparse_header.image_checksum = out->crc32;
-
- ret = out->ops->seek(out, 0);
- if (ret < 0)
- error("failure seeking to start of sparse file");
-
- ret = out->ops->write(out, (u8 *)&sparse_header, sizeof(sparse_header));
- if (ret < 0)
- error("failure updating sparse file header");
+ if (out->chunk_cnt != sparse_header.total_chunks)
+ error("sparse chunk count did not match: %d %d", out->chunk_cnt,
+ sparse_header.total_chunks);
}
out->ops->close(out);
}
-struct output_file *open_output_file(const char *filename, int gz, int sparse)
+struct output_file *open_output_file(const char *filename, int gz, int sparse,
+ int chunks)
{
int ret;
struct output_file *out = malloc(sizeof(struct output_file));
return NULL;
}
} else {
- out->ops = &file_ops;
- out->fd = open(filename, O_WRONLY | O_CREAT | O_TRUNC, 0644);
- if (out->fd < 0) {
- error_errno("open");
- free(out);
- return NULL;
+ if (strcmp(filename, "-")) {
+ out->fd = open(filename, O_WRONLY | O_CREAT | O_TRUNC, 0644);
+ if (out->fd < 0) {
+ error_errno("open");
+ free(out);
+ return NULL;
+ }
+ } else {
+ out->fd = STDOUT_FILENO;
}
+ out->ops = &file_ops;
}
out->sparse = sparse;
out->cur_out_ptr = 0ll;
*/
sparse_header.blk_sz = info.block_size,
sparse_header.total_blks = info.len / info.block_size,
+ sparse_header.total_chunks = chunks;
ret = out->ops->write(out, (u8 *)&sparse_header, sizeof(sparse_header));
if (ret < 0)
return NULL;