Loading fs_mgr/libsnapshot/include/libsnapshot/snapshot.h +15 −0 Original line number Diff line number Diff line Loading @@ -345,6 +345,14 @@ class SnapshotManager final : public ISnapshotManager { bool MapAllSnapshots(const std::chrono::milliseconds& timeout_ms = {}) override; bool UnmapAllSnapshots() override; // We can't use WaitForFile during first-stage init, because ueventd is not // running and therefore will not automatically create symlinks. Instead, // we let init provide us with the correct function to use to ensure // uevents have been processed and symlink/mknod calls completed. void SetUeventRegenCallback(std::function<bool(const std::string&)> callback) { uevent_regen_callback_ = callback; } private: FRIEND_TEST(SnapshotTest, CleanFirstStageMount); FRIEND_TEST(SnapshotTest, CreateSnapshot); Loading Loading @@ -676,6 +684,12 @@ class SnapshotManager final : public ISnapshotManager { // Same as above, but for paths only (no major:minor device strings). bool GetMappedImageDevicePath(const std::string& device_name, std::string* device_path); // Wait for a device to be created by ueventd (eg, its symlink or node to be populated). // This is needed for any code that uses device-mapper path in first-stage init. If // |timeout_ms| is empty or the given device is not a path, WaitForDevice immediately // returns true. bool WaitForDevice(const std::string& device, std::chrono::milliseconds timeout_ms); std::string gsid_dir_; std::string metadata_dir_; std::unique_ptr<IDeviceInfo> device_; Loading @@ -683,6 +697,7 @@ class SnapshotManager final : public ISnapshotManager { bool has_local_image_manager_ = false; bool use_first_stage_snapuserd_ = false; bool in_factory_data_reset_ = false; std::function<bool(const std::string&)> uevent_regen_callback_; std::unique_ptr<SnapuserdClient> snapuserd_client_; }; Loading fs_mgr/libsnapshot/snapshot.cpp +55 −11 Original line number Diff line number Diff line Loading @@ -409,10 +409,12 @@ bool SnapshotManager::MapDmUserCow(LockedFile* lock, const std::string& name, if (!dm.CreateDevice(name, table, path, timeout_ms)) { return false; } if (!WaitForDevice(*path, timeout_ms)) { return false; } auto control_device = "/dev/dm-user/" + misc_name; if (!android::fs_mgr::WaitForFile(control_device, timeout_ms)) { LOG(ERROR) << "Timed out waiting for dm-user misc device: " << control_device; if (!WaitForDevice(control_device, timeout_ms)) { return false; } Loading Loading @@ -1340,10 +1342,12 @@ bool SnapshotManager::PerformSecondStageTransition() { continue; } auto misc_name = user_cow_name; DmTable table; table.Emplace<DmTargetUser>(0, target.spec.length, user_cow_name); table.Emplace<DmTargetUser>(0, target.spec.length, misc_name); if (!dm.LoadTableAndActivate(user_cow_name, table)) { LOG(ERROR) << "Unable to swap tables for " << user_cow_name; LOG(ERROR) << "Unable to swap tables for " << misc_name; continue; } Loading @@ -1369,14 +1373,13 @@ bool SnapshotManager::PerformSecondStageTransition() { } // Wait for ueventd to acknowledge and create the control device node. std::string control_device = "/dev/dm-user/" + user_cow_name; if (!android::fs_mgr::WaitForFile(control_device, 10s)) { LOG(ERROR) << "Could not find control device: " << control_device; std::string control_device = "/dev/dm-user/" + misc_name; if (!WaitForDevice(control_device, 10s)) { continue; } uint64_t base_sectors = snapuserd_client_->InitDmUserCow(user_cow_name, cow_image_device, backing_device); snapuserd_client_->InitDmUserCow(misc_name, cow_image_device, backing_device); if (base_sectors == 0) { // Unrecoverable as metadata reads from cow device failed LOG(FATAL) << "Failed to retrieve base_sectors from Snapuserd"; Loading @@ -1385,7 +1388,7 @@ bool SnapshotManager::PerformSecondStageTransition() { CHECK(base_sectors == target.spec.length); if (!snapuserd_client_->AttachDmUser(user_cow_name)) { if (!snapuserd_client_->AttachDmUser(misc_name)) { // This error is unrecoverable. We cannot proceed because reads to // the underlying device will fail. LOG(FATAL) << "Could not initialize snapuserd for " << user_cow_name; Loading Loading @@ -1924,13 +1927,20 @@ bool SnapshotManager::MapPartitionWithSnapshot(LockedFile* lock, if (live_snapshot_status->compression_enabled()) { auto name = GetDmUserCowName(params.GetPartitionName()); // :TODO: need to force init to process uevents for these in first-stage. std::string cow_path; if (!GetMappedImageDevicePath(cow_name, &cow_path)) { LOG(ERROR) << "Could not determine path for: " << cow_name; return false; } // Ensure both |base_path| and |cow_path| are created, for snapuserd. if (!WaitForDevice(base_path, remaining_time)) { return false; } if (!WaitForDevice(cow_path, remaining_time)) { return false; } std::string new_cow_device; if (!MapDmUserCow(lock, name, cow_path, base_path, remaining_time, &new_cow_device)) { LOG(ERROR) << "Could not map dm-user device for partition " Loading Loading @@ -2070,7 +2080,7 @@ bool SnapshotManager::UnmapCowDevices(LockedFile* lock, const std::string& name) if (!EnsureSnapuserdConnected()) { return false; } if (!dm.DeleteDevice(dm_user_name)) { if (!dm.DeleteDeviceIfExists(dm_user_name)) { LOG(ERROR) << "Cannot unmap " << dm_user_name; return false; } Loading Loading @@ -3302,5 +3312,39 @@ bool SnapshotManager::GetMappedImageDeviceStringOrPath(const std::string& device return true; } bool SnapshotManager::WaitForDevice(const std::string& device, std::chrono::milliseconds timeout_ms) { if (!android::base::StartsWith(device, "/")) { return true; } // In first-stage init, we rely on init setting a callback which can // regenerate uevents and populate /dev for us. if (uevent_regen_callback_) { if (!uevent_regen_callback_(device)) { LOG(ERROR) << "Failed to find device after regenerating uevents: " << device; return false; } return true; } // Otherwise, the only kind of device we need to wait for is a dm-user // misc device. Normal calls to DeviceMapper::CreateDevice() guarantee // the path has been created. if (!android::base::StartsWith(device, "/dev/dm-user/")) { return true; } if (timeout_ms.count() == 0) { LOG(ERROR) << "No timeout was specified to wait for device: " << device; return false; } if (!android::fs_mgr::WaitForFile(device, timeout_ms)) { LOG(ERROR) << "Timed out waiting for device to appear: " << device; return false; } return true; } } // namespace snapshot } // namespace android Loading
fs_mgr/libsnapshot/include/libsnapshot/snapshot.h +15 −0 Original line number Diff line number Diff line Loading @@ -345,6 +345,14 @@ class SnapshotManager final : public ISnapshotManager { bool MapAllSnapshots(const std::chrono::milliseconds& timeout_ms = {}) override; bool UnmapAllSnapshots() override; // We can't use WaitForFile during first-stage init, because ueventd is not // running and therefore will not automatically create symlinks. Instead, // we let init provide us with the correct function to use to ensure // uevents have been processed and symlink/mknod calls completed. void SetUeventRegenCallback(std::function<bool(const std::string&)> callback) { uevent_regen_callback_ = callback; } private: FRIEND_TEST(SnapshotTest, CleanFirstStageMount); FRIEND_TEST(SnapshotTest, CreateSnapshot); Loading Loading @@ -676,6 +684,12 @@ class SnapshotManager final : public ISnapshotManager { // Same as above, but for paths only (no major:minor device strings). bool GetMappedImageDevicePath(const std::string& device_name, std::string* device_path); // Wait for a device to be created by ueventd (eg, its symlink or node to be populated). // This is needed for any code that uses device-mapper path in first-stage init. If // |timeout_ms| is empty or the given device is not a path, WaitForDevice immediately // returns true. bool WaitForDevice(const std::string& device, std::chrono::milliseconds timeout_ms); std::string gsid_dir_; std::string metadata_dir_; std::unique_ptr<IDeviceInfo> device_; Loading @@ -683,6 +697,7 @@ class SnapshotManager final : public ISnapshotManager { bool has_local_image_manager_ = false; bool use_first_stage_snapuserd_ = false; bool in_factory_data_reset_ = false; std::function<bool(const std::string&)> uevent_regen_callback_; std::unique_ptr<SnapuserdClient> snapuserd_client_; }; Loading
fs_mgr/libsnapshot/snapshot.cpp +55 −11 Original line number Diff line number Diff line Loading @@ -409,10 +409,12 @@ bool SnapshotManager::MapDmUserCow(LockedFile* lock, const std::string& name, if (!dm.CreateDevice(name, table, path, timeout_ms)) { return false; } if (!WaitForDevice(*path, timeout_ms)) { return false; } auto control_device = "/dev/dm-user/" + misc_name; if (!android::fs_mgr::WaitForFile(control_device, timeout_ms)) { LOG(ERROR) << "Timed out waiting for dm-user misc device: " << control_device; if (!WaitForDevice(control_device, timeout_ms)) { return false; } Loading Loading @@ -1340,10 +1342,12 @@ bool SnapshotManager::PerformSecondStageTransition() { continue; } auto misc_name = user_cow_name; DmTable table; table.Emplace<DmTargetUser>(0, target.spec.length, user_cow_name); table.Emplace<DmTargetUser>(0, target.spec.length, misc_name); if (!dm.LoadTableAndActivate(user_cow_name, table)) { LOG(ERROR) << "Unable to swap tables for " << user_cow_name; LOG(ERROR) << "Unable to swap tables for " << misc_name; continue; } Loading @@ -1369,14 +1373,13 @@ bool SnapshotManager::PerformSecondStageTransition() { } // Wait for ueventd to acknowledge and create the control device node. std::string control_device = "/dev/dm-user/" + user_cow_name; if (!android::fs_mgr::WaitForFile(control_device, 10s)) { LOG(ERROR) << "Could not find control device: " << control_device; std::string control_device = "/dev/dm-user/" + misc_name; if (!WaitForDevice(control_device, 10s)) { continue; } uint64_t base_sectors = snapuserd_client_->InitDmUserCow(user_cow_name, cow_image_device, backing_device); snapuserd_client_->InitDmUserCow(misc_name, cow_image_device, backing_device); if (base_sectors == 0) { // Unrecoverable as metadata reads from cow device failed LOG(FATAL) << "Failed to retrieve base_sectors from Snapuserd"; Loading @@ -1385,7 +1388,7 @@ bool SnapshotManager::PerformSecondStageTransition() { CHECK(base_sectors == target.spec.length); if (!snapuserd_client_->AttachDmUser(user_cow_name)) { if (!snapuserd_client_->AttachDmUser(misc_name)) { // This error is unrecoverable. We cannot proceed because reads to // the underlying device will fail. LOG(FATAL) << "Could not initialize snapuserd for " << user_cow_name; Loading Loading @@ -1924,13 +1927,20 @@ bool SnapshotManager::MapPartitionWithSnapshot(LockedFile* lock, if (live_snapshot_status->compression_enabled()) { auto name = GetDmUserCowName(params.GetPartitionName()); // :TODO: need to force init to process uevents for these in first-stage. std::string cow_path; if (!GetMappedImageDevicePath(cow_name, &cow_path)) { LOG(ERROR) << "Could not determine path for: " << cow_name; return false; } // Ensure both |base_path| and |cow_path| are created, for snapuserd. if (!WaitForDevice(base_path, remaining_time)) { return false; } if (!WaitForDevice(cow_path, remaining_time)) { return false; } std::string new_cow_device; if (!MapDmUserCow(lock, name, cow_path, base_path, remaining_time, &new_cow_device)) { LOG(ERROR) << "Could not map dm-user device for partition " Loading Loading @@ -2070,7 +2080,7 @@ bool SnapshotManager::UnmapCowDevices(LockedFile* lock, const std::string& name) if (!EnsureSnapuserdConnected()) { return false; } if (!dm.DeleteDevice(dm_user_name)) { if (!dm.DeleteDeviceIfExists(dm_user_name)) { LOG(ERROR) << "Cannot unmap " << dm_user_name; return false; } Loading Loading @@ -3302,5 +3312,39 @@ bool SnapshotManager::GetMappedImageDeviceStringOrPath(const std::string& device return true; } bool SnapshotManager::WaitForDevice(const std::string& device, std::chrono::milliseconds timeout_ms) { if (!android::base::StartsWith(device, "/")) { return true; } // In first-stage init, we rely on init setting a callback which can // regenerate uevents and populate /dev for us. if (uevent_regen_callback_) { if (!uevent_regen_callback_(device)) { LOG(ERROR) << "Failed to find device after regenerating uevents: " << device; return false; } return true; } // Otherwise, the only kind of device we need to wait for is a dm-user // misc device. Normal calls to DeviceMapper::CreateDevice() guarantee // the path has been created. if (!android::base::StartsWith(device, "/dev/dm-user/")) { return true; } if (timeout_ms.count() == 0) { LOG(ERROR) << "No timeout was specified to wait for device: " << device; return false; } if (!android::fs_mgr::WaitForFile(device, timeout_ms)) { LOG(ERROR) << "Timed out waiting for device to appear: " << device; return false; } return true; } } // namespace snapshot } // namespace android