Mark block device as read-only before mounting.
Mounting a block device as read-only still allows the filesystem to
write to the block device, while keeping the user-facing filesystem
interface "read-only". This behavior will make dm-verity to fail after
reboot if any block is modified during postinstall.
This patch marks the block device as read-only before mounting it, and
marks them read-only or read-write before using them during the update.
Bug: 27859604
TEST=Added logging and deployed an update to brillo, devices are marked RO/RW as needed during a delta update.
Change-Id: I781293cc0b3447dac708470ba6efad4103bf2a58
diff --git a/common/utils.cc b/common/utils.cc
index 1b718a4..49502ff 100644
--- a/common/utils.cc
+++ b/common/utils.cc
@@ -638,6 +638,30 @@
return true;
}
+bool SetBlockDeviceReadOnly(const string& device, bool read_only) {
+ int fd = HANDLE_EINTR(open(device.c_str(), O_RDONLY | O_CLOEXEC));
+ if (fd < 0) {
+ PLOG(ERROR) << "Opening block device " << device;
+ return false;
+ }
+ ScopedFdCloser fd_closer(&fd);
+ // We take no action if not needed.
+ int read_only_flag;
+ int expected_flag = read_only ? 1 : 0;
+ int rc = ioctl(fd, BLKROGET, &read_only_flag);
+ // In case of failure reading the setting we will try to set it anyway.
+ if (rc == 0 && read_only_flag == expected_flag)
+ return true;
+
+ rc = ioctl(fd, BLKROSET, &expected_flag);
+ if (rc != 0) {
+ PLOG(ERROR) << "Marking block device " << device << " as read_only="
+ << expected_flag;
+ return false;
+ }
+ return true;
+}
+
bool MountFilesystem(const string& device,
const string& mountpoint,
unsigned long mountflags, // NOLINT(runtime/int)
diff --git a/common/utils.h b/common/utils.h
index dbf92d2..3af6e71 100644
--- a/common/utils.h
+++ b/common/utils.h
@@ -178,6 +178,10 @@
// /dev/sda3. Return empty string on error.
std::string MakePartitionNameForMount(const std::string& part_name);
+// Set the read-only attribute on the block device |device| to the value passed
+// in |read_only|. Return whether the operation succeeded.
+bool SetBlockDeviceReadOnly(const std::string& device, bool read_only);
+
// Synchronously mount or unmount a filesystem. Return true on success.
// When mounting, it will attempt to mount the device as the passed filesystem
// type |type|, with the passed |flags| options. If |type| is empty, "ext2",
diff --git a/payload_consumer/delta_performer.cc b/payload_consumer/delta_performer.cc
index de121ed..d955d03 100644
--- a/payload_consumer/delta_performer.cc
+++ b/payload_consumer/delta_performer.cc
@@ -110,6 +110,10 @@
// Opens path for read/write. On success returns an open FileDescriptor
// and sets *err to 0. On failure, sets *err to errno and returns nullptr.
FileDescriptorPtr OpenFile(const char* path, int mode, int* err) {
+ // Try to mark the block device read-only based on the mode. Ignore any
+ // failure since this won't work when passing regular files.
+ utils::SetBlockDeviceReadOnly(path, (mode & O_ACCMODE) == O_RDONLY);
+
FileDescriptorPtr fd = CreateFileDescriptor(path);
#if USE_MTD
// On NAND devices, we can either read, or write, but not both. So here we
diff --git a/payload_consumer/postinstall_runner_action.cc b/payload_consumer/postinstall_runner_action.cc
index bb9ffe4..fa89857 100644
--- a/payload_consumer/postinstall_runner_action.cc
+++ b/payload_consumer/postinstall_runner_action.cc
@@ -134,6 +134,18 @@
return CompletePostinstall(ErrorCode::kPostinstallRunnerError);
}
+#ifdef __ANDROID__
+ // In Chromium OS, the postinstall step is allowed to write to the block
+ // device on the target image, so we don't mark it as read-only and should
+ // be read-write since we just wrote to it during the update.
+
+ // Mark the block device as read-only before mounting for post-install.
+ if (!utils::SetBlockDeviceReadOnly(mountable_device, true)) {
+ return CompletePartitionPostinstall(
+ 1, "Error marking the device " + mountable_device + " read only.");
+ }
+#endif // __ANDROID__
+
if (!utils::MountFilesystem(mountable_device,
fs_mount_dir_,
MS_RDONLY,