libsnapshot: pass build config o_direct

If o_direct is enabled in build configuration, forward this argument to
snapuserd when the daemon is started.

Bug: 332255580
Test: th
Change-Id: I8e4be5503665031735dba2ebc748e209c45df942
diff --git a/fs_mgr/libsnapshot/android/snapshot/snapshot.proto b/fs_mgr/libsnapshot/android/snapshot/snapshot.proto
index d04c9c1..2e948dd 100644
--- a/fs_mgr/libsnapshot/android/snapshot/snapshot.proto
+++ b/fs_mgr/libsnapshot/android/snapshot/snapshot.proto
@@ -124,8 +124,7 @@
     // Default value is 32, can be set lower for low mem devices
     uint32 read_ahead_size = 17;
 
-    // Enable direct reads on source device
-    bool o_direct = 18;
+    reserved 18;
 
     // Blocks size to be verified at once
     uint64 verify_block_size = 19;
@@ -227,6 +226,9 @@
 
     // legacy dm-snapshot based snapuserd
     bool legacy_snapuserd = 11;
+
+    // Enable direct reads from source device
+    bool o_direct = 12;
 }
 
 // Next: 10
diff --git a/fs_mgr/libsnapshot/include/libsnapshot/snapshot.h b/fs_mgr/libsnapshot/include/libsnapshot/snapshot.h
index 3ccc3db..6d422c6 100644
--- a/fs_mgr/libsnapshot/include/libsnapshot/snapshot.h
+++ b/fs_mgr/libsnapshot/include/libsnapshot/snapshot.h
@@ -410,6 +410,7 @@
     FRIEND_TEST(SnapshotTest, CreateSnapshot);
     FRIEND_TEST(SnapshotTest, FirstStageMountAfterRollback);
     FRIEND_TEST(SnapshotTest, FirstStageMountAndMerge);
+    FRIEND_TEST(SnapshotTest, FlagCheck);
     FRIEND_TEST(SnapshotTest, FlashSuperDuringMerge);
     FRIEND_TEST(SnapshotTest, FlashSuperDuringUpdate);
     FRIEND_TEST(SnapshotTest, MapPartialSnapshot);
@@ -425,6 +426,7 @@
     FRIEND_TEST(SnapshotUpdateTest, DataWipeAfterRollback);
     FRIEND_TEST(SnapshotUpdateTest, DataWipeRollbackInRecovery);
     FRIEND_TEST(SnapshotUpdateTest, DataWipeWithStaleSnapshots);
+    FRIEND_TEST(SnapshotUpdateTest, FlagCheck);
     FRIEND_TEST(SnapshotUpdateTest, FullUpdateFlow);
     FRIEND_TEST(SnapshotUpdateTest, MergeCannotRemoveCow);
     FRIEND_TEST(SnapshotUpdateTest, MergeInRecovery);
@@ -822,6 +824,9 @@
     // Check if io_uring API's need to be used
     bool UpdateUsesIouring(LockedFile* lock);
 
+    // Check if direct reads are enabled for the source image
+    bool UpdateUsesODirect(LockedFile* lock);
+
     // Wrapper around libdm, with diagnostics.
     bool DeleteDeviceIfExists(const std::string& name,
                               const std::chrono::milliseconds& timeout_ms = {});
diff --git a/fs_mgr/libsnapshot/partition_cow_creator.h b/fs_mgr/libsnapshot/partition_cow_creator.h
index a75d993..1adbba2 100644
--- a/fs_mgr/libsnapshot/partition_cow_creator.h
+++ b/fs_mgr/libsnapshot/partition_cow_creator.h
@@ -62,6 +62,9 @@
     uint64_t compression_factor;
     uint32_t read_ahead_size;
 
+    // Enable direct reads on source device
+    bool o_direct;
+
     // True if multi-threaded compression should be enabled
     bool enable_threading;
 
diff --git a/fs_mgr/libsnapshot/snapshot.cpp b/fs_mgr/libsnapshot/snapshot.cpp
index 8620620..c01360e 100644
--- a/fs_mgr/libsnapshot/snapshot.cpp
+++ b/fs_mgr/libsnapshot/snapshot.cpp
@@ -1697,6 +1697,9 @@
         if (UpdateUsesIouring(lock.get())) {
             snapuserd_argv->emplace_back("-io_uring");
         }
+        if (UpdateUsesODirect(lock.get())) {
+            snapuserd_argv->emplace_back("-o_direct");
+        }
     }
 
     size_t num_cows = 0;
@@ -2114,6 +2117,11 @@
     return update_status.io_uring_enabled();
 }
 
+bool SnapshotManager::UpdateUsesODirect(LockedFile* lock) {
+    SnapshotUpdateStatus update_status = ReadSnapshotUpdateStatus(lock);
+    return update_status.o_direct();
+}
+
 /*
  * Please see b/304829384 for more details.
  *
@@ -3016,6 +3024,7 @@
         status.set_userspace_snapshots(old_status.userspace_snapshots());
         status.set_io_uring_enabled(old_status.io_uring_enabled());
         status.set_legacy_snapuserd(old_status.legacy_snapuserd());
+        status.set_o_direct(old_status.o_direct());
     }
     return WriteSnapshotUpdateStatus(lock, status);
 }
@@ -3310,17 +3319,19 @@
     }
     auto read_ahead_size =
             android::base::GetUintProperty<uint>("ro.virtual_ab.read_ahead_size", kReadAheadSizeKb);
-    PartitionCowCreator cow_creator{.target_metadata = target_metadata.get(),
-                                    .target_suffix = target_suffix,
-                                    .target_partition = nullptr,
-                                    .current_metadata = current_metadata.get(),
-                                    .current_suffix = current_suffix,
-                                    .update = nullptr,
-                                    .extra_extents = {},
-                                    .using_snapuserd = using_snapuserd,
-                                    .compression_algorithm = compression_algorithm,
-                                    .compression_factor = compression_factor,
-                                    .read_ahead_size = read_ahead_size};
+    PartitionCowCreator cow_creator{
+            .target_metadata = target_metadata.get(),
+            .target_suffix = target_suffix,
+            .target_partition = nullptr,
+            .current_metadata = current_metadata.get(),
+            .current_suffix = current_suffix,
+            .update = nullptr,
+            .extra_extents = {},
+            .using_snapuserd = using_snapuserd,
+            .compression_algorithm = compression_algorithm,
+            .compression_factor = compression_factor,
+            .read_ahead_size = read_ahead_size,
+    };
 
     if (dap_metadata.vabc_feature_set().has_threaded()) {
         cow_creator.enable_threading = dap_metadata.vabc_feature_set().threaded();
@@ -3388,10 +3399,13 @@
             status.set_io_uring_enabled(true);
             LOG(INFO) << "io_uring for snapshots enabled";
         }
-
+        if (GetODirectEnabledProperty()) {
+            status.set_o_direct(true);
+            LOG(INFO) << "o_direct for source image enabled";
+        }
         if (is_legacy_snapuserd) {
-            LOG(INFO) << "Setting legacy_snapuserd to true";
             status.set_legacy_snapuserd(true);
+            LOG(INFO) << "Setting legacy_snapuserd to true";
         }
     } else if (legacy_compression) {
         LOG(INFO) << "Virtual A/B using legacy snapuserd";
@@ -3827,6 +3841,7 @@
     ss << "Using snapuserd: " << update_status.using_snapuserd() << std::endl;
     ss << "Using userspace snapshots: " << update_status.userspace_snapshots() << std::endl;
     ss << "Using io_uring: " << update_status.io_uring_enabled() << std::endl;
+    ss << "Using o_direct: " << update_status.o_direct() << std::endl;
     ss << "Using XOR compression: " << GetXorCompressionEnabledProperty() << std::endl;
     ss << "Current slot: " << device_->GetSlotSuffix() << std::endl;
     ss << "Boot indicator: booting from " << GetCurrentSlot() << " slot" << std::endl;
diff --git a/fs_mgr/libsnapshot/snapshot_test.cpp b/fs_mgr/libsnapshot/snapshot_test.cpp
index 80dad17..3299ec5 100644
--- a/fs_mgr/libsnapshot/snapshot_test.cpp
+++ b/fs_mgr/libsnapshot/snapshot_test.cpp
@@ -2647,6 +2647,41 @@
     ASSERT_TRUE(sm->CreateUpdateSnapshots(manifest_));
 }
 
+TEST_F(SnapshotTest, FlagCheck) {
+    if (!snapuserd_required_) {
+        GTEST_SKIP() << "Skipping snapuserd test";
+    }
+    ASSERT_TRUE(AcquireLock());
+
+    SnapshotUpdateStatus status = sm->ReadSnapshotUpdateStatus(lock_.get());
+
+    // Set flags in proto
+    status.set_o_direct(true);
+    status.set_io_uring_enabled(true);
+    status.set_userspace_snapshots(true);
+
+    sm->WriteSnapshotUpdateStatus(lock_.get(), status);
+    // Ensure a connection to the second-stage daemon, but use the first-stage
+    // code paths thereafter.
+    ASSERT_TRUE(sm->EnsureSnapuserdConnected());
+    sm->set_use_first_stage_snapuserd(true);
+
+    auto init = NewManagerForFirstStageMount("_b");
+    ASSERT_NE(init, nullptr);
+
+    lock_ = nullptr;
+
+    std::vector<std::string> snapuserd_argv;
+    ASSERT_TRUE(init->PerformInitTransition(SnapshotManager::InitTransition::SELINUX_DETACH,
+                                            &snapuserd_argv));
+    ASSERT_TRUE(std::find(snapuserd_argv.begin(), snapuserd_argv.end(), "-o_direct") !=
+                snapuserd_argv.end());
+    ASSERT_TRUE(std::find(snapuserd_argv.begin(), snapuserd_argv.end(), "-io_uring") !=
+                snapuserd_argv.end());
+    ASSERT_TRUE(std::find(snapuserd_argv.begin(), snapuserd_argv.end(), "-user_snapshot") !=
+                snapuserd_argv.end());
+}
+
 class FlashAfterUpdateTest : public SnapshotUpdateTest,
                              public WithParamInterface<std::tuple<uint32_t, bool>> {
   public:
diff --git a/fs_mgr/libsnapshot/snapuserd/snapuserd_daemon.cpp b/fs_mgr/libsnapshot/snapuserd/snapuserd_daemon.cpp
index 0ebe543..67e9e52 100644
--- a/fs_mgr/libsnapshot/snapuserd/snapuserd_daemon.cpp
+++ b/fs_mgr/libsnapshot/snapuserd/snapuserd_daemon.cpp
@@ -29,6 +29,7 @@
             "If true, perform a socket hand-off with an existing snapuserd instance, then exit.");
 DEFINE_bool(user_snapshot, false, "If true, user-space snapshots are used");
 DEFINE_bool(io_uring, false, "If true, io_uring feature is enabled");
+DEFINE_bool(o_direct, false, "If true, enable direct reads on source device");
 
 namespace android {
 namespace snapshot {
@@ -67,7 +68,6 @@
     if (!user_snapshots) {
         user_snapshots = IsUserspaceSnapshotsEnabled();
     }
-
     if (user_snapshots) {
         LOG(INFO) << "Starting daemon for user-space snapshots.....";
         return StartServerForUserspaceSnapshots(arg_start, argc, argv);
@@ -109,11 +109,13 @@
 
     for (int i = arg_start; i < argc; i++) {
         auto parts = android::base::Split(argv[i], ",");
+
         if (parts.size() != 4) {
-            LOG(ERROR) << "Malformed message, expected four sub-arguments.";
+            LOG(ERROR) << "Malformed message, expected at least four sub-arguments.";
             return false;
         }
-        auto handler = user_server_.AddHandler(parts[0], parts[1], parts[2], parts[3]);
+        auto handler =
+                user_server_.AddHandler(parts[0], parts[1], parts[2], parts[3], FLAGS_o_direct);
         if (!handler || !user_server_.StartHandler(parts[0])) {
             return false;
         }
diff --git a/fs_mgr/libsnapshot/utility.cpp b/fs_mgr/libsnapshot/utility.cpp
index fe2d95c..7eaaca9 100644
--- a/fs_mgr/libsnapshot/utility.cpp
+++ b/fs_mgr/libsnapshot/utility.cpp
@@ -199,7 +199,7 @@
 }
 
 std::ostream& operator<<(std::ostream& os, const Now&) {
-    struct tm now {};
+    struct tm now{};
     time_t t = time(nullptr);
     localtime_r(&t, &now);
     return os << std::put_time(&now, "%Y%m%d-%H%M%S");
@@ -272,6 +272,11 @@
     return fetcher->GetBoolProperty("ro.virtual_ab.compression.xor.enabled", false);
 }
 
+bool GetODirectEnabledProperty() {
+    auto fetcher = IPropertyFetcher::GetInstance();
+    return fetcher->GetBoolProperty("ro.virtual_ab.o_direct.enabled", false);
+}
+
 std::string GetOtherPartitionName(const std::string& name) {
     auto suffix = android::fs_mgr::GetPartitionSlotSuffix(name);
     CHECK(suffix == "_a" || suffix == "_b");
diff --git a/fs_mgr/libsnapshot/utility.h b/fs_mgr/libsnapshot/utility.h
index f956a05..7dae942 100644
--- a/fs_mgr/libsnapshot/utility.h
+++ b/fs_mgr/libsnapshot/utility.h
@@ -133,6 +133,7 @@
 bool GetUserspaceSnapshotsEnabledProperty();
 bool GetIouringEnabledProperty();
 bool GetXorCompressionEnabledProperty();
+bool GetODirectEnabledProperty();
 
 bool CanUseUserspaceSnapshots();
 bool IsDmSnapshotTestingEnabled();