summaryrefslogtreecommitdiff
path: root/libsparse
diff options
context:
space:
mode:
authorTreehugger Robot <treehugger-gerrit@google.com>2022-01-28 05:28:04 +0000
committerGerrit Code Review <noreply-gerritcodereview@google.com>2022-01-28 05:28:04 +0000
commitf401dcdf5a2f350fa46dd29a1389c98a4ab84482 (patch)
tree36db5fb5b0fd029ff0c3d169cbaa04f7d75da80f /libsparse
parentacd4121dbcdd9edfe7336d22d90f5393dca1ea6e (diff)
parentf96466b05543b984ef7315d830bab4a409228d35 (diff)
downloadcore-f401dcdf5a2f350fa46dd29a1389c98a4ab84482.tar.gz
Merge changes from topic "sparse-file-read-enum"
* changes: libsparse: Add "hole" mode to sparse_file_read libsparse: Split off most of sparse_file_read_normal into a helper function
Diffstat (limited to 'libsparse')
-rw-r--r--libsparse/img2simg.cpp2
-rw-r--r--libsparse/include/sparse/sparse.h33
-rw-r--r--libsparse/sparse_read.cpp96
3 files changed, 110 insertions, 21 deletions
diff --git a/libsparse/img2simg.cpp b/libsparse/img2simg.cpp
index 4c2c6ca6f..3e24cc014 100644
--- a/libsparse/img2simg.cpp
+++ b/libsparse/img2simg.cpp
@@ -93,7 +93,7 @@ int main(int argc, char* argv[]) {
}
sparse_file_verbose(s);
- ret = sparse_file_read(s, in, false, false);
+ ret = sparse_file_read(s, in, SPARSE_READ_MODE_NORMAL, false);
if (ret) {
fprintf(stderr, "Failed to read file\n");
exit(-1);
diff --git a/libsparse/include/sparse/sparse.h b/libsparse/include/sparse/sparse.h
index 9f912697a..7c52c3f90 100644
--- a/libsparse/include/sparse/sparse.h
+++ b/libsparse/include/sparse/sparse.h
@@ -225,23 +225,42 @@ int sparse_file_foreach_chunk(struct sparse_file *s, bool sparse, bool crc,
int (*write)(void *priv, const void *data, size_t len, unsigned int block,
unsigned int nr_blocks),
void *priv);
+
+/**
+ * enum sparse_read_mode - The method to use when reading in files
+ * @SPARSE_READ_MODE_NORMAL: The input is a regular file. Constant chunks of
+ * data (including holes) will be be converted to
+ * fill chunks.
+ * @SPARSE_READ_MODE_SPARSE: The input is an Android sparse file.
+ * @SPARSE_READ_MODE_HOLE: The input is a regular file. Holes will be converted
+ * to "don't care" chunks. Other constant chunks will
+ * be converted to fill chunks.
+ */
+enum sparse_read_mode {
+ SPARSE_READ_MODE_NORMAL = false,
+ SPARSE_READ_MODE_SPARSE = true,
+ SPARSE_READ_MODE_HOLE,
+};
+
/**
* sparse_file_read - read a file into a sparse file cookie
*
* @s - sparse file cookie
* @fd - file descriptor to read from
- * @sparse - read a file in the Android sparse file format
+ * @mode - mode to use when reading the input file
* @crc - verify the crc of a file in the Android sparse file format
*
- * Reads a file into a sparse file cookie. If sparse is true, the file is
- * assumed to be in the Android sparse file format. If sparse is false, the
- * file will be sparsed by looking for block aligned chunks of all zeros or
- * another 32 bit value. If crc is true, the crc of the sparse file will be
- * verified.
+ * Reads a file into a sparse file cookie. If @mode is
+ * %SPARSE_READ_MODE_SPARSE, the file is assumed to be in the Android sparse
+ * file format. If @mode is %SPARSE_READ_MODE_NORMAL, the file will be sparsed
+ * by looking for block aligned chunks of all zeros or another 32 bit value. If
+ * @mode is %SPARSE_READ_MODE_HOLE, the file will be sparsed like
+ * %SPARSE_READ_MODE_NORMAL, but holes in the file will be converted to "don't
+ * care" chunks. If crc is true, the crc of the sparse file will be verified.
*
* Returns 0 on success, negative errno on error.
*/
-int sparse_file_read(struct sparse_file *s, int fd, bool sparse, bool crc);
+int sparse_file_read(struct sparse_file *s, int fd, enum sparse_read_mode mode, bool crc);
/**
* sparse_file_import - import an existing sparse file
diff --git a/libsparse/sparse_read.cpp b/libsparse/sparse_read.cpp
index 0f39172e2..028b6bed1 100644
--- a/libsparse/sparse_read.cpp
+++ b/libsparse/sparse_read.cpp
@@ -457,12 +457,10 @@ static int sparse_file_read_sparse(struct sparse_file* s, SparseFileSource* sour
return 0;
}
-static int sparse_file_read_normal(struct sparse_file* s, int fd) {
+static int do_sparse_file_read_normal(struct sparse_file* s, int fd, uint32_t* buf, int64_t offset,
+ int64_t remain) {
int ret;
- uint32_t* buf = (uint32_t*)malloc(s->block_size);
- unsigned int block = 0;
- int64_t remain = s->len;
- int64_t offset = 0;
+ unsigned int block = offset / s->block_size;
unsigned int to_read;
unsigned int i;
bool sparse_block;
@@ -476,7 +474,6 @@ static int sparse_file_read_normal(struct sparse_file* s, int fd) {
ret = read_all(fd, buf, to_read);
if (ret < 0) {
error("failed to read sparse file");
- free(buf);
return ret;
}
@@ -504,20 +501,93 @@ static int sparse_file_read_normal(struct sparse_file* s, int fd) {
block++;
}
+ return 0;
+}
+
+static int sparse_file_read_normal(struct sparse_file* s, int fd) {
+ int ret;
+ uint32_t* buf = (uint32_t*)malloc(s->block_size);
+
+ if (!buf)
+ return -ENOMEM;
+
+ ret = do_sparse_file_read_normal(s, fd, buf, 0, s->len);
+ free(buf);
+ return ret;
+}
+
+#ifdef __linux__
+static int sparse_file_read_hole(struct sparse_file* s, int fd) {
+ int ret;
+ uint32_t* buf = (uint32_t*)malloc(s->block_size);
+ int64_t end = 0;
+ int64_t start = 0;
+
+ if (!buf) {
+ return -ENOMEM;
+ }
+
+ do {
+ start = lseek(fd, end, SEEK_DATA);
+ if (start < 0) {
+ if (errno == ENXIO)
+ /* The rest of the file is a hole */
+ break;
+
+ error("could not seek to data");
+ free(buf);
+ return -errno;
+ } else if (start > s->len) {
+ break;
+ }
+
+ end = lseek(fd, start, SEEK_HOLE);
+ if (end < 0) {
+ error("could not seek to end");
+ free(buf);
+ return -errno;
+ }
+ end = std::min(end, s->len);
+
+ start = ALIGN_DOWN(start, s->block_size);
+ end = ALIGN(end, s->block_size);
+ if (lseek(fd, start, SEEK_SET) < 0) {
+ free(buf);
+ return -errno;
+ }
+
+ ret = do_sparse_file_read_normal(s, fd, buf, start, end - start);
+ if (ret) {
+ free(buf);
+ return ret;
+ }
+ } while (end < s->len);
+
free(buf);
return 0;
}
+#else
+static int sparse_file_read_hole(struct sparse_file* s __unused, int fd __unused) {
+ return -ENOTSUP;
+}
+#endif
-int sparse_file_read(struct sparse_file* s, int fd, bool sparse, bool crc) {
- if (crc && !sparse) {
+int sparse_file_read(struct sparse_file* s, int fd, enum sparse_read_mode mode, bool crc) {
+ if (crc && mode != SPARSE_READ_MODE_SPARSE) {
return -EINVAL;
}
- if (sparse) {
- SparseFileFdSource source(fd);
- return sparse_file_read_sparse(s, &source, crc);
- } else {
- return sparse_file_read_normal(s, fd);
+ switch (mode) {
+ case SPARSE_READ_MODE_SPARSE: {
+ SparseFileFdSource source(fd);
+ return sparse_file_read_sparse(s, &source, crc);
+ }
+ case SPARSE_READ_MODE_NORMAL:
+ return sparse_file_read_normal(s, fd);
+ case SPARSE_READ_MODE_HOLE:
+ return sparse_file_read_hole(s, fd);
+ default:
+ return -EINVAL;
}
}