libsparse: cleanups
Move block loops into sparse.c with iterator helpers in backed_block.c.
Simplify chunk writing by moving skip chunk calls from output_file.c to
sparse.c.
Rename variables to be consistent with new naming.
Remove use of u8, u32, u64.
Change-Id: Ic138ad58bef9f96239266ccee12ee83ea285e7eb
diff --git a/libsparse/output_file.c b/libsparse/output_file.c
index 2c4b557..f911f8c 100644
--- a/libsparse/output_file.c
+++ b/libsparse/output_file.c
@@ -51,36 +51,50 @@
}
#endif
+#define min(a, b) \
+ ({ typeof(a) _a = (a); typeof(b) _b = (b); (_a < _b) ? _a : _b; })
+
#define SPARSE_HEADER_MAJOR_VER 1
#define SPARSE_HEADER_MINOR_VER 0
#define SPARSE_HEADER_LEN (sizeof(sparse_header_t))
#define CHUNK_HEADER_LEN (sizeof(chunk_header_t))
struct output_file_ops {
- int (*seek)(struct output_file *, int64_t);
- int (*write)(struct output_file *, u8 *, int);
+ int (*skip)(struct output_file *, int64_t);
+ int (*write)(struct output_file *, void *, int);
void (*close)(struct output_file *);
};
+struct sparse_file_ops {
+ int (*write_data_chunk)(struct output_file *out, unsigned int len,
+ void *data);
+ int (*write_fill_chunk)(struct output_file *out, unsigned int len,
+ uint32_t fill_val);
+ int (*write_skip_chunk)(struct output_file *out, int64_t len);
+ int (*write_end_chunk)(struct output_file *out);
+};
+
struct output_file {
int fd;
gzFile gz_fd;
bool close_fd;
- int sparse;
int64_t cur_out_ptr;
- u32 chunk_cnt;
- u32 crc32;
+ unsigned int chunk_cnt;
+ uint32_t crc32;
struct output_file_ops *ops;
+ struct sparse_file_ops *sparse_ops;
int use_crc;
unsigned int block_size;
int64_t len;
+ char *zero_buf;
+ uint32_t *fill_buf;
};
-static int file_seek(struct output_file *out, int64_t off)
+static int file_skip(struct output_file *out, int64_t cnt)
{
off64_t ret;
- ret = lseek64(out->fd, off, SEEK_SET);
+ ret = lseek64(out->fd, cnt, SEEK_CUR);
if (ret < 0) {
error_errno("lseek64");
return -1;
@@ -88,7 +102,7 @@
return 0;
}
-static int file_write(struct output_file *out, u8 *data, int len)
+static int file_write(struct output_file *out, void *data, int len)
{
int ret;
ret = write(out->fd, data, len);
@@ -110,18 +124,17 @@
}
}
-
static struct output_file_ops file_ops = {
- .seek = file_seek,
+ .skip = file_skip,
.write = file_write,
.close = file_close,
};
-static int gz_file_seek(struct output_file *out, int64_t off)
+static int gz_file_skip(struct output_file *out, int64_t cnt)
{
off64_t ret;
- ret = gzseek(out->gz_fd, off, SEEK_SET);
+ ret = gzseek(out->gz_fd, cnt, SEEK_CUR);
if (ret < 0) {
error_errno("gzseek");
return -1;
@@ -129,7 +142,7 @@
return 0;
}
-static int gz_file_write(struct output_file *out, u8 *data, int len)
+static int gz_file_write(struct output_file *out, void *data, int len)
{
int ret;
ret = gzwrite(out->gz_fd, data, len);
@@ -150,32 +163,16 @@
}
static struct output_file_ops gz_file_ops = {
- .seek = gz_file_seek,
+ .skip = gz_file_skip,
.write = gz_file_write,
.close = gz_file_close,
};
-static sparse_header_t sparse_header = {
- .magic = SPARSE_HEADER_MAGIC,
- .major_version = SPARSE_HEADER_MAJOR_VER,
- .minor_version = SPARSE_HEADER_MINOR_VER,
- .file_hdr_sz = SPARSE_HEADER_LEN,
- .chunk_hdr_sz = CHUNK_HEADER_LEN,
- .blk_sz = 0,
- .total_blks = 0,
- .total_chunks = 0,
- .image_checksum = 0
-};
-
-static u8 *zero_buf;
-
-static int emit_skip_chunk(struct output_file *out, u64 skip_len)
+static int write_sparse_skip_chunk(struct output_file *out, int64_t skip_len)
{
chunk_header_t chunk_header;
int ret, chunk;
- //DBG printf("skip chunk: 0x%llx bytes\n", skip_len);
-
if (skip_len % out->block_size) {
error("don't care size %llu is not a multiple of the block size %u",
skip_len, out->block_size);
@@ -187,7 +184,7 @@
chunk_header.reserved1 = 0;
chunk_header.chunk_sz = skip_len / out->block_size;
chunk_header.total_sz = CHUNK_HEADER_LEN;
- ret = out->ops->write(out, (u8 *)&chunk_header, sizeof(chunk_header));
+ ret = out->ops->write(out, &chunk_header, sizeof(chunk_header));
if (ret < 0)
return -1;
@@ -197,71 +194,34 @@
return 0;
}
-static int write_chunk_fill(struct output_file *out, int64_t off, u32 fill_val, int len)
+static int write_sparse_fill_chunk(struct output_file *out, unsigned int len,
+ uint32_t fill_val)
{
chunk_header_t chunk_header;
int rnd_up_len, zero_len, count;
int ret;
unsigned int i;
- u32 fill_buf[4096/sizeof(u32)]; /* Maximum size of a block */
- /* We can assume that all the chunks to be written are in
- * ascending order, block-size aligned, and non-overlapping.
- * So, if the offset is less than the current output pointer,
- * throw an error, and if there is a gap, emit a "don't care"
- * chunk. The first write (of the super block) may not be
- * blocksize aligned, so we need to deal with that too.
- */
- //DBG printf("write chunk: offset 0x%llx, length 0x%x bytes\n", off, len);
-
- if (off < out->cur_out_ptr) {
- error("offset %llu is less than the current output offset %llu",
- off, out->cur_out_ptr);
- return -1;
- }
-
- if (off > out->cur_out_ptr) {
- emit_skip_chunk(out, off - out->cur_out_ptr);
- }
-
- if (off % out->block_size) {
- error("write chunk offset %llu is not a multiple of the block size %u",
- off, out->block_size);
- return -1;
- }
-
- if (off != out->cur_out_ptr) {
- error("internal error, offset accounting screwy in write_chunk_raw()");
- return -1;
- }
-
- /* Round up the file length to a multiple of the block size */
- rnd_up_len = (len + (out->block_size - 1)) & (~(out->block_size -1));
+ /* Round up the fill length to a multiple of the block size */
+ rnd_up_len = ALIGN(len, out->block_size);
/* Finally we can safely emit a chunk of data */
chunk_header.chunk_type = CHUNK_TYPE_FILL;
chunk_header.reserved1 = 0;
chunk_header.chunk_sz = rnd_up_len / out->block_size;
chunk_header.total_sz = CHUNK_HEADER_LEN + sizeof(fill_val);
- ret = out->ops->write(out, (u8 *)&chunk_header, sizeof(chunk_header));
+ ret = out->ops->write(out, &chunk_header, sizeof(chunk_header));
if (ret < 0)
return -1;
- ret = out->ops->write(out, (u8 *)&fill_val, sizeof(fill_val));
+ ret = out->ops->write(out, &fill_val, sizeof(fill_val));
if (ret < 0)
return -1;
if (out->use_crc) {
- /* Initialize fill_buf with the fill_val */
- for (i = 0; i < (out->block_size / sizeof(u32)); i++) {
- fill_buf[i] = fill_val;
- }
-
- count = chunk_header.chunk_sz;
- while (count) {
- out->crc32 = sparse_crc32(out->crc32, fill_buf, out->block_size);
- count--;
- }
+ count = out->block_size / sizeof(uint32_t);
+ while (count--)
+ out->crc32 = sparse_crc32(out->crc32, &fill_val, sizeof(uint32_t));
}
out->cur_out_ptr += rnd_up_len;
@@ -270,44 +230,15 @@
return 0;
}
-static int write_chunk_raw(struct output_file *out, int64_t off, u8 *data, int len)
+static int write_sparse_data_chunk(struct output_file *out, unsigned int len,
+ void *data)
{
chunk_header_t chunk_header;
int rnd_up_len, zero_len;
int ret;
- /* We can assume that all the chunks to be written are in
- * ascending order, block-size aligned, and non-overlapping.
- * So, if the offset is less than the current output pointer,
- * throw an error, and if there is a gap, emit a "don't care"
- * chunk. The first write (of the super block) may not be
- * blocksize aligned, so we need to deal with that too.
- */
- //DBG printf("write chunk: offset 0x%llx, length 0x%x bytes\n", off, len);
-
- if (off < out->cur_out_ptr) {
- error("offset %llu is less than the current output offset %llu",
- off, out->cur_out_ptr);
- return -1;
- }
-
- if (off > out->cur_out_ptr) {
- emit_skip_chunk(out, off - out->cur_out_ptr);
- }
-
- if (off % out->block_size) {
- error("write chunk offset %llu is not a multiple of the block size %u",
- off, out->block_size);
- return -1;
- }
-
- if (off != out->cur_out_ptr) {
- error("internal error, offset accounting screwy in write_chunk_raw()");
- return -1;
- }
-
- /* Round up the file length to a multiple of the block size */
- rnd_up_len = (len + (out->block_size - 1)) & (~(out->block_size -1));
+ /* Round up the data length to a multiple of the block size */
+ rnd_up_len = ALIGN(len, out->block_size);
zero_len = rnd_up_len - len;
/* Finally we can safely emit a chunk of data */
@@ -315,7 +246,7 @@
chunk_header.reserved1 = 0;
chunk_header.chunk_sz = rnd_up_len / out->block_size;
chunk_header.total_sz = CHUNK_HEADER_LEN + rnd_up_len;
- ret = out->ops->write(out, (u8 *)&chunk_header, sizeof(chunk_header));
+ ret = out->ops->write(out, &chunk_header, sizeof(chunk_header));
if (ret < 0)
return -1;
@@ -323,7 +254,7 @@
if (ret < 0)
return -1;
if (zero_len) {
- ret = out->ops->write(out, zero_buf, zero_len);
+ ret = out->ops->write(out, out->zero_buf, zero_len);
if (ret < 0)
return -1;
}
@@ -331,7 +262,7 @@
if (out->use_crc) {
out->crc32 = sparse_crc32(out->crc32, data, len);
if (zero_len)
- out->crc32 = sparse_crc32(out->crc32, zero_buf, zero_len);
+ out->crc32 = sparse_crc32(out->crc32, out->zero_buf, zero_len);
}
out->cur_out_ptr += rnd_up_len;
@@ -340,29 +271,115 @@
return 0;
}
+int write_sparse_end_chunk(struct output_file *out)
+{
+ chunk_header_t chunk_header;
+ int ret;
+
+ if (out->use_crc) {
+ chunk_header.chunk_type = CHUNK_TYPE_CRC32;
+ chunk_header.reserved1 = 0;
+ chunk_header.chunk_sz = 0;
+ chunk_header.total_sz = CHUNK_HEADER_LEN + 4;
+
+ ret = out->ops->write(out, &chunk_header, sizeof(chunk_header));
+ if (ret < 0) {
+ return ret;
+ }
+ out->ops->write(out, &out->crc32, 4);
+ if (ret < 0) {
+ return ret;
+ }
+
+ out->chunk_cnt++;
+ }
+
+ return 0;
+}
+
+static struct sparse_file_ops sparse_file_ops = {
+ .write_data_chunk = write_sparse_data_chunk,
+ .write_fill_chunk = write_sparse_fill_chunk,
+ .write_skip_chunk = write_sparse_skip_chunk,
+ .write_end_chunk = write_sparse_end_chunk,
+};
+
+static int write_normal_data_chunk(struct output_file *out, unsigned int len,
+ void *data)
+{
+ int ret;
+ unsigned int rnd_up_len = ALIGN(len, out->block_size);
+
+ ret = out->ops->write(out, data, len);
+ if (ret < 0) {
+ return ret;
+ }
+
+ if (rnd_up_len > len) {
+ ret = out->ops->skip(out, rnd_up_len - len);
+ }
+
+ return ret;
+}
+
+static int write_normal_fill_chunk(struct output_file *out, unsigned int len,
+ uint32_t fill_val)
+{
+ int ret;
+ unsigned int i;
+ unsigned int write_len;
+
+ /* Initialize fill_buf with the fill_val */
+ for (i = 0; i < out->block_size / sizeof(uint32_t); i++) {
+ out->fill_buf[i] = fill_val;
+ }
+
+ while (len) {
+ write_len = min(len, out->block_size);
+ ret = out->ops->write(out, out->fill_buf, write_len);
+ if (ret < 0) {
+ return ret;
+ }
+
+ len -= write_len;
+ }
+
+ return 0;
+}
+
+static int write_normal_skip_chunk(struct output_file *out, int64_t len)
+{
+ int ret;
+
+ return out->ops->skip(out, len);
+}
+
+int write_normal_end_chunk(struct output_file *out)
+{
+ int ret;
+
+ ret = ftruncate64(out->fd, out->len);
+ if (ret < 0) {
+ return -errno;
+ }
+
+ return 0;
+}
+
+static struct sparse_file_ops normal_file_ops = {
+ .write_data_chunk = write_normal_data_chunk,
+ .write_fill_chunk = write_normal_fill_chunk,
+ .write_skip_chunk = write_normal_skip_chunk,
+ .write_end_chunk = write_normal_end_chunk,
+};
+
void close_output_file(struct output_file *out)
{
int ret;
- chunk_header_t chunk_header;
- if (out->sparse) {
- if (out->use_crc) {
- chunk_header.chunk_type = CHUNK_TYPE_CRC32;
- chunk_header.reserved1 = 0;
- chunk_header.chunk_sz = 0;
- chunk_header.total_sz = CHUNK_HEADER_LEN + 4;
-
- out->ops->write(out, (u8 *)&chunk_header, sizeof(chunk_header));
- out->ops->write(out, (u8 *)&out->crc32, 4);
-
- out->chunk_cnt++;
- }
-
- 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->sparse_ops->write_end_chunk(out);
out->ops->close(out);
+ free(out);
}
struct output_file *open_output_fd(int fd, unsigned int block_size, int64_t len,
@@ -374,28 +391,37 @@
error_errno("malloc struct out");
return NULL;
}
- zero_buf = malloc(out->block_size);
- if (!zero_buf) {
+ out->zero_buf = calloc(block_size, 1);
+ if (!out->zero_buf) {
error_errno("malloc zero_buf");
- free(out);
- return NULL;
+ goto err_zero_buf;
}
- memset(zero_buf, '\0', out->block_size);
+
+ out->fill_buf = calloc(block_size, 1);
+ if (!out->fill_buf) {
+ error_errno("malloc fill_buf");
+ goto err_fill_buf;
+ }
if (gz) {
out->ops = &gz_file_ops;
out->gz_fd = gzdopen(fd, "wb9");
if (!out->gz_fd) {
error_errno("gzopen");
- free(out);
- return NULL;
+ goto err_gzopen;
}
} else {
out->fd = fd;
out->ops = &file_ops;
}
+
+ if (sparse) {
+ out->sparse_ops = &sparse_file_ops;
+ } else {
+ out->sparse_ops = &normal_file_ops;
+ }
+
out->close_fd = false;
- out->sparse = sparse;
out->cur_out_ptr = 0ll;
out->chunk_cnt = 0;
@@ -406,19 +432,42 @@
out->len = len;
out->block_size = block_size;
- if (out->sparse) {
- sparse_header.blk_sz = out->block_size,
- sparse_header.total_blks = out->len / out->block_size,
- sparse_header.total_chunks = chunks;
- if (out->use_crc)
- sparse_header.total_chunks++;
+ if (sparse) {
+ sparse_header_t sparse_header = {
+ .magic = SPARSE_HEADER_MAGIC,
+ .major_version = SPARSE_HEADER_MAJOR_VER,
+ .minor_version = SPARSE_HEADER_MINOR_VER,
+ .file_hdr_sz = SPARSE_HEADER_LEN,
+ .chunk_hdr_sz = CHUNK_HEADER_LEN,
+ .blk_sz = out->block_size,
+ .total_blks = out->len / out->block_size,
+ .total_chunks = chunks,
+ .image_checksum = 0
+ };
- ret = out->ops->write(out, (u8 *)&sparse_header, sizeof(sparse_header));
- if (ret < 0)
- return NULL;
+ if (out->use_crc) {
+ sparse_header.total_chunks++;
+ }
+
+ ret = out->ops->write(out, &sparse_header, sizeof(sparse_header));
+ if (ret < 0) {
+ goto err_write;
+ }
}
return out;
+
+err_write:
+ if (gz) {
+ gzclose(out->gz_fd);
+ }
+err_gzopen:
+ free(out->fill_buf);
+err_fill_buf:
+ free(out->zero_buf);
+err_zero_buf:
+ free(out);
+ return NULL;
}
struct output_file *open_output_file(const char *filename,
@@ -449,123 +498,31 @@
return file;
}
-void pad_output_file(struct output_file *out, int64_t len)
-{
- int ret;
-
- if (len > out->len) {
- error("attempted to pad file %llu bytes past end of filesystem",
- len - out->len);
- return;
- }
- if (out->sparse) {
- /* We need to emit a DONT_CARE chunk to pad out the file if the
- * cur_out_ptr is not already at the end of the filesystem.
- */
- if (len < out->cur_out_ptr) {
- error("attempted to pad file %llu bytes less than the current output pointer",
- out->cur_out_ptr - len);
- return;
- }
- if (len > out->cur_out_ptr) {
- emit_skip_chunk(out, len - out->cur_out_ptr);
- }
- } else {
- //KEN TODO: Fixme. If the filesystem image needs no padding,
- // this will overwrite the last byte in the file with 0
- // The answer is to do accounting like the sparse image
- // code does and know if there is already data there.
- ret = out->ops->seek(out, len - 1);
- if (ret < 0)
- return;
-
- ret = out->ops->write(out, (u8*)"", 1);
- if (ret < 0)
- return;
- }
-}
-
/* Write a contiguous region of data blocks from a memory buffer */
-void write_data_block(struct output_file *out, int64_t off, void *data, int len)
+int write_data_chunk(struct output_file *out, unsigned int len, void *data)
{
- int ret;
-
- if (off + len > out->len) {
- error("attempted to write block %llu past end of filesystem",
- off + len - out->len);
- return;
- }
-
- if (out->sparse) {
- write_chunk_raw(out, off, data, len);
- } else {
- ret = out->ops->seek(out, off);
- if (ret < 0)
- return;
-
- ret = out->ops->write(out, data, len);
- if (ret < 0)
- return;
- }
+ return out->sparse_ops->write_data_chunk(out, len, data);
}
/* Write a contiguous region of data blocks with a fill value */
-void write_fill_block(struct output_file *out, int64_t off, unsigned int fill_val, int len)
+int write_fill_chunk(struct output_file *out, unsigned int len,
+ uint32_t fill_val)
{
- int ret;
- unsigned int i;
- int write_len;
- u32 fill_buf[4096/sizeof(u32)]; /* Maximum size of a block */
-
- if (off + len > out->len) {
- error("attempted to write block %llu past end of filesystem",
- off + len - out->len);
- return;
- }
-
- if (out->sparse) {
- write_chunk_fill(out, off, fill_val, len);
- } else {
- /* Initialize fill_buf with the fill_val */
- for (i = 0; i < sizeof(fill_buf)/sizeof(u32); i++) {
- fill_buf[i] = fill_val;
- }
-
- ret = out->ops->seek(out, off);
- if (ret < 0)
- return;
-
- while (len) {
- write_len = (len > (int)sizeof(fill_buf) ? (int)sizeof(fill_buf) : len);
- ret = out->ops->write(out, (u8 *)fill_buf, write_len);
- if (ret < 0) {
- return;
- } else {
- len -= write_len;
- }
- }
- }
+ return out->sparse_ops->write_fill_chunk(out, len, fill_val);
}
/* Write a contiguous region of data blocks from a file */
-void write_data_file(struct output_file *out, int64_t off, const char *file,
- int64_t offset, int len)
+int write_file_chunk(struct output_file *out, unsigned int len,
+ const char *file, int64_t offset)
{
int ret;
int64_t aligned_offset;
int aligned_diff;
int buffer_size;
- if (off + len >= out->len) {
- error("attempted to write block %llu past end of filesystem",
- off + len - out->len);
- return;
- }
-
int file_fd = open(file, O_RDONLY | O_BINARY);
if (file_fd < 0) {
- error_errno("open");
- return;
+ return -errno;
}
aligned_offset = offset & ~(4096 - 1);
@@ -573,41 +530,36 @@
buffer_size = len + aligned_diff;
#ifndef USE_MINGW
- u8 *data = mmap64(NULL, buffer_size, PROT_READ, MAP_SHARED, file_fd,
+ char *data = mmap64(NULL, buffer_size, PROT_READ, MAP_SHARED, file_fd,
aligned_offset);
if (data == MAP_FAILED) {
- error_errno("mmap64");
+ ret = -errno;
close(file_fd);
- return;
+ return ret;
}
#else
- u8 *data = malloc(buffer_size);
+ char *data = malloc(buffer_size);
if (!data) {
- error_errno("malloc");
+ ret = -errno;
close(file_fd);
- return;
+ return ret;
}
memset(data, 0, buffer_size);
#endif
- if (out->sparse) {
- write_chunk_raw(out, off, data + aligned_diff, len);
- } else {
- ret = out->ops->seek(out, off);
- if (ret < 0)
- goto err;
+ ret = out->sparse_ops->write_data_chunk(out, len, data + aligned_diff);
- ret = out->ops->write(out, data + aligned_diff, len);
- if (ret < 0)
- goto err;
- }
-
-err:
#ifndef USE_MINGW
munmap(data, buffer_size);
#else
- write(file_fd, data, buffer_size);
free(data);
#endif
close(file_fd);
+
+ return ret;
+}
+
+int write_skip_chunk(struct output_file *out, int64_t len)
+{
+ return out->sparse_ops->write_skip_chunk(out, len);
}