Snap for 15110852 from c58ca616ec15ef60ac5e51802b8fb4f3b40874e6 to 26Q2-release

Change-Id: Ic89d3e83d86e77dd92869590c4c6e00540986572
diff --git a/common/prefs.cc b/common/prefs.cc
index 3d69238..fe163f7 100644
--- a/common/prefs.cc
+++ b/common/prefs.cc
@@ -199,13 +199,17 @@
     LOG(ERROR) << "prefs directory does not exist: " << source_directory;
     return false;
   }
-  // Copy the directory.
   std::error_code e;
-  std::filesystem::copy(source_directory, destination_directory, e);
+  std::filesystem::copy(source_directory,
+                        destination_directory,
+                        std::filesystem::copy_options::recursive,
+                        e);
   if (e) {
     LOG(ERROR) << "failed to copy prefs to prefs_tmp: " << e.message();
+    DeleteTemporaryPrefs();
     return false;
   }
+  utils::FsyncDirectoryContents(destination_directory.c_str());
 
   return true;
 }
diff --git a/common/prefs_unittest.cc b/common/prefs_unittest.cc
index f0d620f..1cdccf0 100644
--- a/common/prefs_unittest.cc
+++ b/common/prefs_unittest.cc
@@ -581,6 +581,79 @@
   prefs_.RemoveObserver(kInvalidKey, &mock_obserser);
 }
 
+TEST_F(PrefsTest, TransactionAcidTest) {
+  const char kKey1[] = "key1";
+  const char kKey2[] = "key2";
+  const char kValue1[] = "value1";
+  const char kValue2[] = "value2";
+  const char kNewValue1[] = "new-value1";
+
+  // Initial state
+  ASSERT_TRUE(prefs_.SetString(kKey1, kValue1));
+  ASSERT_TRUE(prefs_.SetString(kKey2, kValue2));
+
+  // Start transaction
+  ASSERT_TRUE(prefs_.StartTransaction());
+
+  // Modify key1 in transaction
+  ASSERT_TRUE(prefs_.SetString(kKey1, kNewValue1));
+
+  // Verify Isolation: The original directory should remain UNCHANGED until commit.
+  string val;
+  ASSERT_TRUE(base::ReadFileToString(prefs_dir_.Append(kKey1), &val));
+  ASSERT_EQ(kValue1, val);
+
+  // Unmodified key2 should also be preserved in the original directory.
+  ASSERT_TRUE(base::ReadFileToString(prefs_dir_.Append(kKey2), &val));
+  ASSERT_EQ(kValue2, val);
+
+  // Current prefs object should see the new value (it reads from the transaction state).
+  ASSERT_TRUE(prefs_.GetString(kKey1, &val));
+  ASSERT_EQ(kNewValue1, val);
+
+  // Submit transaction (Atomicity & Durability)
+  ASSERT_TRUE(prefs_.SubmitTransaction());
+
+  // After submit, the main directory should have the new values.
+  ASSERT_TRUE(prefs_.GetString(kKey1, &val));
+  ASSERT_EQ(kNewValue1, val);
+  ASSERT_TRUE(prefs_.GetString(kKey2, &val));
+  ASSERT_EQ(kValue2, val);
+
+  // Verify persistence with a fresh Prefs object
+  Prefs fresh_prefs;
+  ASSERT_TRUE(fresh_prefs.Init(prefs_dir_));
+  ASSERT_TRUE(fresh_prefs.GetString(kKey1, &val));
+  ASSERT_EQ(kNewValue1, val);
+}
+
+TEST_F(PrefsTest, TransactionCancelAcidTest) {
+  const char kKey[] = "cancel-key";
+  const char kOldValue[] = "old";
+  const char kNewValue[] = "new";
+
+  ASSERT_TRUE(prefs_.SetString(kKey, kOldValue));
+  ASSERT_TRUE(prefs_.StartTransaction());
+  ASSERT_TRUE(prefs_.SetString(kKey, kNewValue));
+
+  // Should see new value in current context
+  string val;
+  ASSERT_TRUE(prefs_.GetString(kKey, &val));
+  ASSERT_EQ(kNewValue, val);
+
+  // Cancel transaction
+  ASSERT_TRUE(prefs_.CancelTransaction());
+
+  // Should be back to old value everywhere
+  ASSERT_TRUE(prefs_.GetString(kKey, &val));
+  ASSERT_EQ(kOldValue, val);
+
+  Prefs fresh_prefs;
+  ASSERT_TRUE(fresh_prefs.Init(prefs_dir_));
+  ASSERT_TRUE(fresh_prefs.GetString(kKey, &val));
+  ASSERT_EQ(kOldValue, val);
+}
+
 TEST_F(PrefsTest, MultiNamespaceKeyTest) {
   MultiNamespaceKeyTest();
 }
diff --git a/payload_consumer/delta_performer.cc b/payload_consumer/delta_performer.cc
index a6144f7..dab6b5d 100644
--- a/payload_consumer/delta_performer.cc
+++ b/payload_consumer/delta_performer.cc
@@ -84,7 +84,6 @@
 const uint64_t DeltaPerformer::kCheckpointFrequencySeconds = 1;
 
 namespace {
-const int kUpdateStateOperationInvalid = -1;
 const int kMaxResumedUpdateFailures = 10;
 
 }  // namespace
diff --git a/payload_consumer/delta_performer.h b/payload_consumer/delta_performer.h
index 75f24d1..6a2c273 100644
--- a/payload_consumer/delta_performer.h
+++ b/payload_consumer/delta_performer.h
@@ -62,6 +62,7 @@
   static const unsigned kProgressDownloadWeight;
   static const unsigned kProgressOperationsWeight;
   static const uint64_t kCheckpointFrequencySeconds;
+  static constexpr int64_t kUpdateStateOperationInvalid = -1;
 
   DeltaPerformer(
       PrefsInterface* prefs,
diff --git a/payload_consumer/delta_performer_unittest.cc b/payload_consumer/delta_performer_unittest.cc
index 40221bd..9b6ba4b 100644
--- a/payload_consumer/delta_performer_unittest.cc
+++ b/payload_consumer/delta_performer_unittest.cc
@@ -1369,4 +1369,36 @@
             performer_.VerifyPayload(payload_hash, payload_data.size()));
 }
 
+TEST_F(DeltaPerformerTest, CanResumeUpdateRelaxedCheckTest) {
+  const std::string payload_id = "test-payload-id";
+  const std::string wrong_payload_id = "wrong-payload-id";
+
+  // Set up mandatory prefs for resumption
+  ASSERT_TRUE(prefs_.SetInt64(kPrefsManifestMetadataSize, 100));
+  ASSERT_TRUE(prefs_.SetInt64(kPrefsManifestSignatureSize, 50));
+  ASSERT_TRUE(prefs_.SetInt64(kPrefsUpdateStateNextDataOffset, 0));
+  ASSERT_TRUE(prefs_.SetString(kPrefsUpdateStateSHA256Context, "some-context"));
+
+  // 1. next_operation = 0 (initial state), should SUCCEED now
+  ASSERT_TRUE(prefs_.SetInt64(kPrefsUpdateStateNextOperation, 1));
+  ASSERT_TRUE(prefs_.SetString(kPrefsUpdateCheckResponseHash, payload_id));
+  ASSERT_TRUE(DeltaPerformer::CanResumeUpdate(&prefs_, payload_id));
+
+  // 2. next_operation = 100, should SUCCEED
+  ASSERT_TRUE(prefs_.SetInt64(kPrefsUpdateStateNextOperation, 100));
+  ASSERT_TRUE(DeltaPerformer::CanResumeUpdate(&prefs_, payload_id));
+
+  // 3. Hash mismatch, should FAIL
+  ASSERT_FALSE(DeltaPerformer::CanResumeUpdate(&prefs_, wrong_payload_id));
+
+  // 4. Empty hash in prefs, should FAIL
+  ASSERT_TRUE(prefs_.Delete(kPrefsUpdateCheckResponseHash));
+  ASSERT_FALSE(DeltaPerformer::CanResumeUpdate(&prefs_, payload_id));
+
+  // 5. Missing mandatory metadata size, should FAIL
+  ASSERT_TRUE(prefs_.SetString(kPrefsUpdateCheckResponseHash, payload_id));
+  ASSERT_TRUE(prefs_.Delete(kPrefsManifestMetadataSize));
+  ASSERT_FALSE(DeltaPerformer::CanResumeUpdate(&prefs_, payload_id));
+}
+
 }  // namespace chromeos_update_engine