Merge "Fix `adb logcat` return code."
diff --git a/CleanSpec.mk b/CleanSpec.mk
index e6f8716..0e43dae 100644
--- a/CleanSpec.mk
+++ b/CleanSpec.mk
@@ -77,3 +77,4 @@
$(call add-clean-step, rm -rf $(PRODUCT_OUT)/system/etc/ld.config.txt)
$(call add-clean-step, rm -rf $(PRODUCT_OUT)/system/etc/llndk.libraries.txt)
$(call add-clean-step, rm -rf $(PRODUCT_OUT)/system/etc/vndksp.libraries.txt)
+$(call add-clean-step, rm -rf $(PRODUCT_OUT)/recovery/root/)
diff --git a/adb/Android.bp b/adb/Android.bp
index 553473f..53bf7e3 100644
--- a/adb/Android.bp
+++ b/adb/Android.bp
@@ -302,8 +302,6 @@
name: "adbd",
defaults: ["adb_defaults"],
- // adbd must be static, as it is copied into the recovery image.
- static_executable: true,
recovery_available: true,
srcs: [
@@ -344,7 +342,6 @@
"libselinux",
"libsquashfs_utils",
"libqemu_pipe",
- "libdebuggerd_handler",
"libbase",
"libcutils",
@@ -371,6 +368,7 @@
"liblog",
"libusb",
"libmdnssd",
+ "libselinux",
],
test_suites: ["device-tests"],
}
diff --git a/adb/daemon/main.cpp b/adb/daemon/main.cpp
index 232d9c5..c02cafa 100644
--- a/adb/daemon/main.cpp
+++ b/adb/daemon/main.cpp
@@ -38,7 +38,6 @@
#include <scoped_minijail.h>
#include <private/android_filesystem_config.h>
-#include "debuggerd/handler.h"
#include "selinux/android.h"
#include "adb.h"
@@ -274,7 +273,6 @@
close_stdin();
- debuggerd_init(nullptr);
adb_trace_init(argv);
D("Handling main()");
diff --git a/adb/daemon/shell_service.cpp b/adb/daemon/shell_service.cpp
index 401c99c..56d647a 100644
--- a/adb/daemon/shell_service.cpp
+++ b/adb/daemon/shell_service.cpp
@@ -98,6 +98,7 @@
#include <android-base/properties.h>
#include <android-base/stringprintf.h>
#include <private/android_logger.h>
+#include <selinux/android.h>
#include "adb.h"
#include "adb_io.h"
@@ -343,6 +344,24 @@
adb_write(oom_score_adj_fd, oom_score_adj_value, strlen(oom_score_adj_value)));
}
+#ifdef __ANDROID_RECOVERY__
+ // Special routine for recovery. Switch to shell domain when adbd is
+ // is running with dropped privileged (i.e. not running as root) and
+ // is built for the recovery mode. This is required because recovery
+ // rootfs is not labeled and everything is labeled just as rootfs.
+ char* con = nullptr;
+ if (getcon(&con) == 0) {
+ if (!strcmp(con, "u:r:adbd:s0")) {
+ if (selinux_android_setcon("u:r:shell:s0") < 0) {
+ LOG(FATAL) << "Could not set SELinux context for subprocess";
+ }
+ }
+ freecon(con);
+ } else {
+ LOG(FATAL) << "Failed to get SELinux context";
+ }
+#endif
+
if (command_.empty()) {
// Spawn a login shell if we don't have a command.
execle(_PATH_BSHELL, "-" _PATH_BSHELL, nullptr, cenv.data());
diff --git a/debuggerd/Android.bp b/debuggerd/Android.bp
index 0b13662..1b20157 100644
--- a/debuggerd/Android.bp
+++ b/debuggerd/Android.bp
@@ -47,6 +47,7 @@
cc_library_static {
name: "libtombstoned_client_static",
defaults: ["debuggerd_defaults"],
+ recovery_available: true,
srcs: [
"tombstoned/tombstoned_client.cpp",
"util.cpp",
@@ -90,7 +91,6 @@
cc_library_static {
name: "libdebuggerd_handler",
defaults: ["debuggerd_defaults"],
- recovery_available: true,
srcs: ["handler/debuggerd_fallback_nop.cpp"],
whole_static_libs: [
@@ -104,6 +104,7 @@
cc_library_static {
name: "libdebuggerd_handler_fallback",
defaults: ["debuggerd_defaults"],
+ recovery_available: true,
srcs: [
"handler/debuggerd_fallback.cpp",
],
@@ -120,6 +121,12 @@
"liblzma",
"libcutils",
],
+ target: {
+ recovery: {
+ cflags: ["-DNO_LIBDEXFILE_SUPPORT"],
+ exclude_static_libs: ["libdexfile"],
+ },
+ },
export_include_dirs: ["include"],
}
diff --git a/fs_mgr/Android.bp b/fs_mgr/Android.bp
index bc3b04b..923442f 100644
--- a/fs_mgr/Android.bp
+++ b/fs_mgr/Android.bp
@@ -56,12 +56,15 @@
"libselinux",
"libavb",
"libfstab",
+ "libdm",
],
export_static_lib_headers: [
"libfstab",
+ "libdm",
],
whole_static_libs: [
"liblogwrap",
+ "libdm",
"libfstab",
],
cppflags: [
diff --git a/fs_mgr/libdm/Android.bp b/fs_mgr/libdm/Android.bp
new file mode 100644
index 0000000..672d401
--- /dev/null
+++ b/fs_mgr/libdm/Android.bp
@@ -0,0 +1,37 @@
+//
+// Copyright (C) 2018 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+
+cc_library_static {
+ name: "libdm",
+ recovery_available: true,
+
+ export_include_dirs: ["include"],
+ cflags: [
+ // TODO(b/110035986): Allows us to create a skeleton of required classes
+ "-Wno-unused-private-field",
+ ],
+
+ srcs: [
+ "dm_table.cpp",
+ "dm_target.cpp",
+ "dm.cpp"
+ ],
+
+ header_libs: [
+ "libbase_headers",
+ "liblog_headers",
+ ],
+}
diff --git a/fs_mgr/libdm/dm.cpp b/fs_mgr/libdm/dm.cpp
new file mode 100644
index 0000000..57c1270
--- /dev/null
+++ b/fs_mgr/libdm/dm.cpp
@@ -0,0 +1,270 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <errno.h>
+#include <fcntl.h>
+#include <linux/dm-ioctl.h>
+#include <stdint.h>
+#include <sys/ioctl.h>
+#include <sys/types.h>
+#include <unistd.h>
+
+#include <memory>
+#include <string>
+#include <vector>
+
+#include <android-base/logging.h>
+#include <android-base/macros.h>
+#include <android-base/unique_fd.h>
+
+#include "dm.h"
+
+namespace android {
+namespace dm {
+
+DeviceMapper& DeviceMapper::Instance() {
+ static DeviceMapper instance;
+ return instance;
+}
+// Creates a new device mapper device
+bool DeviceMapper::CreateDevice(const std::string& name) {
+ if (name.empty()) {
+ LOG(ERROR) << "Unnamed device mapper device creation is not supported";
+ return false;
+ }
+
+ if (name.size() >= DM_NAME_LEN) {
+ LOG(ERROR) << "[" << name << "] is too long to be device mapper name";
+ return false;
+ }
+
+ std::unique_ptr<struct dm_ioctl, decltype(&free)> io(
+ static_cast<struct dm_ioctl*>(malloc(sizeof(struct dm_ioctl))), free);
+ if (io == nullptr) {
+ LOG(ERROR) << "Failed to allocate dm_ioctl";
+ return false;
+ }
+ InitIo(io.get(), name);
+
+ if (ioctl(fd_, DM_DEV_CREATE, io.get())) {
+ PLOG(ERROR) << "DM_DEV_CREATE failed to create [" << name << "]";
+ return false;
+ }
+
+ // Check to make sure the newly created device doesn't already have targets
+ // added or opened by someone
+ CHECK(io->target_count == 0) << "Unexpected targets for newly created [" << name << "] device";
+ CHECK(io->open_count == 0) << "Unexpected opens for newly created [" << name << "] device";
+
+ // Creates a new device mapper device with the name passed in
+ return true;
+}
+
+bool DeviceMapper::DeleteDevice(const std::string& name) {
+ if (name.empty()) {
+ LOG(ERROR) << "Unnamed device mapper device creation is not supported";
+ return false;
+ }
+
+ if (name.size() >= DM_NAME_LEN) {
+ LOG(ERROR) << "[" << name << "] is too long to be device mapper name";
+ return false;
+ }
+
+ std::unique_ptr<struct dm_ioctl, decltype(&free)> io(
+ static_cast<struct dm_ioctl*>(malloc(sizeof(struct dm_ioctl))), free);
+ if (io == nullptr) {
+ LOG(ERROR) << "Failed to allocate dm_ioctl";
+ return false;
+ }
+ InitIo(io.get(), name);
+
+ if (ioctl(fd_, DM_DEV_REMOVE, io.get())) {
+ PLOG(ERROR) << "DM_DEV_REMOVE failed to create [" << name << "]";
+ return false;
+ }
+
+ // Check to make sure appropriate uevent is generated so ueventd will
+ // do the right thing and remove the corresponding device node and symlinks.
+ CHECK(io->flags & DM_UEVENT_GENERATED_FLAG)
+ << "Didn't generate uevent for [" << name << "] removal";
+
+ return true;
+}
+
+const std::unique_ptr<DmTable> DeviceMapper::table(const std::string& /* name */) const {
+ // TODO(b/110035986): Return the table, as read from the kernel instead
+ return nullptr;
+}
+
+DmDeviceState DeviceMapper::state(const std::string& /* name */) const {
+ // TODO(b/110035986): Return the state, as read from the kernel instead
+ return DmDeviceState::INVALID;
+}
+
+bool DeviceMapper::LoadTableAndActivate(const std::string& /* name */, const DmTable& /* table */) {
+ return false;
+}
+
+// Reads all the available device mapper targets and their corresponding
+// versions from the kernel and returns in a vector
+bool DeviceMapper::GetAvailableTargets(std::vector<DmTarget>* targets) {
+ targets->clear();
+
+ // calculate the space needed to read a maximum of kMaxPossibleDmTargets
+ uint32_t payload_size = sizeof(struct dm_target_versions);
+ payload_size += DM_MAX_TYPE_NAME;
+ // device mapper wants every target spec to be aligned at 8-byte boundary
+ payload_size = DM_ALIGN(payload_size);
+ payload_size *= kMaxPossibleDmTargets;
+
+ uint32_t data_size = sizeof(struct dm_ioctl) + payload_size;
+ auto buffer = std::unique_ptr<void, void (*)(void*)>(calloc(1, data_size), free);
+ if (buffer == nullptr) {
+ LOG(ERROR) << "failed to allocate memory";
+ return false;
+ }
+
+ // Sets appropriate data size and data_start to make sure we tell kernel
+ // about the total size of the buffer we are passing and where to start
+ // writing the list of targets.
+ struct dm_ioctl* io = reinterpret_cast<struct dm_ioctl*>(buffer.get());
+ InitIo(io);
+ io->data_size = data_size;
+ io->data_start = sizeof(*io);
+
+ if (ioctl(fd_, DM_LIST_VERSIONS, io)) {
+ PLOG(ERROR) << "Failed to get DM_LIST_VERSIONS from kernel";
+ return false;
+ }
+
+ // If the provided buffer wasn't enough to list all targets, note that
+ // any data beyond sizeof(*io) must not be read in this case
+ if (io->flags & DM_BUFFER_FULL_FLAG) {
+ LOG(INFO) << data_size << " is not enough memory to list all dm targets";
+ return false;
+ }
+
+ // if there are no targets registered, return success with empty vector
+ if (io->data_size == sizeof(*io)) {
+ return true;
+ }
+
+ // Parse each target and list the name and version
+ // TODO(b/110035986): Templatize this
+ uint32_t next = sizeof(*io);
+ data_size = io->data_size - next;
+ struct dm_target_versions* vers =
+ reinterpret_cast<struct dm_target_versions*>(static_cast<char*>(buffer.get()) + next);
+ while (next && data_size) {
+ targets->emplace_back((vers));
+ if (vers->next == 0) {
+ break;
+ }
+ next += vers->next;
+ data_size -= vers->next;
+ vers = reinterpret_cast<struct dm_target_versions*>(static_cast<char*>(buffer.get()) + next);
+ }
+
+ return true;
+}
+
+bool DeviceMapper::GetAvailableDevices(std::vector<DmBlockDevice>* devices) {
+ devices->clear();
+
+ // calculate the space needed to read a maximum of 256 targets, each with
+ // name with maximum length of 16 bytes
+ uint32_t payload_size = sizeof(struct dm_name_list);
+ // 128-bytes for the name
+ payload_size += DM_NAME_LEN;
+ // dm wants every device spec to be aligned at 8-byte boundary
+ payload_size = DM_ALIGN(payload_size);
+ payload_size *= kMaxPossibleDmDevices;
+ uint32_t data_size = sizeof(struct dm_ioctl) + payload_size;
+ auto buffer = std::unique_ptr<void, void (*)(void*)>(calloc(1, data_size), free);
+ if (buffer == nullptr) {
+ LOG(ERROR) << "failed to allocate memory";
+ return false;
+ }
+
+ // Sets appropriate data size and data_start to make sure we tell kernel
+ // about the total size of the buffer we are passing and where to start
+ // writing the list of targets.
+ struct dm_ioctl* io = reinterpret_cast<struct dm_ioctl*>(buffer.get());
+ InitIo(io);
+ io->data_size = data_size;
+ io->data_start = sizeof(*io);
+
+ if (ioctl(fd_, DM_LIST_DEVICES, io)) {
+ PLOG(ERROR) << "Failed to get DM_LIST_DEVICES from kernel";
+ return false;
+ }
+
+ // If the provided buffer wasn't enough to list all devices any data
+ // beyond sizeof(*io) must not be read.
+ if (io->flags & DM_BUFFER_FULL_FLAG) {
+ LOG(INFO) << data_size << " is not enough memory to list all dm devices";
+ return false;
+ }
+
+ // if there are no devices created yet, return success with empty vector
+ if (io->data_size == sizeof(*io)) {
+ return true;
+ }
+
+ // Parse each device and add a new DmBlockDevice to the vector
+ // created from the kernel data.
+ uint32_t next = sizeof(*io);
+ data_size = io->data_size - next;
+ struct dm_name_list* dm_dev =
+ reinterpret_cast<struct dm_name_list*>(static_cast<char*>(buffer.get()) + next);
+
+ while (next && data_size) {
+ devices->emplace_back((dm_dev));
+ if (dm_dev->next == 0) {
+ break;
+ }
+ next += dm_dev->next;
+ data_size -= dm_dev->next;
+ dm_dev = reinterpret_cast<struct dm_name_list*>(static_cast<char*>(buffer.get()) + next);
+ }
+
+ return true;
+}
+
+// Accepts a device mapper device name (like system_a, vendor_b etc) and
+// returns the path to it's device node (or symlink to the device node)
+std::string DeviceMapper::GetDmDevicePathByName(const std::string& /* name */) {
+ return "";
+}
+
+// private methods of DeviceMapper
+void DeviceMapper::InitIo(struct dm_ioctl* io, const std::string& name) const {
+ CHECK(io != nullptr) << "nullptr passed to dm_ioctl initialization";
+ memset(io, 0, sizeof(*io));
+
+ io->version[0] = DM_VERSION0;
+ io->version[1] = DM_VERSION1;
+ io->version[2] = DM_VERSION2;
+ io->data_size = sizeof(*io);
+ io->data_start = 0;
+ if (!name.empty()) {
+ strlcpy(io->name, name.c_str(), sizeof(io->name));
+ }
+}
+
+} // namespace dm
+} // namespace android
diff --git a/fs_mgr/libdm/dm_table.cpp b/fs_mgr/libdm/dm_table.cpp
new file mode 100644
index 0000000..14b3932
--- /dev/null
+++ b/fs_mgr/libdm/dm_table.cpp
@@ -0,0 +1,55 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <android-base/logging.h>
+#include <android-base/macros.h>
+
+#include <string>
+#include <vector>
+
+#include "dm_table.h"
+
+namespace android {
+namespace dm {
+
+bool DmTable::AddTarget(std::unique_ptr<DmTarget>&& /* target */) {
+ return true;
+}
+
+bool DmTable::RemoveTarget(std::unique_ptr<DmTarget>&& /* target */) {
+ return true;
+}
+
+bool DmTable::valid() const {
+ return true;
+}
+
+uint64_t DmTable::size() const {
+ return valid() ? size_ : 0;
+}
+
+// Returns a string represnetation of the table that is ready to be passed
+// down to the kernel for loading
+//
+// Implementation must verify there are no gaps in the table, table starts
+// with sector == 0, and iterate over each target to get its table
+// serialized.
+std::string DmTable::Serialize() const {
+ return "";
+}
+
+} // namespace dm
+} // namespace android
diff --git a/fs_mgr/libdm/dm_target.cpp b/fs_mgr/libdm/dm_target.cpp
new file mode 100644
index 0000000..8bcd526
--- /dev/null
+++ b/fs_mgr/libdm/dm_target.cpp
@@ -0,0 +1,29 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <android-base/logging.h>
+#include <android-base/macros.h>
+
+#include <stdint.h>
+
+#include <string>
+#include <vector>
+
+#include "dm_target.h"
+
+namespace android {
+namespace dm {} // namespace dm
+} // namespace android
diff --git a/fs_mgr/libdm/include/dm.h b/fs_mgr/libdm/include/dm.h
new file mode 100644
index 0000000..52a9a11
--- /dev/null
+++ b/fs_mgr/libdm/include/dm.h
@@ -0,0 +1,152 @@
+/*
+ * Copyright 2018 Google, Inc
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef _LIBDM_DM_H_
+#define _LIBDM_DM_H_
+
+#include <errno.h>
+#include <fcntl.h>
+#include <linux/dm-ioctl.h>
+#include <linux/kdev_t.h>
+#include <sys/sysmacros.h>
+#include <unistd.h>
+
+#include <memory>
+
+#include <android-base/logging.h>
+
+#include <dm_table.h>
+
+// The minimum expected device mapper major.minor version
+#define DM_VERSION0 (4)
+#define DM_VERSION1 (0)
+#define DM_VERSION2 (0)
+
+#define DM_ALIGN_MASK (7)
+#define DM_ALIGN(x) ((x + DM_ALIGN_MASK) & ~DM_ALIGN_MASK)
+
+namespace android {
+namespace dm {
+
+enum class DmDeviceState { INVALID, SUSPENDED, ACTIVE };
+
+class DeviceMapper final {
+ public:
+ class DmBlockDevice final {
+ public:
+ // only allow creating this with dm_name_list
+ DmBlockDevice() = delete;
+
+ explicit DmBlockDevice(struct dm_name_list* d) : name_(d->name), dev_(d->dev){};
+
+ // Returs device mapper name associated with the block device
+ const std::string& name() const { return name_; }
+
+ // Return major number for the block device
+ uint32_t Major() const { return major(dev_); }
+
+ // Return minor number for the block device
+ uint32_t Minor() const { return minor(dev_); }
+ ~DmBlockDevice() = default;
+
+ private:
+ std::string name_;
+ uint64_t dev_;
+ };
+
+ // Creates a device mapper device with given name.
+ // Return 'true' on success and 'false' on failure to
+ // create OR if a device mapper device with the same name already
+ // exists.
+ // TODO(b/110035986): Make this method private and to be only
+ // called through LoadTableAndActivate() below.
+ bool CreateDevice(const std::string& name);
+
+ // Removes a device mapper device with the given name.
+ // Returns 'true' on success, false otherwise.
+ bool DeleteDevice(const std::string& name);
+
+ // Reads the device mapper table from the device with given anme and
+ // returns it in a DmTable object.
+ const std::unique_ptr<DmTable> table(const std::string& name) const;
+
+ // Returns the current state of the underlying device mapper device
+ // with given name.
+ // One of INVALID, SUSPENDED or ACTIVE.
+ DmDeviceState state(const std::string& name) const;
+
+ // Loads the device mapper table from parameter into the underlying
+ // device mapper device with given name and activate / resumes the device in the process.
+ // If a device mapper device with the 'name', doesn't exist, it will be created.
+ // Returns 'true' on success, false otherwise.
+ bool LoadTableAndActivate(const std::string& name, const DmTable& table);
+
+ // Returns true if a list of available device mapper targets registered in the kernel was
+ // successfully read and stored in 'targets'. Returns 'false' otherwise.
+ bool GetAvailableTargets(std::vector<DmTarget>* targets);
+
+ // Return 'true' if it can successfully read the list of device mapper block devices
+ // currently created. 'devices' will be empty if the kernel interactions
+ // were successful and there are no block devices at the moment. Returns
+ // 'false' in case of any failure along the way.
+ bool GetAvailableDevices(std::vector<DmBlockDevice>* devices);
+
+ // Returns the path to the device mapper device node in '/dev' corresponding to
+ // 'name'.
+ std::string GetDmDevicePathByName(const std::string& name);
+
+ // The only way to create a DeviceMapper object.
+ static DeviceMapper& Instance();
+
+ ~DeviceMapper() {
+ if (fd_ != -1) {
+ ::close(fd_);
+ }
+ }
+
+ private:
+ // Maximum possible device mapper targets registered in the kernel.
+ // This is only used to read the list of targets from kernel so we allocate
+ // a finite amount of memory. This limit is in no way enforced by the kernel.
+ static constexpr uint32_t kMaxPossibleDmTargets = 256;
+
+ // Maximum possible device mapper created block devices. Note that this is restricted by
+ // the minor numbers (that used to be 8 bits) that can be range from 0 to 2^20-1 in newer
+ // kernels. In Android systems however, we never expect these to grow beyond the artificial
+ // limit we are imposing here of 256.
+ static constexpr uint32_t kMaxPossibleDmDevices = 256;
+
+ void InitIo(struct dm_ioctl* io, const std::string& name = std::string()) const;
+
+ DeviceMapper() : fd_(-1) {
+ fd_ = TEMP_FAILURE_RETRY(open("/dev/device-mapper", O_RDWR | O_CLOEXEC));
+ if (fd_ < 0) {
+ PLOG(ERROR) << "Failed to open device-mapper";
+ }
+ }
+
+ int fd_;
+ // Non-copyable & Non-movable
+ DeviceMapper(const DeviceMapper&) = delete;
+ DeviceMapper& operator=(const DeviceMapper&) = delete;
+ DeviceMapper& operator=(DeviceMapper&&) = delete;
+ DeviceMapper(DeviceMapper&&) = delete;
+};
+
+} // namespace dm
+} // namespace android
+
+#endif /* _LIBDM_DM_H_ */
diff --git a/fs_mgr/libdm/include/dm_table.h b/fs_mgr/libdm/include/dm_table.h
new file mode 100644
index 0000000..0b1685d
--- /dev/null
+++ b/fs_mgr/libdm/include/dm_table.h
@@ -0,0 +1,75 @@
+/*
+ * Copyright 2018 Google, Inc
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef _LIBDM_DMTABLE_H_
+#define _LIBDM_DMTABLE_H_
+
+#include <stdint.h>
+
+#include <memory>
+#include <string>
+#include <vector>
+
+#include "dm_target.h"
+
+namespace android {
+namespace dm {
+
+class DmTable {
+ public:
+ DmTable() : size_(0){};
+
+ // Adds a target to the device mapper table for a range specified in the target object.
+ // The function will return 'true' if the target was successfully added and doesn't overlap with
+ // any of the existing targets in the table. Gaps are allowed. The final check, including
+ // overlaps and gaps are done before loading the table. Returns 'false' on failure.
+ bool AddTarget(std::unique_ptr<DmTarget>&& target);
+
+ // Removes a target from the table for the range specified in the target object. Returns 'false'
+ // if the target name doesn't match with the one in the table. Returns 'true' if target is
+ // successfully removed.
+ bool RemoveTarget(std::unique_ptr<DmTarget>&& target);
+
+ // Checks the table to make sure it is valid. i.e. Checks for range overlaps, range gaps
+ // and returns 'true' if the table is ready to be loaded into kernel. Returns 'false' if the
+ // table is malformed.
+ bool valid() const;
+
+ // Returns the total size represented by the table in terms of number of 512-byte sectors.
+ // NOTE: This function will overlook if there are any gaps in the targets added in the table.
+ uint64_t size() const;
+
+ // Returns the string represntation of the table that is ready to be passed into the kernel
+ // as part of the DM_TABLE_LOAD ioctl.
+ std::string Serialize() const;
+
+ ~DmTable() = default;
+
+ private:
+ // list of targets defined in this table sorted by
+ // their start and end sectors.
+ // Note: Overlapping targets MUST never be added in this list.
+ std::vector<std::unique_ptr<DmTarget>> targets_;
+
+ // Total size in terms of # of sectors, as calculated by looking at the last and the first
+ // target in 'target_'.
+ uint64_t size_;
+};
+
+} // namespace dm
+} // namespace android
+
+#endif /* _LIBDM_DMTABLE_H_ */
diff --git a/fs_mgr/libdm/include/dm_target.h b/fs_mgr/libdm/include/dm_target.h
new file mode 100644
index 0000000..31b0cb6
--- /dev/null
+++ b/fs_mgr/libdm/include/dm_target.h
@@ -0,0 +1,77 @@
+/*
+ * Copyright 2018 Google, Inc
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef _LIBDM_DMTARGET_H_
+#define _LIBDM_DMTARGET_H_
+
+#include <linux/dm-ioctl.h>
+#include <stdint.h>
+
+#include <android-base/logging.h>
+
+#include <string>
+
+namespace android {
+namespace dm {
+
+class DmTarget {
+ public:
+ DmTarget(const std::string& name, uint64_t start = 0, uint64_t length = 0)
+ : name_(name), v0_(0), v1_(0), v2_(0), start_(start), length_(length){};
+
+ // Creates a DmTarget object from dm_target_version as read from kernel
+ // with DM_LIST_VERSION ioctl.
+ DmTarget(const struct dm_target_versions* vers) : start_(0), length_(0) {
+ CHECK(vers != nullptr) << "Can't create DmTarget with dm_target_versions set to nullptr";
+ v0_ = vers->version[0];
+ v1_ = vers->version[1];
+ v2_ = vers->version[2];
+ name_ = vers->name;
+ }
+
+ virtual ~DmTarget() = default;
+
+ // Returns name of the target.
+ const std::string& name() const { return name_; }
+
+ // Returns size in number of sectors when this target is part of
+ // a DmTable, return 0 otherwise.
+ uint64_t size() const { return length_; }
+
+ // Return string representation of the device mapper target version.
+ std::string version() const {
+ return std::to_string(v0_) + "." + std::to_string(v1_) + "." + std::to_string(v2_);
+ }
+
+ // Function that converts this object to a string of arguments that can
+ // be passed to the kernel for adding this target in a table. Each target (e.g. verity, linear)
+ // must implement this, for it to be used on a device.
+ virtual std::string Serialize() const { return ""; }
+
+ private:
+ // Name of the target.
+ std::string name_;
+ // Target version.
+ uint32_t v0_, v1_, v2_;
+ // logical sector number start and total length (in terms of 512-byte sectors) represented
+ // by this target within a DmTable.
+ uint64_t start_, length_;
+};
+
+} // namespace dm
+} // namespace android
+
+#endif /* _LIBDM_DMTARGET_H_ */
diff --git a/fs_mgr/tools/Android.bp b/fs_mgr/tools/Android.bp
new file mode 100644
index 0000000..4d4aae4
--- /dev/null
+++ b/fs_mgr/tools/Android.bp
@@ -0,0 +1,31 @@
+//
+// Copyright (C) 2018 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+
+cc_binary {
+ name: "dmctl",
+ srcs: ["dmctl.cpp"],
+
+ static_libs: [
+ "libfs_mgr",
+ ],
+
+ shared_libs: [
+ "libbase",
+ "liblog",
+ ],
+
+ cflags: ["-Werror"],
+}
diff --git a/fs_mgr/tools/dmctl.cpp b/fs_mgr/tools/dmctl.cpp
new file mode 100644
index 0000000..b40b83a
--- /dev/null
+++ b/fs_mgr/tools/dmctl.cpp
@@ -0,0 +1,174 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <errno.h>
+#include <fcntl.h>
+#include <getopt.h>
+#include <linux/dm-ioctl.h>
+#include <sys/ioctl.h>
+#include <sys/types.h>
+#include <unistd.h>
+
+#include <android-base/unique_fd.h>
+#include <dm.h>
+
+#include <functional>
+#include <iomanip>
+#include <ios>
+#include <iostream>
+#include <map>
+#include <string>
+#include <vector>
+
+using DeviceMapper = ::android::dm::DeviceMapper;
+using DmTarget = ::android::dm::DmTarget;
+using DmBlockDevice = ::android::dm::DeviceMapper::DmBlockDevice;
+
+static int Usage(void) {
+ std::cerr << "usage: dmctl <command> [command options]" << std::endl;
+ std::cerr << "commands:" << std::endl;
+ std::cerr << " create <dm-name> [<dm-target> [-lo <filename>] <dm-target-args>]" << std::endl;
+ std::cerr << " delete <dm-name>" << std::endl;
+ std::cerr << " list <devices | targets>" << std::endl;
+ std::cerr << " help" << std::endl;
+ return -EINVAL;
+}
+
+static int DmCreateCmdHandler(int argc, char** argv) {
+ if (argc < 1) {
+ std::cerr << "DmCreateCmdHandler: atleast 'name' MUST be provided for target device";
+ return -EINVAL;
+ }
+
+ std::string name = argv[0];
+ DeviceMapper& dm = DeviceMapper::Instance();
+ if (!dm.CreateDevice(name)) {
+ std::cerr << "DmCreateCmdHandler: Failed to create " << name << " device";
+ return -EIO;
+ }
+
+ // if we also have target specified
+ if (argc > 1) {
+ // fall through for now. This will eventually create a DmTarget() based on the target name
+ // passing it the table that is specified at the command line
+ }
+
+ return 0;
+}
+
+static int DmDeleteCmdHandler(int argc, char** argv) {
+ if (argc < 1) {
+ std::cerr << "DmCreateCmdHandler: atleast 'name' MUST be provided for target device";
+ return -EINVAL;
+ }
+
+ std::string name = argv[0];
+ DeviceMapper& dm = DeviceMapper::Instance();
+ if (!dm.DeleteDevice(name)) {
+ std::cerr << "DmCreateCmdHandler: Failed to create " << name << " device";
+ return -EIO;
+ }
+
+ return 0;
+}
+
+static int DmListTargets(DeviceMapper& dm) {
+ std::vector<DmTarget> targets;
+ if (!dm.GetAvailableTargets(&targets)) {
+ std::cerr << "Failed to read available device mapper targets" << std::endl;
+ return -errno;
+ }
+
+ std::cout << "Available Device Mapper Targets:" << std::endl;
+ if (targets.empty()) {
+ std::cout << " <empty>" << std::endl;
+ return 0;
+ }
+
+ for (const auto& target : targets) {
+ std::cout << std::left << std::setw(20) << target.name() << " : " << target.version()
+ << std::endl;
+ }
+
+ return 0;
+}
+
+static int DmListDevices(DeviceMapper& dm) {
+ std::vector<DmBlockDevice> devices;
+ if (!dm.GetAvailableDevices(&devices)) {
+ std::cerr << "Failed to read available device mapper devices" << std::endl;
+ return -errno;
+ }
+ std::cout << "Available Device Mapper Devices:" << std::endl;
+ if (devices.empty()) {
+ std::cout << " <empty>" << std::endl;
+ return 0;
+ }
+
+ for (const auto& dev : devices) {
+ std::cout << std::left << std::setw(20) << dev.name() << " : " << dev.Major() << ":"
+ << dev.Minor() << std::endl;
+ }
+
+ return 0;
+}
+
+static const std::map<std::string, std::function<int(DeviceMapper&)>> listmap = {
+ {"targets", DmListTargets},
+ {"devices", DmListDevices},
+};
+
+static int DmListCmdHandler(int argc, char** argv) {
+ if (argc < 1) {
+ std::cerr << "Invalid arguments, see \'dmctl help\'" << std::endl;
+ return -EINVAL;
+ }
+
+ DeviceMapper& dm = DeviceMapper::Instance();
+ for (const auto& l : listmap) {
+ if (l.first == argv[0]) return l.second(dm);
+ }
+
+ std::cerr << "Invalid argument to \'dmctl list\': " << argv[0] << std::endl;
+ return -EINVAL;
+}
+
+static int HelpCmdHandler(int /* argc */, char** /* argv */) {
+ Usage();
+ return 0;
+}
+
+static std::map<std::string, std::function<int(int, char**)>> cmdmap = {
+ {"create", DmCreateCmdHandler},
+ {"delete", DmDeleteCmdHandler},
+ {"list", DmListCmdHandler},
+ {"help", HelpCmdHandler},
+};
+
+int main(int argc, char** argv) {
+ android::base::InitLogging(argv, &android::base::StderrLogger);
+ if (argc < 2) {
+ return Usage();
+ }
+
+ for (const auto& cmd : cmdmap) {
+ if (cmd.first == argv[1]) {
+ return cmd.second(argc - 2, argv + 2);
+ }
+ }
+
+ return Usage();
+}
diff --git a/init/Android.bp b/init/Android.bp
index 25877c0..7d863c8 100644
--- a/init/Android.bp
+++ b/init/Android.bp
@@ -67,24 +67,26 @@
"libsquashfs_utils",
"liblogwrap",
"libext4_utils",
- "libcutils",
- "libbase",
- "libc",
"libseccomp_policy",
- "libselinux",
- "liblog",
"libcrypto_utils",
- "libcrypto",
- "libc++_static",
- "libdl",
"libsparse",
- "libz",
"libprocessgroup",
"libavb",
"libkeyutils",
"libprotobuf-cpp-lite",
"libpropertyinfoserializer",
"libpropertyinfoparser",
+ "libselinux",
+ ],
+ shared_libs: [
+ "libcutils",
+ "libbase",
+ "libc",
+ "liblog",
+ "libcrypto",
+ "libc++",
+ "libdl",
+ "libz",
],
}
@@ -166,7 +168,6 @@
cc_test {
name: "init_tests",
defaults: ["init_defaults"],
- static_executable: true,
srcs: [
"devices_test.cpp",
"init_test.cpp",
@@ -187,7 +188,6 @@
cc_benchmark {
name: "init_benchmarks",
- static_executable: true,
defaults: ["init_defaults"],
srcs: [
"subcontext_benchmark.cpp",
diff --git a/init/Android.mk b/init/Android.mk
index c4a6a50..da27a73 100644
--- a/init/Android.mk
+++ b/init/Android.mk
@@ -45,7 +45,6 @@
LOCAL_MODULE:= init
-LOCAL_FORCE_STATIC_EXECUTABLE := true
LOCAL_MODULE_PATH := $(TARGET_ROOT_OUT)
LOCAL_UNSTRIPPED_PATH := $(TARGET_ROOT_OUT_UNSTRIPPED)
@@ -59,18 +58,10 @@
libsquashfs_utils \
liblogwrap \
libext4_utils \
- libcutils \
- libbase \
- libc \
libseccomp_policy \
- libselinux \
- liblog \
libcrypto_utils \
- libcrypto \
- libc++_static \
- libdl \
libsparse \
- libz \
+ libselinux \
libprocessgroup \
libavb \
libkeyutils \
@@ -78,6 +69,25 @@
libpropertyinfoserializer \
libpropertyinfoparser \
+shared_libs := \
+ libcutils \
+ libbase \
+ liblog \
+ libcrypto \
+ libdl \
+ libz \
+
+ifneq ($(BOARD_BUILD_SYSTEM_ROOT_IMAGE),true)
+# init is static executable for non-system-as-root devices, because the dynamic linker
+# and shared libs are not available before /system is mounted, but init has to run
+# before the partition is mounted.
+LOCAL_STATIC_LIBRARIES += $(shared_libs) libc++_static
+LOCAL_FORCE_STATIC_EXECUTABLE := true
+else
+LOCAL_SHARED_LIBRARIES := $(shared_libs) libc++
+endif
+shared_libs :=
+
LOCAL_REQUIRED_MODULES := \
e2fsdroid \
mke2fs \
diff --git a/init/uevent_listener.cpp b/init/uevent_listener.cpp
index 24b14c4..81486e1 100644
--- a/init/uevent_listener.cpp
+++ b/init/uevent_listener.cpp
@@ -23,7 +23,11 @@
#include <memory>
+#include <android-base/chrono_utils.h>
+#include <android-base/file.h>
#include <android-base/logging.h>
+#include <android-base/scopeguard.h>
+#include <android-base/stringprintf.h>
#include <cutils/uevent.h>
namespace android {
@@ -130,9 +134,19 @@
int fd = openat(dfd, "uevent", O_WRONLY);
if (fd >= 0) {
+ android::base::Timer t;
write(fd, "add\n", 4);
+ const std::string fd_path = android::base::StringPrintf("/proc/self/fd/%d", fd);
+ std::string uevent_file_path;
+ android::base::Readlink(fd_path, &uevent_file_path);
close(fd);
+ auto guard = android::base::make_scope_guard([&t, &uevent_file_path]() {
+ if (t.duration() > 50ms) {
+ LOG(WARNING) << "ReadUevent took " << t << " on '" << uevent_file_path << "'";
+ }
+ });
+
Uevent uevent;
while (ReadUevent(&uevent)) {
if (callback(uevent) == ListenerAction::kStop) return ListenerAction::kStop;
diff --git a/libsparse/.clang-format b/libsparse/.clang-format
new file mode 120000
index 0000000..fd0645f
--- /dev/null
+++ b/libsparse/.clang-format
@@ -0,0 +1 @@
+../.clang-format-2
\ No newline at end of file
diff --git a/libsparse/Android.bp b/libsparse/Android.bp
index c7c089f..8ad339f 100644
--- a/libsparse/Android.bp
+++ b/libsparse/Android.bp
@@ -6,11 +6,11 @@
recovery_available: true,
unique_host_soname: true,
srcs: [
- "backed_block.c",
- "output_file.c",
- "sparse.c",
- "sparse_crc32.c",
- "sparse_err.c",
+ "backed_block.cpp",
+ "output_file.cpp",
+ "sparse.cpp",
+ "sparse_crc32.cpp",
+ "sparse_err.cpp",
"sparse_read.cpp",
],
cflags: ["-Werror"],
@@ -31,8 +31,8 @@
name: "simg2img",
host_supported: true,
srcs: [
- "simg2img.c",
- "sparse_crc32.c",
+ "simg2img.cpp",
+ "sparse_crc32.cpp",
],
static_libs: [
"libsparse",
@@ -46,7 +46,7 @@
cc_binary {
name: "img2simg",
host_supported: true,
- srcs: ["img2simg.c"],
+ srcs: ["img2simg.cpp"],
static_libs: [
"libsparse",
"libz",
@@ -58,7 +58,7 @@
cc_binary_host {
name: "append2simg",
- srcs: ["append2simg.c"],
+ srcs: ["append2simg.cpp"],
static_libs: [
"libsparse",
"libz",
diff --git a/libsparse/OWNERS b/libsparse/OWNERS
new file mode 100644
index 0000000..70b375f
--- /dev/null
+++ b/libsparse/OWNERS
@@ -0,0 +1 @@
+ccross@google.com
diff --git a/libsparse/append2simg.c b/libsparse/append2simg.c
deleted file mode 100644
index eef8764..0000000
--- a/libsparse/append2simg.c
+++ /dev/null
@@ -1,140 +0,0 @@
-/*
- * Copyright (C) 2013 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#define _FILE_OFFSET_BITS 64
-#define _LARGEFILE64_SOURCE 1
-#define _GNU_SOURCE
-
-#include <errno.h>
-#include <fcntl.h>
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-#include <unistd.h>
-
-#include <sparse/sparse.h>
-#include "sparse_file.h"
-#include "backed_block.h"
-
-#ifndef O_BINARY
-#define O_BINARY 0
-#endif
-
-#if defined(__APPLE__) && defined(__MACH__)
-#define lseek64 lseek
-#endif
-#if defined(__APPLE__) && defined(__MACH__)
-#define lseek64 lseek
-#define off64_t off_t
-#endif
-
-void usage()
-{
- fprintf(stderr, "Usage: append2simg <output> <input>\n");
-}
-
-int main(int argc, char *argv[])
-{
- int output;
- int output_block;
- char *output_path;
- struct sparse_file *sparse_output;
-
- int input;
- char *input_path;
- off64_t input_len;
-
- int tmp_fd;
- char *tmp_path;
-
- int ret;
-
- if (argc == 3) {
- output_path = argv[1];
- input_path = argv[2];
- } else {
- usage();
- exit(-1);
- }
-
- ret = asprintf(&tmp_path, "%s.append2simg", output_path);
- if (ret < 0) {
- fprintf(stderr, "Couldn't allocate filename\n");
- exit(-1);
- }
-
- output = open(output_path, O_RDWR | O_BINARY);
- if (output < 0) {
- fprintf(stderr, "Couldn't open output file (%s)\n", strerror(errno));
- exit(-1);
- }
-
- sparse_output = sparse_file_import_auto(output, false, true);
- if (!sparse_output) {
- fprintf(stderr, "Couldn't import output file\n");
- exit(-1);
- }
-
- input = open(input_path, O_RDONLY | O_BINARY);
- if (input < 0) {
- fprintf(stderr, "Couldn't open input file (%s)\n", strerror(errno));
- exit(-1);
- }
-
- input_len = lseek64(input, 0, SEEK_END);
- if (input_len < 0) {
- fprintf(stderr, "Couldn't get input file length (%s)\n", strerror(errno));
- exit(-1);
- } else if (input_len % sparse_output->block_size) {
- fprintf(stderr, "Input file is not a multiple of the output file's block size");
- exit(-1);
- }
- lseek64(input, 0, SEEK_SET);
-
- output_block = sparse_output->len / sparse_output->block_size;
- if (sparse_file_add_fd(sparse_output, input, 0, input_len, output_block) < 0) {
- fprintf(stderr, "Couldn't add input file\n");
- exit(-1);
- }
- sparse_output->len += input_len;
-
- tmp_fd = open(tmp_path, O_WRONLY | O_CREAT | O_BINARY, 0664);
- if (tmp_fd < 0) {
- fprintf(stderr, "Couldn't open temporary file (%s)\n", strerror(errno));
- exit(-1);
- }
-
- lseek64(output, 0, SEEK_SET);
- if (sparse_file_write(sparse_output, tmp_fd, false, true, false) < 0) {
- fprintf(stderr, "Failed to write sparse file\n");
- exit(-1);
- }
-
- sparse_file_destroy(sparse_output);
- close(tmp_fd);
- close(output);
- close(input);
-
- ret = rename(tmp_path, output_path);
- if (ret < 0) {
- fprintf(stderr, "Failed to rename temporary file (%s)\n", strerror(errno));
- exit(-1);
- }
-
- free(tmp_path);
-
- exit(0);
-}
diff --git a/libsparse/append2simg.cpp b/libsparse/append2simg.cpp
new file mode 100644
index 0000000..99f4339
--- /dev/null
+++ b/libsparse/append2simg.cpp
@@ -0,0 +1,137 @@
+/*
+ * Copyright (C) 2013 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#define _FILE_OFFSET_BITS 64
+#define _LARGEFILE64_SOURCE 1
+
+#include <errno.h>
+#include <fcntl.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#include <sparse/sparse.h>
+#include "backed_block.h"
+#include "sparse_file.h"
+
+#ifndef O_BINARY
+#define O_BINARY 0
+#endif
+
+#if defined(__APPLE__) && defined(__MACH__)
+#define lseek64 lseek
+#endif
+#if defined(__APPLE__) && defined(__MACH__)
+#define lseek64 lseek
+#define off64_t off_t
+#endif
+
+void usage() {
+ fprintf(stderr, "Usage: append2simg <output> <input>\n");
+}
+
+int main(int argc, char* argv[]) {
+ int output;
+ int output_block;
+ char* output_path;
+ struct sparse_file* sparse_output;
+
+ int input;
+ char* input_path;
+ off64_t input_len;
+
+ int tmp_fd;
+ char* tmp_path;
+
+ int ret;
+
+ if (argc == 3) {
+ output_path = argv[1];
+ input_path = argv[2];
+ } else {
+ usage();
+ exit(-1);
+ }
+
+ ret = asprintf(&tmp_path, "%s.append2simg", output_path);
+ if (ret < 0) {
+ fprintf(stderr, "Couldn't allocate filename\n");
+ exit(-1);
+ }
+
+ output = open(output_path, O_RDWR | O_BINARY);
+ if (output < 0) {
+ fprintf(stderr, "Couldn't open output file (%s)\n", strerror(errno));
+ exit(-1);
+ }
+
+ sparse_output = sparse_file_import_auto(output, false, true);
+ if (!sparse_output) {
+ fprintf(stderr, "Couldn't import output file\n");
+ exit(-1);
+ }
+
+ input = open(input_path, O_RDONLY | O_BINARY);
+ if (input < 0) {
+ fprintf(stderr, "Couldn't open input file (%s)\n", strerror(errno));
+ exit(-1);
+ }
+
+ input_len = lseek64(input, 0, SEEK_END);
+ if (input_len < 0) {
+ fprintf(stderr, "Couldn't get input file length (%s)\n", strerror(errno));
+ exit(-1);
+ } else if (input_len % sparse_output->block_size) {
+ fprintf(stderr, "Input file is not a multiple of the output file's block size");
+ exit(-1);
+ }
+ lseek64(input, 0, SEEK_SET);
+
+ output_block = sparse_output->len / sparse_output->block_size;
+ if (sparse_file_add_fd(sparse_output, input, 0, input_len, output_block) < 0) {
+ fprintf(stderr, "Couldn't add input file\n");
+ exit(-1);
+ }
+ sparse_output->len += input_len;
+
+ tmp_fd = open(tmp_path, O_WRONLY | O_CREAT | O_BINARY, 0664);
+ if (tmp_fd < 0) {
+ fprintf(stderr, "Couldn't open temporary file (%s)\n", strerror(errno));
+ exit(-1);
+ }
+
+ lseek64(output, 0, SEEK_SET);
+ if (sparse_file_write(sparse_output, tmp_fd, false, true, false) < 0) {
+ fprintf(stderr, "Failed to write sparse file\n");
+ exit(-1);
+ }
+
+ sparse_file_destroy(sparse_output);
+ close(tmp_fd);
+ close(output);
+ close(input);
+
+ ret = rename(tmp_path, output_path);
+ if (ret < 0) {
+ fprintf(stderr, "Failed to rename temporary file (%s)\n", strerror(errno));
+ exit(-1);
+ }
+
+ free(tmp_path);
+
+ exit(0);
+}
diff --git a/libsparse/backed_block.c b/libsparse/backed_block.c
deleted file mode 100644
index 794cd6b..0000000
--- a/libsparse/backed_block.c
+++ /dev/null
@@ -1,404 +0,0 @@
-/*
- * Copyright (C) 2010 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include <assert.h>
-#include <errno.h>
-#include <stdint.h>
-#include <stdlib.h>
-#include <string.h>
-
-#include "backed_block.h"
-#include "sparse_defs.h"
-
-struct backed_block {
- unsigned int block;
- unsigned int len;
- enum backed_block_type type;
- union {
- struct {
- void *data;
- } data;
- struct {
- char *filename;
- int64_t offset;
- } file;
- struct {
- int fd;
- int64_t offset;
- } fd;
- struct {
- uint32_t val;
- } fill;
- };
- struct backed_block *next;
-};
-
-struct backed_block_list {
- struct backed_block *data_blocks;
- struct backed_block *last_used;
- unsigned int block_size;
-};
-
-struct backed_block *backed_block_iter_new(struct backed_block_list *bbl)
-{
- return bbl->data_blocks;
-}
-
-struct backed_block *backed_block_iter_next(struct backed_block *bb)
-{
- return bb->next;
-}
-
-unsigned int backed_block_len(struct backed_block *bb)
-{
- return bb->len;
-}
-
-unsigned int backed_block_block(struct backed_block *bb)
-{
- return bb->block;
-}
-
-void *backed_block_data(struct backed_block *bb)
-{
- assert(bb->type == BACKED_BLOCK_DATA);
- return bb->data.data;
-}
-
-const char *backed_block_filename(struct backed_block *bb)
-{
- assert(bb->type == BACKED_BLOCK_FILE);
- return bb->file.filename;
-}
-
-int backed_block_fd(struct backed_block *bb)
-{
- assert(bb->type == BACKED_BLOCK_FD);
- return bb->fd.fd;
-}
-
-int64_t backed_block_file_offset(struct backed_block *bb)
-{
- assert(bb->type == BACKED_BLOCK_FILE || bb->type == BACKED_BLOCK_FD);
- if (bb->type == BACKED_BLOCK_FILE) {
- return bb->file.offset;
- } else { /* bb->type == BACKED_BLOCK_FD */
- return bb->fd.offset;
- }
-}
-
-uint32_t backed_block_fill_val(struct backed_block *bb)
-{
- assert(bb->type == BACKED_BLOCK_FILL);
- return bb->fill.val;
-}
-
-enum backed_block_type backed_block_type(struct backed_block *bb)
-{
- return bb->type;
-}
-
-void backed_block_destroy(struct backed_block *bb)
-{
- if (bb->type == BACKED_BLOCK_FILE) {
- free(bb->file.filename);
- }
-
- free(bb);
-}
-
-struct backed_block_list *backed_block_list_new(unsigned int block_size)
-{
- struct backed_block_list *b = calloc(sizeof(struct backed_block_list), 1);
- b->block_size = block_size;
- return b;
-}
-
-void backed_block_list_destroy(struct backed_block_list *bbl)
-{
- if (bbl->data_blocks) {
- struct backed_block *bb = bbl->data_blocks;
- while (bb) {
- struct backed_block *next = bb->next;
- backed_block_destroy(bb);
- bb = next;
- }
- }
-
- free(bbl);
-}
-
-void backed_block_list_move(struct backed_block_list *from,
- struct backed_block_list *to, struct backed_block *start,
- struct backed_block *end)
-{
- struct backed_block *bb;
-
- if (start == NULL) {
- start = from->data_blocks;
- }
-
- if (!end) {
- for (end = start; end && end->next; end = end->next)
- ;
- }
-
- if (start == NULL || end == NULL) {
- return;
- }
-
- from->last_used = NULL;
- to->last_used = NULL;
- if (from->data_blocks == start) {
- from->data_blocks = end->next;
- } else {
- for (bb = from->data_blocks; bb; bb = bb->next) {
- if (bb->next == start) {
- bb->next = end->next;
- break;
- }
- }
- }
-
- if (!to->data_blocks) {
- to->data_blocks = start;
- end->next = NULL;
- } else {
- for (bb = to->data_blocks; bb; bb = bb->next) {
- if (!bb->next || bb->next->block > start->block) {
- end->next = bb->next;
- bb->next = start;
- break;
- }
- }
- }
-}
-
-/* may free b */
-static int merge_bb(struct backed_block_list *bbl,
- struct backed_block *a, struct backed_block *b)
-{
- unsigned int block_len;
-
- /* Block doesn't exist (possible if one block is the last block) */
- if (!a || !b) {
- return -EINVAL;
- }
-
- assert(a->block < b->block);
-
- /* Blocks are of different types */
- if (a->type != b->type) {
- return -EINVAL;
- }
-
- /* Blocks are not adjacent */
- block_len = a->len / bbl->block_size; /* rounds down */
- if (a->block + block_len != b->block) {
- return -EINVAL;
- }
-
- switch (a->type) {
- case BACKED_BLOCK_DATA:
- /* Don't support merging data for now */
- return -EINVAL;
- case BACKED_BLOCK_FILL:
- if (a->fill.val != b->fill.val) {
- return -EINVAL;
- }
- break;
- case BACKED_BLOCK_FILE:
- /* Already make sure b->type is BACKED_BLOCK_FILE */
- if (strcmp(a->file.filename, b->file.filename) ||
- a->file.offset + a->len != b->file.offset) {
- return -EINVAL;
- }
- break;
- case BACKED_BLOCK_FD:
- if (a->fd.fd != b->fd.fd ||
- a->fd.offset + a->len != b->fd.offset) {
- return -EINVAL;
- }
- break;
- }
-
- /* Blocks are compatible and adjacent, with a before b. Merge b into a,
- * and free b */
- a->len += b->len;
- a->next = b->next;
-
- backed_block_destroy(b);
-
- return 0;
-}
-
-static int queue_bb(struct backed_block_list *bbl, struct backed_block *new_bb)
-{
- struct backed_block *bb;
-
- if (bbl->data_blocks == NULL) {
- bbl->data_blocks = new_bb;
- return 0;
- }
-
- if (bbl->data_blocks->block > new_bb->block) {
- new_bb->next = bbl->data_blocks;
- bbl->data_blocks = new_bb;
- return 0;
- }
-
- /* Optimization: blocks are mostly queued in sequence, so save the
- pointer to the last bb that was added, and start searching from
- there if the next block number is higher */
- if (bbl->last_used && new_bb->block > bbl->last_used->block)
- bb = bbl->last_used;
- else
- bb = bbl->data_blocks;
- bbl->last_used = new_bb;
-
- for (; bb->next && bb->next->block < new_bb->block; bb = bb->next)
- ;
-
- if (bb->next == NULL) {
- bb->next = new_bb;
- } else {
- new_bb->next = bb->next;
- bb->next = new_bb;
- }
-
- merge_bb(bbl, new_bb, new_bb->next);
- if (!merge_bb(bbl, bb, new_bb)) {
- /* new_bb destroyed, point to retained as last_used */
- bbl->last_used = bb;
- }
-
- return 0;
-}
-
-/* Queues a fill block of memory to be written to the specified data blocks */
-int backed_block_add_fill(struct backed_block_list *bbl, unsigned int fill_val,
- unsigned int len, unsigned int block)
-{
- struct backed_block *bb = calloc(1, sizeof(struct backed_block));
- if (bb == NULL) {
- return -ENOMEM;
- }
-
- bb->block = block;
- bb->len = len;
- bb->type = BACKED_BLOCK_FILL;
- bb->fill.val = fill_val;
- bb->next = NULL;
-
- return queue_bb(bbl, bb);
-}
-
-/* Queues a block of memory to be written to the specified data blocks */
-int backed_block_add_data(struct backed_block_list *bbl, void *data,
- unsigned int len, unsigned int block)
-{
- struct backed_block *bb = calloc(1, sizeof(struct backed_block));
- if (bb == NULL) {
- return -ENOMEM;
- }
-
- bb->block = block;
- bb->len = len;
- bb->type = BACKED_BLOCK_DATA;
- bb->data.data = data;
- bb->next = NULL;
-
- return queue_bb(bbl, bb);
-}
-
-/* Queues a chunk of a file on disk to be written to the specified data blocks */
-int backed_block_add_file(struct backed_block_list *bbl, const char *filename,
- int64_t offset, unsigned int len, unsigned int block)
-{
- struct backed_block *bb = calloc(1, sizeof(struct backed_block));
- if (bb == NULL) {
- return -ENOMEM;
- }
-
- bb->block = block;
- bb->len = len;
- bb->type = BACKED_BLOCK_FILE;
- bb->file.filename = strdup(filename);
- bb->file.offset = offset;
- bb->next = NULL;
-
- return queue_bb(bbl, bb);
-}
-
-/* Queues a chunk of a fd to be written to the specified data blocks */
-int backed_block_add_fd(struct backed_block_list *bbl, int fd, int64_t offset,
- unsigned int len, unsigned int block)
-{
- struct backed_block *bb = calloc(1, sizeof(struct backed_block));
- if (bb == NULL) {
- return -ENOMEM;
- }
-
- bb->block = block;
- bb->len = len;
- bb->type = BACKED_BLOCK_FD;
- bb->fd.fd = fd;
- bb->fd.offset = offset;
- bb->next = NULL;
-
- return queue_bb(bbl, bb);
-}
-
-int backed_block_split(struct backed_block_list *bbl, struct backed_block *bb,
- unsigned int max_len)
-{
- struct backed_block *new_bb;
-
- max_len = ALIGN_DOWN(max_len, bbl->block_size);
-
- if (bb->len <= max_len) {
- return 0;
- }
-
- new_bb = malloc(sizeof(struct backed_block));
- if (new_bb == NULL) {
- return -ENOMEM;
- }
-
- *new_bb = *bb;
-
- new_bb->len = bb->len - max_len;
- new_bb->block = bb->block + max_len / bbl->block_size;
- new_bb->next = bb->next;
- bb->next = new_bb;
- bb->len = max_len;
-
- switch (bb->type) {
- case BACKED_BLOCK_DATA:
- new_bb->data.data = (char *)bb->data.data + max_len;
- break;
- case BACKED_BLOCK_FILE:
- new_bb->file.offset += max_len;
- break;
- case BACKED_BLOCK_FD:
- new_bb->fd.offset += max_len;
- break;
- case BACKED_BLOCK_FILL:
- break;
- }
-
- return 0;
-}
diff --git a/libsparse/backed_block.cpp b/libsparse/backed_block.cpp
new file mode 100644
index 0000000..7f5632e
--- /dev/null
+++ b/libsparse/backed_block.cpp
@@ -0,0 +1,380 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <assert.h>
+#include <errno.h>
+#include <stdint.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "backed_block.h"
+#include "sparse_defs.h"
+
+struct backed_block {
+ unsigned int block;
+ unsigned int len;
+ enum backed_block_type type;
+ union {
+ struct {
+ void* data;
+ } data;
+ struct {
+ char* filename;
+ int64_t offset;
+ } file;
+ struct {
+ int fd;
+ int64_t offset;
+ } fd;
+ struct {
+ uint32_t val;
+ } fill;
+ };
+ struct backed_block* next;
+};
+
+struct backed_block_list {
+ struct backed_block* data_blocks;
+ struct backed_block* last_used;
+ unsigned int block_size;
+};
+
+struct backed_block* backed_block_iter_new(struct backed_block_list* bbl) {
+ return bbl->data_blocks;
+}
+
+struct backed_block* backed_block_iter_next(struct backed_block* bb) {
+ return bb->next;
+}
+
+unsigned int backed_block_len(struct backed_block* bb) {
+ return bb->len;
+}
+
+unsigned int backed_block_block(struct backed_block* bb) {
+ return bb->block;
+}
+
+void* backed_block_data(struct backed_block* bb) {
+ assert(bb->type == BACKED_BLOCK_DATA);
+ return bb->data.data;
+}
+
+const char* backed_block_filename(struct backed_block* bb) {
+ assert(bb->type == BACKED_BLOCK_FILE);
+ return bb->file.filename;
+}
+
+int backed_block_fd(struct backed_block* bb) {
+ assert(bb->type == BACKED_BLOCK_FD);
+ return bb->fd.fd;
+}
+
+int64_t backed_block_file_offset(struct backed_block* bb) {
+ assert(bb->type == BACKED_BLOCK_FILE || bb->type == BACKED_BLOCK_FD);
+ if (bb->type == BACKED_BLOCK_FILE) {
+ return bb->file.offset;
+ } else { /* bb->type == BACKED_BLOCK_FD */
+ return bb->fd.offset;
+ }
+}
+
+uint32_t backed_block_fill_val(struct backed_block* bb) {
+ assert(bb->type == BACKED_BLOCK_FILL);
+ return bb->fill.val;
+}
+
+enum backed_block_type backed_block_type(struct backed_block* bb) {
+ return bb->type;
+}
+
+void backed_block_destroy(struct backed_block* bb) {
+ if (bb->type == BACKED_BLOCK_FILE) {
+ free(bb->file.filename);
+ }
+
+ free(bb);
+}
+
+struct backed_block_list* backed_block_list_new(unsigned int block_size) {
+ struct backed_block_list* b =
+ reinterpret_cast<backed_block_list*>(calloc(sizeof(struct backed_block_list), 1));
+ b->block_size = block_size;
+ return b;
+}
+
+void backed_block_list_destroy(struct backed_block_list* bbl) {
+ if (bbl->data_blocks) {
+ struct backed_block* bb = bbl->data_blocks;
+ while (bb) {
+ struct backed_block* next = bb->next;
+ backed_block_destroy(bb);
+ bb = next;
+ }
+ }
+
+ free(bbl);
+}
+
+void backed_block_list_move(struct backed_block_list* from, struct backed_block_list* to,
+ struct backed_block* start, struct backed_block* end) {
+ struct backed_block* bb;
+
+ if (start == NULL) {
+ start = from->data_blocks;
+ }
+
+ if (!end) {
+ for (end = start; end && end->next; end = end->next)
+ ;
+ }
+
+ if (start == NULL || end == NULL) {
+ return;
+ }
+
+ from->last_used = NULL;
+ to->last_used = NULL;
+ if (from->data_blocks == start) {
+ from->data_blocks = end->next;
+ } else {
+ for (bb = from->data_blocks; bb; bb = bb->next) {
+ if (bb->next == start) {
+ bb->next = end->next;
+ break;
+ }
+ }
+ }
+
+ if (!to->data_blocks) {
+ to->data_blocks = start;
+ end->next = NULL;
+ } else {
+ for (bb = to->data_blocks; bb; bb = bb->next) {
+ if (!bb->next || bb->next->block > start->block) {
+ end->next = bb->next;
+ bb->next = start;
+ break;
+ }
+ }
+ }
+}
+
+/* may free b */
+static int merge_bb(struct backed_block_list* bbl, struct backed_block* a, struct backed_block* b) {
+ unsigned int block_len;
+
+ /* Block doesn't exist (possible if one block is the last block) */
+ if (!a || !b) {
+ return -EINVAL;
+ }
+
+ assert(a->block < b->block);
+
+ /* Blocks are of different types */
+ if (a->type != b->type) {
+ return -EINVAL;
+ }
+
+ /* Blocks are not adjacent */
+ block_len = a->len / bbl->block_size; /* rounds down */
+ if (a->block + block_len != b->block) {
+ return -EINVAL;
+ }
+
+ switch (a->type) {
+ case BACKED_BLOCK_DATA:
+ /* Don't support merging data for now */
+ return -EINVAL;
+ case BACKED_BLOCK_FILL:
+ if (a->fill.val != b->fill.val) {
+ return -EINVAL;
+ }
+ break;
+ case BACKED_BLOCK_FILE:
+ /* Already make sure b->type is BACKED_BLOCK_FILE */
+ if (strcmp(a->file.filename, b->file.filename) || a->file.offset + a->len != b->file.offset) {
+ return -EINVAL;
+ }
+ break;
+ case BACKED_BLOCK_FD:
+ if (a->fd.fd != b->fd.fd || a->fd.offset + a->len != b->fd.offset) {
+ return -EINVAL;
+ }
+ break;
+ }
+
+ /* Blocks are compatible and adjacent, with a before b. Merge b into a,
+ * and free b */
+ a->len += b->len;
+ a->next = b->next;
+
+ backed_block_destroy(b);
+
+ return 0;
+}
+
+static int queue_bb(struct backed_block_list* bbl, struct backed_block* new_bb) {
+ struct backed_block* bb;
+
+ if (bbl->data_blocks == NULL) {
+ bbl->data_blocks = new_bb;
+ return 0;
+ }
+
+ if (bbl->data_blocks->block > new_bb->block) {
+ new_bb->next = bbl->data_blocks;
+ bbl->data_blocks = new_bb;
+ return 0;
+ }
+
+ /* Optimization: blocks are mostly queued in sequence, so save the
+ pointer to the last bb that was added, and start searching from
+ there if the next block number is higher */
+ if (bbl->last_used && new_bb->block > bbl->last_used->block)
+ bb = bbl->last_used;
+ else
+ bb = bbl->data_blocks;
+ bbl->last_used = new_bb;
+
+ for (; bb->next && bb->next->block < new_bb->block; bb = bb->next)
+ ;
+
+ if (bb->next == NULL) {
+ bb->next = new_bb;
+ } else {
+ new_bb->next = bb->next;
+ bb->next = new_bb;
+ }
+
+ merge_bb(bbl, new_bb, new_bb->next);
+ if (!merge_bb(bbl, bb, new_bb)) {
+ /* new_bb destroyed, point to retained as last_used */
+ bbl->last_used = bb;
+ }
+
+ return 0;
+}
+
+/* Queues a fill block of memory to be written to the specified data blocks */
+int backed_block_add_fill(struct backed_block_list* bbl, unsigned int fill_val, unsigned int len,
+ unsigned int block) {
+ struct backed_block* bb = reinterpret_cast<backed_block*>(calloc(1, sizeof(struct backed_block)));
+ if (bb == NULL) {
+ return -ENOMEM;
+ }
+
+ bb->block = block;
+ bb->len = len;
+ bb->type = BACKED_BLOCK_FILL;
+ bb->fill.val = fill_val;
+ bb->next = NULL;
+
+ return queue_bb(bbl, bb);
+}
+
+/* Queues a block of memory to be written to the specified data blocks */
+int backed_block_add_data(struct backed_block_list* bbl, void* data, unsigned int len,
+ unsigned int block) {
+ struct backed_block* bb = reinterpret_cast<backed_block*>(calloc(1, sizeof(struct backed_block)));
+ if (bb == NULL) {
+ return -ENOMEM;
+ }
+
+ bb->block = block;
+ bb->len = len;
+ bb->type = BACKED_BLOCK_DATA;
+ bb->data.data = data;
+ bb->next = NULL;
+
+ return queue_bb(bbl, bb);
+}
+
+/* Queues a chunk of a file on disk to be written to the specified data blocks */
+int backed_block_add_file(struct backed_block_list* bbl, const char* filename, int64_t offset,
+ unsigned int len, unsigned int block) {
+ struct backed_block* bb = reinterpret_cast<backed_block*>(calloc(1, sizeof(struct backed_block)));
+ if (bb == NULL) {
+ return -ENOMEM;
+ }
+
+ bb->block = block;
+ bb->len = len;
+ bb->type = BACKED_BLOCK_FILE;
+ bb->file.filename = strdup(filename);
+ bb->file.offset = offset;
+ bb->next = NULL;
+
+ return queue_bb(bbl, bb);
+}
+
+/* Queues a chunk of a fd to be written to the specified data blocks */
+int backed_block_add_fd(struct backed_block_list* bbl, int fd, int64_t offset, unsigned int len,
+ unsigned int block) {
+ struct backed_block* bb = reinterpret_cast<backed_block*>(calloc(1, sizeof(struct backed_block)));
+ if (bb == NULL) {
+ return -ENOMEM;
+ }
+
+ bb->block = block;
+ bb->len = len;
+ bb->type = BACKED_BLOCK_FD;
+ bb->fd.fd = fd;
+ bb->fd.offset = offset;
+ bb->next = NULL;
+
+ return queue_bb(bbl, bb);
+}
+
+int backed_block_split(struct backed_block_list* bbl, struct backed_block* bb,
+ unsigned int max_len) {
+ struct backed_block* new_bb;
+
+ max_len = ALIGN_DOWN(max_len, bbl->block_size);
+
+ if (bb->len <= max_len) {
+ return 0;
+ }
+
+ new_bb = reinterpret_cast<backed_block*>(malloc(sizeof(struct backed_block)));
+ if (new_bb == NULL) {
+ return -ENOMEM;
+ }
+
+ *new_bb = *bb;
+
+ new_bb->len = bb->len - max_len;
+ new_bb->block = bb->block + max_len / bbl->block_size;
+ new_bb->next = bb->next;
+ bb->next = new_bb;
+ bb->len = max_len;
+
+ switch (bb->type) {
+ case BACKED_BLOCK_DATA:
+ new_bb->data.data = (char*)bb->data.data + max_len;
+ break;
+ case BACKED_BLOCK_FILE:
+ new_bb->file.offset += max_len;
+ break;
+ case BACKED_BLOCK_FD:
+ new_bb->fd.offset += max_len;
+ break;
+ case BACKED_BLOCK_FILL:
+ break;
+ }
+
+ return 0;
+}
diff --git a/libsparse/backed_block.h b/libsparse/backed_block.h
index 1a159be..3a75460 100644
--- a/libsparse/backed_block.h
+++ b/libsparse/backed_block.h
@@ -23,42 +23,40 @@
struct backed_block;
enum backed_block_type {
- BACKED_BLOCK_DATA,
- BACKED_BLOCK_FILE,
- BACKED_BLOCK_FD,
- BACKED_BLOCK_FILL,
+ BACKED_BLOCK_DATA,
+ BACKED_BLOCK_FILE,
+ BACKED_BLOCK_FD,
+ BACKED_BLOCK_FILL,
};
-int backed_block_add_data(struct backed_block_list *bbl, void *data,
- unsigned int len, unsigned int block);
-int backed_block_add_fill(struct backed_block_list *bbl, unsigned int fill_val,
- unsigned int len, unsigned int block);
-int backed_block_add_file(struct backed_block_list *bbl, const char *filename,
- int64_t offset, unsigned int len, unsigned int block);
-int backed_block_add_fd(struct backed_block_list *bbl, int fd,
- int64_t offset, unsigned int len, unsigned int block);
+int backed_block_add_data(struct backed_block_list* bbl, void* data, unsigned int len,
+ unsigned int block);
+int backed_block_add_fill(struct backed_block_list* bbl, unsigned int fill_val, unsigned int len,
+ unsigned int block);
+int backed_block_add_file(struct backed_block_list* bbl, const char* filename, int64_t offset,
+ unsigned int len, unsigned int block);
+int backed_block_add_fd(struct backed_block_list* bbl, int fd, int64_t offset, unsigned int len,
+ unsigned int block);
-struct backed_block *backed_block_iter_new(struct backed_block_list *bbl);
-struct backed_block *backed_block_iter_next(struct backed_block *bb);
-unsigned int backed_block_len(struct backed_block *bb);
-unsigned int backed_block_block(struct backed_block *bb);
-void *backed_block_data(struct backed_block *bb);
-const char *backed_block_filename(struct backed_block *bb);
-int backed_block_fd(struct backed_block *bb);
-int64_t backed_block_file_offset(struct backed_block *bb);
-uint32_t backed_block_fill_val(struct backed_block *bb);
-enum backed_block_type backed_block_type(struct backed_block *bb);
-int backed_block_split(struct backed_block_list *bbl, struct backed_block *bb,
- unsigned int max_len);
+struct backed_block* backed_block_iter_new(struct backed_block_list* bbl);
+struct backed_block* backed_block_iter_next(struct backed_block* bb);
+unsigned int backed_block_len(struct backed_block* bb);
+unsigned int backed_block_block(struct backed_block* bb);
+void* backed_block_data(struct backed_block* bb);
+const char* backed_block_filename(struct backed_block* bb);
+int backed_block_fd(struct backed_block* bb);
+int64_t backed_block_file_offset(struct backed_block* bb);
+uint32_t backed_block_fill_val(struct backed_block* bb);
+enum backed_block_type backed_block_type(struct backed_block* bb);
+int backed_block_split(struct backed_block_list* bbl, struct backed_block* bb, unsigned int max_len);
-struct backed_block *backed_block_iter_new(struct backed_block_list *bbl);
-struct backed_block *backed_block_iter_next(struct backed_block *bb);
+struct backed_block* backed_block_iter_new(struct backed_block_list* bbl);
+struct backed_block* backed_block_iter_next(struct backed_block* bb);
-struct backed_block_list *backed_block_list_new(unsigned int block_size);
-void backed_block_list_destroy(struct backed_block_list *bbl);
+struct backed_block_list* backed_block_list_new(unsigned int block_size);
+void backed_block_list_destroy(struct backed_block_list* bbl);
-void backed_block_list_move(struct backed_block_list *from,
- struct backed_block_list *to, struct backed_block *start,
- struct backed_block *end);
+void backed_block_list_move(struct backed_block_list* from, struct backed_block_list* to,
+ struct backed_block* start, struct backed_block* end);
#endif
diff --git a/libsparse/defs.h b/libsparse/defs.h
index 34e63c5..28e5cab 100644
--- a/libsparse/defs.h
+++ b/libsparse/defs.h
@@ -17,7 +17,7 @@
#ifndef _LIBSPARSE_DEFS_H_
#ifndef __unused
-#define __unused __attribute__((__unused__))
+#define __unused __attribute__((__unused__))
#endif
#endif /* _LIBSPARSE_DEFS_H_ */
diff --git a/libsparse/img2simg.c b/libsparse/img2simg.c
deleted file mode 100644
index a0db36f..0000000
--- a/libsparse/img2simg.c
+++ /dev/null
@@ -1,115 +0,0 @@
-/*
- * Copyright (C) 2012 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#define _FILE_OFFSET_BITS 64
-#define _LARGEFILE64_SOURCE 1
-
-#include <fcntl.h>
-#include <stdbool.h>
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-#include <sys/types.h>
-#include <sys/stat.h>
-#include <sys/types.h>
-#include <unistd.h>
-
-#include <sparse/sparse.h>
-
-#ifndef O_BINARY
-#define O_BINARY 0
-#endif
-
-#if defined(__APPLE__) && defined(__MACH__)
-#define lseek64 lseek
-#define off64_t off_t
-#endif
-
-void usage()
-{
- fprintf(stderr, "Usage: img2simg <raw_image_file> <sparse_image_file> [<block_size>]\n");
-}
-
-int main(int argc, char *argv[])
-{
- int in;
- int out;
- int ret;
- struct sparse_file *s;
- unsigned int block_size = 4096;
- off64_t len;
-
- if (argc < 3 || argc > 4) {
- usage();
- exit(-1);
- }
-
- if (argc == 4) {
- block_size = atoi(argv[3]);
- }
-
- if (block_size < 1024 || block_size % 4 != 0) {
- usage();
- exit(-1);
- }
-
- if (strcmp(argv[1], "-") == 0) {
- in = STDIN_FILENO;
- } else {
- in = open(argv[1], O_RDONLY | O_BINARY);
- if (in < 0) {
- fprintf(stderr, "Cannot open input file %s\n", argv[1]);
- exit(-1);
- }
- }
-
- if (strcmp(argv[2], "-") == 0) {
- out = STDOUT_FILENO;
- } else {
- out = open(argv[2], O_WRONLY | O_CREAT | O_TRUNC | O_BINARY, 0664);
- if (out < 0) {
- fprintf(stderr, "Cannot open output file %s\n", argv[2]);
- exit(-1);
- }
- }
-
- len = lseek64(in, 0, SEEK_END);
- lseek64(in, 0, SEEK_SET);
-
- s = sparse_file_new(block_size, len);
- if (!s) {
- fprintf(stderr, "Failed to create sparse file\n");
- exit(-1);
- }
-
- sparse_file_verbose(s);
- ret = sparse_file_read(s, in, false, false);
- if (ret) {
- fprintf(stderr, "Failed to read file\n");
- exit(-1);
- }
-
- ret = sparse_file_write(s, out, false, true, false);
- if (ret) {
- fprintf(stderr, "Failed to write sparse file\n");
- exit(-1);
- }
-
- close(in);
- close(out);
-
- exit(0);
-}
diff --git a/libsparse/img2simg.cpp b/libsparse/img2simg.cpp
new file mode 100644
index 0000000..4c2c6ca
--- /dev/null
+++ b/libsparse/img2simg.cpp
@@ -0,0 +1,112 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#define _FILE_OFFSET_BITS 64
+#define _LARGEFILE64_SOURCE 1
+
+#include <fcntl.h>
+#include <stdbool.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <unistd.h>
+
+#include <sparse/sparse.h>
+
+#ifndef O_BINARY
+#define O_BINARY 0
+#endif
+
+#if defined(__APPLE__) && defined(__MACH__)
+#define lseek64 lseek
+#define off64_t off_t
+#endif
+
+void usage() {
+ fprintf(stderr, "Usage: img2simg <raw_image_file> <sparse_image_file> [<block_size>]\n");
+}
+
+int main(int argc, char* argv[]) {
+ int in;
+ int out;
+ int ret;
+ struct sparse_file* s;
+ unsigned int block_size = 4096;
+ off64_t len;
+
+ if (argc < 3 || argc > 4) {
+ usage();
+ exit(-1);
+ }
+
+ if (argc == 4) {
+ block_size = atoi(argv[3]);
+ }
+
+ if (block_size < 1024 || block_size % 4 != 0) {
+ usage();
+ exit(-1);
+ }
+
+ if (strcmp(argv[1], "-") == 0) {
+ in = STDIN_FILENO;
+ } else {
+ in = open(argv[1], O_RDONLY | O_BINARY);
+ if (in < 0) {
+ fprintf(stderr, "Cannot open input file %s\n", argv[1]);
+ exit(-1);
+ }
+ }
+
+ if (strcmp(argv[2], "-") == 0) {
+ out = STDOUT_FILENO;
+ } else {
+ out = open(argv[2], O_WRONLY | O_CREAT | O_TRUNC | O_BINARY, 0664);
+ if (out < 0) {
+ fprintf(stderr, "Cannot open output file %s\n", argv[2]);
+ exit(-1);
+ }
+ }
+
+ len = lseek64(in, 0, SEEK_END);
+ lseek64(in, 0, SEEK_SET);
+
+ s = sparse_file_new(block_size, len);
+ if (!s) {
+ fprintf(stderr, "Failed to create sparse file\n");
+ exit(-1);
+ }
+
+ sparse_file_verbose(s);
+ ret = sparse_file_read(s, in, false, false);
+ if (ret) {
+ fprintf(stderr, "Failed to read file\n");
+ exit(-1);
+ }
+
+ ret = sparse_file_write(s, out, false, true, false);
+ if (ret) {
+ fprintf(stderr, "Failed to write sparse file\n");
+ exit(-1);
+ }
+
+ close(in);
+ close(out);
+
+ exit(0);
+}
diff --git a/libsparse/include/sparse/sparse.h b/libsparse/include/sparse/sparse.h
index 1b91ead..3d5fb0c 100644
--- a/libsparse/include/sparse/sparse.h
+++ b/libsparse/include/sparse/sparse.h
@@ -246,9 +246,24 @@
int sparse_file_read(struct sparse_file *s, int fd, bool sparse, bool crc);
/**
- * sparse_file_import - import an existing sparse file
+ * sparse_file_read_buf - read a buffer into a sparse file cookie
*
* @s - sparse file cookie
+ * @buf - buffer to read from
+ * @crc - verify the crc of a file in the Android sparse file format
+ *
+ * Reads a buffer into a sparse file cookie. The buffer must remain
+ * valid until the sparse file cookie is freed. 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_buf(struct sparse_file *s, char *buf, bool crc);
+
+/**
+ * sparse_file_import - import an existing sparse file
+ *
+ * @fd - file descriptor to read from
* @verbose - print verbose errors while reading the sparse file
* @crc - verify the crc of a file in the Android sparse file format
*
@@ -261,6 +276,21 @@
struct sparse_file *sparse_file_import(int fd, bool verbose, bool crc);
/**
+ * sparse_file_import_buf - import an existing sparse file from a buffer
+ *
+ * @buf - buffer to read from
+ * @verbose - print verbose errors while reading the sparse file
+ * @crc - verify the crc of a file in the Android sparse file format
+ *
+ * Reads existing sparse file data into a sparse file cookie, recreating the same
+ * sparse cookie that was used to write it. If verbose is true, prints verbose
+ * errors when the sparse file is formatted incorrectly.
+ *
+ * Returns a new sparse file cookie on success, NULL on error.
+ */
+struct sparse_file *sparse_file_import_buf(char* buf, bool verbose, bool crc);
+
+/**
* sparse_file_import_auto - import an existing sparse or normal file
*
* @fd - file descriptor to read from
diff --git a/libsparse/output_file.c b/libsparse/output_file.c
deleted file mode 100644
index 002ad27..0000000
--- a/libsparse/output_file.c
+++ /dev/null
@@ -1,776 +0,0 @@
-/*
- * Copyright (C) 2010 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#define _FILE_OFFSET_BITS 64
-#define _LARGEFILE64_SOURCE 1
-
-#include <fcntl.h>
-#include <inttypes.h>
-#include <limits.h>
-#include <stdbool.h>
-#include <stddef.h>
-#include <stdlib.h>
-#include <string.h>
-#include <sys/stat.h>
-#include <sys/types.h>
-#include <unistd.h>
-#include <zlib.h>
-
-#include "defs.h"
-#include "output_file.h"
-#include "sparse_crc32.h"
-#include "sparse_format.h"
-
-#ifndef _WIN32
-#include <sys/mman.h>
-#define O_BINARY 0
-#else
-#define ftruncate64 ftruncate
-#endif
-
-#if defined(__APPLE__) && defined(__MACH__)
-#define lseek64 lseek
-#define ftruncate64 ftruncate
-#define mmap64 mmap
-#define off64_t off_t
-#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))
-
-#define container_of(inner, outer_t, elem) \
- ((outer_t *)((char *)(inner) - offsetof(outer_t, elem)))
-
-struct output_file_ops {
- int (*open)(struct output_file *, int fd);
- int (*skip)(struct output_file *, int64_t);
- int (*pad)(struct output_file *, int64_t);
- int (*write)(struct output_file *, void *, size_t);
- 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 {
- int64_t cur_out_ptr;
- 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;
- char *buf;
-};
-
-struct output_file_gz {
- struct output_file out;
- gzFile gz_fd;
-};
-
-#define to_output_file_gz(_o) \
- container_of((_o), struct output_file_gz, out)
-
-struct output_file_normal {
- struct output_file out;
- int fd;
-};
-
-#define to_output_file_normal(_o) \
- container_of((_o), struct output_file_normal, out)
-
-struct output_file_callback {
- struct output_file out;
- void *priv;
- int (*write)(void *priv, const void *buf, size_t len);
-};
-
-#define to_output_file_callback(_o) \
- container_of((_o), struct output_file_callback, out)
-
-static int file_open(struct output_file *out, int fd)
-{
- struct output_file_normal *outn = to_output_file_normal(out);
-
- outn->fd = fd;
- return 0;
-}
-
-static int file_skip(struct output_file *out, int64_t cnt)
-{
- off64_t ret;
- struct output_file_normal *outn = to_output_file_normal(out);
-
- ret = lseek64(outn->fd, cnt, SEEK_CUR);
- if (ret < 0) {
- error_errno("lseek64");
- return -1;
- }
- return 0;
-}
-
-static int file_pad(struct output_file *out, int64_t len)
-{
- int ret;
- struct output_file_normal *outn = to_output_file_normal(out);
-
- ret = ftruncate64(outn->fd, len);
- if (ret < 0) {
- return -errno;
- }
-
- return 0;
-}
-
-static int file_write(struct output_file *out, void *data, size_t len)
-{
- ssize_t ret;
- struct output_file_normal *outn = to_output_file_normal(out);
-
- while (len > 0) {
- ret = write(outn->fd, data, len);
- if (ret < 0) {
- if (errno == EINTR) {
- continue;
- }
- error_errno("write");
- return -1;
- }
-
- data = (char *)data + ret;
- len -= ret;
- }
-
- return 0;
-}
-
-static void file_close(struct output_file *out)
-{
- struct output_file_normal *outn = to_output_file_normal(out);
-
- free(outn);
-}
-
-static struct output_file_ops file_ops = {
- .open = file_open,
- .skip = file_skip,
- .pad = file_pad,
- .write = file_write,
- .close = file_close,
-};
-
-static int gz_file_open(struct output_file *out, int fd)
-{
- struct output_file_gz *outgz = to_output_file_gz(out);
-
- outgz->gz_fd = gzdopen(fd, "wb9");
- if (!outgz->gz_fd) {
- error_errno("gzopen");
- return -errno;
- }
-
- return 0;
-}
-
-
-static int gz_file_skip(struct output_file *out, int64_t cnt)
-{
- off64_t ret;
- struct output_file_gz *outgz = to_output_file_gz(out);
-
- ret = gzseek(outgz->gz_fd, cnt, SEEK_CUR);
- if (ret < 0) {
- error_errno("gzseek");
- return -1;
- }
- return 0;
-}
-
-static int gz_file_pad(struct output_file *out, int64_t len)
-{
- off64_t ret;
- struct output_file_gz *outgz = to_output_file_gz(out);
-
- ret = gztell(outgz->gz_fd);
- if (ret < 0) {
- return -1;
- }
-
- if (ret >= len) {
- return 0;
- }
-
- ret = gzseek(outgz->gz_fd, len - 1, SEEK_SET);
- if (ret < 0) {
- return -1;
- }
-
- gzwrite(outgz->gz_fd, "", 1);
-
- return 0;
-}
-
-static int gz_file_write(struct output_file *out, void *data, size_t len)
-{
- int ret;
- struct output_file_gz *outgz = to_output_file_gz(out);
-
- while (len > 0) {
- ret = gzwrite(outgz->gz_fd, data,
- min(len, (unsigned int)INT_MAX));
- if (ret == 0) {
- error("gzwrite %s", gzerror(outgz->gz_fd, NULL));
- return -1;
- }
- len -= ret;
- data = (char *)data + ret;
- }
-
- return 0;
-}
-
-static void gz_file_close(struct output_file *out)
-{
- struct output_file_gz *outgz = to_output_file_gz(out);
-
- gzclose(outgz->gz_fd);
- free(outgz);
-}
-
-static struct output_file_ops gz_file_ops = {
- .open = gz_file_open,
- .skip = gz_file_skip,
- .pad = gz_file_pad,
- .write = gz_file_write,
- .close = gz_file_close,
-};
-
-static int callback_file_open(struct output_file *out __unused, int fd __unused)
-{
- return 0;
-}
-
-static int callback_file_skip(struct output_file *out, int64_t off)
-{
- struct output_file_callback *outc = to_output_file_callback(out);
- int to_write;
- int ret;
-
- while (off > 0) {
- to_write = min(off, (int64_t)INT_MAX);
- ret = outc->write(outc->priv, NULL, to_write);
- if (ret < 0) {
- return ret;
- }
- off -= to_write;
- }
-
- return 0;
-}
-
-static int callback_file_pad(struct output_file *out __unused, int64_t len __unused)
-{
- return -1;
-}
-
-static int callback_file_write(struct output_file *out, void *data, size_t len)
-{
- struct output_file_callback *outc = to_output_file_callback(out);
-
- return outc->write(outc->priv, data, len);
-}
-
-static void callback_file_close(struct output_file *out)
-{
- struct output_file_callback *outc = to_output_file_callback(out);
-
- free(outc);
-}
-
-static struct output_file_ops callback_file_ops = {
- .open = callback_file_open,
- .skip = callback_file_skip,
- .pad = callback_file_pad,
- .write = callback_file_write,
- .close = callback_file_close,
-};
-
-int read_all(int fd, void *buf, size_t len)
-{
- size_t total = 0;
- int ret;
- char *ptr = buf;
-
- while (total < len) {
- ret = read(fd, ptr, len - total);
-
- if (ret < 0)
- return -errno;
-
- if (ret == 0)
- return -EINVAL;
-
- ptr += ret;
- total += ret;
- }
-
- return 0;
-}
-
-static int write_sparse_skip_chunk(struct output_file *out, int64_t skip_len)
-{
- chunk_header_t chunk_header;
- int ret;
-
- if (skip_len % out->block_size) {
- error("don't care size %"PRIi64" is not a multiple of the block size %u",
- skip_len, out->block_size);
- return -1;
- }
-
- /* We are skipping data, so emit a don't care chunk. */
- chunk_header.chunk_type = CHUNK_TYPE_DONT_CARE;
- 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, &chunk_header, sizeof(chunk_header));
- if (ret < 0)
- return -1;
-
- out->cur_out_ptr += skip_len;
- out->chunk_cnt++;
-
- return 0;
-}
-
-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, count;
- int ret;
-
- /* 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, &chunk_header, sizeof(chunk_header));
-
- if (ret < 0)
- return -1;
- ret = out->ops->write(out, &fill_val, sizeof(fill_val));
- if (ret < 0)
- return -1;
-
- if (out->use_crc) {
- 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;
- out->chunk_cnt++;
-
- return 0;
-}
-
-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;
-
- /* 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 */
- chunk_header.chunk_type = CHUNK_TYPE_RAW;
- 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, &chunk_header, sizeof(chunk_header));
-
- if (ret < 0)
- return -1;
- ret = out->ops->write(out, data, len);
- if (ret < 0)
- return -1;
- if (zero_len) {
- ret = out->ops->write(out, out->zero_buf, zero_len);
- if (ret < 0)
- return -1;
- }
-
- if (out->use_crc) {
- out->crc32 = sparse_crc32(out->crc32, data, len);
- if (zero_len)
- out->crc32 = sparse_crc32(out->crc32, out->zero_buf, zero_len);
- }
-
- out->cur_out_ptr += rnd_up_len;
- out->chunk_cnt++;
-
- 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)
-{
- return out->ops->skip(out, len);
-}
-
-int write_normal_end_chunk(struct output_file *out)
-{
- return out->ops->pad(out, out->len);
-}
-
-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 output_file_close(struct output_file *out)
-{
- out->sparse_ops->write_end_chunk(out);
- out->ops->close(out);
-}
-
-static int output_file_init(struct output_file *out, int block_size,
- int64_t len, bool sparse, int chunks, bool crc)
-{
- int ret;
-
- out->len = len;
- out->block_size = block_size;
- out->cur_out_ptr = 0ll;
- out->chunk_cnt = 0;
- out->crc32 = 0;
- out->use_crc = crc;
-
- out->zero_buf = calloc(block_size, 1);
- if (!out->zero_buf) {
- error_errno("malloc zero_buf");
- return -ENOMEM;
- }
-
- out->fill_buf = calloc(block_size, 1);
- if (!out->fill_buf) {
- error_errno("malloc fill_buf");
- ret = -ENOMEM;
- goto err_fill_buf;
- }
-
- if (sparse) {
- out->sparse_ops = &sparse_file_ops;
- } else {
- out->sparse_ops = &normal_file_ops;
- }
-
- 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 = DIV_ROUND_UP(out->len, out->block_size),
- .total_chunks = chunks,
- .image_checksum = 0
- };
-
- 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 0;
-
-err_write:
- free(out->fill_buf);
-err_fill_buf:
- free(out->zero_buf);
- return ret;
-}
-
-static struct output_file *output_file_new_gz(void)
-{
- struct output_file_gz *outgz = calloc(1, sizeof(struct output_file_gz));
- if (!outgz) {
- error_errno("malloc struct outgz");
- return NULL;
- }
-
- outgz->out.ops = &gz_file_ops;
-
- return &outgz->out;
-}
-
-static struct output_file *output_file_new_normal(void)
-{
- struct output_file_normal *outn = calloc(1, sizeof(struct output_file_normal));
- if (!outn) {
- error_errno("malloc struct outn");
- return NULL;
- }
-
- outn->out.ops = &file_ops;
-
- return &outn->out;
-}
-
-struct output_file *output_file_open_callback(
- int (*write)(void *, const void *, size_t),
- void *priv, unsigned int block_size, int64_t len,
- int gz __unused, int sparse, int chunks, int crc)
-{
- int ret;
- struct output_file_callback *outc;
-
- outc = calloc(1, sizeof(struct output_file_callback));
- if (!outc) {
- error_errno("malloc struct outc");
- return NULL;
- }
-
- outc->out.ops = &callback_file_ops;
- outc->priv = priv;
- outc->write = write;
-
- ret = output_file_init(&outc->out, block_size, len, sparse, chunks, crc);
- if (ret < 0) {
- free(outc);
- return NULL;
- }
-
- return &outc->out;
-}
-
-struct output_file *output_file_open_fd(int fd, unsigned int block_size, int64_t len,
- int gz, int sparse, int chunks, int crc)
-{
- int ret;
- struct output_file *out;
-
- if (gz) {
- out = output_file_new_gz();
- } else {
- out = output_file_new_normal();
- }
- if (!out) {
- return NULL;
- }
-
- out->ops->open(out, fd);
-
- ret = output_file_init(out, block_size, len, sparse, chunks, crc);
- if (ret < 0) {
- free(out);
- return NULL;
- }
-
- return out;
-}
-
-/* Write a contiguous region of data blocks from a memory buffer */
-int write_data_chunk(struct output_file *out, unsigned int len, void *data)
-{
- return out->sparse_ops->write_data_chunk(out, len, data);
-}
-
-/* Write a contiguous region of data blocks with a fill value */
-int write_fill_chunk(struct output_file *out, unsigned int len,
- uint32_t fill_val)
-{
- return out->sparse_ops->write_fill_chunk(out, len, fill_val);
-}
-
-int write_fd_chunk(struct output_file *out, unsigned int len,
- int fd, int64_t offset)
-{
- int ret;
- int64_t aligned_offset;
- int aligned_diff;
- uint64_t buffer_size;
- char *ptr;
-
- aligned_offset = offset & ~(4096 - 1);
- aligned_diff = offset - aligned_offset;
- buffer_size = (uint64_t)len + (uint64_t)aligned_diff;
-
-#ifndef _WIN32
- if (buffer_size > SIZE_MAX)
- return -E2BIG;
- char *data = mmap64(NULL, buffer_size, PROT_READ, MAP_SHARED, fd,
- aligned_offset);
- if (data == MAP_FAILED) {
- return -errno;
- }
- ptr = data + aligned_diff;
-#else
- off64_t pos;
- char *data = malloc(len);
- if (!data) {
- return -errno;
- }
- pos = lseek64(fd, offset, SEEK_SET);
- if (pos < 0) {
- free(data);
- return -errno;
- }
- ret = read_all(fd, data, len);
- if (ret < 0) {
- free(data);
- return ret;
- }
- ptr = data;
-#endif
-
- ret = out->sparse_ops->write_data_chunk(out, len, ptr);
-
-#ifndef _WIN32
- munmap(data, buffer_size);
-#else
- free(data);
-#endif
-
- return ret;
-}
-
-/* Write a contiguous region of data blocks from a file */
-int write_file_chunk(struct output_file *out, unsigned int len,
- const char *file, int64_t offset)
-{
- int ret;
-
- int file_fd = open(file, O_RDONLY | O_BINARY);
- if (file_fd < 0) {
- return -errno;
- }
-
- ret = write_fd_chunk(out, len, file_fd, offset);
-
- 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);
-}
diff --git a/libsparse/output_file.cpp b/libsparse/output_file.cpp
new file mode 100644
index 0000000..5388e77
--- /dev/null
+++ b/libsparse/output_file.cpp
@@ -0,0 +1,720 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#define _FILE_OFFSET_BITS 64
+#define _LARGEFILE64_SOURCE 1
+
+#include <fcntl.h>
+#include <inttypes.h>
+#include <limits.h>
+#include <stdbool.h>
+#include <stddef.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <unistd.h>
+#include <zlib.h>
+
+#include "defs.h"
+#include "output_file.h"
+#include "sparse_crc32.h"
+#include "sparse_format.h"
+
+#ifndef _WIN32
+#include <sys/mman.h>
+#define O_BINARY 0
+#else
+#define ftruncate64 ftruncate
+#endif
+
+#if defined(__APPLE__) && defined(__MACH__)
+#define lseek64 lseek
+#define ftruncate64 ftruncate
+#define mmap64 mmap
+#define off64_t off_t
+#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))
+
+#define container_of(inner, outer_t, elem) ((outer_t*)((char*)(inner)-offsetof(outer_t, elem)))
+
+struct output_file_ops {
+ int (*open)(struct output_file*, int fd);
+ int (*skip)(struct output_file*, int64_t);
+ int (*pad)(struct output_file*, int64_t);
+ int (*write)(struct output_file*, void*, size_t);
+ 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 {
+ int64_t cur_out_ptr;
+ 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;
+ char* buf;
+};
+
+struct output_file_gz {
+ struct output_file out;
+ gzFile gz_fd;
+};
+
+#define to_output_file_gz(_o) container_of((_o), struct output_file_gz, out)
+
+struct output_file_normal {
+ struct output_file out;
+ int fd;
+};
+
+#define to_output_file_normal(_o) container_of((_o), struct output_file_normal, out)
+
+struct output_file_callback {
+ struct output_file out;
+ void* priv;
+ int (*write)(void* priv, const void* buf, size_t len);
+};
+
+#define to_output_file_callback(_o) container_of((_o), struct output_file_callback, out)
+
+static int file_open(struct output_file* out, int fd) {
+ struct output_file_normal* outn = to_output_file_normal(out);
+
+ outn->fd = fd;
+ return 0;
+}
+
+static int file_skip(struct output_file* out, int64_t cnt) {
+ off64_t ret;
+ struct output_file_normal* outn = to_output_file_normal(out);
+
+ ret = lseek64(outn->fd, cnt, SEEK_CUR);
+ if (ret < 0) {
+ error_errno("lseek64");
+ return -1;
+ }
+ return 0;
+}
+
+static int file_pad(struct output_file* out, int64_t len) {
+ int ret;
+ struct output_file_normal* outn = to_output_file_normal(out);
+
+ ret = ftruncate64(outn->fd, len);
+ if (ret < 0) {
+ return -errno;
+ }
+
+ return 0;
+}
+
+static int file_write(struct output_file* out, void* data, size_t len) {
+ ssize_t ret;
+ struct output_file_normal* outn = to_output_file_normal(out);
+
+ while (len > 0) {
+ ret = write(outn->fd, data, len);
+ if (ret < 0) {
+ if (errno == EINTR) {
+ continue;
+ }
+ error_errno("write");
+ return -1;
+ }
+
+ data = (char*)data + ret;
+ len -= ret;
+ }
+
+ return 0;
+}
+
+static void file_close(struct output_file* out) {
+ struct output_file_normal* outn = to_output_file_normal(out);
+
+ free(outn);
+}
+
+static struct output_file_ops file_ops = {
+ .open = file_open,
+ .skip = file_skip,
+ .pad = file_pad,
+ .write = file_write,
+ .close = file_close,
+};
+
+static int gz_file_open(struct output_file* out, int fd) {
+ struct output_file_gz* outgz = to_output_file_gz(out);
+
+ outgz->gz_fd = gzdopen(fd, "wb9");
+ if (!outgz->gz_fd) {
+ error_errno("gzopen");
+ return -errno;
+ }
+
+ return 0;
+}
+
+static int gz_file_skip(struct output_file* out, int64_t cnt) {
+ off64_t ret;
+ struct output_file_gz* outgz = to_output_file_gz(out);
+
+ ret = gzseek(outgz->gz_fd, cnt, SEEK_CUR);
+ if (ret < 0) {
+ error_errno("gzseek");
+ return -1;
+ }
+ return 0;
+}
+
+static int gz_file_pad(struct output_file* out, int64_t len) {
+ off64_t ret;
+ struct output_file_gz* outgz = to_output_file_gz(out);
+
+ ret = gztell(outgz->gz_fd);
+ if (ret < 0) {
+ return -1;
+ }
+
+ if (ret >= len) {
+ return 0;
+ }
+
+ ret = gzseek(outgz->gz_fd, len - 1, SEEK_SET);
+ if (ret < 0) {
+ return -1;
+ }
+
+ gzwrite(outgz->gz_fd, "", 1);
+
+ return 0;
+}
+
+static int gz_file_write(struct output_file* out, void* data, size_t len) {
+ int ret;
+ struct output_file_gz* outgz = to_output_file_gz(out);
+
+ while (len > 0) {
+ ret = gzwrite(outgz->gz_fd, data, min(len, (unsigned int)INT_MAX));
+ if (ret == 0) {
+ error("gzwrite %s", gzerror(outgz->gz_fd, NULL));
+ return -1;
+ }
+ len -= ret;
+ data = (char*)data + ret;
+ }
+
+ return 0;
+}
+
+static void gz_file_close(struct output_file* out) {
+ struct output_file_gz* outgz = to_output_file_gz(out);
+
+ gzclose(outgz->gz_fd);
+ free(outgz);
+}
+
+static struct output_file_ops gz_file_ops = {
+ .open = gz_file_open,
+ .skip = gz_file_skip,
+ .pad = gz_file_pad,
+ .write = gz_file_write,
+ .close = gz_file_close,
+};
+
+static int callback_file_open(struct output_file* out __unused, int fd __unused) {
+ return 0;
+}
+
+static int callback_file_skip(struct output_file* out, int64_t off) {
+ struct output_file_callback* outc = to_output_file_callback(out);
+ int to_write;
+ int ret;
+
+ while (off > 0) {
+ to_write = min(off, (int64_t)INT_MAX);
+ ret = outc->write(outc->priv, NULL, to_write);
+ if (ret < 0) {
+ return ret;
+ }
+ off -= to_write;
+ }
+
+ return 0;
+}
+
+static int callback_file_pad(struct output_file* out __unused, int64_t len __unused) {
+ return -1;
+}
+
+static int callback_file_write(struct output_file* out, void* data, size_t len) {
+ struct output_file_callback* outc = to_output_file_callback(out);
+
+ return outc->write(outc->priv, data, len);
+}
+
+static void callback_file_close(struct output_file* out) {
+ struct output_file_callback* outc = to_output_file_callback(out);
+
+ free(outc);
+}
+
+static struct output_file_ops callback_file_ops = {
+ .open = callback_file_open,
+ .skip = callback_file_skip,
+ .pad = callback_file_pad,
+ .write = callback_file_write,
+ .close = callback_file_close,
+};
+
+int read_all(int fd, void* buf, size_t len) {
+ size_t total = 0;
+ int ret;
+ char* ptr = reinterpret_cast<char*>(buf);
+
+ while (total < len) {
+ ret = read(fd, ptr, len - total);
+
+ if (ret < 0) return -errno;
+
+ if (ret == 0) return -EINVAL;
+
+ ptr += ret;
+ total += ret;
+ }
+
+ return 0;
+}
+
+static int write_sparse_skip_chunk(struct output_file* out, int64_t skip_len) {
+ chunk_header_t chunk_header;
+ int ret;
+
+ if (skip_len % out->block_size) {
+ error("don't care size %" PRIi64 " is not a multiple of the block size %u", skip_len,
+ out->block_size);
+ return -1;
+ }
+
+ /* We are skipping data, so emit a don't care chunk. */
+ chunk_header.chunk_type = CHUNK_TYPE_DONT_CARE;
+ 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, &chunk_header, sizeof(chunk_header));
+ if (ret < 0) return -1;
+
+ out->cur_out_ptr += skip_len;
+ out->chunk_cnt++;
+
+ return 0;
+}
+
+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, count;
+ int ret;
+
+ /* 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, &chunk_header, sizeof(chunk_header));
+
+ if (ret < 0) return -1;
+ ret = out->ops->write(out, &fill_val, sizeof(fill_val));
+ if (ret < 0) return -1;
+
+ if (out->use_crc) {
+ 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;
+ out->chunk_cnt++;
+
+ return 0;
+}
+
+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;
+
+ /* 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 */
+ chunk_header.chunk_type = CHUNK_TYPE_RAW;
+ 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, &chunk_header, sizeof(chunk_header));
+
+ if (ret < 0) return -1;
+ ret = out->ops->write(out, data, len);
+ if (ret < 0) return -1;
+ if (zero_len) {
+ ret = out->ops->write(out, out->zero_buf, zero_len);
+ if (ret < 0) return -1;
+ }
+
+ if (out->use_crc) {
+ out->crc32 = sparse_crc32(out->crc32, data, len);
+ if (zero_len) out->crc32 = sparse_crc32(out->crc32, out->zero_buf, zero_len);
+ }
+
+ out->cur_out_ptr += rnd_up_len;
+ out->chunk_cnt++;
+
+ 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) {
+ return out->ops->skip(out, len);
+}
+
+int write_normal_end_chunk(struct output_file* out) {
+ return out->ops->pad(out, out->len);
+}
+
+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 output_file_close(struct output_file* out) {
+ out->sparse_ops->write_end_chunk(out);
+ out->ops->close(out);
+}
+
+static int output_file_init(struct output_file* out, int block_size, int64_t len, bool sparse,
+ int chunks, bool crc) {
+ int ret;
+
+ out->len = len;
+ out->block_size = block_size;
+ out->cur_out_ptr = 0ll;
+ out->chunk_cnt = 0;
+ out->crc32 = 0;
+ out->use_crc = crc;
+
+ out->zero_buf = reinterpret_cast<char*>(calloc(block_size, 1));
+ if (!out->zero_buf) {
+ error_errno("malloc zero_buf");
+ return -ENOMEM;
+ }
+
+ out->fill_buf = reinterpret_cast<uint32_t*>(calloc(block_size, 1));
+ if (!out->fill_buf) {
+ error_errno("malloc fill_buf");
+ ret = -ENOMEM;
+ goto err_fill_buf;
+ }
+
+ if (sparse) {
+ out->sparse_ops = &sparse_file_ops;
+ } else {
+ out->sparse_ops = &normal_file_ops;
+ }
+
+ 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 = static_cast<unsigned>(DIV_ROUND_UP(out->len, out->block_size)),
+ .total_chunks = static_cast<unsigned>(chunks),
+ .image_checksum = 0};
+
+ 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 0;
+
+err_write:
+ free(out->fill_buf);
+err_fill_buf:
+ free(out->zero_buf);
+ return ret;
+}
+
+static struct output_file* output_file_new_gz(void) {
+ struct output_file_gz* outgz =
+ reinterpret_cast<struct output_file_gz*>(calloc(1, sizeof(struct output_file_gz)));
+ if (!outgz) {
+ error_errno("malloc struct outgz");
+ return NULL;
+ }
+
+ outgz->out.ops = &gz_file_ops;
+
+ return &outgz->out;
+}
+
+static struct output_file* output_file_new_normal(void) {
+ struct output_file_normal* outn =
+ reinterpret_cast<struct output_file_normal*>(calloc(1, sizeof(struct output_file_normal)));
+ if (!outn) {
+ error_errno("malloc struct outn");
+ return NULL;
+ }
+
+ outn->out.ops = &file_ops;
+
+ return &outn->out;
+}
+
+struct output_file* output_file_open_callback(int (*write)(void*, const void*, size_t), void* priv,
+ unsigned int block_size, int64_t len, int gz __unused,
+ int sparse, int chunks, int crc) {
+ int ret;
+ struct output_file_callback* outc;
+
+ outc =
+ reinterpret_cast<struct output_file_callback*>(calloc(1, sizeof(struct output_file_callback)));
+ if (!outc) {
+ error_errno("malloc struct outc");
+ return NULL;
+ }
+
+ outc->out.ops = &callback_file_ops;
+ outc->priv = priv;
+ outc->write = write;
+
+ ret = output_file_init(&outc->out, block_size, len, sparse, chunks, crc);
+ if (ret < 0) {
+ free(outc);
+ return NULL;
+ }
+
+ return &outc->out;
+}
+
+struct output_file* output_file_open_fd(int fd, unsigned int block_size, int64_t len, int gz,
+ int sparse, int chunks, int crc) {
+ int ret;
+ struct output_file* out;
+
+ if (gz) {
+ out = output_file_new_gz();
+ } else {
+ out = output_file_new_normal();
+ }
+ if (!out) {
+ return NULL;
+ }
+
+ out->ops->open(out, fd);
+
+ ret = output_file_init(out, block_size, len, sparse, chunks, crc);
+ if (ret < 0) {
+ free(out);
+ return NULL;
+ }
+
+ return out;
+}
+
+/* Write a contiguous region of data blocks from a memory buffer */
+int write_data_chunk(struct output_file* out, unsigned int len, void* data) {
+ return out->sparse_ops->write_data_chunk(out, len, data);
+}
+
+/* Write a contiguous region of data blocks with a fill value */
+int write_fill_chunk(struct output_file* out, unsigned int len, uint32_t fill_val) {
+ return out->sparse_ops->write_fill_chunk(out, len, fill_val);
+}
+
+int write_fd_chunk(struct output_file* out, unsigned int len, int fd, int64_t offset) {
+ int ret;
+ int64_t aligned_offset;
+ int aligned_diff;
+ uint64_t buffer_size;
+ char* ptr;
+
+ aligned_offset = offset & ~(4096 - 1);
+ aligned_diff = offset - aligned_offset;
+ buffer_size = (uint64_t)len + (uint64_t)aligned_diff;
+
+#ifndef _WIN32
+ if (buffer_size > SIZE_MAX) return -E2BIG;
+ char* data =
+ reinterpret_cast<char*>(mmap64(NULL, buffer_size, PROT_READ, MAP_SHARED, fd, aligned_offset));
+ if (data == MAP_FAILED) {
+ return -errno;
+ }
+ ptr = data + aligned_diff;
+#else
+ off64_t pos;
+ char* data = reinterpret_cast<char*>(malloc(len));
+ if (!data) {
+ return -errno;
+ }
+ pos = lseek64(fd, offset, SEEK_SET);
+ if (pos < 0) {
+ free(data);
+ return -errno;
+ }
+ ret = read_all(fd, data, len);
+ if (ret < 0) {
+ free(data);
+ return ret;
+ }
+ ptr = data;
+#endif
+
+ ret = out->sparse_ops->write_data_chunk(out, len, ptr);
+
+#ifndef _WIN32
+ munmap(data, buffer_size);
+#else
+ free(data);
+#endif
+
+ return ret;
+}
+
+/* Write a contiguous region of data blocks from a file */
+int write_file_chunk(struct output_file* out, unsigned int len, const char* file, int64_t offset) {
+ int ret;
+
+ int file_fd = open(file, O_RDONLY | O_BINARY);
+ if (file_fd < 0) {
+ return -errno;
+ }
+
+ ret = write_fd_chunk(out, len, file_fd, offset);
+
+ 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);
+}
diff --git a/libsparse/output_file.h b/libsparse/output_file.h
index 690f610..278430b 100644
--- a/libsparse/output_file.h
+++ b/libsparse/output_file.h
@@ -25,23 +25,19 @@
struct output_file;
-struct output_file *output_file_open_fd(int fd, unsigned int block_size, int64_t len,
- int gz, int sparse, int chunks, int crc);
-struct output_file *output_file_open_callback(
- int (*write)(void *, const void *, size_t),
- void *priv, unsigned int block_size, int64_t len, int gz, int sparse,
- int chunks, int crc);
-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_file_chunk(struct output_file *out, unsigned int len,
- const char *file, int64_t offset);
-int write_fd_chunk(struct output_file *out, unsigned int len,
- int fd, int64_t offset);
-int write_skip_chunk(struct output_file *out, int64_t len);
-void output_file_close(struct output_file *out);
+struct output_file* output_file_open_fd(int fd, unsigned int block_size, int64_t len, int gz,
+ int sparse, int chunks, int crc);
+struct output_file* output_file_open_callback(int (*write)(void*, const void*, size_t), void* priv,
+ unsigned int block_size, int64_t len, int gz,
+ int sparse, int chunks, int crc);
+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_file_chunk(struct output_file* out, unsigned int len, const char* file, int64_t offset);
+int write_fd_chunk(struct output_file* out, unsigned int len, int fd, int64_t offset);
+int write_skip_chunk(struct output_file* out, int64_t len);
+void output_file_close(struct output_file* out);
-int read_all(int fd, void *buf, size_t len);
+int read_all(int fd, void* buf, size_t len);
#ifdef __cplusplus
}
diff --git a/libsparse/simg2img.c b/libsparse/simg2img.c
deleted file mode 100644
index b9b438e..0000000
--- a/libsparse/simg2img.c
+++ /dev/null
@@ -1,90 +0,0 @@
-/*
- * Copyright (C) 2010 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include <sparse/sparse.h>
-
-#include <fcntl.h>
-#include <stdbool.h>
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-#include <sys/types.h>
-#include <sys/stat.h>
-#include <sys/types.h>
-#include <unistd.h>
-
-#ifndef O_BINARY
-#define O_BINARY 0
-#endif
-
-void usage()
-{
- fprintf(stderr, "Usage: simg2img <sparse_image_files> <raw_image_file>\n");
-}
-
-int main(int argc, char *argv[])
-{
- int in;
- int out;
- int i;
- struct sparse_file *s;
-
- if (argc < 3) {
- usage();
- exit(-1);
- }
-
- out = open(argv[argc - 1], O_WRONLY | O_CREAT | O_TRUNC | O_BINARY, 0664);
- if (out < 0) {
- fprintf(stderr, "Cannot open output file %s\n", argv[argc - 1]);
- exit(-1);
- }
-
- for (i = 1; i < argc - 1; i++) {
- if (strcmp(argv[i], "-") == 0) {
- in = STDIN_FILENO;
- } else {
- in = open(argv[i], O_RDONLY | O_BINARY);
- if (in < 0) {
- fprintf(stderr, "Cannot open input file %s\n", argv[i]);
- exit(-1);
- }
- }
-
- s = sparse_file_import(in, true, false);
- if (!s) {
- fprintf(stderr, "Failed to read sparse file\n");
- exit(-1);
- }
-
- if (lseek(out, 0, SEEK_SET) == -1) {
- perror("lseek failed");
- exit(EXIT_FAILURE);
- }
-
- if (sparse_file_write(s, out, false, false, false) < 0) {
- fprintf(stderr, "Cannot write output file\n");
- exit(-1);
- }
- sparse_file_destroy(s);
- close(in);
- }
-
- close(out);
-
- exit(0);
-}
-
diff --git a/libsparse/simg2img.cpp b/libsparse/simg2img.cpp
new file mode 100644
index 0000000..8ba5f69
--- /dev/null
+++ b/libsparse/simg2img.cpp
@@ -0,0 +1,86 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <sparse/sparse.h>
+
+#include <fcntl.h>
+#include <stdbool.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <unistd.h>
+
+#ifndef O_BINARY
+#define O_BINARY 0
+#endif
+
+void usage() {
+ fprintf(stderr, "Usage: simg2img <sparse_image_files> <raw_image_file>\n");
+}
+
+int main(int argc, char* argv[]) {
+ int in;
+ int out;
+ int i;
+ struct sparse_file* s;
+
+ if (argc < 3) {
+ usage();
+ exit(-1);
+ }
+
+ out = open(argv[argc - 1], O_WRONLY | O_CREAT | O_TRUNC | O_BINARY, 0664);
+ if (out < 0) {
+ fprintf(stderr, "Cannot open output file %s\n", argv[argc - 1]);
+ exit(-1);
+ }
+
+ for (i = 1; i < argc - 1; i++) {
+ if (strcmp(argv[i], "-") == 0) {
+ in = STDIN_FILENO;
+ } else {
+ in = open(argv[i], O_RDONLY | O_BINARY);
+ if (in < 0) {
+ fprintf(stderr, "Cannot open input file %s\n", argv[i]);
+ exit(-1);
+ }
+ }
+
+ s = sparse_file_import(in, true, false);
+ if (!s) {
+ fprintf(stderr, "Failed to read sparse file\n");
+ exit(-1);
+ }
+
+ if (lseek(out, 0, SEEK_SET) == -1) {
+ perror("lseek failed");
+ exit(EXIT_FAILURE);
+ }
+
+ if (sparse_file_write(s, out, false, false, false) < 0) {
+ fprintf(stderr, "Cannot write output file\n");
+ exit(-1);
+ }
+ sparse_file_destroy(s);
+ close(in);
+ }
+
+ close(out);
+
+ exit(0);
+}
diff --git a/libsparse/simg2simg.c b/libsparse/simg2simg.c
deleted file mode 100644
index 5f9ccf6..0000000
--- a/libsparse/simg2simg.c
+++ /dev/null
@@ -1,115 +0,0 @@
-/*
- * Copyright (C) 2012 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#define _FILE_OFFSET_BITS 64
-#define _LARGEFILE64_SOURCE 1
-#define _GNU_SOURCE
-
-#include <fcntl.h>
-#include <stdbool.h>
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-#include <sys/types.h>
-#include <sys/stat.h>
-#include <sys/types.h>
-#include <unistd.h>
-
-#include <sparse/sparse.h>
-
-#ifndef O_BINARY
-#define O_BINARY 0
-#endif
-
-void usage()
-{
- fprintf(stderr, "Usage: simg2simg <sparse image file> <sparse_image_file> <max_size>\n");
-}
-
-int main(int argc, char *argv[])
-{
- int in;
- int out;
- int i;
- int ret;
- struct sparse_file *s;
- int64_t max_size;
- struct sparse_file **out_s;
- int files;
- char filename[4096];
-
- if (argc != 4) {
- usage();
- exit(-1);
- }
-
- max_size = atoll(argv[3]);
-
- in = open(argv[1], O_RDONLY | O_BINARY);
- if (in < 0) {
- fprintf(stderr, "Cannot open input file %s\n", argv[1]);
- exit(-1);
- }
-
- s = sparse_file_import(in, true, false);
- if (!s) {
- fprintf(stderr, "Failed to import sparse file\n");
- exit(-1);
- }
-
- files = sparse_file_resparse(s, max_size, NULL, 0);
- if (files < 0) {
- fprintf(stderr, "Failed to resparse\n");
- exit(-1);
- }
-
- out_s = calloc(sizeof(struct sparse_file *), files);
- if (!out_s) {
- fprintf(stderr, "Failed to allocate sparse file array\n");
- exit(-1);
- }
-
- files = sparse_file_resparse(s, max_size, out_s, files);
- if (files < 0) {
- fprintf(stderr, "Failed to resparse\n");
- exit(-1);
- }
-
- for (i = 0; i < files; i++) {
- ret = snprintf(filename, sizeof(filename), "%s.%d", argv[2], i);
- if (ret >= (int)sizeof(filename)) {
- fprintf(stderr, "Filename too long\n");
- exit(-1);
- }
-
- out = open(filename, O_WRONLY | O_CREAT | O_TRUNC | O_BINARY, 0664);
- if (out < 0) {
- fprintf(stderr, "Cannot open output file %s\n", argv[2]);
- exit(-1);
- }
-
- ret = sparse_file_write(out_s[i], out, false, true, false);
- if (ret) {
- fprintf(stderr, "Failed to write sparse file\n");
- exit(-1);
- }
- close(out);
- }
-
- close(in);
-
- exit(0);
-}
diff --git a/libsparse/simg2simg.cpp b/libsparse/simg2simg.cpp
new file mode 100644
index 0000000..7e65701
--- /dev/null
+++ b/libsparse/simg2simg.cpp
@@ -0,0 +1,111 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#define _FILE_OFFSET_BITS 64
+#define _LARGEFILE64_SOURCE 1
+
+#include <fcntl.h>
+#include <stdbool.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <unistd.h>
+
+#include <sparse/sparse.h>
+
+#ifndef O_BINARY
+#define O_BINARY 0
+#endif
+
+void usage() {
+ fprintf(stderr, "Usage: simg2simg <sparse image file> <sparse_image_file> <max_size>\n");
+}
+
+int main(int argc, char* argv[]) {
+ int in;
+ int out;
+ int i;
+ int ret;
+ struct sparse_file* s;
+ int64_t max_size;
+ struct sparse_file** out_s;
+ int files;
+ char filename[4096];
+
+ if (argc != 4) {
+ usage();
+ exit(-1);
+ }
+
+ max_size = atoll(argv[3]);
+
+ in = open(argv[1], O_RDONLY | O_BINARY);
+ if (in < 0) {
+ fprintf(stderr, "Cannot open input file %s\n", argv[1]);
+ exit(-1);
+ }
+
+ s = sparse_file_import(in, true, false);
+ if (!s) {
+ fprintf(stderr, "Failed to import sparse file\n");
+ exit(-1);
+ }
+
+ files = sparse_file_resparse(s, max_size, NULL, 0);
+ if (files < 0) {
+ fprintf(stderr, "Failed to resparse\n");
+ exit(-1);
+ }
+
+ out_s = calloc(sizeof(struct sparse_file*), files);
+ if (!out_s) {
+ fprintf(stderr, "Failed to allocate sparse file array\n");
+ exit(-1);
+ }
+
+ files = sparse_file_resparse(s, max_size, out_s, files);
+ if (files < 0) {
+ fprintf(stderr, "Failed to resparse\n");
+ exit(-1);
+ }
+
+ for (i = 0; i < files; i++) {
+ ret = snprintf(filename, sizeof(filename), "%s.%d", argv[2], i);
+ if (ret >= (int)sizeof(filename)) {
+ fprintf(stderr, "Filename too long\n");
+ exit(-1);
+ }
+
+ out = open(filename, O_WRONLY | O_CREAT | O_TRUNC | O_BINARY, 0664);
+ if (out < 0) {
+ fprintf(stderr, "Cannot open output file %s\n", argv[2]);
+ exit(-1);
+ }
+
+ ret = sparse_file_write(out_s[i], out, false, true, false);
+ if (ret) {
+ fprintf(stderr, "Failed to write sparse file\n");
+ exit(-1);
+ }
+ close(out);
+ }
+
+ close(in);
+
+ exit(0);
+}
diff --git a/libsparse/sparse.c b/libsparse/sparse.c
deleted file mode 100644
index 466435f..0000000
--- a/libsparse/sparse.c
+++ /dev/null
@@ -1,395 +0,0 @@
-/*
- * Copyright (C) 2012 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include <assert.h>
-#include <stdlib.h>
-
-#include <sparse/sparse.h>
-
-#include "defs.h"
-#include "sparse_file.h"
-
-#include "output_file.h"
-#include "backed_block.h"
-#include "sparse_defs.h"
-#include "sparse_format.h"
-
-struct sparse_file *sparse_file_new(unsigned int block_size, int64_t len)
-{
- struct sparse_file *s = calloc(sizeof(struct sparse_file), 1);
- if (!s) {
- return NULL;
- }
-
- s->backed_block_list = backed_block_list_new(block_size);
- if (!s->backed_block_list) {
- free(s);
- return NULL;
- }
-
- s->block_size = block_size;
- s->len = len;
-
- return s;
-}
-
-void sparse_file_destroy(struct sparse_file *s)
-{
- backed_block_list_destroy(s->backed_block_list);
- free(s);
-}
-
-int sparse_file_add_data(struct sparse_file *s,
- void *data, unsigned int len, unsigned int block)
-{
- return backed_block_add_data(s->backed_block_list, data, len, block);
-}
-
-int sparse_file_add_fill(struct sparse_file *s,
- uint32_t fill_val, unsigned int len, unsigned int block)
-{
- return backed_block_add_fill(s->backed_block_list, fill_val, len, block);
-}
-
-int sparse_file_add_file(struct sparse_file *s,
- const char *filename, int64_t file_offset, unsigned int len,
- unsigned int block)
-{
- return backed_block_add_file(s->backed_block_list, filename, file_offset,
- len, block);
-}
-
-int sparse_file_add_fd(struct sparse_file *s,
- int fd, int64_t file_offset, unsigned int len, unsigned int block)
-{
- return backed_block_add_fd(s->backed_block_list, fd, file_offset,
- len, block);
-}
-unsigned int sparse_count_chunks(struct sparse_file *s)
-{
- struct backed_block *bb;
- unsigned int last_block = 0;
- unsigned int chunks = 0;
-
- for (bb = backed_block_iter_new(s->backed_block_list); bb;
- bb = backed_block_iter_next(bb)) {
- if (backed_block_block(bb) > last_block) {
- /* If there is a gap between chunks, add a skip chunk */
- chunks++;
- }
- chunks++;
- last_block = backed_block_block(bb) +
- DIV_ROUND_UP(backed_block_len(bb), s->block_size);
- }
- if (last_block < DIV_ROUND_UP(s->len, s->block_size)) {
- chunks++;
- }
-
- return chunks;
-}
-
-static int sparse_file_write_block(struct output_file *out,
- struct backed_block *bb)
-{
- int ret = -EINVAL;
-
- switch (backed_block_type(bb)) {
- case BACKED_BLOCK_DATA:
- ret = write_data_chunk(out, backed_block_len(bb), backed_block_data(bb));
- break;
- case BACKED_BLOCK_FILE:
- ret = write_file_chunk(out, backed_block_len(bb),
- backed_block_filename(bb),
- backed_block_file_offset(bb));
- break;
- case BACKED_BLOCK_FD:
- ret = write_fd_chunk(out, backed_block_len(bb),
- backed_block_fd(bb),
- backed_block_file_offset(bb));
- break;
- case BACKED_BLOCK_FILL:
- ret = write_fill_chunk(out, backed_block_len(bb),
- backed_block_fill_val(bb));
- break;
- }
-
- return ret;
-}
-
-static int write_all_blocks(struct sparse_file *s, struct output_file *out)
-{
- struct backed_block *bb;
- unsigned int last_block = 0;
- int64_t pad;
- int ret = 0;
-
- for (bb = backed_block_iter_new(s->backed_block_list); bb;
- bb = backed_block_iter_next(bb)) {
- if (backed_block_block(bb) > last_block) {
- unsigned int blocks = backed_block_block(bb) - last_block;
- write_skip_chunk(out, (int64_t)blocks * s->block_size);
- }
- ret = sparse_file_write_block(out, bb);
- if (ret)
- return ret;
- last_block = backed_block_block(bb) +
- DIV_ROUND_UP(backed_block_len(bb), s->block_size);
- }
-
- pad = s->len - (int64_t)last_block * s->block_size;
- assert(pad >= 0);
- if (pad > 0) {
- write_skip_chunk(out, pad);
- }
-
- return 0;
-}
-
-int sparse_file_write(struct sparse_file *s, int fd, bool gz, bool sparse,
- bool crc)
-{
- int ret;
- int chunks;
- struct output_file *out;
-
- chunks = sparse_count_chunks(s);
- out = output_file_open_fd(fd, s->block_size, s->len, gz, sparse, chunks, crc);
-
- if (!out)
- return -ENOMEM;
-
- ret = write_all_blocks(s, out);
-
- output_file_close(out);
-
- return ret;
-}
-
-int sparse_file_callback(struct sparse_file *s, bool sparse, bool crc,
- int (*write)(void *priv, const void *data, size_t len), void *priv)
-{
- int ret;
- int chunks;
- struct output_file *out;
-
- chunks = sparse_count_chunks(s);
- out = output_file_open_callback(write, priv, s->block_size, s->len, false,
- sparse, chunks, crc);
-
- if (!out)
- return -ENOMEM;
-
- ret = write_all_blocks(s, out);
-
- output_file_close(out);
-
- return ret;
-}
-
-struct chunk_data {
- void *priv;
- unsigned int block;
- unsigned int nr_blocks;
- int (*write)(void *priv, const void *data, size_t len,
- unsigned int block, unsigned int nr_blocks);
-};
-
-static int foreach_chunk_write(void *priv, const void *data, size_t len)
-{
- struct chunk_data *chk = priv;
-
- return chk->write(chk->priv, data, len, chk->block, chk->nr_blocks);
-}
-
-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)
-{
- int ret;
- int chunks;
- struct chunk_data chk;
- struct output_file *out;
- struct backed_block *bb;
-
- chk.priv = priv;
- chk.write = write;
- chk.block = chk.nr_blocks = 0;
- chunks = sparse_count_chunks(s);
- out = output_file_open_callback(foreach_chunk_write, &chk,
- s->block_size, s->len, false, sparse,
- chunks, crc);
-
- if (!out)
- return -ENOMEM;
-
- for (bb = backed_block_iter_new(s->backed_block_list); bb;
- bb = backed_block_iter_next(bb)) {
- chk.block = backed_block_block(bb);
- chk.nr_blocks = (backed_block_len(bb) - 1) / s->block_size + 1;
- ret = sparse_file_write_block(out, bb);
- if (ret)
- return ret;
- }
-
- output_file_close(out);
-
- return ret;
-}
-
-static int out_counter_write(void *priv, const void *data __unused, size_t len)
-{
- int64_t *count = priv;
- *count += len;
- return 0;
-}
-
-int64_t sparse_file_len(struct sparse_file *s, bool sparse, bool crc)
-{
- int ret;
- int chunks = sparse_count_chunks(s);
- int64_t count = 0;
- struct output_file *out;
-
- out = output_file_open_callback(out_counter_write, &count,
- s->block_size, s->len, false, sparse, chunks, crc);
- if (!out) {
- return -1;
- }
-
- ret = write_all_blocks(s, out);
-
- output_file_close(out);
-
- if (ret < 0) {
- return -1;
- }
-
- return count;
-}
-
-unsigned int sparse_file_block_size(struct sparse_file *s)
-{
- return s->block_size;
-}
-
-static struct backed_block *move_chunks_up_to_len(struct sparse_file *from,
- struct sparse_file *to, unsigned int len)
-{
- int64_t count = 0;
- struct output_file *out_counter;
- struct backed_block *last_bb = NULL;
- struct backed_block *bb;
- struct backed_block *start;
- unsigned int last_block = 0;
- int64_t file_len = 0;
- int ret;
-
- /*
- * overhead is sparse file header, the potential end skip
- * chunk and crc chunk.
- */
- int overhead = sizeof(sparse_header_t) + 2 * sizeof(chunk_header_t) +
- sizeof(uint32_t);
- len -= overhead;
-
- start = backed_block_iter_new(from->backed_block_list);
- out_counter = output_file_open_callback(out_counter_write, &count,
- to->block_size, to->len, false, true, 0, false);
- if (!out_counter) {
- return NULL;
- }
-
- for (bb = start; bb; bb = backed_block_iter_next(bb)) {
- count = 0;
- if (backed_block_block(bb) > last_block)
- count += sizeof(chunk_header_t);
- last_block = backed_block_block(bb) +
- DIV_ROUND_UP(backed_block_len(bb), to->block_size);
-
- /* will call out_counter_write to update count */
- ret = sparse_file_write_block(out_counter, bb);
- if (ret) {
- bb = NULL;
- goto out;
- }
- if (file_len + count > len) {
- /*
- * If the remaining available size is more than 1/8th of the
- * requested size, split the chunk. Results in sparse files that
- * are at least 7/8ths of the requested size
- */
- file_len += sizeof(chunk_header_t);
- if (!last_bb || (len - file_len > (len / 8))) {
- backed_block_split(from->backed_block_list, bb, len - file_len);
- last_bb = bb;
- }
- goto move;
- }
- file_len += count;
- last_bb = bb;
- }
-
-move:
- backed_block_list_move(from->backed_block_list,
- to->backed_block_list, start, last_bb);
-
-out:
- output_file_close(out_counter);
-
- return bb;
-}
-
-int sparse_file_resparse(struct sparse_file *in_s, unsigned int max_len,
- struct sparse_file **out_s, int out_s_count)
-{
- struct backed_block *bb;
- struct sparse_file *s;
- struct sparse_file *tmp;
- int c = 0;
-
- tmp = sparse_file_new(in_s->block_size, in_s->len);
- if (!tmp) {
- return -ENOMEM;
- }
-
- do {
- s = sparse_file_new(in_s->block_size, in_s->len);
-
- bb = move_chunks_up_to_len(in_s, s, max_len);
-
- if (c < out_s_count) {
- out_s[c] = s;
- } else {
- backed_block_list_move(s->backed_block_list, tmp->backed_block_list,
- NULL, NULL);
- sparse_file_destroy(s);
- }
- c++;
- } while (bb);
-
- backed_block_list_move(tmp->backed_block_list, in_s->backed_block_list,
- NULL, NULL);
-
- sparse_file_destroy(tmp);
-
- return c;
-}
-
-void sparse_file_verbose(struct sparse_file *s)
-{
- s->verbose = true;
-}
diff --git a/libsparse/sparse.cpp b/libsparse/sparse.cpp
new file mode 100644
index 0000000..6ff97b6
--- /dev/null
+++ b/libsparse/sparse.cpp
@@ -0,0 +1,348 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <assert.h>
+#include <stdlib.h>
+
+#include <sparse/sparse.h>
+
+#include "defs.h"
+#include "sparse_file.h"
+
+#include "backed_block.h"
+#include "output_file.h"
+#include "sparse_defs.h"
+#include "sparse_format.h"
+
+struct sparse_file* sparse_file_new(unsigned int block_size, int64_t len) {
+ struct sparse_file* s = reinterpret_cast<sparse_file*>(calloc(sizeof(struct sparse_file), 1));
+ if (!s) {
+ return NULL;
+ }
+
+ s->backed_block_list = backed_block_list_new(block_size);
+ if (!s->backed_block_list) {
+ free(s);
+ return NULL;
+ }
+
+ s->block_size = block_size;
+ s->len = len;
+
+ return s;
+}
+
+void sparse_file_destroy(struct sparse_file* s) {
+ backed_block_list_destroy(s->backed_block_list);
+ free(s);
+}
+
+int sparse_file_add_data(struct sparse_file* s, void* data, unsigned int len, unsigned int block) {
+ return backed_block_add_data(s->backed_block_list, data, len, block);
+}
+
+int sparse_file_add_fill(struct sparse_file* s, uint32_t fill_val, unsigned int len,
+ unsigned int block) {
+ return backed_block_add_fill(s->backed_block_list, fill_val, len, block);
+}
+
+int sparse_file_add_file(struct sparse_file* s, const char* filename, int64_t file_offset,
+ unsigned int len, unsigned int block) {
+ return backed_block_add_file(s->backed_block_list, filename, file_offset, len, block);
+}
+
+int sparse_file_add_fd(struct sparse_file* s, int fd, int64_t file_offset, unsigned int len,
+ unsigned int block) {
+ return backed_block_add_fd(s->backed_block_list, fd, file_offset, len, block);
+}
+unsigned int sparse_count_chunks(struct sparse_file* s) {
+ struct backed_block* bb;
+ unsigned int last_block = 0;
+ unsigned int chunks = 0;
+
+ for (bb = backed_block_iter_new(s->backed_block_list); bb; bb = backed_block_iter_next(bb)) {
+ if (backed_block_block(bb) > last_block) {
+ /* If there is a gap between chunks, add a skip chunk */
+ chunks++;
+ }
+ chunks++;
+ last_block = backed_block_block(bb) + DIV_ROUND_UP(backed_block_len(bb), s->block_size);
+ }
+ if (last_block < DIV_ROUND_UP(s->len, s->block_size)) {
+ chunks++;
+ }
+
+ return chunks;
+}
+
+static int sparse_file_write_block(struct output_file* out, struct backed_block* bb) {
+ int ret = -EINVAL;
+
+ switch (backed_block_type(bb)) {
+ case BACKED_BLOCK_DATA:
+ ret = write_data_chunk(out, backed_block_len(bb), backed_block_data(bb));
+ break;
+ case BACKED_BLOCK_FILE:
+ ret = write_file_chunk(out, backed_block_len(bb), backed_block_filename(bb),
+ backed_block_file_offset(bb));
+ break;
+ case BACKED_BLOCK_FD:
+ ret = write_fd_chunk(out, backed_block_len(bb), backed_block_fd(bb),
+ backed_block_file_offset(bb));
+ break;
+ case BACKED_BLOCK_FILL:
+ ret = write_fill_chunk(out, backed_block_len(bb), backed_block_fill_val(bb));
+ break;
+ }
+
+ return ret;
+}
+
+static int write_all_blocks(struct sparse_file* s, struct output_file* out) {
+ struct backed_block* bb;
+ unsigned int last_block = 0;
+ int64_t pad;
+ int ret = 0;
+
+ for (bb = backed_block_iter_new(s->backed_block_list); bb; bb = backed_block_iter_next(bb)) {
+ if (backed_block_block(bb) > last_block) {
+ unsigned int blocks = backed_block_block(bb) - last_block;
+ write_skip_chunk(out, (int64_t)blocks * s->block_size);
+ }
+ ret = sparse_file_write_block(out, bb);
+ if (ret) return ret;
+ last_block = backed_block_block(bb) + DIV_ROUND_UP(backed_block_len(bb), s->block_size);
+ }
+
+ pad = s->len - (int64_t)last_block * s->block_size;
+ assert(pad >= 0);
+ if (pad > 0) {
+ write_skip_chunk(out, pad);
+ }
+
+ return 0;
+}
+
+int sparse_file_write(struct sparse_file* s, int fd, bool gz, bool sparse, bool crc) {
+ int ret;
+ int chunks;
+ struct output_file* out;
+
+ chunks = sparse_count_chunks(s);
+ out = output_file_open_fd(fd, s->block_size, s->len, gz, sparse, chunks, crc);
+
+ if (!out) return -ENOMEM;
+
+ ret = write_all_blocks(s, out);
+
+ output_file_close(out);
+
+ return ret;
+}
+
+int sparse_file_callback(struct sparse_file* s, bool sparse, bool crc,
+ int (*write)(void* priv, const void* data, size_t len), void* priv) {
+ int ret;
+ int chunks;
+ struct output_file* out;
+
+ chunks = sparse_count_chunks(s);
+ out = output_file_open_callback(write, priv, s->block_size, s->len, false, sparse, chunks, crc);
+
+ if (!out) return -ENOMEM;
+
+ ret = write_all_blocks(s, out);
+
+ output_file_close(out);
+
+ return ret;
+}
+
+struct chunk_data {
+ void* priv;
+ unsigned int block;
+ unsigned int nr_blocks;
+ int (*write)(void* priv, const void* data, size_t len, unsigned int block, unsigned int nr_blocks);
+};
+
+static int foreach_chunk_write(void* priv, const void* data, size_t len) {
+ struct chunk_data* chk = reinterpret_cast<chunk_data*>(priv);
+
+ return chk->write(chk->priv, data, len, chk->block, chk->nr_blocks);
+}
+
+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) {
+ int ret;
+ int chunks;
+ struct chunk_data chk;
+ struct output_file* out;
+ struct backed_block* bb;
+
+ chk.priv = priv;
+ chk.write = write;
+ chk.block = chk.nr_blocks = 0;
+ chunks = sparse_count_chunks(s);
+ out = output_file_open_callback(foreach_chunk_write, &chk, s->block_size, s->len, false, sparse,
+ chunks, crc);
+
+ if (!out) return -ENOMEM;
+
+ for (bb = backed_block_iter_new(s->backed_block_list); bb; bb = backed_block_iter_next(bb)) {
+ chk.block = backed_block_block(bb);
+ chk.nr_blocks = (backed_block_len(bb) - 1) / s->block_size + 1;
+ ret = sparse_file_write_block(out, bb);
+ if (ret) return ret;
+ }
+
+ output_file_close(out);
+
+ return ret;
+}
+
+static int out_counter_write(void* priv, const void* data __unused, size_t len) {
+ int64_t* count = reinterpret_cast<int64_t*>(priv);
+ *count += len;
+ return 0;
+}
+
+int64_t sparse_file_len(struct sparse_file* s, bool sparse, bool crc) {
+ int ret;
+ int chunks = sparse_count_chunks(s);
+ int64_t count = 0;
+ struct output_file* out;
+
+ out = output_file_open_callback(out_counter_write, &count, s->block_size, s->len, false, sparse,
+ chunks, crc);
+ if (!out) {
+ return -1;
+ }
+
+ ret = write_all_blocks(s, out);
+
+ output_file_close(out);
+
+ if (ret < 0) {
+ return -1;
+ }
+
+ return count;
+}
+
+unsigned int sparse_file_block_size(struct sparse_file* s) {
+ return s->block_size;
+}
+
+static struct backed_block* move_chunks_up_to_len(struct sparse_file* from, struct sparse_file* to,
+ unsigned int len) {
+ int64_t count = 0;
+ struct output_file* out_counter;
+ struct backed_block* last_bb = NULL;
+ struct backed_block* bb;
+ struct backed_block* start;
+ unsigned int last_block = 0;
+ int64_t file_len = 0;
+ int ret;
+
+ /*
+ * overhead is sparse file header, the potential end skip
+ * chunk and crc chunk.
+ */
+ int overhead = sizeof(sparse_header_t) + 2 * sizeof(chunk_header_t) + sizeof(uint32_t);
+ len -= overhead;
+
+ start = backed_block_iter_new(from->backed_block_list);
+ out_counter = output_file_open_callback(out_counter_write, &count, to->block_size, to->len, false,
+ true, 0, false);
+ if (!out_counter) {
+ return NULL;
+ }
+
+ for (bb = start; bb; bb = backed_block_iter_next(bb)) {
+ count = 0;
+ if (backed_block_block(bb) > last_block) count += sizeof(chunk_header_t);
+ last_block = backed_block_block(bb) + DIV_ROUND_UP(backed_block_len(bb), to->block_size);
+
+ /* will call out_counter_write to update count */
+ ret = sparse_file_write_block(out_counter, bb);
+ if (ret) {
+ bb = NULL;
+ goto out;
+ }
+ if (file_len + count > len) {
+ /*
+ * If the remaining available size is more than 1/8th of the
+ * requested size, split the chunk. Results in sparse files that
+ * are at least 7/8ths of the requested size
+ */
+ file_len += sizeof(chunk_header_t);
+ if (!last_bb || (len - file_len > (len / 8))) {
+ backed_block_split(from->backed_block_list, bb, len - file_len);
+ last_bb = bb;
+ }
+ goto move;
+ }
+ file_len += count;
+ last_bb = bb;
+ }
+
+move:
+ backed_block_list_move(from->backed_block_list, to->backed_block_list, start, last_bb);
+
+out:
+ output_file_close(out_counter);
+
+ return bb;
+}
+
+int sparse_file_resparse(struct sparse_file* in_s, unsigned int max_len, struct sparse_file** out_s,
+ int out_s_count) {
+ struct backed_block* bb;
+ struct sparse_file* s;
+ struct sparse_file* tmp;
+ int c = 0;
+
+ tmp = sparse_file_new(in_s->block_size, in_s->len);
+ if (!tmp) {
+ return -ENOMEM;
+ }
+
+ do {
+ s = sparse_file_new(in_s->block_size, in_s->len);
+
+ bb = move_chunks_up_to_len(in_s, s, max_len);
+
+ if (c < out_s_count) {
+ out_s[c] = s;
+ } else {
+ backed_block_list_move(s->backed_block_list, tmp->backed_block_list, NULL, NULL);
+ sparse_file_destroy(s);
+ }
+ c++;
+ } while (bb);
+
+ backed_block_list_move(tmp->backed_block_list, in_s->backed_block_list, NULL, NULL);
+
+ sparse_file_destroy(tmp);
+
+ return c;
+}
+
+void sparse_file_verbose(struct sparse_file* s) {
+ s->verbose = true;
+}
diff --git a/libsparse/sparse_crc32.c b/libsparse/sparse_crc32.c
deleted file mode 100644
index 38bfe4a..0000000
--- a/libsparse/sparse_crc32.c
+++ /dev/null
@@ -1,111 +0,0 @@
-/*-
- * COPYRIGHT (C) 1986 Gary S. Brown. You may use this program, or
- * code or tables extracted from it, as desired without restriction.
- */
-
-/*
- * First, the polynomial itself and its table of feedback terms. The
- * polynomial is
- * X^32+X^26+X^23+X^22+X^16+X^12+X^11+X^10+X^8+X^7+X^5+X^4+X^2+X^1+X^0
- *
- * Note that we take it "backwards" and put the highest-order term in
- * the lowest-order bit. The X^32 term is "implied"; the LSB is the
- * X^31 term, etc. The X^0 term (usually shown as "+1") results in
- * the MSB being 1
- *
- * Note that the usual hardware shift register implementation, which
- * is what we're using (we're merely optimizing it by doing eight-bit
- * chunks at a time) shifts bits into the lowest-order term. In our
- * implementation, that means shifting towards the right. Why do we
- * do it this way? Because the calculated CRC must be transmitted in
- * order from highest-order term to lowest-order term. UARTs transmit
- * characters in order from LSB to MSB. By storing the CRC this way
- * we hand it to the UART in the order low-byte to high-byte; the UART
- * sends each low-bit to hight-bit; and the result is transmission bit
- * by bit from highest- to lowest-order term without requiring any bit
- * shuffling on our part. Reception works similarly
- *
- * The feedback terms table consists of 256, 32-bit entries. Notes
- *
- * The table can be generated at runtime if desired; code to do so
- * is shown later. It might not be obvious, but the feedback
- * terms simply represent the results of eight shift/xor opera
- * tions for all combinations of data and CRC register values
- *
- * The values must be right-shifted by eight bits by the "updcrc
- * logic; the shift must be unsigned (bring in zeroes). On some
- * hardware you could probably optimize the shift in assembler by
- * using byte-swap instructions
- * polynomial $edb88320
- *
- *
- * CRC32 code derived from work by Gary S. Brown.
- */
-
-/* Code taken from FreeBSD 8 */
-#include <stdint.h>
-
-static uint32_t crc32_tab[] = {
- 0x00000000, 0x77073096, 0xee0e612c, 0x990951ba, 0x076dc419, 0x706af48f,
- 0xe963a535, 0x9e6495a3, 0x0edb8832, 0x79dcb8a4, 0xe0d5e91e, 0x97d2d988,
- 0x09b64c2b, 0x7eb17cbd, 0xe7b82d07, 0x90bf1d91, 0x1db71064, 0x6ab020f2,
- 0xf3b97148, 0x84be41de, 0x1adad47d, 0x6ddde4eb, 0xf4d4b551, 0x83d385c7,
- 0x136c9856, 0x646ba8c0, 0xfd62f97a, 0x8a65c9ec, 0x14015c4f, 0x63066cd9,
- 0xfa0f3d63, 0x8d080df5, 0x3b6e20c8, 0x4c69105e, 0xd56041e4, 0xa2677172,
- 0x3c03e4d1, 0x4b04d447, 0xd20d85fd, 0xa50ab56b, 0x35b5a8fa, 0x42b2986c,
- 0xdbbbc9d6, 0xacbcf940, 0x32d86ce3, 0x45df5c75, 0xdcd60dcf, 0xabd13d59,
- 0x26d930ac, 0x51de003a, 0xc8d75180, 0xbfd06116, 0x21b4f4b5, 0x56b3c423,
- 0xcfba9599, 0xb8bda50f, 0x2802b89e, 0x5f058808, 0xc60cd9b2, 0xb10be924,
- 0x2f6f7c87, 0x58684c11, 0xc1611dab, 0xb6662d3d, 0x76dc4190, 0x01db7106,
- 0x98d220bc, 0xefd5102a, 0x71b18589, 0x06b6b51f, 0x9fbfe4a5, 0xe8b8d433,
- 0x7807c9a2, 0x0f00f934, 0x9609a88e, 0xe10e9818, 0x7f6a0dbb, 0x086d3d2d,
- 0x91646c97, 0xe6635c01, 0x6b6b51f4, 0x1c6c6162, 0x856530d8, 0xf262004e,
- 0x6c0695ed, 0x1b01a57b, 0x8208f4c1, 0xf50fc457, 0x65b0d9c6, 0x12b7e950,
- 0x8bbeb8ea, 0xfcb9887c, 0x62dd1ddf, 0x15da2d49, 0x8cd37cf3, 0xfbd44c65,
- 0x4db26158, 0x3ab551ce, 0xa3bc0074, 0xd4bb30e2, 0x4adfa541, 0x3dd895d7,
- 0xa4d1c46d, 0xd3d6f4fb, 0x4369e96a, 0x346ed9fc, 0xad678846, 0xda60b8d0,
- 0x44042d73, 0x33031de5, 0xaa0a4c5f, 0xdd0d7cc9, 0x5005713c, 0x270241aa,
- 0xbe0b1010, 0xc90c2086, 0x5768b525, 0x206f85b3, 0xb966d409, 0xce61e49f,
- 0x5edef90e, 0x29d9c998, 0xb0d09822, 0xc7d7a8b4, 0x59b33d17, 0x2eb40d81,
- 0xb7bd5c3b, 0xc0ba6cad, 0xedb88320, 0x9abfb3b6, 0x03b6e20c, 0x74b1d29a,
- 0xead54739, 0x9dd277af, 0x04db2615, 0x73dc1683, 0xe3630b12, 0x94643b84,
- 0x0d6d6a3e, 0x7a6a5aa8, 0xe40ecf0b, 0x9309ff9d, 0x0a00ae27, 0x7d079eb1,
- 0xf00f9344, 0x8708a3d2, 0x1e01f268, 0x6906c2fe, 0xf762575d, 0x806567cb,
- 0x196c3671, 0x6e6b06e7, 0xfed41b76, 0x89d32be0, 0x10da7a5a, 0x67dd4acc,
- 0xf9b9df6f, 0x8ebeeff9, 0x17b7be43, 0x60b08ed5, 0xd6d6a3e8, 0xa1d1937e,
- 0x38d8c2c4, 0x4fdff252, 0xd1bb67f1, 0xa6bc5767, 0x3fb506dd, 0x48b2364b,
- 0xd80d2bda, 0xaf0a1b4c, 0x36034af6, 0x41047a60, 0xdf60efc3, 0xa867df55,
- 0x316e8eef, 0x4669be79, 0xcb61b38c, 0xbc66831a, 0x256fd2a0, 0x5268e236,
- 0xcc0c7795, 0xbb0b4703, 0x220216b9, 0x5505262f, 0xc5ba3bbe, 0xb2bd0b28,
- 0x2bb45a92, 0x5cb36a04, 0xc2d7ffa7, 0xb5d0cf31, 0x2cd99e8b, 0x5bdeae1d,
- 0x9b64c2b0, 0xec63f226, 0x756aa39c, 0x026d930a, 0x9c0906a9, 0xeb0e363f,
- 0x72076785, 0x05005713, 0x95bf4a82, 0xe2b87a14, 0x7bb12bae, 0x0cb61b38,
- 0x92d28e9b, 0xe5d5be0d, 0x7cdcefb7, 0x0bdbdf21, 0x86d3d2d4, 0xf1d4e242,
- 0x68ddb3f8, 0x1fda836e, 0x81be16cd, 0xf6b9265b, 0x6fb077e1, 0x18b74777,
- 0x88085ae6, 0xff0f6a70, 0x66063bca, 0x11010b5c, 0x8f659eff, 0xf862ae69,
- 0x616bffd3, 0x166ccf45, 0xa00ae278, 0xd70dd2ee, 0x4e048354, 0x3903b3c2,
- 0xa7672661, 0xd06016f7, 0x4969474d, 0x3e6e77db, 0xaed16a4a, 0xd9d65adc,
- 0x40df0b66, 0x37d83bf0, 0xa9bcae53, 0xdebb9ec5, 0x47b2cf7f, 0x30b5ffe9,
- 0xbdbdf21c, 0xcabac28a, 0x53b39330, 0x24b4a3a6, 0xbad03605, 0xcdd70693,
- 0x54de5729, 0x23d967bf, 0xb3667a2e, 0xc4614ab8, 0x5d681b02, 0x2a6f2b94,
- 0xb40bbe37, 0xc30c8ea1, 0x5a05df1b, 0x2d02ef8d
-};
-
-/*
- * A function that calculates the CRC-32 based on the table above is
- * given below for documentation purposes. An equivalent implementation
- * of this function that's actually used in the kernel can be found
- * in sys/libkern.h, where it can be inlined.
- */
-
-uint32_t sparse_crc32(uint32_t crc_in, const void *buf, int size)
-{
- const uint8_t *p = buf;
- uint32_t crc;
-
- crc = crc_in ^ ~0U;
- while (size--)
- crc = crc32_tab[(crc ^ *p++) & 0xFF] ^ (crc >> 8);
- return crc ^ ~0U;
-}
-
diff --git a/libsparse/sparse_crc32.cpp b/libsparse/sparse_crc32.cpp
new file mode 100644
index 0000000..267322c
--- /dev/null
+++ b/libsparse/sparse_crc32.cpp
@@ -0,0 +1,97 @@
+/*-
+ * COPYRIGHT (C) 1986 Gary S. Brown. You may use this program, or
+ * code or tables extracted from it, as desired without restriction.
+ */
+
+/*
+ * First, the polynomial itself and its table of feedback terms. The
+ * polynomial is
+ * X^32+X^26+X^23+X^22+X^16+X^12+X^11+X^10+X^8+X^7+X^5+X^4+X^2+X^1+X^0
+ *
+ * Note that we take it "backwards" and put the highest-order term in
+ * the lowest-order bit. The X^32 term is "implied"; the LSB is the
+ * X^31 term, etc. The X^0 term (usually shown as "+1") results in
+ * the MSB being 1
+ *
+ * Note that the usual hardware shift register implementation, which
+ * is what we're using (we're merely optimizing it by doing eight-bit
+ * chunks at a time) shifts bits into the lowest-order term. In our
+ * implementation, that means shifting towards the right. Why do we
+ * do it this way? Because the calculated CRC must be transmitted in
+ * order from highest-order term to lowest-order term. UARTs transmit
+ * characters in order from LSB to MSB. By storing the CRC this way
+ * we hand it to the UART in the order low-byte to high-byte; the UART
+ * sends each low-bit to hight-bit; and the result is transmission bit
+ * by bit from highest- to lowest-order term without requiring any bit
+ * shuffling on our part. Reception works similarly
+ *
+ * The feedback terms table consists of 256, 32-bit entries. Notes
+ *
+ * The table can be generated at runtime if desired; code to do so
+ * is shown later. It might not be obvious, but the feedback
+ * terms simply represent the results of eight shift/xor opera
+ * tions for all combinations of data and CRC register values
+ *
+ * The values must be right-shifted by eight bits by the "updcrc
+ * logic; the shift must be unsigned (bring in zeroes). On some
+ * hardware you could probably optimize the shift in assembler by
+ * using byte-swap instructions
+ * polynomial $edb88320
+ *
+ *
+ * CRC32 code derived from work by Gary S. Brown.
+ */
+
+/* Code taken from FreeBSD 8 */
+#include <stdint.h>
+#include <stdio.h>
+
+static uint32_t crc32_tab[] = {
+ 0x00000000, 0x77073096, 0xee0e612c, 0x990951ba, 0x076dc419, 0x706af48f, 0xe963a535, 0x9e6495a3,
+ 0x0edb8832, 0x79dcb8a4, 0xe0d5e91e, 0x97d2d988, 0x09b64c2b, 0x7eb17cbd, 0xe7b82d07, 0x90bf1d91,
+ 0x1db71064, 0x6ab020f2, 0xf3b97148, 0x84be41de, 0x1adad47d, 0x6ddde4eb, 0xf4d4b551, 0x83d385c7,
+ 0x136c9856, 0x646ba8c0, 0xfd62f97a, 0x8a65c9ec, 0x14015c4f, 0x63066cd9, 0xfa0f3d63, 0x8d080df5,
+ 0x3b6e20c8, 0x4c69105e, 0xd56041e4, 0xa2677172, 0x3c03e4d1, 0x4b04d447, 0xd20d85fd, 0xa50ab56b,
+ 0x35b5a8fa, 0x42b2986c, 0xdbbbc9d6, 0xacbcf940, 0x32d86ce3, 0x45df5c75, 0xdcd60dcf, 0xabd13d59,
+ 0x26d930ac, 0x51de003a, 0xc8d75180, 0xbfd06116, 0x21b4f4b5, 0x56b3c423, 0xcfba9599, 0xb8bda50f,
+ 0x2802b89e, 0x5f058808, 0xc60cd9b2, 0xb10be924, 0x2f6f7c87, 0x58684c11, 0xc1611dab, 0xb6662d3d,
+ 0x76dc4190, 0x01db7106, 0x98d220bc, 0xefd5102a, 0x71b18589, 0x06b6b51f, 0x9fbfe4a5, 0xe8b8d433,
+ 0x7807c9a2, 0x0f00f934, 0x9609a88e, 0xe10e9818, 0x7f6a0dbb, 0x086d3d2d, 0x91646c97, 0xe6635c01,
+ 0x6b6b51f4, 0x1c6c6162, 0x856530d8, 0xf262004e, 0x6c0695ed, 0x1b01a57b, 0x8208f4c1, 0xf50fc457,
+ 0x65b0d9c6, 0x12b7e950, 0x8bbeb8ea, 0xfcb9887c, 0x62dd1ddf, 0x15da2d49, 0x8cd37cf3, 0xfbd44c65,
+ 0x4db26158, 0x3ab551ce, 0xa3bc0074, 0xd4bb30e2, 0x4adfa541, 0x3dd895d7, 0xa4d1c46d, 0xd3d6f4fb,
+ 0x4369e96a, 0x346ed9fc, 0xad678846, 0xda60b8d0, 0x44042d73, 0x33031de5, 0xaa0a4c5f, 0xdd0d7cc9,
+ 0x5005713c, 0x270241aa, 0xbe0b1010, 0xc90c2086, 0x5768b525, 0x206f85b3, 0xb966d409, 0xce61e49f,
+ 0x5edef90e, 0x29d9c998, 0xb0d09822, 0xc7d7a8b4, 0x59b33d17, 0x2eb40d81, 0xb7bd5c3b, 0xc0ba6cad,
+ 0xedb88320, 0x9abfb3b6, 0x03b6e20c, 0x74b1d29a, 0xead54739, 0x9dd277af, 0x04db2615, 0x73dc1683,
+ 0xe3630b12, 0x94643b84, 0x0d6d6a3e, 0x7a6a5aa8, 0xe40ecf0b, 0x9309ff9d, 0x0a00ae27, 0x7d079eb1,
+ 0xf00f9344, 0x8708a3d2, 0x1e01f268, 0x6906c2fe, 0xf762575d, 0x806567cb, 0x196c3671, 0x6e6b06e7,
+ 0xfed41b76, 0x89d32be0, 0x10da7a5a, 0x67dd4acc, 0xf9b9df6f, 0x8ebeeff9, 0x17b7be43, 0x60b08ed5,
+ 0xd6d6a3e8, 0xa1d1937e, 0x38d8c2c4, 0x4fdff252, 0xd1bb67f1, 0xa6bc5767, 0x3fb506dd, 0x48b2364b,
+ 0xd80d2bda, 0xaf0a1b4c, 0x36034af6, 0x41047a60, 0xdf60efc3, 0xa867df55, 0x316e8eef, 0x4669be79,
+ 0xcb61b38c, 0xbc66831a, 0x256fd2a0, 0x5268e236, 0xcc0c7795, 0xbb0b4703, 0x220216b9, 0x5505262f,
+ 0xc5ba3bbe, 0xb2bd0b28, 0x2bb45a92, 0x5cb36a04, 0xc2d7ffa7, 0xb5d0cf31, 0x2cd99e8b, 0x5bdeae1d,
+ 0x9b64c2b0, 0xec63f226, 0x756aa39c, 0x026d930a, 0x9c0906a9, 0xeb0e363f, 0x72076785, 0x05005713,
+ 0x95bf4a82, 0xe2b87a14, 0x7bb12bae, 0x0cb61b38, 0x92d28e9b, 0xe5d5be0d, 0x7cdcefb7, 0x0bdbdf21,
+ 0x86d3d2d4, 0xf1d4e242, 0x68ddb3f8, 0x1fda836e, 0x81be16cd, 0xf6b9265b, 0x6fb077e1, 0x18b74777,
+ 0x88085ae6, 0xff0f6a70, 0x66063bca, 0x11010b5c, 0x8f659eff, 0xf862ae69, 0x616bffd3, 0x166ccf45,
+ 0xa00ae278, 0xd70dd2ee, 0x4e048354, 0x3903b3c2, 0xa7672661, 0xd06016f7, 0x4969474d, 0x3e6e77db,
+ 0xaed16a4a, 0xd9d65adc, 0x40df0b66, 0x37d83bf0, 0xa9bcae53, 0xdebb9ec5, 0x47b2cf7f, 0x30b5ffe9,
+ 0xbdbdf21c, 0xcabac28a, 0x53b39330, 0x24b4a3a6, 0xbad03605, 0xcdd70693, 0x54de5729, 0x23d967bf,
+ 0xb3667a2e, 0xc4614ab8, 0x5d681b02, 0x2a6f2b94, 0xb40bbe37, 0xc30c8ea1, 0x5a05df1b, 0x2d02ef8d};
+
+/*
+ * A function that calculates the CRC-32 based on the table above is
+ * given below for documentation purposes. An equivalent implementation
+ * of this function that's actually used in the kernel can be found
+ * in sys/libkern.h, where it can be inlined.
+ */
+
+uint32_t sparse_crc32(uint32_t crc_in, const void* buf, size_t size) {
+ const uint8_t* p = reinterpret_cast<const uint8_t*>(buf);
+ uint32_t crc;
+
+ crc = crc_in ^ ~0U;
+ while (size--) crc = crc32_tab[(crc ^ *p++) & 0xFF] ^ (crc >> 8);
+ return crc ^ ~0U;
+}
diff --git a/libsparse/sparse_crc32.h b/libsparse/sparse_crc32.h
index 50cd9e9..2702c4f 100644
--- a/libsparse/sparse_crc32.h
+++ b/libsparse/sparse_crc32.h
@@ -19,14 +19,6 @@
#include <stdint.h>
-#ifdef __cplusplus
-extern "C" {
-#endif
-
-uint32_t sparse_crc32(uint32_t crc, const void *buf, size_t size);
-
-#ifdef __cplusplus
-}
-#endif
+uint32_t sparse_crc32(uint32_t crc, const void* buf, size_t size);
#endif
diff --git a/libsparse/sparse_defs.h b/libsparse/sparse_defs.h
index b99cfd5..9137805 100644
--- a/libsparse/sparse_defs.h
+++ b/libsparse/sparse_defs.h
@@ -39,11 +39,14 @@
typedef unsigned short int u16;
typedef unsigned char u8;
-#define DIV_ROUND_UP(x, y) (((x) + (y) - 1)/(y))
-#define ALIGN(x, y) ((y) * DIV_ROUND_UP((x), (y)))
+#define DIV_ROUND_UP(x, y) (((x) + (y)-1) / (y))
+#define ALIGN(x, y) ((y)*DIV_ROUND_UP((x), (y)))
#define ALIGN_DOWN(x, y) ((y) * ((x) / (y)))
-#define error(fmt, args...) do { fprintf(stderr, "error: %s: " fmt "\n", __func__, ## args); } while (0)
+#define error(fmt, args...) \
+ do { \
+ fprintf(stderr, "error: %s: " fmt "\n", __func__, ##args); \
+ } while (0)
#define error_errno(s, args...) error(s ": %s", ##args, strerror(errno))
#endif
diff --git a/libsparse/sparse_err.c b/libsparse/sparse_err.cpp
similarity index 73%
rename from libsparse/sparse_err.c
rename to libsparse/sparse_err.cpp
index 0f392ad..6886d31 100644
--- a/libsparse/sparse_err.c
+++ b/libsparse/sparse_err.cpp
@@ -20,14 +20,13 @@
#include <stdio.h>
#include <unistd.h>
-void sparse_default_print(const char *fmt, ...)
-{
- va_list argp;
+void sparse_default_print(const char* fmt, ...) {
+ va_list argp;
- va_start(argp, fmt);
- vfprintf(stderr, fmt, argp);
- va_end(argp);
+ va_start(argp, fmt);
+ vfprintf(stderr, fmt, argp);
+ va_end(argp);
}
-void (*sparse_print_error)(const char *fmt, ...) = sparse_default_print;
-void (*sparse_print_verbose)(const char *fmt, ...) = sparse_default_print;
+void (*sparse_print_error)(const char* fmt, ...) = sparse_default_print;
+void (*sparse_print_verbose)(const char* fmt, ...) = sparse_default_print;
diff --git a/libsparse/sparse_file.h b/libsparse/sparse_file.h
index 763f43f..e565f63 100644
--- a/libsparse/sparse_file.h
+++ b/libsparse/sparse_file.h
@@ -24,12 +24,12 @@
#include <sparse/sparse.h>
struct sparse_file {
- unsigned int block_size;
- int64_t len;
- bool verbose;
+ unsigned int block_size;
+ int64_t len;
+ bool verbose;
- struct backed_block_list *backed_block_list;
- struct output_file *out;
+ struct backed_block_list* backed_block_list;
+ struct output_file* out;
};
#ifdef __cplusplus
diff --git a/libsparse/sparse_format.h b/libsparse/sparse_format.h
index 779e038..a8a721e 100644
--- a/libsparse/sparse_format.h
+++ b/libsparse/sparse_format.h
@@ -23,31 +23,31 @@
#endif
typedef struct sparse_header {
- __le32 magic; /* 0xed26ff3a */
- __le16 major_version; /* (0x1) - reject images with higher major versions */
- __le16 minor_version; /* (0x0) - allow images with higer minor versions */
- __le16 file_hdr_sz; /* 28 bytes for first revision of the file format */
- __le16 chunk_hdr_sz; /* 12 bytes for first revision of the file format */
- __le32 blk_sz; /* block size in bytes, must be a multiple of 4 (4096) */
- __le32 total_blks; /* total blocks in the non-sparse output image */
- __le32 total_chunks; /* total chunks in the sparse input image */
- __le32 image_checksum; /* CRC32 checksum of the original data, counting "don't care" */
- /* as 0. Standard 802.3 polynomial, use a Public Domain */
- /* table implementation */
+ __le32 magic; /* 0xed26ff3a */
+ __le16 major_version; /* (0x1) - reject images with higher major versions */
+ __le16 minor_version; /* (0x0) - allow images with higer minor versions */
+ __le16 file_hdr_sz; /* 28 bytes for first revision of the file format */
+ __le16 chunk_hdr_sz; /* 12 bytes for first revision of the file format */
+ __le32 blk_sz; /* block size in bytes, must be a multiple of 4 (4096) */
+ __le32 total_blks; /* total blocks in the non-sparse output image */
+ __le32 total_chunks; /* total chunks in the sparse input image */
+ __le32 image_checksum; /* CRC32 checksum of the original data, counting "don't care" */
+ /* as 0. Standard 802.3 polynomial, use a Public Domain */
+ /* table implementation */
} sparse_header_t;
-#define SPARSE_HEADER_MAGIC 0xed26ff3a
+#define SPARSE_HEADER_MAGIC 0xed26ff3a
-#define CHUNK_TYPE_RAW 0xCAC1
-#define CHUNK_TYPE_FILL 0xCAC2
-#define CHUNK_TYPE_DONT_CARE 0xCAC3
-#define CHUNK_TYPE_CRC32 0xCAC4
+#define CHUNK_TYPE_RAW 0xCAC1
+#define CHUNK_TYPE_FILL 0xCAC2
+#define CHUNK_TYPE_DONT_CARE 0xCAC3
+#define CHUNK_TYPE_CRC32 0xCAC4
typedef struct chunk_header {
- __le16 chunk_type; /* 0xCAC1 -> raw; 0xCAC2 -> fill; 0xCAC3 -> don't care */
- __le16 reserved1;
- __le32 chunk_sz; /* in blocks in output image */
- __le32 total_sz; /* in bytes of chunk input file including chunk header and data */
+ __le16 chunk_type; /* 0xCAC1 -> raw; 0xCAC2 -> fill; 0xCAC3 -> don't care */
+ __le16 reserved1;
+ __le32 chunk_sz; /* in blocks in output image */
+ __le32 total_sz; /* in bytes of chunk input file including chunk header and data */
} chunk_header_t;
/* Following a Raw or Fill or CRC32 chunk is data.
diff --git a/libsparse/sparse_read.cpp b/libsparse/sparse_read.cpp
index 4379635..56e2c9a 100644
--- a/libsparse/sparse_read.cpp
+++ b/libsparse/sparse_read.cpp
@@ -17,16 +17,16 @@
#define _FILE_OFFSET_BITS 64
#define _LARGEFILE64_SOURCE 1
-#include <algorithm>
-#include <inttypes.h>
#include <fcntl.h>
+#include <inttypes.h>
#include <stdarg.h>
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
-#include <string>
#include <unistd.h>
+#include <algorithm>
+#include <string>
#include <sparse/sparse.h>
@@ -37,447 +37,541 @@
#include "sparse_file.h"
#include "sparse_format.h"
-
#if defined(__APPLE__) && defined(__MACH__)
#define lseek64 lseek
#define off64_t off_t
#endif
#define SPARSE_HEADER_MAJOR_VER 1
-#define SPARSE_HEADER_LEN (sizeof(sparse_header_t))
+#define SPARSE_HEADER_LEN (sizeof(sparse_header_t))
#define CHUNK_HEADER_LEN (sizeof(chunk_header_t))
static constexpr int64_t COPY_BUF_SIZE = 1024 * 1024;
-static char *copybuf;
+static char* copybuf;
-static std::string ErrorString(int err)
-{
- if (err == -EOVERFLOW) return "EOF while reading file";
- if (err == -EINVAL) return "Invalid sparse file format";
- if (err == -ENOMEM) return "Failed allocation while reading file";
- return android::base::StringPrintf("Unknown error %d", err);
+static std::string ErrorString(int err) {
+ if (err == -EOVERFLOW) return "EOF while reading file";
+ if (err == -EINVAL) return "Invalid sparse file format";
+ if (err == -ENOMEM) return "Failed allocation while reading file";
+ return android::base::StringPrintf("Unknown error %d", err);
}
-static void verbose_error(bool verbose, int err, const char *fmt, ...)
-{
- if (!verbose) return;
+class SparseFileSource {
+ public:
+ /* Seeks the source ahead by the given offset. */
+ virtual void Seek(int64_t offset) = 0;
- std::string msg = ErrorString(err);
- if (fmt) {
- msg += " at ";
- va_list argp;
- va_start(argp, fmt);
- android::base::StringAppendV(&msg, fmt, argp);
- va_end(argp);
- }
- sparse_print_verbose("%s\n", msg.c_str());
+ /* Return the current offset. */
+ virtual int64_t GetOffset() = 0;
+
+ /* Set the current offset. Return 0 if successful. */
+ virtual int SetOffset(int64_t offset) = 0;
+
+ /* Adds the given length from the current offset of the source to the file at the given block.
+ * Return 0 if successful. */
+ virtual int AddToSparseFile(struct sparse_file* s, int64_t len, unsigned int block) = 0;
+
+ /* Get data of fixed size from the current offset and seek len bytes. Return 0 if successful. */
+ virtual int ReadValue(void* ptr, int len) = 0;
+
+ /* Find the crc32 of the next len bytes and seek ahead len bytes. Return 0 if successful. */
+ virtual int GetCrc32(uint32_t* crc32, int64_t len) = 0;
+
+ virtual ~SparseFileSource(){};
+};
+
+class SparseFileFdSource : public SparseFileSource {
+ private:
+ int fd;
+
+ public:
+ SparseFileFdSource(int fd) : fd(fd) {}
+ ~SparseFileFdSource() override {}
+
+ void Seek(int64_t off) override { lseek64(fd, off, SEEK_CUR); }
+
+ int64_t GetOffset() override { return lseek64(fd, 0, SEEK_CUR); }
+
+ int SetOffset(int64_t offset) override {
+ return lseek64(fd, offset, SEEK_SET) == offset ? 0 : -errno;
+ }
+
+ int AddToSparseFile(struct sparse_file* s, int64_t len, unsigned int block) override {
+ return sparse_file_add_fd(s, fd, GetOffset(), len, block);
+ }
+
+ int ReadValue(void* ptr, int len) override { return read_all(fd, ptr, len); }
+
+ int GetCrc32(uint32_t* crc32, int64_t len) override {
+ int chunk;
+ int ret;
+ while (len) {
+ chunk = std::min(len, COPY_BUF_SIZE);
+ ret = read_all(fd, copybuf, chunk);
+ if (ret < 0) {
+ return ret;
+ }
+ *crc32 = sparse_crc32(*crc32, copybuf, chunk);
+ len -= chunk;
+ }
+ return 0;
+ }
+};
+
+class SparseFileBufSource : public SparseFileSource {
+ private:
+ char* buf;
+ int64_t offset;
+
+ public:
+ SparseFileBufSource(char* buf) : buf(buf), offset(0) {}
+ ~SparseFileBufSource() override {}
+
+ void Seek(int64_t off) override {
+ buf += off;
+ offset += off;
+ }
+
+ int64_t GetOffset() override { return offset; }
+
+ int SetOffset(int64_t off) override {
+ buf += off - offset;
+ offset = off;
+ return 0;
+ }
+
+ int AddToSparseFile(struct sparse_file* s, int64_t len, unsigned int block) override {
+ return sparse_file_add_data(s, buf, len, block);
+ }
+
+ int ReadValue(void* ptr, int len) override {
+ memcpy(ptr, buf, len);
+ Seek(len);
+ return 0;
+ }
+
+ int GetCrc32(uint32_t* crc32, int64_t len) override {
+ *crc32 = sparse_crc32(*crc32, buf, len);
+ Seek(len);
+ return 0;
+ }
+};
+
+static void verbose_error(bool verbose, int err, const char* fmt, ...) {
+ if (!verbose) return;
+
+ std::string msg = ErrorString(err);
+ if (fmt) {
+ msg += " at ";
+ va_list argp;
+ va_start(argp, fmt);
+ android::base::StringAppendV(&msg, fmt, argp);
+ va_end(argp);
+ }
+ sparse_print_verbose("%s\n", msg.c_str());
}
-static int process_raw_chunk(struct sparse_file *s, unsigned int chunk_size,
- int fd, int64_t offset, unsigned int blocks, unsigned int block,
- uint32_t *crc32)
-{
- int ret;
- int chunk;
- int64_t len = blocks * s->block_size;
+static int process_raw_chunk(struct sparse_file* s, unsigned int chunk_size,
+ SparseFileSource* source, unsigned int blocks, unsigned int block,
+ uint32_t* crc32) {
+ int ret;
+ int64_t len = blocks * s->block_size;
- if (chunk_size % s->block_size != 0) {
- return -EINVAL;
- }
+ if (chunk_size % s->block_size != 0) {
+ return -EINVAL;
+ }
- if (chunk_size / s->block_size != blocks) {
- return -EINVAL;
- }
+ if (chunk_size / s->block_size != blocks) {
+ return -EINVAL;
+ }
- ret = sparse_file_add_fd(s, fd, offset, len, block);
- if (ret < 0) {
- return ret;
- }
+ ret = source->AddToSparseFile(s, len, block);
+ if (ret < 0) {
+ return ret;
+ }
- if (crc32) {
- while (len) {
- chunk = std::min(len, COPY_BUF_SIZE);
- ret = read_all(fd, copybuf, chunk);
- if (ret < 0) {
- return ret;
- }
- *crc32 = sparse_crc32(*crc32, copybuf, chunk);
- len -= chunk;
- }
- } else {
- lseek64(fd, len, SEEK_CUR);
- }
+ if (crc32) {
+ ret = source->GetCrc32(crc32, len);
+ if (ret < 0) {
+ return ret;
+ }
+ } else {
+ source->Seek(len);
+ }
- return 0;
+ return 0;
}
-static int process_fill_chunk(struct sparse_file *s, unsigned int chunk_size,
- int fd, unsigned int blocks, unsigned int block, uint32_t *crc32)
-{
- int ret;
- int chunk;
- int64_t len = (int64_t)blocks * s->block_size;
- uint32_t fill_val;
- uint32_t *fillbuf;
- unsigned int i;
+static int process_fill_chunk(struct sparse_file* s, unsigned int chunk_size,
+ SparseFileSource* source, unsigned int blocks, unsigned int block,
+ uint32_t* crc32) {
+ int ret;
+ int chunk;
+ int64_t len = (int64_t)blocks * s->block_size;
+ uint32_t fill_val;
+ uint32_t* fillbuf;
+ unsigned int i;
- if (chunk_size != sizeof(fill_val)) {
- return -EINVAL;
- }
+ if (chunk_size != sizeof(fill_val)) {
+ return -EINVAL;
+ }
- ret = read_all(fd, &fill_val, sizeof(fill_val));
- if (ret < 0) {
- return ret;
- }
+ ret = source->ReadValue(&fill_val, sizeof(fill_val));
+ if (ret < 0) {
+ return ret;
+ }
- ret = sparse_file_add_fill(s, fill_val, len, block);
- if (ret < 0) {
- return ret;
- }
+ ret = sparse_file_add_fill(s, fill_val, len, block);
+ if (ret < 0) {
+ return ret;
+ }
- if (crc32) {
- /* Fill copy_buf with the fill value */
- fillbuf = (uint32_t *)copybuf;
- for (i = 0; i < (COPY_BUF_SIZE / sizeof(fill_val)); i++) {
- fillbuf[i] = fill_val;
- }
+ if (crc32) {
+ /* Fill copy_buf with the fill value */
+ fillbuf = (uint32_t*)copybuf;
+ for (i = 0; i < (COPY_BUF_SIZE / sizeof(fill_val)); i++) {
+ fillbuf[i] = fill_val;
+ }
- while (len) {
- chunk = std::min(len, COPY_BUF_SIZE);
- *crc32 = sparse_crc32(*crc32, copybuf, chunk);
- len -= chunk;
- }
- }
+ while (len) {
+ chunk = std::min(len, COPY_BUF_SIZE);
+ *crc32 = sparse_crc32(*crc32, copybuf, chunk);
+ len -= chunk;
+ }
+ }
- return 0;
+ return 0;
}
-static int process_skip_chunk(struct sparse_file *s, unsigned int chunk_size,
- int fd __unused, unsigned int blocks,
- unsigned int block __unused, uint32_t *crc32)
-{
- if (chunk_size != 0) {
- return -EINVAL;
- }
+static int process_skip_chunk(struct sparse_file* s, unsigned int chunk_size,
+ SparseFileSource* source __unused, unsigned int blocks,
+ unsigned int block __unused, uint32_t* crc32) {
+ if (chunk_size != 0) {
+ return -EINVAL;
+ }
- if (crc32) {
- int64_t len = (int64_t)blocks * s->block_size;
- memset(copybuf, 0, COPY_BUF_SIZE);
+ if (crc32) {
+ int64_t len = (int64_t)blocks * s->block_size;
+ memset(copybuf, 0, COPY_BUF_SIZE);
- while (len) {
- int chunk = std::min(len, COPY_BUF_SIZE);
- *crc32 = sparse_crc32(*crc32, copybuf, chunk);
- len -= chunk;
- }
- }
+ while (len) {
+ int chunk = std::min(len, COPY_BUF_SIZE);
+ *crc32 = sparse_crc32(*crc32, copybuf, chunk);
+ len -= chunk;
+ }
+ }
- return 0;
+ return 0;
}
-static int process_crc32_chunk(int fd, unsigned int chunk_size, uint32_t *crc32)
-{
- uint32_t file_crc32;
- int ret;
+static int process_crc32_chunk(SparseFileSource* source, unsigned int chunk_size, uint32_t* crc32) {
+ uint32_t file_crc32;
- if (chunk_size != sizeof(file_crc32)) {
- return -EINVAL;
- }
+ if (chunk_size != sizeof(file_crc32)) {
+ return -EINVAL;
+ }
- ret = read_all(fd, &file_crc32, sizeof(file_crc32));
- if (ret < 0) {
- return ret;
- }
+ int ret = source->ReadValue(&file_crc32, sizeof(file_crc32));
+ if (ret < 0) {
+ return ret;
+ }
- if (crc32 != NULL && file_crc32 != *crc32) {
- return -EINVAL;
- }
+ if (crc32 != NULL && file_crc32 != *crc32) {
+ return -EINVAL;
+ }
- return 0;
+ return 0;
}
-static int process_chunk(struct sparse_file *s, int fd, off64_t offset,
- unsigned int chunk_hdr_sz, chunk_header_t *chunk_header,
- unsigned int cur_block, uint32_t *crc_ptr)
-{
- int ret;
- unsigned int chunk_data_size;
+static int process_chunk(struct sparse_file* s, SparseFileSource* source, unsigned int chunk_hdr_sz,
+ chunk_header_t* chunk_header, unsigned int cur_block, uint32_t* crc_ptr) {
+ int ret;
+ unsigned int chunk_data_size;
+ int64_t offset = source->GetOffset();
- chunk_data_size = chunk_header->total_sz - chunk_hdr_sz;
+ chunk_data_size = chunk_header->total_sz - chunk_hdr_sz;
- switch (chunk_header->chunk_type) {
- case CHUNK_TYPE_RAW:
- ret = process_raw_chunk(s, chunk_data_size, fd, offset,
- chunk_header->chunk_sz, cur_block, crc_ptr);
- if (ret < 0) {
- verbose_error(s->verbose, ret, "data block at %" PRId64, offset);
- return ret;
- }
- return chunk_header->chunk_sz;
- case CHUNK_TYPE_FILL:
- ret = process_fill_chunk(s, chunk_data_size, fd,
- chunk_header->chunk_sz, cur_block, crc_ptr);
- if (ret < 0) {
- verbose_error(s->verbose, ret, "fill block at %" PRId64, offset);
- return ret;
- }
- return chunk_header->chunk_sz;
- case CHUNK_TYPE_DONT_CARE:
- ret = process_skip_chunk(s, chunk_data_size, fd,
- chunk_header->chunk_sz, cur_block, crc_ptr);
- if (chunk_data_size != 0) {
- if (ret < 0) {
- verbose_error(s->verbose, ret, "skip block at %" PRId64, offset);
- return ret;
- }
- }
- return chunk_header->chunk_sz;
- case CHUNK_TYPE_CRC32:
- ret = process_crc32_chunk(fd, chunk_data_size, crc_ptr);
- if (ret < 0) {
- verbose_error(s->verbose, -EINVAL, "crc block at %" PRId64,
- offset);
- return ret;
- }
- return 0;
- default:
- verbose_error(s->verbose, -EINVAL, "unknown block %04X at %" PRId64,
- chunk_header->chunk_type, offset);
- }
+ switch (chunk_header->chunk_type) {
+ case CHUNK_TYPE_RAW:
+ ret =
+ process_raw_chunk(s, chunk_data_size, source, chunk_header->chunk_sz, cur_block, crc_ptr);
+ if (ret < 0) {
+ verbose_error(s->verbose, ret, "data block at %" PRId64, offset);
+ return ret;
+ }
+ return chunk_header->chunk_sz;
+ case CHUNK_TYPE_FILL:
+ ret = process_fill_chunk(s, chunk_data_size, source, chunk_header->chunk_sz, cur_block,
+ crc_ptr);
+ if (ret < 0) {
+ verbose_error(s->verbose, ret, "fill block at %" PRId64, offset);
+ return ret;
+ }
+ return chunk_header->chunk_sz;
+ case CHUNK_TYPE_DONT_CARE:
+ ret = process_skip_chunk(s, chunk_data_size, source, chunk_header->chunk_sz, cur_block,
+ crc_ptr);
+ if (chunk_data_size != 0) {
+ if (ret < 0) {
+ verbose_error(s->verbose, ret, "skip block at %" PRId64, offset);
+ return ret;
+ }
+ }
+ return chunk_header->chunk_sz;
+ case CHUNK_TYPE_CRC32:
+ ret = process_crc32_chunk(source, chunk_data_size, crc_ptr);
+ if (ret < 0) {
+ verbose_error(s->verbose, -EINVAL, "crc block at %" PRId64, offset);
+ return ret;
+ }
+ return 0;
+ default:
+ verbose_error(s->verbose, -EINVAL, "unknown block %04X at %" PRId64, chunk_header->chunk_type,
+ offset);
+ }
- return 0;
+ return 0;
}
-static int sparse_file_read_sparse(struct sparse_file *s, int fd, bool crc)
-{
- int ret;
- unsigned int i;
- sparse_header_t sparse_header;
- chunk_header_t chunk_header;
- uint32_t crc32 = 0;
- uint32_t *crc_ptr = 0;
- unsigned int cur_block = 0;
- off64_t offset;
+static int sparse_file_read_sparse(struct sparse_file* s, SparseFileSource* source, bool crc) {
+ int ret;
+ unsigned int i;
+ sparse_header_t sparse_header;
+ chunk_header_t chunk_header;
+ uint32_t crc32 = 0;
+ uint32_t* crc_ptr = 0;
+ unsigned int cur_block = 0;
- if (!copybuf) {
- copybuf = (char *)malloc(COPY_BUF_SIZE);
- }
+ if (!copybuf) {
+ copybuf = (char*)malloc(COPY_BUF_SIZE);
+ }
- if (!copybuf) {
- return -ENOMEM;
- }
+ if (!copybuf) {
+ return -ENOMEM;
+ }
- if (crc) {
- crc_ptr = &crc32;
- }
+ if (crc) {
+ crc_ptr = &crc32;
+ }
- ret = read_all(fd, &sparse_header, sizeof(sparse_header));
- if (ret < 0) {
- return ret;
- }
+ ret = source->ReadValue(&sparse_header, sizeof(sparse_header));
+ if (ret < 0) {
+ return ret;
+ }
- if (sparse_header.magic != SPARSE_HEADER_MAGIC) {
- return -EINVAL;
- }
+ if (sparse_header.magic != SPARSE_HEADER_MAGIC) {
+ return -EINVAL;
+ }
- if (sparse_header.major_version != SPARSE_HEADER_MAJOR_VER) {
- return -EINVAL;
- }
+ if (sparse_header.major_version != SPARSE_HEADER_MAJOR_VER) {
+ return -EINVAL;
+ }
- if (sparse_header.file_hdr_sz < SPARSE_HEADER_LEN) {
- return -EINVAL;
- }
+ if (sparse_header.file_hdr_sz < SPARSE_HEADER_LEN) {
+ return -EINVAL;
+ }
- if (sparse_header.chunk_hdr_sz < sizeof(chunk_header)) {
- return -EINVAL;
- }
+ if (sparse_header.chunk_hdr_sz < sizeof(chunk_header)) {
+ return -EINVAL;
+ }
- if (sparse_header.file_hdr_sz > SPARSE_HEADER_LEN) {
- /* Skip the remaining bytes in a header that is longer than
- * we expected.
- */
- lseek64(fd, sparse_header.file_hdr_sz - SPARSE_HEADER_LEN, SEEK_CUR);
- }
+ if (sparse_header.file_hdr_sz > SPARSE_HEADER_LEN) {
+ /* Skip the remaining bytes in a header that is longer than
+ * we expected.
+ */
+ source->Seek(sparse_header.file_hdr_sz - SPARSE_HEADER_LEN);
+ }
- for (i = 0; i < sparse_header.total_chunks; i++) {
- ret = read_all(fd, &chunk_header, sizeof(chunk_header));
- if (ret < 0) {
- return ret;
- }
+ for (i = 0; i < sparse_header.total_chunks; i++) {
+ ret = source->ReadValue(&chunk_header, sizeof(chunk_header));
+ if (ret < 0) {
+ return ret;
+ }
- if (sparse_header.chunk_hdr_sz > CHUNK_HEADER_LEN) {
- /* Skip the remaining bytes in a header that is longer than
- * we expected.
- */
- lseek64(fd, sparse_header.chunk_hdr_sz - CHUNK_HEADER_LEN, SEEK_CUR);
- }
+ if (sparse_header.chunk_hdr_sz > CHUNK_HEADER_LEN) {
+ /* Skip the remaining bytes in a header that is longer than
+ * we expected.
+ */
+ source->Seek(sparse_header.chunk_hdr_sz - CHUNK_HEADER_LEN);
+ }
- offset = lseek64(fd, 0, SEEK_CUR);
+ ret = process_chunk(s, source, sparse_header.chunk_hdr_sz, &chunk_header, cur_block, crc_ptr);
+ if (ret < 0) {
+ return ret;
+ }
- ret = process_chunk(s, fd, offset, sparse_header.chunk_hdr_sz, &chunk_header,
- cur_block, crc_ptr);
- if (ret < 0) {
- return ret;
- }
+ cur_block += ret;
+ }
- cur_block += ret;
- }
+ if (sparse_header.total_blks != cur_block) {
+ return -EINVAL;
+ }
- if (sparse_header.total_blks != cur_block) {
- return -EINVAL;
- }
-
- return 0;
+ 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);
- unsigned int block = 0;
- int64_t remain = s->len;
- int64_t offset = 0;
- unsigned int to_read;
- unsigned int i;
- bool sparse_block;
+static int sparse_file_read_normal(struct sparse_file* s, int fd) {
+ 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 to_read;
+ unsigned int i;
+ bool sparse_block;
- if (!buf) {
- return -ENOMEM;
- }
+ if (!buf) {
+ return -ENOMEM;
+ }
- while (remain > 0) {
- to_read = std::min(remain, (int64_t)(s->block_size));
- ret = read_all(fd, buf, to_read);
- if (ret < 0) {
- error("failed to read sparse file");
- free(buf);
- return ret;
- }
+ while (remain > 0) {
+ to_read = std::min(remain, (int64_t)(s->block_size));
+ ret = read_all(fd, buf, to_read);
+ if (ret < 0) {
+ error("failed to read sparse file");
+ free(buf);
+ return ret;
+ }
- if (to_read == s->block_size) {
- sparse_block = true;
- for (i = 1; i < s->block_size / sizeof(uint32_t); i++) {
- if (buf[0] != buf[i]) {
- sparse_block = false;
- break;
- }
- }
- } else {
- sparse_block = false;
- }
+ if (to_read == s->block_size) {
+ sparse_block = true;
+ for (i = 1; i < s->block_size / sizeof(uint32_t); i++) {
+ if (buf[0] != buf[i]) {
+ sparse_block = false;
+ break;
+ }
+ }
+ } else {
+ sparse_block = false;
+ }
- if (sparse_block) {
- /* TODO: add flag to use skip instead of fill for buf[0] == 0 */
- sparse_file_add_fill(s, buf[0], to_read, block);
- } else {
- sparse_file_add_fd(s, fd, offset, to_read, block);
- }
+ if (sparse_block) {
+ /* TODO: add flag to use skip instead of fill for buf[0] == 0 */
+ sparse_file_add_fill(s, buf[0], to_read, block);
+ } else {
+ sparse_file_add_fd(s, fd, offset, to_read, block);
+ }
- remain -= to_read;
- offset += to_read;
- block++;
- }
+ remain -= to_read;
+ offset += to_read;
+ block++;
+ }
- free(buf);
- return 0;
+ free(buf);
+ return 0;
}
-int sparse_file_read(struct sparse_file *s, int fd, bool sparse, bool crc)
-{
- if (crc && !sparse) {
- return -EINVAL;
- }
+int sparse_file_read(struct sparse_file* s, int fd, bool sparse, bool crc) {
+ if (crc && !sparse) {
+ return -EINVAL;
+ }
- if (sparse) {
- return sparse_file_read_sparse(s, fd, crc);
- } else {
- return sparse_file_read_normal(s, fd);
- }
+ if (sparse) {
+ SparseFileFdSource source(fd);
+ return sparse_file_read_sparse(s, &source, crc);
+ } else {
+ return sparse_file_read_normal(s, fd);
+ }
}
-struct sparse_file *sparse_file_import(int fd, bool verbose, bool crc)
-{
- int ret;
- sparse_header_t sparse_header;
- int64_t len;
- struct sparse_file *s;
-
- ret = read_all(fd, &sparse_header, sizeof(sparse_header));
- if (ret < 0) {
- verbose_error(verbose, ret, "header");
- return NULL;
- }
-
- if (sparse_header.magic != SPARSE_HEADER_MAGIC) {
- verbose_error(verbose, -EINVAL, "header magic");
- return NULL;
- }
-
- if (sparse_header.major_version != SPARSE_HEADER_MAJOR_VER) {
- verbose_error(verbose, -EINVAL, "header major version");
- return NULL;
- }
-
- if (sparse_header.file_hdr_sz < SPARSE_HEADER_LEN) {
- return NULL;
- }
-
- if (sparse_header.chunk_hdr_sz < sizeof(chunk_header_t)) {
- return NULL;
- }
-
- len = (int64_t)sparse_header.total_blks * sparse_header.blk_sz;
- s = sparse_file_new(sparse_header.blk_sz, len);
- if (!s) {
- verbose_error(verbose, -EINVAL, NULL);
- return NULL;
- }
-
- ret = lseek64(fd, 0, SEEK_SET);
- if (ret < 0) {
- verbose_error(verbose, ret, "seeking");
- sparse_file_destroy(s);
- return NULL;
- }
-
- s->verbose = verbose;
-
- ret = sparse_file_read(s, fd, true, crc);
- if (ret < 0) {
- sparse_file_destroy(s);
- return NULL;
- }
-
- return s;
+int sparse_file_read_buf(struct sparse_file* s, char* buf, bool crc) {
+ SparseFileBufSource source(buf);
+ return sparse_file_read_sparse(s, &source, crc);
}
-struct sparse_file *sparse_file_import_auto(int fd, bool crc, bool verbose)
-{
- struct sparse_file *s;
- int64_t len;
- int ret;
+static struct sparse_file* sparse_file_import_source(SparseFileSource* source, bool verbose,
+ bool crc) {
+ int ret;
+ sparse_header_t sparse_header;
+ int64_t len;
+ struct sparse_file* s;
- s = sparse_file_import(fd, verbose, crc);
- if (s) {
- return s;
- }
+ ret = source->ReadValue(&sparse_header, sizeof(sparse_header));
+ if (ret < 0) {
+ verbose_error(verbose, ret, "header");
+ return NULL;
+ }
- len = lseek64(fd, 0, SEEK_END);
- if (len < 0) {
- return NULL;
- }
+ if (sparse_header.magic != SPARSE_HEADER_MAGIC) {
+ verbose_error(verbose, -EINVAL, "header magic");
+ return NULL;
+ }
- lseek64(fd, 0, SEEK_SET);
+ if (sparse_header.major_version != SPARSE_HEADER_MAJOR_VER) {
+ verbose_error(verbose, -EINVAL, "header major version");
+ return NULL;
+ }
- s = sparse_file_new(4096, len);
- if (!s) {
- return NULL;
- }
+ if (sparse_header.file_hdr_sz < SPARSE_HEADER_LEN) {
+ return NULL;
+ }
- ret = sparse_file_read_normal(s, fd);
- if (ret < 0) {
- sparse_file_destroy(s);
- return NULL;
- }
+ if (sparse_header.chunk_hdr_sz < sizeof(chunk_header_t)) {
+ return NULL;
+ }
- return s;
+ len = (int64_t)sparse_header.total_blks * sparse_header.blk_sz;
+ s = sparse_file_new(sparse_header.blk_sz, len);
+ if (!s) {
+ verbose_error(verbose, -EINVAL, NULL);
+ return NULL;
+ }
+
+ ret = source->SetOffset(0);
+ if (ret < 0) {
+ verbose_error(verbose, ret, "seeking");
+ sparse_file_destroy(s);
+ return NULL;
+ }
+
+ s->verbose = verbose;
+
+ ret = sparse_file_read_sparse(s, source, crc);
+ if (ret < 0) {
+ sparse_file_destroy(s);
+ return NULL;
+ }
+
+ return s;
+}
+
+struct sparse_file* sparse_file_import(int fd, bool verbose, bool crc) {
+ SparseFileFdSource source(fd);
+ return sparse_file_import_source(&source, verbose, crc);
+}
+
+struct sparse_file* sparse_file_import_buf(char* buf, bool verbose, bool crc) {
+ SparseFileBufSource source(buf);
+ return sparse_file_import_source(&source, verbose, crc);
+}
+
+struct sparse_file* sparse_file_import_auto(int fd, bool crc, bool verbose) {
+ struct sparse_file* s;
+ int64_t len;
+ int ret;
+
+ s = sparse_file_import(fd, verbose, crc);
+ if (s) {
+ return s;
+ }
+
+ len = lseek64(fd, 0, SEEK_END);
+ if (len < 0) {
+ return NULL;
+ }
+
+ lseek64(fd, 0, SEEK_SET);
+
+ s = sparse_file_new(4096, len);
+ if (!s) {
+ return NULL;
+ }
+
+ ret = sparse_file_read_normal(s, fd);
+ if (ret < 0) {
+ sparse_file_destroy(s);
+ return NULL;
+ }
+
+ return s;
}
diff --git a/libsync/include/android/sync.h b/libsync/include/android/sync.h
index 68f74a0..32bb878 100644
--- a/libsync/include/android/sync.h
+++ b/libsync/include/android/sync.h
@@ -41,28 +41,8 @@
__BEGIN_DECLS
-struct sync_fence_info_data {
- uint32_t len;
- char name[32];
- int32_t status;
- uint8_t pt_info[0];
-};
-
-struct sync_pt_info {
- uint32_t len;
- char obj_name[32];
- char driver_name[32];
- int32_t status;
- uint64_t timestamp_ns;
- uint8_t driver_data[0];
-};
-
/* timeout in msecs */
int sync_wait(int fd, int timeout);
-struct sync_fence_info_data *sync_fence_info(int fd);
-struct sync_pt_info *sync_pt_info(struct sync_fence_info_data *info,
- struct sync_pt_info *itr);
-void sync_fence_info_free(struct sync_fence_info_data *info);
__END_DECLS
diff --git a/libsync/sync.c b/libsync/sync.c
index 6b187fa..b8c48c7 100644
--- a/libsync/sync.c
+++ b/libsync/sync.c
@@ -30,6 +30,29 @@
#include <android/sync.h>
+/* Prototypes for deprecated functions that used to be declared in the legacy
+ * android/sync.h. They've been moved here to make sure new code does not use
+ * them, but the functions are still defined to avoid breaking existing
+ * binaries. Eventually they can be removed altogether.
+ */
+struct sync_fence_info_data {
+ uint32_t len;
+ char name[32];
+ int32_t status;
+ uint8_t pt_info[0];
+};
+struct sync_pt_info {
+ uint32_t len;
+ char obj_name[32];
+ char driver_name[32];
+ int32_t status;
+ uint64_t timestamp_ns;
+ uint8_t driver_data[0];
+};
+struct sync_fence_info_data* sync_fence_info(int fd);
+struct sync_pt_info* sync_pt_info(struct sync_fence_info_data* info, struct sync_pt_info* itr);
+void sync_fence_info_free(struct sync_fence_info_data* info);
+
/* Legacy Sync API */
struct sync_legacy_merge_data {
diff --git a/libsync/tests/sync_test.cpp b/libsync/tests/sync_test.cpp
index 0fb86d6..011b09d 100644
--- a/libsync/tests/sync_test.cpp
+++ b/libsync/tests/sync_test.cpp
@@ -15,6 +15,35 @@
#include <random>
#include <unordered_map>
+/* These deprecated declarations were in the legacy android/sync.h. They've been removed to
+ * encourage code to move to the modern equivalents. But they are still implemented in libsync.so
+ * to avoid breaking existing binaries; as long as that's true we should keep testing them here.
+ * That means making local copies of the declarations.
+ */
+extern "C" {
+
+struct sync_fence_info_data {
+ uint32_t len;
+ char name[32];
+ int32_t status;
+ uint8_t pt_info[0];
+};
+
+struct sync_pt_info {
+ uint32_t len;
+ char obj_name[32];
+ char driver_name[32];
+ int32_t status;
+ uint64_t timestamp_ns;
+ uint8_t driver_data[0];
+};
+
+struct sync_fence_info_data* sync_fence_info(int fd);
+struct sync_pt_info* sync_pt_info(struct sync_fence_info_data* info, struct sync_pt_info* itr);
+void sync_fence_info_free(struct sync_fence_info_data* info);
+
+} // extern "C"
+
// TODO: better stress tests?
// Handle more than 64 fd's simultaneously, i.e. fix sync_fence_info's 4k limit.
// Handle wraparound in timelines like nvidia.
diff --git a/libutils/Android.bp b/libutils/Android.bp
index 9395ef8..bbfa9d8 100644
--- a/libutils/Android.bp
+++ b/libutils/Android.bp
@@ -47,6 +47,7 @@
cc_defaults {
name: "libutils_defaults",
vendor_available: true,
+ recovery_available: true,
vndk: {
enabled: true,
support_system_process: true,
@@ -90,6 +91,10 @@
},
},
+ recovery: {
+ exclude_shared_libs: ["libvndksupport"],
+ },
+
host: {
cflags: ["-DLIBUTILS_NATIVE=1"],
diff --git a/libutils/misc.cpp b/libutils/misc.cpp
index da28dfa..f074341 100644
--- a/libutils/misc.cpp
+++ b/libutils/misc.cpp
@@ -23,7 +23,7 @@
#include <utils/Log.h>
#include <utils/Vector.h>
-#if defined(__ANDROID__)
+#if defined(__ANDROID__) && !defined(__ANDROID_RECOVERY__)
#include <dlfcn.h>
#include <vndksupport/linker.h>
#endif
@@ -70,7 +70,7 @@
void add_sysprop_change_callback(sysprop_change_callback, int) {}
#endif
-#if defined(__ANDROID__)
+#if defined(__ANDROID__) && !defined(__ANDROID_RECOVERY__)
void (*get_report_sysprop_change_func())() {
void (*func)() = nullptr;
void* handle = android_load_sphal_library("libutils.so", RTLD_NOW);
@@ -85,7 +85,7 @@
void report_sysprop_change() {
do_report_sysprop_change();
-#if defined(__ANDROID__)
+#if defined(__ANDROID__) && !defined(__ANDROID_RECOVERY__)
// libutils.so is double loaded; from the default namespace and from the
// 'sphal' namespace. Redirect the sysprop change event to the other instance
// of libutils.so loaded in the 'sphal' namespace so that listeners attached
diff --git a/libziparchive/Android.bp b/libziparchive/Android.bp
index 6c06618..2606aa9 100644
--- a/libziparchive/Android.bp
+++ b/libziparchive/Android.bp
@@ -58,6 +58,7 @@
name: "libziparchive",
host_supported: true,
vendor_available: true,
+ recovery_available: true,
vndk: {
enabled: true,
},
diff --git a/logd/main.cpp b/logd/main.cpp
index 5a588be..b697d44 100644
--- a/logd/main.cpp
+++ b/logd/main.cpp
@@ -87,30 +87,25 @@
//
static int drop_privs(bool klogd, bool auditd) {
- // Tricky, if ro.build.type is "eng" then this is true because of the
- // side effect that ro.debuggable == 1 as well, else it is false.
- bool eng =
- __android_logger_property_get_bool("ro.build.type", BOOL_DEFAULT_FALSE);
-
- struct sched_param param;
- memset(¶m, 0, sizeof(param));
+ sched_param param = {};
if (set_sched_policy(0, SP_BACKGROUND) < 0) {
android::prdebug("failed to set background scheduling policy");
- if (!eng) return -1;
+ return -1;
}
if (sched_setscheduler((pid_t)0, SCHED_BATCH, ¶m) < 0) {
android::prdebug("failed to set batch scheduler");
- if (!eng) return -1;
+ return -1;
}
if (setpriority(PRIO_PROCESS, 0, ANDROID_PRIORITY_BACKGROUND) < 0) {
android::prdebug("failed to set background cgroup");
- if (!eng) return -1;
+ return -1;
}
- if (!eng && (prctl(PR_SET_DUMPABLE, 0) < 0)) {
+ if (__android_logger_property_get_bool("ro.debuggable", BOOL_DEFAULT_FALSE) &&
+ prctl(PR_SET_DUMPABLE, 0) == -1) {
android::prdebug("failed to clear PR_SET_DUMPABLE");
return -1;
}
@@ -133,24 +128,24 @@
android::prdebug(
"failed to set CAP_SETGID, CAP_SYSLOG or CAP_AUDIT_CONTROL (%d)",
errno);
- if (!eng) return -1;
+ return -1;
}
gid_t groups[] = { AID_READPROC };
if (setgroups(arraysize(groups), groups) == -1) {
android::prdebug("failed to set AID_READPROC groups");
- if (!eng) return -1;
+ return -1;
}
if (setgid(AID_LOGD) != 0) {
android::prdebug("failed to set AID_LOGD gid");
- if (!eng) return -1;
+ return -1;
}
if (setuid(AID_LOGD) != 0) {
android::prdebug("failed to set AID_LOGD uid");
- if (!eng) return -1;
+ return -1;
}
if (cap_set_flag(caps.get(), CAP_PERMITTED, 1, cap_value, CAP_CLEAR) < 0) {
@@ -161,7 +156,7 @@
}
if (cap_set_proc(caps.get()) < 0) {
android::prdebug("failed to clear CAP_SETGID (%d)", errno);
- if (!eng) return -1;
+ return -1;
}
return 0;
@@ -468,7 +463,7 @@
bool auditd =
__android_logger_property_get_bool("ro.logd.auditd", BOOL_DEFAULT_TRUE);
if (drop_privs(klogd, auditd) != 0) {
- return -1;
+ return EXIT_FAILURE;
}
// Serves the purpose of managing the last logs times read on a
@@ -496,7 +491,7 @@
LogReader* reader = new LogReader(logBuf);
if (reader->startListener()) {
- exit(1);
+ return EXIT_FAILURE;
}
// LogListener listens on /dev/socket/logdw for client
@@ -506,7 +501,7 @@
LogListener* swl = new LogListener(logBuf, reader);
// Backlog and /proc/sys/net/unix/max_dgram_qlen set to large value
if (swl->startListener(600)) {
- exit(1);
+ return EXIT_FAILURE;
}
// Command listener listens on /dev/socket/logd for incoming logd
@@ -514,7 +509,7 @@
CommandListener* cl = new CommandListener(logBuf, reader, swl);
if (cl->startListener()) {
- exit(1);
+ return EXIT_FAILURE;
}
// LogAudit listens on NETLINK_AUDIT socket for selinux
@@ -549,5 +544,5 @@
TEMP_FAILURE_RETRY(pause());
- exit(0);
+ return EXIT_SUCCESS;
}
diff --git a/rootdir/Android.mk b/rootdir/Android.mk
index 3c9e5f3..70b6a3e 100644
--- a/rootdir/Android.mk
+++ b/rootdir/Android.mk
@@ -306,6 +306,16 @@
_enforce_vndk_lite_at_runtime :=
#######################################
+# ld.config.txt for recovery
+include $(CLEAR_VARS)
+LOCAL_MODULE := ld.config.recovery.txt
+LOCAL_MODULE_CLASS := ETC
+LOCAL_SRC_FILES := etc/ld.config.recovery.txt
+LOCAL_MODULE_PATH := $(TARGET_RECOVERY_ROOT_OUT)/etc
+LOCAL_MODULE_STEM := ld.config.txt
+include $(BUILD_PREBUILT)
+
+#######################################
# llndk.libraries.txt
include $(CLEAR_VARS)
LOCAL_MODULE := llndk.libraries.txt
diff --git a/rootdir/etc/ld.config.recovery.txt b/rootdir/etc/ld.config.recovery.txt
new file mode 100644
index 0000000..5d6c01a
--- /dev/null
+++ b/rootdir/etc/ld.config.recovery.txt
@@ -0,0 +1,9 @@
+# Copyright (C) 2018 The Android Open Source Project
+#
+# Bionic loader config file for recovery mode
+#
+
+dir.recovery = /system/bin
+
+[recovery]
+namespace.default.search.paths = /system/${LIB}
diff --git a/shell_and_utilities/Android.bp b/shell_and_utilities/Android.bp
index 2e42b70..6438e3d 100644
--- a/shell_and_utilities/Android.bp
+++ b/shell_and_utilities/Android.bp
@@ -12,10 +12,13 @@
"mkshrc_vendor",
"reboot",
"sh",
+ "sh.recovery",
"sh_vendor",
"toolbox",
+ "toolbox.recovery",
"toolbox_vendor",
"toybox",
+ "toybox.recovery",
"toybox_vendor",
],
}
diff --git a/toolbox/Android.bp b/toolbox/Android.bp
index 077f542..f12c810 100644
--- a/toolbox/Android.bp
+++ b/toolbox/Android.bp
@@ -46,6 +46,7 @@
cc_binary {
name: "toolbox",
defaults: ["toolbox_binary_defaults"],
+ recovery_available: true,
}
cc_binary {