Merge "Add reserved AID ranges for all partitions"
diff --git a/adb/Android.bp b/adb/Android.bp
index f6aede8..6558b1b 100644
--- a/adb/Android.bp
+++ b/adb/Android.bp
@@ -436,7 +436,6 @@
shared_libs: [
"libbootloader_message",
"libmdnssd",
- "libext4_utils",
"libfec",
"libfs_mgr",
"libselinux",
diff --git a/base/include/android-base/expected.h b/base/include/android-base/expected.h
index 6153b77..030ef35e 100644
--- a/base/include/android-base/expected.h
+++ b/base/include/android-base/expected.h
@@ -256,7 +256,8 @@
expected& operator=(const expected& rhs) = default;
// Note for SFNAIE above applies to here as well
- expected& operator=(expected&& rhs) = default;
+ expected& operator=(expected&& rhs) noexcept(
+ std::is_nothrow_move_assignable_v<T>&& std::is_nothrow_move_assignable_v<E>) = default;
template <class U = T _ENABLE_IF(
!std::is_void_v<T> &&
@@ -542,7 +543,7 @@
expected& operator=(const expected& rhs) = default;
// Note for SFNAIE above applies to here as well
- expected& operator=(expected&& rhs) = default;
+ expected& operator=(expected&& rhs) noexcept(std::is_nothrow_move_assignable_v<E>) = default;
template<class G = E>
expected& operator=(const unexpected<G>& rhs) {
@@ -633,7 +634,7 @@
public:
// constructors
constexpr unexpected(const unexpected&) = default;
- constexpr unexpected(unexpected&&) = default;
+ constexpr unexpected(unexpected&&) noexcept(std::is_nothrow_move_constructible_v<E>) = default;
template <class Err = E _ENABLE_IF(
std::is_constructible_v<E, Err> &&
@@ -709,7 +710,8 @@
// assignment
constexpr unexpected& operator=(const unexpected&) = default;
- constexpr unexpected& operator=(unexpected&&) = default;
+ constexpr unexpected& operator=(unexpected&&) noexcept(std::is_nothrow_move_assignable_v<E>) =
+ default;
template<class Err = E>
constexpr unexpected& operator=(const unexpected<Err>& rhs) {
val_ = rhs.value();
diff --git a/bootstat/bootstat.cpp b/bootstat/bootstat.cpp
index 6936cc2..cd9fda3 100644
--- a/bootstat/bootstat.cpp
+++ b/bootstat/bootstat.cpp
@@ -140,13 +140,13 @@
{"mba_err", 13},
{"Watchdog", 14},
{"Panic", 15},
- {"power_key", 16}, // Mediatek
- {"power_on", 17},
+ {"power_key", 16}, // aliasReasons to cold,powerkey (Mediatek)
+ {"power_on", 17}, // aliasReasons to cold,powerkey
{"Reboot", 18},
{"rtc", 19},
{"edl", 20},
{"oem_pon1", 21},
- {"oem_powerkey", 22},
+ {"oem_powerkey", 22}, // aliasReasons to cold,powerkey
{"oem_unknown_reset", 23},
{"srto: HWWDT reset SC", 24},
{"srto: HWWDT reset platform", 25},
@@ -201,15 +201,15 @@
{"hard,hw_reset", 72},
{"shutdown,suspend", 73}, // Suspend to RAM
{"shutdown,hibernate", 74}, // Suspend to DISK
- {"power_on_key", 75},
- {"reboot_by_key", 76},
- {"wdt_by_pass_pwk", 77}, // Mediatek
- {"reboot_longkey", 78},
- {"powerkey", 79},
- {"usb", 80}, // Mediatek
- {"wdt", 81}, // Mediatek
- {"tool_by_pass_pwk", 82}, // Mediatek
- {"2sec_reboot", 83}, // Mediatek
+ {"power_on_key", 75}, // aliasReasons to cold,powerkey
+ {"reboot_by_key", 76}, // translated to reboot,by_key
+ {"wdt_by_pass_pwk", 77}, // Mediatek
+ {"reboot_longkey", 78}, // translated to reboot,longkey
+ {"powerkey", 79}, // aliasReasons to cold,powerkey
+ {"usb", 80}, // aliasReasons to cold,charger (Mediatek)
+ {"wdt", 81}, // Mediatek
+ {"tool_by_pass_pwk", 82}, // aliasReasons to reboot,tool (Mediatek)
+ {"2sec_reboot", 83}, // aliasReasons to cold,rtc,2sec (Mediatek)
{"reboot,by_key", 84},
{"reboot,longkey", 85},
{"reboot,2sec", 86}, // Deprecate in two years, replaced with cold,rtc,2sec
@@ -219,28 +219,28 @@
{"reboot,rescueparty", 90},
{"charge", 91},
{"oem_tz_crash", 92},
- {"uvlo", 93}, // aliasReasons converts to reboot,undervoltage
+ {"uvlo", 93}, // aliasReasons to reboot,undervoltage
{"oem_ps_hold", 94},
{"abnormal_reset", 95},
{"oemerr_unknown", 96},
{"reboot_fastboot_mode", 97},
{"watchdog_apps_bite", 98},
{"xpu_err", 99},
- {"power_on_usb", 100},
+ {"power_on_usb", 100}, // aliasReasons to cold,charger
{"watchdog_rpm", 101},
{"watchdog_nonsec", 102},
{"watchdog_apps_bark", 103},
{"reboot_dmverity_corrupted", 104},
- {"reboot_smpl", 105}, // aliasReasons converts to reboot,powerloss
+ {"reboot_smpl", 105}, // aliasReasons to reboot,powerloss
{"watchdog_sdi_apps_reset", 106},
- {"smpl", 107}, // aliasReasons converts to reboot,powerloss
+ {"smpl", 107}, // aliasReasons to reboot,powerloss
{"oem_modem_failed_to_powerup", 108},
{"reboot_normal", 109},
{"oem_lpass_cfg", 110},
{"oem_xpu_ns_error", 111},
- {"power_key_press", 112},
+ {"power_key_press", 112}, // aliasReasons to cold,powerkey
{"hardware_reset", 113},
- {"reboot_by_powerkey", 114},
+ {"reboot_by_powerkey", 114}, // aliasReasons to cold,powerkey (is this correct?)
{"reboot_verity", 115},
{"oem_rpm_undef_error", 116},
{"oem_crash_on_the_lk", 117},
@@ -250,7 +250,7 @@
{"factory_cable", 121},
{"oem_ar6320_failed_to_powerup", 122},
{"watchdog_rpm_bite", 123},
- {"power_on_cable", 124},
+ {"power_on_cable", 124}, // aliasReasons to cold,charger
{"reboot_unknown", 125},
{"wireless_charger", 126},
{"0x776655ff", 127},
@@ -839,12 +839,12 @@
// following table smaller.
static const std::vector<std::pair<const std::string, const std::string>> aliasReasons = {
{"watchdog", "wdog"},
- {"cold,powerkey", "powerkey|power_key|PowerKey"},
{"kernel_panic", "panic"},
{"shutdown,thermal", "thermal"},
{"warm,s3_wakeup", "s3_wakeup"},
{"hard,hw_reset", "hw_reset"},
- {"cold,charger", "usb"},
+ {"cold,charger", "usb|power_on_cable"},
+ {"cold,powerkey", "powerkey|power_key|PowerKey|power_on"},
{"cold,rtc", "rtc"},
{"cold,rtc,2sec", "2sec_reboot"},
{"!warm", "wdt_by_pass_pwk"}, // change flavour of blunt
diff --git a/fastboot/device/utility.cpp b/fastboot/device/utility.cpp
index 2ebd57d..e01e39b 100644
--- a/fastboot/device/utility.cpp
+++ b/fastboot/device/utility.cpp
@@ -61,7 +61,7 @@
LOG(ERROR) << "Could not map partition: " << partition_name;
return false;
}
- auto closer = [partition_name]() -> void { DestroyLogicalPartition(partition_name, 5s); };
+ auto closer = [partition_name]() -> void { DestroyLogicalPartition(partition_name); };
*handle = PartitionHandle(dm_path, std::move(closer));
return true;
}
diff --git a/fs_mgr/Android.bp b/fs_mgr/Android.bp
index 3d3503c..65f0eff 100644
--- a/fs_mgr/Android.bp
+++ b/fs_mgr/Android.bp
@@ -73,6 +73,7 @@
whole_static_libs: [
"liblogwrap",
"libdm",
+ "libext2_uuid",
"libfstab",
],
cppflags: [
diff --git a/fs_mgr/fs_mgr.cpp b/fs_mgr/fs_mgr.cpp
index 259f800..7a0d019 100644
--- a/fs_mgr/fs_mgr.cpp
+++ b/fs_mgr/fs_mgr.cpp
@@ -1331,7 +1331,7 @@
continue;
}
} else if ((current_entry.fs_mgr_flags.verify)) {
- if (!fs_mgr_teardown_verity(¤t_entry, true /* wait */)) {
+ if (!fs_mgr_teardown_verity(¤t_entry)) {
LERROR << "Failed to tear down verified partition on mount point: "
<< current_entry.mount_point;
ret |= FsMgrUmountStatus::ERROR_VERITY;
diff --git a/fs_mgr/fs_mgr_dm_linear.cpp b/fs_mgr/fs_mgr_dm_linear.cpp
index 1f21a71..04ba0bf 100644
--- a/fs_mgr/fs_mgr_dm_linear.cpp
+++ b/fs_mgr/fs_mgr_dm_linear.cpp
@@ -122,19 +122,9 @@
table.set_readonly(false);
}
std::string name = GetPartitionName(partition);
- if (!dm.CreateDevice(name, table)) {
+ if (!dm.CreateDevice(name, table, path, timeout_ms)) {
return false;
}
- if (!dm.GetDmDevicePathByName(name, path)) {
- return false;
- }
- if (timeout_ms > std::chrono::milliseconds::zero()) {
- if (!WaitForFile(*path, timeout_ms)) {
- DestroyLogicalPartition(name, {});
- LERROR << "Timed out waiting for device path: " << *path;
- return false;
- }
- }
LINFO << "Created logical partition " << name << " on device " << *path;
return true;
}
@@ -194,24 +184,16 @@
timeout_ms, path);
}
-bool UnmapDevice(const std::string& name, const std::chrono::milliseconds& timeout_ms) {
+bool UnmapDevice(const std::string& name) {
DeviceMapper& dm = DeviceMapper::Instance();
- std::string path;
- if (timeout_ms > std::chrono::milliseconds::zero()) {
- dm.GetDmDevicePathByName(name, &path);
- }
if (!dm.DeleteDevice(name)) {
return false;
}
- if (!path.empty() && !WaitForFileDeleted(path, timeout_ms)) {
- LERROR << "Timed out waiting for device path to unlink: " << path;
- return false;
- }
return true;
}
-bool DestroyLogicalPartition(const std::string& name, const std::chrono::milliseconds& timeout_ms) {
- if (!UnmapDevice(name, timeout_ms)) {
+bool DestroyLogicalPartition(const std::string& name) {
+ if (!UnmapDevice(name)) {
return false;
}
LINFO << "Unmapped logical partition " << name;
diff --git a/fs_mgr/fs_mgr_fstab.cpp b/fs_mgr/fs_mgr_fstab.cpp
index 31790b1..9a0f4fe 100644
--- a/fs_mgr/fs_mgr_fstab.cpp
+++ b/fs_mgr/fs_mgr_fstab.cpp
@@ -704,10 +704,9 @@
return true;
}
-// For GSI to skip mounting /product and /product_services, until there are
-// well-defined interfaces between them and /system. Otherwise, the GSI flashed
-// on /system might not be able to work with /product and /product_services.
-// When they're skipped here, /system/product and /system/product_services in
+// For GSI to skip mounting /product and /system_ext, until there are well-defined interfaces
+// between them and /system. Otherwise, the GSI flashed on /system might not be able to work with
+// /product and /system_ext. When they're skipped here, /system/product and /system/system_ext in
// GSI will be used.
bool SkipMountingPartitions(Fstab* fstab) {
constexpr const char kSkipMountConfig[] = "/system/etc/init/config/skip_mount.cfg";
diff --git a/fs_mgr/fs_mgr_overlayfs.cpp b/fs_mgr/fs_mgr_overlayfs.cpp
index 05ca5fc..ac15ce4 100644
--- a/fs_mgr/fs_mgr_overlayfs.cpp
+++ b/fs_mgr/fs_mgr_overlayfs.cpp
@@ -32,7 +32,6 @@
#include <unistd.h>
#include <algorithm>
-#include <map>
#include <memory>
#include <string>
#include <vector>
@@ -445,7 +444,7 @@
auto metadata = builder->Export();
if (metadata && UpdatePartitionTable(super_device, *metadata.get(), slot_number)) {
if (change) *change = true;
- if (!DestroyLogicalPartition(partition_name, 0s)) return false;
+ if (!DestroyLogicalPartition(partition_name)) return false;
} else {
LERROR << "delete partition " << overlay;
return false;
@@ -518,10 +517,166 @@
return ret;
}
+bool fs_mgr_overlayfs_set_shared_mount(const std::string& mount_point, bool shared_flag) {
+ auto ret = mount(nullptr, mount_point.c_str(), nullptr, shared_flag ? MS_SHARED : MS_PRIVATE,
+ nullptr);
+ if (ret) {
+ PERROR << "__mount(target=" << mount_point
+ << ",flag=" << (shared_flag ? "MS_SHARED" : "MS_PRIVATE") << ")=" << ret;
+ return false;
+ }
+ return true;
+}
+
+bool fs_mgr_overlayfs_move_mount(const std::string& source, const std::string& target) {
+ auto ret = mount(source.c_str(), target.c_str(), nullptr, MS_MOVE, nullptr);
+ if (ret) {
+ PERROR << "__mount(source=" << source << ",target=" << target << ",flag=MS_MOVE)=" << ret;
+ return false;
+ }
+ return true;
+}
+
+struct mount_info {
+ std::string mount_point;
+ bool shared_flag;
+};
+
+std::vector<mount_info> ReadMountinfoFromFile(const std::string& path) {
+ std::vector<mount_info> info;
+
+ auto file = std::unique_ptr<FILE, decltype(&fclose)>{fopen(path.c_str(), "re"), fclose};
+ if (!file) {
+ PERROR << __FUNCTION__ << "(): cannot open file: '" << path << "'";
+ return info;
+ }
+
+ ssize_t len;
+ size_t alloc_len = 0;
+ char* line = nullptr;
+ while ((len = getline(&line, &alloc_len, file.get())) != -1) {
+ /* if the last character is a newline, shorten the string by 1 byte */
+ if (line[len - 1] == '\n') {
+ line[len - 1] = '\0';
+ }
+
+ static constexpr char delim[] = " \t";
+ char* save_ptr;
+ if (!strtok_r(line, delim, &save_ptr)) {
+ LERROR << "Error parsing mount ID";
+ break;
+ }
+ if (!strtok_r(nullptr, delim, &save_ptr)) {
+ LERROR << "Error parsing parent ID";
+ break;
+ }
+ if (!strtok_r(nullptr, delim, &save_ptr)) {
+ LERROR << "Error parsing mount source";
+ break;
+ }
+ if (!strtok_r(nullptr, delim, &save_ptr)) {
+ LERROR << "Error parsing root";
+ break;
+ }
+
+ char* p;
+ if (!(p = strtok_r(nullptr, delim, &save_ptr))) {
+ LERROR << "Error parsing mount_point";
+ break;
+ }
+ mount_info entry = {p, false};
+
+ if (!strtok_r(nullptr, delim, &save_ptr)) {
+ LERROR << "Error parsing mount_flags";
+ break;
+ }
+
+ while ((p = strtok_r(nullptr, delim, &save_ptr))) {
+ if ((p[0] == '-') && (p[1] == '\0')) break;
+ if (android::base::StartsWith(p, "shared:")) entry.shared_flag = true;
+ }
+ if (!p) {
+ LERROR << "Error parsing fields";
+ break;
+ }
+ info.emplace_back(std::move(entry));
+ }
+
+ free(line);
+ if (info.empty()) {
+ LERROR << __FUNCTION__ << "(): failed to load mountinfo from : '" << path << "'";
+ }
+ return info;
+}
+
bool fs_mgr_overlayfs_mount(const std::string& mount_point) {
auto options = fs_mgr_get_overlayfs_options(mount_point);
if (options.empty()) return false;
+ auto retval = true;
+ auto save_errno = errno;
+
+ struct move_entry {
+ std::string mount_point;
+ std::string dir;
+ bool shared_flag;
+ };
+ std::vector<move_entry> move;
+ auto parent_private = false;
+ auto parent_made_private = false;
+ auto dev_private = false;
+ auto dev_made_private = false;
+ for (auto& entry : ReadMountinfoFromFile("/proc/self/mountinfo")) {
+ if ((entry.mount_point == mount_point) && !entry.shared_flag) {
+ parent_private = true;
+ }
+ if ((entry.mount_point == "/dev") && !entry.shared_flag) {
+ dev_private = true;
+ }
+
+ if (!android::base::StartsWith(entry.mount_point, mount_point + "/")) {
+ continue;
+ }
+ if (std::find_if(move.begin(), move.end(), [&entry](const auto& it) {
+ return android::base::StartsWith(entry.mount_point, it.mount_point + "/");
+ }) != move.end()) {
+ continue;
+ }
+
+ // use as the bound directory in /dev.
+ auto new_context = fs_mgr_get_context(entry.mount_point);
+ if (!new_context.empty() && setfscreatecon(new_context.c_str())) {
+ PERROR << "setfscreatecon " << new_context;
+ }
+ move_entry new_entry = {std::move(entry.mount_point), "/dev/TemporaryDir-XXXXXX",
+ entry.shared_flag};
+ const auto target = mkdtemp(new_entry.dir.data());
+ if (!target) {
+ retval = false;
+ save_errno = errno;
+ PERROR << "temporary directory for MS_BIND";
+ setfscreatecon(nullptr);
+ continue;
+ }
+ setfscreatecon(nullptr);
+
+ if (!parent_private && !parent_made_private) {
+ parent_made_private = fs_mgr_overlayfs_set_shared_mount(mount_point, false);
+ }
+ if (new_entry.shared_flag) {
+ new_entry.shared_flag = fs_mgr_overlayfs_set_shared_mount(new_entry.mount_point, false);
+ }
+ if (!fs_mgr_overlayfs_move_mount(new_entry.mount_point, new_entry.dir)) {
+ retval = false;
+ save_errno = errno;
+ if (new_entry.shared_flag) {
+ fs_mgr_overlayfs_set_shared_mount(new_entry.mount_point, true);
+ }
+ continue;
+ }
+ move.emplace_back(std::move(new_entry));
+ }
+
// hijack __mount() report format to help triage
auto report = "__mount(source=overlay,target="s + mount_point + ",type=overlay";
const auto opt_list = android::base::Split(options, ",");
@@ -536,12 +691,38 @@
auto ret = mount("overlay", mount_point.c_str(), "overlay", MS_RDONLY | MS_RELATIME,
options.c_str());
if (ret) {
+ retval = false;
+ save_errno = errno;
PERROR << report << ret;
- return false;
} else {
LINFO << report << ret;
- return true;
}
+
+ // Move submounts back.
+ for (const auto& entry : move) {
+ if (!dev_private && !dev_made_private) {
+ dev_made_private = fs_mgr_overlayfs_set_shared_mount("/dev", false);
+ }
+
+ if (!fs_mgr_overlayfs_move_mount(entry.dir, entry.mount_point)) {
+ retval = false;
+ save_errno = errno;
+ } else if (entry.shared_flag &&
+ !fs_mgr_overlayfs_set_shared_mount(entry.mount_point, true)) {
+ retval = false;
+ save_errno = errno;
+ }
+ rmdir(entry.dir.c_str());
+ }
+ if (dev_made_private) {
+ fs_mgr_overlayfs_set_shared_mount("/dev", true);
+ }
+ if (parent_made_private) {
+ fs_mgr_overlayfs_set_shared_mount(mount_point, true);
+ }
+
+ errno = save_errno;
+ return retval;
}
// Mount kScratchMountPoint
@@ -663,7 +844,7 @@
if (!android::base::EndsWith(name, suffix)) {
continue;
}
- if (dm.GetState(name) != DmDeviceState::INVALID && !DestroyLogicalPartition(name, 2s)) {
+ if (dm.GetState(name) != DmDeviceState::INVALID && !DestroyLogicalPartition(name)) {
continue;
}
builder->ResizePartition(builder->FindPartition(name), 0);
@@ -737,7 +918,7 @@
return false;
}
}
- if (!partition_create) DestroyLogicalPartition(partition_name, 10s);
+ if (!partition_create) DestroyLogicalPartition(partition_name);
changed = true;
*partition_exists = false;
}
diff --git a/fs_mgr/fs_mgr_priv.h b/fs_mgr/fs_mgr_priv.h
index 3a33cf3..c5e477c 100644
--- a/fs_mgr/fs_mgr_priv.h
+++ b/fs_mgr/fs_mgr_priv.h
@@ -97,10 +97,10 @@
bool fs_mgr_is_ext4(const std::string& blk_device);
bool fs_mgr_is_f2fs(const std::string& blk_device);
-bool fs_mgr_teardown_verity(android::fs_mgr::FstabEntry* fstab, bool wait);
+bool fs_mgr_teardown_verity(android::fs_mgr::FstabEntry* fstab);
namespace android {
namespace fs_mgr {
-bool UnmapDevice(const std::string& name, const std::chrono::milliseconds& timeout_ms);
+bool UnmapDevice(const std::string& name);
} // namespace fs_mgr
} // namespace android
diff --git a/fs_mgr/fs_mgr_verity.cpp b/fs_mgr/fs_mgr_verity.cpp
index be8077b..efa2180 100644
--- a/fs_mgr/fs_mgr_verity.cpp
+++ b/fs_mgr/fs_mgr_verity.cpp
@@ -547,9 +547,9 @@
return retval;
}
-bool fs_mgr_teardown_verity(FstabEntry* entry, bool wait) {
+bool fs_mgr_teardown_verity(FstabEntry* entry) {
const std::string mount_point(basename(entry->mount_point.c_str()));
- if (!android::fs_mgr::UnmapDevice(mount_point, wait ? 1000ms : 0ms)) {
+ if (!android::fs_mgr::UnmapDevice(mount_point)) {
return false;
}
LINFO << "Unmapped verity device " << mount_point;
diff --git a/fs_mgr/include/fs_mgr_dm_linear.h b/fs_mgr/include/fs_mgr_dm_linear.h
index f33fc02..a1dc2dc 100644
--- a/fs_mgr/include/fs_mgr_dm_linear.h
+++ b/fs_mgr/include/fs_mgr_dm_linear.h
@@ -68,7 +68,7 @@
// Destroy the block device for a logical partition, by name. If |timeout_ms|
// is non-zero, then this will block until the device path has been unlinked.
-bool DestroyLogicalPartition(const std::string& name, const std::chrono::milliseconds& timeout_ms);
+bool DestroyLogicalPartition(const std::string& name);
} // namespace fs_mgr
} // namespace android
diff --git a/fs_mgr/libdm/Android.bp b/fs_mgr/libdm/Android.bp
index 21255df..e429d9f 100644
--- a/fs_mgr/libdm/Android.bp
+++ b/fs_mgr/libdm/Android.bp
@@ -29,6 +29,9 @@
"loop_control.cpp",
],
+ static_libs: [
+ "libext2_uuid",
+ ],
header_libs: [
"libbase_headers",
"liblog_headers",
@@ -46,6 +49,7 @@
static_libs: [
"libdm",
"libbase",
+ "libext2_uuid",
"libfs_mgr",
"liblog",
],
diff --git a/fs_mgr/libdm/dm.cpp b/fs_mgr/libdm/dm.cpp
index d54b6ef..d56a4b1 100644
--- a/fs_mgr/libdm/dm.cpp
+++ b/fs_mgr/libdm/dm.cpp
@@ -20,12 +20,20 @@
#include <sys/sysmacros.h>
#include <sys/types.h>
+#include <functional>
+#include <thread>
+
+#include <android-base/file.h>
#include <android-base/logging.h>
#include <android-base/macros.h>
+#include <android-base/strings.h>
+#include <uuid/uuid.h>
namespace android {
namespace dm {
+using namespace std::literals;
+
DeviceMapper::DeviceMapper() : fd_(-1) {
fd_ = TEMP_FAILURE_RETRY(open("/dev/device-mapper", O_RDWR | O_CLOEXEC));
if (fd_ < 0) {
@@ -37,13 +45,13 @@
static DeviceMapper instance;
return instance;
}
+
// Creates a new device mapper device
-bool DeviceMapper::CreateDevice(const std::string& name) {
+bool DeviceMapper::CreateDevice(const std::string& name, const std::string& uuid) {
if (name.empty()) {
LOG(ERROR) << "Unnamed device mapper device creation is not supported";
return false;
}
-
if (name.size() >= DM_NAME_LEN) {
LOG(ERROR) << "[" << name << "] is too long to be device mapper name";
return false;
@@ -51,6 +59,9 @@
struct dm_ioctl io;
InitIo(&io, name);
+ if (!uuid.empty()) {
+ snprintf(io.uuid, sizeof(io.uuid), "%s", uuid.c_str());
+ }
if (ioctl(fd_, DM_DEV_CREATE, &io)) {
PLOG(ERROR) << "DM_DEV_CREATE failed for [" << name << "]";
@@ -67,16 +78,6 @@
}
bool DeviceMapper::DeleteDevice(const std::string& name) {
- if (name.empty()) {
- LOG(ERROR) << "Unnamed device mapper device creation is not supported";
- return false;
- }
-
- if (name.size() >= DM_NAME_LEN) {
- LOG(ERROR) << "[" << name << "] is too long to be device mapper name";
- return false;
- }
-
struct dm_ioctl io;
InitIo(&io, name);
@@ -93,9 +94,81 @@
return true;
}
-const std::unique_ptr<DmTable> DeviceMapper::table(const std::string& /* name */) const {
- // TODO(b/110035986): Return the table, as read from the kernel instead
- return nullptr;
+bool WaitForCondition(const std::function<bool()>& condition,
+ const std::chrono::milliseconds& timeout_ms) {
+ auto start_time = std::chrono::steady_clock::now();
+ while (true) {
+ if (condition()) return true;
+
+ std::this_thread::sleep_for(20ms);
+
+ auto now = std::chrono::steady_clock::now();
+ auto time_elapsed = std::chrono::duration_cast<std::chrono::milliseconds>(now - start_time);
+ if (time_elapsed > timeout_ms) return false;
+ }
+}
+
+static std::string GenerateUuid() {
+ uuid_t uuid_bytes;
+ uuid_generate(uuid_bytes);
+
+ char uuid_chars[37] = {};
+ uuid_unparse_lower(uuid_bytes, uuid_chars);
+
+ return std::string{uuid_chars};
+}
+
+bool DeviceMapper::CreateDevice(const std::string& name, const DmTable& table, std::string* path,
+ const std::chrono::milliseconds& timeout_ms) {
+ std::string uuid = GenerateUuid();
+ if (!CreateDevice(name, uuid)) {
+ return false;
+ }
+
+ // We use the unique path for testing whether the device is ready. After
+ // that, it's safe to use the dm-N path which is compatible with callers
+ // that expect it to be formatted as such.
+ std::string unique_path;
+ if (!LoadTableAndActivate(name, table) || !GetDeviceUniquePath(name, &unique_path) ||
+ !GetDmDevicePathByName(name, path)) {
+ DeleteDevice(name);
+ return false;
+ }
+
+ if (timeout_ms <= std::chrono::milliseconds::zero()) {
+ return true;
+ }
+
+ auto condition = [&]() -> bool {
+ // If the file exists but returns EPERM or something, we consider the
+ // condition met.
+ if (access(unique_path.c_str(), F_OK) != 0) {
+ if (errno == ENOENT) return false;
+ }
+ return true;
+ };
+ if (!WaitForCondition(condition, timeout_ms)) {
+ LOG(ERROR) << "Timed out waiting for device path: " << unique_path;
+ DeleteDevice(name);
+ return false;
+ }
+ return true;
+}
+
+bool DeviceMapper::GetDeviceUniquePath(const std::string& name, std::string* path) {
+ struct dm_ioctl io;
+ InitIo(&io, name);
+ if (ioctl(fd_, DM_DEV_STATUS, &io) < 0) {
+ PLOG(ERROR) << "Failed to get device path: " << name;
+ return false;
+ }
+
+ if (io.uuid[0] == '\0') {
+ LOG(ERROR) << "Device does not have a unique path: " << name;
+ return false;
+ }
+ *path = "/dev/block/mapper/by-uuid/"s + io.uuid;
+ return true;
}
DmDeviceState DeviceMapper::GetState(const std::string& name) const {
@@ -111,11 +184,8 @@
}
bool DeviceMapper::CreateDevice(const std::string& name, const DmTable& table) {
- if (!CreateDevice(name)) {
- return false;
- }
- if (!LoadTableAndActivate(name, table)) {
- DeleteDevice(name);
+ std::string ignore_path;
+ if (!CreateDevice(name, table, &ignore_path, 0ms)) {
return false;
}
return true;
diff --git a/fs_mgr/libdm/dm_test.cpp b/fs_mgr/libdm/dm_test.cpp
index c5881dd..7a834e2 100644
--- a/fs_mgr/libdm/dm_test.cpp
+++ b/fs_mgr/libdm/dm_test.cpp
@@ -52,10 +52,10 @@
public:
TempDevice(const std::string& name, const DmTable& table)
: dm_(DeviceMapper::Instance()), name_(name), valid_(false) {
- valid_ = dm_.CreateDevice(name, table);
+ valid_ = dm_.CreateDevice(name, table, &path_, 5s);
}
TempDevice(TempDevice&& other) noexcept
- : dm_(other.dm_), name_(other.name_), valid_(other.valid_) {
+ : dm_(other.dm_), name_(other.name_), path_(other.path_), valid_(other.valid_) {
other.valid_ = false;
}
~TempDevice() {
@@ -70,29 +70,7 @@
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;
- }
+ std::string path() const { return path_; }
const std::string& name() const { return name_; }
bool valid() const { return valid_; }
@@ -109,6 +87,7 @@
private:
DeviceMapper& dm_;
std::string name_;
+ std::string path_;
bool valid_;
};
@@ -139,7 +118,6 @@
TempDevice dev("libdm-test-dm-linear", table);
ASSERT_TRUE(dev.valid());
ASSERT_FALSE(dev.path().empty());
- ASSERT_TRUE(dev.WaitForUdev());
auto& dm = DeviceMapper::Instance();
@@ -290,7 +268,6 @@
origin_dev_ = std::make_unique<TempDevice>("libdm-test-dm-snapshot-origin", origin_table);
ASSERT_TRUE(origin_dev_->valid());
ASSERT_FALSE(origin_dev_->path().empty());
- ASSERT_TRUE(origin_dev_->WaitForUdev());
// chunk size = 4K blocks.
DmTable snap_table;
@@ -302,7 +279,6 @@
snapshot_dev_ = std::make_unique<TempDevice>("libdm-test-dm-snapshot", snap_table);
ASSERT_TRUE(snapshot_dev_->valid());
ASSERT_FALSE(snapshot_dev_->path().empty());
- ASSERT_TRUE(snapshot_dev_->WaitForUdev());
setup_ok_ = true;
}
diff --git a/fs_mgr/libdm/include/libdm/dm.h b/fs_mgr/libdm/include/libdm/dm.h
index 08376c0..9c0c2f3 100644
--- a/fs_mgr/libdm/include/libdm/dm.h
+++ b/fs_mgr/libdm/include/libdm/dm.h
@@ -25,6 +25,7 @@
#include <sys/sysmacros.h>
#include <unistd.h>
+#include <chrono>
#include <memory>
#include <string>
#include <utility>
@@ -73,10 +74,6 @@
// Returns 'true' on success, false otherwise.
bool DeleteDevice(const std::string& name);
- // Reads the device mapper table from the device with given anme and
- // returns it in a DmTable object.
- const std::unique_ptr<DmTable> table(const std::string& name) const;
-
// Returns the current state of the underlying device mapper device
// with given name.
// One of INVALID, SUSPENDED or ACTIVE.
@@ -84,6 +81,33 @@
// 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.
+ // After creation, |path| contains the result of calling
+ // GetDmDevicePathByName, and the path is guaranteed to exist. If after
+ // |timeout_ms| the path is not available, the device will be deleted and
+ // this function will return false.
+ //
+ // This variant must be used when depending on the device path. The
+ // following manual sequence should not be used:
+ //
+ // 1. CreateDevice(name, table)
+ // 2. GetDmDevicePathByName(name, &path)
+ // 3. fs_mgr::WaitForFile(path, <timeout>)
+ //
+ // This sequence has a race condition where, if another process deletes a
+ // device, CreateDevice may acquire the same path. When this happens, the
+ // WaitForFile() may early-return since ueventd has not yet processed all
+ // of the outstanding udev events. The caller may unexpectedly get an
+ // ENOENT on a system call using the affected path.
+ //
+ // If |timeout_ms| is 0ms, then this function will return true whether or
+ // not |path| is available. It is the caller's responsibility to ensure
+ // there are no races.
+ bool CreateDevice(const std::string& name, const DmTable& table, std::string* path,
+ const std::chrono::milliseconds& timeout_ms);
+
+ // Create a device and activate the given table, without waiting to acquire
+ // a valid path. If the caller will use GetDmDevicePathByName(), it should
+ // use the timeout variant above.
bool CreateDevice(const std::string& name, const DmTable& table);
// Loads the device mapper table from parameter into the underlying device
@@ -110,8 +134,21 @@
// Returns the path to the device mapper device node in '/dev' corresponding to
// 'name'. If the device does not exist, false is returned, and the path
// parameter is not set.
+ //
+ // This returns a path in the format "/dev/block/dm-N" that can be easily
+ // re-used with sysfs.
+ //
+ // WaitForFile() should not be used in conjunction with this call, since it
+ // could race with ueventd.
bool GetDmDevicePathByName(const std::string& name, std::string* path);
+ // Returns a device's unique path as generated by ueventd. This will return
+ // true as long as the device has been created, even if ueventd has not
+ // processed it yet.
+ //
+ // The formatting of this path is /dev/block/mapper/by-uuid/<uuid>.
+ bool GetDeviceUniquePath(const std::string& name, std::string* path);
+
// Returns the dev_t for the named device-mapper node.
bool GetDeviceNumber(const std::string& name, dev_t* dev);
@@ -158,18 +195,12 @@
// limit we are imposing here of 256.
static constexpr uint32_t kMaxPossibleDmDevices = 256;
+ bool CreateDevice(const std::string& name, const std::string& uuid = {});
bool GetTable(const std::string& name, uint32_t flags, std::vector<TargetInfo>* table);
-
void InitIo(struct dm_ioctl* io, const std::string& name = std::string()) const;
DeviceMapper();
- // 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/libfiemap_writer/.clang-format b/fs_mgr/libfiemap_writer/.clang-format
deleted file mode 120000
index 8b770a1..0000000
--- a/fs_mgr/libfiemap_writer/.clang-format
+++ /dev/null
@@ -1 +0,0 @@
-../../.clang-format-4
\ No newline at end of file
diff --git a/fs_mgr/libfiemap_writer/Android.bp b/fs_mgr/libfiemap_writer/Android.bp
deleted file mode 100644
index ed209aa..0000000
--- a/fs_mgr/libfiemap_writer/Android.bp
+++ /dev/null
@@ -1,64 +0,0 @@
-//
-// 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: "libfiemap_writer",
- defaults: ["fs_mgr_defaults"],
- recovery_available: true,
- export_include_dirs: ["include"],
- cflags: [
- "-D_FILE_OFFSET_BITS=64",
- ],
-
- srcs: [
- "fiemap_writer.cpp",
- "split_fiemap_writer.cpp",
- "utility.cpp",
- ],
-
- static_libs: [
- "libdm",
- "libext4_utils",
- ],
-
- header_libs: [
- "libbase_headers",
- "liblog_headers",
- ],
-}
-
-cc_test {
- name: "fiemap_writer_test",
- cflags: [
- "-D_FILE_OFFSET_BITS=64",
- ],
- static_libs: [
- "libbase",
- "libdm",
- "libfiemap_writer",
- "liblog",
- ],
-
- data: [
- "testdata/unaligned_file",
- "testdata/file_4k",
- "testdata/file_32k",
- ],
-
- srcs: [
- "fiemap_writer_test.cpp",
- ],
-}
diff --git a/fs_mgr/libfiemap_writer/Android.mk b/fs_mgr/libfiemap_writer/Android.mk
deleted file mode 100644
index 3c07b8e..0000000
--- a/fs_mgr/libfiemap_writer/Android.mk
+++ /dev/null
@@ -1,22 +0,0 @@
-#
-# Copyright (C) 2019 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.
-#
-
-LOCAL_PATH := $(call my-dir)
-
-include $(CLEAR_VARS)
-
-LOCAL_MODULE := VtsFiemapWriterTest
--include test/vts/tools/build/Android.host_config.mk
diff --git a/fs_mgr/libfiemap_writer/AndroidTest.xml b/fs_mgr/libfiemap_writer/AndroidTest.xml
deleted file mode 100644
index 08cff0e..0000000
--- a/fs_mgr/libfiemap_writer/AndroidTest.xml
+++ /dev/null
@@ -1,29 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!-- Copyright (C) 2019 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.
--->
-<configuration description="Config for VTS VtsFiemapWriterTest">
- <option name="config-descriptor:metadata" key="plan" value="vts-kernel" />
- <target_preparer class="com.android.compatibility.common.tradefed.targetprep.VtsFilePusher">
- <option name="abort-on-push-failure" value="false"/>
- <option name="push-group" value="HostDrivenTest.push"/>
- </target_preparer>
- <test class="com.android.tradefed.testtype.VtsMultiDeviceTest">
- <option name="test-module-name" value="VtsFiemapWriterTest"/>
- <option name="binary-test-source" value="_32bit::DATA/nativetest/fiemap_writer_test/fiemap_writer_test" />
- <option name="binary-test-source" value="_64bit::DATA/nativetest64/fiemap_writer_test/fiemap_writer_test" />
- <option name="binary-test-type" value="gtest"/>
- <option name="test-timeout" value="1m"/>
- </test>
-</configuration>
diff --git a/fs_mgr/libfiemap_writer/fiemap_writer.cpp b/fs_mgr/libfiemap_writer/fiemap_writer.cpp
deleted file mode 100644
index 0a3ba6c..0000000
--- a/fs_mgr/libfiemap_writer/fiemap_writer.cpp
+++ /dev/null
@@ -1,745 +0,0 @@
-/*
- * 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 <libfiemap_writer/fiemap_writer.h>
-
-#include <dirent.h>
-#include <fcntl.h>
-#include <linux/fs.h>
-#include <stdio.h>
-#include <sys/ioctl.h>
-#include <sys/stat.h>
-#include <sys/sysmacros.h>
-#include <sys/types.h>
-#include <sys/vfs.h>
-#include <unistd.h>
-
-#include <limits>
-#include <string>
-#include <utility>
-#include <vector>
-
-#include <android-base/file.h>
-#include <android-base/logging.h>
-#include <android-base/stringprintf.h>
-#include <android-base/strings.h>
-#include <android-base/unique_fd.h>
-#include <libdm/dm.h>
-
-namespace android {
-namespace fiemap_writer {
-
-using namespace android::dm;
-
-// We are expecting no more than 512 extents in a fiemap of the file we create.
-// If we find more, then it is treated as error for now.
-static constexpr const uint32_t kMaxExtents = 512;
-
-// TODO: Fallback to using fibmap if FIEMAP_EXTENT_MERGED is set.
-static constexpr const uint32_t kUnsupportedExtentFlags =
- FIEMAP_EXTENT_UNKNOWN | FIEMAP_EXTENT_UNWRITTEN | FIEMAP_EXTENT_DELALLOC |
- FIEMAP_EXTENT_NOT_ALIGNED | FIEMAP_EXTENT_DATA_INLINE | FIEMAP_EXTENT_DATA_TAIL |
- FIEMAP_EXTENT_UNWRITTEN | FIEMAP_EXTENT_SHARED | FIEMAP_EXTENT_MERGED;
-
-// Large file support must be enabled.
-static_assert(sizeof(off_t) == sizeof(uint64_t));
-
-static inline void cleanup(const std::string& file_path, bool created) {
- if (created) {
- unlink(file_path.c_str());
- }
-}
-
-static bool BlockDeviceToName(uint32_t major, uint32_t minor, std::string* bdev_name) {
- // The symlinks in /sys/dev/block point to the block device node under /sys/device/..
- // The directory name in the target corresponds to the name of the block device. We use
- // that to extract the block device name.
- // e.g for block device name 'ram0', there exists a symlink named '1:0' in /sys/dev/block as
- // follows.
- // 1:0 -> ../../devices/virtual/block/ram0
- std::string sysfs_path = ::android::base::StringPrintf("/sys/dev/block/%u:%u", major, minor);
- std::string sysfs_bdev;
-
- if (!::android::base::Readlink(sysfs_path, &sysfs_bdev)) {
- PLOG(ERROR) << "Failed to read link at: " << sysfs_path;
- return false;
- }
-
- *bdev_name = ::android::base::Basename(sysfs_bdev);
- // Paranoid sanity check to make sure we just didn't get the
- // input in return as-is.
- if (sysfs_bdev == *bdev_name) {
- LOG(ERROR) << "Malformed symlink for block device: " << sysfs_bdev;
- return false;
- }
-
- return true;
-}
-
-static bool ValidateDmTarget(const DeviceMapper::TargetInfo& target) {
- const auto& entry = target.spec;
- if (entry.sector_start != 0) {
- LOG(INFO) << "Stopping at target with non-zero starting sector";
- return false;
- }
-
- auto target_type = DeviceMapper::GetTargetType(entry);
- if (target_type == "bow" || target_type == "default-key" || target_type == "crypt") {
- return true;
- }
- if (target_type == "linear") {
- auto pieces = android::base::Split(target.data, " ");
- if (pieces[1] != "0") {
- LOG(INFO) << "Stopping at complex linear target with non-zero starting sector: "
- << pieces[1];
- return false;
- }
- return true;
- }
-
- LOG(INFO) << "Stopping at complex target type " << target_type;
- return false;
-}
-
-static bool DeviceMapperStackPop(const std::string& bdev, std::string* bdev_raw) {
- *bdev_raw = bdev;
-
- if (!::android::base::StartsWith(bdev, "dm-")) {
- // We are at the bottom of the device mapper stack.
- return true;
- }
-
- // Get the device name.
- auto dm_name_file = "/sys/block/" + bdev + "/dm/name";
- std::string dm_name;
- if (!android::base::ReadFileToString(dm_name_file, &dm_name)) {
- PLOG(ERROR) << "Could not read file: " << dm_name_file;
- return false;
- }
- dm_name = android::base::Trim(dm_name);
-
- auto& dm = DeviceMapper::Instance();
- std::vector<DeviceMapper::TargetInfo> table;
- if (!dm.GetTableInfo(dm_name, &table)) {
- LOG(ERROR) << "Could not read device-mapper table for " << dm_name << " at " << bdev;
- return false;
- }
-
- // The purpose of libfiemap_writer is to provide an extent-based view into
- // a file. This is difficult if devices are not layered in a 1:1 manner;
- // we would have to translate and break up extents based on the actual
- // block mapping. Since this is too complex, we simply stop processing
- // the device-mapper stack if we encounter a complex case.
- //
- // It is up to the caller to decide whether stopping at a virtual block
- // device is allowable. In most cases it is not, because we want either
- // "userdata" or an external volume. It is useful for tests however.
- // Callers can check by comparing the device number to that of userdata,
- // or by checking whether is a device-mapper node.
- if (table.size() > 1) {
- LOG(INFO) << "Stopping at complex table for " << dm_name << " at " << bdev;
- return true;
- }
- if (!ValidateDmTarget(table[0])) {
- return true;
- }
-
- auto dm_leaf_dir = "/sys/block/" + bdev + "/slaves";
- auto d = std::unique_ptr<DIR, decltype(&closedir)>(opendir(dm_leaf_dir.c_str()), closedir);
- if (d == nullptr) {
- PLOG(ERROR) << "Failed to open: " << dm_leaf_dir;
- return false;
- }
-
- struct dirent* de;
- uint32_t num_leaves = 0;
- std::string bdev_next = "";
- while ((de = readdir(d.get())) != nullptr) {
- if (!strcmp(de->d_name, ".") || !strcmp(de->d_name, "..")) {
- continue;
- }
-
- // We set the first name we find here
- if (bdev_next.empty()) {
- bdev_next = de->d_name;
- }
- num_leaves++;
- }
-
- // if we have more than one leaves, we return immediately. We can't continue to create the
- // file since we don't know how to write it out using fiemap, so it will be readable via the
- // underlying block devices later. The reader will also have to construct the same device mapper
- // target in order read the file out.
- if (num_leaves > 1) {
- LOG(ERROR) << "Found " << num_leaves << " leaf block devices under device mapper device "
- << bdev;
- return false;
- }
-
- // recursively call with the block device we found in order to pop the device mapper stack.
- return DeviceMapperStackPop(bdev_next, bdev_raw);
-}
-
-bool FiemapWriter::GetBlockDeviceForFile(const std::string& file_path, std::string* bdev_path,
- bool* uses_dm) {
- struct stat sb;
- if (stat(file_path.c_str(), &sb)) {
- PLOG(ERROR) << "Failed to get stat for: " << file_path;
- return false;
- }
-
- std::string bdev;
- if (!BlockDeviceToName(major(sb.st_dev), minor(sb.st_dev), &bdev)) {
- LOG(ERROR) << "Failed to get block device name for " << major(sb.st_dev) << ":"
- << minor(sb.st_dev);
- return false;
- }
-
- std::string bdev_raw;
- if (!DeviceMapperStackPop(bdev, &bdev_raw)) {
- LOG(ERROR) << "Failed to get the bottom of the device mapper stack for device: " << bdev;
- return false;
- }
-
- if (uses_dm) {
- *uses_dm = (bdev_raw != bdev);
- }
-
- LOG(DEBUG) << "Popped device (" << bdev_raw << ") from device mapper stack starting with ("
- << bdev << ")";
-
- *bdev_path = ::android::base::StringPrintf("/dev/block/%s", bdev_raw.c_str());
-
- // Make sure we are talking to a block device before calling it a success.
- if (stat(bdev_path->c_str(), &sb)) {
- PLOG(ERROR) << "Failed to get stat for block device: " << *bdev_path;
- return false;
- }
-
- if ((sb.st_mode & S_IFMT) != S_IFBLK) {
- PLOG(ERROR) << "File: " << *bdev_path << " is not a block device";
- return false;
- }
-
- return true;
-}
-
-static bool GetBlockDeviceSize(int bdev_fd, const std::string& bdev_path, uint64_t* bdev_size) {
- uint64_t size_in_bytes = 0;
- if (ioctl(bdev_fd, BLKGETSIZE64, &size_in_bytes)) {
- PLOG(ERROR) << "Failed to get total size for: " << bdev_path;
- return false;
- }
-
- *bdev_size = size_in_bytes;
-
- return true;
-}
-
-static uint64_t GetFileSize(const std::string& file_path) {
- struct stat sb;
- if (stat(file_path.c_str(), &sb)) {
- PLOG(ERROR) << "Failed to get size for file: " << file_path;
- return 0;
- }
-
- return sb.st_size;
-}
-
-static bool PerformFileChecks(const std::string& file_path, uint64_t file_size, uint64_t* blocksz,
- uint32_t* fs_type) {
- struct statfs64 sfs;
- if (statfs64(file_path.c_str(), &sfs)) {
- PLOG(ERROR) << "Failed to read file system status at: " << file_path;
- return false;
- }
-
- if (!sfs.f_bsize) {
- LOG(ERROR) << "Unsupported block size: " << sfs.f_bsize;
- return false;
- }
-
- // Check if the filesystem is of supported types.
- // Only ext4, f2fs, and vfat are tested and supported.
- switch (sfs.f_type) {
- case EXT4_SUPER_MAGIC:
- case F2FS_SUPER_MAGIC:
- case MSDOS_SUPER_MAGIC:
- break;
- default:
- LOG(ERROR) << "Unsupported file system type: 0x" << std::hex << sfs.f_type;
- return false;
- }
-
- uint64_t available_bytes = sfs.f_bsize * sfs.f_bavail;
- if (available_bytes <= file_size) {
- LOG(ERROR) << "Not enough free space in file system to create file of size : " << file_size;
- return false;
- }
-
- *blocksz = sfs.f_bsize;
- *fs_type = sfs.f_type;
- return true;
-}
-
-static bool FallocateFallback(int file_fd, uint64_t block_size, uint64_t file_size,
- const std::string& file_path,
- const std::function<bool(uint64_t, uint64_t)>& on_progress) {
- // Even though this is much faster than writing zeroes, it is still slow
- // enough that we need to fire the progress callback periodically. To
- // easily achieve this, we seek in chunks. We use 1000 chunks since
- // normally we only fire the callback on 1/1000th increments.
- uint64_t bytes_per_chunk = std::max(file_size / 1000, block_size);
-
- // Seek just to the end of each chunk and write a single byte, causing
- // the filesystem to allocate blocks.
- off_t cursor = 0;
- off_t end = static_cast<off_t>(file_size);
- while (cursor < end) {
- cursor = std::min(static_cast<off_t>(cursor + bytes_per_chunk), end);
- auto rv = TEMP_FAILURE_RETRY(lseek(file_fd, cursor - 1, SEEK_SET));
- if (rv < 0) {
- PLOG(ERROR) << "Failed to lseek " << file_path;
- return false;
- }
- if (rv != cursor - 1) {
- LOG(ERROR) << "Seek returned wrong offset " << rv << " for file " << file_path;
- return false;
- }
- char buffer[] = {0};
- if (!android::base::WriteFully(file_fd, buffer, 1)) {
- PLOG(ERROR) << "Write failed: " << file_path;
- return false;
- }
- if (on_progress && !on_progress(cursor, file_size)) {
- return false;
- }
- }
- return true;
-}
-
-static bool AllocateFile(int file_fd, const std::string& file_path, uint64_t blocksz,
- uint64_t file_size, unsigned int fs_type,
- std::function<bool(uint64_t, uint64_t)> on_progress) {
- // Reserve space for the file on the file system and write it out to make sure the extents
- // don't come back unwritten. Return from this function with the kernel file offset set to 0.
- // If the filesystem is f2fs, then we also PIN the file on disk to make sure the blocks
- // aren't moved around.
- switch (fs_type) {
- case EXT4_SUPER_MAGIC:
- case F2FS_SUPER_MAGIC:
- if (fallocate(file_fd, FALLOC_FL_ZERO_RANGE, 0, file_size)) {
- PLOG(ERROR) << "Failed to allocate space for file: " << file_path
- << " size: " << file_size;
- return false;
- }
- break;
- case MSDOS_SUPER_MAGIC:
- // fallocate() is not supported, and not needed, since VFAT does not support holes.
- // Instead we can perform a much faster allocation.
- return FallocateFallback(file_fd, blocksz, file_size, file_path, on_progress);
- default:
- LOG(ERROR) << "Missing fallocate() support for file system " << fs_type;
- return false;
- }
-
- // write zeroes in 'blocksz' byte increments until we reach file_size to make sure the data
- // blocks are actually written to by the file system and thus getting rid of the holes in the
- // file.
- auto buffer = std::unique_ptr<void, decltype(&free)>(calloc(1, blocksz), free);
- if (buffer == nullptr) {
- LOG(ERROR) << "failed to allocate memory for writing file";
- return false;
- }
-
- off64_t offset = lseek64(file_fd, 0, SEEK_SET);
- if (offset < 0) {
- PLOG(ERROR) << "Failed to seek at the beginning of : " << file_path;
- return false;
- }
-
- int permille = -1;
- while (offset < file_size) {
- if (!::android::base::WriteFully(file_fd, buffer.get(), blocksz)) {
- PLOG(ERROR) << "Failed to write" << blocksz << " bytes at offset" << offset
- << " in file " << file_path;
- return false;
- }
-
- offset += blocksz;
-
- // Don't invoke the callback every iteration - wait until a significant
- // chunk (here, 1/1000th) of the data has been processed.
- int new_permille = (static_cast<uint64_t>(offset) * 1000) / file_size;
- if (new_permille != permille && static_cast<uint64_t>(offset) != file_size) {
- if (on_progress && !on_progress(offset, file_size)) {
- return false;
- }
- permille = new_permille;
- }
- }
-
- if (lseek64(file_fd, 0, SEEK_SET) < 0) {
- PLOG(ERROR) << "Failed to reset offset at the beginning of : " << file_path;
- return false;
- }
-
- // flush all writes here ..
- if (fsync(file_fd)) {
- PLOG(ERROR) << "Failed to synchronize written file:" << file_path;
- return false;
- }
-
- // Send one last progress notification.
- if (on_progress && !on_progress(file_size, file_size)) {
- return false;
- }
- return true;
-}
-
-static bool PinFile(int file_fd, const std::string& file_path, uint32_t fs_type) {
- if (fs_type != F2FS_SUPER_MAGIC) {
- // No pinning necessary for ext4/msdos. The blocks, once allocated, are
- // expected to be fixed.
- return true;
- }
-
-// F2FS-specific ioctl
-// It requires the below kernel commit merged in v4.16-rc1.
-// 1ad71a27124c ("f2fs: add an ioctl to disable GC for specific file")
-// In android-4.4,
-// 56ee1e817908 ("f2fs: updates on v4.16-rc1")
-// In android-4.9,
-// 2f17e34672a8 ("f2fs: updates on v4.16-rc1")
-// In android-4.14,
-// ce767d9a55bc ("f2fs: updates on v4.16-rc1")
-#ifndef F2FS_IOC_SET_PIN_FILE
-#ifndef F2FS_IOCTL_MAGIC
-#define F2FS_IOCTL_MAGIC 0xf5
-#endif
-#define F2FS_IOC_SET_PIN_FILE _IOW(F2FS_IOCTL_MAGIC, 13, __u32)
-#endif
-
- uint32_t pin_status = 1;
- int error = ioctl(file_fd, F2FS_IOC_SET_PIN_FILE, &pin_status);
- if (error < 0) {
- if ((errno == ENOTTY) || (errno == ENOTSUP)) {
- PLOG(ERROR) << "Failed to pin file, not supported by kernel: " << file_path;
- } else {
- PLOG(ERROR) << "Failed to pin file: " << file_path;
- }
- return false;
- }
-
- return true;
-}
-
-static bool IsFilePinned(int file_fd, const std::string& file_path, uint32_t fs_type) {
- if (fs_type != F2FS_SUPER_MAGIC) {
- // No pinning necessary for ext4 or vfat. The blocks, once allocated,
- // are expected to be fixed.
- return true;
- }
-
-// F2FS-specific ioctl
-// It requires the below kernel commit merged in v4.16-rc1.
-// 1ad71a27124c ("f2fs: add an ioctl to disable GC for specific file")
-// In android-4.4,
-// 56ee1e817908 ("f2fs: updates on v4.16-rc1")
-// In android-4.9,
-// 2f17e34672a8 ("f2fs: updates on v4.16-rc1")
-// In android-4.14,
-// ce767d9a55bc ("f2fs: updates on v4.16-rc1")
-#ifndef F2FS_IOC_GET_PIN_FILE
-#ifndef F2FS_IOCTL_MAGIC
-#define F2FS_IOCTL_MAGIC 0xf5
-#endif
-#define F2FS_IOC_GET_PIN_FILE _IOR(F2FS_IOCTL_MAGIC, 14, __u32)
-#endif
-
- // f2fs: export FS_NOCOW_FL flag to user
- uint32_t flags;
- int error = ioctl(file_fd, FS_IOC_GETFLAGS, &flags);
- if (error < 0) {
- if ((errno == ENOTTY) || (errno == ENOTSUP)) {
- PLOG(ERROR) << "Failed to get flags, not supported by kernel: " << file_path;
- } else {
- PLOG(ERROR) << "Failed to get flags: " << file_path;
- }
- return false;
- }
- if (!(flags & FS_NOCOW_FL)) {
- LOG(ERROR) << "It is not pinned: " << file_path;
- return false;
- }
-
- // F2FS_IOC_GET_PIN_FILE returns the number of blocks moved.
- uint32_t moved_blocks_nr;
- error = ioctl(file_fd, F2FS_IOC_GET_PIN_FILE, &moved_blocks_nr);
- if (error < 0) {
- if ((errno == ENOTTY) || (errno == ENOTSUP)) {
- PLOG(ERROR) << "Failed to get file pin status, not supported by kernel: " << file_path;
- } else {
- PLOG(ERROR) << "Failed to get file pin status: " << file_path;
- }
- return false;
- }
-
- if (moved_blocks_nr) {
- LOG(ERROR) << moved_blocks_nr << " blocks moved in file " << file_path;
- }
- return moved_blocks_nr == 0;
-}
-
-bool FiemapWriter::HasPinnedExtents(const std::string& file_path) {
- android::base::unique_fd fd(open(file_path.c_str(), O_NOFOLLOW | O_CLOEXEC | O_RDONLY));
- if (fd < 0) {
- PLOG(ERROR) << "open: " << file_path;
- return false;
- }
-
- struct statfs64 sfs;
- if (fstatfs64(fd, &sfs)) {
- PLOG(ERROR) << "fstatfs64: " << file_path;
- return false;
- }
- return IsFilePinned(fd, file_path, sfs.f_type);
-}
-
-static bool ReadFiemap(int file_fd, const std::string& file_path,
- std::vector<struct fiemap_extent>* extents) {
- uint64_t fiemap_size =
- sizeof(struct fiemap_extent) + kMaxExtents * sizeof(struct fiemap_extent);
- auto buffer = std::unique_ptr<void, decltype(&free)>(calloc(1, fiemap_size), free);
- if (buffer == nullptr) {
- LOG(ERROR) << "Failed to allocate memory for fiemap";
- return false;
- }
-
- struct fiemap* fiemap = reinterpret_cast<struct fiemap*>(buffer.get());
- fiemap->fm_start = 0;
- fiemap->fm_length = UINT64_MAX;
- // make sure file is synced to disk before we read the fiemap
- fiemap->fm_flags = FIEMAP_FLAG_SYNC;
- fiemap->fm_extent_count = kMaxExtents;
-
- if (ioctl(file_fd, FS_IOC_FIEMAP, fiemap)) {
- PLOG(ERROR) << "Failed to get FIEMAP from the kernel for file: " << file_path;
- return false;
- }
-
- if (fiemap->fm_mapped_extents == 0) {
- LOG(ERROR) << "File " << file_path << " has zero extents";
- return false;
- }
-
- // Iterate through each extent read and make sure its valid before adding it to the vector
- bool last_extent_seen = false;
- struct fiemap_extent* extent = &fiemap->fm_extents[0];
- for (uint32_t i = 0; i < fiemap->fm_mapped_extents; i++, extent++) {
- // LogExtent(i + 1, *extent);
- if (extent->fe_flags & kUnsupportedExtentFlags) {
- LOG(ERROR) << "Extent " << i + 1 << " of file " << file_path
- << " has unsupported flags";
- extents->clear();
- return false;
- }
-
- if (extent->fe_flags & FIEMAP_EXTENT_LAST) {
- last_extent_seen = true;
- if (i != (fiemap->fm_mapped_extents - 1)) {
- LOG(WARNING) << "Extents are being received out-of-order";
- }
- }
- extents->emplace_back(std::move(*extent));
- }
-
- if (!last_extent_seen) {
- // The file is possibly too fragmented.
- if (fiemap->fm_mapped_extents == kMaxExtents) {
- LOG(ERROR) << "File is too fragmented, needs more than " << kMaxExtents << " extents.";
- }
- extents->clear();
- }
-
- return last_extent_seen;
-}
-
-static bool ReadFibmap(int file_fd, const std::string& file_path,
- std::vector<struct fiemap_extent>* extents) {
- struct stat s;
- if (fstat(file_fd, &s)) {
- PLOG(ERROR) << "Failed to stat " << file_path;
- return false;
- }
-
- unsigned int blksize;
- if (ioctl(file_fd, FIGETBSZ, &blksize) < 0) {
- PLOG(ERROR) << "Failed to get FIGETBSZ for " << file_path;
- return false;
- }
- if (!blksize) {
- LOG(ERROR) << "Invalid filesystem block size: " << blksize;
- return false;
- }
-
- uint64_t num_blocks = (s.st_size + blksize - 1) / blksize;
- if (num_blocks > std::numeric_limits<uint32_t>::max()) {
- LOG(ERROR) << "Too many blocks for FIBMAP (" << num_blocks << ")";
- return false;
- }
-
- for (uint32_t last_block, block_number = 0; block_number < num_blocks; block_number++) {
- uint32_t block = block_number;
- if (ioctl(file_fd, FIBMAP, &block)) {
- PLOG(ERROR) << "Failed to get FIBMAP for file " << file_path;
- return false;
- }
- if (!block) {
- LOG(ERROR) << "Logical block " << block_number << " is a hole, which is not supported";
- return false;
- }
-
- if (!extents->empty() && block == last_block + 1) {
- extents->back().fe_length += blksize;
- } else {
- extents->push_back(fiemap_extent{.fe_logical = block_number,
- .fe_physical = static_cast<uint64_t>(block) * blksize,
- .fe_length = static_cast<uint64_t>(blksize),
- .fe_flags = 0});
- }
- last_block = block;
- }
- return true;
-}
-
-FiemapUniquePtr FiemapWriter::Open(const std::string& file_path, uint64_t file_size, bool create,
- std::function<bool(uint64_t, uint64_t)> progress) {
- // if 'create' is false, open an existing file and do not truncate.
- int open_flags = O_RDWR | O_CLOEXEC;
- if (create) {
- if (access(file_path.c_str(), F_OK) == 0) {
- LOG(WARNING) << "File " << file_path << " already exists, truncating";
- }
- open_flags |= O_CREAT | O_TRUNC;
- }
- ::android::base::unique_fd file_fd(
- TEMP_FAILURE_RETRY(open(file_path.c_str(), open_flags, S_IRUSR | S_IWUSR)));
- if (file_fd < 0) {
- PLOG(ERROR) << "Failed to create file at: " << file_path;
- return nullptr;
- }
-
- std::string abs_path;
- if (!::android::base::Realpath(file_path, &abs_path)) {
- PLOG(ERROR) << "Invalid file path: " << file_path;
- cleanup(file_path, create);
- return nullptr;
- }
-
- std::string bdev_path;
- if (!GetBlockDeviceForFile(abs_path, &bdev_path)) {
- LOG(ERROR) << "Failed to get block dev path for file: " << file_path;
- cleanup(abs_path, create);
- return nullptr;
- }
-
- ::android::base::unique_fd bdev_fd(
- TEMP_FAILURE_RETRY(open(bdev_path.c_str(), O_RDONLY | O_CLOEXEC)));
- if (bdev_fd < 0) {
- PLOG(ERROR) << "Failed to open block device: " << bdev_path;
- cleanup(file_path, create);
- return nullptr;
- }
-
- uint64_t bdevsz;
- if (!GetBlockDeviceSize(bdev_fd, bdev_path, &bdevsz)) {
- LOG(ERROR) << "Failed to get block device size for : " << bdev_path;
- cleanup(file_path, create);
- return nullptr;
- }
-
- if (!create) {
- file_size = GetFileSize(abs_path);
- if (file_size == 0) {
- LOG(ERROR) << "Invalid file size of zero bytes for file: " << abs_path;
- return nullptr;
- }
- }
-
- uint64_t blocksz;
- uint32_t fs_type;
- if (!PerformFileChecks(abs_path, file_size, &blocksz, &fs_type)) {
- LOG(ERROR) << "Failed to validate file or file system for file:" << abs_path;
- cleanup(abs_path, create);
- return nullptr;
- }
-
- // Align up to the nearest block size.
- if (file_size % blocksz) {
- file_size += blocksz - (file_size % blocksz);
- }
-
- if (create) {
- if (!AllocateFile(file_fd, abs_path, blocksz, file_size, fs_type, std::move(progress))) {
- LOG(ERROR) << "Failed to allocate file: " << abs_path << " of size: " << file_size
- << " bytes";
- cleanup(abs_path, create);
- return nullptr;
- }
- }
-
- // f2fs may move the file blocks around.
- if (!PinFile(file_fd, abs_path, fs_type)) {
- cleanup(abs_path, create);
- LOG(ERROR) << "Failed to pin the file in storage";
- return nullptr;
- }
-
- // now allocate the FiemapWriter and start setting it up
- FiemapUniquePtr fmap(new FiemapWriter());
- switch (fs_type) {
- case EXT4_SUPER_MAGIC:
- case F2FS_SUPER_MAGIC:
- if (!ReadFiemap(file_fd, abs_path, &fmap->extents_)) {
- LOG(ERROR) << "Failed to read fiemap of file: " << abs_path;
- cleanup(abs_path, create);
- return nullptr;
- }
- break;
- case MSDOS_SUPER_MAGIC:
- if (!ReadFibmap(file_fd, abs_path, &fmap->extents_)) {
- LOG(ERROR) << "Failed to read fibmap of file: " << abs_path;
- cleanup(abs_path, create);
- return nullptr;
- }
- break;
- }
-
- fmap->file_path_ = abs_path;
- fmap->bdev_path_ = bdev_path;
- fmap->file_size_ = file_size;
- fmap->bdev_size_ = bdevsz;
- fmap->fs_type_ = fs_type;
- fmap->block_size_ = blocksz;
-
- LOG(VERBOSE) << "Successfully created FiemapWriter for file " << abs_path << " on block device "
- << bdev_path;
- return fmap;
-}
-
-} // namespace fiemap_writer
-} // namespace android
diff --git a/fs_mgr/libfiemap_writer/fiemap_writer_test.cpp b/fs_mgr/libfiemap_writer/fiemap_writer_test.cpp
deleted file mode 100644
index dda7dfd..0000000
--- a/fs_mgr/libfiemap_writer/fiemap_writer_test.cpp
+++ /dev/null
@@ -1,541 +0,0 @@
-/*
- * 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 <inttypes.h>
-#include <stdint.h>
-#include <stdlib.h>
-#include <string.h>
-#include <sys/mount.h>
-#include <sys/stat.h>
-#include <sys/types.h>
-#include <sys/vfs.h>
-#include <unistd.h>
-
-#include <string>
-
-#include <android-base/file.h>
-#include <android-base/logging.h>
-#include <android-base/stringprintf.h>
-#include <android-base/unique_fd.h>
-#include <gtest/gtest.h>
-#include <libdm/loop_control.h>
-#include <libfiemap_writer/fiemap_writer.h>
-#include <libfiemap_writer/split_fiemap_writer.h>
-
-#include "utility.h"
-
-namespace android {
-namespace fiemap_writer {
-
-using namespace std;
-using namespace std::string_literals;
-using namespace android::fiemap_writer;
-using unique_fd = android::base::unique_fd;
-using LoopDevice = android::dm::LoopDevice;
-
-std::string gTestDir;
-uint64_t testfile_size = 536870912; // default of 512MiB
-size_t gBlockSize = 0;
-
-class FiemapWriterTest : public ::testing::Test {
- protected:
- void SetUp() override {
- const ::testing::TestInfo* tinfo = ::testing::UnitTest::GetInstance()->current_test_info();
- testfile = gTestDir + "/"s + tinfo->name();
- }
-
- void TearDown() override { unlink(testfile.c_str()); }
-
- // name of the file we use for testing
- std::string testfile;
-};
-
-class SplitFiemapTest : public ::testing::Test {
- protected:
- void SetUp() override {
- const ::testing::TestInfo* tinfo = ::testing::UnitTest::GetInstance()->current_test_info();
- testfile = gTestDir + "/"s + tinfo->name();
- }
-
- void TearDown() override {
- std::string message;
- if (!SplitFiemap::RemoveSplitFiles(testfile, &message)) {
- cerr << "Could not remove all split files: " << message;
- }
- }
-
- // name of the file we use for testing
- std::string testfile;
-};
-
-TEST_F(FiemapWriterTest, CreateImpossiblyLargeFile) {
- // Try creating a file of size ~100TB but aligned to
- // 512 byte to make sure block alignment tests don't
- // fail.
- FiemapUniquePtr fptr = FiemapWriter::Open(testfile, 1099511627997184);
- EXPECT_EQ(fptr, nullptr);
- EXPECT_EQ(access(testfile.c_str(), F_OK), -1);
- EXPECT_EQ(errno, ENOENT);
-}
-
-TEST_F(FiemapWriterTest, CreateUnalignedFile) {
- // Try creating a file of size 4097 bytes which is guaranteed
- // to be unaligned to all known block sizes.
- FiemapUniquePtr fptr = FiemapWriter::Open(testfile, gBlockSize + 1);
- ASSERT_NE(fptr, nullptr);
- ASSERT_EQ(fptr->size(), gBlockSize * 2);
-}
-
-TEST_F(FiemapWriterTest, CheckFilePath) {
- FiemapUniquePtr fptr = FiemapWriter::Open(testfile, gBlockSize);
- ASSERT_NE(fptr, nullptr);
- EXPECT_EQ(fptr->size(), gBlockSize);
- EXPECT_EQ(fptr->file_path(), testfile);
- EXPECT_EQ(access(testfile.c_str(), F_OK), 0);
-}
-
-TEST_F(FiemapWriterTest, CheckFileSize) {
- // Create a large-ish file and test that the expected size matches.
- FiemapUniquePtr fptr = FiemapWriter::Open(testfile, 1024 * 1024 * 16);
- ASSERT_NE(fptr, nullptr);
-
- struct stat s;
- ASSERT_EQ(stat(testfile.c_str(), &s), 0);
- EXPECT_EQ(static_cast<uint64_t>(s.st_size), fptr->size());
-}
-
-TEST_F(FiemapWriterTest, CheckProgress) {
- std::vector<uint64_t> expected;
- size_t invocations = 0;
- auto callback = [&](uint64_t done, uint64_t total) -> bool {
- if (invocations >= expected.size()) {
- return false;
- }
- EXPECT_EQ(done, expected[invocations]);
- EXPECT_EQ(total, gBlockSize);
- invocations++;
- return true;
- };
-
- expected.push_back(gBlockSize);
-
- auto ptr = FiemapWriter::Open(testfile, gBlockSize, true, std::move(callback));
- EXPECT_NE(ptr, nullptr);
- EXPECT_EQ(invocations, expected.size());
-}
-
-TEST_F(FiemapWriterTest, CheckPinning) {
- auto ptr = FiemapWriter::Open(testfile, 4096);
- ASSERT_NE(ptr, nullptr);
- EXPECT_TRUE(FiemapWriter::HasPinnedExtents(testfile));
-}
-
-TEST_F(FiemapWriterTest, CheckBlockDevicePath) {
- FiemapUniquePtr fptr = FiemapWriter::Open(testfile, gBlockSize);
- EXPECT_EQ(fptr->size(), gBlockSize);
- EXPECT_EQ(fptr->bdev_path().find("/dev/block/"), size_t(0));
- EXPECT_EQ(fptr->bdev_path().find("/dev/block/dm-"), string::npos);
-}
-
-TEST_F(FiemapWriterTest, CheckFileCreated) {
- FiemapUniquePtr fptr = FiemapWriter::Open(testfile, 32768);
- ASSERT_NE(fptr, nullptr);
- unique_fd fd(open(testfile.c_str(), O_RDONLY));
- EXPECT_GT(fd, -1);
-}
-
-TEST_F(FiemapWriterTest, CheckFileSizeActual) {
- FiemapUniquePtr fptr = FiemapWriter::Open(testfile, testfile_size);
- ASSERT_NE(fptr, nullptr);
-
- struct stat sb;
- ASSERT_EQ(stat(testfile.c_str(), &sb), 0);
- EXPECT_GE(sb.st_size, testfile_size);
-}
-
-TEST_F(FiemapWriterTest, CheckFileExtents) {
- FiemapUniquePtr fptr = FiemapWriter::Open(testfile, testfile_size);
- ASSERT_NE(fptr, nullptr);
- EXPECT_GT(fptr->extents().size(), 0);
-}
-
-TEST_F(FiemapWriterTest, ExistingFile) {
- // Create the file.
- { ASSERT_NE(FiemapWriter::Open(testfile, gBlockSize), nullptr); }
- // Test that we can still open it.
- {
- auto ptr = FiemapWriter::Open(testfile, 0, false);
- ASSERT_NE(ptr, nullptr);
- EXPECT_GT(ptr->extents().size(), 0);
- }
-}
-
-TEST_F(FiemapWriterTest, FileDeletedOnError) {
- auto callback = [](uint64_t, uint64_t) -> bool { return false; };
- auto ptr = FiemapWriter::Open(testfile, gBlockSize, true, std::move(callback));
- EXPECT_EQ(ptr, nullptr);
- EXPECT_EQ(access(testfile.c_str(), F_OK), -1);
- EXPECT_EQ(errno, ENOENT);
-}
-
-TEST_F(FiemapWriterTest, MaxBlockSize) {
- ASSERT_GT(DetermineMaximumFileSize(testfile), 0);
-}
-
-TEST_F(FiemapWriterTest, FibmapBlockAddressing) {
- FiemapUniquePtr fptr = FiemapWriter::Open(testfile, gBlockSize);
- ASSERT_NE(fptr, nullptr);
-
- switch (fptr->fs_type()) {
- case F2FS_SUPER_MAGIC:
- case EXT4_SUPER_MAGIC:
- // Skip the test for FIEMAP supported filesystems. This is really
- // because f2fs/ext4 have caches that seem to defeat reading back
- // directly from the block device, and writing directly is too
- // dangerous.
- std::cout << "Skipping test, filesystem does not use FIBMAP\n";
- return;
- }
-
- bool uses_dm;
- std::string bdev_path;
- ASSERT_TRUE(FiemapWriter::GetBlockDeviceForFile(testfile, &bdev_path, &uses_dm));
-
- if (uses_dm) {
- // We could use a device-mapper wrapper here to bypass encryption, but
- // really this test is for FIBMAP correctness on VFAT (where encryption
- // is never used), so we don't bother.
- std::cout << "Skipping test, block device is metadata encrypted\n";
- return;
- }
-
- std::string data(fptr->size(), '\0');
- for (size_t i = 0; i < data.size(); i++) {
- data[i] = 'A' + static_cast<char>(data.size() % 26);
- }
-
- {
- unique_fd fd(open(testfile.c_str(), O_WRONLY | O_CLOEXEC));
- ASSERT_GE(fd, 0);
- ASSERT_TRUE(android::base::WriteFully(fd, data.data(), data.size()));
- ASSERT_EQ(fsync(fd), 0);
- }
-
- ASSERT_FALSE(fptr->extents().empty());
- const auto& first_extent = fptr->extents()[0];
-
- unique_fd bdev(open(fptr->bdev_path().c_str(), O_RDONLY | O_CLOEXEC));
- ASSERT_GE(bdev, 0);
-
- off_t where = first_extent.fe_physical;
- ASSERT_EQ(lseek(bdev, where, SEEK_SET), where);
-
- // Note: this will fail on encrypted folders.
- std::string actual(data.size(), '\0');
- ASSERT_GE(first_extent.fe_length, data.size());
- ASSERT_TRUE(android::base::ReadFully(bdev, actual.data(), actual.size()));
- EXPECT_EQ(memcmp(actual.data(), data.data(), data.size()), 0);
-}
-
-TEST_F(SplitFiemapTest, Create) {
- auto ptr = SplitFiemap::Create(testfile, 1024 * 768, 1024 * 32);
- ASSERT_NE(ptr, nullptr);
-
- auto extents = ptr->extents();
-
- // Destroy the fiemap, closing file handles. This should not delete them.
- ptr = nullptr;
-
- std::vector<std::string> files;
- ASSERT_TRUE(SplitFiemap::GetSplitFileList(testfile, &files));
- for (const auto& path : files) {
- EXPECT_EQ(access(path.c_str(), F_OK), 0);
- }
-
- ASSERT_GE(extents.size(), files.size());
-}
-
-TEST_F(SplitFiemapTest, Open) {
- {
- auto ptr = SplitFiemap::Create(testfile, 1024 * 768, 1024 * 32);
- ASSERT_NE(ptr, nullptr);
- }
-
- auto ptr = SplitFiemap::Open(testfile);
- ASSERT_NE(ptr, nullptr);
-
- auto extents = ptr->extents();
- ASSERT_GE(extents.size(), 24);
-}
-
-TEST_F(SplitFiemapTest, DeleteOnFail) {
- auto ptr = SplitFiemap::Create(testfile, 1024 * 1024 * 100, 1);
- ASSERT_EQ(ptr, nullptr);
-
- std::string first_file = testfile + ".0001";
- ASSERT_NE(access(first_file.c_str(), F_OK), 0);
- ASSERT_EQ(errno, ENOENT);
- ASSERT_NE(access(testfile.c_str(), F_OK), 0);
- ASSERT_EQ(errno, ENOENT);
-}
-
-static string ReadSplitFiles(const std::string& base_path, size_t num_files) {
- std::string result;
- for (int i = 0; i < num_files; i++) {
- std::string path = base_path + android::base::StringPrintf(".%04d", i);
- std::string data;
- if (!android::base::ReadFileToString(path, &data)) {
- return {};
- }
- result += data;
- }
- return result;
-}
-
-TEST_F(SplitFiemapTest, WriteWholeFile) {
- static constexpr size_t kChunkSize = 32768;
- static constexpr size_t kSize = kChunkSize * 3;
- auto ptr = SplitFiemap::Create(testfile, kSize, kChunkSize);
- ASSERT_NE(ptr, nullptr);
-
- auto buffer = std::make_unique<int[]>(kSize / sizeof(int));
- for (size_t i = 0; i < kSize / sizeof(int); i++) {
- buffer[i] = i;
- }
- ASSERT_TRUE(ptr->Write(buffer.get(), kSize));
-
- std::string expected(reinterpret_cast<char*>(buffer.get()), kSize);
- auto actual = ReadSplitFiles(testfile, 3);
- ASSERT_EQ(expected.size(), actual.size());
- EXPECT_EQ(memcmp(expected.data(), actual.data(), actual.size()), 0);
-}
-
-TEST_F(SplitFiemapTest, WriteFileInChunks1) {
- static constexpr size_t kChunkSize = 32768;
- static constexpr size_t kSize = kChunkSize * 3;
- auto ptr = SplitFiemap::Create(testfile, kSize, kChunkSize);
- ASSERT_NE(ptr, nullptr);
-
- auto buffer = std::make_unique<int[]>(kSize / sizeof(int));
- for (size_t i = 0; i < kSize / sizeof(int); i++) {
- buffer[i] = i;
- }
-
- // Write in chunks of 1000 (so some writes straddle the boundary of two
- // files).
- size_t bytes_written = 0;
- while (bytes_written < kSize) {
- size_t to_write = std::min(kSize - bytes_written, (size_t)1000);
- char* data = reinterpret_cast<char*>(buffer.get()) + bytes_written;
- ASSERT_TRUE(ptr->Write(data, to_write));
- bytes_written += to_write;
- }
-
- std::string expected(reinterpret_cast<char*>(buffer.get()), kSize);
- auto actual = ReadSplitFiles(testfile, 3);
- ASSERT_EQ(expected.size(), actual.size());
- EXPECT_EQ(memcmp(expected.data(), actual.data(), actual.size()), 0);
-}
-
-TEST_F(SplitFiemapTest, WriteFileInChunks2) {
- static constexpr size_t kChunkSize = 32768;
- static constexpr size_t kSize = kChunkSize * 3;
- auto ptr = SplitFiemap::Create(testfile, kSize, kChunkSize);
- ASSERT_NE(ptr, nullptr);
-
- auto buffer = std::make_unique<int[]>(kSize / sizeof(int));
- for (size_t i = 0; i < kSize / sizeof(int); i++) {
- buffer[i] = i;
- }
-
- // Write in chunks of 32KiB so every write is exactly at the end of the
- // current file.
- size_t bytes_written = 0;
- while (bytes_written < kSize) {
- size_t to_write = std::min(kSize - bytes_written, kChunkSize);
- char* data = reinterpret_cast<char*>(buffer.get()) + bytes_written;
- ASSERT_TRUE(ptr->Write(data, to_write));
- bytes_written += to_write;
- }
-
- std::string expected(reinterpret_cast<char*>(buffer.get()), kSize);
- auto actual = ReadSplitFiles(testfile, 3);
- ASSERT_EQ(expected.size(), actual.size());
- EXPECT_EQ(memcmp(expected.data(), actual.data(), actual.size()), 0);
-}
-
-TEST_F(SplitFiemapTest, WritePastEnd) {
- static constexpr size_t kChunkSize = 32768;
- static constexpr size_t kSize = kChunkSize * 3;
- auto ptr = SplitFiemap::Create(testfile, kSize, kChunkSize);
- ASSERT_NE(ptr, nullptr);
-
- auto buffer = std::make_unique<int[]>(kSize / sizeof(int));
- for (size_t i = 0; i < kSize / sizeof(int); i++) {
- buffer[i] = i;
- }
- ASSERT_TRUE(ptr->Write(buffer.get(), kSize));
- ASSERT_FALSE(ptr->Write(buffer.get(), kSize));
-}
-
-class VerifyBlockWritesExt4 : public ::testing::Test {
- // 2GB Filesystem and 4k block size by default
- static constexpr uint64_t block_size = 4096;
- static constexpr uint64_t fs_size = 2147483648;
-
- protected:
- void SetUp() override {
- fs_path = std::string(getenv("TMPDIR")) + "/ext4_2G.img";
- uint64_t count = fs_size / block_size;
- std::string dd_cmd =
- ::android::base::StringPrintf("/system/bin/dd if=/dev/zero of=%s bs=%" PRIu64
- " count=%" PRIu64 " > /dev/null 2>&1",
- fs_path.c_str(), block_size, count);
- std::string mkfs_cmd =
- ::android::base::StringPrintf("/system/bin/mkfs.ext4 -q %s", fs_path.c_str());
- // create mount point
- mntpoint = std::string(getenv("TMPDIR")) + "/fiemap_mnt";
- ASSERT_EQ(mkdir(mntpoint.c_str(), S_IRWXU), 0);
- // create file for the file system
- int ret = system(dd_cmd.c_str());
- ASSERT_EQ(ret, 0);
- // Get and attach a loop device to the filesystem we created
- LoopDevice loop_dev(fs_path);
- ASSERT_TRUE(loop_dev.valid());
- // create file system
- ret = system(mkfs_cmd.c_str());
- ASSERT_EQ(ret, 0);
-
- // mount the file system
- ASSERT_EQ(mount(loop_dev.device().c_str(), mntpoint.c_str(), "ext4", 0, nullptr), 0);
- }
-
- void TearDown() override {
- umount(mntpoint.c_str());
- rmdir(mntpoint.c_str());
- unlink(fs_path.c_str());
- }
-
- std::string mntpoint;
- std::string fs_path;
-};
-
-class VerifyBlockWritesF2fs : public ::testing::Test {
- // 2GB Filesystem and 4k block size by default
- static constexpr uint64_t block_size = 4096;
- static constexpr uint64_t fs_size = 2147483648;
-
- protected:
- void SetUp() override {
- fs_path = std::string(getenv("TMPDIR")) + "/f2fs_2G.img";
- uint64_t count = fs_size / block_size;
- std::string dd_cmd =
- ::android::base::StringPrintf("/system/bin/dd if=/dev/zero of=%s bs=%" PRIu64
- " count=%" PRIu64 " > /dev/null 2>&1",
- fs_path.c_str(), block_size, count);
- std::string mkfs_cmd =
- ::android::base::StringPrintf("/system/bin/make_f2fs -q %s", fs_path.c_str());
- // create mount point
- mntpoint = std::string(getenv("TMPDIR")) + "/fiemap_mnt";
- ASSERT_EQ(mkdir(mntpoint.c_str(), S_IRWXU), 0);
- // create file for the file system
- int ret = system(dd_cmd.c_str());
- ASSERT_EQ(ret, 0);
- // Get and attach a loop device to the filesystem we created
- LoopDevice loop_dev(fs_path);
- ASSERT_TRUE(loop_dev.valid());
- // create file system
- ret = system(mkfs_cmd.c_str());
- ASSERT_EQ(ret, 0);
-
- // mount the file system
- ASSERT_EQ(mount(loop_dev.device().c_str(), mntpoint.c_str(), "f2fs", 0, nullptr), 0);
- }
-
- void TearDown() override {
- umount(mntpoint.c_str());
- rmdir(mntpoint.c_str());
- unlink(fs_path.c_str());
- }
-
- std::string mntpoint;
- std::string fs_path;
-};
-
-bool DetermineBlockSize() {
- struct statfs s;
- if (statfs(gTestDir.c_str(), &s)) {
- std::cerr << "Could not call statfs: " << strerror(errno) << "\n";
- return false;
- }
- if (!s.f_bsize) {
- std::cerr << "Invalid block size: " << s.f_bsize << "\n";
- return false;
- }
-
- gBlockSize = s.f_bsize;
- return true;
-}
-
-} // namespace fiemap_writer
-} // namespace android
-
-using namespace android::fiemap_writer;
-
-int main(int argc, char** argv) {
- ::testing::InitGoogleTest(&argc, argv);
- if (argc > 1 && argv[1] == "-h"s) {
- cerr << "Usage: [test_dir] [file_size]\n";
- cerr << "\n";
- cerr << "Note: test_dir must be a writable, unencrypted directory.\n";
- exit(EXIT_FAILURE);
- }
- ::android::base::InitLogging(argv, ::android::base::StderrLogger);
-
- std::string root_dir = "/data/local/unencrypted";
- if (access(root_dir.c_str(), F_OK)) {
- root_dir = "/data";
- }
-
- std::string tempdir = root_dir + "/XXXXXX"s;
- if (!mkdtemp(tempdir.data())) {
- cerr << "unable to create tempdir on " << root_dir << "\n";
- exit(EXIT_FAILURE);
- }
- if (!android::base::Realpath(tempdir, &gTestDir)) {
- cerr << "unable to find realpath for " << tempdir;
- exit(EXIT_FAILURE);
- }
-
- if (argc > 2) {
- testfile_size = strtoull(argv[2], NULL, 0);
- if (testfile_size == ULLONG_MAX) {
- testfile_size = 512 * 1024 * 1024;
- }
- }
-
- if (!DetermineBlockSize()) {
- exit(EXIT_FAILURE);
- }
-
- auto result = RUN_ALL_TESTS();
-
- std::string cmd = "rm -rf " + gTestDir;
- system(cmd.c_str());
-
- return result;
-}
diff --git a/fs_mgr/libfiemap_writer/include/libfiemap_writer/fiemap_writer.h b/fs_mgr/libfiemap_writer/include/libfiemap_writer/fiemap_writer.h
deleted file mode 100644
index ee79262..0000000
--- a/fs_mgr/libfiemap_writer/include/libfiemap_writer/fiemap_writer.h
+++ /dev/null
@@ -1,118 +0,0 @@
-/*
- * 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.
- */
-
-#pragma once
-
-#include <linux/fiemap.h>
-#include <stdint.h>
-#include <sys/types.h>
-#include <unistd.h>
-
-#include <functional>
-#include <string>
-#include <vector>
-
-#include <android-base/unique_fd.h>
-
-namespace android {
-namespace fiemap_writer {
-
-class FiemapWriter;
-using FiemapUniquePtr = std::unique_ptr<FiemapWriter>;
-
-class FiemapWriter final {
- public:
- // Factory method for FiemapWriter.
- // The method returns FiemapUniquePtr that contains all the data necessary to be able to write
- // to the given file directly using raw block i/o. The optional progress callback will be
- // invoked, if create is true, while the file is being initialized. It receives the bytes
- // written and the number of total bytes. If the callback returns false, the operation will
- // fail.
- //
- // Note: when create is true, the file size will be aligned up to the nearest file system
- // block.
- static FiemapUniquePtr Open(const std::string& file_path, uint64_t file_size,
- bool create = true,
- std::function<bool(uint64_t, uint64_t)> progress = {});
-
- // Check that a file still has the same extents since it was last opened with FiemapWriter,
- // assuming the file was not resized outside of FiemapWriter. Returns false either on error
- // or if the file was not pinned.
- //
- // This will always return true on Ext4. On F2FS, it will return true if either of the
- // following cases are true:
- // - The file was never pinned.
- // - The file is pinned and has not been moved by the GC.
- // Thus, this method should only be called for pinned files (such as those returned by
- // FiemapWriter::Open).
- static bool HasPinnedExtents(const std::string& file_path);
-
- // Returns the underlying block device of a file. This will look past device-mapper layers
- // as long as each layer would not change block mappings (i.e., dm-crypt, dm-bow, and dm-
- // default-key tables are okay; dm-linear is not). If a mapping such as dm-linear is found,
- // it will be returned in place of any physical block device.
- //
- // It is the caller's responsibility to check whether the returned block device is acceptable.
- // Gsid, for example, will only accept /dev/block/by-name/userdata as the bottom device.
- // Callers can check the device name (dm- or loop prefix), inspect sysfs, or compare the major
- // number against a boot device.
- //
- // If device-mapper nodes were encountered, then |uses_dm| will be set to true.
- static bool GetBlockDeviceForFile(const std::string& file_path, std::string* bdev_path,
- bool* uses_dm = nullptr);
-
- ~FiemapWriter() = default;
-
- const std::string& file_path() const { return file_path_; };
- uint64_t size() const { return file_size_; };
- const std::string& bdev_path() const { return bdev_path_; };
- uint64_t block_size() const { return block_size_; };
- const std::vector<struct fiemap_extent>& extents() { return extents_; };
- uint32_t fs_type() const { return fs_type_; }
-
- // Non-copyable & Non-movable
- FiemapWriter(const FiemapWriter&) = delete;
- FiemapWriter& operator=(const FiemapWriter&) = delete;
- FiemapWriter& operator=(FiemapWriter&&) = delete;
- FiemapWriter(FiemapWriter&&) = delete;
-
- private:
- // Name of the file managed by this class.
- std::string file_path_;
- // Block device on which we have created the file.
- std::string bdev_path_;
-
- // Size in bytes of the file this class is writing
- uint64_t file_size_;
-
- // total size in bytes of the block device
- uint64_t bdev_size_;
-
- // Filesystem type where the file is being created.
- // See: <uapi/linux/magic.h> for filesystem magic numbers
- uint32_t fs_type_;
-
- // block size as reported by the kernel of the underlying block device;
- uint64_t block_size_;
-
- // This file's fiemap
- std::vector<struct fiemap_extent> extents_;
-
- FiemapWriter() = default;
-};
-
-} // namespace fiemap_writer
-} // namespace android
diff --git a/fs_mgr/libfiemap_writer/include/libfiemap_writer/split_fiemap_writer.h b/fs_mgr/libfiemap_writer/include/libfiemap_writer/split_fiemap_writer.h
deleted file mode 100644
index 7b977e1..0000000
--- a/fs_mgr/libfiemap_writer/include/libfiemap_writer/split_fiemap_writer.h
+++ /dev/null
@@ -1,97 +0,0 @@
-/*
- * Copyright (C) 2019 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 <stdint.h>
-
-#include <functional>
-#include <memory>
-#include <string>
-#include <vector>
-
-#include <android-base/unique_fd.h>
-
-#include "fiemap_writer.h"
-
-namespace android {
-namespace fiemap_writer {
-
-// Wrapper around FiemapWriter that is able to split images across files if
-// necessary.
-class SplitFiemap final {
- public:
- using ProgressCallback = std::function<bool(uint64_t, uint64_t)>;
-
- // Create a new split fiemap file. If |max_piece_size| is 0, the number of
- // pieces will be determined automatically by detecting the filesystem.
- // Otherwise, the file will be split evenly (with the remainder in the
- // final file).
- static std::unique_ptr<SplitFiemap> Create(const std::string& file_path, uint64_t file_size,
- uint64_t max_piece_size,
- ProgressCallback progress = {});
-
- // Open an existing split fiemap file.
- static std::unique_ptr<SplitFiemap> Open(const std::string& file_path);
-
- ~SplitFiemap();
-
- // Return a list of all files created for a split file.
- static bool GetSplitFileList(const std::string& file_path, std::vector<std::string>* list);
-
- // Destroy all components of a split file. If the root file does not exist,
- // this returns true and does not report an error.
- static bool RemoveSplitFiles(const std::string& file_path, std::string* message = nullptr);
-
- // Return whether all components of a split file still have pinned extents.
- bool HasPinnedExtents() const;
-
- // Helper method for writing data that spans files. Note there is no seek
- // method (yet); this starts at 0 and increments the position by |bytes|.
- bool Write(const void* data, uint64_t bytes);
-
- // Flush all writes to all split files.
- bool Flush();
-
- const std::vector<struct fiemap_extent>& extents();
- uint32_t block_size() const;
- uint64_t size() const { return total_size_; }
- const std::string& bdev_path() const;
-
- // Non-copyable & Non-movable
- SplitFiemap(const SplitFiemap&) = delete;
- SplitFiemap& operator=(const SplitFiemap&) = delete;
- SplitFiemap& operator=(SplitFiemap&&) = delete;
- SplitFiemap(SplitFiemap&&) = delete;
-
- private:
- SplitFiemap() = default;
- void AddFile(FiemapUniquePtr&& file);
-
- bool creating_ = false;
- std::string list_file_;
- std::vector<FiemapUniquePtr> files_;
- std::vector<struct fiemap_extent> extents_;
- uint64_t total_size_ = 0;
-
- // Most recently open file and position for Write().
- size_t cursor_index_ = 0;
- uint64_t cursor_file_pos_ = 0;
- android::base::unique_fd cursor_fd_;
-};
-
-} // namespace fiemap_writer
-} // namespace android
diff --git a/fs_mgr/libfiemap_writer/split_fiemap_writer.cpp b/fs_mgr/libfiemap_writer/split_fiemap_writer.cpp
deleted file mode 100644
index 16a82d2..0000000
--- a/fs_mgr/libfiemap_writer/split_fiemap_writer.cpp
+++ /dev/null
@@ -1,298 +0,0 @@
-/*
- * Copyright (C) 2019 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 <libfiemap_writer/split_fiemap_writer.h>
-
-#include <fcntl.h>
-#include <stdint.h>
-#include <sys/stat.h>
-#include <sys/types.h>
-#include <unistd.h>
-
-#include <memory>
-#include <string>
-#include <vector>
-
-#include <android-base/file.h>
-#include <android-base/logging.h>
-#include <android-base/stringprintf.h>
-#include <android-base/strings.h>
-#include <android-base/unique_fd.h>
-
-#include "utility.h"
-
-namespace android {
-namespace fiemap_writer {
-
-using android::base::unique_fd;
-
-// We use a four-digit suffix at the end of filenames.
-static const size_t kMaxFilePieces = 500;
-
-std::unique_ptr<SplitFiemap> SplitFiemap::Create(const std::string& file_path, uint64_t file_size,
- uint64_t max_piece_size,
- ProgressCallback progress) {
- if (!file_size) {
- LOG(ERROR) << "Cannot create a fiemap for a 0-length file: " << file_path;
- return nullptr;
- }
-
- if (!max_piece_size) {
- max_piece_size = DetermineMaximumFileSize(file_path);
- if (!max_piece_size) {
- LOG(ERROR) << "Could not determine maximum file size for " << file_path;
- return nullptr;
- }
- }
-
- // Call |progress| only when the total percentage would significantly change.
- int permille = -1;
- uint64_t total_bytes_written = 0;
- auto on_progress = [&](uint64_t written, uint64_t) -> bool {
- uint64_t actual_written = total_bytes_written + written;
- int new_permille = (actual_written * 1000) / file_size;
- if (new_permille != permille && actual_written < file_size) {
- if (progress && !progress(actual_written, file_size)) {
- return false;
- }
- permille = new_permille;
- }
- return true;
- };
-
- std::unique_ptr<SplitFiemap> out(new SplitFiemap());
- out->creating_ = true;
- out->list_file_ = file_path;
-
- // Create the split files.
- uint64_t remaining_bytes = file_size;
- while (remaining_bytes) {
- if (out->files_.size() >= kMaxFilePieces) {
- LOG(ERROR) << "Requested size " << file_size << " created too many split files";
- return nullptr;
- }
- std::string chunk_path =
- android::base::StringPrintf("%s.%04d", file_path.c_str(), (int)out->files_.size());
- uint64_t chunk_size = std::min(max_piece_size, remaining_bytes);
- auto writer = FiemapWriter::Open(chunk_path, chunk_size, true, on_progress);
- if (!writer) {
- return nullptr;
- }
-
- // To make sure the alignment doesn't create too much inconsistency, we
- // account the *actual* size, not the requested size.
- total_bytes_written += writer->size();
- // writer->size() is block size aligned and could be bigger than remaining_bytes
- // If remaining_bytes is bigger, set remaining_bytes to 0 to avoid underflow error.
- remaining_bytes = remaining_bytes > writer->size() ? (remaining_bytes - writer->size()) : 0;
- out->AddFile(std::move(writer));
- }
-
- // Create the split file list.
- unique_fd fd(open(out->list_file_.c_str(), O_CREAT | O_WRONLY | O_CLOEXEC, 0660));
- if (fd < 0) {
- PLOG(ERROR) << "Failed to open " << file_path;
- return nullptr;
- }
-
- for (const auto& writer : out->files_) {
- std::string line = android::base::Basename(writer->file_path()) + "\n";
- if (!android::base::WriteFully(fd, line.data(), line.size())) {
- PLOG(ERROR) << "Write failed " << file_path;
- return nullptr;
- }
- }
-
- // Unset this bit, so we don't unlink on destruction.
- out->creating_ = false;
- return out;
-}
-
-std::unique_ptr<SplitFiemap> SplitFiemap::Open(const std::string& file_path) {
- std::vector<std::string> files;
- if (!GetSplitFileList(file_path, &files)) {
- return nullptr;
- }
-
- std::unique_ptr<SplitFiemap> out(new SplitFiemap());
- out->list_file_ = file_path;
-
- for (const auto& file : files) {
- auto writer = FiemapWriter::Open(file, 0, false);
- if (!writer) {
- // Error was logged in Open().
- return nullptr;
- }
- out->AddFile(std::move(writer));
- }
- return out;
-}
-
-bool SplitFiemap::GetSplitFileList(const std::string& file_path, std::vector<std::string>* list) {
- // This is not the most efficient thing, but it is simple and recovering
- // the fiemap/fibmap is much more expensive.
- std::string contents;
- if (!android::base::ReadFileToString(file_path, &contents, true)) {
- PLOG(ERROR) << "Error reading file: " << file_path;
- return false;
- }
-
- std::vector<std::string> names = android::base::Split(contents, "\n");
- std::string dir = android::base::Dirname(file_path);
- for (const auto& name : names) {
- if (!name.empty()) {
- list->emplace_back(dir + "/" + name);
- }
- }
- return true;
-}
-
-bool SplitFiemap::RemoveSplitFiles(const std::string& file_path, std::string* message) {
- // Early exit if this does not exist, and do not report an error.
- if (access(file_path.c_str(), F_OK) && errno == ENOENT) {
- return true;
- }
-
- bool ok = true;
- std::vector<std::string> files;
- if (GetSplitFileList(file_path, &files)) {
- for (const auto& file : files) {
- ok &= android::base::RemoveFileIfExists(file, message);
- }
- }
- ok &= android::base::RemoveFileIfExists(file_path, message);
- return ok;
-}
-
-bool SplitFiemap::HasPinnedExtents() const {
- for (const auto& file : files_) {
- if (!FiemapWriter::HasPinnedExtents(file->file_path())) {
- return false;
- }
- }
- return true;
-}
-
-const std::vector<struct fiemap_extent>& SplitFiemap::extents() {
- if (extents_.empty()) {
- for (const auto& file : files_) {
- const auto& extents = file->extents();
- extents_.insert(extents_.end(), extents.begin(), extents.end());
- }
- }
- return extents_;
-}
-
-bool SplitFiemap::Write(const void* data, uint64_t bytes) {
- // Open the current file.
- FiemapWriter* file = files_[cursor_index_].get();
-
- const uint8_t* data_ptr = reinterpret_cast<const uint8_t*>(data);
- uint64_t bytes_remaining = bytes;
- while (bytes_remaining) {
- // How many bytes can we write into the current file?
- uint64_t file_bytes_left = file->size() - cursor_file_pos_;
- if (!file_bytes_left) {
- if (cursor_index_ == files_.size() - 1) {
- LOG(ERROR) << "write past end of file requested";
- return false;
- }
-
- // No space left in the current file, but we have more files to
- // use, so prep the next one.
- cursor_fd_ = {};
- cursor_file_pos_ = 0;
- file = files_[++cursor_index_].get();
- file_bytes_left = file->size();
- }
-
- // Open the current file if it's not open.
- if (cursor_fd_ < 0) {
- cursor_fd_.reset(open(file->file_path().c_str(), O_CLOEXEC | O_WRONLY));
- if (cursor_fd_ < 0) {
- PLOG(ERROR) << "open failed: " << file->file_path();
- return false;
- }
- CHECK(cursor_file_pos_ == 0);
- }
-
- if (!FiemapWriter::HasPinnedExtents(file->file_path())) {
- LOG(ERROR) << "file is no longer pinned: " << file->file_path();
- return false;
- }
-
- uint64_t bytes_to_write = std::min(file_bytes_left, bytes_remaining);
- if (!android::base::WriteFully(cursor_fd_, data_ptr, bytes_to_write)) {
- PLOG(ERROR) << "write failed: " << file->file_path();
- return false;
- }
- data_ptr += bytes_to_write;
- bytes_remaining -= bytes_to_write;
- cursor_file_pos_ += bytes_to_write;
- }
-
- // If we've reached the end of the current file, close it for sanity.
- if (cursor_file_pos_ == file->size()) {
- cursor_fd_ = {};
- }
- return true;
-}
-
-bool SplitFiemap::Flush() {
- for (const auto& file : files_) {
- unique_fd fd(open(file->file_path().c_str(), O_RDONLY | O_CLOEXEC));
- if (fd < 0) {
- PLOG(ERROR) << "open failed: " << file->file_path();
- return false;
- }
- if (fsync(fd)) {
- PLOG(ERROR) << "fsync failed: " << file->file_path();
- return false;
- }
- }
- return true;
-}
-
-SplitFiemap::~SplitFiemap() {
- if (!creating_) {
- return;
- }
-
- // We failed to finish creating, so unlink everything.
- unlink(list_file_.c_str());
- for (auto&& file : files_) {
- std::string path = file->file_path();
- file = nullptr;
-
- unlink(path.c_str());
- }
-}
-
-void SplitFiemap::AddFile(FiemapUniquePtr&& file) {
- total_size_ += file->size();
- files_.emplace_back(std::move(file));
-}
-
-uint32_t SplitFiemap::block_size() const {
- return files_[0]->block_size();
-}
-
-const std::string& SplitFiemap::bdev_path() const {
- return files_[0]->bdev_path();
-}
-
-} // namespace fiemap_writer
-} // namespace android
diff --git a/fs_mgr/libfiemap_writer/testdata/file_32k b/fs_mgr/libfiemap_writer/testdata/file_32k
deleted file mode 100644
index 12f3be4..0000000
--- a/fs_mgr/libfiemap_writer/testdata/file_32k
+++ /dev/null
Binary files differ
diff --git a/fs_mgr/libfiemap_writer/testdata/file_4k b/fs_mgr/libfiemap_writer/testdata/file_4k
deleted file mode 100644
index 08e7df1..0000000
--- a/fs_mgr/libfiemap_writer/testdata/file_4k
+++ /dev/null
Binary files differ
diff --git a/fs_mgr/libfiemap_writer/testdata/unaligned_file b/fs_mgr/libfiemap_writer/testdata/unaligned_file
deleted file mode 100644
index c107c26..0000000
--- a/fs_mgr/libfiemap_writer/testdata/unaligned_file
+++ /dev/null
Binary files differ
diff --git a/fs_mgr/libfiemap_writer/utility.cpp b/fs_mgr/libfiemap_writer/utility.cpp
deleted file mode 100644
index 192ec16..0000000
--- a/fs_mgr/libfiemap_writer/utility.cpp
+++ /dev/null
@@ -1,66 +0,0 @@
-/*
- * Copyright (C) 2019 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 <stdint.h>
-#include <sys/vfs.h>
-#include <unistd.h>
-
-#include <android-base/logging.h>
-#include <libfiemap_writer/fiemap_writer.h>
-
-namespace android {
-namespace fiemap_writer {
-
-uint64_t DetermineMaximumFileSize(const std::string& file_path) {
- // Create the smallest file possible (one block).
- auto writer = FiemapWriter::Open(file_path, 1);
- if (!writer) {
- return 0;
- }
-
- uint64_t result = 0;
- switch (writer->fs_type()) {
- case EXT4_SUPER_MAGIC:
- // The minimum is 16GiB, so just report that. If we wanted we could parse the
- // superblock and figure out if 64-bit support is enabled.
- result = 17179869184ULL;
- break;
- case F2FS_SUPER_MAGIC:
- // Formula is from https://www.kernel.org/doc/Documentation/filesystems/f2fs.txt
- // 4KB * (923 + 2 * 1018 + 2 * 1018 * 1018 + 1018 * 1018 * 1018) := 3.94TB.
- result = 4329690886144ULL;
- break;
- case MSDOS_SUPER_MAGIC:
- // 4GB-1, which we want aligned to the block size.
- result = 4294967295;
- result -= (result % writer->block_size());
- break;
- default:
- LOG(ERROR) << "Unknown file system type: " << writer->fs_type();
- break;
- }
-
- // Close and delete the temporary file.
- writer = nullptr;
- unlink(file_path.c_str());
-
- return result;
-}
-
-} // namespace fiemap_writer
-} // namespace android
diff --git a/fs_mgr/libfiemap_writer/utility.h b/fs_mgr/libfiemap_writer/utility.h
deleted file mode 100644
index 2d418da..0000000
--- a/fs_mgr/libfiemap_writer/utility.h
+++ /dev/null
@@ -1,30 +0,0 @@
-/*
- * Copyright (C) 2019 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 <string>
-
-namespace android {
-namespace fiemap_writer {
-
-// Given a file that will be created, determine the maximum size its containing
-// filesystem allows. Note this is a theoretical maximum size; free space is
-// ignored entirely.
-uint64_t DetermineMaximumFileSize(const std::string& file_path);
-
-} // namespace fiemap_writer
-} // namespace android
diff --git a/fs_mgr/libfs_avb/Android.bp b/fs_mgr/libfs_avb/Android.bp
index a3c76ab..414a186 100644
--- a/fs_mgr/libfs_avb/Android.bp
+++ b/fs_mgr/libfs_avb/Android.bp
@@ -61,6 +61,7 @@
"libavb",
"libavb_host_sysdeps",
"libdm",
+ "libext2_uuid",
"libfs_avb",
"libfstab",
"libgtest_host",
diff --git a/fs_mgr/libfs_avb/avb_util.cpp b/fs_mgr/libfs_avb/avb_util.cpp
index d9650f3..4505382 100644
--- a/fs_mgr/libfs_avb/avb_util.cpp
+++ b/fs_mgr/libfs_avb/avb_util.cpp
@@ -104,31 +104,23 @@
}
table.set_readonly(true);
+ std::chrono::milliseconds timeout = {};
+ if (wait_for_verity_dev) timeout = 1s;
+
+ std::string dev_path;
const std::string mount_point(Basename(fstab_entry->mount_point));
const std::string device_name(GetVerityDeviceName(*fstab_entry));
android::dm::DeviceMapper& dm = android::dm::DeviceMapper::Instance();
- if (!dm.CreateDevice(device_name, table)) {
+ if (!dm.CreateDevice(device_name, table, &dev_path, timeout)) {
LERROR << "Couldn't create verity device!";
return false;
}
- std::string dev_path;
- if (!dm.GetDmDevicePathByName(device_name, &dev_path)) {
- LERROR << "Couldn't get verity device path!";
- return false;
- }
-
// Marks the underlying block device as read-only.
SetBlockDeviceReadOnly(fstab_entry->blk_device);
// Updates fstab_rec->blk_device to verity device name.
fstab_entry->blk_device = dev_path;
-
- // Makes sure we've set everything up properly.
- if (wait_for_verity_dev && !WaitForFile(dev_path, 1s)) {
- return false;
- }
-
return true;
}
diff --git a/init/Android.bp b/init/Android.bp
index 86dcb4c..ee339dd 100644
--- a/init/Android.bp
+++ b/init/Android.bp
@@ -74,7 +74,6 @@
shared_libs: [
"libbacktrace",
"libbase",
- "libbinder",
"libbootloader_message",
"libcutils",
"libcrypto",
@@ -243,11 +242,12 @@
],
whole_static_libs: ["libcap"],
shared_libs: [
- "libprotobuf-cpp-lite",
- "libhidl-gen-utils",
- "libprocessgroup",
- "liblog",
"libcutils",
+ "libhidl-gen-utils",
+ "libjsoncpp",
+ "liblog",
+ "libprocessgroup",
+ "libprotobuf-cpp-lite",
],
srcs: [
"action.cpp",
diff --git a/init/Android.mk b/init/Android.mk
index 9017772..006e1bf 100644
--- a/init/Android.mk
+++ b/init/Android.mk
@@ -113,6 +113,7 @@
libunwindstack \
libbacktrace \
libmodprobe \
+ libext2_uuid \
LOCAL_SANITIZE := signed-integer-overflow
# First stage init is weird: it may start without stdout/stderr, and no /proc.
diff --git a/init/action.cpp b/init/action.cpp
index a169591..8cf7645 100644
--- a/init/action.cpp
+++ b/init/action.cpp
@@ -95,7 +95,7 @@
}
void Action::AddCommand(BuiltinFunction f, std::vector<std::string>&& args, int line) {
- commands_.emplace_back(f, false, std::move(args), line);
+ commands_.emplace_back(std::move(f), false, std::move(args), line);
}
std::size_t Action::NumCommands() const {
diff --git a/init/action_manager.cpp b/init/action_manager.cpp
index 985b8ad..541c8f2 100644
--- a/init/action_manager.cpp
+++ b/init/action_manager.cpp
@@ -47,7 +47,7 @@
void ActionManager::QueueBuiltinAction(BuiltinFunction func, const std::string& name) {
auto action = std::make_unique<Action>(true, nullptr, "<Builtin Action>", 0, name,
std::map<std::string, std::string>{});
- action->AddCommand(func, {name}, 0);
+ action->AddCommand(std::move(func), {name}, 0);
event_queue_.emplace(action.get());
actions_.emplace_back(std::move(action));
diff --git a/init/devices.cpp b/init/devices.cpp
index e8e6cd7..f6e453a 100644
--- a/init/devices.cpp
+++ b/init/devices.cpp
@@ -22,7 +22,6 @@
#include <unistd.h>
#include <chrono>
-#include <map>
#include <memory>
#include <string>
#include <thread>
@@ -111,24 +110,16 @@
// the supplied buffer with the dm module's instantiated name.
// If it doesn't start with a virtual block device, or there is some
// error, return false.
-static bool FindDmDevicePartition(const std::string& path, std::string* result) {
- result->clear();
+static bool FindDmDevice(const std::string& path, std::string* name, std::string* uuid) {
if (!StartsWith(path, "/devices/virtual/block/dm-")) return false;
- if (getpid() == 1) return false; // first_stage_init has no sepolicy needs
- static std::map<std::string, std::string> cache;
- // wait_for_file will not work, the content is also delayed ...
- for (android::base::Timer t; t.duration() < 200ms; std::this_thread::sleep_for(10ms)) {
- if (ReadFileToString("/sys" + path + "/dm/name", result) && !result->empty()) {
- // Got it, set cache with result, when node arrives
- cache[path] = *result = Trim(*result);
- return true;
- }
+ if (!ReadFileToString("/sys" + path + "/dm/name", name)) {
+ return false;
}
- auto it = cache.find(path);
- if ((it == cache.end()) || (it->second.empty())) return false;
- // Return cached results, when node goes away
- *result = it->second;
+ ReadFileToString("/sys" + path + "/dm/uuid", uuid);
+
+ *name = android::base::Trim(*name);
+ *uuid = android::base::Trim(*uuid);
return true;
}
@@ -325,6 +316,7 @@
std::string device;
std::string type;
std::string partition;
+ std::string uuid;
if (FindPlatformDevice(uevent.path, &device)) {
// Skip /devices/platform or /devices/ if present
@@ -342,8 +334,12 @@
type = "pci";
} else if (FindVbdDevicePrefix(uevent.path, &device)) {
type = "vbd";
- } else if (FindDmDevicePartition(uevent.path, &partition)) {
- return {"/dev/block/mapper/" + partition};
+ } else if (FindDmDevice(uevent.path, &partition, &uuid)) {
+ std::vector<std::string> symlinks = {"/dev/block/mapper/" + partition};
+ if (!uuid.empty()) {
+ symlinks.emplace_back("/dev/block/mapper/by-uuid/" + uuid);
+ }
+ return symlinks;
} else {
return {};
}
@@ -379,10 +375,41 @@
return links;
}
+static void RemoveDeviceMapperLinks(const std::string& devpath) {
+ std::vector<std::string> dirs = {
+ "/dev/block/mapper",
+ "/dev/block/mapper/by-uuid",
+ };
+ for (const auto& dir : dirs) {
+ if (access(dir.c_str(), F_OK) != 0) continue;
+
+ std::unique_ptr<DIR, decltype(&closedir)> dh(opendir(dir.c_str()), closedir);
+ if (!dh) {
+ PLOG(ERROR) << "Failed to open directory " << dir;
+ continue;
+ }
+
+ struct dirent* dp;
+ std::string link_path;
+ while ((dp = readdir(dh.get())) != nullptr) {
+ if (dp->d_type != DT_LNK) continue;
+
+ auto path = dir + "/" + dp->d_name;
+ if (Readlink(path, &link_path) && link_path == devpath) {
+ unlink(path.c_str());
+ }
+ }
+ }
+}
+
void DeviceHandler::HandleDevice(const std::string& action, const std::string& devpath, bool block,
int major, int minor, const std::vector<std::string>& links) const {
if (action == "add") {
MakeDevice(devpath, block, major, minor, links);
+ }
+
+ // We don't have full device-mapper information until a change event is fired.
+ if (action == "add" || (action == "change" && StartsWith(devpath, "/dev/block/dm-"))) {
for (const auto& link : links) {
if (!mkdir_recursive(Dirname(link), 0755)) {
PLOG(ERROR) << "Failed to create directory " << Dirname(link);
@@ -401,6 +428,9 @@
}
if (action == "remove") {
+ if (StartsWith(devpath, "/dev/block/dm-")) {
+ RemoveDeviceMapperLinks(devpath);
+ }
for (const auto& link : links) {
std::string link_path;
if (Readlink(link, &link_path) && link_path == devpath) {
diff --git a/init/devices.h b/init/devices.h
index 9d39eaa..442c53f 100644
--- a/init/devices.h
+++ b/init/devices.h
@@ -84,9 +84,9 @@
};
Subsystem() {}
- Subsystem(const std::string& name) : name_(name) {}
- Subsystem(const std::string& name, DevnameSource source, const std::string& dir_name)
- : name_(name), devname_source_(source), dir_name_(dir_name) {}
+ Subsystem(std::string name) : name_(std::move(name)) {}
+ Subsystem(std::string name, DevnameSource source, std::string dir_name)
+ : name_(std::move(name)), devname_source_(source), dir_name_(std::move(dir_name)) {}
// Returns the full path for a uevent of a device that is a member of this subsystem,
// according to the rules parsed from ueventd.rc
diff --git a/init/devices_test.cpp b/init/devices_test.cpp
index 3e7c1a8..c408bc1 100644
--- a/init/devices_test.cpp
+++ b/init/devices_test.cpp
@@ -30,7 +30,7 @@
class DeviceHandlerTester {
public:
void TestGetSymlinks(const std::string& platform_device, const Uevent& uevent,
- const std::vector<std::string> expected_links) {
+ const std::vector<std::string>& expected_links) {
TemporaryDir fake_sys_root;
device_handler_.sysfs_mount_point_ = fake_sys_root.path;
diff --git a/init/host_init_verifier.cpp b/init/host_init_verifier.cpp
index 8aa3509..92c2aa5 100644
--- a/init/host_init_verifier.cpp
+++ b/init/host_init_verifier.cpp
@@ -20,6 +20,7 @@
#include <stdio.h>
#include <stdlib.h>
+#include <fstream>
#include <iostream>
#include <iterator>
#include <string>
@@ -29,6 +30,7 @@
#include <android-base/logging.h>
#include <android-base/parseint.h>
#include <android-base/strings.h>
+#include <json/json.h>
#include "action.h"
#include "action_manager.h"
@@ -129,21 +131,33 @@
return nullptr;
}
-static std::optional<std::set<std::string>> ReadKnownInterfaces(
- const std::string& known_interfaces_file) {
- if (known_interfaces_file.empty()) {
- LOG(WARNING) << "Missing a known interfaces file.";
+static std::optional<android::init::InterfaceInheritanceHierarchyMap>
+ReadInterfaceInheritanceHierarchy(const std::string& interface_inheritance_hierarchy_file) {
+ if (interface_inheritance_hierarchy_file.empty()) {
+ LOG(WARNING) << "Missing an interface inheritance hierarchy file.";
return {};
}
- std::string known_interfaces;
- if (!ReadFileToString(known_interfaces_file, &known_interfaces)) {
- LOG(ERROR) << "Failed to read known interfaces file '" << known_interfaces_file << "'";
+ Json::Value root;
+ Json::Reader reader;
+ std::ifstream stream(interface_inheritance_hierarchy_file);
+ if (!reader.parse(stream, root)) {
+ LOG(ERROR) << "Failed to read interface inheritance hierarchy file: "
+ << interface_inheritance_hierarchy_file << "\n"
+ << reader.getFormattedErrorMessages();
return {};
}
- auto interfaces = Split(known_interfaces, " ");
- return std::set<std::string>(interfaces.begin(), interfaces.end());
+ android::init::InterfaceInheritanceHierarchyMap result;
+ for (const Json::Value& entry : root) {
+ std::set<std::string> inherited_interfaces;
+ for (const Json::Value& intf : entry["inheritedInterfaces"]) {
+ inherited_interfaces.insert(intf.asString());
+ }
+ result[entry["interface"].asString()] = inherited_interfaces;
+ }
+
+ return result;
}
namespace android {
@@ -169,7 +183,7 @@
android::base::InitLogging(argv, &android::base::StdioLogger);
android::base::SetMinimumLogSeverity(android::base::ERROR);
- std::string known_interfaces_file;
+ std::string interface_inheritance_hierarchy_file;
while (true) {
static const struct option long_options[] = {
@@ -177,7 +191,7 @@
{nullptr, 0, nullptr, 0},
};
- int arg = getopt_long(argc, argv, "p:k:", long_options, nullptr);
+ int arg = getopt_long(argc, argv, "p:i:", long_options, nullptr);
if (arg == -1) {
break;
@@ -190,8 +204,8 @@
case 'p':
passwd_files.emplace_back(optarg);
break;
- case 'k':
- known_interfaces_file = optarg;
+ case 'i':
+ interface_inheritance_hierarchy_file = optarg;
break;
default:
std::cerr << "getprop: getopt returned invalid result: " << arg << std::endl;
@@ -213,8 +227,10 @@
ServiceList& sl = ServiceList::GetInstance();
Parser parser;
parser.AddSectionParser(
- "service", std::make_unique<ServiceParser>(&sl, nullptr,
- ReadKnownInterfaces(known_interfaces_file)));
+ "service",
+ std::make_unique<ServiceParser>(
+ &sl, nullptr,
+ ReadInterfaceInheritanceHierarchy(interface_inheritance_hierarchy_file)));
parser.AddSectionParser("on", std::make_unique<ActionParser>(&am, nullptr));
parser.AddSectionParser("import", std::make_unique<HostImportParser>());
diff --git a/init/init.cpp b/init/init.cpp
index 675f3e5..5dba54d 100644
--- a/init/init.cpp
+++ b/init/init.cpp
@@ -50,10 +50,6 @@
#include <processgroup/setup.h>
#include <selinux/android.h>
-#ifndef RECOVERY
-#include <binder/ProcessState.h>
-#endif
-
#include "action_parser.h"
#include "boringssl_self_test.h"
#include "builtins.h"
@@ -139,12 +135,12 @@
if (!parser.ParseConfig("/system/etc/init")) {
late_import_paths.emplace_back("/system/etc/init");
}
+ // late_import is available only in Q and earlier release. As we don't
+ // have system_ext in those versions, skip late_import for system_ext.
+ parser.ParseConfig("/system_ext/etc/init");
if (!parser.ParseConfig("/product/etc/init")) {
late_import_paths.emplace_back("/product/etc/init");
}
- if (!parser.ParseConfig("/product_services/etc/init")) {
- late_import_paths.emplace_back("/product_services/etc/init");
- }
if (!parser.ParseConfig("/odm/etc/init")) {
late_import_paths.emplace_back("/odm/etc/init");
}
@@ -453,24 +449,6 @@
return {};
}
-static Result<void> InitBinder(const BuiltinArguments& args) {
- // init's use of binder is very limited. init cannot:
- // - have any binder threads
- // - receive incoming binder calls
- // - pass local binder services to remote processes
- // - use death recipients
- // The main supported usecases are:
- // - notifying other daemons (oneway calls only)
- // - retrieving data that is necessary to boot
- // Also, binder can't be used by recovery.
-#ifndef RECOVERY
- android::ProcessState::self()->setThreadPoolMaxThreadCount(0);
- android::ProcessState::self()->setCallRestriction(
- ProcessState::CallRestriction::ERROR_IF_NOT_ONEWAY);
-#endif
- return {};
-}
-
// Set the UDC controller for the ConfigFS USB Gadgets.
// Read the UDC controller in use from "/sys/class/udc".
// In case of multiple UDC controllers select the first one.
@@ -766,9 +744,6 @@
// wasn't ready immediately after wait_for_coldboot_done
am.QueueBuiltinAction(MixHwrngIntoLinuxRngAction, "MixHwrngIntoLinuxRng");
- // Initialize binder before bringing up other system services
- am.QueueBuiltinAction(InitBinder, "InitBinder");
-
// Don't mount filesystems or start core system services in charger mode.
std::string bootmode = GetProperty("ro.bootmode", "");
if (bootmode == "charger") {
diff --git a/init/keychords.cpp b/init/keychords.cpp
index d0ca3e7..5f2682b 100644
--- a/init/keychords.cpp
+++ b/init/keychords.cpp
@@ -285,7 +285,7 @@
void Keychords::Start(Epoll* epoll, std::function<void(const std::vector<int>&)> handler) {
epoll_ = epoll;
- handler_ = handler;
+ handler_ = std::move(handler);
if (entries_.size()) GeteventOpenDevice();
}
diff --git a/init/mount_handler.cpp b/init/mount_handler.cpp
index b0b63c5..791a019 100644
--- a/init/mount_handler.cpp
+++ b/init/mount_handler.cpp
@@ -140,11 +140,11 @@
}
}
free(buf);
- for (auto entry : untouched) {
+ for (auto& entry : untouched) {
SetMountProperty(entry, false);
mounts_.erase(entry);
}
- for (auto entry : touched) {
+ for (auto& entry : touched) {
SetMountProperty(entry, true);
mounts_.emplace(std::move(entry));
}
diff --git a/init/parser.cpp b/init/parser.cpp
index bbfbdc6..6ab61cb 100644
--- a/init/parser.cpp
+++ b/init/parser.cpp
@@ -37,7 +37,7 @@
}
void Parser::AddSingleLineParser(const std::string& prefix, LineCallback callback) {
- line_callbacks_.emplace_back(prefix, callback);
+ line_callbacks_.emplace_back(prefix, std::move(callback));
}
void Parser::ParseData(const std::string& filename, std::string* data) {
diff --git a/init/property_service.cpp b/init/property_service.cpp
index b89914f..8623c30 100644
--- a/init/property_service.cpp
+++ b/init/property_service.cpp
@@ -782,10 +782,9 @@
"brand", "device", "manufacturer", "model", "name",
};
const char* RO_PRODUCT_PROPS_ALLOWED_SOURCES[] = {
- "odm", "product", "product_services", "system", "vendor",
+ "odm", "product", "system_ext", "system", "vendor",
};
- const char* RO_PRODUCT_PROPS_DEFAULT_SOURCE_ORDER =
- "product,product_services,odm,vendor,system";
+ const char* RO_PRODUCT_PROPS_DEFAULT_SOURCE_ORDER = "product,odm,vendor,system_ext,system";
const std::string EMPTY = "";
std::string ro_product_props_source_order =
@@ -892,6 +891,7 @@
}
}
load_properties_from_file("/system/build.prop", nullptr, &properties);
+ load_properties_from_file("/system_ext/build.prop", nullptr, &properties);
load_properties_from_file("/vendor/default.prop", nullptr, &properties);
load_properties_from_file("/vendor/build.prop", nullptr, &properties);
if (SelinuxGetVendorAndroidVersion() >= __ANDROID_API_Q__) {
@@ -901,7 +901,6 @@
load_properties_from_file("/odm/build.prop", nullptr, &properties);
}
load_properties_from_file("/product/build.prop", nullptr, &properties);
- load_properties_from_file("/product_services/build.prop", nullptr, &properties);
load_properties_from_file("/factory/factory.prop", "ro.*", &properties);
if (load_debug_prop) {
diff --git a/init/reboot.cpp b/init/reboot.cpp
index d9d885c..cb54d34 100644
--- a/init/reboot.cpp
+++ b/init/reboot.cpp
@@ -636,11 +636,9 @@
bool run_fsck = false;
bool command_invalid = false;
- if (cmd_params.size() > 3) {
- command_invalid = true;
- } else if (cmd_params[0] == "shutdown") {
+ if (cmd_params[0] == "shutdown") {
cmd = ANDROID_RB_POWEROFF;
- if (cmd_params.size() == 2) {
+ if (cmd_params.size() >= 2) {
if (cmd_params[1] == "userrequested") {
// The shutdown reason is PowerManager.SHUTDOWN_USER_REQUESTED.
// Run fsck once the file system is remounted in read-only mode.
@@ -671,6 +669,13 @@
"bootloader_message: "
<< err;
}
+ } else if (reboot_target == "recovery") {
+ const std::vector<std::string> options = {};
+ std::string err;
+ if (!write_bootloader_message(options, &err)) {
+ LOG(ERROR) << "Failed to set bootloader message: " << err;
+ return false;
+ }
} else if (reboot_target == "sideload" || reboot_target == "sideload-auto-reboot" ||
reboot_target == "fastboot") {
std::string arg = reboot_target == "sideload-auto-reboot" ? "sideload_auto_reboot"
@@ -686,9 +691,9 @@
reboot_target = "recovery";
}
- // If there is an additional parameter, pass it along
- if ((cmd_params.size() == 3) && cmd_params[2].size()) {
- reboot_target += "," + cmd_params[2];
+ // If there are additional parameter, pass them along
+ for (size_t i = 2; (cmd_params.size() > i) && cmd_params[i].size(); ++i) {
+ reboot_target += "," + cmd_params[i];
}
}
} else {
diff --git a/init/security.cpp b/init/security.cpp
index 5d87f3c..586d0c7 100644
--- a/init/security.cpp
+++ b/init/security.cpp
@@ -83,7 +83,7 @@
return {};
}
-static bool SetHighestAvailableOptionValue(std::string path, int min, int max) {
+static bool SetHighestAvailableOptionValue(const std::string& path, int min, int max) {
std::ifstream inf(path, std::fstream::in);
if (!inf) {
LOG(ERROR) << "Cannot open for reading: " << path;
diff --git a/init/service_parser.cpp b/init/service_parser.cpp
index ba35104..88ce364 100644
--- a/init/service_parser.cpp
+++ b/init/service_parser.cpp
@@ -18,6 +18,9 @@
#include <linux/input.h>
+#include <algorithm>
+#include <sstream>
+
#include <android-base/logging.h>
#include <android-base/parseint.h>
#include <android-base/strings.h>
@@ -152,12 +155,6 @@
return Error() << "Interface name must not be a value name '" << interface_name << "'";
}
- if (known_interfaces_ && known_interfaces_->count(interface_name) == 0) {
- return Error() << "Interface is not in the known set of hidl_interfaces: '"
- << interface_name << "'. Please ensure the interface is built "
- << "by a hidl_interface target.";
- }
-
const std::string fullname = interface_name + "/" + instance_name;
for (const auto& svc : *service_list_) {
@@ -540,6 +537,37 @@
return {};
}
+ if (interface_inheritance_hierarchy_) {
+ std::set<std::string> interface_names;
+ for (const std::string& intf : service_->interfaces()) {
+ interface_names.insert(Split(intf, "/")[0]);
+ }
+ std::ostringstream error_stream;
+ for (const std::string& intf : interface_names) {
+ if (interface_inheritance_hierarchy_->count(intf) == 0) {
+ error_stream << "\nInterface is not in the known set of hidl_interfaces: '" << intf
+ << "'. Please ensure the interface is spelled correctly and built "
+ << "by a hidl_interface target.";
+ continue;
+ }
+ const std::set<std::string>& required_interfaces =
+ (*interface_inheritance_hierarchy_)[intf];
+ std::set<std::string> diff;
+ std::set_difference(required_interfaces.begin(), required_interfaces.end(),
+ interface_names.begin(), interface_names.end(),
+ std::inserter(diff, diff.begin()));
+ if (!diff.empty()) {
+ error_stream << "\nInterface '" << intf << "' requires its full inheritance "
+ << "hierarchy to be listed in this init_rc file. Missing "
+ << "interfaces: [" << base::Join(diff, " ") << "]";
+ }
+ }
+ const std::string& errors = error_stream.str();
+ if (!errors.empty()) {
+ return Error() << errors;
+ }
+ }
+
Service* old_service = service_list_->FindService(service_->name());
if (old_service) {
if (!service_->is_override()) {
diff --git a/init/service_parser.h b/init/service_parser.h
index 5a16768..5ad26ef 100644
--- a/init/service_parser.h
+++ b/init/service_parser.h
@@ -26,13 +26,16 @@
namespace android {
namespace init {
+using InterfaceInheritanceHierarchyMap = std::map<std::string, std::set<std::string>>;
+
class ServiceParser : public SectionParser {
public:
- ServiceParser(ServiceList* service_list, std::vector<Subcontext>* subcontexts,
- const std::optional<std::set<std::string>>& known_interfaces)
+ ServiceParser(
+ ServiceList* service_list, std::vector<Subcontext>* subcontexts,
+ const std::optional<InterfaceInheritanceHierarchyMap>& interface_inheritance_hierarchy)
: service_list_(service_list),
subcontexts_(subcontexts),
- known_interfaces_(known_interfaces),
+ interface_inheritance_hierarchy_(interface_inheritance_hierarchy),
service_(nullptr) {}
Result<void> ParseSection(std::vector<std::string>&& args, const std::string& filename,
int line) override;
@@ -85,7 +88,7 @@
ServiceList* service_list_;
std::vector<Subcontext>* subcontexts_;
- std::optional<std::set<std::string>> known_interfaces_;
+ std::optional<InterfaceInheritanceHierarchyMap> interface_inheritance_hierarchy_;
std::unique_ptr<Service> service_;
std::string filename_;
};
diff --git a/init/test_function_map.h b/init/test_function_map.h
index 293f1f9..466836c 100644
--- a/init/test_function_map.h
+++ b/init/test_function_map.h
@@ -30,17 +30,17 @@
public:
// Helper for argument-less functions
using BuiltinFunctionNoArgs = std::function<void(void)>;
- void Add(const std::string& name, const BuiltinFunctionNoArgs function) {
- Add(name, 0, 0, false, [function](const BuiltinArguments&) {
- function();
+ void Add(const std::string& name, BuiltinFunctionNoArgs function) {
+ Add(name, 0, 0, false, [f = std::move(function)](const BuiltinArguments&) {
+ f();
return Result<void>{};
});
}
void Add(const std::string& name, std::size_t min_parameters, std::size_t max_parameters,
- bool run_in_subcontext, const BuiltinFunction function) {
- builtin_functions_[name] =
- make_tuple(min_parameters, max_parameters, make_pair(run_in_subcontext, function));
+ bool run_in_subcontext, BuiltinFunction function) {
+ builtin_functions_[name] = make_tuple(min_parameters, max_parameters,
+ make_pair(run_in_subcontext, std::move(function)));
}
private:
diff --git a/init/ueventd.cpp b/init/ueventd.cpp
index 3b9de0f..8b2cf62 100644
--- a/init/ueventd.cpp
+++ b/init/ueventd.cpp
@@ -146,7 +146,7 @@
void ColdBoot::RegenerateUevents() {
uevent_listener_.RegenerateUevents([this](const Uevent& uevent) {
- uevent_queue_.emplace_back(std::move(uevent));
+ uevent_queue_.emplace_back(uevent);
return ListenerAction::kContinue;
});
}
diff --git a/libcutils/fs_config.cpp b/libcutils/fs_config.cpp
index 897a169..b29638c 100644
--- a/libcutils/fs_config.cpp
+++ b/libcutils/fs_config.cpp
@@ -106,7 +106,7 @@
// oem/ file-system since the intent is to provide support for customized
// portions of a separate vendor.img or oem.img. Has to remain open so that
// customization can also land on /system/vendor, /system/oem, /system/odm,
-// /system/product or /system/product_services.
+// /system/product or /system/system_ext.
//
// We expect build-time checking or filtering when constructing the associated
// fs_config_* files (see build/tools/fs_config/fs_config_generate.c)
@@ -118,15 +118,12 @@
static const char odm_conf_file[] = "/odm/etc/fs_config_files";
static const char product_conf_dir[] = "/product/etc/fs_config_dirs";
static const char product_conf_file[] = "/product/etc/fs_config_files";
-static const char product_services_conf_dir[] = "/product_services/etc/fs_config_dirs";
-static const char product_services_conf_file[] = "/product_services/etc/fs_config_files";
+static const char system_ext_conf_dir[] = "/system_ext/etc/fs_config_dirs";
+static const char system_ext_conf_file[] = "/system_ext/etc/fs_config_files";
static const char* conf[][2] = {
- {sys_conf_file, sys_conf_dir},
- {ven_conf_file, ven_conf_dir},
- {oem_conf_file, oem_conf_dir},
- {odm_conf_file, odm_conf_dir},
- {product_conf_file, product_conf_dir},
- {product_services_conf_file, product_services_conf_dir},
+ {sys_conf_file, sys_conf_dir}, {ven_conf_file, ven_conf_dir},
+ {oem_conf_file, oem_conf_dir}, {odm_conf_file, odm_conf_dir},
+ {product_conf_file, product_conf_dir}, {system_ext_conf_file, system_ext_conf_dir},
};
// Do not use android_files to grant Linux capabilities. Use ambient capabilities in their
@@ -158,9 +155,9 @@
{ 00600, AID_ROOT, AID_ROOT, 0, "product/build.prop" },
{ 00444, AID_ROOT, AID_ROOT, 0, product_conf_dir + 1 },
{ 00444, AID_ROOT, AID_ROOT, 0, product_conf_file + 1 },
- { 00600, AID_ROOT, AID_ROOT, 0, "product_services/build.prop" },
- { 00444, AID_ROOT, AID_ROOT, 0, product_services_conf_dir + 1 },
- { 00444, AID_ROOT, AID_ROOT, 0, product_services_conf_file + 1 },
+ { 00600, AID_ROOT, AID_ROOT, 0, "system_ext/build.prop" },
+ { 00444, AID_ROOT, AID_ROOT, 0, system_ext_conf_dir + 1 },
+ { 00444, AID_ROOT, AID_ROOT, 0, system_ext_conf_file + 1 },
{ 00755, AID_ROOT, AID_SHELL, 0, "system/bin/crash_dump32" },
{ 00755, AID_ROOT, AID_SHELL, 0, "system/bin/crash_dump64" },
{ 00755, AID_ROOT, AID_SHELL, 0, "system/bin/debuggerd" },
@@ -248,9 +245,9 @@
}
// if path is "odm/<stuff>", "oem/<stuff>", "product/<stuff>",
-// "product_services/<stuff>" or "vendor/<stuff>"
+// "system_ext/<stuff>" or "vendor/<stuff>"
static bool is_partition(const std::string& path) {
- static const char* partitions[] = {"odm/", "oem/", "product/", "product_services/", "vendor/"};
+ static const char* partitions[] = {"odm/", "oem/", "product/", "system_ext/", "vendor/"};
for (size_t i = 0; i < (sizeof(partitions) / sizeof(partitions[0])); ++i) {
if (StartsWith(path, partitions[i])) return true;
}
@@ -285,10 +282,8 @@
if (fnmatch(pattern.c_str(), input.c_str(), fnm_flags) == 0) return true;
// Check match between logical partition's files and patterns.
- static constexpr const char* kLogicalPartitions[] = {"system/product/",
- "system/product_services/",
- "system/vendor/",
- "vendor/odm/"};
+ static constexpr const char* kLogicalPartitions[] = {"system/product/", "system/system_ext/",
+ "system/vendor/", "vendor/odm/"};
for (auto& logical_partition : kLogicalPartitions) {
if (StartsWith(input, logical_partition)) {
std::string input_in_partition = input.substr(input.find('/') + 1);
diff --git a/libutils/include/utils/RefBase.h b/libutils/include/utils/RefBase.h
index a105474..3a02a8a 100644
--- a/libutils/include/utils/RefBase.h
+++ b/libutils/include/utils/RefBase.h
@@ -188,9 +188,6 @@
// ---------------------------------------------------------------------------
namespace android {
-class TextOutput;
-TextOutput& printWeakPointer(TextOutput& to, const void* val);
-
// ---------------------------------------------------------------------------
#define COMPARE_WEAK(_op_) \
@@ -459,9 +456,6 @@
weakref_type* m_refs;
};
-template <typename T>
-TextOutput& operator<<(TextOutput& to, const wp<T>& val);
-
#undef COMPARE_WEAK
// ---------------------------------------------------------------------------
@@ -635,12 +629,6 @@
}
}
-template <typename T>
-inline TextOutput& operator<<(TextOutput& to, const wp<T>& val)
-{
- return printWeakPointer(to, val.unsafe_get());
-}
-
// ---------------------------------------------------------------------------
// this class just serves as a namespace so TYPE::moveReferences can stay
diff --git a/lmkd/lmkd.c b/lmkd/lmkd.c
index 48140b8..42af751 100644
--- a/lmkd/lmkd.c
+++ b/lmkd/lmkd.c
@@ -79,6 +79,7 @@
#define MEMCG_MEMORYSW_USAGE "/dev/memcg/memory.memsw.usage_in_bytes"
#define ZONEINFO_PATH "/proc/zoneinfo"
#define MEMINFO_PATH "/proc/meminfo"
+#define PROC_STATUS_TGID_FIELD "Tgid:"
#define LINE_MAX 128
/* Android Logger event logtags (see event.logtags) */
@@ -551,6 +552,49 @@
(to->tv_nsec - from->tv_nsec) / (long)NS_PER_MS;
}
+static int proc_get_tgid(int pid) {
+ char path[PATH_MAX];
+ char buf[PAGE_SIZE];
+ int fd;
+ ssize_t size;
+ char *pos;
+ int64_t tgid = -1;
+
+ snprintf(path, PATH_MAX, "/proc/%d/status", pid);
+ fd = open(path, O_RDONLY | O_CLOEXEC);
+ if (fd < 0) {
+ return -1;
+ }
+
+ size = read_all(fd, buf, sizeof(buf) - 1);
+ if (size < 0) {
+ goto out;
+ }
+ buf[size] = 0;
+
+ pos = buf;
+ while (true) {
+ pos = strstr(pos, PROC_STATUS_TGID_FIELD);
+ /* Stop if TGID tag not found or found at the line beginning */
+ if (pos == NULL || pos == buf || pos[-1] == '\n') {
+ break;
+ }
+ pos++;
+ }
+
+ if (pos == NULL) {
+ goto out;
+ }
+
+ pos += strlen(PROC_STATUS_TGID_FIELD);
+ while (*pos == ' ') pos++;
+ parse_int64(pos, &tgid);
+
+out:
+ close(fd);
+ return (int)tgid;
+}
+
static void cmd_procprio(LMKD_CTRL_PACKET packet) {
struct proc *procp;
char path[80];
@@ -559,6 +603,7 @@
struct lmk_procprio params;
bool is_system_server;
struct passwd *pwdrec;
+ int tgid;
lmkd_pack_get_procprio(packet, ¶ms);
@@ -568,6 +613,14 @@
return;
}
+ /* Check if registered process is a thread group leader */
+ tgid = proc_get_tgid(params.pid);
+ if (tgid >= 0 && tgid != params.pid) {
+ ALOGE("Attempt to register a task that is not a thread group leader (tid %d, tgid %d)",
+ params.pid, tgid);
+ return;
+ }
+
/* gid containing AID_READPROC required */
/* CAP_SYS_RESOURCE required */
/* CAP_DAC_OVERRIDE required */
@@ -1332,6 +1385,7 @@
static int kill_one_process(struct proc* procp, int min_oom_score) {
int pid = procp->pid;
uid_t uid = procp->uid;
+ int tgid;
char *taskname;
int tasksize;
int r;
@@ -1345,6 +1399,12 @@
(void)(min_oom_score);
#endif
+ tgid = proc_get_tgid(pid);
+ if (tgid >= 0 && tgid != pid) {
+ ALOGE("Possible pid reuse detected (pid %d, tgid %d)!", pid, tgid);
+ goto out;
+ }
+
taskname = proc_get_name(pid);
if (!taskname) {
goto out;
diff --git a/rootdir/etc/ld.config.txt b/rootdir/etc/ld.config.txt
index b1616d3..a6ea2ab 100644
--- a/rootdir/etc/ld.config.txt
+++ b/rootdir/etc/ld.config.txt
@@ -7,6 +7,7 @@
# absolute path of an executable is selected.
dir.system = /system/bin/
dir.system = /system/xbin/
+dir.system = /%SYSTEM_EXT%/bin/
dir.system = /%PRODUCT%/bin/
dir.vendor = /odm/bin/
@@ -48,8 +49,8 @@
namespace.default.visible = true
namespace.default.search.paths = /system/${LIB}
+namespace.default.search.paths += /%SYSTEM_EXT%/${LIB}
namespace.default.search.paths += /%PRODUCT%/${LIB}
-namespace.default.search.paths += /%PRODUCT_SERVICES%/${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
@@ -61,12 +62,15 @@
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 += /%SYSTEM_EXT%/${LIB}
namespace.default.permitted.paths += /%PRODUCT%/${LIB}
-namespace.default.permitted.paths += /%PRODUCT_SERVICES%/${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
namespace.default.permitted.paths += /system/priv-app
+namespace.default.permitted.paths += /%SYSTEM_EXT%/framework
+namespace.default.permitted.paths += /%SYSTEM_EXT%/app
+namespace.default.permitted.paths += /%SYSTEM_EXT%/priv-app
namespace.default.permitted.paths += /vendor/framework
namespace.default.permitted.paths += /vendor/app
namespace.default.permitted.paths += /vendor/priv-app
@@ -80,9 +84,6 @@
namespace.default.permitted.paths += /%PRODUCT%/framework
namespace.default.permitted.paths += /%PRODUCT%/app
namespace.default.permitted.paths += /%PRODUCT%/priv-app
-namespace.default.permitted.paths += /%PRODUCT_SERVICES%/framework
-namespace.default.permitted.paths += /%PRODUCT_SERVICES%/app
-namespace.default.permitted.paths += /%PRODUCT_SERVICES%/priv-app
namespace.default.permitted.paths += /data
namespace.default.permitted.paths += /mnt/expand
namespace.default.permitted.paths += /apex/com.android.runtime/${LIB}/bionic
@@ -90,10 +91,10 @@
namespace.default.asan.search.paths = /data/asan/system/${LIB}
namespace.default.asan.search.paths += /system/${LIB}
+namespace.default.asan.search.paths += /data/asan/%SYSTEM_EXT%/${LIB}
+namespace.default.asan.search.paths += /%SYSTEM_EXT%/${LIB}
namespace.default.asan.search.paths += /data/asan/%PRODUCT%/${LIB}
namespace.default.asan.search.paths += /%PRODUCT%/${LIB}
-namespace.default.asan.search.paths += /data/asan/%PRODUCT_SERVICES%/${LIB}
-namespace.default.asan.search.paths += /%PRODUCT_SERVICES%/${LIB}
namespace.default.asan.permitted.paths = /data
namespace.default.asan.permitted.paths += /system/${LIB}/drm
@@ -102,6 +103,10 @@
namespace.default.asan.permitted.paths += /system/framework
namespace.default.asan.permitted.paths += /system/app
namespace.default.asan.permitted.paths += /system/priv-app
+namespace.default.asan.permitted.paths += /%SYSTEM_EXT%/${LIB}
+namespace.default.asan.permitted.paths += /%SYSTEM_EXT%/framework
+namespace.default.asan.permitted.paths += /%SYSTEM_EXT%/app
+namespace.default.asan.permitted.paths += /%SYSTEM_EXT%/priv-app
namespace.default.asan.permitted.paths += /vendor/framework
namespace.default.asan.permitted.paths += /vendor/app
namespace.default.asan.permitted.paths += /vendor/priv-app
@@ -116,10 +121,6 @@
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_SERVICES%/${LIB}
-namespace.default.asan.permitted.paths += /%PRODUCT_SERVICES%/framework
-namespace.default.asan.permitted.paths += /%PRODUCT_SERVICES%/app
-namespace.default.asan.permitted.paths += /%PRODUCT_SERVICES%/priv-app
namespace.default.asan.permitted.paths += /mnt/expand
namespace.default.asan.permitted.paths += /apex/com.android.runtime/${LIB}/bionic
namespace.default.asan.permitted.paths += /system/${LIB}/bootstrap
@@ -510,15 +511,15 @@
namespace.system.isolated = false
namespace.system.search.paths = /system/${LIB}
+namespace.system.search.paths += /%SYSTEM_EXT%/${LIB}
namespace.system.search.paths += /%PRODUCT%/${LIB}
-namespace.system.search.paths += /%PRODUCT_SERVICES%/${LIB}
namespace.system.asan.search.paths = /data/asan/system/${LIB}
namespace.system.asan.search.paths += /system/${LIB}
-namespace.system.asan.search.paths += /data/asan/product/${LIB}
+namespace.system.asan.search.paths += /data/asan/%SYSTEM_EXT%/${LIB}
+namespace.system.asan.search.paths += /%SYSTEM_EXT%/${LIB}
+namespace.system.asan.search.paths += /data/asan/%PRODUCT%/${LIB}
namespace.system.asan.search.paths += /%PRODUCT%/${LIB}
-namespace.system.asan.search.paths += /data/asan/product_services/${LIB}
-namespace.system.asan.search.paths += /%PRODUCT_SERVICES%/${LIB}
namespace.system.links = runtime
namespace.system.link.runtime.shared_libs = libdexfile_external.so
@@ -553,15 +554,15 @@
# The search paths here should be kept the same as that of the 'system'
# namespace.
namespace.vndk_in_system.search.paths = /system/${LIB}
+namespace.vndk_in_system.search.paths += /%SYSTEM_EXT%/${LIB}
namespace.vndk_in_system.search.paths += /%PRODUCT%/${LIB}
-namespace.vndk_in_system.search.paths += /%PRODUCT_SERVICES%/${LIB}
namespace.vndk_in_system.asan.search.paths = /data/asan/system/${LIB}
namespace.vndk_in_system.asan.search.paths += /system/${LIB}
-namespace.vndk_in_system.asan.search.paths += /data/asan/product/${LIB}
+namespace.vndk_in_system.asan.search.paths += /data/asan/%SYSTEM_EXT%/${LIB}
+namespace.vndk_in_system.asan.search.paths += /%SYSTEM_EXT%/${LIB}
+namespace.vndk_in_system.asan.search.paths += /data/asan/%PRODUCT%/${LIB}
namespace.vndk_in_system.asan.search.paths += /%PRODUCT%/${LIB}
-namespace.vndk_in_system.asan.search.paths += /data/asan/product_services/${LIB}
-namespace.vndk_in_system.asan.search.paths += /%PRODUCT_SERVICES%/${LIB}
namespace.vndk_in_system.whitelisted = %VNDK_USING_CORE_VARIANT_LIBRARIES%
@@ -705,7 +706,7 @@
[postinstall]
namespace.default.isolated = false
namespace.default.search.paths = /system/${LIB}
+namespace.default.search.paths += /%SYSTEM_EXT%/${LIB}
namespace.default.search.paths += /%PRODUCT%/${LIB}
-namespace.default.search.paths += /%PRODUCT_SERVICES%/${LIB}
namespace.default.link.runtime.shared_libs = %SANITIZER_RUNTIME_LIBRARIES%
diff --git a/rootdir/etc/ld.config.vndk_lite.txt b/rootdir/etc/ld.config.vndk_lite.txt
index 9212408..69535a9 100644
--- a/rootdir/etc/ld.config.vndk_lite.txt
+++ b/rootdir/etc/ld.config.vndk_lite.txt
@@ -7,6 +7,7 @@
# absolute path of an executable is selected.
dir.system = /system/bin/
dir.system = /system/xbin/
+dir.system = /%SYSTEM_EXT%/bin/
dir.system = /%PRODUCT%/bin/
dir.vendor = /odm/bin/
@@ -48,21 +49,21 @@
namespace.default.visible = true
namespace.default.search.paths = /system/${LIB}
+namespace.default.search.paths += /%SYSTEM_EXT%/${LIB}
+namespace.default.search.paths += /%PRODUCT%/${LIB}
namespace.default.search.paths += /odm/${LIB}
namespace.default.search.paths += /vendor/${LIB}
-namespace.default.search.paths += /%PRODUCT%/${LIB}
-namespace.default.search.paths += /%PRODUCT_SERVICES%/${LIB}
namespace.default.asan.search.paths = /data/asan/system/${LIB}
namespace.default.asan.search.paths += /system/${LIB}
+namespace.default.asan.search.paths += /data/asan/%SYSTEM_EXT%/${LIB}
+namespace.default.asan.search.paths += /%SYSTEM_EXT%/${LIB}
+namespace.default.asan.search.paths += /data/asan/%PRODUCT%/${LIB}
+namespace.default.asan.search.paths += /%PRODUCT%/${LIB}
namespace.default.asan.search.paths += /data/asan/odm/${LIB}
namespace.default.asan.search.paths += /odm/${LIB}
namespace.default.asan.search.paths += /data/asan/vendor/${LIB}
namespace.default.asan.search.paths += /vendor/${LIB}
-namespace.default.asan.search.paths += /data/asan/%PRODUCT%/${LIB}
-namespace.default.asan.search.paths += /%PRODUCT%/${LIB}
-namespace.default.asan.search.paths += /data/asan/%PRODUCT_SERVICES%/${LIB}
-namespace.default.asan.search.paths += /%PRODUCT_SERVICES%/${LIB}
# Keep in sync with the "platform" namespace in art/build/apex/ld.config.txt.
# If a shared library or an executable requests a shared library that
@@ -336,8 +337,9 @@
# Access to system libraries is allowed
namespace.default.search.paths += /system/${LIB}/vndk-sp%VNDK_VER%
namespace.default.search.paths += /system/${LIB}
+namespace.default.search.paths += /%SYSTEM_EXT%/${LIB}
namespace.default.search.paths += /%PRODUCT%/${LIB}
-namespace.default.search.paths += /%PRODUCT_SERVICES%/${LIB}
+# Put /system/lib/vndk at the last search order in vndk_lite for GSI
namespace.default.search.paths += /system/${LIB}/vndk%VNDK_VER%
namespace.default.asan.search.paths = /data/asan/odm/${LIB}
@@ -356,10 +358,10 @@
namespace.default.asan.search.paths += /system/${LIB}/vndk-sp%VNDK_VER%
namespace.default.asan.search.paths += /data/asan/system/${LIB}
namespace.default.asan.search.paths += /system/${LIB}
-namespace.default.asan.search.paths += /data/asan/product/${LIB}
+namespace.default.asan.search.paths += /data/asan/%SYSTEM_EXT%/${LIB}
+namespace.default.asan.search.paths += /%SYSTEM_EXT%/${LIB}
+namespace.default.asan.search.paths += /data/asan/%PRODUCT%/${LIB}
namespace.default.asan.search.paths += /%PRODUCT%/${LIB}
-namespace.default.asan.search.paths += /data/asan/product_services/${LIB}
-namespace.default.asan.search.paths += /%PRODUCT_SERVICES%/${LIB}
namespace.default.asan.search.paths += /data/asan/system/${LIB}/vndk%VNDK_VER%
namespace.default.asan.search.paths += /system/${LIB}/vndk%VNDK_VER%
@@ -514,5 +516,5 @@
[postinstall]
namespace.default.isolated = false
namespace.default.search.paths = /system/${LIB}
+namespace.default.search.paths += /%SYSTEM_EXT%/${LIB}
namespace.default.search.paths += /%PRODUCT%/${LIB}
-namespace.default.search.paths += /%PRODUCT_SERVICES%/${LIB}
diff --git a/rootdir/init.rc b/rootdir/init.rc
index 3acf301..96ffa69 100644
--- a/rootdir/init.rc
+++ b/rootdir/init.rc
@@ -413,6 +413,10 @@
# HALs required before storage encryption can get unlocked (FBE/FDE)
class_start early_hal
+ # Check and mark a successful boot, before mounting userdata with mount_all.
+ # No-op for non-A/B device.
+ exec_start update_verifier_nonencrypted
+
on post-fs-data
mark_post_data
@@ -628,22 +632,16 @@
# It is recommended to put unnecessary data/ initialization from post-fs-data
# to start-zygote in device's init.rc to unblock zygote start.
on zygote-start && property:ro.crypto.state=unencrypted
- # A/B update verifier that marks a successful boot.
- exec_start update_verifier_nonencrypted
start netd
start zygote
start zygote_secondary
on zygote-start && property:ro.crypto.state=unsupported
- # A/B update verifier that marks a successful boot.
- exec_start update_verifier_nonencrypted
start netd
start zygote
start zygote_secondary
on zygote-start && property:ro.crypto.state=encrypted && property:ro.crypto.type=file
- # A/B update verifier that marks a successful boot.
- exec_start update_verifier_nonencrypted
start netd
start zygote
start zygote_secondary
@@ -770,8 +768,6 @@
trigger zygote-start
on property:vold.decrypt=trigger_restart_min_framework
- # A/B update verifier that marks a successful boot.
- exec_start update_verifier
class_start main
on property:vold.decrypt=trigger_restart_framework
diff --git a/rootdir/update_and_install_ld_config.mk b/rootdir/update_and_install_ld_config.mk
index f62c3df..c949a4f 100644
--- a/rootdir/update_and_install_ld_config.mk
+++ b/rootdir/update_and_install_ld_config.mk
@@ -147,12 +147,7 @@
$(hide) sed -i.bak -e "s?%SANITIZER_RUNTIME_LIBRARIES%?$(PRIVATE_SANITIZER_RUNTIME_LIBRARIES)?g" $@
$(hide) sed -i.bak -e "s?%VNDK_VER%?$(PRIVATE_VNDK_VERSION_SUFFIX)?g" $@
$(hide) sed -i.bak -e "s?%PRODUCT%?$(TARGET_COPY_OUT_PRODUCT)?g" $@
-ifeq ($(TARGET_COPY_OUT_PRODUCT),$(TARGET_COPY_OUT_PRODUCT_SERVICES))
- # Remove lines containing %PRODUCT_SERVICES% (identical to the %PRODUCT% ones)
- $(hide) sed -i.bak -e "\?%PRODUCT_SERVICES%?d" $@
-else
- $(hide) sed -i.bak -e "s?%PRODUCT_SERVICES%?$(TARGET_COPY_OUT_PRODUCT_SERVICES)?g" $@
-endif
+ $(hide) sed -i.bak -e "s?%SYSTEM_EXT%?$(TARGET_COPY_OUT_SYSTEM_EXT)?g" $@
$(hide) sed -i.bak -e "s?^$(PRIVATE_VNDK_VERSION_TAG)??g" $@
$(hide) sed -i.bak "/^\#VNDK[0-9]\{2\}\#.*$$/d" $@
$(hide) rm -f $@.bak