Merge "Fix memory leak when GateKeeperProxy.verify() returns"
diff --git a/adb/Android.bp b/adb/Android.bp
index 53bf7e3..97c9762 100644
--- a/adb/Android.bp
+++ b/adb/Android.bp
@@ -104,6 +104,7 @@
"socket_spec.cpp",
"sysdeps/errno.cpp",
"transport.cpp",
+ "transport_fd.cpp",
"transport_local.cpp",
"transport_usb.cpp",
]
diff --git a/adb/OVERVIEW.TXT b/adb/OVERVIEW.TXT
index 29a6992..f0b184c 100644
--- a/adb/OVERVIEW.TXT
+++ b/adb/OVERVIEW.TXT
@@ -103,9 +103,6 @@
4-byte hex length, followed by a string giving the reason
for failure.
- 3. As a special exception, for 'host:version', a 4-byte
- hex string corresponding to the server's internal version number
-
Note that the connection is still alive after an OKAY, which allows the
client to make other requests. But in certain cases, an OKAY will even
change the state of the connection.
diff --git a/adb/SERVICES.TXT b/adb/SERVICES.TXT
index 30c21f7..3e18a54 100644
--- a/adb/SERVICES.TXT
+++ b/adb/SERVICES.TXT
@@ -7,10 +7,6 @@
host:version
Ask the ADB server for its internal version number.
- As a special exception, the server will respond with a 4-byte
- hex string corresponding to its internal version number, without
- any OKAY or FAIL.
-
host:kill
Ask the ADB server to quit immediately. This is used when the
ADB client detects that an obsolete server is running after an
diff --git a/adb/client/commandline.cpp b/adb/client/commandline.cpp
index 06fcf16..e07dba7 100644
--- a/adb/client/commandline.cpp
+++ b/adb/client/commandline.cpp
@@ -1795,6 +1795,11 @@
}
else if (!strcmp(argv[0], "track-devices")) {
return adb_connect_command("host:track-devices");
+ } else if (!strcmp(argv[0], "raw")) {
+ if (argc != 2) {
+ return syntax_error("adb raw SERVICE");
+ }
+ return adb_connect_command(argv[1]);
}
diff --git a/adb/fdevent.cpp b/adb/fdevent.cpp
index 1b7758c..098a39d 100644
--- a/adb/fdevent.cpp
+++ b/adb/fdevent.cpp
@@ -21,6 +21,7 @@
#include "fdevent.h"
#include <fcntl.h>
+#include <inttypes.h>
#include <stdint.h>
#include <stdlib.h>
#include <string.h>
@@ -75,6 +76,8 @@
static bool main_thread_valid;
static uint64_t main_thread_id;
+static uint64_t fdevent_id;
+
static bool run_needs_flush = false;
static auto& run_queue_notify_fd = *new unique_fd();
static auto& run_queue_mutex = *new std::mutex();
@@ -111,7 +114,8 @@
if (fde->state & FDE_ERROR) {
state += "E";
}
- return android::base::StringPrintf("(fdevent %d %s)", fde->fd.get(), state.c_str());
+ return android::base::StringPrintf("(fdevent %" PRIu64 ": fd %d %s)", fde->id, fde->fd.get(),
+ state.c_str());
}
void fdevent_install(fdevent* fde, int fd, fd_func func, void* arg) {
@@ -125,6 +129,7 @@
CHECK_GE(fd, 0);
fdevent* fde = new fdevent();
+ fde->id = fdevent_id++;
fde->state = FDE_ACTIVE;
fde->fd.reset(fd);
fde->func = func;
diff --git a/adb/fdevent.h b/adb/fdevent.h
index 69c4072..d501b86 100644
--- a/adb/fdevent.h
+++ b/adb/fdevent.h
@@ -32,8 +32,7 @@
typedef void (*fd_func)(int fd, unsigned events, void *userdata);
struct fdevent {
- fdevent* next = nullptr;
- fdevent* prev = nullptr;
+ uint64_t id;
unique_fd fd;
int force_eof = 0;
diff --git a/adb/transport.h b/adb/transport.h
index ae9cc02..cb20615 100644
--- a/adb/transport.h
+++ b/adb/transport.h
@@ -92,6 +92,8 @@
std::string transport_name_;
ReadCallback read_callback_;
ErrorCallback error_callback_;
+
+ static std::unique_ptr<Connection> FromFd(unique_fd fd);
};
// Abstraction for a blocking packet transport.
diff --git a/adb/transport_benchmark.cpp b/adb/transport_benchmark.cpp
index da24aa7..022808f 100644
--- a/adb/transport_benchmark.cpp
+++ b/adb/transport_benchmark.cpp
@@ -24,13 +24,19 @@
#include "sysdeps.h"
#include "transport.h"
-#define ADB_CONNECTION_BENCHMARK(benchmark_name, ...) \
- BENCHMARK_TEMPLATE(benchmark_name, FdConnection, ##__VA_ARGS__) \
- ->Arg(1) \
- ->Arg(16384) \
- ->Arg(MAX_PAYLOAD) \
+#define ADB_CONNECTION_BENCHMARK(benchmark_name, ...) \
+ BENCHMARK_TEMPLATE(benchmark_name, FdConnection, ##__VA_ARGS__) \
+ ->Arg(1) \
+ ->Arg(16384) \
+ ->Arg(MAX_PAYLOAD) \
+ ->UseRealTime(); \
+ BENCHMARK_TEMPLATE(benchmark_name, NonblockingFdConnection, ##__VA_ARGS__) \
+ ->Arg(1) \
+ ->Arg(16384) \
+ ->Arg(MAX_PAYLOAD) \
->UseRealTime()
+struct NonblockingFdConnection;
template <typename ConnectionType>
std::unique_ptr<Connection> MakeConnection(unique_fd fd);
@@ -40,6 +46,11 @@
return std::make_unique<BlockingConnectionAdapter>(std::move(fd_connection));
}
+template <>
+std::unique_ptr<Connection> MakeConnection<NonblockingFdConnection>(unique_fd fd) {
+ return Connection::FromFd(std::move(fd));
+}
+
template <typename ConnectionType>
void BM_Connection_Unidirectional(benchmark::State& state) {
int fds[2];
diff --git a/adb/transport_fd.cpp b/adb/transport_fd.cpp
new file mode 100644
index 0000000..85f3c52
--- /dev/null
+++ b/adb/transport_fd.cpp
@@ -0,0 +1,239 @@
+/*
+ * 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 <stdint.h>
+
+#include <deque>
+#include <mutex>
+#include <string>
+#include <thread>
+
+#include <android-base/logging.h>
+#include <android-base/stringprintf.h>
+#include <android-base/thread_annotations.h>
+
+#include "adb_unique_fd.h"
+#include "adb_utils.h"
+#include "sysdeps.h"
+#include "sysdeps/memory.h"
+#include "transport.h"
+#include "types.h"
+
+static void CreateWakeFds(unique_fd* read, unique_fd* write) {
+ // TODO: eventfd on linux?
+ int wake_fds[2];
+ int rc = adb_socketpair(wake_fds);
+ set_file_block_mode(wake_fds[0], false);
+ set_file_block_mode(wake_fds[1], false);
+ CHECK_EQ(0, rc);
+ *read = unique_fd(wake_fds[0]);
+ *write = unique_fd(wake_fds[1]);
+}
+
+struct NonblockingFdConnection : public Connection {
+ NonblockingFdConnection(unique_fd fd) : started_(false), fd_(std::move(fd)) {
+ set_file_block_mode(fd_.get(), false);
+ CreateWakeFds(&wake_fd_read_, &wake_fd_write_);
+ }
+
+ void SetRunning(bool value) {
+ std::lock_guard<std::mutex> lock(run_mutex_);
+ running_ = value;
+ }
+
+ bool IsRunning() {
+ std::lock_guard<std::mutex> lock(run_mutex_);
+ return running_;
+ }
+
+ void Run(std::string* error) {
+ SetRunning(true);
+ while (IsRunning()) {
+ adb_pollfd pfds[2] = {
+ {.fd = fd_.get(), .events = POLLIN},
+ {.fd = wake_fd_read_.get(), .events = POLLIN},
+ };
+
+ {
+ std::lock_guard<std::mutex> lock(this->write_mutex_);
+ if (!writable_) {
+ pfds[0].events |= POLLOUT;
+ }
+ }
+
+ int rc = adb_poll(pfds, 2, -1);
+ if (rc == -1) {
+ *error = android::base::StringPrintf("poll failed: %s", strerror(errno));
+ return;
+ } else if (rc == 0) {
+ LOG(FATAL) << "poll timed out with an infinite timeout?";
+ }
+
+ if (pfds[0].revents) {
+ if ((pfds[0].revents & POLLOUT)) {
+ std::lock_guard<std::mutex> lock(this->write_mutex_);
+ WriteResult result = DispatchWrites();
+ switch (result) {
+ case WriteResult::Error:
+ *error = "write failed";
+ return;
+
+ case WriteResult::Completed:
+ writable_ = true;
+ break;
+
+ case WriteResult::TryAgain:
+ break;
+ }
+ }
+
+ if (pfds[0].revents & POLLIN) {
+ // TODO: Should we be getting blocks from a free list?
+ auto block = std::make_unique<IOVector::block_type>(MAX_PAYLOAD);
+ rc = adb_read(fd_.get(), &(*block)[0], block->size());
+ if (rc == -1) {
+ *error = std::string("read failed: ") + strerror(errno);
+ return;
+ } else if (rc == 0) {
+ *error = "read failed: EOF";
+ return;
+ }
+ block->resize(rc);
+ read_buffer_.append(std::move(block));
+
+ if (!read_header_ && read_buffer_.size() >= sizeof(amessage)) {
+ auto header_buf = read_buffer_.take_front(sizeof(amessage)).coalesce();
+ CHECK_EQ(sizeof(amessage), header_buf.size());
+ read_header_ = std::make_unique<amessage>();
+ memcpy(read_header_.get(), header_buf.data(), sizeof(amessage));
+ }
+
+ if (read_header_ && read_buffer_.size() >= read_header_->data_length) {
+ auto data_chain = read_buffer_.take_front(read_header_->data_length);
+
+ // TODO: Make apacket carry around a IOVector instead of coalescing.
+ auto payload = data_chain.coalesce<apacket::payload_type>();
+ auto packet = std::make_unique<apacket>();
+ packet->msg = *read_header_;
+ packet->payload = std::move(payload);
+ read_header_ = nullptr;
+ read_callback_(this, std::move(packet));
+ }
+ }
+ }
+
+ if (pfds[1].revents) {
+ uint64_t buf;
+ rc = adb_read(wake_fd_read_.get(), &buf, sizeof(buf));
+ CHECK_EQ(static_cast<int>(sizeof(buf)), rc);
+
+ // We were woken up either to add POLLOUT to our events, or to exit.
+ // Do nothing.
+ }
+ }
+ }
+
+ void Start() override final {
+ if (started_.exchange(true)) {
+ LOG(FATAL) << "Connection started multiple times?";
+ }
+
+ thread_ = std::thread([this]() {
+ std::string error = "connection closed";
+ Run(&error);
+ this->error_callback_(this, error);
+ });
+ }
+
+ void Stop() override final {
+ SetRunning(false);
+ WakeThread();
+ thread_.join();
+ }
+
+ void WakeThread() {
+ uint64_t buf = 0;
+ if (TEMP_FAILURE_RETRY(adb_write(wake_fd_write_.get(), &buf, sizeof(buf))) != sizeof(buf)) {
+ LOG(FATAL) << "failed to wake up thread";
+ }
+ }
+
+ enum class WriteResult {
+ Error,
+ Completed,
+ TryAgain,
+ };
+
+ WriteResult DispatchWrites() REQUIRES(write_mutex_) {
+ CHECK(!write_buffer_.empty());
+ if (!writable_) {
+ return WriteResult::TryAgain;
+ }
+
+ auto iovs = write_buffer_.iovecs();
+ ssize_t rc = adb_writev(fd_.get(), iovs.data(), iovs.size());
+ if (rc == -1) {
+ return WriteResult::Error;
+ } else if (rc == 0) {
+ errno = 0;
+ return WriteResult::Error;
+ }
+
+ // TODO: Implement a more efficient drop_front?
+ write_buffer_.take_front(rc);
+ if (write_buffer_.empty()) {
+ return WriteResult::Completed;
+ }
+
+ // There's data left in the range, which means our write returned early.
+ return WriteResult::TryAgain;
+ }
+
+ bool Write(std::unique_ptr<apacket> packet) final {
+ std::lock_guard<std::mutex> lock(write_mutex_);
+ const char* header_begin = reinterpret_cast<const char*>(&packet->msg);
+ const char* header_end = header_begin + sizeof(packet->msg);
+ auto header_block = std::make_unique<IOVector::block_type>(header_begin, header_end);
+ write_buffer_.append(std::move(header_block));
+ if (!packet->payload.empty()) {
+ write_buffer_.append(std::make_unique<IOVector::block_type>(std::move(packet->payload)));
+ }
+ return DispatchWrites() != WriteResult::Error;
+ }
+
+ std::thread thread_;
+
+ std::atomic<bool> started_;
+ std::mutex run_mutex_;
+ bool running_ GUARDED_BY(run_mutex_);
+
+ std::unique_ptr<amessage> read_header_;
+ IOVector read_buffer_;
+
+ unique_fd fd_;
+ unique_fd wake_fd_read_;
+ unique_fd wake_fd_write_;
+
+ std::mutex write_mutex_;
+ bool writable_ GUARDED_BY(write_mutex_) = true;
+ IOVector write_buffer_ GUARDED_BY(write_mutex_);
+
+ IOVector incoming_queue_;
+};
+
+std::unique_ptr<Connection> Connection::FromFd(unique_fd fd) {
+ return std::make_unique<NonblockingFdConnection>(std::move(fd));
+}
diff --git a/bootstat/Android.bp b/bootstat/Android.bp
index dd9ba88..8fc2171 100644
--- a/bootstat/Android.bp
+++ b/bootstat/Android.bp
@@ -63,7 +63,6 @@
name: "bootstat",
defaults: ["bootstat_defaults"],
static_libs: ["libbootstat"],
- shared_libs: ["liblogcat"],
init_rc: ["bootstat.rc"],
product_variables: {
pdk: {
diff --git a/bootstat/bootstat.cpp b/bootstat/bootstat.cpp
index 9fe25fd..7ec57ec 100644
--- a/bootstat/bootstat.cpp
+++ b/bootstat/bootstat.cpp
@@ -43,7 +43,6 @@
#include <android/log.h>
#include <cutils/android_reboot.h>
#include <cutils/properties.h>
-#include <log/logcat.h>
#include <metricslogger/metrics_logger.h>
#include "boot_event_record_store.h"
@@ -870,86 +869,7 @@
}
}
- // The following battery test should migrate to a default system health HAL
-
- // Let us not worry if the reboot command was issued, for the cases of
- // reboot -p, reboot <no reason>, reboot cold, reboot warm and reboot hard.
- // Same for bootloader and ro.boot.bootreasons of this set, but a dead
- // battery could conceivably lead to these, so worthy of override.
- if (isBluntRebootReason(ret)) {
- // Heuristic to determine if shutdown possibly because of a dead battery?
- // Really a hail-mary pass to find it in last klog content ...
- static const int battery_dead_threshold = 2; // percent
- static const char battery[] = "healthd: battery l=";
- const pstoreConsole console(content);
- size_t pos = console.rfind(battery); // last one
- std::string digits;
- if (pos != std::string::npos) {
- digits = content.substr(pos + strlen(battery), strlen("100 "));
- // correct common errors
- correctForBitError(digits, "100 ");
- if (digits[0] == '!') digits[0] = '1';
- if (digits[1] == '!') digits[1] = '1';
- }
- const char* endptr = digits.c_str();
- unsigned level = 0;
- while (::isdigit(*endptr)) {
- level *= 10;
- level += *endptr++ - '0';
- // make sure no leading zeros, except zero itself, and range check.
- if ((level == 0) || (level > 100)) break;
- }
- // example bit error rate issues for 10%
- // 'l=10 ' no bits in error
- // 'l=00 ' single bit error (fails above)
- // 'l=1 ' single bit error
- // 'l=0 ' double bit error
- // There are others, not typically critical because of 2%
- // battery_dead_threshold. KISS check, make sure second
- // character after digit sequence is not a space.
- if ((level <= 100) && (endptr != digits.c_str()) && (endptr[0] == ' ') && (endptr[1] != ' ')) {
- LOG(INFO) << "Battery level at shutdown " << level << "%";
- if (level <= battery_dead_threshold) {
- ret = "shutdown,battery";
- }
- } else { // Most likely
- digits = ""; // reset digits
-
- // Content buffer no longer will have console data. Beware if more
- // checks added below, that depend on parsing console content.
- content = "";
-
- LOG(DEBUG) << "Can not find last low battery in last console messages";
- android_logcat_context ctx = create_android_logcat();
- FILE* fp = android_logcat_popen(&ctx, "logcat -b kernel -v brief -d");
- if (fp != nullptr) {
- android::base::ReadFdToString(fileno(fp), &content);
- }
- android_logcat_pclose(&ctx, fp);
- static const char logcat_battery[] = "W/healthd ( 0): battery l=";
-
- pos = content.find(logcat_battery); // The first one it finds.
- if (pos != std::string::npos) {
- digits = content.substr(pos + strlen(logcat_battery), strlen("100 "));
- }
- endptr = digits.c_str();
- level = 0;
- while (::isdigit(*endptr)) {
- level *= 10;
- level += *endptr++ - '0';
- // make sure no leading zeros, except zero itself, and range check.
- if ((level == 0) || (level > 100)) break;
- }
- if ((level <= 100) && (endptr != digits.c_str()) && (*endptr == ' ')) {
- LOG(INFO) << "Battery level at startup " << level << "%";
- if (level <= battery_dead_threshold) {
- ret = "shutdown,battery";
- }
- } else {
- LOG(DEBUG) << "Can not find first battery level in dmesg or logcat";
- }
- }
- }
+ // TODO: use the HAL to get battery level (http://b/77725702).
// Is there a controlled shutdown hint in last_reboot_reason_property?
if (isBluntRebootReason(ret)) {
diff --git a/fs_mgr/Android.bp b/fs_mgr/Android.bp
index 923442f..b0b4839 100644
--- a/fs_mgr/Android.bp
+++ b/fs_mgr/Android.bp
@@ -57,12 +57,15 @@
"libavb",
"libfstab",
"libdm",
+ "liblp",
],
export_static_lib_headers: [
"libfstab",
"libdm",
+ "liblp",
],
whole_static_libs: [
+ "liblp",
"liblogwrap",
"libdm",
"libfstab",
diff --git a/fs_mgr/fs_mgr_dm_linear.cpp b/fs_mgr/fs_mgr_dm_linear.cpp
index b2f3a68..5159b4c 100644
--- a/fs_mgr/fs_mgr_dm_linear.cpp
+++ b/fs_mgr/fs_mgr_dm_linear.cpp
@@ -37,95 +37,35 @@
#include <android-base/stringprintf.h>
#include <android-base/strings.h>
#include <android-base/unique_fd.h>
+#include <liblp/reader.h>
#include "fs_mgr_priv.h"
-#include "fs_mgr_priv_dm_ioctl.h"
namespace android {
namespace fs_mgr {
-std::string LogicalPartitionExtent::Serialize() const {
- // Note: we need to include an explicit null-terminator.
- std::string argv =
- android::base::StringPrintf("%s %" PRIu64, block_device_.c_str(), first_sector_);
- argv.push_back(0);
+using DeviceMapper = android::dm::DeviceMapper;
+using DmTable = android::dm::DmTable;
+using DmTarget = android::dm::DmTarget;
+using DmTargetZero = android::dm::DmTargetZero;
+using DmTargetLinear = android::dm::DmTargetLinear;
- // The kernel expects each target to be aligned.
- size_t spec_bytes = sizeof(struct dm_target_spec) + argv.size();
- size_t padding = ((spec_bytes + 7) & ~7) - spec_bytes;
- for (size_t i = 0; i < padding; i++) {
- argv.push_back(0);
- }
-
- struct dm_target_spec spec;
- spec.sector_start = logical_sector_;
- spec.length = num_sectors_;
- spec.status = 0;
- strcpy(spec.target_type, "linear");
- spec.next = sizeof(struct dm_target_spec) + argv.size();
-
- return std::string((char*)&spec, sizeof(spec)) + argv;
-}
-
-static bool LoadDmTable(int dm_fd, const LogicalPartition& partition) {
- // Combine all dm_target_spec buffers together.
- std::string target_string;
+static bool CreateDmDeviceForPartition(DeviceMapper& dm, const LogicalPartition& partition) {
+ DmTable table;
for (const auto& extent : partition.extents) {
- target_string += extent.Serialize();
+ table.AddTarget(std::make_unique<DmTargetLinear>(extent));
}
-
- // Allocate the ioctl buffer.
- size_t buffer_size = sizeof(struct dm_ioctl) + target_string.size();
- std::unique_ptr<uint8_t[]> buffer = std::make_unique<uint8_t[]>(buffer_size);
-
- // Initialize the ioctl buffer header, then copy our target specs in.
- struct dm_ioctl* io = reinterpret_cast<struct dm_ioctl*>(buffer.get());
- fs_mgr_dm_ioctl_init(io, buffer_size, partition.name);
- io->target_count = partition.extents.size();
- if (partition.attributes & kPartitionReadonly) {
- io->flags |= DM_READONLY_FLAG;
- }
- memcpy(io + 1, target_string.c_str(), target_string.size());
-
- if (ioctl(dm_fd, DM_TABLE_LOAD, io)) {
- PERROR << "Failed ioctl() on DM_TABLE_LOAD, partition " << partition.name;
+ if (!dm.CreateDevice(partition.name, table)) {
return false;
}
- return true;
-}
-
-static bool LoadTablesAndActivate(int dm_fd, const LogicalPartition& partition) {
- if (!LoadDmTable(dm_fd, partition)) {
- return false;
- }
-
- struct dm_ioctl io;
- return fs_mgr_dm_resume_table(&io, partition.name, dm_fd);
-}
-
-static bool CreateDmDeviceForPartition(int dm_fd, const LogicalPartition& partition) {
- struct dm_ioctl io;
- if (!fs_mgr_dm_create_device(&io, partition.name, dm_fd)) {
- return false;
- }
- if (!LoadTablesAndActivate(dm_fd, partition)) {
- // Remove the device rather than leave it in an inactive state.
- fs_mgr_dm_destroy_device(&io, partition.name, dm_fd);
- return false;
- }
-
LINFO << "Created device-mapper device: " << partition.name;
return true;
}
bool CreateLogicalPartitions(const LogicalPartitionTable& table) {
- android::base::unique_fd dm_fd(open("/dev/device-mapper", O_RDWR));
- if (dm_fd < 0) {
- PLOG(ERROR) << "failed to open /dev/device-mapper";
- return false;
- }
+ DeviceMapper& dm = DeviceMapper::Instance();
for (const auto& partition : table.partitions) {
- if (!CreateDmDeviceForPartition(dm_fd, partition)) {
+ if (!CreateDmDeviceForPartition(dm, partition)) {
LOG(ERROR) << "could not create dm-linear device for partition: " << partition.name;
return false;
}
@@ -137,5 +77,59 @@
return nullptr;
}
+static bool CreateDmTable(const std::string& block_device, const LpMetadata& metadata,
+ const LpMetadataPartition& partition, DmTable* table) {
+ uint64_t sector = 0;
+ for (size_t i = 0; i < partition.num_extents; i++) {
+ const auto& extent = metadata.extents[partition.first_extent_index + i];
+ std::unique_ptr<DmTarget> target;
+ switch (extent.target_type) {
+ case LP_TARGET_TYPE_ZERO:
+ target = std::make_unique<DmTargetZero>(sector, extent.num_sectors);
+ break;
+ case LP_TARGET_TYPE_LINEAR:
+ target = std::make_unique<DmTargetLinear>(sector, extent.num_sectors, block_device,
+ extent.target_data);
+ break;
+ default:
+ LOG(ERROR) << "Unknown target type in metadata: " << extent.target_type;
+ return false;
+ }
+ if (!table->AddTarget(std::move(target))) {
+ return false;
+ }
+ sector += extent.num_sectors;
+ }
+ if (partition.attributes & LP_PARTITION_ATTR_READONLY) {
+ table->set_readonly(true);
+ }
+ return true;
+}
+
+bool CreateLogicalPartitions(const std::string& block_device) {
+ uint32_t slot = SlotNumberForSlotSuffix(fs_mgr_get_slot_suffix());
+ auto metadata = ReadMetadata(block_device.c_str(), slot);
+ if (!metadata) {
+ LOG(ERROR) << "Could not read partition table.";
+ return true;
+ }
+
+ DeviceMapper& dm = DeviceMapper::Instance();
+ for (const auto& partition : metadata->partitions) {
+ DmTable table;
+ if (!CreateDmTable(block_device, *metadata.get(), partition, &table)) {
+ return false;
+ }
+ std::string name = GetPartitionName(partition);
+ if (!dm.CreateDevice(name, table)) {
+ return false;
+ }
+ std::string path;
+ dm.GetDmDevicePathByName(partition.name, &path);
+ LINFO << "Created logical partition " << name << " on device " << path;
+ }
+ return true;
+}
+
} // namespace fs_mgr
} // namespace android
diff --git a/fs_mgr/include/fs_mgr_dm_linear.h b/fs_mgr/include/fs_mgr_dm_linear.h
index 7f928e5..3b0c791 100644
--- a/fs_mgr/include/fs_mgr_dm_linear.h
+++ b/fs_mgr/include/fs_mgr_dm_linear.h
@@ -26,53 +26,20 @@
#define __CORE_FS_MGR_DM_LINEAR_H
#include <stdint.h>
+
#include <memory>
#include <string>
#include <vector>
+#include <libdm/dm.h>
+#include <liblp/metadata_format.h>
+
namespace android {
namespace fs_mgr {
-static const uint32_t kPartitionReadonly = 0x1;
-
-class LogicalPartitionExtent {
- public:
- LogicalPartitionExtent() : logical_sector_(0), first_sector_(0), num_sectors_(0) {}
- LogicalPartitionExtent(uint64_t logical_sector, uint64_t first_sector, uint64_t num_sectors,
- const std::string& block_device)
- : logical_sector_(logical_sector),
- first_sector_(first_sector),
- num_sectors_(num_sectors),
- block_device_(block_device) {}
-
- // Return a string containing the dm_target_spec buffer needed to use this
- // extent in a device-mapper table.
- std::string Serialize() const;
-
- const std::string& block_device() const { return block_device_; }
-
- private:
- // Logical sector this extent represents in the presented block device.
- // This is equal to the previous extent's logical sector plus the number
- // of sectors in that extent. The first extent always starts at 0.
- uint64_t logical_sector_;
- // First 512-byte sector of this extent, on the source block device.
- uint64_t first_sector_;
- // Number of 512-byte sectors.
- uint64_t num_sectors_;
- // Target block device.
- std::string block_device_;
-};
-
struct LogicalPartition {
- LogicalPartition() : attributes(0), num_sectors(0) {}
-
std::string name;
- uint32_t attributes;
- // Number of 512-byte sectors total.
- uint64_t num_sectors;
- // List of extents.
- std::vector<LogicalPartitionExtent> extents;
+ std::vector<android::dm::DmTargetLinear> extents;
};
struct LogicalPartitionTable {
@@ -92,6 +59,7 @@
// /dev/block/dm-<name> where |name| is the partition name.
//
bool CreateLogicalPartitions(const LogicalPartitionTable& table);
+bool CreateLogicalPartitions(const std::string& block_device);
} // namespace fs_mgr
} // namespace android
diff --git a/fs_mgr/libdm/Android.bp b/fs_mgr/libdm/Android.bp
index 672d401..22af123 100644
--- a/fs_mgr/libdm/Android.bp
+++ b/fs_mgr/libdm/Android.bp
@@ -16,6 +16,7 @@
cc_library_static {
name: "libdm",
+ defaults: ["fs_mgr_defaults"],
recovery_available: true,
export_include_dirs: ["include"],
@@ -27,7 +28,8 @@
srcs: [
"dm_table.cpp",
"dm_target.cpp",
- "dm.cpp"
+ "dm.cpp",
+ "loop_control.cpp",
],
header_libs: [
@@ -35,3 +37,18 @@
"liblog_headers",
],
}
+
+cc_test {
+ name: "libdm_test",
+ defaults: ["fs_mgr_defaults"],
+ static_libs: [
+ "libdm",
+ "libbase",
+ "liblog",
+ ],
+ srcs: [
+ "dm_test.cpp",
+ "loop_control_test.cpp",
+ "test_util.cpp",
+ ]
+}
diff --git a/fs_mgr/libdm/dm.cpp b/fs_mgr/libdm/dm.cpp
index 57c1270..b96f4c1 100644
--- a/fs_mgr/libdm/dm.cpp
+++ b/fs_mgr/libdm/dm.cpp
@@ -14,23 +14,13 @@
* limitations under the License.
*/
-#include <errno.h>
-#include <fcntl.h>
-#include <linux/dm-ioctl.h>
-#include <stdint.h>
+#include "libdm/dm.h"
+
#include <sys/ioctl.h>
+#include <sys/sysmacros.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 {
@@ -51,23 +41,18 @@
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);
+ struct dm_ioctl io;
+ InitIo(&io, name);
- if (ioctl(fd_, DM_DEV_CREATE, io.get())) {
- PLOG(ERROR) << "DM_DEV_CREATE failed to create [" << name << "]";
+ if (ioctl(fd_, DM_DEV_CREATE, &io)) {
+ PLOG(ERROR) << "DM_DEV_CREATE failed for [" << 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";
+ 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;
@@ -84,22 +69,17 @@
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);
+ struct dm_ioctl io;
+ InitIo(&io, name);
- if (ioctl(fd_, DM_DEV_REMOVE, io.get())) {
- PLOG(ERROR) << "DM_DEV_REMOVE failed to create [" << name << "]";
+ if (ioctl(fd_, DM_DEV_REMOVE, &io)) {
+ PLOG(ERROR) << "DM_DEV_REMOVE failed for [" << 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)
+ CHECK(io.flags & DM_UEVENT_GENERATED_FLAG)
<< "Didn't generate uevent for [" << name << "] removal";
return true;
@@ -115,13 +95,45 @@
return DmDeviceState::INVALID;
}
-bool DeviceMapper::LoadTableAndActivate(const std::string& /* name */, const DmTable& /* table */) {
- return false;
+bool DeviceMapper::CreateDevice(const std::string& name, const DmTable& table) {
+ if (!CreateDevice(name)) {
+ return false;
+ }
+ if (!LoadTableAndActivate(name, table)) {
+ DeleteDevice(name);
+ return false;
+ }
+ return true;
+}
+
+bool DeviceMapper::LoadTableAndActivate(const std::string& name, const DmTable& table) {
+ std::string ioctl_buffer(sizeof(struct dm_ioctl), 0);
+ ioctl_buffer += table.Serialize();
+
+ struct dm_ioctl* io = reinterpret_cast<struct dm_ioctl*>(&ioctl_buffer[0]);
+ InitIo(io, name);
+ io->data_size = ioctl_buffer.size();
+ io->data_start = sizeof(struct dm_ioctl);
+ io->target_count = static_cast<uint32_t>(table.num_targets());
+ if (table.readonly()) {
+ io->flags |= DM_READONLY_FLAG;
+ }
+ if (ioctl(fd_, DM_TABLE_LOAD, io)) {
+ PLOG(ERROR) << "DM_TABLE_LOAD failed";
+ return false;
+ }
+
+ InitIo(io, name);
+ if (ioctl(fd_, DM_DEV_SUSPEND, io)) {
+ PLOG(ERROR) << "DM_TABLE_SUSPEND resume failed";
+ return false;
+ }
+ return true;
}
// 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) {
+bool DeviceMapper::GetAvailableTargets(std::vector<DmTargetTypeInfo>* targets) {
targets->clear();
// calculate the space needed to read a maximum of kMaxPossibleDmTargets
@@ -147,7 +159,7 @@
io->data_start = sizeof(*io);
if (ioctl(fd_, DM_LIST_VERSIONS, io)) {
- PLOG(ERROR) << "Failed to get DM_LIST_VERSIONS from kernel";
+ PLOG(ERROR) << "DM_LIST_VERSIONS failed";
return false;
}
@@ -170,7 +182,7 @@
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));
+ targets->emplace_back(vers);
if (vers->next == 0) {
break;
}
@@ -209,7 +221,7 @@
io->data_start = sizeof(*io);
if (ioctl(fd_, DM_LIST_DEVICES, io)) {
- PLOG(ERROR) << "Failed to get DM_LIST_DEVICES from kernel";
+ PLOG(ERROR) << "DM_LIST_DEVICES failed";
return false;
}
@@ -247,8 +259,17 @@
// 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 "";
+bool DeviceMapper::GetDmDevicePathByName(const std::string& name, std::string* path) {
+ struct dm_ioctl io;
+ InitIo(&io, name);
+ if (ioctl(fd_, DM_DEV_STATUS, &io) < 0) {
+ PLOG(ERROR) << "DM_DEV_STATUS failed for " << name;
+ return false;
+ }
+
+ uint32_t dev_num = minor(io.dev);
+ *path = "/dev/block/dm-" + std::to_string(dev_num);
+ return true;
}
// private methods of DeviceMapper
diff --git a/fs_mgr/libdm/dm_table.cpp b/fs_mgr/libdm/dm_table.cpp
index 14b3932..cb6f210 100644
--- a/fs_mgr/libdm/dm_table.cpp
+++ b/fs_mgr/libdm/dm_table.cpp
@@ -14,18 +14,16 @@
* limitations under the License.
*/
+#include "libdm/dm_table.h"
+
#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 */) {
+bool DmTable::AddTarget(std::unique_ptr<DmTarget>&& target) {
+ targets_.push_back(std::move(target));
return true;
}
@@ -34,21 +32,37 @@
}
bool DmTable::valid() const {
+ if (targets_.empty()) {
+ LOG(ERROR) << "Device-mapper table must have at least one target.";
+ return "";
+ }
+ if (targets_[0]->start() != 0) {
+ LOG(ERROR) << "Device-mapper table must start at logical sector 0.";
+ return "";
+ }
return true;
}
-uint64_t DmTable::size() const {
- return valid() ? size_ : 0;
+uint64_t DmTable::num_sectors() const {
+ return valid() ? num_sectors_ : 0;
}
-// Returns a string represnetation of the table that is ready to be passed
-// down to the kernel for loading
+// Returns a string representation 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 "";
+ if (!valid()) {
+ return "";
+ }
+
+ std::string table;
+ for (const auto& target : targets_) {
+ table += target->Serialize();
+ }
+ return table;
}
} // namespace dm
diff --git a/fs_mgr/libdm/dm_target.cpp b/fs_mgr/libdm/dm_target.cpp
index 8bcd526..5934416 100644
--- a/fs_mgr/libdm/dm_target.cpp
+++ b/fs_mgr/libdm/dm_target.cpp
@@ -14,16 +14,46 @@
* limitations under the License.
*/
+#include "libdm/dm_target.h"
+
#include <android-base/logging.h>
#include <android-base/macros.h>
-#include <stdint.h>
-
-#include <string>
-#include <vector>
-
-#include "dm_target.h"
+#include <libdm/dm.h>
namespace android {
-namespace dm {} // namespace dm
+namespace dm {
+
+std::string DmTarget::Serialize() const {
+ // Create a string containing a dm_target_spec, parameter data, and an
+ // explicit null terminator.
+ std::string data(sizeof(dm_target_spec), '\0');
+ data += GetParameterString();
+ data.push_back('\0');
+
+ // The kernel expects each target to be 8-byte aligned.
+ size_t padding = DM_ALIGN(data.size()) - data.size();
+ for (size_t i = 0; i < padding; i++) {
+ data.push_back('\0');
+ }
+
+ // Finally fill in the dm_target_spec.
+ struct dm_target_spec* spec = reinterpret_cast<struct dm_target_spec*>(&data[0]);
+ spec->sector_start = start();
+ spec->length = size();
+ strlcpy(spec->target_type, name().c_str(), sizeof(spec->target_type));
+ spec->next = (uint32_t)data.size();
+ return data;
+}
+
+std::string DmTargetZero::GetParameterString() const {
+ // The zero target type has no additional parameters.
+ return "";
+}
+
+std::string DmTargetLinear::GetParameterString() const {
+ return block_device_ + " " + std::to_string(physical_sector_);
+}
+
+} // namespace dm
} // namespace android
diff --git a/fs_mgr/libdm/dm_test.cpp b/fs_mgr/libdm/dm_test.cpp
new file mode 100644
index 0000000..67dc958
--- /dev/null
+++ b/fs_mgr/libdm/dm_test.cpp
@@ -0,0 +1,166 @@
+/*
+ * 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 <fcntl.h>
+#include <stdint.h>
+#include <stdio.h>
+#include <sys/ioctl.h>
+#include <sys/types.h>
+#include <time.h>
+#include <unistd.h>
+
+#include <chrono>
+#include <ctime>
+#include <map>
+#include <thread>
+
+#include <android-base/file.h>
+#include <android-base/unique_fd.h>
+#include <gtest/gtest.h>
+#include <libdm/dm.h>
+#include <libdm/loop_control.h>
+#include "test_util.h"
+
+using namespace std;
+using namespace android::dm;
+using unique_fd = android::base::unique_fd;
+
+TEST(libdm, HasMinimumTargets) {
+ DeviceMapper& dm = DeviceMapper::Instance();
+ vector<DmTargetTypeInfo> targets;
+ ASSERT_TRUE(dm.GetAvailableTargets(&targets));
+
+ map<string, DmTargetTypeInfo> by_name;
+ for (const auto& target : targets) {
+ by_name[target.name()] = target;
+ }
+
+ auto iter = by_name.find("linear");
+ EXPECT_NE(iter, by_name.end());
+}
+
+// Helper to ensure that device mapper devices are released.
+class TempDevice {
+ public:
+ TempDevice(const std::string& name, const DmTable& table)
+ : dm_(DeviceMapper::Instance()), name_(name), valid_(false) {
+ valid_ = dm_.CreateDevice(name, table);
+ }
+ TempDevice(TempDevice&& other) : dm_(other.dm_), name_(other.name_), valid_(other.valid_) {
+ other.valid_ = false;
+ }
+ ~TempDevice() {
+ if (valid_) {
+ dm_.DeleteDevice(name_);
+ }
+ }
+ bool Destroy() {
+ if (!valid_) {
+ return false;
+ }
+ valid_ = false;
+ return dm_.DeleteDevice(name_);
+ }
+ bool WaitForUdev() const {
+ auto start_time = std::chrono::steady_clock::now();
+ while (true) {
+ if (!access(path().c_str(), F_OK)) {
+ return true;
+ }
+ if (errno != ENOENT) {
+ return false;
+ }
+ std::this_thread::sleep_for(50ms);
+ std::chrono::duration elapsed = std::chrono::steady_clock::now() - start_time;
+ if (elapsed >= 5s) {
+ return false;
+ }
+ }
+ }
+ std::string path() const {
+ std::string device_path;
+ if (!dm_.GetDmDevicePathByName(name_, &device_path)) {
+ return "";
+ }
+ return device_path;
+ }
+ const std::string& name() const { return name_; }
+ bool valid() const { return valid_; }
+
+ TempDevice(const TempDevice&) = delete;
+ TempDevice& operator=(const TempDevice&) = delete;
+
+ TempDevice& operator=(TempDevice&& other) {
+ name_ = other.name_;
+ valid_ = other.valid_;
+ other.valid_ = false;
+ return *this;
+ }
+
+ private:
+ DeviceMapper& dm_;
+ std::string name_;
+ bool valid_;
+};
+
+TEST(libdm, DmLinear) {
+ unique_fd tmp1(CreateTempFile("file_1", 4096));
+ ASSERT_GE(tmp1, 0);
+ unique_fd tmp2(CreateTempFile("file_2", 4096));
+ ASSERT_GE(tmp2, 0);
+
+ // Create two different files. These will back two separate loop devices.
+ const char message1[] = "Hello! This is sector 1.";
+ const char message2[] = "Goodbye. This is sector 2.";
+ ASSERT_TRUE(android::base::WriteFully(tmp1, message1, sizeof(message1)));
+ ASSERT_TRUE(android::base::WriteFully(tmp2, message2, sizeof(message2)));
+
+ LoopDevice loop_a(tmp1);
+ ASSERT_TRUE(loop_a.valid());
+ LoopDevice loop_b(tmp2);
+ ASSERT_TRUE(loop_b.valid());
+
+ // Define a 2-sector device, with each sector mapping to the first sector
+ // of one of our loop devices.
+ DmTable table;
+ ASSERT_TRUE(table.AddTarget(make_unique<DmTargetLinear>(0, 1, loop_a.device(), 0)));
+ ASSERT_TRUE(table.AddTarget(make_unique<DmTargetLinear>(1, 1, loop_b.device(), 0)));
+ ASSERT_TRUE(table.valid());
+
+ TempDevice dev("libdm-test-dm-linear", table);
+ ASSERT_TRUE(dev.valid());
+ ASSERT_FALSE(dev.path().empty());
+ ASSERT_TRUE(dev.WaitForUdev());
+
+ // Note: a scope is needed to ensure that there are no open descriptors
+ // when we go to close the device.
+ {
+ unique_fd dev_fd(open(dev.path().c_str(), O_RDWR));
+ ASSERT_GE(dev_fd, 0);
+
+ // Test that each sector of our device is correctly mapped to each loop
+ // device.
+ char sector[512];
+ ASSERT_TRUE(android::base::ReadFully(dev_fd, sector, sizeof(sector)));
+ ASSERT_EQ(strncmp(sector, message1, sizeof(message1)), 0);
+ ASSERT_TRUE(android::base::ReadFully(dev_fd, sector, sizeof(sector)));
+ ASSERT_EQ(strncmp(sector, message2, sizeof(message2)), 0);
+ }
+
+ // Normally the TestDevice destructor would delete this, but at least one
+ // test should ensure that device deletion works.
+ ASSERT_TRUE(dev.Destroy());
+}
diff --git a/fs_mgr/libdm/include/dm_target.h b/fs_mgr/libdm/include/dm_target.h
deleted file mode 100644
index 31b0cb6..0000000
--- a/fs_mgr/libdm/include/dm_target.h
+++ /dev/null
@@ -1,77 +0,0 @@
-/*
- * 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/libdm/include/dm.h b/fs_mgr/libdm/include/libdm/dm.h
similarity index 87%
rename from fs_mgr/libdm/include/dm.h
rename to fs_mgr/libdm/include/libdm/dm.h
index 52a9a11..60bceed 100644
--- a/fs_mgr/libdm/include/dm.h
+++ b/fs_mgr/libdm/include/libdm/dm.h
@@ -17,18 +17,20 @@
#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 <stdint.h>
#include <sys/sysmacros.h>
#include <unistd.h>
#include <memory>
+#include <string>
+#include <vector>
#include <android-base/logging.h>
-#include <dm_table.h>
+#include "dm_table.h"
// The minimum expected device mapper major.minor version
#define DM_VERSION0 (4)
@@ -67,14 +69,6 @@
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);
@@ -88,15 +82,20 @@
// 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.
+ // Creates a device, loads the given table, and activates it. If the device
+ // is not able to be activated, it is destroyed, and false is returned.
+ bool CreateDevice(const std::string& name, const DmTable& table);
+
+ // Loads the device mapper table from parameter into the underlying device
+ // mapper device with given name and activate / resumes the device in the
+ // process. A device with the given name must already exist.
+ //
// 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);
+ bool GetAvailableTargets(std::vector<DmTargetTypeInfo>* 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
@@ -105,8 +104,9 @@
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);
+ // 'name'. If the device does not exist, false is returned, and the path
+ // parameter is not set.
+ bool GetDmDevicePathByName(const std::string& name, std::string* path);
// The only way to create a DeviceMapper object.
static DeviceMapper& Instance();
@@ -138,6 +138,12 @@
}
}
+ // 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.
+ bool CreateDevice(const std::string& name);
+
int fd_;
// Non-copyable & Non-movable
DeviceMapper(const DeviceMapper&) = delete;
diff --git a/fs_mgr/libdm/include/dm_table.h b/fs_mgr/libdm/include/libdm/dm_table.h
similarity index 86%
rename from fs_mgr/libdm/include/dm_table.h
rename to fs_mgr/libdm/include/libdm/dm_table.h
index 0b1685d..5c639be 100644
--- a/fs_mgr/libdm/include/dm_table.h
+++ b/fs_mgr/libdm/include/libdm/dm_table.h
@@ -30,7 +30,7 @@
class DmTable {
public:
- DmTable() : size_(0){};
+ DmTable() : num_sectors_(0), readonly_(false) {}
// 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
@@ -48,14 +48,20 @@
// table is malformed.
bool valid() const;
+ // Returns the toatl number of targets.
+ size_t num_targets() const { return targets_.size(); }
+
// 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;
+ uint64_t num_sectors() 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;
+ void set_readonly(bool readonly) { readonly_ = readonly; }
+ bool readonly() const { return readonly_; }
+
~DmTable() = default;
private:
@@ -66,7 +72,10 @@
// Total size in terms of # of sectors, as calculated by looking at the last and the first
// target in 'target_'.
- uint64_t size_;
+ uint64_t num_sectors_;
+
+ // True if the device should be read-only; false otherwise.
+ bool readonly_;
};
} // namespace dm
diff --git a/fs_mgr/libdm/include/libdm/dm_target.h b/fs_mgr/libdm/include/libdm/dm_target.h
new file mode 100644
index 0000000..8fb49ac
--- /dev/null
+++ b/fs_mgr/libdm/include/libdm/dm_target.h
@@ -0,0 +1,109 @@
+/*
+ * 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 <string>
+
+#include <android-base/logging.h>
+
+namespace android {
+namespace dm {
+
+class DmTargetTypeInfo {
+ public:
+ DmTargetTypeInfo() : major_(0), minor_(0), patch_(0) {}
+ DmTargetTypeInfo(const struct dm_target_versions* info)
+ : name_(info->name),
+ major_(info->version[0]),
+ minor_(info->version[1]),
+ patch_(info->version[2]) {}
+
+ const std::string& name() const { return name_; }
+ std::string version() const {
+ return std::to_string(major_) + "." + std::to_string(minor_) + "." + std::to_string(patch_);
+ }
+
+ private:
+ std::string name_;
+ uint32_t major_;
+ uint32_t minor_;
+ uint32_t patch_;
+};
+
+class DmTarget {
+ public:
+ DmTarget(uint64_t start, uint64_t length) : start_(start), length_(length) {}
+
+ virtual ~DmTarget() = default;
+
+ // Returns name of the target.
+ virtual std::string name() const = 0;
+
+ // Return the first logical sector represented by this target.
+ uint64_t start() const { return start_; }
+
+ // Returns size in number of sectors when this target is part of
+ // a DmTable, return 0 otherwise.
+ uint64_t size() const { return length_; }
+
+ // 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.
+ std::string Serialize() const;
+
+ protected:
+ // Get the parameter string that is passed to the end of the dm_target_spec
+ // for this target type.
+ virtual std::string GetParameterString() const = 0;
+
+ private:
+ // logical sector number start and total length (in terms of 512-byte sectors) represented
+ // by this target within a DmTable.
+ uint64_t start_, length_;
+};
+
+class DmTargetZero final : public DmTarget {
+ public:
+ DmTargetZero(uint64_t start, uint64_t length) : DmTarget(start, length) {}
+
+ std::string name() const override { return "zero"; }
+ std::string GetParameterString() const override;
+};
+
+class DmTargetLinear final : public DmTarget {
+ public:
+ DmTargetLinear(uint64_t start, uint64_t length, const std::string& block_device,
+ uint64_t physical_sector)
+ : DmTarget(start, length), block_device_(block_device), physical_sector_(physical_sector) {}
+
+ std::string name() const override { return "linear"; }
+ std::string GetParameterString() const override;
+ const std::string& block_device() const { return block_device_; }
+
+ private:
+ std::string block_device_;
+ uint64_t physical_sector_;
+};
+
+} // namespace dm
+} // namespace android
+
+#endif /* _LIBDM_DMTARGET_H_ */
diff --git a/fs_mgr/libdm/include/libdm/loop_control.h b/fs_mgr/libdm/include/libdm/loop_control.h
new file mode 100644
index 0000000..e6e83f4
--- /dev/null
+++ b/fs_mgr/libdm/include/libdm/loop_control.h
@@ -0,0 +1,82 @@
+/*
+ * 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_LOOP_CONTROL_H_
+#define _LIBDM_LOOP_CONTROL_H_
+
+#include <string>
+
+#include <android-base/unique_fd.h>
+
+namespace android {
+namespace dm {
+
+class LoopControl final {
+ public:
+ LoopControl();
+
+ // Attaches the file specified by 'file_fd' to the loop device specified
+ // by 'loopdev'
+ bool Attach(int file_fd, std::string* loopdev) const;
+
+ // Detach the loop device given by 'loopdev' from the attached backing file.
+ bool Detach(const std::string& loopdev) const;
+
+ LoopControl(const LoopControl&) = delete;
+ LoopControl& operator=(const LoopControl&) = delete;
+ LoopControl& operator=(LoopControl&&) = default;
+ LoopControl(LoopControl&&) = default;
+
+ private:
+ bool FindFreeLoopDevice(std::string* loopdev) const;
+
+ static constexpr const char* kLoopControlDevice = "/dev/loop-control";
+
+ android::base::unique_fd control_fd_;
+};
+
+// Create a temporary loop device around a file descriptor or path.
+class LoopDevice {
+ public:
+ // Create a loop device for the given file descriptor. It is closed when
+ // LoopDevice is destroyed only if auto_close is true.
+ LoopDevice(int fd, bool auto_close = false);
+ // Create a loop device for the given file path. It will be opened for
+ // reading and writing and closed when the loop device is detached.
+ explicit LoopDevice(const std::string& path);
+ ~LoopDevice();
+
+ bool valid() const { return fd_ != -1 && !device_.empty(); }
+ const std::string& device() const { return device_; }
+
+ LoopDevice(const LoopDevice&) = delete;
+ LoopDevice& operator=(const LoopDevice&) = delete;
+ LoopDevice& operator=(LoopDevice&&) = default;
+ LoopDevice(LoopDevice&&) = default;
+
+ private:
+ void Init();
+
+ android::base::unique_fd fd_;
+ bool owns_fd_;
+ std::string device_;
+ LoopControl control_;
+};
+
+} // namespace dm
+} // namespace android
+
+#endif /* _LIBDM_LOOP_CONTROL_H_ */
diff --git a/fs_mgr/libdm/loop_control.cpp b/fs_mgr/libdm/loop_control.cpp
new file mode 100644
index 0000000..0beb1a6
--- /dev/null
+++ b/fs_mgr/libdm/loop_control.cpp
@@ -0,0 +1,121 @@
+/*
+ * 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 "libdm/loop_control.h"
+
+#include <fcntl.h>
+#include <linux/loop.h>
+#include <stdint.h>
+#include <sys/ioctl.h>
+#include <sys/types.h>
+#include <unistd.h>
+
+#include <android-base/logging.h>
+#include <android-base/stringprintf.h>
+#include <android-base/unique_fd.h>
+
+namespace android {
+namespace dm {
+
+LoopControl::LoopControl() : control_fd_(-1) {
+ control_fd_.reset(TEMP_FAILURE_RETRY(open(kLoopControlDevice, O_RDWR | O_CLOEXEC)));
+ if (control_fd_ < 0) {
+ PLOG(ERROR) << "Failed to open loop-control";
+ }
+}
+
+bool LoopControl::Attach(int file_fd, std::string* loopdev) const {
+ if (!FindFreeLoopDevice(loopdev)) {
+ LOG(ERROR) << "Failed to attach, no free loop devices";
+ return false;
+ }
+
+ android::base::unique_fd loop_fd(TEMP_FAILURE_RETRY(open(loopdev->c_str(), O_RDWR | O_CLOEXEC)));
+ if (loop_fd < 0) {
+ PLOG(ERROR) << "Failed to open: " << *loopdev;
+ return false;
+ }
+
+ int rc = ioctl(loop_fd, LOOP_SET_FD, file_fd);
+ if (rc < 0) {
+ PLOG(ERROR) << "Failed LOOP_SET_FD";
+ return false;
+ }
+ return true;
+}
+
+bool LoopControl::Detach(const std::string& loopdev) const {
+ if (loopdev.empty()) {
+ LOG(ERROR) << "Must provide a loop device";
+ return false;
+ }
+
+ android::base::unique_fd loop_fd(TEMP_FAILURE_RETRY(open(loopdev.c_str(), O_RDWR | O_CLOEXEC)));
+ if (loop_fd < 0) {
+ PLOG(ERROR) << "Failed to open: " << loopdev;
+ return false;
+ }
+
+ int rc = ioctl(loop_fd, LOOP_CLR_FD, 0);
+ if (rc) {
+ PLOG(ERROR) << "Failed LOOP_CLR_FD for '" << loopdev << "'";
+ return false;
+ }
+ return true;
+}
+
+bool LoopControl::FindFreeLoopDevice(std::string* loopdev) const {
+ int rc = ioctl(control_fd_, LOOP_CTL_GET_FREE);
+ if (rc < 0) {
+ PLOG(ERROR) << "Failed to get free loop device";
+ return false;
+ }
+
+ // Ueventd on android creates all loop devices as /dev/block/loopX
+ // The total number of available devices is determined by 'loop.max_part'
+ // kernel command line argument.
+ *loopdev = ::android::base::StringPrintf("/dev/block/loop%d", rc);
+ return true;
+}
+
+LoopDevice::LoopDevice(int fd, bool auto_close) : fd_(fd), owns_fd_(auto_close) {
+ Init();
+}
+
+LoopDevice::LoopDevice(const std::string& path) : fd_(-1), owns_fd_(true) {
+ fd_.reset(open(path.c_str(), O_RDWR | O_CLOEXEC));
+ if (fd_ < -1) {
+ PLOG(ERROR) << "open failed for " << path;
+ return;
+ }
+ Init();
+}
+
+LoopDevice::~LoopDevice() {
+ if (valid()) {
+ control_.Detach(device_);
+ }
+ if (!owns_fd_) {
+ (void)fd_.release();
+ }
+}
+
+void LoopDevice::Init() {
+ control_.Attach(fd_, &device_);
+}
+
+} // namespace dm
+} // namespace android
diff --git a/fs_mgr/libdm/loop_control_test.cpp b/fs_mgr/libdm/loop_control_test.cpp
new file mode 100644
index 0000000..08bdc00
--- /dev/null
+++ b/fs_mgr/libdm/loop_control_test.cpp
@@ -0,0 +1,64 @@
+/*
+ * 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 "libdm/loop_control.h"
+
+#include <fcntl.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <unistd.h>
+
+#include <android-base/file.h>
+#include <android-base/unique_fd.h>
+#include <gtest/gtest.h>
+#include "test_util.h"
+
+using namespace std;
+using namespace android::dm;
+using unique_fd = android::base::unique_fd;
+
+static unique_fd TempFile() {
+ // A loop device needs to be at least one sector to actually work, so fill
+ // up the file with a message.
+ unique_fd fd(CreateTempFile("temp", 0));
+ if (fd < 0) {
+ return {};
+ }
+ char buffer[] = "Hello";
+ for (size_t i = 0; i < 1000; i++) {
+ if (!android::base::WriteFully(fd, buffer, sizeof(buffer))) {
+ perror("write");
+ return {};
+ }
+ }
+ return fd;
+}
+
+TEST(libdm, LoopControl) {
+ unique_fd fd = TempFile();
+ ASSERT_GE(fd, 0);
+
+ LoopDevice loop(fd);
+ ASSERT_TRUE(loop.valid());
+
+ char buffer[6];
+ unique_fd loop_fd(open(loop.device().c_str(), O_RDWR));
+ ASSERT_GE(loop_fd, 0);
+ ASSERT_TRUE(android::base::ReadFully(loop_fd, buffer, sizeof(buffer)));
+ ASSERT_EQ(memcmp(buffer, "Hello", 6), 0);
+}
diff --git a/fs_mgr/libdm/test_util.cpp b/fs_mgr/libdm/test_util.cpp
new file mode 100644
index 0000000..307251c
--- /dev/null
+++ b/fs_mgr/libdm/test_util.cpp
@@ -0,0 +1,52 @@
+/*
+ * 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 <fcntl.h>
+#include <linux/memfd.h>
+#include <stdio.h>
+#include <sys/syscall.h>
+#include <sys/types.h>
+#include <unistd.h>
+
+#include "test_util.h"
+
+namespace android {
+namespace dm {
+
+using unique_fd = android::base::unique_fd;
+
+// Create a temporary in-memory file. If size is non-zero, the file will be
+// created with a fixed size.
+unique_fd CreateTempFile(const std::string& name, size_t size) {
+ unique_fd fd(syscall(__NR_memfd_create, name.c_str(), MFD_ALLOW_SEALING));
+ if (fd < 0) {
+ return {};
+ }
+ if (size) {
+ if (ftruncate(fd, size) < 0) {
+ perror("ftruncate");
+ return {};
+ }
+ if (fcntl(fd, F_ADD_SEALS, F_SEAL_GROW | F_SEAL_SHRINK) < 0) {
+ perror("fcntl");
+ return {};
+ }
+ }
+ return fd;
+}
+
+} // namespace dm
+} // namespace android
diff --git a/fs_mgr/libdm/test_util.h b/fs_mgr/libdm/test_util.h
new file mode 100644
index 0000000..96b051c
--- /dev/null
+++ b/fs_mgr/libdm/test_util.h
@@ -0,0 +1,35 @@
+/*
+ * 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.
+ */
+
+#ifndef _LIBDM_TEST_UTILS_H_
+#define _LIBDM_TEST_UTILS_H_
+
+#include <android-base/unique_fd.h>
+#include <stddef.h>
+
+#include <string>
+
+namespace android {
+namespace dm {
+
+// Create a temporary in-memory file. If size is non-zero, the file will be
+// created with a fixed size.
+android::base::unique_fd CreateTempFile(const std::string& name, size_t size);
+
+} // namespace dm
+} // namespace android
+
+#endif // _LIBDM_TEST_UTILS_H_
diff --git a/fs_mgr/liblp/Android.bp b/fs_mgr/liblp/Android.bp
new file mode 100644
index 0000000..f7086a8
--- /dev/null
+++ b/fs_mgr/liblp/Android.bp
@@ -0,0 +1,61 @@
+//
+// 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: "liblp",
+ host_supported: true,
+ recovery_available: true,
+ defaults: ["fs_mgr_defaults"],
+ cppflags: [
+ "-D_FILE_OFFSET_BITS=64",
+ ],
+ srcs: [
+ "builder.cpp",
+ "reader.cpp",
+ "utility.cpp",
+ "writer.cpp",
+ ],
+ static_libs: [
+ "libbase",
+ "liblog",
+ "libcrypto",
+ "libcrypto_utils",
+ ],
+ whole_static_libs: [
+ "libext2_uuid",
+ "libext4_utils",
+ "libsparse",
+ "libz",
+ ],
+ export_include_dirs: ["include"],
+}
+
+cc_test {
+ name: "liblp_test",
+ defaults: ["fs_mgr_defaults"],
+ static_libs: [
+ "libbase",
+ "liblog",
+ "libcrypto",
+ "libcrypto_utils",
+ "liblp",
+ ],
+ srcs: [
+ "builder_test.cpp",
+ "io_test.cpp",
+ "utility_test.cpp",
+ ],
+}
diff --git a/fs_mgr/liblp/builder.cpp b/fs_mgr/liblp/builder.cpp
new file mode 100644
index 0000000..0e4838c
--- /dev/null
+++ b/fs_mgr/liblp/builder.cpp
@@ -0,0 +1,355 @@
+/*
+ * 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 "liblp/builder.h"
+
+#include <string.h>
+
+#include <algorithm>
+
+#include <uuid/uuid.h>
+
+#include "liblp/metadata_format.h"
+#include "utility.h"
+
+namespace android {
+namespace fs_mgr {
+
+// Align a byte count up to the nearest 512-byte sector.
+template <typename T>
+static inline T AlignToSector(T value) {
+ return (value + (LP_SECTOR_SIZE - 1)) & ~T(LP_SECTOR_SIZE - 1);
+}
+
+void LinearExtent::AddTo(LpMetadata* out) const {
+ out->extents.push_back(LpMetadataExtent{num_sectors_, LP_TARGET_TYPE_LINEAR, physical_sector_});
+}
+
+void ZeroExtent::AddTo(LpMetadata* out) const {
+ out->extents.push_back(LpMetadataExtent{num_sectors_, LP_TARGET_TYPE_ZERO, 0});
+}
+
+Partition::Partition(const std::string& name, const std::string& guid, uint32_t attributes)
+ : name_(name), guid_(guid), attributes_(attributes), size_(0) {}
+
+void Partition::AddExtent(std::unique_ptr<Extent>&& extent) {
+ size_ += extent->num_sectors() * LP_SECTOR_SIZE;
+ extents_.push_back(std::move(extent));
+}
+
+void Partition::RemoveExtents() {
+ size_ = 0;
+ extents_.clear();
+}
+
+void Partition::ShrinkTo(uint64_t requested_size) {
+ uint64_t aligned_size = AlignToSector(requested_size);
+ if (size_ <= aligned_size) {
+ return;
+ }
+ if (aligned_size == 0) {
+ RemoveExtents();
+ return;
+ }
+
+ // Remove or shrink extents of any kind until the total partition size is
+ // equal to the requested size.
+ uint64_t sectors_to_remove = (size_ - aligned_size) / LP_SECTOR_SIZE;
+ while (sectors_to_remove) {
+ Extent* extent = extents_.back().get();
+ if (extent->num_sectors() > sectors_to_remove) {
+ size_ -= sectors_to_remove * LP_SECTOR_SIZE;
+ extent->set_num_sectors(extent->num_sectors() - sectors_to_remove);
+ break;
+ }
+ size_ -= (extent->num_sectors() * LP_SECTOR_SIZE);
+ sectors_to_remove -= extent->num_sectors();
+ extents_.pop_back();
+ }
+ DCHECK(size_ == requested_size);
+}
+
+std::unique_ptr<MetadataBuilder> MetadataBuilder::New(uint64_t blockdevice_size,
+ uint32_t metadata_max_size,
+ uint32_t metadata_slot_count) {
+ std::unique_ptr<MetadataBuilder> builder(new MetadataBuilder());
+ if (!builder->Init(blockdevice_size, metadata_max_size, metadata_slot_count)) {
+ return nullptr;
+ }
+ return builder;
+}
+
+std::unique_ptr<MetadataBuilder> MetadataBuilder::New(const LpMetadata& metadata) {
+ std::unique_ptr<MetadataBuilder> builder(new MetadataBuilder());
+ if (!builder->Init(metadata)) {
+ return nullptr;
+ }
+ return builder;
+}
+
+MetadataBuilder::MetadataBuilder() {
+ memset(&geometry_, 0, sizeof(geometry_));
+ geometry_.magic = LP_METADATA_GEOMETRY_MAGIC;
+ geometry_.struct_size = sizeof(geometry_);
+
+ memset(&header_, 0, sizeof(header_));
+ header_.magic = LP_METADATA_HEADER_MAGIC;
+ header_.major_version = LP_METADATA_MAJOR_VERSION;
+ header_.minor_version = LP_METADATA_MINOR_VERSION;
+ header_.header_size = sizeof(header_);
+ header_.partitions.entry_size = sizeof(LpMetadataPartition);
+ header_.extents.entry_size = sizeof(LpMetadataExtent);
+}
+
+bool MetadataBuilder::Init(const LpMetadata& metadata) {
+ geometry_ = metadata.geometry;
+
+ for (const auto& partition : metadata.partitions) {
+ Partition* builder = AddPartition(GetPartitionName(partition), GetPartitionGuid(partition),
+ partition.attributes);
+ if (!builder) {
+ return false;
+ }
+
+ for (size_t i = 0; i < partition.num_extents; i++) {
+ const LpMetadataExtent& extent = metadata.extents[partition.first_extent_index + i];
+ if (extent.target_type == LP_TARGET_TYPE_LINEAR) {
+ auto copy = std::make_unique<LinearExtent>(extent.num_sectors, extent.target_data);
+ builder->AddExtent(std::move(copy));
+ } else if (extent.target_type == LP_TARGET_TYPE_ZERO) {
+ auto copy = std::make_unique<ZeroExtent>(extent.num_sectors);
+ builder->AddExtent(std::move(copy));
+ }
+ }
+ }
+ return true;
+}
+
+bool MetadataBuilder::Init(uint64_t blockdevice_size, uint32_t metadata_max_size,
+ uint32_t metadata_slot_count) {
+ if (metadata_max_size < sizeof(LpMetadataHeader)) {
+ LERROR << "Invalid metadata maximum size.";
+ return false;
+ }
+ if (metadata_slot_count == 0) {
+ LERROR << "Invalid metadata slot count.";
+ return false;
+ }
+
+ // Align the metadata size up to the nearest sector.
+ metadata_max_size = AlignToSector(metadata_max_size);
+
+ // We reserve a geometry block (4KB) plus space for each copy of the
+ // maximum size of a metadata blob. Then, we double that space since
+ // we store a backup copy of everything.
+ uint64_t reserved =
+ LP_METADATA_GEOMETRY_SIZE + (uint64_t(metadata_max_size) * metadata_slot_count);
+ uint64_t total_reserved = reserved * 2;
+
+ if (blockdevice_size < total_reserved || blockdevice_size - total_reserved < LP_SECTOR_SIZE) {
+ LERROR << "Attempting to create metadata on a block device that is too small.";
+ return false;
+ }
+
+ // The last sector is inclusive. We subtract one to make sure that logical
+ // partitions won't overlap with the same sector as the backup metadata,
+ // which could happen if the block device was not aligned to LP_SECTOR_SIZE.
+ geometry_.first_logical_sector = reserved / LP_SECTOR_SIZE;
+ geometry_.last_logical_sector = ((blockdevice_size - reserved) / LP_SECTOR_SIZE) - 1;
+ geometry_.metadata_max_size = metadata_max_size;
+ geometry_.metadata_slot_count = metadata_slot_count;
+ DCHECK(geometry_.last_logical_sector >= geometry_.first_logical_sector);
+ return true;
+}
+
+Partition* MetadataBuilder::AddPartition(const std::string& name, const std::string& guid,
+ uint32_t attributes) {
+ if (name.empty()) {
+ LERROR << "Partition must have a non-empty name.";
+ return nullptr;
+ }
+ if (FindPartition(name)) {
+ LERROR << "Attempting to create duplication partition with name: " << name;
+ return nullptr;
+ }
+ partitions_.push_back(std::make_unique<Partition>(name, guid, attributes));
+ return partitions_.back().get();
+}
+
+Partition* MetadataBuilder::FindPartition(const std::string& name) {
+ for (const auto& partition : partitions_) {
+ if (partition->name() == name) {
+ return partition.get();
+ }
+ }
+ return nullptr;
+}
+
+void MetadataBuilder::RemovePartition(const std::string& name) {
+ for (auto iter = partitions_.begin(); iter != partitions_.end(); iter++) {
+ if ((*iter)->name() == name) {
+ partitions_.erase(iter);
+ return;
+ }
+ }
+}
+
+bool MetadataBuilder::GrowPartition(Partition* partition, uint64_t requested_size) {
+ // Align the space needed up to the nearest sector.
+ uint64_t aligned_size = AlignToSector(requested_size);
+ if (partition->size() >= aligned_size) {
+ return true;
+ }
+
+ // Figure out how much we need to allocate.
+ uint64_t space_needed = aligned_size - partition->size();
+ uint64_t sectors_needed = space_needed / LP_SECTOR_SIZE;
+ DCHECK(sectors_needed * LP_SECTOR_SIZE == space_needed);
+
+ struct Interval {
+ uint64_t start;
+ uint64_t end;
+
+ Interval(uint64_t start, uint64_t end) : start(start), end(end) {}
+ bool operator<(const Interval& other) const { return start < other.start; }
+ };
+ std::vector<Interval> intervals;
+
+ // Collect all extents in the partition table.
+ for (const auto& partition : partitions_) {
+ for (const auto& extent : partition->extents()) {
+ LinearExtent* linear = extent->AsLinearExtent();
+ if (!linear) {
+ continue;
+ }
+ intervals.emplace_back(linear->physical_sector(),
+ linear->physical_sector() + extent->num_sectors());
+ }
+ }
+
+ // Sort extents by starting sector.
+ std::sort(intervals.begin(), intervals.end());
+
+ // Find gaps that we can use for new extents. Note we store new extents in a
+ // temporary vector, and only commit them if we are guaranteed enough free
+ // space.
+ std::vector<std::unique_ptr<LinearExtent>> new_extents;
+ for (size_t i = 1; i < intervals.size(); i++) {
+ const Interval& previous = intervals[i - 1];
+ const Interval& current = intervals[i];
+
+ if (previous.end >= current.start) {
+ // There is no gap between these two extents, try the next one. Note that
+ // extents may never overlap, but just for safety, we ignore them if they
+ // do.
+ DCHECK(previous.end == current.start);
+ continue;
+ }
+
+ // This gap is enough to hold the remainder of the space requested, so we
+ // can allocate what we need and return.
+ if (current.start - previous.end >= sectors_needed) {
+ auto extent = std::make_unique<LinearExtent>(sectors_needed, previous.end);
+ sectors_needed -= extent->num_sectors();
+ new_extents.push_back(std::move(extent));
+ break;
+ }
+
+ // This gap is not big enough to fit the remainder of the space requested,
+ // so consume the whole thing and keep looking for more.
+ auto extent = std::make_unique<LinearExtent>(current.start - previous.end, previous.end);
+ sectors_needed -= extent->num_sectors();
+ new_extents.push_back(std::move(extent));
+ }
+
+ // If we still have more to allocate, take it from the remaining free space
+ // in the allocatable region.
+ if (sectors_needed) {
+ uint64_t first_sector;
+ if (intervals.empty()) {
+ first_sector = geometry_.first_logical_sector;
+ } else {
+ first_sector = intervals.back().end;
+ }
+ DCHECK(first_sector <= geometry_.last_logical_sector);
+
+ // Note: the last usable sector is inclusive.
+ if (geometry_.last_logical_sector + 1 - first_sector < sectors_needed) {
+ LERROR << "Not enough free space to expand partition: " << partition->name();
+ return false;
+ }
+ auto extent = std::make_unique<LinearExtent>(sectors_needed, first_sector);
+ new_extents.push_back(std::move(extent));
+ }
+
+ for (auto& extent : new_extents) {
+ partition->AddExtent(std::move(extent));
+ }
+ return true;
+}
+
+void MetadataBuilder::ShrinkPartition(Partition* partition, uint64_t requested_size) {
+ partition->ShrinkTo(requested_size);
+}
+
+std::unique_ptr<LpMetadata> MetadataBuilder::Export() {
+ std::unique_ptr<LpMetadata> metadata = std::make_unique<LpMetadata>();
+ metadata->header = header_;
+ metadata->geometry = geometry_;
+
+ // Flatten the partition and extent structures into an LpMetadata, which
+ // makes it very easy to validate, serialize, or pass on to device-mapper.
+ for (const auto& partition : partitions_) {
+ LpMetadataPartition part;
+ memset(&part, 0, sizeof(part));
+
+ if (partition->name().size() > sizeof(part.name)) {
+ LERROR << "Partition name is too long: " << partition->name();
+ return nullptr;
+ }
+ if (partition->attributes() & ~(LP_PARTITION_ATTRIBUTE_MASK)) {
+ LERROR << "Partition " << partition->name() << " has unsupported attribute.";
+ return nullptr;
+ }
+
+ strncpy(part.name, partition->name().c_str(), sizeof(part.name));
+ if (uuid_parse(partition->guid().c_str(), part.guid) != 0) {
+ LERROR << "Could not parse guid " << partition->guid() << " for partition "
+ << partition->name();
+ return nullptr;
+ }
+
+ part.first_extent_index = static_cast<uint32_t>(metadata->extents.size());
+ part.num_extents = static_cast<uint32_t>(partition->extents().size());
+ part.attributes = partition->attributes();
+
+ for (const auto& extent : partition->extents()) {
+ extent->AddTo(metadata.get());
+ }
+ metadata->partitions.push_back(part);
+ }
+
+ metadata->header.partitions.num_entries = static_cast<uint32_t>(metadata->partitions.size());
+ metadata->header.extents.num_entries = static_cast<uint32_t>(metadata->extents.size());
+ return metadata;
+}
+
+uint64_t MetadataBuilder::AllocatableSpace() const {
+ return (geometry_.last_logical_sector - geometry_.first_logical_sector + 1) * LP_SECTOR_SIZE;
+}
+
+} // namespace fs_mgr
+} // namespace android
diff --git a/fs_mgr/liblp/builder_test.cpp b/fs_mgr/liblp/builder_test.cpp
new file mode 100644
index 0000000..2983f0f
--- /dev/null
+++ b/fs_mgr/liblp/builder_test.cpp
@@ -0,0 +1,326 @@
+/*
+ * 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 <gtest/gtest.h>
+#include <liblp/builder.h>
+
+using namespace std;
+using namespace android::fs_mgr;
+
+static const char* TEST_GUID = "A799D1D6-669F-41D8-A3F0-EBB7572D8302";
+static const char* TEST_GUID2 = "A799D1D6-669F-41D8-A3F0-EBB7572D8303";
+
+TEST(liblp, BuildBasic) {
+ unique_ptr<MetadataBuilder> builder = MetadataBuilder::New(1024 * 1024, 1024, 2);
+
+ Partition* partition = builder->AddPartition("system", TEST_GUID, LP_PARTITION_ATTR_READONLY);
+ ASSERT_NE(partition, nullptr);
+ EXPECT_EQ(partition->name(), "system");
+ EXPECT_EQ(partition->guid(), TEST_GUID);
+ EXPECT_EQ(partition->attributes(), LP_PARTITION_ATTR_READONLY);
+ EXPECT_EQ(partition->size(), 0);
+ EXPECT_EQ(builder->FindPartition("system"), partition);
+
+ builder->RemovePartition("system");
+ EXPECT_EQ(builder->FindPartition("system"), nullptr);
+}
+
+TEST(liblp, ResizePartition) {
+ unique_ptr<MetadataBuilder> builder = MetadataBuilder::New(1024 * 1024, 1024, 2);
+
+ Partition* system = builder->AddPartition("system", TEST_GUID, LP_PARTITION_ATTR_READONLY);
+ ASSERT_NE(system, nullptr);
+ EXPECT_EQ(builder->GrowPartition(system, 65536), true);
+ EXPECT_EQ(system->size(), 65536);
+ ASSERT_EQ(system->extents().size(), 1);
+
+ LinearExtent* extent = system->extents()[0]->AsLinearExtent();
+ ASSERT_NE(extent, nullptr);
+ EXPECT_EQ(extent->num_sectors(), 65536 / LP_SECTOR_SIZE);
+ // The first logical sector will be (4096+1024*2)/512 = 12.
+ EXPECT_EQ(extent->physical_sector(), 12);
+
+ // Test growing to the same size.
+ EXPECT_EQ(builder->GrowPartition(system, 65536), true);
+ EXPECT_EQ(system->size(), 65536);
+ EXPECT_EQ(system->extents().size(), 1);
+ EXPECT_EQ(system->extents()[0]->num_sectors(), 65536 / LP_SECTOR_SIZE);
+ // Test growing to a smaller size.
+ EXPECT_EQ(builder->GrowPartition(system, 0), true);
+ EXPECT_EQ(system->size(), 65536);
+ EXPECT_EQ(system->extents().size(), 1);
+ EXPECT_EQ(system->extents()[0]->num_sectors(), 65536 / LP_SECTOR_SIZE);
+ // Test shrinking to a greater size.
+ builder->ShrinkPartition(system, 131072);
+ EXPECT_EQ(system->size(), 65536);
+ EXPECT_EQ(system->extents().size(), 1);
+ EXPECT_EQ(system->extents()[0]->num_sectors(), 65536 / LP_SECTOR_SIZE);
+
+ // Test shrinking within the same extent.
+ builder->ShrinkPartition(system, 32768);
+ EXPECT_EQ(system->size(), 32768);
+ EXPECT_EQ(system->extents().size(), 1);
+ extent = system->extents()[0]->AsLinearExtent();
+ ASSERT_NE(extent, nullptr);
+ EXPECT_EQ(extent->num_sectors(), 32768 / LP_SECTOR_SIZE);
+ EXPECT_EQ(extent->physical_sector(), 12);
+
+ // Test shrinking to 0.
+ builder->ShrinkPartition(system, 0);
+ EXPECT_EQ(system->size(), 0);
+ EXPECT_EQ(system->extents().size(), 0);
+}
+
+TEST(liblp, PartitionAlignment) {
+ unique_ptr<MetadataBuilder> builder = MetadataBuilder::New(1024 * 1024, 1024, 2);
+
+ // Test that we align up to one sector.
+ Partition* system = builder->AddPartition("system", TEST_GUID, LP_PARTITION_ATTR_READONLY);
+ ASSERT_NE(system, nullptr);
+ EXPECT_EQ(builder->GrowPartition(system, 10000), true);
+ EXPECT_EQ(system->size(), 10240);
+ EXPECT_EQ(system->extents().size(), 1);
+
+ builder->ShrinkPartition(system, 9000);
+ EXPECT_EQ(system->size(), 9216);
+ EXPECT_EQ(system->extents().size(), 1);
+}
+
+TEST(liblp, DiskAlignment) {
+ static const uint64_t kDiskSize = 1000000;
+ static const uint32_t kMetadataSize = 1024;
+ static const uint32_t kMetadataSlots = 2;
+
+ // If the disk size is not aligned to 512 bytes, make sure it still leaves
+ // space at the end for backup metadata, and that it doesn't overlap with
+ // the space for logical partitions.
+ unique_ptr<MetadataBuilder> builder =
+ MetadataBuilder::New(kDiskSize, kMetadataSize, kMetadataSlots);
+ unique_ptr<LpMetadata> exported = builder->Export();
+ ASSERT_NE(exported, nullptr);
+
+ static const size_t kMetadataSpace =
+ (kMetadataSize * kMetadataSlots) + LP_METADATA_GEOMETRY_SIZE;
+ uint64_t space_at_end =
+ kDiskSize - (exported->geometry.last_logical_sector + 1) * LP_SECTOR_SIZE;
+ EXPECT_GE(space_at_end, kMetadataSpace);
+}
+
+TEST(liblp, MetadataAlignment) {
+ // Make sure metadata sizes get aligned up.
+ unique_ptr<MetadataBuilder> builder = MetadataBuilder::New(1024 * 1024, 1000, 2);
+ unique_ptr<LpMetadata> exported = builder->Export();
+ ASSERT_NE(exported, nullptr);
+ EXPECT_EQ(exported->geometry.metadata_max_size, 1024);
+}
+
+TEST(liblp, UseAllDiskSpace) {
+ unique_ptr<MetadataBuilder> builder = MetadataBuilder::New(1024 * 1024, 1024, 2);
+ EXPECT_EQ(builder->AllocatableSpace(), 1036288);
+
+ Partition* system = builder->AddPartition("system", TEST_GUID, LP_PARTITION_ATTR_READONLY);
+ ASSERT_NE(system, nullptr);
+ EXPECT_EQ(builder->GrowPartition(system, 1036288), true);
+ EXPECT_EQ(system->size(), 1036288);
+ EXPECT_EQ(builder->GrowPartition(system, 1036289), false);
+}
+
+TEST(liblp, BuildComplex) {
+ unique_ptr<MetadataBuilder> builder = MetadataBuilder::New(1024 * 1024, 1024, 2);
+
+ Partition* system = builder->AddPartition("system", TEST_GUID, LP_PARTITION_ATTR_READONLY);
+ Partition* vendor = builder->AddPartition("vendor", TEST_GUID2, LP_PARTITION_ATTR_READONLY);
+ ASSERT_NE(system, nullptr);
+ ASSERT_NE(vendor, nullptr);
+ EXPECT_EQ(builder->GrowPartition(system, 65536), true);
+ EXPECT_EQ(builder->GrowPartition(vendor, 32768), true);
+ EXPECT_EQ(builder->GrowPartition(system, 98304), true);
+ EXPECT_EQ(system->size(), 98304);
+ EXPECT_EQ(vendor->size(), 32768);
+
+ // We now expect to have 3 extents total: 2 for system, 1 for vendor, since
+ // our allocation strategy is greedy/first-fit.
+ ASSERT_EQ(system->extents().size(), 2);
+ ASSERT_EQ(vendor->extents().size(), 1);
+
+ LinearExtent* system1 = system->extents()[0]->AsLinearExtent();
+ LinearExtent* system2 = system->extents()[1]->AsLinearExtent();
+ LinearExtent* vendor1 = vendor->extents()[0]->AsLinearExtent();
+ ASSERT_NE(system1, nullptr);
+ ASSERT_NE(system2, nullptr);
+ ASSERT_NE(vendor1, nullptr);
+ EXPECT_EQ(system1->num_sectors(), 65536 / LP_SECTOR_SIZE);
+ EXPECT_EQ(system1->physical_sector(), 12);
+ EXPECT_EQ(system2->num_sectors(), 32768 / LP_SECTOR_SIZE);
+ EXPECT_EQ(system2->physical_sector(), 204);
+ EXPECT_EQ(vendor1->num_sectors(), 32768 / LP_SECTOR_SIZE);
+ EXPECT_EQ(vendor1->physical_sector(), 140);
+ EXPECT_EQ(system1->physical_sector() + system1->num_sectors(), vendor1->physical_sector());
+ EXPECT_EQ(vendor1->physical_sector() + vendor1->num_sectors(), system2->physical_sector());
+}
+
+TEST(liblp, AddInvalidPartition) {
+ unique_ptr<MetadataBuilder> builder = MetadataBuilder::New(1024 * 1024, 1024, 2);
+
+ Partition* partition = builder->AddPartition("system", TEST_GUID, LP_PARTITION_ATTR_READONLY);
+ ASSERT_NE(partition, nullptr);
+
+ // Duplicate name.
+ partition = builder->AddPartition("system", TEST_GUID, LP_PARTITION_ATTR_READONLY);
+ EXPECT_EQ(partition, nullptr);
+
+ // Empty name.
+ partition = builder->AddPartition("", TEST_GUID, LP_PARTITION_ATTR_READONLY);
+ EXPECT_EQ(partition, nullptr);
+}
+
+TEST(liblp, BuilderExport) {
+ static const uint64_t kDiskSize = 1024 * 1024;
+ static const uint32_t kMetadataSize = 1024;
+ static const uint32_t kMetadataSlots = 2;
+ unique_ptr<MetadataBuilder> builder =
+ MetadataBuilder::New(kDiskSize, kMetadataSize, kMetadataSlots);
+
+ Partition* system = builder->AddPartition("system", TEST_GUID, LP_PARTITION_ATTR_READONLY);
+ Partition* vendor = builder->AddPartition("vendor", TEST_GUID2, LP_PARTITION_ATTR_READONLY);
+ ASSERT_NE(system, nullptr);
+ ASSERT_NE(vendor, nullptr);
+ EXPECT_EQ(builder->GrowPartition(system, 65536), true);
+ EXPECT_EQ(builder->GrowPartition(vendor, 32768), true);
+ EXPECT_EQ(builder->GrowPartition(system, 98304), true);
+
+ unique_ptr<LpMetadata> exported = builder->Export();
+ EXPECT_NE(exported, nullptr);
+
+ // Verify geometry. Some details of this may change if we change the
+ // metadata structures. So in addition to checking the exact values, we
+ // also check that they are internally consistent after.
+ const LpMetadataGeometry& geometry = exported->geometry;
+ EXPECT_EQ(geometry.magic, LP_METADATA_GEOMETRY_MAGIC);
+ EXPECT_EQ(geometry.struct_size, sizeof(geometry));
+ EXPECT_EQ(geometry.metadata_max_size, 1024);
+ EXPECT_EQ(geometry.metadata_slot_count, 2);
+ EXPECT_EQ(geometry.first_logical_sector, 12);
+ EXPECT_EQ(geometry.last_logical_sector, 2035);
+
+ static const size_t kMetadataSpace =
+ (kMetadataSize * kMetadataSlots) + LP_METADATA_GEOMETRY_SIZE;
+ uint64_t space_at_end = kDiskSize - (geometry.last_logical_sector + 1) * LP_SECTOR_SIZE;
+ EXPECT_GE(space_at_end, kMetadataSpace);
+ EXPECT_GE(geometry.first_logical_sector * LP_SECTOR_SIZE, kMetadataSpace);
+
+ // Verify header.
+ const LpMetadataHeader& header = exported->header;
+ EXPECT_EQ(header.magic, LP_METADATA_HEADER_MAGIC);
+ EXPECT_EQ(header.major_version, LP_METADATA_MAJOR_VERSION);
+ EXPECT_EQ(header.minor_version, LP_METADATA_MINOR_VERSION);
+
+ ASSERT_EQ(exported->partitions.size(), 2);
+ ASSERT_EQ(exported->extents.size(), 3);
+
+ for (const auto& partition : exported->partitions) {
+ Partition* original = builder->FindPartition(GetPartitionName(partition));
+ ASSERT_NE(original, nullptr);
+ EXPECT_EQ(original->guid(), GetPartitionGuid(partition));
+ for (size_t i = 0; i < partition.num_extents; i++) {
+ const auto& extent = exported->extents[partition.first_extent_index + i];
+ LinearExtent* original_extent = original->extents()[i]->AsLinearExtent();
+ EXPECT_EQ(extent.num_sectors, original_extent->num_sectors());
+ EXPECT_EQ(extent.target_type, LP_TARGET_TYPE_LINEAR);
+ EXPECT_EQ(extent.target_data, original_extent->physical_sector());
+ }
+ EXPECT_EQ(partition.attributes, original->attributes());
+ }
+}
+
+TEST(liblp, BuilderImport) {
+ unique_ptr<MetadataBuilder> builder = MetadataBuilder::New(1024 * 1024, 1024, 2);
+
+ Partition* system = builder->AddPartition("system", TEST_GUID, LP_PARTITION_ATTR_READONLY);
+ Partition* vendor = builder->AddPartition("vendor", TEST_GUID2, LP_PARTITION_ATTR_READONLY);
+ ASSERT_NE(system, nullptr);
+ ASSERT_NE(vendor, nullptr);
+ EXPECT_EQ(builder->GrowPartition(system, 65536), true);
+ EXPECT_EQ(builder->GrowPartition(vendor, 32768), true);
+ EXPECT_EQ(builder->GrowPartition(system, 98304), true);
+
+ unique_ptr<LpMetadata> exported = builder->Export();
+ ASSERT_NE(exported, nullptr);
+
+ builder = MetadataBuilder::New(*exported.get());
+ ASSERT_NE(builder, nullptr);
+ system = builder->FindPartition("system");
+ ASSERT_NE(system, nullptr);
+ vendor = builder->FindPartition("vendor");
+ ASSERT_NE(vendor, nullptr);
+
+ EXPECT_EQ(system->size(), 98304);
+ ASSERT_EQ(system->extents().size(), 2);
+ EXPECT_EQ(system->guid(), TEST_GUID);
+ EXPECT_EQ(system->attributes(), LP_PARTITION_ATTR_READONLY);
+ EXPECT_EQ(vendor->size(), 32768);
+ ASSERT_EQ(vendor->extents().size(), 1);
+ EXPECT_EQ(vendor->guid(), TEST_GUID2);
+ EXPECT_EQ(vendor->attributes(), LP_PARTITION_ATTR_READONLY);
+
+ LinearExtent* system1 = system->extents()[0]->AsLinearExtent();
+ LinearExtent* system2 = system->extents()[1]->AsLinearExtent();
+ LinearExtent* vendor1 = vendor->extents()[0]->AsLinearExtent();
+ EXPECT_EQ(system1->num_sectors(), 65536 / LP_SECTOR_SIZE);
+ EXPECT_EQ(system1->physical_sector(), 12);
+ EXPECT_EQ(system2->num_sectors(), 32768 / LP_SECTOR_SIZE);
+ EXPECT_EQ(system2->physical_sector(), 204);
+ EXPECT_EQ(vendor1->num_sectors(), 32768 / LP_SECTOR_SIZE);
+}
+
+TEST(liblp, ExportNameTooLong) {
+ unique_ptr<MetadataBuilder> builder = MetadataBuilder::New(1024 * 1024, 1024, 2);
+
+ std::string name = "abcdefghijklmnopqrstuvwxyz0123456789";
+ Partition* system = builder->AddPartition(name + name, TEST_GUID, LP_PARTITION_ATTR_READONLY);
+ EXPECT_NE(system, nullptr);
+
+ unique_ptr<LpMetadata> exported = builder->Export();
+ EXPECT_EQ(exported, nullptr);
+}
+
+TEST(liblp, ExportInvalidGuid) {
+ unique_ptr<MetadataBuilder> builder = MetadataBuilder::New(1024 * 1024, 1024, 2);
+
+ Partition* system = builder->AddPartition("system", "bad", LP_PARTITION_ATTR_READONLY);
+ EXPECT_NE(system, nullptr);
+
+ unique_ptr<LpMetadata> exported = builder->Export();
+ EXPECT_EQ(exported, nullptr);
+}
+
+TEST(liblp, MetadataTooLarge) {
+ static const size_t kDiskSize = 128 * 1024;
+ static const size_t kMetadataSize = 64 * 1024;
+
+ // No space to store metadata + geometry.
+ unique_ptr<MetadataBuilder> builder = MetadataBuilder::New(kDiskSize, kMetadataSize, 1);
+ EXPECT_EQ(builder, nullptr);
+
+ // No space to store metadata + geometry + one free sector.
+ builder = MetadataBuilder::New(kDiskSize + LP_METADATA_GEOMETRY_SIZE * 2, kMetadataSize, 1);
+ EXPECT_EQ(builder, nullptr);
+
+ // Space for metadata + geometry + one free sector.
+ builder = MetadataBuilder::New(kDiskSize + LP_METADATA_GEOMETRY_SIZE * 2 + LP_SECTOR_SIZE,
+ kMetadataSize, 1);
+ EXPECT_NE(builder, nullptr);
+}
diff --git a/fs_mgr/liblp/include/liblp/builder.h b/fs_mgr/liblp/include/liblp/builder.h
new file mode 100644
index 0000000..671a3bd
--- /dev/null
+++ b/fs_mgr/liblp/include/liblp/builder.h
@@ -0,0 +1,172 @@
+//
+// 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.
+//
+
+#ifndef LIBLP_METADATA_BUILDER_H
+#define LIBLP_METADATA_BUILDER_H
+
+#include <stddef.h>
+#include <stdint.h>
+
+#include <map>
+#include <memory>
+
+#include "metadata_format.h"
+
+namespace android {
+namespace fs_mgr {
+
+class LinearExtent;
+
+// Abstraction around dm-targets that can be encoded into logical partition tables.
+class Extent {
+ public:
+ explicit Extent(uint64_t num_sectors) : num_sectors_(num_sectors) {}
+ virtual ~Extent() {}
+
+ virtual void AddTo(LpMetadata* out) const = 0;
+ virtual LinearExtent* AsLinearExtent() { return nullptr; }
+
+ uint64_t num_sectors() const { return num_sectors_; }
+ void set_num_sectors(uint64_t num_sectors) { num_sectors_ = num_sectors; }
+
+ protected:
+ uint64_t num_sectors_;
+};
+
+// This corresponds to a dm-linear target.
+class LinearExtent final : public Extent {
+ public:
+ LinearExtent(uint64_t num_sectors, uint64_t physical_sector)
+ : Extent(num_sectors), physical_sector_(physical_sector) {}
+
+ void AddTo(LpMetadata* metadata) const override;
+ LinearExtent* AsLinearExtent() override { return this; }
+
+ uint64_t physical_sector() const { return physical_sector_; }
+
+ private:
+ uint64_t physical_sector_;
+};
+
+// This corresponds to a dm-zero target.
+class ZeroExtent final : public Extent {
+ public:
+ explicit ZeroExtent(uint64_t num_sectors) : Extent(num_sectors) {}
+
+ void AddTo(LpMetadata* out) const override;
+};
+
+class Partition final {
+ public:
+ Partition(const std::string& name, const std::string& guid, uint32_t attributes);
+
+ // Add a raw extent.
+ void AddExtent(std::unique_ptr<Extent>&& extent);
+
+ // Remove all extents from this partition.
+ void RemoveExtents();
+
+ // Remove and/or shrink extents until the partition is the requested size.
+ // See MetadataBuilder::ShrinkPartition for more information.
+ void ShrinkTo(uint64_t requested_size);
+
+ const std::string& name() const { return name_; }
+ uint32_t attributes() const { return attributes_; }
+ const std::string& guid() const { return guid_; }
+ const std::vector<std::unique_ptr<Extent>>& extents() const { return extents_; }
+ uint64_t size() const { return size_; }
+
+ private:
+ std::string name_;
+ std::string guid_;
+ std::vector<std::unique_ptr<Extent>> extents_;
+ uint32_t attributes_;
+ uint64_t size_;
+};
+
+class MetadataBuilder {
+ public:
+ // Construct an empty logical partition table builder. The block device size
+ // and maximum metadata size must be specified, as this will determine which
+ // areas of the physical partition can be flashed for metadata vs for logical
+ // partitions.
+ //
+ // If the parameters would yield invalid metadata, nullptr is returned. This
+ // could happen if the block device size is too small to store the metadata
+ // and backup copies.
+ static std::unique_ptr<MetadataBuilder> New(uint64_t blockdevice_size,
+ uint32_t metadata_max_size,
+ uint32_t metadata_slot_count);
+
+ // Import an existing table for modification. If the table is not valid, for
+ // example it contains duplicate partition names, then nullptr is returned.
+ static std::unique_ptr<MetadataBuilder> New(const LpMetadata& metadata);
+
+ // Export metadata so it can be serialized to an image, to disk, or mounted
+ // via device-mapper.
+ std::unique_ptr<LpMetadata> Export();
+
+ // Add a partition, returning a handle so it can be sized as needed. If a
+ // partition with the given name already exists, nullptr is returned.
+ Partition* AddPartition(const std::string& name, const std::string& guid, uint32_t attributes);
+
+ // Delete a partition by name if it exists.
+ void RemovePartition(const std::string& name);
+
+ // Find a partition by name. If no partition is found, nullptr is returned.
+ Partition* FindPartition(const std::string& name);
+
+ // Grow a partition to the requested size. If the partition's size is already
+ // greater or equal to the requested size, this will return true and the
+ // partition table will not be changed. Otherwise, a greedy algorithm is
+ // used to find free gaps in the partition table and allocate them for this
+ // partition. If not enough space can be allocated, false is returned, and
+ // the parition table will not be modified.
+ //
+ // The size will be rounded UP to the nearest sector.
+ //
+ // Note, this is an in-memory operation, and it does not alter the
+ // underlying filesystem or contents of the partition on disk.
+ bool GrowPartition(Partition* partition, uint64_t requested_size);
+
+ // Shrink a partition to the requested size. If the partition is already
+ // smaller than the given size, this will return and the partition table
+ // will not be changed. Otherwise, extents will be removed and/or shrunk
+ // from the end of the partition until it is the requested size.
+ //
+ // The size will be rounded UP to the nearest sector.
+ //
+ // Note, this is an in-memory operation, and it does not alter the
+ // underlying filesystem or contents of the partition on disk.
+ void ShrinkPartition(Partition* partition, uint64_t requested_size);
+
+ // Amount of space that can be allocated to logical partitions.
+ uint64_t AllocatableSpace() const;
+
+ private:
+ MetadataBuilder();
+ bool Init(uint64_t blockdevice_size, uint32_t metadata_max_size, uint32_t metadata_slot_count);
+ bool Init(const LpMetadata& metadata);
+
+ LpMetadataGeometry geometry_;
+ LpMetadataHeader header_;
+ std::vector<std::unique_ptr<Partition>> partitions_;
+};
+
+} // namespace fs_mgr
+} // namespace android
+
+#endif /* LIBLP_METADATA_BUILDER_H */
diff --git a/fs_mgr/liblp/include/liblp/metadata_format.h b/fs_mgr/liblp/include/liblp/metadata_format.h
new file mode 100644
index 0000000..8522435
--- /dev/null
+++ b/fs_mgr/liblp/include/liblp/metadata_format.h
@@ -0,0 +1,267 @@
+/*
+ * 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.
+ */
+
+#ifndef LOGICAL_PARTITION_METADATA_FORMAT_H_
+#define LOGICAL_PARTITION_METADATA_FORMAT_H_
+
+#ifdef __cplusplus
+#include <string>
+#include <vector>
+#endif
+
+#include <stdint.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/* Magic signature for LpMetadataGeometry. */
+#define LP_METADATA_GEOMETRY_MAGIC 0x616c4467
+
+/* Space reserved for geometry information. */
+#define LP_METADATA_GEOMETRY_SIZE 4096
+
+/* Magic signature for LpMetadataHeader. */
+#define LP_METADATA_HEADER_MAGIC 0x414C5030
+
+/* Current metadata version. */
+#define LP_METADATA_MAJOR_VERSION 1
+#define LP_METADATA_MINOR_VERSION 0
+
+/* Attributes for the LpMetadataPartition::attributes field.
+ *
+ * READONLY - The partition should not be considered writable. When used with
+ * device mapper, the block device will be created as read-only.
+ */
+#define LP_PARTITION_ATTR_NONE 0x0
+#define LP_PARTITION_ATTR_READONLY 0x1
+
+/* Mask that defines all valid attributes. */
+#define LP_PARTITION_ATTRIBUTE_MASK (LP_PARTITION_ATTR_READONLY)
+
+/* Default name of the physical partition that holds logical partition entries.
+ * The layout of this partition will look like:
+ *
+ * +--------------------+
+ * | Disk Geometry |
+ * +--------------------+
+ * | Metadata |
+ * +--------------------+
+ * | Logical Partitions |
+ * +--------------------+
+ * | Backup Metadata |
+ * +--------------------+
+ * | Geometry Backup |
+ * +--------------------+
+ */
+#define LP_METADATA_PARTITION_NAME "android"
+
+/* Size of a sector is always 512 bytes for compatibility with the Linux kernel. */
+#define LP_SECTOR_SIZE 512
+
+/* This structure is stored at sector 0 in the first 4096 bytes of the
+ * partition, and again in the very last 4096 bytes. It is never modified and
+ * describes how logical partition information can be located.
+ */
+typedef struct LpMetadataGeometry {
+ /* 0: Magic signature (LP_METADATA_GEOMETRY_MAGIC). */
+ uint32_t magic;
+
+ /* 4: Size of the LpMetadataGeometry struct. */
+ uint32_t struct_size;
+
+ /* 8: SHA256 checksum of this struct, with this field set to 0. */
+ uint8_t checksum[32];
+
+ /* 40: Maximum amount of space a single copy of the metadata can use. */
+ uint32_t metadata_max_size;
+
+ /* 44: Number of copies of the metadata to keep. For A/B devices, this
+ * will be 2. For an A/B/C device, it would be 3, et cetera. For Non-A/B
+ * it will be 1. A backup copy of each slot is kept, so if this is "2",
+ * there will be four copies total.
+ */
+ uint32_t metadata_slot_count;
+
+ /* 48: First usable sector for allocating logical partitions. this will be
+ * the first sector after the initial 4096 geometry block, followed by the
+ * space consumed by metadata_max_size*metadata_slot_count.
+ */
+ uint64_t first_logical_sector;
+
+ /* 56: Last usable sector, inclusive, for allocating logical partitions.
+ * At the end of this sector will follow backup metadata slots and the
+ * backup geometry block at the very end.
+ */
+ uint64_t last_logical_sector;
+} __attribute__((packed)) LpMetadataGeometry;
+
+/* The logical partition metadata has a number of tables; they are described
+ * in the header via the following structure.
+ *
+ * The size of the table can be computed by multiplying entry_size by
+ * num_entries, and the result must not overflow a 32-bit signed integer.
+ */
+typedef struct LpMetadataTableDescriptor {
+ /* 0: Location of the table, relative to the metadata header. */
+ uint32_t offset;
+ /* 4: Number of entries in the table. */
+ uint32_t num_entries;
+ /* 8: Size of each entry in the table, in bytes. */
+ uint32_t entry_size;
+} __attribute__((packed)) LpMetadataTableDescriptor;
+
+/* Binary format for the header of the logical partition metadata format.
+ *
+ * The format has three sections. The header must occur first, and the
+ * proceeding tables may be placed in any order after.
+ *
+ * +-----------------------------------------+
+ * | Header data - fixed size |
+ * +-----------------------------------------+
+ * | Partition table - variable size |
+ * +-----------------------------------------+
+ * | Partition table extents - variable size |
+ * +-----------------------------------------+
+ *
+ * The "Header" portion is described by LpMetadataHeader. It will always
+ * precede the other three blocks.
+ *
+ * All fields are stored in little-endian byte order when serialized.
+ *
+ * This struct is versioned; see the |major_version| and |minor_version|
+ * fields.
+ */
+typedef struct LpMetadataHeader {
+ /* 0: Four bytes equal to LP_METADATA_HEADER_MAGIC. */
+ uint32_t magic;
+
+ /* 4: Version number required to read this metadata. If the version is not
+ * equal to the library version, the metadata should be considered
+ * incompatible.
+ */
+ uint16_t major_version;
+
+ /* 6: Minor version. A library supporting newer features should be able to
+ * read metadata with an older minor version. However, an older library
+ * should not support reading metadata if its minor version is higher.
+ */
+ uint16_t minor_version;
+
+ /* 8: The size of this header struct. */
+ uint32_t header_size;
+
+ /* 12: SHA256 checksum of the header, up to |header_size| bytes, computed as
+ * if this field were set to 0.
+ */
+ uint8_t header_checksum[32];
+
+ /* 44: The total size of all tables. This size is contiguous; tables may not
+ * have gaps in between, and they immediately follow the header.
+ */
+ uint32_t tables_size;
+
+ /* 48: SHA256 checksum of all table contents. */
+ uint8_t tables_checksum[32];
+
+ /* 80: Partition table descriptor. */
+ LpMetadataTableDescriptor partitions;
+ /* 92: Extent table descriptor. */
+ LpMetadataTableDescriptor extents;
+} __attribute__((packed)) LpMetadataHeader;
+
+/* This struct defines a logical partition entry, similar to what would be
+ * present in a GUID Partition Table.
+ */
+typedef struct LpMetadataPartition {
+ /* 0: Name of this partition in ASCII characters. Any unused characters in
+ * the buffer must be set to 0. Characters may only be alphanumeric or _.
+ * The name must include at least one ASCII character, and it must be unique
+ * across all partition names. The length (36) is the same as the maximum
+ * length of a GPT partition name.
+ */
+ char name[36];
+
+ /* 36: Globally unique identifier (GUID) of this partition. */
+ uint8_t guid[16];
+
+ /* 52: Attributes for the partition (see LP_PARTITION_ATTR_* flags above). */
+ uint32_t attributes;
+
+ /* 56: Index of the first extent owned by this partition. The extent will
+ * start at logical sector 0. Gaps between extents are not allowed.
+ */
+ uint32_t first_extent_index;
+
+ /* 60: Number of extents in the partition. Every partition must have at
+ * least one extent.
+ */
+ uint32_t num_extents;
+} __attribute__((packed)) LpMetadataPartition;
+
+/* This extent is a dm-linear target, and the index is an index into the
+ * LinearExtent table.
+ */
+#define LP_TARGET_TYPE_LINEAR 0
+
+/* This extent is a dm-zero target. The index is ignored and must be 0. */
+#define LP_TARGET_TYPE_ZERO 1
+
+/* This struct defines an extent entry in the extent table block. */
+typedef struct LpMetadataExtent {
+ /* 0: Length of this extent, in 512-byte sectors. */
+ uint64_t num_sectors;
+
+ /* 8: Target type for device-mapper (see LP_TARGET_TYPE_* values). */
+ uint32_t target_type;
+
+ /* 12: Contents depends on target_type.
+ *
+ * LINEAR: The sector on the physical partition that this extent maps onto.
+ * ZERO: This field must be 0.
+ */
+ uint64_t target_data;
+} __attribute__((packed)) LpMetadataExtent;
+
+#ifdef __cplusplus
+} /* extern "C" */
+#endif
+
+#ifdef __cplusplus
+namespace android {
+namespace fs_mgr {
+
+// Helper structure for easily interpreting deserialized metadata, or
+// re-serializing metadata.
+struct LpMetadata {
+ LpMetadataGeometry geometry;
+ LpMetadataHeader header;
+ std::vector<LpMetadataPartition> partitions;
+ std::vector<LpMetadataExtent> extents;
+};
+
+// Helper to extract safe C++ strings from partition info.
+std::string GetPartitionName(const LpMetadataPartition& partition);
+std::string GetPartitionGuid(const LpMetadataPartition& partition);
+
+// Helper to return a slot number for a slot suffix.
+uint32_t SlotNumberForSlotSuffix(const std::string& suffix);
+
+} // namespace fs_mgr
+} // namespace android
+#endif
+
+#endif /* LOGICAL_PARTITION_METADATA_FORMAT_H_ */
diff --git a/fs_mgr/liblp/include/liblp/reader.h b/fs_mgr/liblp/include/liblp/reader.h
new file mode 100644
index 0000000..982fe65
--- /dev/null
+++ b/fs_mgr/liblp/include/liblp/reader.h
@@ -0,0 +1,46 @@
+/*
+ * 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.
+ */
+
+#ifndef LIBLP_READER_H_
+#define LIBLP_READER_H_
+
+#include <stddef.h>
+
+#include <memory>
+
+#include "metadata_format.h"
+
+namespace android {
+namespace fs_mgr {
+
+// Read logical partition metadata from its predetermined location on a block
+// device. If readback fails, we also attempt to load from a backup copy.
+std::unique_ptr<LpMetadata> ReadMetadata(const char* block_device, uint32_t slot_number);
+std::unique_ptr<LpMetadata> ReadMetadata(int fd, uint32_t slot_number);
+
+// Read and validate the logical partition geometry from a block device.
+bool ReadLogicalPartitionGeometry(const char* block_device, LpMetadataGeometry* geometry);
+bool ReadLogicalPartitionGeometry(int fd, LpMetadataGeometry* geometry);
+
+// Read logical partition metadata from an image file that was created with
+// WriteToImageFile().
+std::unique_ptr<LpMetadata> ReadFromImageFile(const char* file);
+std::unique_ptr<LpMetadata> ReadFromImageFile(int fd);
+
+} // namespace fs_mgr
+} // namespace android
+
+#endif /* LIBLP_READER_H_ */
diff --git a/fs_mgr/liblp/include/liblp/writer.h b/fs_mgr/liblp/include/liblp/writer.h
new file mode 100644
index 0000000..efa409d
--- /dev/null
+++ b/fs_mgr/liblp/include/liblp/writer.h
@@ -0,0 +1,56 @@
+/*
+ * 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.
+ */
+
+#ifndef LIBLP_WRITER_H
+#define LIBLP_WRITER_H
+
+#include "metadata_format.h"
+
+namespace android {
+namespace fs_mgr {
+
+// When flashing the initial logical partition layout, we also write geometry
+// information at the start and end of the big physical partition. This helps
+// locate metadata and backup metadata in the case of corruption or a failed
+// update. For normal changes to the metadata, we never modify the geometry.
+enum class SyncMode {
+ // Write geometry information.
+ Flash,
+ // Normal update of a single slot.
+ Update
+};
+
+// Write the given partition table to the given block device, writing only
+// copies according to the given sync mode.
+//
+// This will perform some verification, such that the device has enough space
+// to store the metadata as well as all of its extents.
+//
+// The slot number indicates which metadata slot to use.
+bool WritePartitionTable(const char* block_device, const LpMetadata& metadata, SyncMode sync_mode,
+ uint32_t slot_number);
+bool WritePartitionTable(int fd, const LpMetadata& metadata, SyncMode sync_mode,
+ uint32_t slot_number);
+
+// Helper function to serialize geometry and metadata to a normal file, for
+// flashing or debugging.
+bool WriteToImageFile(const char* file, const LpMetadata& metadata);
+bool WriteToImageFile(int fd, const LpMetadata& metadata);
+
+} // namespace fs_mgr
+} // namespace android
+
+#endif /* LIBLP_WRITER_H */
diff --git a/fs_mgr/liblp/io_test.cpp b/fs_mgr/liblp/io_test.cpp
new file mode 100644
index 0000000..2595654
--- /dev/null
+++ b/fs_mgr/liblp/io_test.cpp
@@ -0,0 +1,395 @@
+/*
+ * 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 <fcntl.h>
+#include <linux/memfd.h>
+#include <stdio.h>
+#include <sys/syscall.h>
+
+#include <android-base/file.h>
+#include <android-base/unique_fd.h>
+#include <gtest/gtest.h>
+#include <liblp/builder.h>
+#include <liblp/reader.h>
+#include <liblp/writer.h>
+
+#include "utility.h"
+
+using namespace std;
+using namespace android::fs_mgr;
+using unique_fd = android::base::unique_fd;
+
+// Our tests assume a 128KiB disk with two 512 byte metadata slots.
+static const size_t kDiskSize = 131072;
+static const size_t kMetadataSize = 512;
+static const size_t kMetadataSlots = 2;
+static const char* TEST_GUID_BASE = "A799D1D6-669F-41D8-A3F0-EBB7572D830";
+static const char* TEST_GUID = "A799D1D6-669F-41D8-A3F0-EBB7572D8302";
+
+// Helper function for creating an in-memory file descriptor. This lets us
+// simulate read/writing logical partition metadata as if we had a block device
+// for a physical partition.
+static unique_fd CreateFakeDisk(off_t size) {
+ unique_fd fd(syscall(__NR_memfd_create, "fake_disk", MFD_ALLOW_SEALING));
+ if (fd < 0) {
+ perror("memfd_create");
+ return {};
+ }
+ if (ftruncate(fd, size) < 0) {
+ perror("ftruncate");
+ return {};
+ }
+ // Prevent anything from accidentally growing/shrinking the file, as it
+ // would not be allowed on an actual partition.
+ if (fcntl(fd, F_ADD_SEALS, F_SEAL_GROW | F_SEAL_SHRINK) < 0) {
+ perror("fcntl");
+ return {};
+ }
+ // Write garbage to the "disk" so we can tell what has been zeroed or not.
+ unique_ptr<uint8_t[]> buffer = make_unique<uint8_t[]>(size);
+ memset(buffer.get(), 0xcc, size);
+ if (!android::base::WriteFully(fd, buffer.get(), size)) {
+ return {};
+ }
+ return fd;
+}
+
+// Create a disk of the default size.
+static unique_fd CreateFakeDisk() {
+ return CreateFakeDisk(kDiskSize);
+}
+
+// Create a MetadataBuilder around some default sizes.
+static unique_ptr<MetadataBuilder> CreateDefaultBuilder() {
+ unique_ptr<MetadataBuilder> builder =
+ MetadataBuilder::New(kDiskSize, kMetadataSize, kMetadataSlots);
+ return builder;
+}
+
+static bool AddDefaultPartitions(MetadataBuilder* builder) {
+ Partition* system = builder->AddPartition("system", TEST_GUID, LP_PARTITION_ATTR_NONE);
+ if (!system) {
+ return false;
+ }
+ return builder->GrowPartition(system, 24 * 1024);
+}
+
+// Create a temporary disk and flash it with the default partition setup.
+static unique_fd CreateFlashedDisk() {
+ unique_ptr<MetadataBuilder> builder = CreateDefaultBuilder();
+ if (!builder || !AddDefaultPartitions(builder.get())) {
+ return {};
+ }
+ unique_fd fd = CreateFakeDisk();
+ if (fd < 0) {
+ return {};
+ }
+ // Export and flash.
+ unique_ptr<LpMetadata> exported = builder->Export();
+ if (!exported) {
+ return {};
+ }
+ if (!WritePartitionTable(fd, *exported.get(), SyncMode::Flash, 0)) {
+ return {};
+ }
+ return fd;
+}
+
+// Test that our CreateFakeDisk() function works.
+TEST(liblp, CreateFakeDisk) {
+ unique_fd fd = CreateFakeDisk();
+ ASSERT_GE(fd, 0);
+
+ uint64_t size;
+ ASSERT_TRUE(GetDescriptorSize(fd, &size));
+ ASSERT_EQ(size, kDiskSize);
+}
+
+// Flashing metadata should not work if the metadata was created for a larger
+// disk than the destination disk.
+TEST(liblp, ExportDiskTooSmall) {
+ unique_ptr<MetadataBuilder> builder = MetadataBuilder::New(kDiskSize + 1024, 512, 2);
+ ASSERT_NE(builder, nullptr);
+ unique_ptr<LpMetadata> exported = builder->Export();
+ ASSERT_NE(exported, nullptr);
+
+ // A larger geometry should fail to flash, since there won't be enough
+ // space to store the logical partition range that was specified.
+ unique_fd fd = CreateFakeDisk();
+ ASSERT_GE(fd, 0);
+
+ EXPECT_FALSE(WritePartitionTable(fd, *exported.get(), SyncMode::Flash, 0));
+}
+
+// Test the basics of flashing a partition and reading it back.
+TEST(liblp, FlashAndReadback) {
+ unique_ptr<MetadataBuilder> builder = CreateDefaultBuilder();
+ ASSERT_NE(builder, nullptr);
+ ASSERT_TRUE(AddDefaultPartitions(builder.get()));
+
+ unique_fd fd = CreateFakeDisk();
+ ASSERT_GE(fd, 0);
+
+ // Export and flash.
+ unique_ptr<LpMetadata> exported = builder->Export();
+ ASSERT_NE(exported, nullptr);
+ ASSERT_TRUE(WritePartitionTable(fd, *exported.get(), SyncMode::Flash, 0));
+
+ // Read back. Note that some fields are only filled in during
+ // serialization, so exported and imported will not be identical. For
+ // example, table sizes and checksums are computed in WritePartitionTable.
+ // Therefore we check on a field-by-field basis.
+ unique_ptr<LpMetadata> imported = ReadMetadata(fd, 0);
+ ASSERT_NE(imported, nullptr);
+
+ // Check geometry and header.
+ EXPECT_EQ(exported->geometry.metadata_max_size, imported->geometry.metadata_max_size);
+ EXPECT_EQ(exported->geometry.metadata_slot_count, imported->geometry.metadata_slot_count);
+ EXPECT_EQ(exported->geometry.first_logical_sector, imported->geometry.first_logical_sector);
+ EXPECT_EQ(exported->geometry.last_logical_sector, imported->geometry.last_logical_sector);
+ EXPECT_EQ(exported->header.major_version, imported->header.major_version);
+ EXPECT_EQ(exported->header.minor_version, imported->header.minor_version);
+ EXPECT_EQ(exported->header.header_size, imported->header.header_size);
+
+ // Check partition tables.
+ ASSERT_EQ(exported->partitions.size(), imported->partitions.size());
+ EXPECT_EQ(GetPartitionName(exported->partitions[0]), GetPartitionName(imported->partitions[0]));
+ EXPECT_EQ(GetPartitionGuid(exported->partitions[0]), GetPartitionGuid(imported->partitions[0]));
+ EXPECT_EQ(exported->partitions[0].attributes, imported->partitions[0].attributes);
+ EXPECT_EQ(exported->partitions[0].first_extent_index,
+ imported->partitions[0].first_extent_index);
+ EXPECT_EQ(exported->partitions[0].num_extents, imported->partitions[0].num_extents);
+
+ // Check extent tables.
+ ASSERT_EQ(exported->extents.size(), imported->extents.size());
+ EXPECT_EQ(exported->extents[0].num_sectors, imported->extents[0].num_sectors);
+ EXPECT_EQ(exported->extents[0].target_type, imported->extents[0].target_type);
+ EXPECT_EQ(exported->extents[0].target_data, imported->extents[0].target_data);
+}
+
+// Test that we can update metadata slots without disturbing others.
+TEST(liblp, UpdateAnyMetadataSlot) {
+ unique_fd fd = CreateFlashedDisk();
+ ASSERT_GE(fd, 0);
+
+ unique_ptr<LpMetadata> imported = ReadMetadata(fd, 0);
+ ASSERT_NE(imported, nullptr);
+ ASSERT_EQ(imported->partitions.size(), 1);
+ EXPECT_EQ(GetPartitionName(imported->partitions[0]), "system");
+
+ // Verify that we can't read unwritten metadata.
+ ASSERT_EQ(ReadMetadata(fd, 1), nullptr);
+
+ // Change the name before writing to the next slot.
+ strncpy(imported->partitions[0].name, "vendor", sizeof(imported->partitions[0].name));
+ ASSERT_TRUE(WritePartitionTable(fd, *imported.get(), SyncMode::Update, 1));
+
+ // Read back the original slot, make sure it hasn't changed.
+ imported = ReadMetadata(fd, 0);
+ ASSERT_NE(imported, nullptr);
+ ASSERT_EQ(imported->partitions.size(), 1);
+ EXPECT_EQ(GetPartitionName(imported->partitions[0]), "system");
+
+ // Now read back the new slot, and verify that it has a different name.
+ imported = ReadMetadata(fd, 1);
+ ASSERT_NE(imported, nullptr);
+ ASSERT_EQ(imported->partitions.size(), 1);
+ EXPECT_EQ(GetPartitionName(imported->partitions[0]), "vendor");
+
+ // Verify that we didn't overwrite anything in the logical paritition area.
+ // We expect the disk to be filled with 0xcc on creation so we can read
+ // this back and compare it.
+ char expected[LP_SECTOR_SIZE];
+ memset(expected, 0xcc, sizeof(expected));
+ for (uint64_t i = imported->geometry.first_logical_sector;
+ i <= imported->geometry.last_logical_sector; i++) {
+ char buffer[LP_SECTOR_SIZE];
+ ASSERT_GE(lseek(fd, i * LP_SECTOR_SIZE, SEEK_SET), 0);
+ ASSERT_TRUE(android::base::ReadFully(fd, buffer, sizeof(buffer)));
+ ASSERT_EQ(memcmp(expected, buffer, LP_SECTOR_SIZE), 0);
+ }
+}
+
+TEST(liblp, InvalidMetadataSlot) {
+ unique_fd fd = CreateFlashedDisk();
+ ASSERT_GE(fd, 0);
+
+ // Make sure all slots are filled.
+ unique_ptr<LpMetadata> metadata = ReadMetadata(fd, 0);
+ ASSERT_NE(metadata, nullptr);
+ for (uint32_t i = 1; i < kMetadataSlots; i++) {
+ ASSERT_TRUE(WritePartitionTable(fd, *metadata.get(), SyncMode::Update, i));
+ }
+
+ // Verify that we can't read unavailable slots.
+ EXPECT_EQ(ReadMetadata(fd, kMetadataSlots), nullptr);
+}
+
+// Test that updating a metadata slot does not allow it to be computed based
+// on mismatching geometry.
+TEST(liblp, NoChangingGeometry) {
+ unique_fd fd = CreateFlashedDisk();
+ ASSERT_GE(fd, 0);
+
+ unique_ptr<LpMetadata> imported = ReadMetadata(fd, 0);
+ ASSERT_NE(imported, nullptr);
+ ASSERT_TRUE(WritePartitionTable(fd, *imported.get(), SyncMode::Update, 1));
+
+ imported->geometry.metadata_max_size += LP_SECTOR_SIZE;
+ ASSERT_FALSE(WritePartitionTable(fd, *imported.get(), SyncMode::Update, 1));
+
+ imported = ReadMetadata(fd, 0);
+ ASSERT_NE(imported, nullptr);
+ imported->geometry.metadata_slot_count++;
+ ASSERT_FALSE(WritePartitionTable(fd, *imported.get(), SyncMode::Update, 1));
+
+ imported = ReadMetadata(fd, 0);
+ ASSERT_NE(imported, nullptr);
+ imported->geometry.first_logical_sector++;
+ ASSERT_FALSE(WritePartitionTable(fd, *imported.get(), SyncMode::Update, 1));
+
+ imported = ReadMetadata(fd, 0);
+ ASSERT_NE(imported, nullptr);
+ imported->geometry.last_logical_sector--;
+ ASSERT_FALSE(WritePartitionTable(fd, *imported.get(), SyncMode::Update, 1));
+}
+
+// Test that changing one bit of metadata is enough to break the checksum.
+TEST(liblp, BitFlipGeometry) {
+ unique_fd fd = CreateFlashedDisk();
+ ASSERT_GE(fd, 0);
+
+ LpMetadataGeometry geometry;
+ ASSERT_GE(lseek(fd, 0, SEEK_SET), 0);
+ ASSERT_TRUE(android::base::ReadFully(fd, &geometry, sizeof(geometry)));
+
+ LpMetadataGeometry bad_geometry = geometry;
+ bad_geometry.metadata_slot_count++;
+ ASSERT_TRUE(android::base::WriteFully(fd, &bad_geometry, sizeof(bad_geometry)));
+
+ unique_ptr<LpMetadata> metadata = ReadMetadata(fd, 0);
+ ASSERT_NE(metadata, nullptr);
+ EXPECT_EQ(metadata->geometry.metadata_slot_count, 2);
+}
+
+TEST(liblp, ReadBackupGeometry) {
+ unique_fd fd = CreateFlashedDisk();
+ ASSERT_GE(fd, 0);
+
+ char corruption[LP_METADATA_GEOMETRY_SIZE];
+ memset(corruption, 0xff, sizeof(corruption));
+
+ // Corrupt the first 4096 bytes of the disk.
+ ASSERT_GE(lseek(fd, 0, SEEK_SET), 0);
+ ASSERT_TRUE(android::base::WriteFully(fd, corruption, sizeof(corruption)));
+ EXPECT_NE(ReadMetadata(fd, 0), nullptr);
+
+ // Corrupt the last 4096 bytes too.
+ ASSERT_GE(lseek(fd, -LP_METADATA_GEOMETRY_SIZE, SEEK_END), 0);
+ ASSERT_TRUE(android::base::WriteFully(fd, corruption, sizeof(corruption)));
+ EXPECT_EQ(ReadMetadata(fd, 0), nullptr);
+}
+
+TEST(liblp, ReadBackupMetadata) {
+ unique_fd fd = CreateFlashedDisk();
+ ASSERT_GE(fd, 0);
+
+ unique_ptr<LpMetadata> metadata = ReadMetadata(fd, 0);
+
+ char corruption[kMetadataSize];
+ memset(corruption, 0xff, sizeof(corruption));
+
+ ASSERT_GE(lseek(fd, LP_METADATA_GEOMETRY_SIZE, SEEK_SET), 0);
+ ASSERT_TRUE(android::base::WriteFully(fd, corruption, sizeof(corruption)));
+ EXPECT_NE(ReadMetadata(fd, 0), nullptr);
+
+ off_t offset = LP_METADATA_GEOMETRY_SIZE + kMetadataSize * 2;
+
+ // Corrupt the backup metadata.
+ ASSERT_GE(lseek(fd, -offset, SEEK_END), 0);
+ ASSERT_TRUE(android::base::WriteFully(fd, corruption, sizeof(corruption)));
+ EXPECT_EQ(ReadMetadata(fd, 0), nullptr);
+}
+
+// Test that we don't attempt to write metadata if it would overflow its
+// reserved space.
+TEST(liblp, TooManyPartitions) {
+ unique_ptr<MetadataBuilder> builder = CreateDefaultBuilder();
+ ASSERT_NE(builder, nullptr);
+
+ // Compute the maximum number of partitions we can fit in 1024 bytes of metadata.
+ size_t max_partitions = (kMetadataSize - sizeof(LpMetadataHeader)) / sizeof(LpMetadataPartition);
+ EXPECT_LT(max_partitions, 10);
+
+ // Add this number of partitions.
+ Partition* partition = nullptr;
+ for (size_t i = 0; i < max_partitions; i++) {
+ std::string guid = std::string(TEST_GUID) + to_string(i);
+ partition = builder->AddPartition(to_string(i), TEST_GUID, LP_PARTITION_ATTR_NONE);
+ ASSERT_NE(partition, nullptr);
+ }
+ ASSERT_NE(partition, nullptr);
+ // Add one extent to any partition to fill up more space - we're at 508
+ // bytes after this, out of 512.
+ ASSERT_TRUE(builder->GrowPartition(partition, 1024));
+
+ unique_ptr<LpMetadata> exported = builder->Export();
+ ASSERT_NE(exported, nullptr);
+
+ unique_fd fd = CreateFakeDisk();
+ ASSERT_GE(fd, 0);
+
+ // Check that we are able to write our table.
+ ASSERT_TRUE(WritePartitionTable(fd, *exported.get(), SyncMode::Flash, 0));
+ ASSERT_TRUE(WritePartitionTable(fd, *exported.get(), SyncMode::Update, 1));
+
+ // Check that adding one more partition overflows the metadata allotment.
+ partition = builder->AddPartition("final", TEST_GUID, LP_PARTITION_ATTR_NONE);
+ EXPECT_NE(partition, nullptr);
+
+ exported = builder->Export();
+ ASSERT_NE(exported, nullptr);
+
+ // The new table should be too large to be written.
+ ASSERT_FALSE(WritePartitionTable(fd, *exported.get(), SyncMode::Update, 1));
+
+ // Check that the first and last logical sectors weren't touched when we
+ // wrote this almost-full metadata.
+ char expected[LP_SECTOR_SIZE];
+ memset(expected, 0xcc, sizeof(expected));
+ char buffer[LP_SECTOR_SIZE];
+ ASSERT_GE(lseek(fd, exported->geometry.first_logical_sector * LP_SECTOR_SIZE, SEEK_SET), 0);
+ ASSERT_TRUE(android::base::ReadFully(fd, buffer, sizeof(buffer)));
+ EXPECT_EQ(memcmp(expected, buffer, LP_SECTOR_SIZE), 0);
+ ASSERT_GE(lseek(fd, exported->geometry.last_logical_sector * LP_SECTOR_SIZE, SEEK_SET), 0);
+ ASSERT_TRUE(android::base::ReadFully(fd, buffer, sizeof(buffer)));
+ EXPECT_EQ(memcmp(expected, buffer, LP_SECTOR_SIZE), 0);
+}
+
+// Test that we can read and write image files.
+TEST(liblp, ImageFiles) {
+ unique_ptr<MetadataBuilder> builder = CreateDefaultBuilder();
+ ASSERT_NE(builder, nullptr);
+ ASSERT_TRUE(AddDefaultPartitions(builder.get()));
+ unique_ptr<LpMetadata> exported = builder->Export();
+
+ unique_fd fd(syscall(__NR_memfd_create, "image_file", 0));
+ ASSERT_GE(fd, 0);
+ ASSERT_TRUE(WriteToImageFile(fd, *exported.get()));
+
+ unique_ptr<LpMetadata> imported = ReadFromImageFile(fd);
+ ASSERT_NE(imported, nullptr);
+}
diff --git a/fs_mgr/liblp/reader.cpp b/fs_mgr/liblp/reader.cpp
new file mode 100644
index 0000000..7938186
--- /dev/null
+++ b/fs_mgr/liblp/reader.cpp
@@ -0,0 +1,324 @@
+/*
+ * 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 "liblp/reader.h"
+
+#include <stddef.h>
+#include <stdlib.h>
+#include <unistd.h>
+
+#include <functional>
+
+#include <android-base/file.h>
+#include <android-base/unique_fd.h>
+
+#include "utility.h"
+
+namespace android {
+namespace fs_mgr {
+
+// Parse an LpMetadataGeometry from a buffer. The buffer must be at least
+// LP_METADATA_GEOMETRY_SIZE bytes in size.
+static bool ParseGeometry(const void* buffer, LpMetadataGeometry* geometry) {
+ static_assert(sizeof(*geometry) <= LP_METADATA_GEOMETRY_SIZE);
+ memcpy(geometry, buffer, sizeof(*geometry));
+
+ // Check the magic signature.
+ if (geometry->magic != LP_METADATA_GEOMETRY_MAGIC) {
+ LERROR << "Logical partition metadata has invalid geometry magic signature.";
+ return false;
+ }
+ // Recompute and check the CRC32.
+ {
+ LpMetadataGeometry temp = *geometry;
+ memset(&temp.checksum, 0, sizeof(temp.checksum));
+ SHA256(&temp, sizeof(temp), temp.checksum);
+ if (memcmp(temp.checksum, geometry->checksum, sizeof(temp.checksum)) != 0) {
+ LERROR << "Logical partition metadata has invalid geometry checksum.";
+ return false;
+ }
+ }
+ // Check that the struct size is equal (this will have to change if we ever
+ // change the struct size in a release).
+ if (geometry->struct_size != sizeof(LpMetadataGeometry)) {
+ LERROR << "Logical partition metadata has invalid struct size.";
+ return false;
+ }
+ if (geometry->metadata_slot_count == 0) {
+ LERROR << "Logical partition metadata has invalid slot count.";
+ return false;
+ }
+
+ // Check that the metadata area and logical partition areas don't overlap.
+ int64_t end_of_metadata =
+ GetPrimaryMetadataOffset(*geometry, geometry->metadata_slot_count - 1) +
+ geometry->metadata_max_size;
+ if (uint64_t(end_of_metadata) > geometry->first_logical_sector * LP_SECTOR_SIZE) {
+ LERROR << "Logical partition metadata overlaps with logical partition contents.";
+ return false;
+ }
+ return true;
+}
+
+// Read and validate geometry information from a block device that holds
+// logical partitions. If the information is corrupted, this will attempt
+// to read it from a secondary backup location.
+bool ReadLogicalPartitionGeometry(int fd, LpMetadataGeometry* geometry) {
+ // Read the first 4096 bytes.
+ std::unique_ptr<uint8_t[]> buffer = std::make_unique<uint8_t[]>(LP_METADATA_GEOMETRY_SIZE);
+ if (SeekFile64(fd, 0, SEEK_SET) < 0) {
+ PERROR << __PRETTY_FUNCTION__ << "lseek failed";
+ return false;
+ }
+ if (!android::base::ReadFully(fd, buffer.get(), LP_METADATA_GEOMETRY_SIZE)) {
+ PERROR << __PRETTY_FUNCTION__ << "read " << LP_METADATA_GEOMETRY_SIZE << " bytes failed";
+ return false;
+ }
+ if (ParseGeometry(buffer.get(), geometry)) {
+ return true;
+ }
+
+ // Try the backup copy in the last 4096 bytes.
+ if (SeekFile64(fd, -LP_METADATA_GEOMETRY_SIZE, SEEK_END) < 0) {
+ PERROR << __PRETTY_FUNCTION__ << "lseek failed, offset " << -LP_METADATA_GEOMETRY_SIZE;
+ return false;
+ }
+ if (!android::base::ReadFully(fd, buffer.get(), LP_METADATA_GEOMETRY_SIZE)) {
+ PERROR << __PRETTY_FUNCTION__ << "backup read " << LP_METADATA_GEOMETRY_SIZE
+ << " bytes failed";
+ return false;
+ }
+ return ParseGeometry(buffer.get(), geometry);
+}
+
+// Helper function to read geometry from a device without an open descriptor.
+bool ReadLogicalPartitionGeometry(const char* block_device, LpMetadataGeometry* geometry) {
+ android::base::unique_fd fd(open(block_device, O_RDONLY));
+ if (fd < 0) {
+ PERROR << __PRETTY_FUNCTION__ << "open failed: " << block_device;
+ return false;
+ }
+ return ReadLogicalPartitionGeometry(fd, geometry);
+}
+
+static bool ValidateTableBounds(const LpMetadataHeader& header,
+ const LpMetadataTableDescriptor& table) {
+ if (table.offset > header.tables_size) {
+ return false;
+ }
+ uint64_t table_size = uint64_t(table.num_entries) * table.entry_size;
+ if (header.tables_size - table.offset < table_size) {
+ return false;
+ }
+ return true;
+}
+
+static bool ValidateMetadataHeader(const LpMetadataHeader& header) {
+ // To compute the header's checksum, we have to temporarily set its checksum
+ // field to 0.
+ {
+ LpMetadataHeader temp = header;
+ memset(&temp.header_checksum, 0, sizeof(temp.header_checksum));
+ SHA256(&temp, sizeof(temp), temp.header_checksum);
+ if (memcmp(temp.header_checksum, header.header_checksum, sizeof(temp.header_checksum)) != 0) {
+ LERROR << "Logical partition metadata has invalid checksum.";
+ return false;
+ }
+ }
+
+ // Do basic validation of key metadata bits.
+ if (header.magic != LP_METADATA_HEADER_MAGIC) {
+ LERROR << "Logical partition metadata has invalid magic value.";
+ return false;
+ }
+ // Check that the version is compatible.
+ if (header.major_version != LP_METADATA_MAJOR_VERSION ||
+ header.minor_version > LP_METADATA_MINOR_VERSION) {
+ LERROR << "Logical partition metadata has incompatible version.";
+ return false;
+ }
+ if (!ValidateTableBounds(header, header.partitions) ||
+ !ValidateTableBounds(header, header.extents)) {
+ LERROR << "Logical partition metadata has invalid table bounds.";
+ return false;
+ }
+ // Check that table entry sizes can accomodate their respective structs. If
+ // table sizes change, these checks will have to be adjusted.
+ if (header.partitions.entry_size != sizeof(LpMetadataPartition)) {
+ LERROR << "Logical partition metadata has invalid partition table entry size.";
+ return false;
+ }
+ if (header.extents.entry_size != sizeof(LpMetadataExtent)) {
+ LERROR << "Logical partition metadata has invalid extent table entry size.";
+ return false;
+ }
+ return true;
+}
+
+using ReadMetadataFn = std::function<bool(void* buffer, size_t num_bytes)>;
+
+// Parse and validate all metadata at the current position in the given file
+// descriptor.
+static std::unique_ptr<LpMetadata> ParseMetadata(int fd) {
+ // First read and validate the header.
+ std::unique_ptr<LpMetadata> metadata = std::make_unique<LpMetadata>();
+ if (!android::base::ReadFully(fd, &metadata->header, sizeof(metadata->header))) {
+ PERROR << __PRETTY_FUNCTION__ << "read " << sizeof(metadata->header) << "bytes failed";
+ return nullptr;
+ }
+ if (!ValidateMetadataHeader(metadata->header)) {
+ return nullptr;
+ }
+
+ LpMetadataHeader& header = metadata->header;
+
+ // Read the metadata payload. Allocation is fallible in case the metadata is
+ // corrupt and has some huge value.
+ std::unique_ptr<uint8_t[]> buffer(new (std::nothrow) uint8_t[header.tables_size]);
+ if (!buffer) {
+ LERROR << "Out of memory reading logical partition tables.";
+ return nullptr;
+ }
+ if (!android::base::ReadFully(fd, buffer.get(), header.tables_size)) {
+ PERROR << __PRETTY_FUNCTION__ << "read " << header.tables_size << "bytes failed";
+ return nullptr;
+ }
+
+ uint8_t checksum[32];
+ SHA256(buffer.get(), header.tables_size, checksum);
+ if (memcmp(checksum, header.tables_checksum, sizeof(checksum)) != 0) {
+ LERROR << "Logical partition metadata has invalid table checksum.";
+ return nullptr;
+ }
+
+ // ValidateTableSize ensured that |cursor| is valid for the number of
+ // entries in the table.
+ uint8_t* cursor = buffer.get() + header.partitions.offset;
+ for (size_t i = 0; i < header.partitions.num_entries; i++) {
+ LpMetadataPartition partition;
+ memcpy(&partition, cursor, sizeof(partition));
+ cursor += header.partitions.entry_size;
+
+ if (partition.attributes & ~LP_PARTITION_ATTRIBUTE_MASK) {
+ LERROR << "Logical partition has invalid attribute set.";
+ return nullptr;
+ }
+ if (partition.first_extent_index + partition.num_extents > header.extents.num_entries) {
+ LERROR << "Logical partition has invalid extent list.";
+ return nullptr;
+ }
+
+ metadata->partitions.push_back(partition);
+ }
+
+ cursor = buffer.get() + header.extents.offset;
+ for (size_t i = 0; i < header.extents.num_entries; i++) {
+ LpMetadataExtent extent;
+ memcpy(&extent, cursor, sizeof(extent));
+ cursor += header.extents.entry_size;
+
+ metadata->extents.push_back(extent);
+ }
+
+ return metadata;
+}
+
+std::unique_ptr<LpMetadata> ReadMetadata(int fd, uint32_t slot_number) {
+ LpMetadataGeometry geometry;
+ if (!ReadLogicalPartitionGeometry(fd, &geometry)) {
+ return nullptr;
+ }
+
+ if (slot_number >= geometry.metadata_slot_count) {
+ LERROR << __PRETTY_FUNCTION__ << "invalid metadata slot number";
+ return nullptr;
+ }
+
+ // First try the primary copy.
+ int64_t offset = GetPrimaryMetadataOffset(geometry, slot_number);
+ if (SeekFile64(fd, offset, SEEK_SET) < 0) {
+ PERROR << __PRETTY_FUNCTION__ << "lseek failed: offset " << offset;
+ return nullptr;
+ }
+ std::unique_ptr<LpMetadata> metadata = ParseMetadata(fd);
+
+ // If the primary copy failed, try the backup copy.
+ if (!metadata) {
+ offset = GetBackupMetadataOffset(geometry, slot_number);
+ if (SeekFile64(fd, offset, SEEK_END) < 0) {
+ PERROR << __PRETTY_FUNCTION__ << "lseek failed: offset " << offset;
+ return nullptr;
+ }
+ metadata = ParseMetadata(fd);
+ }
+
+ if (metadata) {
+ metadata->geometry = geometry;
+ }
+ return metadata;
+}
+
+std::unique_ptr<LpMetadata> ReadMetadata(const char* block_device, uint32_t slot_number) {
+ android::base::unique_fd fd(open(block_device, O_RDONLY));
+ if (fd < 0) {
+ PERROR << __PRETTY_FUNCTION__ << "open failed: " << block_device;
+ return nullptr;
+ }
+ return ReadMetadata(fd, slot_number);
+}
+
+std::unique_ptr<LpMetadata> ReadFromImageFile(int fd) {
+ LpMetadataGeometry geometry;
+ if (!ReadLogicalPartitionGeometry(fd, &geometry)) {
+ return nullptr;
+ }
+ if (SeekFile64(fd, LP_METADATA_GEOMETRY_SIZE, SEEK_SET) < 0) {
+ PERROR << __PRETTY_FUNCTION__ << "lseek failed: offset " << LP_METADATA_GEOMETRY_SIZE;
+ return nullptr;
+ }
+ std::unique_ptr<LpMetadata> metadata = ParseMetadata(fd);
+ if (!metadata) {
+ return nullptr;
+ }
+ metadata->geometry = geometry;
+ return metadata;
+}
+
+std::unique_ptr<LpMetadata> ReadFromImageFile(const char* file) {
+ android::base::unique_fd fd(open(file, O_RDONLY));
+ if (fd < 0) {
+ PERROR << __PRETTY_FUNCTION__ << "open failed: " << file;
+ return nullptr;
+ }
+ return ReadFromImageFile(fd);
+}
+
+static std::string NameFromFixedArray(const char* name, size_t buffer_size) {
+ // If the end of the buffer has a null character, it's safe to assume the
+ // buffer is null terminated. Otherwise, we cap the string to the input
+ // buffer size.
+ if (name[buffer_size - 1] == '\0') {
+ return std::string(name);
+ }
+ return std::string(name, buffer_size);
+}
+
+std::string GetPartitionName(const LpMetadataPartition& partition) {
+ return NameFromFixedArray(partition.name, sizeof(partition.name));
+}
+
+} // namespace fs_mgr
+} // namespace android
diff --git a/fs_mgr/liblp/utility.cpp b/fs_mgr/liblp/utility.cpp
new file mode 100644
index 0000000..5310cab
--- /dev/null
+++ b/fs_mgr/liblp/utility.cpp
@@ -0,0 +1,104 @@
+/*
+ * 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 <fcntl.h>
+#include <stdint.h>
+#include <sys/ioctl.h>
+#include <sys/stat.h>
+#include <unistd.h>
+
+#include <android-base/file.h>
+#include <ext4_utils/ext4_utils.h>
+#include <openssl/sha.h>
+#include <uuid/uuid.h>
+
+#include "utility.h"
+
+namespace android {
+namespace fs_mgr {
+
+bool GetDescriptorSize(int fd, uint64_t* size) {
+ struct stat s;
+ if (fstat(fd, &s) < 0) {
+ PERROR << __PRETTY_FUNCTION__ << "fstat failed";
+ return false;
+ }
+
+ if (S_ISBLK(s.st_mode)) {
+ return get_block_device_size(fd);
+ }
+
+ int64_t result = SeekFile64(fd, 0, SEEK_END);
+ if (result == -1) {
+ PERROR << __PRETTY_FUNCTION__ << "lseek failed";
+ return false;
+ }
+
+ *size = result;
+ return true;
+}
+
+int64_t SeekFile64(int fd, int64_t offset, int whence) {
+ static_assert(sizeof(off_t) == sizeof(int64_t), "Need 64-bit lseek");
+ return lseek(fd, offset, whence);
+}
+
+int64_t GetPrimaryMetadataOffset(const LpMetadataGeometry& geometry, uint32_t slot_number) {
+ CHECK(slot_number < geometry.metadata_slot_count);
+
+ int64_t offset = LP_METADATA_GEOMETRY_SIZE + geometry.metadata_max_size * slot_number;
+ CHECK(offset + geometry.metadata_max_size <=
+ int64_t(geometry.first_logical_sector * LP_SECTOR_SIZE));
+ return offset;
+}
+
+int64_t GetBackupMetadataOffset(const LpMetadataGeometry& geometry, uint32_t slot_number) {
+ CHECK(slot_number < geometry.metadata_slot_count);
+ int64_t start = int64_t(-LP_METADATA_GEOMETRY_SIZE) -
+ int64_t(geometry.metadata_max_size) * geometry.metadata_slot_count;
+ return start + int64_t(geometry.metadata_max_size * slot_number);
+}
+
+void SHA256(const void* data, size_t length, uint8_t out[32]) {
+ SHA256_CTX c;
+ SHA256_Init(&c);
+ SHA256_Update(&c, data, length);
+ SHA256_Final(out, &c);
+}
+
+std::string GetPartitionGuid(const LpMetadataPartition& partition) {
+ // 32 hex characters, four hyphens. Unfortunately libext2_uuid provides no
+ // macro to assist with buffer sizing.
+ static const size_t kGuidLen = 36;
+ char buffer[kGuidLen + 1];
+ uuid_unparse_upper(partition.guid, buffer);
+ return buffer;
+}
+
+uint32_t SlotNumberForSlotSuffix(const std::string& suffix) {
+ if (suffix.empty()) {
+ return 0;
+ }
+ if (suffix.size() != 2 || suffix[0] != '_' || suffix[1] < 'a') {
+ LERROR << __PRETTY_FUNCTION__ << "slot '" << suffix
+ << "' does not have a recognized format.";
+ return 0;
+ }
+ return suffix[1] - 'a';
+}
+
+} // namespace fs_mgr
+} // namespace android
diff --git a/fs_mgr/liblp/utility.h b/fs_mgr/liblp/utility.h
new file mode 100644
index 0000000..09ed314
--- /dev/null
+++ b/fs_mgr/liblp/utility.h
@@ -0,0 +1,56 @@
+/*
+ * 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.
+ */
+
+#ifndef LIBLP_UTILITY_H
+#define LIBLP_UTILITY_H
+
+#include <stddef.h>
+#include <stdint.h>
+#include <sys/types.h>
+
+#include <android-base/logging.h>
+
+#include "liblp/metadata_format.h"
+
+#define LP_TAG "[liblp]"
+#define LERROR LOG(ERROR) << LP_TAG
+#define PERROR PLOG(ERROR) << LP_TAG
+
+namespace android {
+namespace fs_mgr {
+
+// Determine the size of a block device (or file). Logs and returns false on
+// error. After calling this, the position of |fd| may have changed.
+bool GetDescriptorSize(int fd, uint64_t* size);
+
+// Return the offset of a primary metadata slot, relative to the start of the
+// device.
+int64_t GetPrimaryMetadataOffset(const LpMetadataGeometry& geometry, uint32_t slot_number);
+
+// Return the offset of a backup metadata slot, relative to the end of the
+// device.
+int64_t GetBackupMetadataOffset(const LpMetadataGeometry& geometry, uint32_t slot_number);
+
+// Cross-platform helper for lseek64().
+int64_t SeekFile64(int fd, int64_t offset, int whence);
+
+// Compute a SHA256 hash.
+void SHA256(const void* data, size_t length, uint8_t out[32]);
+
+} // namespace fs_mgr
+} // namespace android
+
+#endif // LIBLP_UTILITY_H
diff --git a/fs_mgr/liblp/utility_test.cpp b/fs_mgr/liblp/utility_test.cpp
new file mode 100644
index 0000000..25e8a25
--- /dev/null
+++ b/fs_mgr/liblp/utility_test.cpp
@@ -0,0 +1,43 @@
+/*
+ * 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 "utility.h"
+#include <gtest/gtest.h>
+
+using namespace android;
+using namespace android::fs_mgr;
+
+TEST(liblp, SlotNumberForSlotSuffix) {
+ EXPECT_EQ(SlotNumberForSlotSuffix(""), 0);
+ EXPECT_EQ(SlotNumberForSlotSuffix("_a"), 0);
+ EXPECT_EQ(SlotNumberForSlotSuffix("_b"), 1);
+ EXPECT_EQ(SlotNumberForSlotSuffix("_c"), 2);
+ EXPECT_EQ(SlotNumberForSlotSuffix("_d"), 3);
+}
+
+TEST(liblp, GetMetadataOffset) {
+ LpMetadataGeometry geometry = {
+ LP_METADATA_GEOMETRY_MAGIC, sizeof(geometry), {0}, 16384, 4, 10000, 80000};
+ EXPECT_EQ(GetPrimaryMetadataOffset(geometry, 0), 4096);
+ EXPECT_EQ(GetPrimaryMetadataOffset(geometry, 1), 4096 + 16384);
+ EXPECT_EQ(GetPrimaryMetadataOffset(geometry, 2), 4096 + 16384 * 2);
+ EXPECT_EQ(GetPrimaryMetadataOffset(geometry, 3), 4096 + 16384 * 3);
+
+ EXPECT_EQ(GetBackupMetadataOffset(geometry, 3), -4096 - 16384 * 1);
+ EXPECT_EQ(GetBackupMetadataOffset(geometry, 2), -4096 - 16384 * 2);
+ EXPECT_EQ(GetBackupMetadataOffset(geometry, 1), -4096 - 16384 * 3);
+ EXPECT_EQ(GetBackupMetadataOffset(geometry, 0), -4096 - 16384 * 4);
+}
diff --git a/fs_mgr/liblp/writer.cpp b/fs_mgr/liblp/writer.cpp
new file mode 100644
index 0000000..89cbabd
--- /dev/null
+++ b/fs_mgr/liblp/writer.cpp
@@ -0,0 +1,248 @@
+/*
+ * Copyright (C) 2007 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 <inttypes.h>
+#include <unistd.h>
+
+#include <string>
+
+#include <android-base/file.h>
+#include <android-base/unique_fd.h>
+
+#include "liblp/reader.h"
+#include "liblp/writer.h"
+#include "utility.h"
+
+namespace android {
+namespace fs_mgr {
+
+static std::string SerializeGeometry(const LpMetadataGeometry& input) {
+ LpMetadataGeometry geometry = input;
+ memset(geometry.checksum, 0, sizeof(geometry.checksum));
+ SHA256(&geometry, sizeof(geometry), geometry.checksum);
+ return std::string(reinterpret_cast<const char*>(&geometry), sizeof(geometry));
+}
+
+static bool CompareGeometry(const LpMetadataGeometry& g1, const LpMetadataGeometry& g2) {
+ return g1.metadata_max_size == g2.metadata_max_size &&
+ g1.metadata_slot_count == g2.metadata_slot_count &&
+ g1.first_logical_sector == g2.first_logical_sector &&
+ g1.last_logical_sector == g2.last_logical_sector;
+}
+
+static std::string SerializeMetadata(const LpMetadata& input) {
+ LpMetadata metadata = input;
+ LpMetadataHeader& header = metadata.header;
+
+ // Serialize individual tables.
+ std::string partitions(reinterpret_cast<const char*>(metadata.partitions.data()),
+ metadata.partitions.size() * sizeof(LpMetadataPartition));
+ std::string extents(reinterpret_cast<const char*>(metadata.extents.data()),
+ metadata.extents.size() * sizeof(LpMetadataExtent));
+
+ // Compute positions of tables.
+ header.partitions.offset = 0;
+ header.extents.offset = header.partitions.offset + partitions.size();
+ header.tables_size = header.extents.offset + extents.size();
+
+ // Compute payload checksum.
+ std::string tables = partitions + extents;
+ SHA256(tables.data(), tables.size(), header.tables_checksum);
+
+ // Compute header checksum.
+ memset(header.header_checksum, 0, sizeof(header.header_checksum));
+ SHA256(&header, sizeof(header), header.header_checksum);
+
+ std::string header_blob =
+ std::string(reinterpret_cast<const char*>(&metadata.header), sizeof(metadata.header));
+ return header_blob + tables;
+}
+
+// Perform sanity checks so we don't accidentally overwrite valid metadata
+// with potentially invalid metadata, or random partition data with metadata.
+static bool ValidateGeometryAndMetadata(const LpMetadata& metadata, uint64_t blockdevice_size,
+ uint64_t metadata_size) {
+ const LpMetadataHeader& header = metadata.header;
+ const LpMetadataGeometry& geometry = metadata.geometry;
+ // Validate the usable sector range.
+ if (geometry.first_logical_sector > geometry.last_logical_sector) {
+ LERROR << "Logical partition metadata has invalid sector range.";
+ return false;
+ }
+ // Make sure we're writing within the space reserved.
+ if (metadata_size > geometry.metadata_max_size) {
+ LERROR << "Logical partition metadata is too large.";
+ return false;
+ }
+
+ // Make sure the device has enough space to store two backup copies of the
+ // metadata.
+ uint64_t reserved_size = LP_METADATA_GEOMETRY_SIZE +
+ uint64_t(geometry.metadata_max_size) * geometry.metadata_slot_count;
+ if (reserved_size > blockdevice_size ||
+ reserved_size > geometry.first_logical_sector * LP_SECTOR_SIZE) {
+ LERROR << "Not enough space to store all logical partition metadata slots.";
+ return false;
+ }
+ if (blockdevice_size - reserved_size < (geometry.last_logical_sector + 1) * LP_SECTOR_SIZE) {
+ LERROR << "Not enough space to backup all logical partition metadata slots.";
+ return false;
+ }
+
+ // Make sure all partition entries reference valid extents.
+ for (const auto& partition : metadata.partitions) {
+ if (partition.first_extent_index + partition.num_extents > metadata.extents.size()) {
+ LERROR << "Partition references invalid extent.";
+ return false;
+ }
+ }
+
+ // Make sure all linear extents have a valid range.
+ for (const auto& extent : metadata.extents) {
+ if (extent.target_type == LP_TARGET_TYPE_LINEAR) {
+ uint64_t physical_sector = extent.target_data;
+ if (physical_sector < geometry.first_logical_sector ||
+ physical_sector + extent.num_sectors > geometry.last_logical_sector) {
+ LERROR << "Extent table entry is out of bounds.";
+ return false;
+ }
+ }
+ }
+ return true;
+}
+
+bool WritePartitionTable(int fd, const LpMetadata& metadata, SyncMode sync_mode,
+ uint32_t slot_number) {
+ uint64_t size;
+ if (!GetDescriptorSize(fd, &size)) {
+ return false;
+ }
+
+ const LpMetadataGeometry& geometry = metadata.geometry;
+ if (sync_mode != SyncMode::Flash) {
+ // Verify that the old geometry is identical. If it's not, then we've
+ // based this new metadata on invalid assumptions.
+ LpMetadataGeometry old_geometry;
+ if (!ReadLogicalPartitionGeometry(fd, &old_geometry)) {
+ return false;
+ }
+ if (!CompareGeometry(geometry, old_geometry)) {
+ LERROR << "Incompatible geometry in new logical partition metadata";
+ return false;
+ }
+ }
+
+ // Make sure we're writing to a valid metadata slot.
+ if (slot_number >= geometry.metadata_slot_count) {
+ LERROR << "Invalid logical partition metadata slot number.";
+ return false;
+ }
+
+ // Before writing geometry and/or logical partition tables, perform some
+ // basic checks that the geometry and tables are coherent, and will fit
+ // on the given block device.
+ std::string blob = SerializeMetadata(metadata);
+ if (!ValidateGeometryAndMetadata(metadata, size, blob.size())) {
+ return false;
+ }
+
+ // First write geometry if this is a flash operation. It gets written to
+ // the first and last 4096-byte regions of the device.
+ if (sync_mode == SyncMode::Flash) {
+ std::string blob = SerializeGeometry(metadata.geometry);
+ if (SeekFile64(fd, 0, SEEK_SET) < 0) {
+ PERROR << __PRETTY_FUNCTION__ << "lseek failed: offset 0";
+ return false;
+ }
+ if (!android::base::WriteFully(fd, blob.data(), blob.size())) {
+ PERROR << __PRETTY_FUNCTION__ << "write " << blob.size() << " bytes failed";
+ return false;
+ }
+ if (SeekFile64(fd, -LP_METADATA_GEOMETRY_SIZE, SEEK_END) < 0) {
+ PERROR << __PRETTY_FUNCTION__ << "lseek failed: offset " << -LP_METADATA_GEOMETRY_SIZE;
+ return false;
+ }
+ if (!android::base::WriteFully(fd, blob.data(), blob.size())) {
+ PERROR << __PRETTY_FUNCTION__ << "backup write " << blob.size() << " bytes failed";
+ return false;
+ }
+ }
+
+ // Write the primary copy of the metadata.
+ int64_t primary_offset = GetPrimaryMetadataOffset(geometry, slot_number);
+ if (SeekFile64(fd, primary_offset, SEEK_SET) < 0) {
+ PERROR << __PRETTY_FUNCTION__ << "lseek failed: offset " << primary_offset;
+ return false;
+ }
+ if (!android::base::WriteFully(fd, blob.data(), blob.size())) {
+ PERROR << __PRETTY_FUNCTION__ << "write " << blob.size() << " bytes failed";
+ return false;
+ }
+
+ // Write the backup copy of the metadata.
+ int64_t backup_offset = GetBackupMetadataOffset(geometry, slot_number);
+ int64_t abs_offset = SeekFile64(fd, backup_offset, SEEK_END);
+ if (abs_offset == (int64_t)-1) {
+ PERROR << __PRETTY_FUNCTION__ << "lseek failed: offset " << backup_offset;
+ return false;
+ }
+ if (abs_offset < int64_t((geometry.last_logical_sector + 1) * LP_SECTOR_SIZE)) {
+ PERROR << __PRETTY_FUNCTION__ << "backup offset " << abs_offset
+ << " is within logical partition bounds, sector " << geometry.last_logical_sector;
+ return false;
+ }
+ if (!android::base::WriteFully(fd, blob.data(), blob.size())) {
+ PERROR << __PRETTY_FUNCTION__ << "backup write " << blob.size() << " bytes failed";
+ return false;
+ }
+ return true;
+}
+
+bool WritePartitionTable(const char* block_device, const LpMetadata& metadata, SyncMode sync_mode,
+ uint32_t slot_number) {
+ android::base::unique_fd fd(open(block_device, O_RDWR | O_SYNC));
+ if (fd < 0) {
+ PERROR << __PRETTY_FUNCTION__ << "open failed: " << block_device;
+ return false;
+ }
+ return WritePartitionTable(fd, metadata, sync_mode, slot_number);
+}
+
+bool WriteToImageFile(int fd, const LpMetadata& input) {
+ std::string geometry = SerializeGeometry(input.geometry);
+ std::string padding(LP_METADATA_GEOMETRY_SIZE - geometry.size(), '\0');
+ std::string metadata = SerializeMetadata(input);
+
+ std::string everything = geometry + padding + metadata;
+
+ if (!android::base::WriteFully(fd, everything.data(), everything.size())) {
+ PERROR << __PRETTY_FUNCTION__ << "write " << everything.size() << " bytes failed";
+ return false;
+ }
+ return true;
+}
+
+bool WriteToImageFile(const char* file, const LpMetadata& input) {
+ android::base::unique_fd fd(open(file, O_CREAT | O_RDWR | O_TRUNC, 0644));
+ if (fd < 0) {
+ PERROR << __PRETTY_FUNCTION__ << "open failed: " << file;
+ return false;
+ }
+ return WriteToImageFile(fd, input);
+}
+
+} // namespace fs_mgr
+} // namespace android
diff --git a/fs_mgr/tools/dmctl.cpp b/fs_mgr/tools/dmctl.cpp
index b40b83a..9d48b8c 100644
--- a/fs_mgr/tools/dmctl.cpp
+++ b/fs_mgr/tools/dmctl.cpp
@@ -22,63 +22,154 @@
#include <sys/types.h>
#include <unistd.h>
+#include <android-base/parseint.h>
#include <android-base/unique_fd.h>
-#include <dm.h>
+#include <libdm/dm.h>
#include <functional>
#include <iomanip>
#include <ios>
#include <iostream>
#include <map>
+#include <sstream>
#include <string>
#include <vector>
using DeviceMapper = ::android::dm::DeviceMapper;
+using DmTable = ::android::dm::DmTable;
using DmTarget = ::android::dm::DmTarget;
+using DmTargetLinear = ::android::dm::DmTargetLinear;
+using DmTargetZero = ::android::dm::DmTargetZero;
+using DmTargetTypeInfo = ::android::dm::DmTargetTypeInfo;
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 << " create <dm-name> [-ro] <targets...>" << std::endl;
std::cerr << " delete <dm-name>" << std::endl;
std::cerr << " list <devices | targets>" << std::endl;
+ std::cerr << " getpath <dm-name>" << std::endl;
std::cerr << " help" << std::endl;
+ std::cerr << std::endl;
+ std::cerr << "Target syntax:" << std::endl;
+ std::cerr << " <target_type> <start_sector> <num_sectors> [target_data]" << std::endl;
return -EINVAL;
}
+class TargetParser final {
+ public:
+ TargetParser(int argc, char** argv) : arg_index_(0), argc_(argc), argv_(argv) {}
+
+ bool More() const { return arg_index_ < argc_; }
+ std::unique_ptr<DmTarget> Next() {
+ if (!HasArgs(3)) {
+ std::cerr << "Expected <target_type> <start_sector> <num_sectors>" << std::endl;
+ return nullptr;
+ }
+
+ std::string target_type = NextArg();
+ uint64_t start_sector, num_sectors;
+ if (!android::base::ParseUint(NextArg(), &start_sector)) {
+ std::cerr << "Expected start sector, got: " << PreviousArg() << std::endl;
+ return nullptr;
+ }
+ if (!android::base::ParseUint(NextArg(), &num_sectors) || !num_sectors) {
+ std::cerr << "Expected non-zero sector count, got: " << PreviousArg() << std::endl;
+ return nullptr;
+ }
+
+ if (target_type == "zero") {
+ return std::make_unique<DmTargetZero>(start_sector, num_sectors);
+ } else if (target_type == "linear") {
+ if (!HasArgs(2)) {
+ std::cerr << "Expected \"linear\" <block_device> <sector>" << std::endl;
+ return nullptr;
+ }
+
+ std::string block_device = NextArg();
+ uint64_t physical_sector;
+ if (!android::base::ParseUint(NextArg(), &physical_sector)) {
+ std::cerr << "Expected sector, got: \"" << PreviousArg() << "\"" << std::endl;
+ return nullptr;
+ }
+ return std::make_unique<DmTargetLinear>(start_sector, num_sectors, block_device,
+ physical_sector);
+ } else {
+ std::cerr << "Unrecognized target type: " << target_type << std::endl;
+ return nullptr;
+ }
+ }
+
+ private:
+ bool HasArgs(int count) { return arg_index_ + count <= argc_; }
+ const char* NextArg() {
+ CHECK(arg_index_ < argc_);
+ return argv_[arg_index_++];
+ }
+ const char* PreviousArg() {
+ CHECK(arg_index_ >= 0);
+ return argv_[arg_index_ - 1];
+ }
+
+ private:
+ int arg_index_;
+ int argc_;
+ char** argv_;
+};
+
static int DmCreateCmdHandler(int argc, char** argv) {
if (argc < 1) {
- std::cerr << "DmCreateCmdHandler: atleast 'name' MUST be provided for target device";
+ std::cerr << "Usage: dmctl create <dm-name> [-ro] <targets...>" << std::endl;
+ return -EINVAL;
+ }
+ std::string name = argv[0];
+
+ // Parse extended options first.
+ DmTable table;
+ int arg_index = 1;
+ while (arg_index < argc && argv[arg_index][0] == '-') {
+ if (strcmp(argv[arg_index], "-ro") == 0) {
+ table.set_readonly(true);
+ } else {
+ std::cerr << "Unrecognized option: " << argv[arg_index] << std::endl;
+ return -EINVAL;
+ }
+ arg_index++;
+ }
+
+ // Parse everything else as target information.
+ TargetParser parser(argc - arg_index, argv + arg_index);
+ while (parser.More()) {
+ std::unique_ptr<DmTarget> target = parser.Next();
+ if (!target || !table.AddTarget(std::move(target))) {
+ return -EINVAL;
+ }
+ }
+
+ if (table.num_targets() == 0) {
+ std::cerr << "Must define at least one target." << std::endl;
return -EINVAL;
}
- std::string name = argv[0];
DeviceMapper& dm = DeviceMapper::Instance();
- if (!dm.CreateDevice(name)) {
- std::cerr << "DmCreateCmdHandler: Failed to create " << name << " device";
+ if (!dm.CreateDevice(name, table)) {
+ std::cerr << "Failed to create device-mapper device with name: " << name << std::endl;
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";
+ std::cerr << "Usage: dmctl delete <name>" << std::endl;
return -EINVAL;
}
std::string name = argv[0];
DeviceMapper& dm = DeviceMapper::Instance();
if (!dm.DeleteDevice(name)) {
- std::cerr << "DmCreateCmdHandler: Failed to create " << name << " device";
+ std::cerr << "Failed to delete [" << name << "]" << std::endl;
return -EIO;
}
@@ -86,7 +177,7 @@
}
static int DmListTargets(DeviceMapper& dm) {
- std::vector<DmTarget> targets;
+ std::vector<DmTargetTypeInfo> targets;
if (!dm.GetAvailableTargets(&targets)) {
std::cerr << "Failed to read available device mapper targets" << std::endl;
return -errno;
@@ -151,11 +242,30 @@
return 0;
}
+static int GetPathCmdHandler(int argc, char** argv) {
+ if (argc != 1) {
+ std::cerr << "Invalid arguments, see \'dmctl help\'" << std::endl;
+ return -EINVAL;
+ }
+
+ DeviceMapper& dm = DeviceMapper::Instance();
+ std::string path;
+ if (!dm.GetDmDevicePathByName(argv[0], &path)) {
+ std::cerr << "Could not query path of device \"" << argv[0] << "\"." << std::endl;
+ return -EINVAL;
+ }
+ std::cout << path << std::endl;
+ return 0;
+}
+
static std::map<std::string, std::function<int(int, char**)>> cmdmap = {
+ // clang-format off
{"create", DmCreateCmdHandler},
{"delete", DmDeleteCmdHandler},
{"list", DmListCmdHandler},
{"help", HelpCmdHandler},
+ {"getpath", GetPathCmdHandler},
+ // clang-format on
};
int main(int argc, char** argv) {
diff --git a/init/Android.bp b/init/Android.bp
index 7d863c8..cf7637f 100644
--- a/init/Android.bp
+++ b/init/Android.bp
@@ -76,7 +76,6 @@
"libprotobuf-cpp-lite",
"libpropertyinfoserializer",
"libpropertyinfoparser",
- "libselinux",
],
shared_libs: [
"libcutils",
@@ -87,6 +86,7 @@
"libc++",
"libdl",
"libz",
+ "libselinux",
],
}
diff --git a/init/Android.mk b/init/Android.mk
index da27a73..a81a0f6 100644
--- a/init/Android.mk
+++ b/init/Android.mk
@@ -61,7 +61,6 @@
libseccomp_policy \
libcrypto_utils \
libsparse \
- libselinux \
libprocessgroup \
libavb \
libkeyutils \
@@ -76,6 +75,7 @@
libcrypto \
libdl \
libz \
+ libselinux \
ifneq ($(BOARD_BUILD_SYSTEM_ROOT_IMAGE),true)
# init is static executable for non-system-as-root devices, because the dynamic linker
diff --git a/init/README.md b/init/README.md
index 550ef05..b0a73b9 100644
--- a/init/README.md
+++ b/init/README.md
@@ -752,3 +752,22 @@
kill -SIGCONT 4343
> strace runs
+
+Host Init Script Verification
+-----------------------------
+
+Init scripts are checked for correctness during build time. Specifically the below is checked.
+
+1) Well formatted action, service and import sections, e.g. no actions without a preceding 'on'
+line, and no extraneous lines after an 'import' statement.
+2) All commands map to a valid keyword and the argument count is within the correct range.
+3) All service options are valid. This is stricter than how commands are checked as the service
+options' arguments are fully parsed, e.g. UIDs and GIDs must resolve.
+
+There are other parts of init scripts that are only parsed at runtime and therefore not checked
+during build time, among them are the below.
+
+1) The validity of the arguments of commands, e.g. no checking if file paths actually exist, if
+SELinux would permit the operation, or if the UIDs and GIDs resolve.
+2) No checking if a service exists or has a valid SELinux domain defined
+3) No checking if a service has not been previously defined in a different init script.
diff --git a/init/host_import_parser.cpp b/init/host_import_parser.cpp
index faf6fc1..93e363f 100644
--- a/init/host_import_parser.cpp
+++ b/init/host_import_parser.cpp
@@ -23,22 +23,17 @@
namespace android {
namespace init {
-Result<Success> HostImportParser::ParseSection(std::vector<std::string>&& args,
- const std::string& filename, int line) {
+Result<Success> HostImportParser::ParseSection(std::vector<std::string>&& args, const std::string&,
+ int) {
if (args.size() != 2) {
return Error() << "single argument needed for import\n";
}
- auto import_path = args[1];
+ return Success();
+}
- if (StartsWith(import_path, "/system") || StartsWith(import_path, "/product") ||
- StartsWith(import_path, "/odm") || StartsWith(import_path, "/vendor")) {
- import_path = out_dir_ + "/" + import_path;
- } else {
- import_path = out_dir_ + "/root/" + import_path;
- }
-
- return ImportParser::ParseSection({"import", import_path}, filename, line);
+Result<Success> HostImportParser::ParseLineSection(std::vector<std::string>&&, int) {
+ return Error() << "Unexpected line found after import statement";
}
} // namespace init
diff --git a/init/host_import_parser.h b/init/host_import_parser.h
index e2980b2..52b8891 100644
--- a/init/host_import_parser.h
+++ b/init/host_import_parser.h
@@ -19,21 +19,16 @@
#include <string>
#include <vector>
-#include "import_parser.h"
#include "parser.h"
namespace android {
namespace init {
-class HostImportParser : public ImportParser {
+class HostImportParser : public SectionParser {
public:
- HostImportParser(const std::string& out_dir, Parser* parser)
- : ImportParser(parser), out_dir_(out_dir) {}
- Result<Success> ParseSection(std::vector<std::string>&& args, const std::string& filename,
- int line) override;
-
- private:
- std::string out_dir_;
+ HostImportParser() {}
+ Result<Success> ParseSection(std::vector<std::string>&& args, const std::string&, int) override;
+ Result<Success> ParseLineSection(std::vector<std::string>&&, int) override;
};
} // namespace init
diff --git a/init/host_init_verifier.cpp b/init/host_init_verifier.cpp
index d6884af..8407729 100644
--- a/init/host_init_verifier.cpp
+++ b/init/host_init_verifier.cpp
@@ -17,6 +17,7 @@
#include <errno.h>
#include <pwd.h>
#include <stdio.h>
+#include <stdlib.h>
#include <iostream>
#include <string>
@@ -45,11 +46,11 @@
using android::base::ReadFileToString;
using android::base::Split;
-static std::string out_dir;
+static std::string passwd_file;
static std::vector<std::pair<std::string, int>> GetVendorPasswd() {
std::string passwd;
- if (!ReadFileToString(out_dir + "/vendor/etc/passwd", &passwd)) {
+ if (!ReadFileToString(passwd_file, &passwd)) {
return {};
}
@@ -102,6 +103,14 @@
}
}
+ unsigned int oem_uid;
+ if (sscanf(login, "oem_%u", &oem_uid) == 1) {
+ snprintf(static_name, sizeof(static_name), "%s", login);
+ static_passwd.pw_uid = oem_uid;
+ static_passwd.pw_gid = oem_uid;
+ return &static_passwd;
+ }
+
errno = ENOENT;
return nullptr;
}
@@ -118,20 +127,14 @@
int main(int argc, char** argv) {
android::base::InitLogging(argv, &android::base::StdioLogger);
android::base::SetMinimumLogSeverity(android::base::ERROR);
- if (argc != 3) {
- LOG(ERROR) << "Usage: " << argv[0] << " <out directory> <properties>";
- return -1;
+
+ if (argc != 2 && argc != 3) {
+ LOG(ERROR) << "Usage: " << argv[0] << " <init rc file> [passwd file]";
+ return EXIT_FAILURE;
}
- out_dir = argv[1];
-
- auto properties = Split(argv[2], ",");
- for (const auto& property : properties) {
- auto split_property = Split(property, "=");
- if (split_property.size() != 2) {
- continue;
- }
- property_set(split_property[0], split_property[1]);
+ if (argc == 3) {
+ passwd_file = argv[2];
}
const BuiltinFunctionMap function_map;
@@ -141,22 +144,23 @@
Parser parser;
parser.AddSectionParser("service", std::make_unique<ServiceParser>(&sl, nullptr));
parser.AddSectionParser("on", std::make_unique<ActionParser>(&am, nullptr));
- parser.AddSectionParser("import", std::make_unique<HostImportParser>(out_dir, &parser));
+ parser.AddSectionParser("import", std::make_unique<HostImportParser>());
- if (!parser.ParseConfig(argv[1] + "/root/init.rc"s)) {
- LOG(ERROR) << "Failed to find root init.rc script";
- return -1;
+ if (!parser.ParseConfigFileInsecure(argv[1])) {
+ LOG(ERROR) << "Failed to open init rc script '" << argv[1] << "'";
+ return EXIT_FAILURE;
}
if (parser.parse_error_count() > 0) {
- LOG(ERROR) << "Init script parsing failed with " << parser.parse_error_count() << " errors";
- return -1;
+ LOG(ERROR) << "Failed to parse init script '" << argv[1] << "' with "
+ << parser.parse_error_count() << " errors";
+ return EXIT_FAILURE;
}
- return 0;
+ return EXIT_SUCCESS;
}
} // namespace init
} // namespace android
int main(int argc, char** argv) {
- android::init::main(argc, argv);
+ return android::init::main(argc, argv);
}
diff --git a/init/init.cpp b/init/init.cpp
index b494bcc..77c4fc4 100644
--- a/init/init.cpp
+++ b/init/init.cpp
@@ -352,21 +352,23 @@
}
static void export_kernel_boot_props() {
+ constexpr const char* UNSET = "";
struct {
const char *src_prop;
const char *dst_prop;
const char *default_value;
} prop_map[] = {
- { "ro.boot.serialno", "ro.serialno", "", },
+ { "ro.boot.serialno", "ro.serialno", UNSET, },
{ "ro.boot.mode", "ro.bootmode", "unknown", },
{ "ro.boot.baseband", "ro.baseband", "unknown", },
{ "ro.boot.bootloader", "ro.bootloader", "unknown", },
{ "ro.boot.hardware", "ro.hardware", "unknown", },
{ "ro.boot.revision", "ro.revision", "0", },
};
- for (size_t i = 0; i < arraysize(prop_map); i++) {
- std::string value = GetProperty(prop_map[i].src_prop, "");
- property_set(prop_map[i].dst_prop, (!value.empty()) ? value : prop_map[i].default_value);
+ for (const auto& prop : prop_map) {
+ std::string value = GetProperty(prop.src_prop, prop.default_value);
+ if (value != UNSET)
+ property_set(prop.dst_prop, value);
}
}
diff --git a/init/init_first_stage.cpp b/init/init_first_stage.cpp
index db60ce1..2bc9f3a 100644
--- a/init/init_first_stage.cpp
+++ b/init/init_first_stage.cpp
@@ -29,6 +29,7 @@
#include <android-base/file.h>
#include <android-base/logging.h>
#include <android-base/strings.h>
+#include <liblp/metadata_format.h>
#include "devices.h"
#include "fs_mgr.h"
@@ -75,6 +76,7 @@
std::unique_ptr<fstab, decltype(&fs_mgr_free_fstab)> device_tree_fstab_;
std::unique_ptr<LogicalPartitionTable> dm_linear_table_;
+ std::string lp_metadata_partition_;
std::vector<fstab_rec*> mount_fstab_recs_;
std::set<std::string> required_devices_partition_names_;
std::unique_ptr<DeviceHandler> device_handler_;
@@ -120,13 +122,17 @@
}
static inline bool IsDmLinearEnabled() {
- bool enabled = false;
- import_kernel_cmdline(
- false, [&enabled](const std::string& key, const std::string& value, bool in_qemu) {
- if (key == "androidboot.logical_partitions" && value == "1") {
- enabled = true;
- }
- });
+ static bool checked = false;
+ static bool enabled = false;
+ if (checked) {
+ return enabled;
+ }
+ import_kernel_cmdline(false, [](const std::string& key, const std::string& value, bool in_qemu) {
+ if (key == "androidboot.logical_partitions" && value == "1") {
+ enabled = true;
+ }
+ });
+ checked = true;
return enabled;
}
@@ -163,7 +169,7 @@
}
bool FirstStageMount::DoFirstStageMount() {
- if (!dm_linear_table_ && mount_fstab_recs_.empty()) {
+ if (!IsDmLinearEnabled() && mount_fstab_recs_.empty()) {
// Nothing to mount.
LOG(INFO) << "First stage mount skipped (missing/incompatible/empty fstab in device tree)";
return true;
@@ -184,14 +190,18 @@
bool FirstStageMount::GetBackingDmLinearDevices() {
// Add any additional devices required for dm-linear mappings.
- if (!dm_linear_table_) {
+ if (!IsDmLinearEnabled()) {
return true;
}
- for (const auto& partition : dm_linear_table_->partitions) {
- for (const auto& extent : partition.extents) {
- const std::string& partition_name = android::base::Basename(extent.block_device());
- required_devices_partition_names_.emplace(partition_name);
+ required_devices_partition_names_.emplace(LP_METADATA_PARTITION_NAME);
+
+ if (dm_linear_table_) {
+ for (const auto& partition : dm_linear_table_->partitions) {
+ for (const auto& extent : partition.extents) {
+ const std::string& partition_name = android::base::Basename(extent.block_device());
+ required_devices_partition_names_.emplace(partition_name);
+ }
}
}
return true;
@@ -205,7 +215,7 @@
return true;
}
- if (dm_linear_table_ || need_dm_verity_) {
+ if (IsDmLinearEnabled() || need_dm_verity_) {
const std::string dm_path = "/devices/virtual/misc/device-mapper";
bool found = false;
auto dm_callback = [this, &dm_path, &found](const Uevent& uevent) {
@@ -253,10 +263,21 @@
}
bool FirstStageMount::CreateLogicalPartitions() {
- if (!dm_linear_table_) {
+ if (!IsDmLinearEnabled()) {
return true;
}
- return android::fs_mgr::CreateLogicalPartitions(*dm_linear_table_.get());
+
+ if (lp_metadata_partition_.empty()) {
+ LOG(ERROR) << "Could not locate logical partition tables in partition "
+ << LP_METADATA_PARTITION_NAME;
+ return false;
+ }
+ if (dm_linear_table_) {
+ if (!android::fs_mgr::CreateLogicalPartitions(*dm_linear_table_.get())) {
+ return false;
+ }
+ }
+ return android::fs_mgr::CreateLogicalPartitions(lp_metadata_partition_);
}
ListenerAction FirstStageMount::HandleBlockDevice(const std::string& name, const Uevent& uevent) {
@@ -266,6 +287,10 @@
auto iter = required_devices_partition_names_.find(name);
if (iter != required_devices_partition_names_.end()) {
LOG(VERBOSE) << __PRETTY_FUNCTION__ << ": found partition: " << *iter;
+ if (IsDmLinearEnabled() && name == LP_METADATA_PARTITION_NAME) {
+ std::vector<std::string> links = device_handler_->GetBlockDeviceSymlinks(uevent);
+ lp_metadata_partition_ = links[0];
+ }
required_devices_partition_names_.erase(iter);
device_handler_->HandleDeviceEvent(uevent);
if (required_devices_partition_names_.empty()) {
diff --git a/init/parser.cpp b/init/parser.cpp
index 4f1cac4..fa0fd11 100644
--- a/init/parser.cpp
+++ b/init/parser.cpp
@@ -19,6 +19,7 @@
#include <dirent.h>
#include <android-base/chrono_utils.h>
+#include <android-base/file.h>
#include <android-base/logging.h>
#include <android-base/stringprintf.h>
#include <android-base/strings.h>
@@ -39,14 +40,13 @@
line_callbacks_.emplace_back(prefix, callback);
}
-void Parser::ParseData(const std::string& filename, const std::string& data) {
- // TODO: Use a parser with const input and remove this copy
- std::vector<char> data_copy(data.begin(), data.end());
- data_copy.push_back('\0');
+void Parser::ParseData(const std::string& filename, std::string* data) {
+ data->push_back('\n'); // TODO: fix tokenizer
+ data->push_back('\0');
parse_state state;
state.line = 0;
- state.ptr = &data_copy[0];
+ state.ptr = data->data();
state.nexttoken = 0;
SectionParser* section_parser = nullptr;
@@ -69,6 +69,11 @@
switch (next_token(&state)) {
case T_EOF:
end_section();
+
+ for (const auto& [section_name, section_parser] : section_parsers_) {
+ section_parser->EndFile();
+ }
+
return;
case T_NEWLINE: {
state.line++;
@@ -118,6 +123,16 @@
}
}
+bool Parser::ParseConfigFileInsecure(const std::string& path) {
+ std::string config_contents;
+ if (!android::base::ReadFileToString(path, &config_contents)) {
+ return false;
+ }
+
+ ParseData(path, &config_contents);
+ return true;
+}
+
bool Parser::ParseConfigFile(const std::string& path) {
LOG(INFO) << "Parsing file " << path << "...";
android::base::Timer t;
@@ -127,11 +142,7 @@
return false;
}
- config_contents->push_back('\n'); // TODO: fix parse_config.
- ParseData(path, *config_contents);
- for (const auto& [section_name, section_parser] : section_parsers_) {
- section_parser->EndFile();
- }
+ ParseData(path, &config_contents.value());
LOG(VERBOSE) << "(Parsing " << path << " took " << t << ".)";
return true;
diff --git a/init/parser.h b/init/parser.h
index 3501d8c..2454b6a 100644
--- a/init/parser.h
+++ b/init/parser.h
@@ -75,10 +75,13 @@
void AddSectionParser(const std::string& name, std::unique_ptr<SectionParser> parser);
void AddSingleLineParser(const std::string& prefix, LineCallback callback);
+ // Host init verifier check file permissions.
+ bool ParseConfigFileInsecure(const std::string& path);
+
size_t parse_error_count() const { return parse_error_count_; }
private:
- void ParseData(const std::string& filename, const std::string& data);
+ void ParseData(const std::string& filename, std::string* data);
bool ParseConfigFile(const std::string& path);
bool ParseConfigDir(const std::string& path);
diff --git a/init/service.cpp b/init/service.cpp
index 565cae7..95b37ab 100644
--- a/init/service.cpp
+++ b/init/service.cpp
@@ -787,9 +787,9 @@
flags_ |= SVC_EXEC;
is_exec_service_running_ = true;
- LOG(INFO) << "SVC_EXEC pid " << pid_ << " (uid " << uid_ << " gid " << gid_ << "+"
- << supp_gids_.size() << " context " << (!seclabel_.empty() ? seclabel_ : "default")
- << ") started; waiting...";
+ LOG(INFO) << "SVC_EXEC service '" << name_ << "' pid " << pid_ << " (uid " << uid_ << " gid "
+ << gid_ << "+" << supp_gids_.size() << " context "
+ << (!seclabel_.empty() ? seclabel_ : "default") << ") started; waiting...";
return Success();
}
diff --git a/liblog/tests/libc_test.cpp b/liblog/tests/libc_test.cpp
index 329ba85..6d78ed6 100644
--- a/liblog/tests/libc_test.cpp
+++ b/liblog/tests/libc_test.cpp
@@ -21,6 +21,7 @@
TEST(libc, __pstore_append) {
#ifdef __ANDROID__
+#ifndef NO_PSTORE
FILE* fp;
ASSERT_TRUE(NULL != (fp = fopen("/dev/pmsg0", "a")));
static const char message[] = "libc.__pstore_append\n";
@@ -43,6 +44,9 @@
"Reboot, ensure string libc.__pstore_append is in "
"/sys/fs/pstore/pmsg-ramoops-0\n");
}
+#else /* NO_PSTORE */
+ GTEST_LOG_(INFO) << "This test does nothing because of NO_PSTORE.\n";
+#endif /* NO_PSTORE */
#else
GTEST_LOG_(INFO) << "This test does nothing.\n";
#endif
diff --git a/liblog/tests/liblog_test.cpp b/liblog/tests/liblog_test.cpp
index 597a6bb..a8a9a12 100644
--- a/liblog/tests/liblog_test.cpp
+++ b/liblog/tests/liblog_test.cpp
@@ -3180,113 +3180,6 @@
}
#endif // USING_LOGGER_DEFAULT
-#ifdef USING_LOGGER_DEFAULT // Do not retest event mapping functionality
-#ifdef __ANDROID__
-// must be: '<needle:> 0 kB'
-static bool isZero(const std::string& content, std::string::size_type pos,
- const char* needle) {
- std::string::size_type offset = content.find(needle, pos);
- return (offset != std::string::npos) &&
- ((offset = content.find_first_not_of(" \t", offset + strlen(needle))) !=
- std::string::npos) &&
- (content.find_first_not_of('0', offset) != offset);
-}
-
-// must not be: '<needle:> 0 kB'
-static bool isNotZero(const std::string& content, std::string::size_type pos,
- const char* needle) {
- std::string::size_type offset = content.find(needle, pos);
- return (offset != std::string::npos) &&
- ((offset = content.find_first_not_of(" \t", offset + strlen(needle))) !=
- std::string::npos) &&
- (content.find_first_not_of("123456789", offset) != offset);
-}
-
-static void event_log_tags_test_smap(pid_t pid) {
- std::string filename = android::base::StringPrintf("/proc/%d/smaps", pid);
-
- std::string content;
- if (!android::base::ReadFileToString(filename, &content)) return;
-
- bool shared_ok = false;
- bool private_ok = false;
- bool anonymous_ok = false;
- bool pass_ok = false;
-
- static const char event_log_tags[] = "event-log-tags";
- std::string::size_type pos = 0;
- while ((pos = content.find(event_log_tags, pos)) != std::string::npos) {
- pos += strlen(event_log_tags);
-
- // must not be: 'Shared_Clean: 0 kB'
- bool ok =
- isNotZero(content, pos, "Shared_Clean:") ||
- // If not /etc/event-log-tags, thus r/w, then half points
- // back for not 'Shared_Dirty: 0 kB'
- ((content.substr(pos - 5 - strlen(event_log_tags), 5) != "/etc/") &&
- isNotZero(content, pos, "Shared_Dirty:"));
- if (ok && !pass_ok) {
- shared_ok = true;
- } else if (!ok) {
- shared_ok = false;
- }
-
- // must be: 'Private_Dirty: 0 kB' and 'Private_Clean: 0 kB'
- ok = isZero(content, pos, "Private_Dirty:") ||
- isZero(content, pos, "Private_Clean:");
- if (ok && !pass_ok) {
- private_ok = true;
- } else if (!ok) {
- private_ok = false;
- }
-
- // must be: 'Anonymous: 0 kB'
- ok = isZero(content, pos, "Anonymous:");
- if (ok && !pass_ok) {
- anonymous_ok = true;
- } else if (!ok) {
- anonymous_ok = false;
- }
-
- pass_ok = true;
- }
- content = "";
-
- if (!pass_ok) return;
- if (shared_ok && anonymous_ok && private_ok) return;
-
- filename = android::base::StringPrintf("/proc/%d/comm", pid);
- android::base::ReadFileToString(filename, &content);
- content = android::base::StringPrintf(
- "%d:%s", pid, content.substr(0, content.find('\n')).c_str());
-
- EXPECT_TRUE(IsOk(shared_ok, content));
- EXPECT_TRUE(IsOk(private_ok, content));
- EXPECT_TRUE(IsOk(anonymous_ok, content));
-}
-#endif // __ANDROID__
-
-TEST(liblog, event_log_tags) {
-#ifdef __ANDROID__
- std::unique_ptr<DIR, int (*)(DIR*)> proc_dir(opendir("/proc"), closedir);
- ASSERT_FALSE(!proc_dir);
-
- dirent* e;
- while ((e = readdir(proc_dir.get()))) {
- if (e->d_type != DT_DIR) continue;
- if (!isdigit(e->d_name[0])) continue;
- long long id = atoll(e->d_name);
- if (id <= 0) continue;
- pid_t pid = id;
- if (id != pid) continue;
- event_log_tags_test_smap(pid);
- }
-#else
- GTEST_LOG_(INFO) << "This test does nothing.\n";
-#endif
-}
-#endif // USING_LOGGER_DEFAULT
-
#ifdef USING_LOGGER_DEFAULT // Do not retest ratelimit
TEST(liblog, __android_log_ratelimit) {
time_t state = 0;
diff --git a/libsync/include/ndk/sync.h b/libsync/include/ndk/sync.h
index a786d3e..49f01e1 100644
--- a/libsync/include/ndk/sync.h
+++ b/libsync/include/ndk/sync.h
@@ -32,8 +32,6 @@
__BEGIN_DECLS
-#if __ANDROID_API__ >= __ANDROID_API_O__
-
/* Fences indicate the status of an asynchronous task. They are initially
* in unsignaled state (0), and make a one-time transition to either signaled
* (1) or error (< 0) state. A sync file is a collection of one or more fences;
@@ -63,14 +61,14 @@
* The original fences remain valid, and the caller is responsible for closing
* them.
*/
-int32_t sync_merge(const char *name, int32_t fd1, int32_t fd2);
+int32_t sync_merge(const char* name, int32_t fd1, int32_t fd2) __INTRODUCED_IN(26);
/**
* Retrieve detailed information about a sync file and its fences.
*
* The returned sync_file_info must be freed by calling sync_file_info_free().
*/
-struct sync_file_info *sync_file_info(int32_t fd);
+struct sync_file_info* sync_file_info(int32_t fd) __INTRODUCED_IN(26);
/**
* Get the array of fence infos from the sync file's info.
@@ -88,9 +86,7 @@
}
/** Free a struct sync_file_info structure */
-void sync_file_info_free(struct sync_file_info *info);
-
-#endif // __ANDROID_API__ >= __ANDROID_API_O__
+void sync_file_info_free(struct sync_file_info* info) __INTRODUCED_IN(26);
__END_DECLS
diff --git a/libunwindstack/ElfInterface.cpp b/libunwindstack/ElfInterface.cpp
index 954a821..915cddb 100644
--- a/libunwindstack/ElfInterface.cpp
+++ b/libunwindstack/ElfInterface.cpp
@@ -211,7 +211,7 @@
return false;
}
- if (HandleType(offset, phdr.p_type, *load_bias)) {
+ if (HandleType(offset, phdr.p_type)) {
continue;
}
diff --git a/libunwindstack/ElfInterfaceArm.cpp b/libunwindstack/ElfInterfaceArm.cpp
index 9b61599..a3244e8 100644
--- a/libunwindstack/ElfInterfaceArm.cpp
+++ b/libunwindstack/ElfInterfaceArm.cpp
@@ -87,20 +87,22 @@
#define PT_ARM_EXIDX 0x70000001
#endif
-bool ElfInterfaceArm::HandleType(uint64_t offset, uint32_t type, uint64_t load_bias) {
+bool ElfInterfaceArm::HandleType(uint64_t offset, uint32_t type) {
if (type != PT_ARM_EXIDX) {
return false;
}
Elf32_Phdr phdr;
- if (!memory_->ReadField(offset, &phdr, &phdr.p_vaddr, sizeof(phdr.p_vaddr))) {
+ if (!memory_->ReadFully(offset, &phdr, sizeof(phdr))) {
return true;
}
- if (!memory_->ReadField(offset, &phdr, &phdr.p_memsz, sizeof(phdr.p_memsz))) {
- return true;
- }
- start_offset_ = phdr.p_vaddr - load_bias;
- total_entries_ = phdr.p_memsz / 8;
+
+ // The offset already takes into account the load bias.
+ start_offset_ = phdr.p_offset;
+
+ // Always use filesz instead of memsz. In most cases they are the same,
+ // but some shared libraries wind up setting one correctly and not the other.
+ total_entries_ = phdr.p_filesz / 8;
return true;
}
diff --git a/libunwindstack/ElfInterfaceArm.h b/libunwindstack/ElfInterfaceArm.h
index 18efb6c..3bee9cf 100644
--- a/libunwindstack/ElfInterfaceArm.h
+++ b/libunwindstack/ElfInterfaceArm.h
@@ -70,7 +70,7 @@
bool FindEntry(uint32_t pc, uint64_t* entry_offset);
- bool HandleType(uint64_t offset, uint32_t type, uint64_t load_bias) override;
+ bool HandleType(uint64_t offset, uint32_t type) override;
bool Step(uint64_t pc, Regs* regs, Memory* process_memory, bool* finished) override;
diff --git a/libunwindstack/include/unwindstack/ElfInterface.h b/libunwindstack/include/unwindstack/ElfInterface.h
index 4d25c40..0c588da 100644
--- a/libunwindstack/include/unwindstack/ElfInterface.h
+++ b/libunwindstack/include/unwindstack/ElfInterface.h
@@ -118,7 +118,7 @@
template <typename SymType>
bool GetGlobalVariableWithTemplate(const std::string& name, uint64_t* memory_address);
- virtual bool HandleType(uint64_t, uint32_t, uint64_t) { return false; }
+ virtual bool HandleType(uint64_t, uint32_t) { return false; }
template <typename EhdrType>
static void GetMaxSizeWithTemplate(Memory* memory, uint64_t* size);
diff --git a/libunwindstack/tests/ElfInterfaceArmTest.cpp b/libunwindstack/tests/ElfInterfaceArmTest.cpp
index 5f1c2ac..a8bb4aa 100644
--- a/libunwindstack/tests/ElfInterfaceArmTest.cpp
+++ b/libunwindstack/tests/ElfInterfaceArmTest.cpp
@@ -245,56 +245,41 @@
TEST_F(ElfInterfaceArmTest, HandleType_not_arm_exidx) {
ElfInterfaceArmFake interface(&memory_);
- ASSERT_FALSE(interface.HandleType(0x1000, PT_NULL, 0));
- ASSERT_FALSE(interface.HandleType(0x1000, PT_LOAD, 0));
- ASSERT_FALSE(interface.HandleType(0x1000, PT_DYNAMIC, 0));
- ASSERT_FALSE(interface.HandleType(0x1000, PT_INTERP, 0));
- ASSERT_FALSE(interface.HandleType(0x1000, PT_NOTE, 0));
- ASSERT_FALSE(interface.HandleType(0x1000, PT_SHLIB, 0));
- ASSERT_FALSE(interface.HandleType(0x1000, PT_PHDR, 0));
- ASSERT_FALSE(interface.HandleType(0x1000, PT_TLS, 0));
- ASSERT_FALSE(interface.HandleType(0x1000, PT_LOOS, 0));
- ASSERT_FALSE(interface.HandleType(0x1000, PT_HIOS, 0));
- ASSERT_FALSE(interface.HandleType(0x1000, PT_LOPROC, 0));
- ASSERT_FALSE(interface.HandleType(0x1000, PT_HIPROC, 0));
- ASSERT_FALSE(interface.HandleType(0x1000, PT_GNU_EH_FRAME, 0));
- ASSERT_FALSE(interface.HandleType(0x1000, PT_GNU_STACK, 0));
+ ASSERT_FALSE(interface.HandleType(0x1000, PT_NULL));
+ ASSERT_FALSE(interface.HandleType(0x1000, PT_LOAD));
+ ASSERT_FALSE(interface.HandleType(0x1000, PT_DYNAMIC));
+ ASSERT_FALSE(interface.HandleType(0x1000, PT_INTERP));
+ ASSERT_FALSE(interface.HandleType(0x1000, PT_NOTE));
+ ASSERT_FALSE(interface.HandleType(0x1000, PT_SHLIB));
+ ASSERT_FALSE(interface.HandleType(0x1000, PT_PHDR));
+ ASSERT_FALSE(interface.HandleType(0x1000, PT_TLS));
+ ASSERT_FALSE(interface.HandleType(0x1000, PT_LOOS));
+ ASSERT_FALSE(interface.HandleType(0x1000, PT_HIOS));
+ ASSERT_FALSE(interface.HandleType(0x1000, PT_LOPROC));
+ ASSERT_FALSE(interface.HandleType(0x1000, PT_HIPROC));
+ ASSERT_FALSE(interface.HandleType(0x1000, PT_GNU_EH_FRAME));
+ ASSERT_FALSE(interface.HandleType(0x1000, PT_GNU_STACK));
}
TEST_F(ElfInterfaceArmTest, HandleType_arm_exidx) {
ElfInterfaceArmFake interface(&memory_);
- Elf32_Phdr phdr;
+ Elf32_Phdr phdr = {};
interface.FakeSetStartOffset(0x1000);
interface.FakeSetTotalEntries(100);
- phdr.p_vaddr = 0x2000;
- phdr.p_memsz = 0xa00;
+ phdr.p_offset = 0x2000;
+ phdr.p_filesz = 0xa00;
// Verify that if reads fail, we don't set the values but still get true.
- ASSERT_TRUE(interface.HandleType(0x1000, 0x70000001, 0));
- ASSERT_EQ(0x1000U, interface.start_offset());
- ASSERT_EQ(100U, interface.total_entries());
-
- // Verify that if the second read fails, we still don't set the values.
- memory_.SetData32(
- 0x1000 + reinterpret_cast<uint64_t>(&phdr.p_vaddr) - reinterpret_cast<uint64_t>(&phdr),
- phdr.p_vaddr);
- ASSERT_TRUE(interface.HandleType(0x1000, 0x70000001, 0));
+ ASSERT_TRUE(interface.HandleType(0x1000, 0x70000001));
ASSERT_EQ(0x1000U, interface.start_offset());
ASSERT_EQ(100U, interface.total_entries());
// Everything is correct and present.
- memory_.SetData32(
- 0x1000 + reinterpret_cast<uint64_t>(&phdr.p_memsz) - reinterpret_cast<uint64_t>(&phdr),
- phdr.p_memsz);
- ASSERT_TRUE(interface.HandleType(0x1000, 0x70000001, 0));
+ memory_.SetMemory(0x1000, &phdr, sizeof(phdr));
+ ASSERT_TRUE(interface.HandleType(0x1000, 0x70000001));
ASSERT_EQ(0x2000U, interface.start_offset());
ASSERT_EQ(320U, interface.total_entries());
-
- // Non-zero load bias.
- ASSERT_TRUE(interface.HandleType(0x1000, 0x70000001, 0x1000));
- ASSERT_EQ(0x1000U, interface.start_offset());
- ASSERT_EQ(320U, interface.total_entries());
}
TEST_F(ElfInterfaceArmTest, StepExidx) {
diff --git a/libunwindstack/tests/ElfInterfaceTest.cpp b/libunwindstack/tests/ElfInterfaceTest.cpp
index 4008e9b..487d39c 100644
--- a/libunwindstack/tests/ElfInterfaceTest.cpp
+++ b/libunwindstack/tests/ElfInterfaceTest.cpp
@@ -116,8 +116,7 @@
template <typename Sym>
void ElfInterfaceTest::InitSym(uint64_t offset, uint32_t value, uint32_t size, uint32_t name_offset,
uint64_t sym_offset, const char* name) {
- Sym sym;
- memset(&sym, 0, sizeof(sym));
+ Sym sym = {};
sym.st_info = STT_FUNC;
sym.st_value = value;
sym.st_size = size;
@@ -132,15 +131,13 @@
void ElfInterfaceTest::SinglePtLoad() {
std::unique_ptr<ElfInterface> elf(new ElfInterfaceType(&memory_));
- Ehdr ehdr;
- memset(&ehdr, 0, sizeof(ehdr));
+ Ehdr ehdr = {};
ehdr.e_phoff = 0x100;
ehdr.e_phnum = 1;
ehdr.e_phentsize = sizeof(Phdr);
memory_.SetMemory(0, &ehdr, sizeof(ehdr));
- Phdr phdr;
- memset(&phdr, 0, sizeof(phdr));
+ Phdr phdr = {};
phdr.p_type = PT_LOAD;
phdr.p_vaddr = 0x2000;
phdr.p_memsz = 0x10000;
@@ -172,15 +169,13 @@
void ElfInterfaceTest::MultipleExecutablePtLoads() {
std::unique_ptr<ElfInterface> elf(new ElfInterfaceType(&memory_));
- Ehdr ehdr;
- memset(&ehdr, 0, sizeof(ehdr));
+ Ehdr ehdr = {};
ehdr.e_phoff = 0x100;
ehdr.e_phnum = 3;
ehdr.e_phentsize = sizeof(Phdr);
memory_.SetMemory(0, &ehdr, sizeof(ehdr));
- Phdr phdr;
- memset(&phdr, 0, sizeof(phdr));
+ Phdr phdr = {};
phdr.p_type = PT_LOAD;
phdr.p_vaddr = 0x2000;
phdr.p_memsz = 0x10000;
@@ -241,15 +236,13 @@
void ElfInterfaceTest::MultipleExecutablePtLoadsIncrementsNotSizeOfPhdr() {
std::unique_ptr<ElfInterface> elf(new ElfInterfaceType(&memory_));
- Ehdr ehdr;
- memset(&ehdr, 0, sizeof(ehdr));
+ Ehdr ehdr = {};
ehdr.e_phoff = 0x100;
ehdr.e_phnum = 3;
ehdr.e_phentsize = sizeof(Phdr) + 100;
memory_.SetMemory(0, &ehdr, sizeof(ehdr));
- Phdr phdr;
- memset(&phdr, 0, sizeof(phdr));
+ Phdr phdr = {};
phdr.p_type = PT_LOAD;
phdr.p_vaddr = 0x2000;
phdr.p_memsz = 0x10000;
@@ -312,15 +305,13 @@
void ElfInterfaceTest::NonExecutablePtLoads() {
std::unique_ptr<ElfInterface> elf(new ElfInterfaceType(&memory_));
- Ehdr ehdr;
- memset(&ehdr, 0, sizeof(ehdr));
+ Ehdr ehdr = {};
ehdr.e_phoff = 0x100;
ehdr.e_phnum = 3;
ehdr.e_phentsize = sizeof(Phdr);
memory_.SetMemory(0, &ehdr, sizeof(ehdr));
- Phdr phdr;
- memset(&phdr, 0, sizeof(phdr));
+ Phdr phdr = {};
phdr.p_type = PT_LOAD;
phdr.p_vaddr = 0x2000;
phdr.p_memsz = 0x10000;
@@ -371,17 +362,15 @@
void ElfInterfaceTest::ManyPhdrs() {
std::unique_ptr<ElfInterface> elf(new ElfInterfaceType(&memory_));
- Ehdr ehdr;
- memset(&ehdr, 0, sizeof(ehdr));
+ Ehdr ehdr = {};
ehdr.e_phoff = 0x100;
ehdr.e_phnum = 7;
ehdr.e_phentsize = sizeof(Phdr);
memory_.SetMemory(0, &ehdr, sizeof(ehdr));
- Phdr phdr;
uint64_t phdr_offset = 0x100;
- memset(&phdr, 0, sizeof(phdr));
+ Phdr phdr = {};
phdr.p_type = PT_LOAD;
phdr.p_vaddr = 0x2000;
phdr.p_memsz = 0x10000;
@@ -444,18 +433,16 @@
TEST_F(ElfInterfaceTest, elf32_arm) {
ElfInterfaceArm elf_arm(&memory_);
- Elf32_Ehdr ehdr;
- memset(&ehdr, 0, sizeof(ehdr));
+ Elf32_Ehdr ehdr = {};
ehdr.e_phoff = 0x100;
ehdr.e_phnum = 1;
ehdr.e_phentsize = sizeof(Elf32_Phdr);
memory_.SetMemory(0, &ehdr, sizeof(ehdr));
- Elf32_Phdr phdr;
- memset(&phdr, 0, sizeof(phdr));
+ Elf32_Phdr phdr = {};
phdr.p_type = PT_ARM_EXIDX;
- phdr.p_vaddr = 0x2000;
- phdr.p_memsz = 16;
+ phdr.p_offset = 0x2000;
+ phdr.p_filesz = 16;
memory_.SetMemory(0x100, &phdr, sizeof(phdr));
// Add arm exidx entries.
@@ -480,8 +467,7 @@
template <typename Ehdr, typename Phdr, typename Shdr, typename Dyn>
void ElfInterfaceTest::SonameInit(SonameTestEnum test_type) {
- Ehdr ehdr;
- memset(&ehdr, 0, sizeof(ehdr));
+ Ehdr ehdr = {};
ehdr.e_shoff = 0x200;
ehdr.e_shnum = 2;
ehdr.e_shentsize = sizeof(Shdr);
@@ -490,8 +476,7 @@
ehdr.e_phentsize = sizeof(Phdr);
memory_.SetMemory(0, &ehdr, sizeof(ehdr));
- Shdr shdr;
- memset(&shdr, 0, sizeof(shdr));
+ Shdr shdr = {};
shdr.sh_type = SHT_STRTAB;
if (test_type == SONAME_MISSING_MAP) {
shdr.sh_addr = 0x20100;
@@ -501,8 +486,7 @@
shdr.sh_offset = 0x10000;
memory_.SetMemory(0x200 + sizeof(shdr), &shdr, sizeof(shdr));
- Phdr phdr;
- memset(&phdr, 0, sizeof(phdr));
+ Phdr phdr = {};
phdr.p_type = PT_DYNAMIC;
phdr.p_offset = 0x2000;
phdr.p_memsz = sizeof(Dyn) * 3;
@@ -748,8 +732,7 @@
void ElfInterfaceTest::InitSectionHeadersMalformed() {
std::unique_ptr<ElfInterfaceType> elf(new ElfInterfaceType(&memory_));
- Ehdr ehdr;
- memset(&ehdr, 0, sizeof(ehdr));
+ Ehdr ehdr = {};
ehdr.e_shoff = 0x1000;
ehdr.e_shnum = 10;
ehdr.e_shentsize = sizeof(Shdr);
@@ -774,8 +757,7 @@
uint64_t offset = 0x1000;
- Ehdr ehdr;
- memset(&ehdr, 0, sizeof(ehdr));
+ Ehdr ehdr = {};
ehdr.e_shoff = offset;
ehdr.e_shnum = 10;
ehdr.e_shentsize = entry_size;
@@ -783,8 +765,7 @@
offset += ehdr.e_shentsize;
- Shdr shdr;
- memset(&shdr, 0, sizeof(shdr));
+ Shdr shdr = {};
shdr.sh_type = SHT_SYMTAB;
shdr.sh_link = 4;
shdr.sh_addr = 0x5000;
@@ -863,8 +844,7 @@
uint64_t offset = 0x2000;
- Ehdr ehdr;
- memset(&ehdr, 0, sizeof(ehdr));
+ Ehdr ehdr = {};
ehdr.e_shoff = offset;
ehdr.e_shnum = 10;
ehdr.e_shentsize = sizeof(Shdr);
@@ -873,8 +853,7 @@
offset += ehdr.e_shentsize;
- Shdr shdr;
- memset(&shdr, 0, sizeof(shdr));
+ Shdr shdr = {};
shdr.sh_type = SHT_PROGBITS;
shdr.sh_link = 2;
shdr.sh_name = 0x200;
@@ -956,15 +935,13 @@
TEST_F(ElfInterfaceTest, is_valid_pc_from_pt_load) {
std::unique_ptr<ElfInterface> elf(new ElfInterface32(&memory_));
- Elf32_Ehdr ehdr;
- memset(&ehdr, 0, sizeof(ehdr));
+ Elf32_Ehdr ehdr = {};
ehdr.e_phoff = 0x100;
ehdr.e_phnum = 1;
ehdr.e_phentsize = sizeof(Elf32_Phdr);
memory_.SetMemory(0, &ehdr, sizeof(ehdr));
- Elf32_Phdr phdr;
- memset(&phdr, 0, sizeof(phdr));
+ Elf32_Phdr phdr = {};
phdr.p_type = PT_LOAD;
phdr.p_vaddr = 0;
phdr.p_memsz = 0x10000;
@@ -984,15 +961,13 @@
TEST_F(ElfInterfaceTest, is_valid_pc_from_pt_load_non_zero_load_bias) {
std::unique_ptr<ElfInterface> elf(new ElfInterface32(&memory_));
- Elf32_Ehdr ehdr;
- memset(&ehdr, 0, sizeof(ehdr));
+ Elf32_Ehdr ehdr = {};
ehdr.e_phoff = 0x100;
ehdr.e_phnum = 1;
ehdr.e_phentsize = sizeof(Elf32_Phdr);
memory_.SetMemory(0, &ehdr, sizeof(ehdr));
- Elf32_Phdr phdr;
- memset(&phdr, 0, sizeof(phdr));
+ Elf32_Phdr phdr = {};
phdr.p_type = PT_LOAD;
phdr.p_vaddr = 0x2000;
phdr.p_memsz = 0x10000;
@@ -1017,16 +992,14 @@
uint64_t sh_offset = 0x100;
- Elf32_Ehdr ehdr;
- memset(&ehdr, 0, sizeof(ehdr));
+ Elf32_Ehdr ehdr = {};
ehdr.e_shstrndx = 1;
ehdr.e_shoff = sh_offset;
ehdr.e_shentsize = sizeof(Elf32_Shdr);
ehdr.e_shnum = 3;
memory_.SetMemory(0, &ehdr, sizeof(ehdr));
- Elf32_Shdr shdr;
- memset(&shdr, 0, sizeof(shdr));
+ Elf32_Shdr shdr = {};
shdr.sh_type = SHT_NULL;
memory_.SetMemory(sh_offset, &shdr, sizeof(shdr));
@@ -1080,16 +1053,14 @@
uint64_t sh_offset = 0x100;
- Elf32_Ehdr ehdr;
- memset(&ehdr, 0, sizeof(ehdr));
+ Elf32_Ehdr ehdr = {};
ehdr.e_shstrndx = 1;
ehdr.e_shoff = sh_offset;
ehdr.e_shentsize = sizeof(Elf32_Shdr);
ehdr.e_shnum = 3;
memory_.SetMemory(0, &ehdr, sizeof(ehdr));
- Elf32_Shdr shdr;
- memset(&shdr, 0, sizeof(shdr));
+ Elf32_Shdr shdr = {};
shdr.sh_type = SHT_NULL;
memory_.SetMemory(sh_offset, &shdr, sizeof(shdr));
diff --git a/logcat/.clang-format b/logcat/.clang-format
deleted file mode 100644
index 393c309..0000000
--- a/logcat/.clang-format
+++ /dev/null
@@ -1,11 +0,0 @@
-BasedOnStyle: Google
-AllowShortFunctionsOnASingleLine: false
-
-CommentPragmas: NOLINT:.*
-DerivePointerAlignment: false
-IndentWidth: 4
-PointerAlignment: Left
-TabWidth: 4
-PenaltyExcessCharacter: 32
-
-Cpp11BracedListStyle: false
diff --git a/logcat/Android.bp b/logcat/Android.bp
index 01beb53..b0563a6 100644
--- a/logcat/Android.bp
+++ b/logcat/Android.bp
@@ -1,5 +1,5 @@
//
-// Copyright (C) 2006-2017 The Android Open Source Project
+// Copyright (C) 2006 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.
@@ -31,25 +31,13 @@
logtags: ["event.logtags"],
}
-cc_library {
- name: "liblogcat",
-
- defaults: ["logcat_defaults"],
- srcs: [
- "logcat.cpp",
- "getopt_long.cpp",
- "logcat_system.cpp",
- ],
- export_include_dirs: ["include"],
-}
-
cc_binary {
name: "logcat",
defaults: ["logcat_defaults"],
- shared_libs: ["liblogcat"],
srcs: [
"logcat_main.cpp",
+ "logcat.cpp",
],
}
@@ -57,9 +45,9 @@
name: "logcatd",
defaults: ["logcat_defaults"],
- shared_libs: ["liblogcat"],
srcs: [
"logcatd_main.cpp",
+ "logcat.cpp",
],
}
diff --git a/logcat/getopt_long.cpp b/logcat/getopt_long.cpp
deleted file mode 100644
index da99906..0000000
--- a/logcat/getopt_long.cpp
+++ /dev/null
@@ -1,401 +0,0 @@
-/* $OpenBSD: getopt_long.c,v 1.26 2013/06/08 22:47:56 millert Exp $ */
-/* $NetBSD: getopt_long.c,v 1.15 2002/01/31 22:43:40 tv Exp $ */
-
-/*
- * Copyright (c) 2002 Todd C. Miller <Todd.Miller@courtesan.com>
- *
- * Permission to use, copy, modify, and distribute this software for any
- * purpose with or without fee is hereby granted, provided that the above
- * copyright notice and this permission notice appear in all copies.
- *
- * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
- * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
- * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
- * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
- * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
- * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
- * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
- *
- * Sponsored in part by the Defense Advanced Research Projects
- * Agency (DARPA) and Air Force Research Laboratory, Air Force
- * Materiel Command, USAF, under agreement number F39502-99-1-0512.
- */
-/*-
- * Copyright (c) 2000 The NetBSD Foundation, Inc.
- * All rights reserved.
- *
- * This code is derived from software contributed to The NetBSD Foundation
- * by Dieter Baron and Thomas Klausner.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- * 1. Redistributions of source code must retain the above copyright
- * notice, this list of conditions and the following disclaimer.
- * 2. Redistributions in binary form must reproduce the above copyright
- * notice, this list of conditions and the following disclaimer in the
- * documentation and/or other materials provided with the distribution.
- *
- * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
- * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
- * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
- * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
- * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
- * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
- * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
- * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
- * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
- * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
- * POSSIBILITY OF SUCH DAMAGE.
- */
-
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-#include <sys/cdefs.h>
-
-#include <log/getopt.h>
-
-#define PRINT_ERROR ((context->opterr) && (*options != ':'))
-
-#define FLAG_PERMUTE 0x01 // permute non-options to the end of argv
-#define FLAG_ALLARGS 0x02 // treat non-options as args to option "-1"
-
-// return values
-#define BADCH (int)'?'
-#define BADARG ((*options == ':') ? (int)':' : (int)'?')
-#define INORDER (int)1
-
-#define D_PREFIX 0
-#define DD_PREFIX 1
-#define W_PREFIX 2
-
-// Compute the greatest common divisor of a and b.
-static int gcd(int a, int b) {
- int c = a % b;
- while (c) {
- a = b;
- b = c;
- c = a % b;
- }
- return b;
-}
-
-// Exchange the block from nonopt_start to nonopt_end with the block from
-// nonopt_end to opt_end (keeping the same order of arguments in each block).
-// Returns optind - (nonopt_end - nonopt_start) for convenience.
-static int permute_args(getopt_context* context, char* const* nargv) {
- // compute lengths of blocks and number and size of cycles
- int nnonopts = context->nonopt_end - context->nonopt_start;
- int nopts = context->optind - context->nonopt_end;
- int ncycle = gcd(nnonopts, nopts);
- int cyclelen = (context->optind - context->nonopt_start) / ncycle;
-
- for (int i = 0; i < ncycle; i++) {
- int cstart = context->nonopt_end + i;
- int pos = cstart;
- for (int j = 0; j < cyclelen; j++) {
- if (pos >= context->nonopt_end) {
- pos -= nnonopts;
- } else {
- pos += nopts;
- }
- char* swap = nargv[pos];
- const_cast<char**>(nargv)[pos] = nargv[cstart];
- const_cast<char**>(nargv)[cstart] = swap;
- }
- }
- return context->optind - (context->nonopt_end - context->nonopt_start);
-}
-
-// parse_long_options_r --
-// Parse long options in argc/argv argument vector.
-// Returns -1 if short_too is set and the option does not match long_options.
-static int parse_long_options_r(char* const* nargv, const char* options,
- const struct option* long_options, int* idx,
- bool short_too, struct getopt_context* context) {
- const char* current_argv = context->place;
- const char* current_dash;
- switch (context->dash_prefix) {
- case D_PREFIX:
- current_dash = "-";
- break;
- case DD_PREFIX:
- current_dash = "--";
- break;
- case W_PREFIX:
- current_dash = "-W ";
- break;
- default:
- current_dash = "";
- break;
- }
- context->optind++;
-
- const char* has_equal;
- size_t current_argv_len;
- if (!!(has_equal = strchr(current_argv, '='))) {
- // argument found (--option=arg)
- current_argv_len = has_equal - current_argv;
- has_equal++;
- } else {
- current_argv_len = strlen(current_argv);
- }
-
- int match = -1;
- bool exact_match = false;
- bool second_partial_match = false;
- for (int i = 0; long_options[i].name; i++) {
- // find matching long option
- if (strncmp(current_argv, long_options[i].name, current_argv_len)) {
- continue;
- }
-
- if (strlen(long_options[i].name) == current_argv_len) {
- // exact match
- match = i;
- exact_match = true;
- break;
- }
- // If this is a known short option, don't allow
- // a partial match of a single character.
- if (short_too && current_argv_len == 1) continue;
-
- if (match == -1) { // first partial match
- match = i;
- } else if (long_options[i].has_arg != long_options[match].has_arg ||
- long_options[i].flag != long_options[match].flag ||
- long_options[i].val != long_options[match].val) {
- second_partial_match = true;
- }
- }
- if (!exact_match && second_partial_match) {
- // ambiguous abbreviation
- if (PRINT_ERROR) {
- fprintf(context->optstderr ?: stderr,
- "option `%s%.*s' is ambiguous", current_dash,
- (int)current_argv_len, current_argv);
- }
- context->optopt = 0;
- return BADCH;
- }
- if (match != -1) { // option found
- if (long_options[match].has_arg == no_argument && has_equal) {
- if (PRINT_ERROR) {
- fprintf(context->optstderr ?: stderr,
- "option `%s%.*s' doesn't allow an argument",
- current_dash, (int)current_argv_len, current_argv);
- }
- // XXX: GNU sets optopt to val regardless of flag
- context->optopt =
- long_options[match].flag ? 0 : long_options[match].val;
- return BADCH;
- }
- if (long_options[match].has_arg == required_argument ||
- long_options[match].has_arg == optional_argument) {
- if (has_equal) {
- context->optarg = has_equal;
- } else if (long_options[match].has_arg == required_argument) {
- // optional argument doesn't use next nargv
- context->optarg = nargv[context->optind++];
- }
- }
- if ((long_options[match].has_arg == required_argument) &&
- !context->optarg) {
- // Missing argument; leading ':' indicates no error
- // should be generated.
- if (PRINT_ERROR) {
- fprintf(context->optstderr ?: stderr,
- "option `%s%s' requires an argument", current_dash,
- current_argv);
- }
- // XXX: GNU sets optopt to val regardless of flag
- context->optopt =
- long_options[match].flag ? 0 : long_options[match].val;
- context->optind--;
- return BADARG;
- }
- } else { // unknown option
- if (short_too) {
- context->optind--;
- return -1;
- }
- if (PRINT_ERROR) {
- fprintf(context->optstderr ?: stderr, "unrecognized option `%s%s'",
- current_dash, current_argv);
- }
- context->optopt = 0;
- return BADCH;
- }
- if (idx) *idx = match;
- if (long_options[match].flag) {
- *long_options[match].flag = long_options[match].val;
- return 0;
- }
- return long_options[match].val;
-}
-
-// getopt_long_r --
-// Parse argc/argv argument vector.
-int getopt_long_r(int nargc, char* const* nargv, const char* options,
- const struct option* long_options, int* idx,
- struct getopt_context* context) {
- if (!options) return -1;
-
- // XXX Some GNU programs (like cvs) set optind to 0 instead of
- // XXX using optreset. Work around this braindamage.
- if (!context->optind) context->optind = context->optreset = 1;
-
- // Disable GNU extensions if options string begins with a '+'.
- int flags = FLAG_PERMUTE;
- if (*options == '-') {
- flags |= FLAG_ALLARGS;
- } else if (*options == '+') {
- flags &= ~FLAG_PERMUTE;
- }
- if (*options == '+' || *options == '-') options++;
-
- context->optarg = nullptr;
- if (context->optreset) context->nonopt_start = context->nonopt_end = -1;
-start:
- if (context->optreset || !*context->place) { // update scanning pointer
- context->optreset = 0;
- if (context->optind >= nargc) { // end of argument vector
- context->place = EMSG;
- if (context->nonopt_end != -1) {
- // do permutation, if we have to
- context->optind = permute_args(context, nargv);
- } else if (context->nonopt_start != -1) {
- // If we skipped non-options, set optind to the first of them.
- context->optind = context->nonopt_start;
- }
- context->nonopt_start = context->nonopt_end = -1;
- return -1;
- }
- if (*(context->place = nargv[context->optind]) != '-' ||
- context->place[1] == '\0') {
- context->place = EMSG; // found non-option
- if (flags & FLAG_ALLARGS) {
- // GNU extension: return non-option as argument to option 1
- context->optarg = nargv[context->optind++];
- return INORDER;
- }
- if (!(flags & FLAG_PERMUTE)) {
- // If no permutation wanted, stop parsing at first non-option.
- return -1;
- }
- // do permutation
- if (context->nonopt_start == -1) {
- context->nonopt_start = context->optind;
- } else if (context->nonopt_end != -1) {
- context->nonopt_start = permute_args(context, nargv);
- context->nonopt_end = -1;
- }
- context->optind++;
- // process next argument
- goto start;
- }
- if (context->nonopt_start != -1 && context->nonopt_end == -1) {
- context->nonopt_end = context->optind;
- }
-
- // If we have "-" do nothing, if "--" we are done.
- if (context->place[1] != '\0' && *++(context->place) == '-' &&
- context->place[1] == '\0') {
- context->optind++;
- context->place = EMSG;
- // We found an option (--), so if we skipped
- // non-options, we have to permute.
- if (context->nonopt_end != -1) {
- context->optind = permute_args(context, nargv);
- }
- context->nonopt_start = context->nonopt_end = -1;
- return -1;
- }
- }
-
- int optchar;
- // Check long options if:
- // 1) we were passed some
- // 2) the arg is not just "-"
- // 3) either the arg starts with -- we are getopt_long_only()
- if (long_options && context->place != nargv[context->optind] &&
- (*context->place == '-')) {
- bool short_too = false;
- context->dash_prefix = D_PREFIX;
- if (*context->place == '-') {
- context->place++; // --foo long option
- context->dash_prefix = DD_PREFIX;
- } else if (*context->place != ':' && strchr(options, *context->place)) {
- short_too = true; // could be short option too
- }
-
- optchar = parse_long_options_r(nargv, options, long_options, idx,
- short_too, context);
- if (optchar != -1) {
- context->place = EMSG;
- return optchar;
- }
- }
-
- const char* oli; // option letter list index
- if ((optchar = (int)*(context->place)++) == (int)':' ||
- (optchar == (int)'-' && *context->place != '\0') ||
- !(oli = strchr(options, optchar))) {
- // If the user specified "-" and '-' isn't listed in
- // options, return -1 (non-option) as per POSIX.
- // Otherwise, it is an unknown option character (or ':').
- if (optchar == (int)'-' && *context->place == '\0') return -1;
- if (!*context->place) context->optind++;
- if (PRINT_ERROR) {
- fprintf(context->optstderr ?: stderr, "invalid option -- %c",
- optchar);
- }
- context->optopt = optchar;
- return BADCH;
- }
-
- static const char recargchar[] = "option requires an argument -- %c";
- if (long_options && optchar == 'W' && oli[1] == ';') {
- // -W long-option
- if (*context->place) { // no space
- ; // NOTHING
- } else if (++(context->optind) >= nargc) { // no arg
- context->place = EMSG;
- if (PRINT_ERROR) {
- fprintf(context->optstderr ?: stderr, recargchar, optchar);
- }
- context->optopt = optchar;
- return BADARG;
- } else { // white space
- context->place = nargv[context->optind];
- }
- context->dash_prefix = W_PREFIX;
- optchar = parse_long_options_r(nargv, options, long_options, idx, false,
- context);
- context->place = EMSG;
- return optchar;
- }
- if (*++oli != ':') { // doesn't take argument
- if (!*context->place) context->optind++;
- } else { // takes (optional) argument
- context->optarg = nullptr;
- if (*context->place) { // no white space
- context->optarg = context->place;
- } else if (oli[1] != ':') { // arg not optional
- if (++(context->optind) >= nargc) { // no arg
- context->place = EMSG;
- if (PRINT_ERROR) {
- fprintf(context->optstderr ?: stderr, recargchar, optchar);
- }
- context->optopt = optchar;
- return BADARG;
- }
- context->optarg = nargv[context->optind];
- }
- context->place = EMSG;
- context->optind++;
- }
- // dump back option letter
- return optchar;
-}
diff --git a/logcat/include/log/getopt.h b/logcat/include/log/getopt.h
deleted file mode 100644
index 0da2b10..0000000
--- a/logcat/include/log/getopt.h
+++ /dev/null
@@ -1,67 +0,0 @@
-/*
- * Copyright (C) 2017 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.
- */
-
-#ifndef _LOG_GETOPT_H_
-#define _LOG_GETOPT_H_
-
-#ifndef __ANDROID_USE_LIBLOG_LOGCAT_INTERFACE
-#ifndef __ANDROID_API__
-#define __ANDROID_USE_LIBLOG_LOGCAT_INTERFACE 1
-#elif __ANDROID_API__ > 24 /* > Nougat */
-#define __ANDROID_USE_LIBLOG_LOGCAT_INTERFACE 1
-#else
-#define __ANDROID_USE_LIBLOG_LOGCAT_INTERFACE 0
-#endif
-#endif
-
-#if __ANDROID_USE_LIBLOG_LOGCAT_INTERFACE
-
-#include <getopt.h>
-#include <sys/cdefs.h>
-
-struct getopt_context {
- int opterr;
- int optind;
- int optopt;
- int optreset;
- const char* optarg;
- FILE* optstderr; /* NULL defaults to stderr */
- /* private */
- const char* place;
- int nonopt_start;
- int nonopt_end;
- int dash_prefix;
- /* expansion space */
- int __extra__;
- void* __stuff__;
-};
-
-#define EMSG ""
-#define NO_PREFIX (-1)
-
-#define INIT_GETOPT_CONTEXT(context) \
- context = { 1, 1, '?', 0, NULL, NULL, EMSG, -1, -1, NO_PREFIX, 0, NULL }
-
-__BEGIN_DECLS
-int getopt_long_r(int nargc, char* const* nargv, const char* options,
- const struct option* long_options, int* idx,
- struct getopt_context* context);
-
-__END_DECLS
-
-#endif /* __ANDROID_USE_LIBLOG_LOGCAT_INTERFACE */
-
-#endif /* !_LOG_GETOPT_H_ */
diff --git a/logcat/include/log/logcat.h b/logcat/include/log/logcat.h
deleted file mode 100644
index 009672c..0000000
--- a/logcat/include/log/logcat.h
+++ /dev/null
@@ -1,122 +0,0 @@
-/*
- * Copyright (C) 2005-2017 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.
- */
-
-#ifndef _LIBS_LOGCAT_H /* header boilerplate */
-#define _LIBS_LOGCAT_H
-
-#include <stdio.h>
-
-#ifdef __cplusplus
-extern "C" {
-#endif
-
-#ifndef __ANDROID_USE_LIBLOG_LOGCAT_INTERFACE
-#ifndef __ANDROID_API__
-#define __ANDROID_USE_LIBLOG_LOGCAT_INTERFACE 1
-#elif __ANDROID_API__ > 24 /* > Nougat */
-#define __ANDROID_USE_LIBLOG_LOGCAT_INTERFACE 1
-#else
-#define __ANDROID_USE_LIBLOG_LOGCAT_INTERFACE 0
-#endif
-#endif
-
-#if __ANDROID_USE_LIBLOG_LOGCAT_INTERFACE
-
-/* For managing an in-process logcat function, rather than forking/execing
- *
- * It also serves as the basis for the logcat command.
- *
- * The following C API allows a logcat instance to be created, run
- * to completion, and then release all the associated resources.
- */
-
-/*
- * The opaque context
- */
-#ifndef __android_logcat_context_defined /* typedef boilerplate */
-#define __android_logcat_context_defined
-typedef struct android_logcat_context_internal* android_logcat_context;
-#endif
-
-/* Creates a context associated with this logcat instance
- *
- * Returns a pointer to the context, or a NULL on error.
- */
-android_logcat_context create_android_logcat();
-
-/* Collects and outputs the logcat data to output and error file descriptors
- *
- * Will block, performed in-thread and in-process
- *
- * The output file descriptor variable, if greater than or equal to 0, is
- * where the output (ie: stdout) will be sent. The file descriptor is closed
- * on android_logcat_destroy which terminates the instance, or when an -f flag
- * (output redirect to a file) is present in the command. The error file
- * descriptor variable, if greater than or equal to 0, is where the error
- * stream (ie: stderr) will be sent, also closed on android_logcat_destroy.
- * The error file descriptor can be set to equal to the output file descriptor,
- * which will mix output and error stream content, and will defer closure of
- * the file descriptor on -f flag redirection. Negative values for the file
- * descriptors will use stdout and stderr FILE references respectively
- * internally, and will not close the references as noted above.
- *
- * Return value is 0 for success, non-zero for errors.
- */
-int android_logcat_run_command(android_logcat_context ctx, int output, int error,
- int argc, char* const* argv, char* const* envp);
-
-/* Will not block, performed in-process
- *
- * Starts a thread, opens a pipe, returns reading end fd, saves off argv.
- * The command supports 2>&1 (mix content) and 2>/dev/null (drop content) for
- * scripted error (stderr) redirection.
- */
-int android_logcat_run_command_thread(android_logcat_context ctx, int argc,
- char* const* argv, char* const* envp);
-int android_logcat_run_command_thread_running(android_logcat_context ctx);
-
-/* Finished with context
- *
- * Kill the command thread ASAP (if any), and free up all associated resources.
- *
- * Return value is the result of the android_logcat_run_command, or
- * non-zero for any errors.
- */
-int android_logcat_destroy(android_logcat_context* ctx);
-
-/* derived helpers */
-
-/*
- * In-process thread that acts like somewhat like libc-like system and popen
- * respectively. Can not handle shell scripting, only pure calls to the
- * logcat operations. The android_logcat_system is a wrapper for the
- * create_android_logcat, android_logcat_run_command and android_logcat_destroy
- * API above. The android_logcat_popen is a wrapper for the
- * android_logcat_run_command_thread API above. The android_logcat_pclose is
- * a wrapper for a reasonable wait until output has subsided for command
- * completion, fclose on the FILE pointer and the android_logcat_destroy API.
- */
-int android_logcat_system(const char* command);
-FILE* android_logcat_popen(android_logcat_context* ctx, const char* command);
-int android_logcat_pclose(android_logcat_context* ctx, FILE* output);
-
-#endif /* __ANDROID_USE_LIBLOG_LOGCAT_INTERFACE */
-
-#ifdef __cplusplus
-}
-#endif
-
-#endif /* _LIBS_LOGCAT_H */
diff --git a/logcat/logcat.cpp b/logcat/logcat.cpp
index ff85f54..0f56337 100644
--- a/logcat/logcat.cpp
+++ b/logcat/logcat.cpp
@@ -14,12 +14,15 @@
* limitations under the License.
*/
+#include "logcat.h"
+
#include <arpa/inet.h>
#include <assert.h>
#include <ctype.h>
#include <dirent.h>
#include <errno.h>
#include <fcntl.h>
+#include <getopt.h>
#include <math.h>
#include <pthread.h>
#include <sched.h>
@@ -47,8 +50,6 @@
#include <cutils/sched_policy.h>
#include <cutils/sockets.h>
#include <log/event_tag_map.h>
-#include <log/getopt.h>
-#include <log/logcat.h>
#include <log/logprint.h>
#include <private/android_logger.h>
#include <system/thread_defs.h>
@@ -854,14 +855,8 @@
// net for stability dealing with possible mistaken inputs.
static const char delimiters[] = ",:; \t\n\r\f";
- struct getopt_context optctx;
- INIT_GETOPT_CONTEXT(optctx);
- optctx.opterr = !!context->error;
- optctx.optstderr = context->error;
-
- for (;;) {
- int ret;
-
+ optind = 0;
+ while (true) {
int option_index = 0;
// list of long-argument only strings for later comparison
static const char pid_str[] = "pid";
@@ -902,19 +897,18 @@
};
// clang-format on
- ret = getopt_long_r(argc, argv, ":cdDhLt:T:gG:sQf:r:n:v:b:BSpP:m:e:",
- long_options, &option_index, &optctx);
- if (ret < 0) break;
+ int c = getopt_long(argc, argv, ":cdDhLt:T:gG:sQf:r:n:v:b:BSpP:m:e:", long_options,
+ &option_index);
+ if (c == -1) break;
- switch (ret) {
+ switch (c) {
case 0:
// only long options
if (long_options[option_index].name == pid_str) {
// ToDo: determine runtime PID_MAX?
- if (!getSizeTArg(optctx.optarg, &pid, 1)) {
+ if (!getSizeTArg(optarg, &pid, 1)) {
logcat_panic(context, HELP_TRUE, "%s %s out of range\n",
- long_options[option_index].name,
- optctx.optarg);
+ long_options[option_index].name, optarg);
goto exit;
}
break;
@@ -924,11 +918,9 @@
ANDROID_LOG_NONBLOCK;
// ToDo: implement API that supports setting a wrap timeout
size_t dummy = ANDROID_LOG_WRAP_DEFAULT_TIMEOUT;
- if (optctx.optarg &&
- !getSizeTArg(optctx.optarg, &dummy, 1)) {
+ if (optarg && !getSizeTArg(optarg, &dummy, 1)) {
logcat_panic(context, HELP_TRUE, "%s %s out of range\n",
- long_options[option_index].name,
- optctx.optarg);
+ long_options[option_index].name, optarg);
goto exit;
}
if ((dummy != ANDROID_LOG_WRAP_DEFAULT_TIMEOUT) &&
@@ -949,8 +941,7 @@
break;
}
if (long_options[option_index].name == id_str) {
- setId = (optctx.optarg && optctx.optarg[0]) ? optctx.optarg
- : nullptr;
+ setId = (optarg && optarg[0]) ? optarg : nullptr;
}
break;
@@ -978,32 +969,27 @@
mode |= ANDROID_LOG_RDONLY | ANDROID_LOG_NONBLOCK;
// FALLTHRU
case 'T':
- if (strspn(optctx.optarg, "0123456789") !=
- strlen(optctx.optarg)) {
- char* cp = parseTime(tail_time, optctx.optarg);
+ if (strspn(optarg, "0123456789") != strlen(optarg)) {
+ char* cp = parseTime(tail_time, optarg);
if (!cp) {
- logcat_panic(context, HELP_FALSE,
- "-%c \"%s\" not in time format\n", ret,
- optctx.optarg);
+ logcat_panic(context, HELP_FALSE, "-%c \"%s\" not in time format\n", c,
+ optarg);
goto exit;
}
if (*cp) {
- char c = *cp;
+ char ch = *cp;
*cp = '\0';
if (context->error) {
- fprintf(
- context->error,
- "WARNING: -%c \"%s\"\"%c%s\" time truncated\n",
- ret, optctx.optarg, c, cp + 1);
+ fprintf(context->error, "WARNING: -%c \"%s\"\"%c%s\" time truncated\n",
+ c, optarg, ch, cp + 1);
}
- *cp = c;
+ *cp = ch;
}
} else {
- if (!getSizeTArg(optctx.optarg, &tail_lines, 1)) {
+ if (!getSizeTArg(optarg, &tail_lines, 1)) {
if (context->error) {
- fprintf(context->error,
- "WARNING: -%c %s invalid, setting to 1\n",
- ret, optctx.optarg);
+ fprintf(context->error, "WARNING: -%c %s invalid, setting to 1\n", c,
+ optarg);
}
tail_lines = 1;
}
@@ -1015,21 +1001,19 @@
break;
case 'e':
- context->regex = new pcrecpp::RE(optctx.optarg);
+ context->regex = new pcrecpp::RE(optarg);
break;
case 'm': {
- if (!getSizeTArg(optctx.optarg, &context->maxCount)) {
+ if (!getSizeTArg(optarg, &context->maxCount)) {
logcat_panic(context, HELP_FALSE,
- "-%c \"%s\" isn't an "
- "integer greater than zero\n",
- ret, optctx.optarg);
+ "-%c \"%s\" isn't an integer greater than zero\n", c, optarg);
goto exit;
}
} break;
case 'g':
- if (!optctx.optarg) {
+ if (!optarg) {
getLogSize = true;
break;
}
@@ -1037,8 +1021,8 @@
case 'G': {
char* cp;
- if (strtoll(optctx.optarg, &cp, 0) > 0) {
- setLogSize = strtoll(optctx.optarg, &cp, 0);
+ if (strtoll(optarg, &cp, 0) > 0) {
+ setLogSize = strtoll(optarg, &cp, 0);
} else {
setLogSize = 0;
}
@@ -1071,19 +1055,18 @@
} break;
case 'p':
- if (!optctx.optarg) {
+ if (!optarg) {
getPruneList = true;
break;
}
// FALLTHRU
case 'P':
- setPruneList = optctx.optarg;
+ setPruneList = optarg;
break;
case 'b': {
- std::unique_ptr<char, void (*)(void*)> buffers(
- strdup(optctx.optarg), free);
+ std::unique_ptr<char, void (*)(void*)> buffers(strdup(optarg), free);
char* arg = buffers.get();
unsigned idMask = 0;
char* sv = nullptr; // protect against -ENOMEM above
@@ -1147,40 +1130,33 @@
case 'f':
if ((tail_time == log_time::EPOCH) && !tail_lines) {
- tail_time = lastLogTime(optctx.optarg);
+ tail_time = lastLogTime(optarg);
}
// redirect output to a file
- context->outputFileName = optctx.optarg;
+ context->outputFileName = optarg;
break;
case 'r':
- if (!getSizeTArg(optctx.optarg, &context->logRotateSizeKBytes,
- 1)) {
- logcat_panic(context, HELP_TRUE,
- "Invalid parameter \"%s\" to -r\n",
- optctx.optarg);
+ if (!getSizeTArg(optarg, &context->logRotateSizeKBytes, 1)) {
+ logcat_panic(context, HELP_TRUE, "Invalid parameter \"%s\" to -r\n", optarg);
goto exit;
}
break;
case 'n':
- if (!getSizeTArg(optctx.optarg, &context->maxRotatedLogs, 1)) {
- logcat_panic(context, HELP_TRUE,
- "Invalid parameter \"%s\" to -n\n",
- optctx.optarg);
+ if (!getSizeTArg(optarg, &context->maxRotatedLogs, 1)) {
+ logcat_panic(context, HELP_TRUE, "Invalid parameter \"%s\" to -n\n", optarg);
goto exit;
}
break;
case 'v': {
- if (!strcmp(optctx.optarg, "help") ||
- !strcmp(optctx.optarg, "--help")) {
+ if (!strcmp(optarg, "help") || !strcmp(optarg, "--help")) {
show_format_help(context);
context->retval = EXIT_SUCCESS;
goto exit;
}
- std::unique_ptr<char, void (*)(void*)> formats(
- strdup(optctx.optarg), free);
+ std::unique_ptr<char, void (*)(void*)> formats(strdup(optarg), free);
char* arg = formats.get();
char* sv = nullptr; // protect against -ENOMEM above
while (!!(arg = strtok_r(arg, delimiters, &sv))) {
@@ -1300,8 +1276,7 @@
break;
case ':':
- logcat_panic(context, HELP_TRUE,
- "Option -%c needs an argument\n", optctx.optopt);
+ logcat_panic(context, HELP_TRUE, "Option -%c needs an argument\n", optopt);
goto exit;
case 'h':
@@ -1310,8 +1285,7 @@
goto exit;
default:
- logcat_panic(context, HELP_TRUE, "Unrecognized Option %c\n",
- optctx.optopt);
+ logcat_panic(context, HELP_TRUE, "Unrecognized Option %c\n", optopt);
goto exit;
}
}
@@ -1400,7 +1374,7 @@
"Invalid filter expression in logcat args\n");
goto exit;
}
- } else if (argc == optctx.optind) {
+ } else if (argc == optind) {
// Add from environment variable
const char* env_tags_orig = android::getenv(context, "ANDROID_LOG_TAGS");
@@ -1416,7 +1390,7 @@
}
} else {
// Add from commandline
- for (int i = optctx.optind ; i < argc ; i++) {
+ for (int i = optind ; i < argc ; i++) {
// skip stderr redirections of _all_ kinds
if ((argv[i][0] == '2') && (argv[i][1] == '>')) continue;
// skip stdout redirections of _all_ kinds
@@ -1707,105 +1681,6 @@
return __logcat(context);
}
-// starts a thread, opens a pipe, returns reading end.
-int android_logcat_run_command_thread(android_logcat_context ctx,
- int argc, char* const* argv,
- char* const* envp) {
- android_logcat_context_internal* context = ctx;
-
- int save_errno = EBUSY;
- if ((context->fds[0] >= 0) || (context->fds[1] >= 0)) goto exit;
-
- if (pipe(context->fds) < 0) {
- save_errno = errno;
- goto exit;
- }
-
- pthread_attr_t attr;
- if (pthread_attr_init(&attr)) {
- save_errno = errno;
- goto close_exit;
- }
-
- struct sched_param param;
- memset(¶m, 0, sizeof(param));
- pthread_attr_setschedparam(&attr, ¶m);
- pthread_attr_setschedpolicy(&attr, SCHED_BATCH);
- if (pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED)) {
- save_errno = errno;
- goto pthread_attr_exit;
- }
-
- context->stop = false;
- context->thread_stopped = false;
- context->output_fd = context->fds[1];
- // save off arguments so they remain while thread is active.
- for (int i = 0; i < argc; ++i) {
- context->args.push_back(std::string(argv[i]));
- }
- // save off environment so they remain while thread is active.
- if (envp) for (size_t i = 0; envp[i]; ++i) {
- context->envs.push_back(std::string(envp[i]));
- }
-
- for (auto& str : context->args) {
- context->argv_hold.push_back(str.c_str());
- }
- context->argv_hold.push_back(nullptr);
- for (auto& str : context->envs) {
- context->envp_hold.push_back(str.c_str());
- }
- context->envp_hold.push_back(nullptr);
-
- context->argc = context->argv_hold.size() - 1;
- context->argv = (char* const*)&context->argv_hold[0];
- context->envp = (char* const*)&context->envp_hold[0];
-
-#ifdef DEBUG
- fprintf(stderr, "argv[%d] = {", context->argc);
- for (auto str : context->argv_hold) {
- fprintf(stderr, " \"%s\"", str ?: "nullptr");
- }
- fprintf(stderr, " }\n");
- fflush(stderr);
-#endif
- context->retval = EXIT_SUCCESS;
- if (pthread_create(&context->thr, &attr,
- (void*(*)(void*))__logcat, context)) {
- save_errno = errno;
- goto argv_exit;
- }
- pthread_attr_destroy(&attr);
-
- return context->fds[0];
-
-argv_exit:
- context->argv_hold.clear();
- context->args.clear();
- context->envp_hold.clear();
- context->envs.clear();
-pthread_attr_exit:
- pthread_attr_destroy(&attr);
-close_exit:
- close(context->fds[0]);
- context->fds[0] = -1;
- close(context->fds[1]);
- context->fds[1] = -1;
-exit:
- errno = save_errno;
- context->stop = true;
- context->thread_stopped = true;
- context->retval = EXIT_FAILURE;
- return -1;
-}
-
-// test if the thread is still doing 'stuff'
-int android_logcat_run_command_thread_running(android_logcat_context ctx) {
- android_logcat_context_internal* context = ctx;
-
- return context->thread_stopped == false;
-}
-
// Finished with context
int android_logcat_destroy(android_logcat_context* ctx) {
android_logcat_context_internal* context = *ctx;
diff --git a/logcat/logcat.h b/logcat/logcat.h
new file mode 100644
index 0000000..85ed7da
--- /dev/null
+++ b/logcat/logcat.h
@@ -0,0 +1,60 @@
+/*
+ * Copyright (C) 2005-2017 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.
+ */
+
+#pragma once
+
+#include <stdio.h>
+
+/*
+ * The opaque context
+ */
+typedef struct android_logcat_context_internal* android_logcat_context;
+
+/* Creates a context associated with this logcat instance
+ *
+ * Returns a pointer to the context, or a NULL on error.
+ */
+android_logcat_context create_android_logcat();
+
+/* Collects and outputs the logcat data to output and error file descriptors
+ *
+ * Will block, performed in-thread and in-process
+ *
+ * The output file descriptor variable, if greater than or equal to 0, is
+ * where the output (ie: stdout) will be sent. The file descriptor is closed
+ * on android_logcat_destroy which terminates the instance, or when an -f flag
+ * (output redirect to a file) is present in the command. The error file
+ * descriptor variable, if greater than or equal to 0, is where the error
+ * stream (ie: stderr) will be sent, also closed on android_logcat_destroy.
+ * The error file descriptor can be set to equal to the output file descriptor,
+ * which will mix output and error stream content, and will defer closure of
+ * the file descriptor on -f flag redirection. Negative values for the file
+ * descriptors will use stdout and stderr FILE references respectively
+ * internally, and will not close the references as noted above.
+ *
+ * Return value is 0 for success, non-zero for errors.
+ */
+int android_logcat_run_command(android_logcat_context ctx, int output, int error, int argc,
+ char* const* argv, char* const* envp);
+
+/* Finished with context
+ *
+ * Kill the command thread ASAP (if any), and free up all associated resources.
+ *
+ * Return value is the result of the android_logcat_run_command, or
+ * non-zero for any errors.
+ */
+int android_logcat_destroy(android_logcat_context* ctx);
diff --git a/logcat/logcat_main.cpp b/logcat/logcat_main.cpp
index 9477e79..ecfa2ba 100644
--- a/logcat/logcat_main.cpp
+++ b/logcat/logcat_main.cpp
@@ -17,7 +17,7 @@
#include <signal.h>
#include <stdlib.h>
-#include <log/logcat.h>
+#include "logcat.h"
int main(int argc, char** argv, char** envp) {
android_logcat_context ctx = create_android_logcat();
diff --git a/logcat/logcat_system.cpp b/logcat/logcat_system.cpp
deleted file mode 100644
index 6dfd110..0000000
--- a/logcat/logcat_system.cpp
+++ /dev/null
@@ -1,153 +0,0 @@
-/*
- * Copyright (C) 2017 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 <ctype.h>
-#include <fcntl.h>
-#include <stdio.h>
-#include <string.h>
-#include <unistd.h>
-
-#include <string>
-#include <vector>
-
-#include <log/logcat.h>
-
-static std::string unquote(const char*& cp, const char*& delim) {
- if ((*cp == '\'') || (*cp == '"')) {
- // KISS: Simple quotes. Do not handle the case
- // of concatenation like "blah"foo'bar'
- char quote = *cp++;
- delim = strchr(cp, quote);
- if (!delim) delim = cp + strlen(cp);
- std::string str(cp, delim);
- if (*delim) ++delim;
- return str;
- }
- delim = strpbrk(cp, " \t\f\r\n");
- if (!delim) delim = cp + strlen(cp);
- return std::string(cp, delim);
-}
-
-static bool __android_logcat_parse(const char* command,
- std::vector<std::string>& args,
- std::vector<std::string>& envs) {
- for (const char *delim, *cp = command; cp && *cp; cp = delim) {
- while (isspace(*cp)) ++cp;
- if ((args.size() == 0) && (*cp != '=') && !isdigit(*cp)) {
- const char* env = cp;
- while (isalnum(*cp) || (*cp == '_')) ++cp;
- if (cp && (*cp == '=')) {
- std::string str(env, ++cp);
- str += unquote(cp, delim);
- envs.push_back(str);
- continue;
- }
- cp = env;
- }
- args.push_back(unquote(cp, delim));
- if ((args.size() == 1) && (args[0] != "logcat") &&
- (args[0] != "/system/bin/logcat")) {
- return false;
- }
- }
- return args.size() != 0;
-}
-
-FILE* android_logcat_popen(android_logcat_context* ctx, const char* command) {
- *ctx = NULL;
-
- std::vector<std::string> args;
- std::vector<std::string> envs;
- if (!__android_logcat_parse(command, args, envs)) return NULL;
-
- std::vector<const char*> argv;
- for (auto& str : args) {
- argv.push_back(str.c_str());
- }
- argv.push_back(NULL);
-
- std::vector<const char*> envp;
- for (auto& str : envs) {
- envp.push_back(str.c_str());
- }
- envp.push_back(NULL);
-
- *ctx = create_android_logcat();
- if (!*ctx) return NULL;
-
- int fd = android_logcat_run_command_thread(
- *ctx, argv.size() - 1, (char* const*)&argv[0], (char* const*)&envp[0]);
- argv.clear();
- args.clear();
- envp.clear();
- envs.clear();
- if (fd < 0) {
- android_logcat_destroy(ctx);
- return NULL;
- }
-
- int duped = dup(fd);
- FILE* retval = fdopen(duped, "reb");
- if (!retval) {
- close(duped);
- android_logcat_destroy(ctx);
- }
- return retval;
-}
-
-int android_logcat_pclose(android_logcat_context* ctx, FILE* output) {
- if (*ctx) {
- static const useconds_t wait_sample = 20000;
- // Wait two seconds maximum
- for (size_t retry = ((2 * 1000000) + wait_sample - 1) / wait_sample;
- android_logcat_run_command_thread_running(*ctx) && retry; --retry) {
- usleep(wait_sample);
- }
- }
-
- if (output) fclose(output);
- return android_logcat_destroy(ctx);
-}
-
-int android_logcat_system(const char* command) {
- std::vector<std::string> args;
- std::vector<std::string> envs;
- if (!__android_logcat_parse(command, args, envs)) return -1;
-
- std::vector<const char*> argv;
- for (auto& str : args) {
- argv.push_back(str.c_str());
- }
- argv.push_back(NULL);
-
- std::vector<const char*> envp;
- for (auto& str : envs) {
- envp.push_back(str.c_str());
- }
- envp.push_back(NULL);
-
- android_logcat_context ctx = create_android_logcat();
- if (!ctx) return -1;
- /* Command return value */
- int retval = android_logcat_run_command(ctx, -1, -1, argv.size() - 1,
- (char* const*)&argv[0],
- (char* const*)&envp[0]);
- /* destroy return value */
- int ret = android_logcat_destroy(&ctx);
- /* Paranoia merging any discrepancies between the two return values */
- if (!ret) ret = retval;
- return ret;
-}
diff --git a/logcat/logcatd_main.cpp b/logcat/logcatd_main.cpp
index 9109eb1..c131846 100644
--- a/logcat/logcatd_main.cpp
+++ b/logcat/logcatd_main.cpp
@@ -21,7 +21,7 @@
#include <string>
#include <vector>
-#include <log/logcat.h>
+#include "logcat.h"
int main(int argc, char** argv, char** envp) {
android_logcat_context ctx = create_android_logcat();
diff --git a/logcat/tests/Android.mk b/logcat/tests/Android.mk
index defd3c4..66f6724 100644
--- a/logcat/tests/Android.mk
+++ b/logcat/tests/Android.mk
@@ -32,7 +32,6 @@
benchmark_src_files := \
logcat_benchmark.cpp \
- exec_benchmark.cpp \
# Build benchmarks for the device. Run with:
# adb shell /data/nativetest/logcat-benchmarks/logcat-benchmarks
@@ -41,7 +40,7 @@
LOCAL_MODULE_TAGS := $(test_tags)
LOCAL_CFLAGS += $(test_c_flags)
LOCAL_SRC_FILES := $(benchmark_src_files)
-LOCAL_SHARED_LIBRARIES := libbase liblogcat
+LOCAL_SHARED_LIBRARIES := libbase
include $(BUILD_NATIVE_BENCHMARK)
# -----------------------------------------------------------------------------
@@ -51,7 +50,6 @@
test_src_files := \
logcat_test.cpp \
logcatd_test.cpp \
- liblogcat_test.cpp \
# Build tests for the device (with .so). Run with:
# adb shell /data/nativetest/logcat-unit-tests/logcat-unit-tests
@@ -59,6 +57,6 @@
LOCAL_MODULE := $(test_module_prefix)unit-tests
LOCAL_MODULE_TAGS := $(test_tags)
LOCAL_CFLAGS += $(test_c_flags)
-LOCAL_SHARED_LIBRARIES := liblog libbase liblogcat
+LOCAL_SHARED_LIBRARIES := liblog libbase
LOCAL_SRC_FILES := $(test_src_files)
include $(BUILD_NATIVE_TEST)
diff --git a/logcat/tests/exec_benchmark.cpp b/logcat/tests/exec_benchmark.cpp
deleted file mode 100644
index c30a5f5..0000000
--- a/logcat/tests/exec_benchmark.cpp
+++ /dev/null
@@ -1,96 +0,0 @@
-/*
- * Copyright (C) 2017 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 <stdio.h>
-
-#include <android-base/file.h>
-#include <benchmark/benchmark.h>
-#include <log/logcat.h>
-
-// Dump the statistics and report results
-
-static void logcat_popen_libc(benchmark::State& state, const char* cmd) {
- while (state.KeepRunning()) {
- FILE* fp = popen(cmd, "r");
- std::string ret;
- android::base::ReadFdToString(fileno(fp), &ret);
- pclose(fp);
- }
-}
-
-static void BM_logcat_stat_popen_libc(benchmark::State& state) {
- logcat_popen_libc(state, "logcat -b all -S");
-}
-BENCHMARK(BM_logcat_stat_popen_libc);
-
-static void logcat_popen_liblogcat(benchmark::State& state, const char* cmd) {
- while (state.KeepRunning()) {
- android_logcat_context ctx;
- FILE* fp = android_logcat_popen(&ctx, cmd);
- std::string ret;
- android::base::ReadFdToString(fileno(fp), &ret);
- android_logcat_pclose(&ctx, fp);
- }
-}
-
-static void BM_logcat_stat_popen_liblogcat(benchmark::State& state) {
- logcat_popen_liblogcat(state, "logcat -b all -S");
-}
-BENCHMARK(BM_logcat_stat_popen_liblogcat);
-
-static void logcat_system_libc(benchmark::State& state, const char* cmd) {
- while (state.KeepRunning()) {
- system(cmd);
- }
-}
-
-static void BM_logcat_stat_system_libc(benchmark::State& state) {
- logcat_system_libc(state, "logcat -b all -S >/dev/null 2>/dev/null");
-}
-BENCHMARK(BM_logcat_stat_system_libc);
-
-static void logcat_system_liblogcat(benchmark::State& state, const char* cmd) {
- while (state.KeepRunning()) {
- android_logcat_system(cmd);
- }
-}
-
-static void BM_logcat_stat_system_liblogcat(benchmark::State& state) {
- logcat_system_liblogcat(state, "logcat -b all -S >/dev/null 2>/dev/null");
-}
-BENCHMARK(BM_logcat_stat_system_liblogcat);
-
-// Dump the logs and report results
-
-static void BM_logcat_dump_popen_libc(benchmark::State& state) {
- logcat_popen_libc(state, "logcat -b all -d");
-}
-BENCHMARK(BM_logcat_dump_popen_libc);
-
-static void BM_logcat_dump_popen_liblogcat(benchmark::State& state) {
- logcat_popen_liblogcat(state, "logcat -b all -d");
-}
-BENCHMARK(BM_logcat_dump_popen_liblogcat);
-
-static void BM_logcat_dump_system_libc(benchmark::State& state) {
- logcat_system_libc(state, "logcat -b all -d >/dev/null 2>/dev/null");
-}
-BENCHMARK(BM_logcat_dump_system_libc);
-
-static void BM_logcat_dump_system_liblogcat(benchmark::State& state) {
- logcat_system_liblogcat(state, "logcat -b all -d >/dev/null 2>/dev/null");
-}
-BENCHMARK(BM_logcat_dump_system_liblogcat);
diff --git a/logcat/tests/liblogcat_test.cpp b/logcat/tests/liblogcat_test.cpp
deleted file mode 100644
index c8a00da..0000000
--- a/logcat/tests/liblogcat_test.cpp
+++ /dev/null
@@ -1,25 +0,0 @@
-/*
- * Copyright (C) 2017 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 <log/logcat.h>
-
-#define logcat_define(context) android_logcat_context context
-#define logcat_popen(context, command) android_logcat_popen(&(context), command)
-#define logcat_pclose(context, fp) android_logcat_pclose(&(context), fp)
-#define logcat_system(command) android_logcat_system(command)
-#define logcat liblogcat
-
-#include "logcat_test.cpp"
diff --git a/logcat/tests/logcat_test.cpp b/logcat/tests/logcat_test.cpp
index 786fb14..cc1632a 100644
--- a/logcat/tests/logcat_test.cpp
+++ b/logcat/tests/logcat_test.cpp
@@ -37,12 +37,6 @@
#include <log/log.h>
#include <log/log_event_list.h>
-#ifndef logcat_popen
-#define logcat_define(context)
-#define logcat_popen(context, command) popen((command), "r")
-#define logcat_pclose(context, fp) pclose(fp)
-#define logcat_system(command) system(command)
-#endif
#ifndef logcat_executable
#define USING_LOGCAT_EXECUTABLE_DEFAULT
#define logcat_executable "logcat"
@@ -78,7 +72,6 @@
TEST(logcat, buckets) {
FILE* fp;
- logcat_define(ctx);
#undef LOG_TAG
#define LOG_TAG "inject.buckets"
@@ -90,10 +83,9 @@
__android_log_bswrite(0, logcat_executable ".inject.buckets");
rest();
- ASSERT_TRUE(NULL !=
- (fp = logcat_popen(
- ctx, logcat_executable
- " -b radio -b events -b system -b main -d 2>/dev/null")));
+ ASSERT_TRUE(NULL != (fp = popen(logcat_executable
+ " -b radio -b events -b system -b main -d 2>/dev/null",
+ "r")));
char buffer[BIG_BUFFER];
@@ -111,7 +103,7 @@
}
}
- logcat_pclose(ctx, fp);
+ pclose(fp);
EXPECT_EQ(ids, 15);
@@ -120,7 +112,6 @@
TEST(logcat, event_tag_filter) {
FILE* fp;
- logcat_define(ctx);
#undef LOG_TAG
#define LOG_TAG "inject.filter"
@@ -135,7 +126,7 @@
logcat_executable
" -b radio -b system -b main --pid=%d -d -s inject.filter 2>/dev/null",
getpid());
- ASSERT_TRUE(NULL != (fp = logcat_popen(ctx, command.c_str())));
+ ASSERT_TRUE(NULL != (fp = popen(command.c_str(), "r")));
char buffer[BIG_BUFFER];
@@ -145,7 +136,7 @@
if (strncmp(begin, buffer, sizeof(begin) - 1)) ++count;
}
- logcat_pclose(ctx, fp);
+ pclose(fp);
// logcat, liblogcat and logcatd test instances result in the progression
// of 3, 6 and 9 for our counts as each round is performed.
@@ -191,7 +182,6 @@
do {
FILE* fp;
- logcat_define(ctx);
char needle[32];
time_t now;
@@ -205,9 +195,8 @@
#endif
strftime(needle, sizeof(needle), "[ %Y-", ptm);
- ASSERT_TRUE(NULL != (fp = logcat_popen(
- ctx, logcat_executable
- " -v long -v year -b all -t 3 2>/dev/null")));
+ ASSERT_TRUE(NULL !=
+ (fp = popen(logcat_executable " -v long -v year -b all -t 3 2>/dev/null", "r")));
char buffer[BIG_BUFFER];
@@ -218,7 +207,7 @@
++count;
}
}
- logcat_pclose(ctx, fp);
+ pclose(fp);
} while ((count < 3) && --tries && inject(3 - count));
@@ -268,12 +257,10 @@
do {
FILE* fp;
- logcat_define(ctx);
- ASSERT_TRUE(NULL !=
- (fp = logcat_popen(ctx, logcat_executable
- " -v long -v America/Los_Angeles "
- "-b all -t 3 2>/dev/null")));
+ ASSERT_TRUE(NULL != (fp = popen(logcat_executable
+ " -v long -v America/Los_Angeles -b all -t 3 2>/dev/null",
+ "r")));
char buffer[BIG_BUFFER];
@@ -287,7 +274,7 @@
}
}
- logcat_pclose(ctx, fp);
+ pclose(fp);
} while ((count < 3) && --tries && inject(3 - count));
@@ -296,11 +283,11 @@
TEST(logcat, ntz) {
FILE* fp;
- logcat_define(ctx);
- ASSERT_TRUE(NULL != (fp = logcat_popen(ctx, logcat_executable
- " -v long -v America/Los_Angeles -v "
- "zone -b all -t 3 2>/dev/null")));
+ ASSERT_TRUE(NULL !=
+ (fp = popen(logcat_executable
+ " -v long -v America/Los_Angeles -v zone -b all -t 3 2>/dev/null",
+ "r")));
char buffer[BIG_BUFFER];
@@ -312,7 +299,7 @@
}
}
- logcat_pclose(ctx, fp);
+ pclose(fp);
ASSERT_EQ(0, count);
}
@@ -330,8 +317,7 @@
"ANDROID_PRINTF_LOG=long logcat -b all -t %d 2>/dev/null", num);
FILE* fp;
- logcat_define(ctx);
- ASSERT_TRUE(NULL != (fp = logcat_popen(ctx, buffer)));
+ ASSERT_TRUE(NULL != (fp = popen(buffer, "r")));
count = 0;
@@ -339,7 +325,7 @@
++count;
}
- logcat_pclose(ctx, fp);
+ pclose(fp);
} while ((count < num) && --tries && inject(num - count));
@@ -377,8 +363,7 @@
do {
snprintf(buffer, sizeof(buffer), "%s -t 10 2>&1", cmd);
- logcat_define(ctx);
- ASSERT_TRUE(NULL != (fp = logcat_popen(ctx, buffer)));
+ ASSERT_TRUE(NULL != (fp = popen(buffer, "r")));
count = 0;
while ((input = fgetLongTime(buffer, sizeof(buffer), fp))) {
@@ -391,7 +376,7 @@
free(last_timestamp);
last_timestamp = strdup(input);
}
- logcat_pclose(ctx, fp);
+ pclose(fp);
} while ((count < 10) && --tries && inject(10 - count));
@@ -401,8 +386,7 @@
EXPECT_TRUE(second_timestamp != NULL);
snprintf(buffer, sizeof(buffer), "%s -t '%s' 2>&1", cmd, first_timestamp);
- logcat_define(ctx);
- ASSERT_TRUE(NULL != (fp = logcat_popen(ctx, buffer)));
+ ASSERT_TRUE(NULL != (fp = popen(buffer, "r")));
int second_count = 0;
int last_timestamp_count = -1;
@@ -442,7 +426,7 @@
last_timestamp_count = second_count;
}
}
- logcat_pclose(ctx, fp);
+ pclose(fp);
EXPECT_TRUE(found);
if (!found) {
@@ -483,10 +467,8 @@
ASSERT_LT(0, __android_log_btwrite(0, EVENT_TYPE_LONG, &ts, sizeof(ts)));
FILE* fp;
- logcat_define(ctx);
ASSERT_TRUE(NULL !=
- (fp = logcat_popen(ctx, logcat_executable
- " -v brief -b events -t 100 2>/dev/null")));
+ (fp = popen(logcat_executable " -v brief -b events -t 100 2>/dev/null", "r")));
char buffer[BIG_BUFFER];
@@ -507,7 +489,7 @@
}
}
- logcat_pclose(ctx, fp);
+ pclose(fp);
ASSERT_EQ(1, count);
}
@@ -521,12 +503,9 @@
FILE* fp[256]; // does this count as a multitude!
memset(fp, 0, sizeof(fp));
- logcat_define(ctx[sizeof(fp) / sizeof(fp[0])]);
size_t num = 0;
do {
- EXPECT_TRUE(NULL !=
- (fp[num] = logcat_popen(ctx[num], logcat_executable
- " -v brief -b events -t 100")));
+ EXPECT_TRUE(NULL != (fp[num] = popen(logcat_executable " -v brief -b events -t 100", "r")));
if (!fp[num]) {
fprintf(stderr,
"WARNING: limiting to %zu simultaneous logcat operations\n",
@@ -556,7 +535,7 @@
}
}
- logcat_pclose(ctx[idx], fp[idx]);
+ pclose(fp[idx]);
}
ASSERT_EQ(num, count);
@@ -564,10 +543,9 @@
static int get_groups(const char* cmd) {
FILE* fp;
- logcat_define(ctx);
// NB: crash log only available in user space
- EXPECT_TRUE(NULL != (fp = logcat_popen(ctx, cmd)));
+ EXPECT_TRUE(NULL != (fp = popen(cmd, "r")));
if (fp == NULL) {
return 0;
@@ -631,7 +609,7 @@
}
}
- logcat_pclose(ctx, fp);
+ pclose(fp);
return count;
}
@@ -815,7 +793,7 @@
snprintf(command, sizeof(command), comm, buf);
int ret;
- EXPECT_FALSE(IsFalse(ret = logcat_system(command), command));
+ EXPECT_FALSE(IsFalse(ret = system(command), command));
if (!ret) {
snprintf(command, sizeof(command), "ls -s %s 2>/dev/null", buf);
@@ -861,7 +839,7 @@
snprintf(command, sizeof(command), logcat_cmd, tmp_out_dir);
int ret;
- EXPECT_FALSE(IsFalse(ret = logcat_system(command), command));
+ EXPECT_FALSE(IsFalse(ret = system(command), command));
if (!ret) {
snprintf(command, sizeof(command), "ls %s 2>/dev/null", tmp_out_dir);
@@ -920,7 +898,7 @@
snprintf(command, sizeof(command), logcat_cmd, tmp_out_dir, log_filename);
int ret;
- EXPECT_FALSE(IsFalse(ret = logcat_system(command), command));
+ EXPECT_FALSE(IsFalse(ret = system(command), command));
if (ret) {
snprintf(command, sizeof(command), cleanup_cmd, tmp_out_dir);
EXPECT_FALSE(IsFalse(system(command), command));
@@ -969,7 +947,7 @@
// re-run the command, it should only add a few lines more content if it
// continues where it left off.
snprintf(command, sizeof(command), logcat_cmd, tmp_out_dir, log_filename);
- EXPECT_FALSE(IsFalse(ret = logcat_system(command), command));
+ EXPECT_FALSE(IsFalse(ret = system(command), command));
if (ret) {
snprintf(command, sizeof(command), cleanup_cmd, tmp_out_dir);
EXPECT_FALSE(IsFalse(system(command), command));
@@ -1052,7 +1030,7 @@
tmp_out_dir, log_filename, num_val);
int ret;
- EXPECT_FALSE(IsFalse(ret = logcat_system(command), command));
+ EXPECT_FALSE(IsFalse(ret = system(command), command));
if (ret) {
snprintf(command, sizeof(command), cleanup_cmd, tmp_out_dir);
EXPECT_FALSE(IsFalse(system(command), command));
@@ -1082,7 +1060,7 @@
strcat(command, clear_cmd);
int ret;
- EXPECT_FALSE(IsFalse(ret = logcat_system(command), command));
+ EXPECT_FALSE(IsFalse(ret = system(command), command));
if (ret) {
snprintf(command, sizeof(command), cleanup_cmd, tmp_out_dir);
EXPECT_FALSE(system(command));
@@ -1120,7 +1098,7 @@
snprintf(command, sizeof(command), logcat_cmd, tmp_out_dir, log_filename);
- int ret = logcat_system(command);
+ int ret = system(command);
if (ret) {
fprintf(stderr, "system(\"%s\")=%d", command, ret);
return -1;
@@ -1194,7 +1172,7 @@
" -b all -d"
" -f /das/nein/gerfingerpoken/logcat/log.txt"
" -n 256 -r 1024";
- EXPECT_FALSE(IsFalse(0 == logcat_system(command), command));
+ EXPECT_FALSE(IsFalse(0 == system(command), command));
}
#ifndef logcat
@@ -1329,10 +1307,7 @@
#endif
static bool get_white_black(char** list) {
- FILE* fp;
- logcat_define(ctx);
-
- fp = logcat_popen(ctx, logcat_executable " -p 2>/dev/null");
+ FILE* fp = popen(logcat_executable " -p 2>/dev/null", "r");
if (fp == NULL) {
fprintf(stderr, "ERROR: logcat -p 2>/dev/null\n");
return false;
@@ -1360,19 +1335,15 @@
asprintf(list, "%s", buf);
}
}
- logcat_pclose(ctx, fp);
+ pclose(fp);
return *list != NULL;
}
static bool set_white_black(const char* list) {
- FILE* fp;
- logcat_define(ctx);
-
char buffer[BIG_BUFFER];
-
snprintf(buffer, sizeof(buffer), logcat_executable " -P '%s' 2>&1",
list ? list : "");
- fp = logcat_popen(ctx, buffer);
+ FILE* fp = popen(buffer, "r");
if (fp == NULL) {
fprintf(stderr, "ERROR: %s\n", buffer);
return false;
@@ -1391,10 +1362,10 @@
continue;
}
fprintf(stderr, "%s\n", buf);
- logcat_pclose(ctx, fp);
+ pclose(fp);
return false;
}
- return logcat_pclose(ctx, fp) == 0;
+ return pclose(fp) == 0;
}
TEST(logcat, white_black_adjust) {
@@ -1429,7 +1400,6 @@
TEST(logcat, regex) {
FILE* fp;
- logcat_define(ctx);
int count = 0;
char buffer[BIG_BUFFER];
@@ -1450,7 +1420,7 @@
// Let the logs settle
rest();
- ASSERT_TRUE(NULL != (fp = logcat_popen(ctx, buffer)));
+ ASSERT_TRUE(NULL != (fp = popen(buffer, "r")));
while (fgets(buffer, sizeof(buffer), fp)) {
if (!strncmp(begin, buffer, sizeof(begin) - 1)) {
@@ -1462,14 +1432,13 @@
count++;
}
- logcat_pclose(ctx, fp);
+ pclose(fp);
ASSERT_EQ(2, count);
}
TEST(logcat, maxcount) {
FILE* fp;
- logcat_define(ctx);
int count = 0;
char buffer[BIG_BUFFER];
@@ -1488,7 +1457,7 @@
rest();
- ASSERT_TRUE(NULL != (fp = logcat_popen(ctx, buffer)));
+ ASSERT_TRUE(NULL != (fp = popen(buffer, "r")));
while (fgets(buffer, sizeof(buffer), fp)) {
if (!strncmp(begin, buffer, sizeof(begin) - 1)) {
@@ -1498,7 +1467,7 @@
count++;
}
- logcat_pclose(ctx, fp);
+ pclose(fp);
ASSERT_EQ(3, count);
}
@@ -1510,13 +1479,7 @@
;
static bool End_to_End(const char* tag, const char* fmt, ...) {
- logcat_define(ctx);
- FILE* fp = logcat_popen(ctx, logcat_executable
- " -v brief"
- " -b events"
- " -v descriptive"
- " -t 100"
- " 2>/dev/null");
+ FILE* fp = popen(logcat_executable " -v brief -b events -v descriptive -t 100 2>/dev/null", "r");
if (!fp) {
fprintf(stderr, "End_to_End: popen failed");
return false;
@@ -1551,7 +1514,7 @@
}
}
- logcat_pclose(ctx, fp);
+ pclose(fp);
if ((count == 0) && (lastMatch.length() > 0)) {
// Help us pinpoint where things went wrong ...
@@ -1741,13 +1704,12 @@
}
static bool reportedSecurity(const char* command) {
- logcat_define(ctx);
- FILE* fp = logcat_popen(ctx, command);
+ FILE* fp = popen(command, "r");
if (!fp) return true;
std::string ret;
bool val = android::base::ReadFdToString(fileno(fp), &ret);
- logcat_pclose(ctx, fp);
+ pclose(fp);
if (!val) return true;
return std::string::npos != ret.find("'security'");
@@ -1762,13 +1724,12 @@
}
static size_t commandOutputSize(const char* command) {
- logcat_define(ctx);
- FILE* fp = logcat_popen(ctx, command);
+ FILE* fp = popen(command, "r");
if (!fp) return 0;
std::string ret;
if (!android::base::ReadFdToString(fileno(fp), &ret)) return 0;
- if (logcat_pclose(ctx, fp) != 0) return 0;
+ if (pclose(fp) != 0) return 0;
return ret.size();
}
diff --git a/rootdir/Android.mk b/rootdir/Android.mk
index 70b6a3e..478b8d0 100644
--- a/rootdir/Android.mk
+++ b/rootdir/Android.mk
@@ -201,6 +201,7 @@
$$(hide) sed -i -e 's?%VNDK_CORE_LIBRARIES%?$$(PRIVATE_VNDK_CORE_LIBRARIES)?g' $$@
$$(hide) sed -i -e 's?%SANITIZER_RUNTIME_LIBRARIES%?$$(PRIVATE_SANITIZER_RUNTIME_LIBRARIES)?g' $$@
$$(hide) sed -i -e 's?%VNDK_VER%?$$(PRIVATE_VNDK_VERSION)?g' $$@
+ $$(hide) sed -i -e 's?%PRODUCT%?$$(TARGET_COPY_OUT_PRODUCT)?g' $$@
llndk_libraries_list :=
vndksp_libraries_list :=
diff --git a/rootdir/etc/ld.config.txt b/rootdir/etc/ld.config.txt
index a0b1996..42dc7ab 100644
--- a/rootdir/etc/ld.config.txt
+++ b/rootdir/etc/ld.config.txt
@@ -7,7 +7,7 @@
# absolute path of an executable is selected.
dir.system = /system/bin/
dir.system = /system/xbin/
-dir.system = /product/bin/
+dir.system = /%PRODUCT%/bin/
dir.vendor = /odm/bin/
dir.vendor = /vendor/bin/
@@ -39,7 +39,7 @@
namespace.default.isolated = true
namespace.default.search.paths = /system/${LIB}
-namespace.default.search.paths += /product/${LIB}
+namespace.default.search.paths += /%PRODUCT%/${LIB}
# We can't have entire /system/${LIB} as permitted paths because doing so
# makes it possible to load libs in /system/${LIB}/vndk* directories by
@@ -51,7 +51,7 @@
namespace.default.permitted.paths = /system/${LIB}/drm
namespace.default.permitted.paths += /system/${LIB}/extractors
namespace.default.permitted.paths += /system/${LIB}/hw
-namespace.default.permitted.paths += /product/${LIB}
+namespace.default.permitted.paths += /%PRODUCT%/${LIB}
# These are where odex files are located. libart has to be able to dlopen the files
namespace.default.permitted.paths += /system/framework
namespace.default.permitted.paths += /system/app
@@ -63,9 +63,9 @@
namespace.default.permitted.paths += /odm/app
namespace.default.permitted.paths += /odm/priv-app
namespace.default.permitted.paths += /oem/app
-namespace.default.permitted.paths += /product/framework
-namespace.default.permitted.paths += /product/app
-namespace.default.permitted.paths += /product/priv-app
+namespace.default.permitted.paths += /%PRODUCT%/framework
+namespace.default.permitted.paths += /%PRODUCT%/app
+namespace.default.permitted.paths += /%PRODUCT%/priv-app
namespace.default.permitted.paths += /data
namespace.default.permitted.paths += /mnt/expand
@@ -88,10 +88,10 @@
namespace.default.asan.permitted.paths += /odm/app
namespace.default.asan.permitted.paths += /odm/priv-app
namespace.default.asan.permitted.paths += /oem/app
-namespace.default.asan.permitted.paths += /product/${LIB}
-namespace.default.asan.permitted.paths += /product/framework
-namespace.default.asan.permitted.paths += /product/app
-namespace.default.asan.permitted.paths += /product/priv-app
+namespace.default.asan.permitted.paths += /%PRODUCT%/${LIB}
+namespace.default.asan.permitted.paths += /%PRODUCT%/framework
+namespace.default.asan.permitted.paths += /%PRODUCT%/app
+namespace.default.asan.permitted.paths += /%PRODUCT%/priv-app
namespace.default.asan.permitted.paths += /mnt/expand
###############################################################################
@@ -327,7 +327,7 @@
namespace.system.isolated = false
namespace.system.search.paths = /system/${LIB}
-namespace.system.search.paths += /product/${LIB}
+namespace.system.search.paths += /%PRODUCT%/${LIB}
namespace.system.asan.search.paths = /data/asan/system/${LIB}
namespace.system.asan.search.paths += /system/${LIB}
@@ -345,4 +345,4 @@
[postinstall]
namespace.default.isolated = false
namespace.default.search.paths = /system/${LIB}
-namespace.default.search.paths += /product/${LIB}
+namespace.default.search.paths += /%PRODUCT%/${LIB}
diff --git a/shell_and_utilities/Android.bp b/shell_and_utilities/Android.bp
index 6438e3d..7834dd5 100644
--- a/shell_and_utilities/Android.bp
+++ b/shell_and_utilities/Android.bp
@@ -1,24 +1,46 @@
phony {
name: "shell_and_utilities",
required: [
+ "shell_and_utilities_system",
+ "shell_and_utilities_recovery",
+ "shell_and_utilities_vendor",
+ ],
+}
+
+phony {
+ name: "shell_and_utilities_system",
+ required: [
"awk",
- "awk_vendor",
"bzip2",
"grep",
- "grep_vendor",
"logwrapper",
- "logwrapper_vendor",
"mkshrc",
- "mkshrc_vendor",
+ "newfs_msdos",
"reboot",
"sh",
- "sh.recovery",
- "sh_vendor",
"toolbox",
- "toolbox.recovery",
- "toolbox_vendor",
"toybox",
+ ],
+}
+
+phony {
+ name: "shell_and_utilities_recovery",
+ required: [
+ "sh.recovery",
+ "toolbox.recovery",
"toybox.recovery",
+ ],
+}
+
+phony {
+ name: "shell_and_utilities_vendor",
+ required: [
+ "awk_vendor",
+ "grep_vendor",
+ "logwrapper_vendor",
+ "mkshrc_vendor",
+ "sh_vendor",
+ "toolbox_vendor",
"toybox_vendor",
],
}
diff --git a/shell_and_utilities/README.md b/shell_and_utilities/README.md
index b15be1f..e310e6b 100644
--- a/shell_and_utilities/README.md
+++ b/shell_and_utilities/README.md
@@ -193,13 +193,15 @@
Android Q
---------
+BSD: fsck\_msdos newfs\_msdos
+
bzip2: bzcat bzip2 bunzip2
one-true-awk: awk
PCRE: egrep fgrep grep
-toolbox: getevent getprop newfs\_msdos
+toolbox: getevent getprop
toybox: acpi base64 basename blockdev cal cat chcon chgrp chmod chown
chroot chrt cksum clear cmp comm cp cpio cut date dd df diff dirname
diff --git a/toolbox/Android.bp b/toolbox/Android.bp
index f12c810..fc51705 100644
--- a/toolbox/Android.bp
+++ b/toolbox/Android.bp
@@ -26,7 +26,6 @@
"toolbox.c",
"getevent.c",
"getprop.cpp",
- "newfs_msdos.c",
],
generated_headers: [
"toolbox_input_labels",
@@ -39,7 +38,6 @@
symlinks: [
"getevent",
"getprop",
- "newfs_msdos",
],
}
@@ -62,10 +60,3 @@
defaults: ["toolbox_defaults"],
srcs: ["r.c"],
}
-
-cc_binary_host {
- name: "newfs_msdos",
- defaults: ["toolbox_defaults"],
- srcs: ["newfs_msdos.c"],
- cflags: ["-Dnewfs_msdos_main=main"]
-}
diff --git a/toolbox/MODULE_LICENSE_BSD b/toolbox/MODULE_LICENSE_APACHE2
similarity index 100%
rename from toolbox/MODULE_LICENSE_BSD
rename to toolbox/MODULE_LICENSE_APACHE2
diff --git a/toolbox/NOTICE b/toolbox/NOTICE
index e9ab58d..8e8a91c 100644
--- a/toolbox/NOTICE
+++ b/toolbox/NOTICE
@@ -1,4 +1,4 @@
-Copyright (C) 2010 The Android Open Source Project
+Copyright (C) 2017 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.
@@ -14,974 +14,3 @@
-------------------------------------------------------------------
-Copyright (C) 2014, The Android Open Source Project
-All rights reserved.
-
-Redistribution and use in source and binary forms, with or without
-modification, are permitted provided that the following conditions
-are met:
- * Redistributions of source code must retain the above copyright
- notice, this list of conditions and the following disclaimer.
- * Redistributions in binary form must reproduce the above copyright
- notice, this list of conditions and the following disclaimer in
- the documentation and/or other materials provided with the
- distribution.
-
-THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
-"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
-LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
-FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
-COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
-INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
-BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
-OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
-AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
-OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
-OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
-SUCH DAMAGE.
-
--------------------------------------------------------------------
-
-Copyright (c) 1987, 1993
- The Regents of the University of California. All rights reserved.
-
-Redistribution and use in source and binary forms, with or without
-modification, are permitted provided that the following conditions
-are met:
-1. Redistributions of source code must retain the above copyright
- notice, this list of conditions and the following disclaimer.
-2. Redistributions in binary form must reproduce the above copyright
- notice, this list of conditions and the following disclaimer in the
- documentation and/or other materials provided with the distribution.
-3. Neither the name of the University nor the names of its contributors
- may be used to endorse or promote products derived from this software
- without specific prior written permission.
-
-THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
-ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
-IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
-ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
-FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
-DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
-OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
-HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
-LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
-OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
-SUCH DAMAGE.
-
--------------------------------------------------------------------
-
-Copyright (c) 1987, 1993, 1994
- The Regents of the University of California. All rights reserved.
-
-Redistribution and use in source and binary forms, with or without
-modification, are permitted provided that the following conditions
-are met:
-1. Redistributions of source code must retain the above copyright
- notice, this list of conditions and the following disclaimer.
-2. Redistributions in binary form must reproduce the above copyright
- notice, this list of conditions and the following disclaimer in the
- documentation and/or other materials provided with the distribution.
-3. Neither the name of the University nor the names of its contributors
- may be used to endorse or promote products derived from this software
- without specific prior written permission.
-
-THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
-ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
-IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
-ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
-FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
-DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
-OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
-HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
-LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
-OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
-SUCH DAMAGE.
-
--------------------------------------------------------------------
-
-Copyright (c) 1988, 1993
- The Regents of the University of California. All rights reserved.
-
-This code is derived from software contributed to Berkeley by
-Jeffrey Mogul.
-
-Redistribution and use in source and binary forms, with or without
-modification, are permitted provided that the following conditions
-are met:
-1. Redistributions of source code must retain the above copyright
- notice, this list of conditions and the following disclaimer.
-2. Redistributions in binary form must reproduce the above copyright
- notice, this list of conditions and the following disclaimer in the
- documentation and/or other materials provided with the distribution.
-3. Neither the name of the University nor the names of its contributors
- may be used to endorse or promote products derived from this software
- without specific prior written permission.
-
-THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
-ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
-IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
-ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
-FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
-DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
-OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
-HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
-LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
-OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
-SUCH DAMAGE.
-
--------------------------------------------------------------------
-
-Copyright (c) 1988, 1993, 1994
- The Regents of the University of California. All rights reserved.
-
-Redistribution and use in source and binary forms, with or without
-modification, are permitted provided that the following conditions
-are met:
-1. Redistributions of source code must retain the above copyright
- notice, this list of conditions and the following disclaimer.
-2. Redistributions in binary form must reproduce the above copyright
- notice, this list of conditions and the following disclaimer in the
- documentation and/or other materials provided with the distribution.
-3. Neither the name of the University nor the names of its contributors
- may be used to endorse or promote products derived from this software
- without specific prior written permission.
-
-THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
-ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
-IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
-ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
-FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
-DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
-OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
-HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
-LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
-OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
-SUCH DAMAGE.
-
--------------------------------------------------------------------
-
-Copyright (c) 1988, 1993, 1994
- The Regents of the University of California. All rights reserved.
-
-This code is derived from software contributed to Berkeley by
-David Hitz of Auspex Systems Inc.
-
-Redistribution and use in source and binary forms, with or without
-modification, are permitted provided that the following conditions
-are met:
-1. Redistributions of source code must retain the above copyright
- notice, this list of conditions and the following disclaimer.
-2. Redistributions in binary form must reproduce the above copyright
- notice, this list of conditions and the following disclaimer in the
- documentation and/or other materials provided with the distribution.
-3. Neither the name of the University nor the names of its contributors
- may be used to endorse or promote products derived from this software
- without specific prior written permission.
-
-THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
-ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
-IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
-ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
-FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
-DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
-OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
-HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
-LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
-OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
-SUCH DAMAGE.
-
--------------------------------------------------------------------
-
-Copyright (c) 1988, 1993, 1994, 2003
- The Regents of the University of California. All rights reserved.
-
-Redistribution and use in source and binary forms, with or without
-modification, are permitted provided that the following conditions
-are met:
-1. Redistributions of source code must retain the above copyright
- notice, this list of conditions and the following disclaimer.
-2. Redistributions in binary form must reproduce the above copyright
- notice, this list of conditions and the following disclaimer in the
- documentation and/or other materials provided with the distribution.
-3. Neither the name of the University nor the names of its contributors
- may be used to endorse or promote products derived from this software
- without specific prior written permission.
-
-THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
-ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
-IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
-ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
-FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
-DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
-OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
-HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
-LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
-OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
-SUCH DAMAGE.
-
--------------------------------------------------------------------
-
-Copyright (c) 1989, 1993
- The Regents of the University of California. All rights reserved.
-
-Redistribution and use in source and binary forms, with or without
-modification, are permitted provided that the following conditions
-are met:
-1. Redistributions of source code must retain the above copyright
- notice, this list of conditions and the following disclaimer.
-2. Redistributions in binary form must reproduce the above copyright
- notice, this list of conditions and the following disclaimer in the
- documentation and/or other materials provided with the distribution.
-3. Neither the name of the University nor the names of its contributors
- may be used to endorse or promote products derived from this software
- without specific prior written permission.
-
-THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
-ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
-IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
-ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
-FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
-DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
-OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
-HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
-LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
-OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
-SUCH DAMAGE.
-
--------------------------------------------------------------------
-
-Copyright (c) 1989, 1993
- The Regents of the University of California. All rights reserved.
-
-This code is derived from software contributed to Berkeley by
-Kevin Fall.
-
-Redistribution and use in source and binary forms, with or without
-modification, are permitted provided that the following conditions
-are met:
-1. Redistributions of source code must retain the above copyright
- notice, this list of conditions and the following disclaimer.
-2. Redistributions in binary form must reproduce the above copyright
- notice, this list of conditions and the following disclaimer in the
- documentation and/or other materials provided with the distribution.
-3. Neither the name of the University nor the names of its contributors
- may be used to endorse or promote products derived from this software
- without specific prior written permission.
-
-THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
-ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
-IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
-ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
-FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
-DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
-OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
-HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
-LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
-OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
-SUCH DAMAGE.
-
--------------------------------------------------------------------
-
-Copyright (c) 1989, 1993, 1994
- The Regents of the University of California. All rights reserved.
-
-This code is derived from software contributed to Berkeley by
-Chris Newcomb.
-
-Redistribution and use in source and binary forms, with or without
-modification, are permitted provided that the following conditions
-are met:
-1. Redistributions of source code must retain the above copyright
- notice, this list of conditions and the following disclaimer.
-2. Redistributions in binary form must reproduce the above copyright
- notice, this list of conditions and the following disclaimer in the
- documentation and/or other materials provided with the distribution.
-3. Neither the name of the University nor the names of its contributors
- may be used to endorse or promote products derived from this software
- without specific prior written permission.
-
-THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
-ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
-IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
-ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
-FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
-DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
-OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
-HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
-LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
-OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
-SUCH DAMAGE.
-
--------------------------------------------------------------------
-
-Copyright (c) 1989, 1993, 1994
- The Regents of the University of California. All rights reserved.
-
-This code is derived from software contributed to Berkeley by
-Ken Smith of The State University of New York at Buffalo.
-
-Redistribution and use in source and binary forms, with or without
-modification, are permitted provided that the following conditions
-are met:
-1. Redistributions of source code must retain the above copyright
- notice, this list of conditions and the following disclaimer.
-2. Redistributions in binary form must reproduce the above copyright
- notice, this list of conditions and the following disclaimer in the
- documentation and/or other materials provided with the distribution.
-3. Neither the name of the University nor the names of its contributors
- may be used to endorse or promote products derived from this software
- without specific prior written permission.
-
-THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
-ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
-IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
-ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
-FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
-DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
-OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
-HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
-LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
-OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
-SUCH DAMAGE.
-
--------------------------------------------------------------------
-
-Copyright (c) 1990, 1993, 1994, 2003
- The Regents of the University of California. All rights reserved.
-
-Redistribution and use in source and binary forms, with or without
-modification, are permitted provided that the following conditions
-are met:
-1. Redistributions of source code must retain the above copyright
- notice, this list of conditions and the following disclaimer.
-2. Redistributions in binary form must reproduce the above copyright
- notice, this list of conditions and the following disclaimer in the
- documentation and/or other materials provided with the distribution.
-3. Neither the name of the University nor the names of its contributors
- may be used to endorse or promote products derived from this software
- without specific prior written permission.
-
-THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
-ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
-IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
-ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
-FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
-DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
-OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
-HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
-LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
-OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
-SUCH DAMAGE.
-
--------------------------------------------------------------------
-
-Copyright (c) 1991, 1993
- The Regents of the University of California. All rights reserved.
-
-Redistribution and use in source and binary forms, with or without
-modification, are permitted provided that the following conditions
-are met:
-1. Redistributions of source code must retain the above copyright
- notice, this list of conditions and the following disclaimer.
-2. Redistributions in binary form must reproduce the above copyright
- notice, this list of conditions and the following disclaimer in the
- documentation and/or other materials provided with the distribution.
-3. Neither the name of the University nor the names of its contributors
- may be used to endorse or promote products derived from this software
- without specific prior written permission.
-
-THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
-ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
-IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
-ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
-FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
-DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
-OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
-HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
-LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
-OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
-SUCH DAMAGE.
-
--------------------------------------------------------------------
-
-Copyright (c) 1991, 1993, 1994
- The Regents of the University of California. All rights reserved.
-
-Redistribution and use in source and binary forms, with or without
-modification, are permitted provided that the following conditions
-are met:
-1. Redistributions of source code must retain the above copyright
- notice, this list of conditions and the following disclaimer.
-2. Redistributions in binary form must reproduce the above copyright
- notice, this list of conditions and the following disclaimer in the
- documentation and/or other materials provided with the distribution.
-3. Neither the name of the University nor the names of its contributors
- may be used to endorse or promote products derived from this software
- without specific prior written permission.
-
-THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
-ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
-IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
-ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
-FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
-DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
-OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
-HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
-LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
-OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
-SUCH DAMAGE.
-
--------------------------------------------------------------------
-
-Copyright (c) 1991, 1993, 1994
- The Regents of the University of California. All rights reserved.
-
-This code is derived from software contributed to Berkeley by
-Keith Muller of the University of California, San Diego and Lance
-Visser of Convex Computer Corporation.
-
-Redistribution and use in source and binary forms, with or without
-modification, are permitted provided that the following conditions
-are met:
-1. Redistributions of source code must retain the above copyright
- notice, this list of conditions and the following disclaimer.
-2. Redistributions in binary form must reproduce the above copyright
- notice, this list of conditions and the following disclaimer in the
- documentation and/or other materials provided with the distribution.
-3. Neither the name of the University nor the names of its contributors
- may be used to endorse or promote products derived from this software
- without specific prior written permission.
-
-THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
-ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
-IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
-ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
-FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
-DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
-OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
-HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
-LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
-OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
-SUCH DAMAGE.
-
--------------------------------------------------------------------
-
-Copyright (c) 1992, 1993, 1994
- The Regents of the University of California. All rights reserved.
-
-Redistribution and use in source and binary forms, with or without
-modification, are permitted provided that the following conditions
-are met:
-1. Redistributions of source code must retain the above copyright
- notice, this list of conditions and the following disclaimer.
-2. Redistributions in binary form must reproduce the above copyright
- notice, this list of conditions and the following disclaimer in the
- documentation and/or other materials provided with the distribution.
-3. Neither the name of the University nor the names of its contributors
- may be used to endorse or promote products derived from this software
- without specific prior written permission.
-
-THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
-ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
-IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
-ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
-FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
-DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
-OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
-HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
-LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
-OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
-SUCH DAMAGE.
-
--------------------------------------------------------------------
-
-Copyright (c) 1997, 1998, 1999, 2002 The NetBSD Foundation, Inc.
-All rights reserved.
-
-This code is derived from software contributed to The NetBSD Foundation
-by Jason R. Thorpe of the Numerical Aerospace Simulation Facility,
-NASA Ames Research Center, by Luke Mewburn and by Tomas Svensson.
-
-Redistribution and use in source and binary forms, with or without
-modification, are permitted provided that the following conditions
-are met:
-1. Redistributions of source code must retain the above copyright
- notice, this list of conditions and the following disclaimer.
-2. Redistributions in binary form must reproduce the above copyright
- notice, this list of conditions and the following disclaimer in the
- documentation and/or other materials provided with the distribution.
-
-THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
-``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
-TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
-PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
-BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
-CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
-SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
-INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
-CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
-ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
-POSSIBILITY OF SUCH DAMAGE.
-
--------------------------------------------------------------------
-
-Copyright (c) 1998 Robert Nordier
-All rights reserved.
-
-Redistribution and use in source and binary forms, with or without
-modification, are permitted provided that the following conditions
-are met:
-1. Redistributions of source code must retain the above copyright
- notice, this list of conditions and the following disclaimer.
-2. Redistributions in binary form must reproduce the above copyright
- notice, this list of conditions and the following disclaimer in
- the documentation and/or other materials provided with the
- distribution.
-
-THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS
-OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
-WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
-ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY
-DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
-DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
-GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
-INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER
-IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
-OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN
-IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
-
--------------------------------------------------------------------
-
-Copyright (c) 1999 James Howard and Dag-Erling Coïdan Smørgrav
-All rights reserved.
-
-Redistribution and use in source and binary forms, with or without
-modification, are permitted provided that the following conditions
-are met:
-1. Redistributions of source code must retain the above copyright
- notice, this list of conditions and the following disclaimer.
-2. Redistributions in binary form must reproduce the above copyright
- notice, this list of conditions and the following disclaimer in the
- documentation and/or other materials provided with the distribution.
-
-THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
-ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
-IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
-ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
-FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
-DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
-OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
-HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
-LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
-OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
-SUCH DAMAGE.
-
--------------------------------------------------------------------
-
-Copyright (c) 1999 James Howard and Dag-Erling Coïdan Smørgrav
-Copyright (C) 2008 Gabor Kovesdan <gabor@FreeBSD.org>
-All rights reserved.
-
-Redistribution and use in source and binary forms, with or without
-modification, are permitted provided that the following conditions
-are met:
-1. Redistributions of source code must retain the above copyright
- notice, this list of conditions and the following disclaimer.
-2. Redistributions in binary form must reproduce the above copyright
- notice, this list of conditions and the following disclaimer in the
- documentation and/or other materials provided with the distribution.
-
-THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
-ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
-IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
-ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
-FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
-DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
-OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
-HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
-LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
-OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
-SUCH DAMAGE.
-
--------------------------------------------------------------------
-
-Copyright (c) 1999 James Howard and Dag-Erling Coïdan Smørgrav
-Copyright (C) 2008-2009 Gabor Kovesdan <gabor@FreeBSD.org>
-All rights reserved.
-
-Redistribution and use in source and binary forms, with or without
-modification, are permitted provided that the following conditions
-are met:
-1. Redistributions of source code must retain the above copyright
- notice, this list of conditions and the following disclaimer.
-2. Redistributions in binary form must reproduce the above copyright
- notice, this list of conditions and the following disclaimer in the
- documentation and/or other materials provided with the distribution.
-
-THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
-ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
-IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
-ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
-FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
-DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
-OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
-HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
-LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
-OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
-SUCH DAMAGE.
-
--------------------------------------------------------------------
-
-Copyright (c) 1999 James Howard and Dag-Erling Coïdan Smørgrav
-Copyright (C) 2008-2010 Gabor Kovesdan <gabor@FreeBSD.org>
-All rights reserved.
-
-Redistribution and use in source and binary forms, with or without
-modification, are permitted provided that the following conditions
-are met:
-1. Redistributions of source code must retain the above copyright
- notice, this list of conditions and the following disclaimer.
-2. Redistributions in binary form must reproduce the above copyright
- notice, this list of conditions and the following disclaimer in the
- documentation and/or other materials provided with the distribution.
-
-THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
-ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
-IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
-ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
-FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
-DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
-OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
-HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
-LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
-OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
-SUCH DAMAGE.
-
--------------------------------------------------------------------
-
-Copyright (c) 1999 James Howard and Dag-Erling Coïdan Smørgrav
-Copyright (C) 2008-2010 Gabor Kovesdan <gabor@FreeBSD.org>
-Copyright (C) 2010 Dimitry Andric <dimitry@andric.com>
-All rights reserved.
-
-Redistribution and use in source and binary forms, with or without
-modification, are permitted provided that the following conditions
-are met:
-1. Redistributions of source code must retain the above copyright
- notice, this list of conditions and the following disclaimer.
-2. Redistributions in binary form must reproduce the above copyright
- notice, this list of conditions and the following disclaimer in the
- documentation and/or other materials provided with the distribution.
-
-THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
-ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
-IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
-ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
-FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
-DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
-OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
-HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
-LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
-OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
-SUCH DAMAGE.
-
--------------------------------------------------------------------
-
-Copyright (c) 1999 James Howard and Dag-Erling Coïdan Smørgrav
-Copyright (c) 2008-2009 Gabor Kovesdan <gabor@FreeBSD.org>
-All rights reserved.
-
-Redistribution and use in source and binary forms, with or without
-modification, are permitted provided that the following conditions
-are met:
-1. Redistributions of source code must retain the above copyright
- notice, this list of conditions and the following disclaimer.
-2. Redistributions in binary form must reproduce the above copyright
- notice, this list of conditions and the following disclaimer in the
- documentation and/or other materials provided with the distribution.
-
-THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
-ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
-IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
-ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
-FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
-DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
-OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
-HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
-LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
-OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
-SUCH DAMAGE.
-
--------------------------------------------------------------------
-
-Copyright (c) 2001-2002,2004 The NetBSD Foundation, Inc.
-All rights reserved.
-
-This code is derived from software contributed to The NetBSD Foundation
-by Luke Mewburn.
-
-Redistribution and use in source and binary forms, with or without
-modification, are permitted provided that the following conditions
-are met:
-1. Redistributions of source code must retain the above copyright
- notice, this list of conditions and the following disclaimer.
-2. Redistributions in binary form must reproduce the above copyright
- notice, this list of conditions and the following disclaimer in the
- documentation and/or other materials provided with the distribution.
-
-THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
-``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
-TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
-PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
-BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
-CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
-SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
-INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
-CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
-ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
-POSSIBILITY OF SUCH DAMAGE.
-
--------------------------------------------------------------------
-
-Copyright (c) 2007 The NetBSD Foundation, Inc.
-All rights reserved.
-
-This code is derived from software contributed to The NetBSD Foundation
-by Luke Mewburn.
-
-Redistribution and use in source and binary forms, with or without
-modification, are permitted provided that the following conditions
-are met:
-1. Redistributions of source code must retain the above copyright
- notice, this list of conditions and the following disclaimer.
-2. Redistributions in binary form must reproduce the above copyright
- notice, this list of conditions and the following disclaimer in the
- documentation and/or other materials provided with the distribution.
-
-THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
-``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
-TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
-PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
-BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
-CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
-SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
-INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
-CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
-ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
-POSSIBILITY OF SUCH DAMAGE.
-
--------------------------------------------------------------------
-
-Copyright (c) 2008, The Android Open Source Project
-All rights reserved.
-
-Redistribution and use in source and binary forms, with or without
-modification, are permitted provided that the following conditions
-are met:
- * Redistributions of source code must retain the above copyright
- notice, this list of conditions and the following disclaimer.
- * Redistributions in binary form must reproduce the above copyright
- notice, this list of conditions and the following disclaimer in
- the documentation and/or other materials provided with the
- distribution.
- * Neither the name of Google, Inc. nor the names of its contributors
- may be used to endorse or promote products derived from this
- software without specific prior written permission.
-
-THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
-"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
-LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
-FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
-COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
-INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
-BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
-OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
-AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
-OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
-OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
-SUCH DAMAGE.
-
--------------------------------------------------------------------
-
-Copyright (c) 2009, The Android Open Source Project
-All rights reserved.
-
-Redistribution and use in source and binary forms, with or without
-modification, are permitted provided that the following conditions
-are met:
- * Redistributions of source code must retain the above copyright
- notice, this list of conditions and the following disclaimer.
- * Redistributions in binary form must reproduce the above copyright
- notice, this list of conditions and the following disclaimer in
- the documentation and/or other materials provided with the
- distribution.
- * Neither the name of Google, Inc. nor the names of its contributors
- may be used to endorse or promote products derived from this
- software without specific prior written permission.
-
-THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
-"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
-LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
-FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
-COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
-INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
-BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
-OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
-AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
-OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
-OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
-SUCH DAMAGE.
-
--------------------------------------------------------------------
-
-Copyright (c) 2010 The NetBSD Foundation, Inc.
-All rights reserved.
-
-Redistribution and use in source and binary forms, with or without
-modification, are permitted provided that the following conditions
-are met:
-1. Redistributions of source code must retain the above copyright
- notice, this list of conditions and the following disclaimer.
-2. Redistributions in binary form must reproduce the above copyright
- notice, this list of conditions and the following disclaimer in the
- documentation and/or other materials provided with the distribution.
-
-THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
-``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
-TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
-PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
-BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
-CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
-SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
-INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
-CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
-ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
-POSSIBILITY OF SUCH DAMAGE.
-
--------------------------------------------------------------------
-
-Copyright (c) 2010, The Android Open Source Project
-All rights reserved.
-
-Redistribution and use in source and binary forms, with or without
-modification, are permitted provided that the following conditions
-are met:
- * Redistributions of source code must retain the above copyright
- notice, this list of conditions and the following disclaimer.
- * Redistributions in binary form must reproduce the above copyright
- notice, this list of conditions and the following disclaimer in
- the documentation and/or other materials provided with the
- distribution.
- * Neither the name of Google, Inc. nor the names of its contributors
- may be used to endorse or promote products derived from this
- software without specific prior written permission.
-
-THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
-"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
-LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
-FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
-COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
-INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
-BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
-OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
-AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
-OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
-OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
-SUCH DAMAGE.
-
--------------------------------------------------------------------
-
-Copyright (c) 2012, The Android Open Source Project
-All rights reserved.
-
-Redistribution and use in source and binary forms, with or without
-modification, are permitted provided that the following conditions
-are met:
- * Redistributions of source code must retain the above copyright
- notice, this list of conditions and the following disclaimer.
- * Redistributions in binary form must reproduce the above copyright
- notice, this list of conditions and the following disclaimer in
- the documentation and/or other materials provided with the
- distribution.
- * Neither the name of Google, Inc. nor the names of its contributors
- may be used to endorse or promote products derived from this
- software without specific prior written permission.
-
-THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
-"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
-LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
-FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
-COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
-INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
-BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
-OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
-AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
-OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
-OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
-SUCH DAMAGE.
-
--------------------------------------------------------------------
-
-Copyright (c) 2013, The Android Open Source Project
-All rights reserved.
-
-Redistribution and use in source and binary forms, with or without
-modification, are permitted provided that the following conditions
-are met:
- * Redistributions of source code must retain the above copyright
- notice, this list of conditions and the following disclaimer.
- * Redistributions in binary form must reproduce the above copyright
- notice, this list of conditions and the following disclaimer in
- the documentation and/or other materials provided with the
- distribution.
- * Neither the name of Google, Inc. nor the names of its contributors
- may be used to endorse or promote products derived from this
- software without specific prior written permission.
-
-THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
-"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
-LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
-FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
-COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
-INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
-BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
-OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
-AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
-OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
-OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
-SUCH DAMAGE.
-
--------------------------------------------------------------------
-
-Copyright (c) 2014, The Android Open Source Project
-All rights reserved.
-
-Redistribution and use in source and binary forms, with or without
-modification, are permitted provided that the following conditions
-are met:
- * Redistributions of source code must retain the above copyright
- notice, this list of conditions and the following disclaimer.
- * Redistributions in binary form must reproduce the above copyright
- notice, this list of conditions and the following disclaimer in
- the documentation and/or other materials provided with the
- distribution.
- * Neither the name of Google, Inc. nor the names of its contributors
- may be used to endorse or promote products derived from this
- software without specific prior written permission.
-
-THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
-"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
-LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
-FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
-COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
-INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
-BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
-OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
-AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
-OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
-OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
-SUCH DAMAGE.
-
--------------------------------------------------------------------
-
diff --git a/toolbox/getprop.cpp b/toolbox/getprop.cpp
index 9e324a0..ca345cb 100644
--- a/toolbox/getprop.cpp
+++ b/toolbox/getprop.cpp
@@ -1,18 +1,18 @@
-//
-// Copyright (C) 2017 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.
-//
+/*
+ * Copyright (C) 2017 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 <getopt.h>
#include <sys/system_properties.h>
diff --git a/toolbox/newfs_msdos.c b/toolbox/newfs_msdos.c
deleted file mode 100644
index 5fc8b02..0000000
--- a/toolbox/newfs_msdos.c
+++ /dev/null
@@ -1,1108 +0,0 @@
-/*
- * Copyright (c) 1998 Robert Nordier
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- * 1. Redistributions of source code must retain the above copyright
- * notice, this list of conditions and the following disclaimer.
- * 2. Redistributions in binary form must reproduce the above copyright
- * notice, this list of conditions and the following disclaimer in
- * the documentation and/or other materials provided with the
- * distribution.
- *
- * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS
- * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
- * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
- * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY
- * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
- * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
- * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
- * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER
- * IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
- * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN
- * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
- */
-
-#ifndef lint
-static const char rcsid[] =
- "$FreeBSD: src/sbin/newfs_msdos/newfs_msdos.c,v 1.33 2009/04/11 14:56:29 ed Exp $";
-#endif /* not lint */
-
-#include <sys/param.h>
-
-#ifdef __APPLE__
-#elif defined(ANDROID)
-#include <linux/fs.h>
-#include <linux/hdreg.h>
-#include <stdarg.h>
-#include <sys/ioctl.h>
-#else
-#include <sys/fdcio.h>
-#include <sys/disk.h>
-#include <sys/disklabel.h>
-#include <sys/mount.h>
-#endif
-
-#include <sys/stat.h>
-#include <sys/time.h>
-
-#include <ctype.h>
-#include <err.h>
-#include <errno.h>
-#include <fcntl.h>
-#include <inttypes.h>
-#include <paths.h>
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-#include <time.h>
-#include <unistd.h>
-
-#ifndef __unused
-#define __unused __attribute__((__unused__))
-#endif
-
-#define MAXU16 0xffff /* maximum unsigned 16-bit quantity */
-#define BPN 4 /* bits per nibble */
-#define NPB 2 /* nibbles per byte */
-
-#define DOSMAGIC 0xaa55 /* DOS magic number */
-#define MINBPS 512 /* minimum bytes per sector */
-#define MAXSPC 128 /* maximum sectors per cluster */
-#define MAXNFT 16 /* maximum number of FATs */
-#define DEFBLK 4096 /* default block size */
-#define DEFBLK16 2048 /* default block size FAT16 */
-#define DEFRDE 512 /* default root directory entries */
-#define RESFTE 2 /* reserved FAT entries */
-#define MINCLS12 1 /* minimum FAT12 clusters */
-#define MINCLS16 0x1000 /* minimum FAT16 clusters */
-#define MINCLS32 2 /* minimum FAT32 clusters */
-#define MAXCLS12 0xfed /* maximum FAT12 clusters */
-#define MAXCLS16 0xfff5 /* maximum FAT16 clusters */
-#define MAXCLS32 0xffffff5 /* maximum FAT32 clusters */
-
-#define mincls(fat) ((fat) == 12 ? MINCLS12 : \
- (fat) == 16 ? MINCLS16 : \
- MINCLS32)
-
-#define maxcls(fat) ((fat) == 12 ? MAXCLS12 : \
- (fat) == 16 ? MAXCLS16 : \
- MAXCLS32)
-
-#define mk1(p, x) \
- (p) = (u_int8_t)(x)
-
-#define mk2(p, x) \
- (p)[0] = (u_int8_t)(x), \
- (p)[1] = (u_int8_t)((x) >> 010)
-
-#define mk4(p, x) \
- (p)[0] = (u_int8_t)(x), \
- (p)[1] = (u_int8_t)((x) >> 010), \
- (p)[2] = (u_int8_t)((x) >> 020), \
- (p)[3] = (u_int8_t)((x) >> 030)
-
-#define argto1(arg, lo, msg) argtou(arg, lo, 0xff, msg)
-#define argto2(arg, lo, msg) argtou(arg, lo, 0xffff, msg)
-#define argto4(arg, lo, msg) argtou(arg, lo, 0xffffffff, msg)
-#define argtox(arg, lo, msg) argtou(arg, lo, UINT_MAX, msg)
-
-struct bs {
- u_int8_t jmp[3]; /* bootstrap entry point */
- u_int8_t oem[8]; /* OEM name and version */
-};
-
-struct bsbpb {
- u_int8_t bps[2]; /* bytes per sector */
- u_int8_t spc; /* sectors per cluster */
- u_int8_t res[2]; /* reserved sectors */
- u_int8_t nft; /* number of FATs */
- u_int8_t rde[2]; /* root directory entries */
- u_int8_t sec[2]; /* total sectors */
- u_int8_t mid; /* media descriptor */
- u_int8_t spf[2]; /* sectors per FAT */
- u_int8_t spt[2]; /* sectors per track */
- u_int8_t hds[2]; /* drive heads */
- u_int8_t hid[4]; /* hidden sectors */
- u_int8_t bsec[4]; /* big total sectors */
-};
-
-struct bsxbpb {
- u_int8_t bspf[4]; /* big sectors per FAT */
- u_int8_t xflg[2]; /* FAT control flags */
- u_int8_t vers[2]; /* file system version */
- u_int8_t rdcl[4]; /* root directory start cluster */
- u_int8_t infs[2]; /* file system info sector */
- u_int8_t bkbs[2]; /* backup boot sector */
- u_int8_t rsvd[12]; /* reserved */
-};
-
-struct bsx {
- u_int8_t drv; /* drive number */
- u_int8_t rsvd; /* reserved */
- u_int8_t sig; /* extended boot signature */
- u_int8_t volid[4]; /* volume ID number */
- u_int8_t label[11]; /* volume label */
- u_int8_t type[8]; /* file system type */
-};
-
-struct de {
- u_int8_t namext[11]; /* name and extension */
- u_int8_t attr; /* attributes */
- u_int8_t rsvd[10]; /* reserved */
- u_int8_t time[2]; /* creation time */
- u_int8_t date[2]; /* creation date */
- u_int8_t clus[2]; /* starting cluster */
- u_int8_t size[4]; /* size */
-};
-
-struct bpb {
- u_int bps; /* bytes per sector */
- u_int spc; /* sectors per cluster */
- u_int res; /* reserved sectors */
- u_int nft; /* number of FATs */
- u_int rde; /* root directory entries */
- u_int sec; /* total sectors */
- u_int mid; /* media descriptor */
- u_int spf; /* sectors per FAT */
- u_int spt; /* sectors per track */
- u_int hds; /* drive heads */
- u_int hid; /* hidden sectors */
- u_int bsec; /* big total sectors */
- u_int bspf; /* big sectors per FAT */
- u_int rdcl; /* root directory start cluster */
- u_int infs; /* file system info sector */
- u_int bkbs; /* backup boot sector */
-};
-
-#define BPBGAP 0, 0, 0, 0, 0, 0
-
-static struct {
- const char *name;
- struct bpb bpb;
-} const stdfmt[] = {
- {"160", {512, 1, 1, 2, 64, 320, 0xfe, 1, 8, 1, BPBGAP}},
- {"180", {512, 1, 1, 2, 64, 360, 0xfc, 2, 9, 1, BPBGAP}},
- {"320", {512, 2, 1, 2, 112, 640, 0xff, 1, 8, 2, BPBGAP}},
- {"360", {512, 2, 1, 2, 112, 720, 0xfd, 2, 9, 2, BPBGAP}},
- {"640", {512, 2, 1, 2, 112, 1280, 0xfb, 2, 8, 2, BPBGAP}},
- {"720", {512, 2, 1, 2, 112, 1440, 0xf9, 3, 9, 2, BPBGAP}},
- {"1200", {512, 1, 1, 2, 224, 2400, 0xf9, 7, 15, 2, BPBGAP}},
- {"1232", {1024,1, 1, 2, 192, 1232, 0xfe, 2, 8, 2, BPBGAP}},
- {"1440", {512, 1, 1, 2, 224, 2880, 0xf0, 9, 18, 2, BPBGAP}},
- {"2880", {512, 2, 1, 2, 240, 5760, 0xf0, 9, 36, 2, BPBGAP}}
-};
-
-static const u_int8_t bootcode[] = {
- 0xfa, /* cli */
- 0x31, 0xc0, /* xor ax,ax */
- 0x8e, 0xd0, /* mov ss,ax */
- 0xbc, 0x00, 0x7c, /* mov sp,7c00h */
- 0xfb, /* sti */
- 0x8e, 0xd8, /* mov ds,ax */
- 0xe8, 0x00, 0x00, /* call $ + 3 */
- 0x5e, /* pop si */
- 0x83, 0xc6, 0x19, /* add si,+19h */
- 0xbb, 0x07, 0x00, /* mov bx,0007h */
- 0xfc, /* cld */
- 0xac, /* lodsb */
- 0x84, 0xc0, /* test al,al */
- 0x74, 0x06, /* jz $ + 8 */
- 0xb4, 0x0e, /* mov ah,0eh */
- 0xcd, 0x10, /* int 10h */
- 0xeb, 0xf5, /* jmp $ - 9 */
- 0x30, 0xe4, /* xor ah,ah */
- 0xcd, 0x16, /* int 16h */
- 0xcd, 0x19, /* int 19h */
- 0x0d, 0x0a,
- 'N', 'o', 'n', '-', 's', 'y', 's', 't',
- 'e', 'm', ' ', 'd', 'i', 's', 'k',
- 0x0d, 0x0a,
- 'P', 'r', 'e', 's', 's', ' ', 'a', 'n',
- 'y', ' ', 'k', 'e', 'y', ' ', 't', 'o',
- ' ', 'r', 'e', 'b', 'o', 'o', 't',
- 0x0d, 0x0a,
- 0
-};
-
-static void check_mounted(const char *, mode_t);
-static void getstdfmt(const char *, struct bpb *);
-static void getdiskinfo(int, const char *, const char *, int, struct bpb *);
-static void print_bpb(struct bpb *);
-static u_int ckgeom(const char *, u_int, const char *);
-static u_int argtou(const char *, u_int, u_int, const char *);
-static off_t argtooff(const char *, const char *);
-static int oklabel(const char *);
-static void mklabel(u_int8_t *, const char *);
-static void setstr(u_int8_t *, const char *, size_t);
-static void usage(void);
-
-/*
- * Construct a FAT12, FAT16, or FAT32 file system.
- */
-int newfs_msdos_main(int argc, char *argv[])
-{
- static const char opts[] = "@:NAB:C:F:I:L:O:S:a:b:c:e:f:h:i:k:m:n:o:r:s:u:";
- const char *opt_B = NULL, *opt_L = NULL, *opt_O = NULL, *opt_f = NULL;
- u_int opt_F = 0, opt_I = 0, opt_S = 0, opt_a = 0, opt_b = 0, opt_c = 0;
- u_int opt_e = 0, opt_h = 0, opt_i = 0, opt_k = 0, opt_m = 0, opt_n = 0;
- u_int opt_o = 0, opt_r = 0, opt_s = 0, opt_u = 0;
- u_int opt_A = 0;
- int opt_N = 0;
- int Iflag = 0, mflag = 0, oflag = 0;
- char buf[MAXPATHLEN];
- struct stat sb;
- struct timeval tv;
- struct bpb bpb;
- struct tm *tm;
- struct bs *bs;
- struct bsbpb *bsbpb;
- struct bsxbpb *bsxbpb;
- struct bsx *bsx;
- struct de *de;
- u_int8_t *img;
- const char *fname, *dtype, *bname;
- ssize_t n;
- time_t now;
- u_int fat, bss, rds, cls, dir, lsn, x, x1, x2;
- u_int extra_res, alignment=0, set_res, set_spf, set_spc, tempx, attempts=0;
- int ch, fd, fd1;
- off_t opt_create = 0, opt_ofs = 0;
-
- while ((ch = getopt(argc, argv, opts)) != -1)
- switch (ch) {
- case '@':
- opt_ofs = argtooff(optarg, "offset");
- break;
- case 'N':
- opt_N = 1;
- break;
- case 'A':
- opt_A = 1;
- break;
- case 'B':
- opt_B = optarg;
- break;
- case 'C':
- opt_create = argtooff(optarg, "create size");
- break;
- case 'F':
- if (strcmp(optarg, "12") && strcmp(optarg, "16") && strcmp(optarg, "32"))
- errx(1, "%s: bad FAT type", optarg);
- opt_F = atoi(optarg);
- break;
- case 'I':
- opt_I = argto4(optarg, 0, "volume ID");
- Iflag = 1;
- break;
- case 'L':
- if (!oklabel(optarg))
- errx(1, "%s: bad volume label", optarg);
- opt_L = optarg;
- break;
- case 'O':
- if (strlen(optarg) > 8)
- errx(1, "%s: bad OEM string", optarg);
- opt_O = optarg;
- break;
- case 'S':
- opt_S = argto2(optarg, 1, "bytes/sector");
- break;
- case 'a':
- opt_a = argto4(optarg, 1, "sectors/FAT");
- break;
- case 'b':
- opt_b = argtox(optarg, 1, "block size");
- opt_c = 0;
- break;
- case 'c':
- opt_c = argto1(optarg, 1, "sectors/cluster");
- opt_b = 0;
- break;
- case 'e':
- opt_e = argto2(optarg, 1, "directory entries");
- break;
- case 'f':
- opt_f = optarg;
- break;
- case 'h':
- opt_h = argto2(optarg, 1, "drive heads");
- break;
- case 'i':
- opt_i = argto2(optarg, 1, "info sector");
- break;
- case 'k':
- opt_k = argto2(optarg, 1, "backup sector");
- break;
- case 'm':
- opt_m = argto1(optarg, 0, "media descriptor");
- mflag = 1;
- break;
- case 'n':
- opt_n = argto1(optarg, 1, "number of FATs");
- break;
- case 'o':
- opt_o = argto4(optarg, 0, "hidden sectors");
- oflag = 1;
- break;
- case 'r':
- opt_r = argto2(optarg, 1, "reserved sectors");
- break;
- case 's':
- opt_s = argto4(optarg, 1, "file system size");
- break;
- case 'u':
- opt_u = argto2(optarg, 1, "sectors/track");
- break;
- default:
- usage();
- }
- argc -= optind;
- argv += optind;
- if (argc < 1 || argc > 2)
- usage();
- fname = *argv++;
- if (!opt_create && !strchr(fname, '/')) {
- snprintf(buf, sizeof(buf), "%s%s", _PATH_DEV, fname);
- if (!(fname = strdup(buf)))
- err(1, "%s", buf);
- }
- dtype = *argv;
- if (opt_A) {
- if (opt_r)
- errx(1, "align (-A) is incompatible with -r");
- if (opt_N)
- errx(1, "align (-A) is incompatible with -N");
- }
- if (opt_create) {
- if (opt_N)
- errx(1, "create (-C) is incompatible with -N");
- fd = open(fname, O_RDWR | O_CREAT | O_TRUNC, 0644);
- if (fd == -1)
- errx(1, "failed to create %s", fname);
- if (ftruncate(fd, opt_create))
- errx(1, "failed to initialize %jd bytes", (intmax_t)opt_create);
- } else if ((fd = open(fname, opt_N ? O_RDONLY : O_RDWR)) == -1)
- err(1, "%s", fname);
- if (fstat(fd, &sb))
- err(1, "%s", fname);
- if (opt_create) {
- if (!S_ISREG(sb.st_mode))
- warnx("warning, %s is not a regular file", fname);
- } else {
- if (!S_ISCHR(sb.st_mode))
- warnx("warning, %s is not a character device", fname);
- }
- if (!opt_N)
- check_mounted(fname, sb.st_mode);
- if (opt_ofs && opt_ofs != lseek(fd, opt_ofs, SEEK_SET))
- errx(1, "cannot seek to %jd", (intmax_t)opt_ofs);
- memset(&bpb, 0, sizeof(bpb));
- if (opt_f) {
- getstdfmt(opt_f, &bpb);
- bpb.bsec = bpb.sec;
- bpb.sec = 0;
- bpb.bspf = bpb.spf;
- bpb.spf = 0;
- }
- if (opt_h)
- bpb.hds = opt_h;
- if (opt_u)
- bpb.spt = opt_u;
- if (opt_S)
- bpb.bps = opt_S;
- if (opt_s)
- bpb.bsec = opt_s;
- if (oflag)
- bpb.hid = opt_o;
- if (!(opt_f || (opt_h && opt_u && opt_S && opt_s && oflag))) {
- off_t delta;
- getdiskinfo(fd, fname, dtype, oflag, &bpb);
- if (opt_s) {
- bpb.bsec = opt_s;
- }
- bpb.bsec -= (opt_ofs / bpb.bps);
- delta = bpb.bsec % bpb.spt;
- if (delta != 0) {
- warnx("trim %d sectors from %d to adjust to a multiple of %d",
- (int)delta, bpb.bsec, bpb.spt);
- bpb.bsec -= delta;
- }
- if (bpb.spc == 0) { /* set defaults */
- if (bpb.bsec <= 6000) /* about 3MB -> 512 bytes */
- bpb.spc = 1;
- else if (bpb.bsec <= (1<<17)) /* 64M -> 4k */
- bpb.spc = 8;
- else if (bpb.bsec <= (1<<19)) /* 256M -> 8k */
- bpb.spc = 16;
- else if (bpb.bsec <= (1<<22)) /* 2G -> 16k, some versions of windows
- require a minimum of 65527 clusters */
- bpb.spc = 32;
- else
- bpb.spc = 64; /* otherwise 32k */
- }
- }
- if (!powerof2(bpb.bps))
- errx(1, "bytes/sector (%u) is not a power of 2", bpb.bps);
- if (bpb.bps < MINBPS)
- errx(1, "bytes/sector (%u) is too small; minimum is %u",
- bpb.bps, MINBPS);
- if (!(fat = opt_F)) {
- if (opt_f)
- fat = 12;
- else if (!opt_e && (opt_i || opt_k))
- fat = 32;
- }
- if ((fat == 32 && opt_e) || (fat != 32 && (opt_i || opt_k)))
- errx(1, "-%c is not a legal FAT%s option",
- fat == 32 ? 'e' : opt_i ? 'i' : 'k',
- fat == 32 ? "32" : "12/16");
- if (opt_f && fat == 32)
- bpb.rde = 0;
- if (opt_b) {
- if (!powerof2(opt_b))
- errx(1, "block size (%u) is not a power of 2", opt_b);
- if (opt_b < bpb.bps)
- errx(1, "block size (%u) is too small; minimum is %u",
- opt_b, bpb.bps);
- if (opt_b > bpb.bps * MAXSPC)
- errx(1, "block size (%u) is too large; maximum is %u", opt_b, bpb.bps * MAXSPC);
- bpb.spc = opt_b / bpb.bps;
- }
- if (opt_c) {
- if (!powerof2(opt_c))
- errx(1, "sectors/cluster (%u) is not a power of 2", opt_c);
- bpb.spc = opt_c;
- }
- if (opt_r)
- bpb.res = opt_r;
- if (opt_n) {
- if (opt_n > MAXNFT)
- errx(1, "number of FATs (%u) is too large; maximum is %u", opt_n, MAXNFT);
- bpb.nft = opt_n;
- }
- if (opt_e)
- bpb.rde = opt_e;
- if (mflag) {
- if (opt_m < 0xf0)
- errx(1, "illegal media descriptor (%#x)", opt_m);
- bpb.mid = opt_m;
- }
- if (opt_a)
- bpb.bspf = opt_a;
- if (opt_i)
- bpb.infs = opt_i;
- if (opt_k)
- bpb.bkbs = opt_k;
- bss = 1;
- bname = NULL;
- fd1 = -1;
- if (opt_B) {
- bname = opt_B;
- if (!strchr(bname, '/')) {
- snprintf(buf, sizeof(buf), "/boot/%s", bname);
- if (!(bname = strdup(buf)))
- err(1, "%s", buf);
- }
- if ((fd1 = open(bname, O_RDONLY)) == -1 || fstat(fd1, &sb))
- err(1, "%s", bname);
- if (!S_ISREG(sb.st_mode) || sb.st_size % bpb.bps ||
- sb.st_size < bpb.bps || sb.st_size > bpb.bps * MAXU16)
- errx(1, "%s: inappropriate file type or format", bname);
- bss = sb.st_size / bpb.bps;
- }
- if (!bpb.nft)
- bpb.nft = 2;
- if (!fat) {
- if (bpb.bsec < (bpb.res ? bpb.res : bss) +
- howmany((RESFTE + (bpb.spc ? MINCLS16 : MAXCLS12 + 1)) *
- ((bpb.spc ? 16 : 12) / BPN), bpb.bps * NPB) *
- bpb.nft +
- howmany(bpb.rde ? bpb.rde : DEFRDE,
- bpb.bps / sizeof(struct de)) +
- (bpb.spc ? MINCLS16 : MAXCLS12 + 1) *
- (bpb.spc ? bpb.spc : howmany(DEFBLK, bpb.bps)))
- fat = 12;
- else if (bpb.rde || bpb.bsec <
- (bpb.res ? bpb.res : bss) +
- howmany((RESFTE + MAXCLS16) * 2, bpb.bps) * bpb.nft +
- howmany(DEFRDE, bpb.bps / sizeof(struct de)) +
- (MAXCLS16 + 1) *
- (bpb.spc ? bpb.spc : howmany(8192, bpb.bps)))
- fat = 16;
- else
- fat = 32;
- }
- x = bss;
- if (fat == 32) {
- if (!bpb.infs) {
- if (x == MAXU16 || x == bpb.bkbs)
- errx(1, "no room for info sector");
- bpb.infs = x;
- }
- if (bpb.infs != MAXU16 && x <= bpb.infs)
- x = bpb.infs + 1;
- if (!bpb.bkbs) {
- if (x == MAXU16)
- errx(1, "no room for backup sector");
- bpb.bkbs = x;
- } else if (bpb.bkbs != MAXU16 && bpb.bkbs == bpb.infs)
- errx(1, "backup sector would overwrite info sector");
- if (bpb.bkbs != MAXU16 && x <= bpb.bkbs)
- x = bpb.bkbs + 1;
- }
-
- extra_res = 0;
- set_res = !bpb.res;
- set_spf = !bpb.bspf;
- set_spc = !bpb.spc;
- tempx = x;
- /*
- * Attempt to align if opt_A is set. This is done by increasing the number
- * of reserved blocks. This can cause other factors to change, which can in
- * turn change the alignment. This should take at most 2 iterations, as
- * increasing the reserved amount may cause the FAT size to decrease by 1,
- * requiring another nft reserved blocks. If spc changes, it will
- * be half of its previous size, and thus will not throw off alignment.
- */
- do {
- x = tempx;
- if (set_res)
- bpb.res = (fat == 32 ? MAX(x, MAX(16384 / bpb.bps, 4)) : x) + extra_res;
- else if (bpb.res < x)
- errx(1, "too few reserved sectors");
- if (fat != 32 && !bpb.rde)
- bpb.rde = DEFRDE;
- rds = howmany(bpb.rde, bpb.bps / sizeof(struct de));
- if (set_spc)
- for (bpb.spc = howmany(fat == 16 ? DEFBLK16 : DEFBLK, bpb.bps);
- bpb.spc < MAXSPC &&
- bpb.res +
- howmany((RESFTE + maxcls(fat)) * (fat / BPN),
- bpb.bps * NPB) * bpb.nft +
- rds +
- (u_int64_t)(maxcls(fat) + 1) * bpb.spc <= bpb.bsec;
- bpb.spc <<= 1);
- if (fat != 32 && bpb.bspf > MAXU16)
- errx(1, "too many sectors/FAT for FAT12/16");
- x1 = bpb.res + rds;
- x = bpb.bspf ? bpb.bspf : 1;
- if (x1 + (u_int64_t)x * bpb.nft > bpb.bsec)
- errx(1, "meta data exceeds file system size");
- x1 += x * bpb.nft;
- x = (u_int64_t)(bpb.bsec - x1) * bpb.bps * NPB /
- (bpb.spc * bpb.bps * NPB + fat / BPN * bpb.nft);
- x2 = howmany((RESFTE + MIN(x, maxcls(fat))) * (fat / BPN), bpb.bps * NPB);
- if (set_spf) {
- if (!bpb.bspf) {
- bpb.bspf = x2;
- }
- x1 += (bpb.bspf - 1) * bpb.nft;
- }
- if(set_res) {
- /* attempt to align root directory */
- alignment = (bpb.res + bpb.bspf * bpb.nft) % bpb.spc;
- extra_res += bpb.spc - alignment;
- }
- attempts++;
- } while(opt_A && alignment != 0 && attempts < 2);
- if (alignment != 0)
- warnx("warning: Alignment failed.");
-
- cls = (bpb.bsec - x1) / bpb.spc;
- x = (u_int64_t)bpb.bspf * bpb.bps * NPB / (fat / BPN) - RESFTE;
- if (cls > x)
- cls = x;
- if (bpb.bspf < x2)
- warnx("warning: sectors/FAT limits file system to %u clusters", cls);
- if (cls < mincls(fat))
- errx(1, "%u clusters too few clusters for FAT%u, need %u", cls, fat, mincls(fat));
- if (cls > maxcls(fat)) {
- cls = maxcls(fat);
- bpb.bsec = x1 + (cls + 1) * bpb.spc - 1;
- warnx("warning: FAT type limits file system to %u sectors", bpb.bsec);
- }
- printf("%s: %u sector%s in %u FAT%u cluster%s (%u bytes/cluster)\n",
- fname, cls * bpb.spc, cls * bpb.spc == 1 ? "" : "s", cls, fat,
- cls == 1 ? "" : "s", bpb.bps * bpb.spc);
- if (!bpb.mid)
- bpb.mid = !bpb.hid ? 0xf0 : 0xf8;
- if (fat == 32)
- bpb.rdcl = RESFTE;
- if (bpb.hid + bpb.bsec <= MAXU16) {
- bpb.sec = bpb.bsec;
- bpb.bsec = 0;
- }
- if (fat != 32) {
- bpb.spf = bpb.bspf;
- bpb.bspf = 0;
- }
- print_bpb(&bpb);
- if (!opt_N) {
- gettimeofday(&tv, NULL);
- now = tv.tv_sec;
- tm = localtime(&now);
- if (!(img = malloc(bpb.bps)))
- err(1, "%u", bpb.bps);
- dir = bpb.res + (bpb.spf ? bpb.spf : bpb.bspf) * bpb.nft;
- for (lsn = 0; lsn < dir + (fat == 32 ? bpb.spc : rds); lsn++) {
- x = lsn;
- if (opt_B && fat == 32 && bpb.bkbs != MAXU16 && bss <= bpb.bkbs && x >= bpb.bkbs) {
- x -= bpb.bkbs;
- if (!x && lseek(fd1, opt_ofs, SEEK_SET))
- err(1, "%s", bname);
- }
- if (opt_B && x < bss) {
- if ((n = read(fd1, img, bpb.bps)) == -1)
- err(1, "%s", bname);
- if ((unsigned)n != bpb.bps)
- errx(1, "%s: can't read sector %u", bname, x);
- } else
- memset(img, 0, bpb.bps);
- if (!lsn || (fat == 32 && bpb.bkbs != MAXU16 && lsn == bpb.bkbs)) {
- x1 = sizeof(struct bs);
- bsbpb = (struct bsbpb *)(img + x1);
- mk2(bsbpb->bps, bpb.bps);
- mk1(bsbpb->spc, bpb.spc);
- mk2(bsbpb->res, bpb.res);
- mk1(bsbpb->nft, bpb.nft);
- mk2(bsbpb->rde, bpb.rde);
- mk2(bsbpb->sec, bpb.sec);
- mk1(bsbpb->mid, bpb.mid);
- mk2(bsbpb->spf, bpb.spf);
- mk2(bsbpb->spt, bpb.spt);
- mk2(bsbpb->hds, bpb.hds);
- mk4(bsbpb->hid, bpb.hid);
- mk4(bsbpb->bsec, bpb.bsec);
- x1 += sizeof(struct bsbpb);
- if (fat == 32) {
- bsxbpb = (struct bsxbpb *)(img + x1);
- mk4(bsxbpb->bspf, bpb.bspf);
- mk2(bsxbpb->xflg, 0);
- mk2(bsxbpb->vers, 0);
- mk4(bsxbpb->rdcl, bpb.rdcl);
- mk2(bsxbpb->infs, bpb.infs);
- mk2(bsxbpb->bkbs, bpb.bkbs);
- x1 += sizeof(struct bsxbpb);
- }
- bsx = (struct bsx *)(img + x1);
- mk1(bsx->sig, 0x29);
- if (Iflag)
- x = opt_I;
- else
- x = (((u_int)(1 + tm->tm_mon) << 8 |
- (u_int)tm->tm_mday) +
- ((u_int)tm->tm_sec << 8 |
- (u_int)(tv.tv_usec / 10))) << 16 |
- ((u_int)(1900 + tm->tm_year) +
- ((u_int)tm->tm_hour << 8 |
- (u_int)tm->tm_min));
- mk4(bsx->volid, x);
- mklabel(bsx->label, opt_L ? opt_L : "NO NAME");
- snprintf(buf, sizeof(buf), "FAT%u", fat);
- setstr(bsx->type, buf, sizeof(bsx->type));
- if (!opt_B) {
- x1 += sizeof(struct bsx);
- bs = (struct bs *)img;
- mk1(bs->jmp[0], 0xeb);
- mk1(bs->jmp[1], x1 - 2);
- mk1(bs->jmp[2], 0x90);
- setstr(bs->oem, opt_O ? opt_O : "BSD 4.4",
- sizeof(bs->oem));
- memcpy(img + x1, bootcode, sizeof(bootcode));
- mk2(img + MINBPS - 2, DOSMAGIC);
- }
- } else if (fat == 32 && bpb.infs != MAXU16 &&
- (lsn == bpb.infs || (bpb.bkbs != MAXU16 &&
- lsn == bpb.bkbs + bpb.infs))) {
- mk4(img, 0x41615252);
- mk4(img + MINBPS - 28, 0x61417272);
- mk4(img + MINBPS - 24, 0xffffffff);
- mk4(img + MINBPS - 20, bpb.rdcl);
- mk2(img + MINBPS - 2, DOSMAGIC);
- } else if (lsn >= bpb.res && lsn < dir &&
- !((lsn - bpb.res) % (bpb.spf ? bpb.spf : bpb.bspf))) {
- mk1(img[0], bpb.mid);
- for (x = 1; x < fat * (fat == 32 ? 3 : 2) / 8; x++)
- mk1(img[x], fat == 32 && x % 4 == 3 ? 0x0f : 0xff);
- } else if (lsn == dir && opt_L) {
- de = (struct de *)img;
- mklabel(de->namext, opt_L);
- mk1(de->attr, 050);
- x = (u_int)tm->tm_hour << 11 |
- (u_int)tm->tm_min << 5 |
- (u_int)tm->tm_sec >> 1;
- mk2(de->time, x);
- x = (u_int)(tm->tm_year - 80) << 9 |
- (u_int)(tm->tm_mon + 1) << 5 |
- (u_int)tm->tm_mday;
- mk2(de->date, x);
- }
- if ((n = write(fd, img, bpb.bps)) == -1)
- err(1, "%s", fname);
- if ((unsigned)n != bpb.bps) {
- errx(1, "%s: can't write sector %u", fname, lsn);
- exit(1);
- }
- }
- free(img);
- }
- return 0;
-}
-
-/*
- * Exit with error if file system is mounted.
- */
-static void check_mounted(const char *fname, mode_t mode)
-{
-#ifdef ANDROID
- warnx("Skipping mount checks");
-#else
- struct statfs *mp;
- const char *s1, *s2;
- size_t len;
- int n, r;
-
- if (!(n = getmntinfo(&mp, MNT_NOWAIT)))
- err(1, "getmntinfo");
- len = strlen(_PATH_DEV);
- s1 = fname;
- if (!strncmp(s1, _PATH_DEV, len))
- s1 += len;
- r = S_ISCHR(mode) && s1 != fname && *s1 == 'r';
- for (; n--; mp++) {
- s2 = mp->f_mntfromname;
- if (!strncmp(s2, _PATH_DEV, len))
- s2 += len;
- if ((r && s2 != mp->f_mntfromname && !strcmp(s1 + 1, s2)) || !strcmp(s1, s2))
- errx(1, "%s is mounted on %s", fname, mp->f_mntonname);
- }
-#endif
-}
-
-/*
- * Get a standard format.
- */
-static void getstdfmt(const char *fmt, struct bpb *bpb)
-{
- u_int x, i;
-
- x = sizeof(stdfmt) / sizeof(stdfmt[0]);
- for (i = 0; i < x && strcmp(fmt, stdfmt[i].name); i++);
- if (i == x)
- errx(1, "%s: unknown standard format", fmt);
- *bpb = stdfmt[i].bpb;
-}
-
-/*
- * Get disk slice, partition, and geometry information.
- */
-
-#ifdef __APPLE__
-static void getdiskinfo(__unused int fd, __unused const char* fname, __unused const char* dtype,
- __unused int oflag, __unused struct bpb* bpb) {}
-#elif ANDROID
-static void getdiskinfo(int fd, const char *fname, const char *dtype,
- __unused int oflag,struct bpb *bpb)
-{
- struct hd_geometry geom;
- u_long block_size;
-
- if (ioctl(fd, BLKSSZGET, &bpb->bps)) {
- fprintf(stderr, "Error getting bytes / sector (%s)\n", strerror(errno));
- exit(1);
- }
-
- ckgeom(fname, bpb->bps, "bytes/sector");
-
- if (ioctl(fd, BLKGETSIZE, &block_size)) {
- fprintf(stderr, "Error getting blocksize (%s)\n", strerror(errno));
- exit(1);
- }
-
- if (block_size > UINT32_MAX) {
- fprintf(stderr, "Error blocksize too large: %lu\n", block_size);
- exit(1);
- }
-
- bpb->bsec = (u_int)block_size;
-
- if (ioctl(fd, HDIO_GETGEO, &geom)) {
- fprintf(stderr, "Error getting gemoetry (%s) - trying sane values\n", strerror(errno));
- geom.heads = 64;
- geom.sectors = 63;
- }
-
- if (!geom.heads) {
- printf("Bogus heads from kernel - setting sane value\n");
- geom.heads = 64;
- }
-
- if (!geom.sectors) {
- printf("Bogus sectors from kernel - setting sane value\n");
- geom.sectors = 63;
- }
-
- bpb->spt = geom.sectors;
- ckgeom(fname, bpb->spt, "sectors/track");
-
- bpb->hds = geom.heads;
- ckgeom(fname, bpb->hds, "drive heads");
-}
-
-#else
-
-static void getdiskinfo(int fd, const char *fname, const char *dtype,
- __unused int oflag, struct bpb *bpb)
-{
- struct disklabel *lp, dlp;
- struct fd_type type;
- off_t ms, hs = 0;
-
- lp = NULL;
-
- /* If the user specified a disk type, try to use that */
- if (dtype != NULL) {
- lp = getdiskbyname(dtype);
- }
-
- /* Maybe it's a floppy drive */
- if (lp == NULL) {
- if (ioctl(fd, DIOCGMEDIASIZE, &ms) == -1) {
- struct stat st;
-
- if (fstat(fd, &st))
- err(1, "Cannot get disk size");
- /* create a fake geometry for a file image */
- ms = st.st_size;
- dlp.d_secsize = 512;
- dlp.d_nsectors = 63;
- dlp.d_ntracks = 255;
- dlp.d_secperunit = ms / dlp.d_secsize;
- lp = &dlp;
- } else if (ioctl(fd, FD_GTYPE, &type) != -1) {
- dlp.d_secsize = 128 << type.secsize;
- dlp.d_nsectors = type.sectrac;
- dlp.d_ntracks = type.heads;
- dlp.d_secperunit = ms / dlp.d_secsize;
- lp = &dlp;
- }
- }
-
- /* Maybe it's a fixed drive */
- if (lp == NULL) {
- if (ioctl(fd, DIOCGDINFO, &dlp) == -1) {
- if (bpb->bps == 0 && ioctl(fd, DIOCGSECTORSIZE, &dlp.d_secsize) == -1)
- errx(1, "Cannot get sector size, %s", strerror(errno));
-
- /* XXX Should we use bpb->bps if it's set? */
- dlp.d_secperunit = ms / dlp.d_secsize;
-
- if (bpb->spt == 0 && ioctl(fd, DIOCGFWSECTORS, &dlp.d_nsectors) == -1) {
- warnx("Cannot get number of sectors per track, %s", strerror(errno));
- dlp.d_nsectors = 63;
- }
- if (bpb->hds == 0 && ioctl(fd, DIOCGFWHEADS, &dlp.d_ntracks) == -1) {
- warnx("Cannot get number of heads, %s", strerror(errno));
- if (dlp.d_secperunit <= 63*1*1024)
- dlp.d_ntracks = 1;
- else if (dlp.d_secperunit <= 63*16*1024)
- dlp.d_ntracks = 16;
- else
- dlp.d_ntracks = 255;
- }
- }
-
- hs = (ms / dlp.d_secsize) - dlp.d_secperunit;
- lp = &dlp;
- }
-
- if (bpb->bps == 0)
- bpb->bps = ckgeom(fname, lp->d_secsize, "bytes/sector");
- if (bpb->spt == 0)
- bpb->spt = ckgeom(fname, lp->d_nsectors, "sectors/track");
- if (bpb->hds == 0)
- bpb->hds = ckgeom(fname, lp->d_ntracks, "drive heads");
- if (bpb->bsec == 0)
- bpb->bsec = lp->d_secperunit;
- if (bpb->hid == 0)
- bpb->hid = hs;
-}
-#endif
-
-/*
- * Print out BPB values.
- */
-static void print_bpb(struct bpb *bpb)
-{
- printf("bps=%u spc=%u res=%u nft=%u", bpb->bps, bpb->spc, bpb->res,
- bpb->nft);
- if (bpb->rde)
- printf(" rde=%u", bpb->rde);
- if (bpb->sec)
- printf(" sec=%u", bpb->sec);
- printf(" mid=%#x", bpb->mid);
- if (bpb->spf)
- printf(" spf=%u", bpb->spf);
- printf(" spt=%u hds=%u hid=%u", bpb->spt, bpb->hds, bpb->hid);
- if (bpb->bsec)
- printf(" bsec=%u", bpb->bsec);
- if (!bpb->spf) {
- printf(" bspf=%u rdcl=%u", bpb->bspf, bpb->rdcl);
- printf(" infs=");
- printf(bpb->infs == MAXU16 ? "%#x" : "%u", bpb->infs);
- printf(" bkbs=");
- printf(bpb->bkbs == MAXU16 ? "%#x" : "%u", bpb->bkbs);
- }
- printf("\n");
-}
-
-/*
- * Check a disk geometry value.
- */
-static u_int ckgeom(const char *fname, u_int val, const char *msg)
-{
- if (!val)
- errx(1, "%s: no default %s", fname, msg);
- if (val > MAXU16)
- errx(1, "%s: illegal %s %d", fname, msg, val);
- return val;
-}
-
-/*
- * Convert and check a numeric option argument.
- */
-static u_int argtou(const char *arg, u_int lo, u_int hi, const char *msg)
-{
- char *s;
- u_long x;
-
- errno = 0;
- x = strtoul(arg, &s, 0);
- if (errno || !*arg || *s || x < lo || x > hi)
- errx(1, "%s: bad %s", arg, msg);
- return x;
-}
-
-/*
- * Same for off_t, with optional skmgpP suffix
- */
-static off_t argtooff(const char *arg, const char *msg)
-{
- char *s;
- off_t x;
-
- x = strtoll(arg, &s, 0);
- /* allow at most one extra char */
- if (errno || x < 0 || (s[0] && s[1]) )
- errx(1, "%s: bad %s", arg, msg);
- if (*s) { /* the extra char is the multiplier */
- switch (*s) {
- default:
- errx(1, "%s: bad %s", arg, msg);
- /* notreached */
-
- case 's': /* sector */
- case 'S':
- x <<= 9; /* times 512 */
- break;
-
- case 'k': /* kilobyte */
- case 'K':
- x <<= 10; /* times 1024 */
- break;
-
- case 'm': /* megabyte */
- case 'M':
- x <<= 20; /* times 1024*1024 */
- break;
-
- case 'g': /* gigabyte */
- case 'G':
- x <<= 30; /* times 1024*1024*1024 */
- break;
-
- case 'p': /* partition start */
- case 'P': /* partition start */
- case 'l': /* partition length */
- case 'L': /* partition length */
- errx(1, "%s: not supported yet %s", arg, msg);
- /* notreached */
- }
- }
- return x;
-}
-
-/*
- * Check a volume label.
- */
-static int oklabel(const char *src)
-{
- int c, i;
-
- for (i = 0; i <= 11; i++) {
- c = (u_char)*src++;
- if (c < ' ' + !i || strchr("\"*+,./:;<=>?[\\]|", c))
- break;
- }
- return i && !c;
-}
-
-/*
- * Make a volume label.
- */
-static void mklabel(u_int8_t *dest, const char *src)
-{
- int c, i;
-
- for (i = 0; i < 11; i++) {
- c = *src ? toupper(*src++) : ' ';
- *dest++ = !i && c == '\xe5' ? 5 : c;
- }
-}
-
-/*
- * Copy string, padding with spaces.
- */
-static void setstr(u_int8_t *dest, const char *src, size_t len)
-{
- while (len--)
- *dest++ = *src ? *src++ : ' ';
-}
-
-/*
- * Print usage message.
- */
-static void usage(void)
-{
- fprintf(stderr,
- "usage: newfs_msdos [ -options ] special [disktype]\n"
- "where the options are:\n"
- "\t-@ create file system at specified offset\n"
- "\t-A Attempt to cluster align root directory\n"
- "\t-B get bootstrap from file\n"
- "\t-C create image file with specified size\n"
- "\t-F FAT type (12, 16, or 32)\n"
- "\t-I volume ID\n"
- "\t-L volume label\n"
- "\t-N don't create file system: just print out parameters\n"
- "\t-O OEM string\n"
- "\t-S bytes/sector\n"
- "\t-a sectors/FAT\n"
- "\t-b block size\n"
- "\t-c sectors/cluster\n"
- "\t-e root directory entries\n"
- "\t-f standard format\n"
- "\t-h drive heads\n"
- "\t-i file system info sector\n"
- "\t-k backup boot sector\n"
- "\t-m media descriptor\n"
- "\t-n number of FATs\n"
- "\t-o hidden sectors\n"
- "\t-r reserved sectors\n"
- "\t-s file system size (sectors)\n"
- "\t-u sectors/track\n");
- exit(1);
-}
diff --git a/toolbox/tools.h b/toolbox/tools.h
index 3d4bc3e..abeb3ef 100644
--- a/toolbox/tools.h
+++ b/toolbox/tools.h
@@ -1,4 +1,3 @@
TOOL(getevent)
TOOL(getprop)
-TOOL(newfs_msdos)
TOOL(toolbox)