This is an automated email from the ASF dual-hosted git repository.

yiguolei pushed a commit to branch branch-4.0
in repository https://gitbox.apache.org/repos/asf/doris.git


The following commit(s) were added to refs/heads/branch-4.0 by this push:
     new 5b94bd1ab7f branch-4.0: [fix](backup) reject upload snapshots on 
broken storage path #61251 (#61435)
5b94bd1ab7f is described below

commit 5b94bd1ab7fd0f01bdc499534e98409c5c6a9160
Author: github-actions[bot] 
<41898282+github-actions[bot]@users.noreply.github.com>
AuthorDate: Mon Mar 23 14:00:28 2026 +0800

    branch-4.0: [fix](backup) reject upload snapshots on broken storage path 
#61251 (#61435)
    
    Cherry-picked from #61251
    
    ---------
    
    Co-authored-by: Luwei <[email protected]>
---
 be/src/io/fs/local_file_system.cpp       | 14 +++++++
 be/src/io/fs/local_file_system.h         |  2 +
 be/src/runtime/snapshot_loader.cpp       | 28 +++++++++++++
 be/src/runtime/snapshot_loader.h         |  2 +
 be/test/io/fs/local_file_system_test.cpp |  9 +++++
 be/test/runtime/snapshot_loader_test.cpp | 68 +++++++++++++++++++++++++++++++-
 6 files changed, 122 insertions(+), 1 deletion(-)

diff --git a/be/src/io/fs/local_file_system.cpp 
b/be/src/io/fs/local_file_system.cpp
index 358ba7d5ff1..6dc366a0bfd 100644
--- a/be/src/io/fs/local_file_system.cpp
+++ b/be/src/io/fs/local_file_system.cpp
@@ -412,6 +412,20 @@ bool LocalFileSystem::contain_path(const Path& parent_, 
const Path& sub_) {
     return true;
 }
 
+bool LocalFileSystem::equal_or_sub_path(const Path& parent, const Path& child) 
{
+    auto parent_path = parent.lexically_normal();
+    auto child_path = child.lexically_normal();
+    auto parent_it = parent_path.begin();
+    auto child_it = child_path.begin();
+    for (; parent_it != parent_path.end() && child_it != child_path.end();
+         ++parent_it, ++child_it) {
+        if (*parent_it != *child_it) {
+            return false;
+        }
+    }
+    return parent_it == parent_path.end();
+}
+
 const std::shared_ptr<LocalFileSystem>& global_local_filesystem() {
     static std::shared_ptr<LocalFileSystem> local_fs(new LocalFileSystem());
     return local_fs;
diff --git a/be/src/io/fs/local_file_system.h b/be/src/io/fs/local_file_system.h
index 4540df47c16..c1ffbd22949 100644
--- a/be/src/io/fs/local_file_system.h
+++ b/be/src/io/fs/local_file_system.h
@@ -59,6 +59,8 @@ public:
     Status copy_path(const Path& src, const Path& dest);
     // return true if parent path contain sub path
     static bool contain_path(const Path& parent, const Path& sub);
+    // return true if `parent` equals `child` or contains it as a descendant 
path.
+    static bool equal_or_sub_path(const Path& parent, const Path& child);
     // delete dir or file
     Status delete_directory_or_file(const Path& path);
     // change the file permission of the given path
diff --git a/be/src/runtime/snapshot_loader.cpp 
b/be/src/runtime/snapshot_loader.cpp
index d6fb88a6552..16a5d8bf6ce 100644
--- a/be/src/runtime/snapshot_loader.cpp
+++ b/be/src/runtime/snapshot_loader.cpp
@@ -1332,11 +1332,39 @@ Status SnapshotLoader::_check_local_snapshot_paths(
             LOG(WARNING) << ss.str();
             return Status::RuntimeError(ss.str());
         }
+        if (check_src) {
+            RETURN_IF_ERROR(_check_snapshot_path_on_broken_storage(path));
+        }
     }
     LOG(INFO) << "all local snapshot paths are existing. num: " << 
src_to_dest_path.size();
     return Status::OK();
 }
 
+Status SnapshotLoader::_check_snapshot_path_on_broken_storage(const 
std::string& path) {
+    std::string canonical_path;
+    RETURN_IF_ERROR(io::global_local_filesystem()->canonicalize(path, 
&canonical_path));
+
+    auto broken_paths = _engine.get_broken_paths();
+    for (auto* store : _engine.get_stores(true)) {
+        std::string canonical_store_path;
+        RETURN_IF_ERROR(
+                io::global_local_filesystem()->canonicalize(store->path(), 
&canonical_store_path));
+        if (!io::LocalFileSystem::equal_or_sub_path(canonical_store_path, 
canonical_path)) {
+            continue;
+        }
+        if (!store->is_used() || broken_paths.contains(store->path()) ||
+            broken_paths.contains(canonical_store_path)) {
+            return Status::IOError(
+                    "snapshot path is on broken storage path, 
snapshot_path={}, "
+                    "storage_path={}",
+                    canonical_path, canonical_store_path);
+        }
+        break;
+    }
+
+    return Status::OK();
+}
+
 Status SnapshotLoader::_get_existing_files_from_local(const std::string& 
local_path,
                                                       
std::vector<std::string>* local_files) {
     bool exists = true;
diff --git a/be/src/runtime/snapshot_loader.h b/be/src/runtime/snapshot_loader.h
index b023c0ef245..222a4b6ba87 100644
--- a/be/src/runtime/snapshot_loader.h
+++ b/be/src/runtime/snapshot_loader.h
@@ -127,6 +127,8 @@ private:
     Status _replace_tablet_id(const std::string& file_name, int64_t tablet_id,
                               std::string* new_file_name);
 
+    Status _check_snapshot_path_on_broken_storage(const std::string& path);
+
     Status _check_local_snapshot_paths(const std::map<std::string, 
std::string>& src_to_dest_path,
                                        bool check_src);
 
diff --git a/be/test/io/fs/local_file_system_test.cpp 
b/be/test/io/fs/local_file_system_test.cpp
index c930ba72eab..450c3daea8f 100644
--- a/be/test/io/fs/local_file_system_test.cpp
+++ b/be/test/io/fs/local_file_system_test.cpp
@@ -467,4 +467,13 @@ TEST_F(LocalFileSystemTest, TestConvertToAbsPath) {
     st = doris::io::LocalFileSystem::convert_to_abs_path("hdfs:/abc", 
abs_path);
     ASSERT_TRUE(!st.ok());
 }
+
+TEST_F(LocalFileSystemTest, TestEqualOrSubPath) {
+    EXPECT_TRUE(io::LocalFileSystem::equal_or_sub_path("/data/store", 
"/data/store"));
+    EXPECT_TRUE(io::LocalFileSystem::equal_or_sub_path("/data/store", 
"/data/store/snapshot"));
+    
EXPECT_TRUE(io::LocalFileSystem::equal_or_sub_path("/data/store/./snapshot",
+                                                       
"/data/store/snapshot/dir/../dir"));
+    EXPECT_FALSE(io::LocalFileSystem::equal_or_sub_path("/data/store1", 
"/data/store11/snapshot"));
+    
EXPECT_FALSE(io::LocalFileSystem::equal_or_sub_path("/data/store/snapshot", 
"/data/store"));
+}
 } // namespace doris
diff --git a/be/test/runtime/snapshot_loader_test.cpp 
b/be/test/runtime/snapshot_loader_test.cpp
index 9b6e64a7d31..af675abfea5 100644
--- a/be/test/runtime/snapshot_loader_test.cpp
+++ b/be/test/runtime/snapshot_loader_test.cpp
@@ -92,7 +92,7 @@ static std::string hostname;
 static std::string address;
 static ClusterInfo cluster_info;
 
-static void set_up() {
+static void set_up(bool mark_storage_root_broken = false) {
     char buffer[MAX_PATH_LEN];
     EXPECT_NE(getcwd(buffer, MAX_PATH_LEN), nullptr);
     storage_root_path = std::string(buffer) + "/snapshot_data_test";
@@ -105,6 +105,9 @@ static void set_up() {
 
     doris::EngineOptions options;
     options.store_paths = paths;
+    if (mark_storage_root_broken) {
+        options.broken_paths.insert(storage_root_path);
+    }
     options.backend_uid = UniqueId::gen_uid();
     auto engine = std::make_unique<StorageEngine>(options);
     engine_ref = engine.get();
@@ -267,6 +270,15 @@ public:
     static void TearDownTestSuite() { tear_down(); }
 };
 
+class BrokenSnapshotLoaderTest : public ::testing::Test {
+public:
+    BrokenSnapshotLoaderTest() {}
+    ~BrokenSnapshotLoaderTest() {}
+    static void SetUpTestSuite() { set_up(true); }
+
+    static void TearDownTestSuite() { tear_down(); }
+};
+
 TEST_F(SnapshotLoaderTest, NormalCase) {
     StorageEngine engine({});
     SnapshotLoader loader(engine, ExecEnv::GetInstance(), 1L, 2L);
@@ -326,6 +338,60 @@ TEST_F(SnapshotLoaderTest, NormalCase) {
     EXPECT_EQ(10005, tablet_id);
 }
 
+TEST_F(SnapshotLoaderTest, AcceptHealthySnapshotPath) {
+    SnapshotLoader loader(*engine_ref, ExecEnv::GetInstance(), 1L, 2L);
+    auto snapshot_path =
+            fmt::format("{}/snapshot/20260311120000.0.86400/10001/12345", 
storage_root_path);
+    std::filesystem::remove_all(snapshot_path);
+    std::filesystem::create_directories(snapshot_path);
+
+    std::map<std::string, std::string> src_to_dest;
+    src_to_dest[snapshot_path] = "unused";
+
+    auto st = loader._check_local_snapshot_paths(src_to_dest, true);
+    ASSERT_TRUE(st.ok()) << st;
+    std::filesystem::remove_all(snapshot_path);
+}
+
+TEST_F(BrokenSnapshotLoaderTest, RejectBrokenSnapshotPath) {
+    SnapshotLoader loader(*engine_ref, ExecEnv::GetInstance(), 1L, 2L);
+    auto snapshot_path =
+            fmt::format("{}/snapshot/20260311120000.0.86400/10001/12345", 
storage_root_path);
+    std::filesystem::remove_all(snapshot_path);
+    std::filesystem::create_directories(snapshot_path);
+
+    std::map<std::string, std::string> src_to_dest;
+    src_to_dest[snapshot_path] = "unused";
+
+    auto st = loader._check_local_snapshot_paths(src_to_dest, true);
+    EXPECT_FALSE(st.ok());
+    EXPECT_TRUE(st.is<ErrorCode::IO_ERROR>()) << st;
+    EXPECT_NE(st.to_string().find("broken storage path"), std::string::npos) 
<< st;
+    std::filesystem::remove_all(snapshot_path);
+}
+
+TEST_F(BrokenSnapshotLoaderTest, RejectBrokenSnapshotPathAfterCanonicalize) {
+    SnapshotLoader loader(*engine_ref, ExecEnv::GetInstance(), 1L, 2L);
+    auto snapshot_path =
+            fmt::format("{}/snapshot/20260311120001.0.86400/10001/12345", 
storage_root_path);
+    auto symlink_path = fmt::format("{}/snapshot-link", storage_root_path);
+    std::filesystem::remove_all(snapshot_path);
+    std::filesystem::remove(symlink_path);
+    std::filesystem::create_directories(snapshot_path);
+    std::filesystem::create_directory_symlink(snapshot_path, symlink_path);
+
+    std::map<std::string, std::string> src_to_dest;
+    src_to_dest[symlink_path] = "unused";
+
+    auto st = loader._check_local_snapshot_paths(src_to_dest, true);
+    EXPECT_FALSE(st.ok());
+    EXPECT_TRUE(st.is<ErrorCode::IO_ERROR>()) << st;
+    EXPECT_NE(st.to_string().find("broken storage path"), std::string::npos) 
<< st;
+
+    std::filesystem::remove(symlink_path);
+    std::filesystem::remove_all(snapshot_path);
+}
+
 TEST_F(SnapshotLoaderTest, DirMoveTaskIsIdempotent) {
     // 1. create a tablet
     int64_t tablet_id = 111;


---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]

Reply via email to