merge in nyc-release history after reset to nyc-dev
diff --git a/common/utils.cc b/common/utils.cc
index 304aba2..5d15da0 100644
--- a/common/utils.cc
+++ b/common/utils.cc
@@ -641,6 +641,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 686f117..88c33b7 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 e2613da..faf67a0 100644
--- a/payload_consumer/delta_performer.cc
+++ b/payload_consumer/delta_performer.cc
@@ -109,6 +109,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 db1ec3c..98f4eaf 100644
--- a/payload_consumer/postinstall_runner_action.cc
+++ b/payload_consumer/postinstall_runner_action.cc
@@ -132,6 +132,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,
diff --git a/payload_generator/annotated_operation.cc b/payload_generator/annotated_operation.cc
index b7f3434..e28fe85 100644
--- a/payload_generator/annotated_operation.cc
+++ b/payload_generator/annotated_operation.cc
@@ -38,6 +38,11 @@
 
 bool AnnotatedOperation::SetOperationBlob(const brillo::Blob& blob,
                                           BlobFileWriter* blob_file) {
+  if (blob.empty()) {
+    op.clear_data_offset();
+    op.clear_data_length();
+    return true;
+  }
   off_t data_offset = blob_file->StoreBlob(blob);
   TEST_AND_RETURN_FALSE(data_offset != -1);
   op.set_data_offset(data_offset);
diff --git a/payload_generator/delta_diff_utils.cc b/payload_generator/delta_diff_utils.cc
index 1884681..0782a8c 100644
--- a/payload_generator/delta_diff_utils.cc
+++ b/payload_generator/delta_diff_utils.cc
@@ -480,12 +480,7 @@
     aop.op = operation;
 
     // Write the data
-    if (operation.type() != InstallOperation::MOVE &&
-        operation.type() != InstallOperation::SOURCE_COPY) {
-      TEST_AND_RETURN_FALSE(aop.SetOperationBlob(data, blob_file));
-    } else {
-      TEST_AND_RETURN_FALSE(blob_file->StoreBlob(data) != -1);
-    }
+    TEST_AND_RETURN_FALSE(aop.SetOperationBlob(data, blob_file));
     aops->emplace_back(aop);
   }
   return true;
diff --git a/payload_generator/full_update_generator.cc b/payload_generator/full_update_generator.cc
index 3240606..8fdb6ec 100644
--- a/payload_generator/full_update_generator.cc
+++ b/payload_generator/full_update_generator.cc
@@ -107,9 +107,7 @@
       buffer_in_, version_, &op_blob, &op_type));
 
   aop_->op.set_type(op_type);
-  if (!op_blob.empty()) {
-    TEST_AND_RETURN_FALSE(aop_->SetOperationBlob(op_blob, blob_file_));
-  }
+  TEST_AND_RETURN_FALSE(aop_->SetOperationBlob(op_blob, blob_file_));
   return true;
 }