Lazy unmount postinstall if it fails to unmount.
If postinstall forks a new child that's not killed when killing the main
postinstall process or if postinstall makes any other running process to
hold a file descriptor open in the mounted filesystem, the filesystem is
busy an we can't unmount /postinstall. Since the postinstall mountpoint is
fix in Android, we need to force a lazy unmount in order for the process
to succeed in a future run.
This case can only occur when canceling an update during postinstall (for
example if the update was retried from the server) and would otherwise
clear itself after a few unmount retries if the process using the fd
stops using it.
Bug: 31021934
Test: Added native tests to excersice this case.
Change-Id: I69404658f481082b944f2ea6077e9a754a690ae6
diff --git a/common/utils_unittest.cc b/common/utils_unittest.cc
index b5887e3..eaae023 100644
--- a/common/utils_unittest.cc
+++ b/common/utils_unittest.cc
@@ -16,7 +16,9 @@
#include "update_engine/common/utils.h"
+#include <fcntl.h>
#include <stdint.h>
+#include <sys/mount.h>
#include <sys/stat.h>
#include <sys/types.h>
@@ -441,4 +443,37 @@
EXPECT_TRUE(BoolMacroTestHelper());
}
+TEST(UtilsTest, UnmountFilesystemFailureTest) {
+ EXPECT_FALSE(utils::UnmountFilesystem("/path/to/non-existing-dir"));
+}
+
+TEST(UtilsTest, UnmountFilesystemBusyFailureTest) {
+ string tmp_image;
+ EXPECT_TRUE(utils::MakeTempFile("img.XXXXXX", &tmp_image, nullptr));
+ ScopedPathUnlinker tmp_image_unlinker(tmp_image);
+
+ EXPECT_TRUE(base::CopyFile(
+ test_utils::GetBuildArtifactsPath().Append("gen/disk_ext2_4k.img"),
+ base::FilePath(tmp_image)));
+
+ base::ScopedTempDir mnt_dir;
+ EXPECT_TRUE(mnt_dir.CreateUniqueTempDir());
+
+ string loop_dev;
+ test_utils::ScopedLoopbackDeviceBinder loop_binder(
+ tmp_image, true, &loop_dev);
+
+ // This is the actual test part. While we hold a file descriptor open for the
+ // mounted filesystem, umount should still succeed.
+ EXPECT_TRUE(utils::MountFilesystem(
+ loop_dev, mnt_dir.path().value(), MS_RDONLY, "ext4", ""));
+ string target_file = mnt_dir.path().Append("empty-file").value();
+ int fd = HANDLE_EINTR(open(target_file.c_str(), O_RDONLY));
+ EXPECT_GE(fd, 0);
+ EXPECT_TRUE(utils::UnmountFilesystem(mnt_dir.path().value()));
+ IGNORE_EINTR(close(fd));
+ // The filesystem was already unmounted so this call should fail.
+ EXPECT_FALSE(utils::UnmountFilesystem(mnt_dir.path().value()));
+}
+
} // namespace chromeos_update_engine