Merge "adb: fix obsolete documentation."
diff --git a/adb/fdevent.cpp b/adb/fdevent.cpp
index f98c11a..098a39d 100644
--- a/adb/fdevent.cpp
+++ b/adb/fdevent.cpp
@@ -35,7 +35,6 @@
#include <unordered_map>
#include <vector>
-#include <android-base/file.h>
#include <android-base/logging.h>
#include <android-base/stringprintf.h>
#include <android-base/thread_annotations.h>
@@ -358,56 +357,10 @@
}
}
-static void fdevent_check_spin(uint64_t cycle) {
- // Check to see if we're spinning because we forgot about an fdevent
- // by keeping track of how long fdevents have been continuously pending.
- struct SpinCheck {
- fdevent* fde;
- std::chrono::steady_clock::time_point timestamp;
- uint64_t cycle;
- };
- static auto& g_continuously_pending = *new std::unordered_map<uint64_t, SpinCheck>();
-
- auto now = std::chrono::steady_clock::now();
- for (auto* fde : g_pending_list) {
- auto it = g_continuously_pending.find(fde->id);
- if (it == g_continuously_pending.end()) {
- g_continuously_pending[fde->id] =
- SpinCheck{.fde = fde, .timestamp = now, .cycle = cycle};
- } else {
- it->second.cycle = cycle;
- }
- }
-
- for (auto it = g_continuously_pending.begin(); it != g_continuously_pending.end();) {
- if (it->second.cycle != cycle) {
- it = g_continuously_pending.erase(it);
- } else {
- // Use an absurdly long window, since all we really care about is
- // getting a bugreport eventually.
- if (now - it->second.timestamp > std::chrono::minutes(5)) {
- LOG(FATAL_WITHOUT_ABORT) << "detected spin in fdevent: " << dump_fde(it->second.fde);
-#if defined(__linux__)
- int fd = it->second.fde->fd.get();
- std::string fd_path = android::base::StringPrintf("/proc/self/fd/%d", fd);
- std::string path;
- if (!android::base::Readlink(fd_path, &path)) {
- PLOG(FATAL_WITHOUT_ABORT) << "readlink of fd " << fd << " failed";
- }
- LOG(FATAL_WITHOUT_ABORT) << "fd " << fd << " = " << path;
-#endif
- abort();
- }
- ++it;
- }
- }
-}
-
void fdevent_loop() {
set_main_thread();
fdevent_run_setup();
- uint64_t cycle = 0;
while (true) {
if (terminate_loop) {
return;
@@ -417,8 +370,6 @@
fdevent_process();
- fdevent_check_spin(cycle++);
-
while (!g_pending_list.empty()) {
fdevent* fde = g_pending_list.front();
g_pending_list.pop_front();
diff --git a/adb/services.cpp b/adb/services.cpp
index b613d83..a757d90 100644
--- a/adb/services.cpp
+++ b/adb/services.cpp
@@ -181,29 +181,6 @@
kick_transport(t);
}
-static void spin_service(int fd, void*) {
- unique_fd sfd(fd);
-
- if (!__android_log_is_debuggable()) {
- WriteFdExactly(sfd.get(), "refusing to spin on non-debuggable build\n");
- return;
- }
-
- // A service that creates an fdevent that's always pending, and then ignores it.
- unique_fd pipe_read, pipe_write;
- if (!Pipe(&pipe_read, &pipe_write)) {
- WriteFdExactly(sfd.get(), "failed to create pipe\n");
- return;
- }
-
- fdevent_run_on_main_thread([fd = pipe_read.release()]() {
- fdevent* fde = fdevent_create(fd, [](int, unsigned, void*) {}, nullptr);
- fdevent_add(fde, FDE_READ);
- });
-
- WriteFdExactly(sfd.get(), "spinning\n");
-}
-
int reverse_service(const char* command, atransport* transport) {
int s[2];
if (adb_socketpair(s)) {
@@ -351,8 +328,6 @@
reinterpret_cast<void*>(1));
} else if (!strcmp(name, "reconnect")) {
ret = create_service_thread("reconnect", reconnect_service, transport);
- } else if (!strcmp(name, "spin")) {
- ret = create_service_thread("spin", spin_service, nullptr);
#endif
}
if (ret >= 0) {
diff --git a/fs_mgr/libdm/Android.bp b/fs_mgr/libdm/Android.bp
index 672d401..f1b18dd 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"],
@@ -35,3 +36,16 @@
"liblog_headers",
],
}
+
+cc_test {
+ name: "libdm_test",
+ defaults: ["fs_mgr_defaults"],
+ static_libs: [
+ "libdm",
+ "libbase",
+ "liblog",
+ ],
+ srcs: [
+ "dm_test.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..adbe820
--- /dev/null
+++ b/fs_mgr/libdm/dm_test.cpp
@@ -0,0 +1,37 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <map>
+
+#include <gtest/gtest.h>
+#include <libdm/dm.h>
+
+using namespace std;
+using namespace android::dm;
+
+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());
+}
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..31b6a70
--- /dev/null
+++ b/fs_mgr/libdm/include/libdm/dm_target.h
@@ -0,0 +1,108 @@
+/*
+ * 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;
+
+ private:
+ std::string block_device_;
+ uint64_t physical_sector_;
+};
+
+} // namespace dm
+} // namespace android
+
+#endif /* _LIBDM_DMTARGET_H_ */
diff --git a/fs_mgr/liblp/Android.bp b/fs_mgr/liblp/Android.bp
index 0ca8938..f7086a8 100644
--- a/fs_mgr/liblp/Android.bp
+++ b/fs_mgr/liblp/Android.bp
@@ -42,3 +42,20 @@
],
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
index a084893..0e4838c 100644
--- a/fs_mgr/liblp/builder.cpp
+++ b/fs_mgr/liblp/builder.cpp
@@ -287,7 +287,7 @@
DCHECK(first_sector <= geometry_.last_logical_sector);
// Note: the last usable sector is inclusive.
- if (first_sector + sectors_needed > geometry_.last_logical_sector) {
+ if (geometry_.last_logical_sector + 1 - first_sector < sectors_needed) {
LERROR << "Not enough free space to expand partition: " << partition->name();
return false;
}
@@ -347,5 +347,9 @@
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
index fb982e2..671a3bd 100644
--- a/fs_mgr/liblp/include/liblp/builder.h
+++ b/fs_mgr/liblp/include/liblp/builder.h
@@ -153,6 +153,9 @@
// 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);
diff --git a/fs_mgr/liblp/include/liblp/metadata_format.h b/fs_mgr/liblp/include/liblp/metadata_format.h
index 6a2c655..8522435 100644
--- a/fs_mgr/liblp/include/liblp/metadata_format.h
+++ b/fs_mgr/liblp/include/liblp/metadata_format.h
@@ -46,6 +46,7 @@
* 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. */
diff --git a/fs_mgr/liblp/include/liblp/reader.h b/fs_mgr/liblp/include/liblp/reader.h
index e7fa46d..982fe65 100644
--- a/fs_mgr/liblp/include/liblp/reader.h
+++ b/fs_mgr/liblp/include/liblp/reader.h
@@ -29,13 +29,16 @@
// 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
diff --git a/fs_mgr/liblp/include/liblp/writer.h b/fs_mgr/liblp/include/liblp/writer.h
index 02fb21f..efa409d 100644
--- a/fs_mgr/liblp/include/liblp/writer.h
+++ b/fs_mgr/liblp/include/liblp/writer.h
@@ -42,10 +42,13 @@
// 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
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
index 328fe37..7938186 100644
--- a/fs_mgr/liblp/reader.cpp
+++ b/fs_mgr/liblp/reader.cpp
@@ -76,7 +76,7 @@
// 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.
-static bool ReadLogicalPartitionGeometry(int fd, LpMetadataGeometry* geometry) {
+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) {
@@ -236,43 +236,51 @@
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;
- }
+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;
}
- if (std::unique_ptr<LpMetadata> metadata = ParseMetadata(fd)) {
- return metadata;
+ 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);
}
- // Next try the backup copy.
- offset = GetBackupMetadataOffset(geometry, slot_number);
- if (SeekFile64(fd, offset, SEEK_END) < 0) {
- PERROR << __PRETTY_FUNCTION__ << "lseek failed: offset " << offset;
- return nullptr;
+ if (metadata) {
+ metadata->geometry = geometry;
}
- return ParseMetadata(fd);
+ return metadata;
}
-std::unique_ptr<LpMetadata> ReadFromImageFile(const char* file) {
- android::base::unique_fd fd(open(file, O_RDONLY));
+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: " << file;
+ 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;
@@ -289,6 +297,15 @@
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
diff --git a/fs_mgr/liblp/utility.cpp b/fs_mgr/liblp/utility.cpp
index 217d802..5310cab 100644
--- a/fs_mgr/liblp/utility.cpp
+++ b/fs_mgr/liblp/utility.cpp
@@ -84,7 +84,7 @@
// macro to assist with buffer sizing.
static const size_t kGuidLen = 36;
char buffer[kGuidLen + 1];
- uuid_unparse(partition.guid, buffer);
+ uuid_unparse_upper(partition.guid, buffer);
return buffer;
}
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
index 6a9c124..89cbabd 100644
--- a/fs_mgr/liblp/writer.cpp
+++ b/fs_mgr/liblp/writer.cpp
@@ -124,14 +124,8 @@
return true;
}
-bool WritePartitionTable(const char* block_device, const LpMetadata& metadata, SyncMode sync_mode,
+bool WritePartitionTable(int fd, 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;
- }
-
uint64_t size;
if (!GetDescriptorSize(fd, &size)) {
return false;
@@ -142,7 +136,7 @@
// 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(block_device, &old_geometry)) {
+ if (!ReadLogicalPartitionGeometry(fd, &old_geometry)) {
return false;
}
if (!CompareGeometry(geometry, old_geometry)) {
@@ -174,8 +168,7 @@
return false;
}
if (!android::base::WriteFully(fd, blob.data(), blob.size())) {
- PERROR << __PRETTY_FUNCTION__ << "write " << blob.size()
- << " bytes failed: " << block_device;
+ PERROR << __PRETTY_FUNCTION__ << "write " << blob.size() << " bytes failed";
return false;
}
if (SeekFile64(fd, -LP_METADATA_GEOMETRY_SIZE, SEEK_END) < 0) {
@@ -183,8 +176,7 @@
return false;
}
if (!android::base::WriteFully(fd, blob.data(), blob.size())) {
- PERROR << __PRETTY_FUNCTION__ << "backup write " << blob.size()
- << " bytes failed: " << block_device;
+ PERROR << __PRETTY_FUNCTION__ << "backup write " << blob.size() << " bytes failed";
return false;
}
}
@@ -196,8 +188,7 @@
return false;
}
if (!android::base::WriteFully(fd, blob.data(), blob.size())) {
- PERROR << __PRETTY_FUNCTION__ << "write " << blob.size()
- << " bytes failed: " << block_device;
+ PERROR << __PRETTY_FUNCTION__ << "write " << blob.size() << " bytes failed";
return false;
}
@@ -214,30 +205,43 @@
return false;
}
if (!android::base::WriteFully(fd, blob.data(), blob.size())) {
- PERROR << __PRETTY_FUNCTION__ << "backup write " << blob.size()
- << " bytes failed: " << block_device;
+ PERROR << __PRETTY_FUNCTION__ << "backup write " << blob.size() << " bytes failed";
return false;
}
return true;
}
-bool WriteToImageFile(const char* file, const LpMetadata& input) {
+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;
}
- if (!android::base::WriteFully(fd, everything.data(), everything.size())) {
- PERROR << __PRETTY_FUNCTION__ << "write " << everything.size() << " bytes failed: " << file;
- return false;
- }
- return true;
+ return WriteToImageFile(fd, input);
}
} // namespace fs_mgr
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/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/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/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/shell_and_utilities/Android.bp b/shell_and_utilities/Android.bp
index dfb88f7..7834dd5 100644
--- a/shell_and_utilities/Android.bp
+++ b/shell_and_utilities/Android.bp
@@ -1,25 +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