Coverage Report

Created: 2026-05-26 16:56

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
be/src/io/fs/local_file_system.cpp
Line
Count
Source
1
// Licensed to the Apache Software Foundation (ASF) under one
2
// or more contributor license agreements.  See the NOTICE file
3
// distributed with this work for additional information
4
// regarding copyright ownership.  The ASF licenses this file
5
// to you under the Apache License, Version 2.0 (the
6
// "License"); you may not use this file except in compliance
7
// with the License.  You may obtain a copy of the License at
8
//
9
//   http://www.apache.org/licenses/LICENSE-2.0
10
//
11
// Unless required by applicable law or agreed to in writing,
12
// software distributed under the License is distributed on an
13
// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
14
// KIND, either express or implied.  See the License for the
15
// specific language governing permissions and limitations
16
// under the License.
17
18
#include "io/fs/local_file_system.h"
19
20
#include <fcntl.h>
21
#include <fmt/format.h>
22
#include <glob.h>
23
#include <glog/logging.h>
24
#include <openssl/md5.h>
25
#include <sys/mman.h>
26
#include <sys/stat.h>
27
#include <unistd.h>
28
29
#include <filesystem>
30
#include <iomanip>
31
#include <istream>
32
#include <system_error>
33
#include <utility>
34
35
#include "common/exception.h"
36
#include "cpp/sync_point.h"
37
#include "io/fs/err_utils.h"
38
#include "io/fs/file_system.h"
39
#include "io/fs/file_writer.h"
40
#include "io/fs/local_file_reader.h"
41
#include "io/fs/local_file_writer.h"
42
#include "runtime/thread_context.h"
43
#include "storage/data_dir.h"
44
#include "util/async_io.h" // IWYU pragma: keep
45
#include "util/debug_points.h"
46
#include "util/defer_op.h"
47
48
namespace doris::io {
49
50
std::filesystem::perms LocalFileSystem::PERMS_OWNER_RW =
51
        std::filesystem::perms::owner_read | std::filesystem::perms::owner_write;
52
53
1
LocalFileSystem::LocalFileSystem() : FileSystem(FileSystem::TMP_FS_ID, FileSystemType::LOCAL) {}
54
55
0
LocalFileSystem::~LocalFileSystem() = default;
56
57
Status LocalFileSystem::create_file_impl(const Path& file, FileWriterPtr* writer,
58
10.1k
                                         const FileWriterOptions* opts) {
59
10.1k
    VLOG_DEBUG << "create file: " << file.native()
60
0
               << ", sync_data: " << (opts ? opts->sync_file_data : true);
61
10.1k
    TEST_SYNC_POINT_RETURN_WITH_VALUE("LocalFileSystem::create_file_impl",
62
10.1k
                                      Status::IOError("inject io error"));
63
    // O_TRUNC: if file already exists (last tmp), clear the content
64
10.1k
    int fd = ::open(file.c_str(), O_TRUNC | O_WRONLY | O_CREAT | O_CLOEXEC, 0666);
65
10.1k
    DBUG_EXECUTE_IF("LocalFileSystem.create_file_impl.open_file_failed", {
66
        // spare '.testfile' to make bad disk checker happy
67
10.1k
        auto sub_path = dp->param<std::string>("sub_path", "");
68
10.1k
        if ((sub_path.empty() && file.filename().compare(kTestFilePath)) ||
69
10.1k
            (!sub_path.empty() && file.native().find(sub_path) != std::string::npos)) {
70
10.1k
            ::close(fd);
71
10.1k
            fd = -1;
72
10.1k
            errno = EIO;
73
10.1k
            LOG(WARNING) << Status::IOError("debug open io error: {}", file.native());
74
10.1k
        }
75
10.1k
    });
76
10.1k
    if (-1 == fd) {
77
0
        return localfs_error(errno, fmt::format("failed to create file {}", file.native()));
78
0
    }
79
10.1k
    bool sync_data = opts != nullptr ? opts->sync_file_data : true;
80
10.1k
    *writer = std::make_unique<LocalFileWriter>(file, fd, sync_data);
81
10.1k
    return Status::OK();
82
10.1k
}
83
84
Status LocalFileSystem::open_file_impl(const Path& file, FileReaderSPtr* reader,
85
15.6k
                                       const FileReaderOptions* opts) {
86
15.6k
    TEST_SYNC_POINT_RETURN_WITH_VALUE("LocalFileSystem::open_file_impl",
87
15.6k
                                      Status::IOError("inject io error"));
88
15.6k
    int64_t fsize = opts ? opts->file_size : -1;
89
15.6k
    if (fsize < 0) {
90
14.1k
        RETURN_IF_ERROR(file_size_impl(file, &fsize));
91
14.1k
    }
92
15.6k
    int fd = -1;
93
15.6k
    RETRY_ON_EINTR(fd, open(file.c_str(), O_RDONLY));
94
15.6k
    DBUG_EXECUTE_IF("LocalFileSystem.create_file_impl.open_file_failed", {
95
        // spare '.testfile' to make bad disk checker happy
96
15.6k
        auto sub_path = dp->param<std::string>("sub_path", "");
97
15.6k
        if ((sub_path.empty() && file.filename().compare(kTestFilePath)) ||
98
15.6k
            (!sub_path.empty() && file.native().find(sub_path) != std::string::npos)) {
99
15.6k
            ::close(fd);
100
15.6k
            fd = -1;
101
15.6k
            errno = EIO;
102
15.6k
            LOG(WARNING) << Status::IOError("debug open io error: {}", file.native());
103
15.6k
        }
104
15.6k
    });
105
15.6k
    if (fd < 0) {
106
1
        return localfs_error(errno, fmt::format("failed to open {}", file.native()));
107
1
    }
108
15.6k
    *reader = std::make_shared<LocalFileReader>(file, fsize, fd);
109
15.6k
    return Status::OK();
110
15.6k
}
111
112
115k
Status LocalFileSystem::create_directory_impl(const Path& dir, bool failed_if_exists) {
113
115k
    VLOG_DEBUG << "create directory: " << dir.native()
114
0
               << ", failed_if_exists: " << failed_if_exists;
115
115k
    bool exists = true;
116
115k
    RETURN_IF_ERROR(exists_impl(dir, &exists));
117
115k
    if (exists && failed_if_exists) {
118
0
        return Status::AlreadyExist("failed to create {}, already exists", dir.native());
119
0
    }
120
115k
    if (!exists) {
121
110k
        std::error_code ec;
122
110k
        std::filesystem::create_directories(dir, ec);
123
110k
        if (ec) {
124
0
            return localfs_error(ec, fmt::format("failed to create {}", dir.native()));
125
0
        }
126
110k
    }
127
115k
    return Status::OK();
128
115k
}
129
130
2.90k
Status LocalFileSystem::delete_file_impl(const Path& file) {
131
2.90k
    VLOG_DEBUG << "delete file: " << file.native();
132
2.90k
    bool exists = true;
133
2.90k
    RETURN_IF_ERROR(exists_impl(file, &exists));
134
2.90k
    if (!exists) {
135
1.32k
        return Status::OK();
136
1.32k
    }
137
1.58k
    if (!std::filesystem::is_regular_file(file)) {
138
1
        return Status::InternalError("failed to delete {}, not a file", file.native());
139
1
    }
140
1.58k
    std::error_code ec;
141
1.58k
    std::filesystem::remove(file, ec);
142
1.58k
    if (ec) {
143
0
        return localfs_error(ec, fmt::format("failed to delete {}", file.native()));
144
0
    }
145
1.58k
    return Status::OK();
146
1.58k
}
147
148
2.82k
Status LocalFileSystem::delete_directory_impl(const Path& dir) {
149
2.82k
    VLOG_DEBUG << "delete directory: " << dir.native();
150
2.82k
    bool exists = true;
151
2.82k
    RETURN_IF_ERROR(exists_impl(dir, &exists));
152
2.82k
    if (!exists) {
153
985
        return Status::OK();
154
985
    }
155
1.84k
    if (!std::filesystem::is_directory(dir)) {
156
1
        return Status::InternalError("failed to delete {}, not a directory", dir.native());
157
1
    }
158
1.84k
    std::error_code ec;
159
1.84k
    std::filesystem::remove_all(dir, ec);
160
1.84k
    if (ec) {
161
0
        return localfs_error(ec, fmt::format("failed to delete {}", dir.native()));
162
0
    }
163
1.84k
    return Status::OK();
164
1.84k
}
165
166
1
Status LocalFileSystem::delete_directory_or_file(const Path& path) {
167
1
    FILESYSTEM_M(delete_directory_or_file_impl(path));
168
0
}
169
170
458
Status LocalFileSystem::delete_empty_directory(const Path& dir) {
171
458
    FILESYSTEM_M(delete_empty_directory_impl(dir));
172
0
}
173
174
458
Status LocalFileSystem::delete_empty_directory_impl(const Path& dir) {
175
458
    Path path;
176
458
    RETURN_IF_ERROR(absolute_path(dir, path));
177
458
    VLOG_DEBUG << "delete empty directory: " << path.native();
178
458
    int ret = 0;
179
458
    RETRY_ON_EINTR(ret, rmdir(path.c_str()));
180
458
    if (ret != 0) {
181
2
        std::error_code ec(errno, std::generic_category());
182
2
        if (ec == std::errc::no_such_file_or_directory) {
183
1
            return Status::OK();
184
1
        }
185
1
        return localfs_error(ec, fmt::format("failed to delete empty directory {}", path.native()));
186
2
    }
187
456
    return Status::OK();
188
458
}
189
190
1
Status LocalFileSystem::delete_directory_or_file_impl(const Path& path) {
191
1
    bool is_dir;
192
1
    RETURN_IF_ERROR(is_directory(path, &is_dir));
193
1
    if (is_dir) {
194
1
        return delete_directory_impl(path);
195
1
    } else {
196
0
        return delete_file_impl(path);
197
0
    }
198
1
}
199
200
0
Status LocalFileSystem::batch_delete_impl(const std::vector<Path>& files) {
201
0
    for (auto& file : files) {
202
0
        RETURN_IF_ERROR(delete_file_impl(file));
203
0
    }
204
0
    return Status::OK();
205
0
}
206
207
235k
Status LocalFileSystem::exists_impl(const Path& path, bool* res) const {
208
235k
    std::error_code ec;
209
235k
    *res = std::filesystem::exists(path, ec);
210
235k
    if (ec) {
211
0
        return localfs_error(ec, fmt::format("failed to check exists {}", path.native()));
212
0
    }
213
235k
    return Status::OK();
214
235k
}
215
216
15.2k
Status LocalFileSystem::file_size_impl(const Path& file, int64_t* file_size) const {
217
15.2k
    std::error_code ec;
218
15.2k
    *file_size = std::filesystem::file_size(file, ec);
219
15.2k
    if (ec) {
220
38
        return localfs_error(ec, fmt::format("failed to get file size {}", file.native()));
221
38
    }
222
15.2k
    return Status::OK();
223
15.2k
}
224
225
0
Status LocalFileSystem::directory_size(const Path& dir_path, size_t* dir_size) {
226
0
    *dir_size = 0;
227
0
    if (std::filesystem::exists(dir_path) && std::filesystem::is_directory(dir_path)) {
228
0
        for (const auto& entry : std::filesystem::recursive_directory_iterator(dir_path)) {
229
0
            if (std::filesystem::is_regular_file(entry)) {
230
0
                *dir_size += std::filesystem::file_size(entry);
231
0
            }
232
0
        }
233
0
        return Status::OK();
234
0
    }
235
    // TODO(plat1ko): Use error code according to std::error_code
236
0
    return Status::InternalError("faile to get dir size {}", dir_path.native());
237
0
}
238
239
Status LocalFileSystem::list_impl(const Path& dir, bool only_file, std::vector<FileInfo>* files,
240
3.30k
                                  bool* exists) {
241
3.30k
    RETURN_IF_ERROR(exists_impl(dir, exists));
242
3.30k
    if (!(*exists)) {
243
618
        return Status::OK();
244
618
    }
245
2.68k
    std::error_code ec;
246
2.68k
    try {
247
84.9k
        for (const auto& entry : std::filesystem::directory_iterator(dir, ec)) {
248
84.9k
            if (only_file && !entry.is_regular_file()) {
249
0
                continue;
250
0
            }
251
84.9k
            FileInfo file_info;
252
84.9k
            file_info.file_name = entry.path().filename();
253
84.9k
            file_info.is_file = entry.is_regular_file(ec);
254
84.9k
            if (ec) {
255
0
                break;
256
0
            }
257
84.9k
            if (file_info.is_file) {
258
84.7k
                file_info.file_size = entry.file_size(ec);
259
84.7k
                if (ec) {
260
0
                    break;
261
0
                }
262
84.7k
            }
263
84.9k
            files->push_back(std::move(file_info));
264
84.9k
        }
265
2.68k
    } catch (const std::filesystem::filesystem_error& e) {
266
        // although `directory_iterator(dir, ec)` does not throw an exception,
267
        // it may throw an exception during iterator++, so we need to catch the exception here
268
0
        return localfs_error(e.code(), fmt::format("failed to list {}, error message: {}",
269
0
                                                   dir.native(), e.what()));
270
0
    }
271
2.68k
    if (ec) {
272
1
        return localfs_error(ec, fmt::format("failed to list {}", dir.native()));
273
1
    }
274
2.68k
    return Status::OK();
275
2.68k
}
276
277
5.38k
Status LocalFileSystem::rename_impl(const Path& orig_name, const Path& new_name) {
278
5.38k
    VLOG_DEBUG << "rename file: " << orig_name.native() << " to " << new_name.native();
279
5.38k
    TEST_SYNC_POINT_RETURN_WITH_VALUE("LocalFileSystem::rename",
280
5.38k
                                      Status::IOError("inject io error"));
281
5.38k
    std::error_code ec;
282
5.38k
    std::filesystem::rename(orig_name, new_name, ec);
283
5.38k
    if (ec) {
284
2
        return localfs_error(ec, fmt::format("failed to rename {} to {}", orig_name.native(),
285
2
                                             new_name.native()));
286
2
    }
287
5.38k
    return Status::OK();
288
5.38k
}
289
290
77
Status LocalFileSystem::link_file(const Path& src, const Path& dest) {
291
77
    FILESYSTEM_M(link_file_impl(src, dest));
292
0
}
293
294
77
Status LocalFileSystem::link_file_impl(const Path& src, const Path& dest) {
295
77
    VLOG_DEBUG << "link file: " << src.native() << " to " << dest.native();
296
77
    if (::link(src.c_str(), dest.c_str()) != 0) {
297
0
        return localfs_error(errno, fmt::format("failed to create hard link from {} to {}",
298
0
                                                src.native(), dest.native()));
299
0
    }
300
77
    return Status::OK();
301
77
}
302
303
90
Status LocalFileSystem::canonicalize(const Path& path, std::string* real_path) {
304
90
    std::error_code ec;
305
90
    Path res = std::filesystem::canonical(path, ec);
306
90
    if (ec) {
307
0
        return localfs_error(ec, fmt::format("failed to canonicalize {}", path.native()));
308
0
    }
309
90
    *real_path = res.string();
310
90
    return Status::OK();
311
90
}
312
313
13
Status LocalFileSystem::is_directory(const Path& path, bool* res) {
314
13
    std::error_code ec;
315
13
    *res = std::filesystem::is_directory(path, ec);
316
13
    if (ec) {
317
2
        return localfs_error(ec, fmt::format("failed to canonicalize {}", path.native()));
318
2
    }
319
11
    return Status::OK();
320
13
}
321
322
0
Status LocalFileSystem::md5sum(const Path& file, std::string* md5sum) {
323
0
    FILESYSTEM_M(md5sum_impl(file, md5sum));
324
0
}
325
326
0
Status LocalFileSystem::md5sum_impl(const Path& file, std::string* md5sum) {
327
0
    int fd = open(file.c_str(), O_RDONLY);
328
0
    if (fd < 0) {
329
0
        return localfs_error(errno,
330
0
                             fmt::format("failed to open file for md5sum {}", file.native()));
331
0
    }
332
333
0
    struct stat statbuf;
334
0
    if (fstat(fd, &statbuf) < 0) {
335
0
        std::string err = errno_to_str();
336
0
        close(fd);
337
0
        return localfs_error(errno, fmt::format("failed to stat file {}", file.native()));
338
0
    }
339
0
    size_t file_len = statbuf.st_size;
340
0
    void* buf = mmap(nullptr, file_len, PROT_READ, MAP_SHARED, fd, 0);
341
342
0
    unsigned char result[MD5_DIGEST_LENGTH];
343
0
    MD5((unsigned char*)buf, file_len, result);
344
0
    munmap(buf, file_len);
345
346
0
    std::stringstream ss;
347
0
    for (int32_t i = 0; i < MD5_DIGEST_LENGTH; i++) {
348
0
        ss << std::setfill('0') << std::setw(2) << std::hex << (int)result[i];
349
0
    }
350
0
    ss >> *md5sum;
351
352
0
    close(fd);
353
0
    return Status::OK();
354
0
}
355
356
Status LocalFileSystem::iterate_directory(const std::string& dir,
357
5
                                          const std::function<bool(const FileInfo& file)>& cb) {
358
5
    FILESYSTEM_M(iterate_directory_impl(dir, cb));
359
0
}
360
361
Status LocalFileSystem::iterate_directory_impl(
362
5
        const std::string& dir, const std::function<bool(const FileInfo& file)>& cb) {
363
5
    bool exists = true;
364
5
    std::vector<FileInfo> files;
365
5
    RETURN_IF_ERROR(list_impl(dir, false, &files, &exists));
366
7
    for (auto& file : files) {
367
7
        if (!cb(file)) {
368
2
            break;
369
2
        }
370
7
    }
371
5
    return Status::OK();
372
5
}
373
374
15.7k
Status LocalFileSystem::get_space_info(const Path& dir, size_t* capacity, size_t* available) {
375
15.7k
    FILESYSTEM_M(get_space_info_impl(dir, capacity, available));
376
0
}
377
378
15.7k
Status LocalFileSystem::get_space_info_impl(const Path& path, size_t* capacity, size_t* available) {
379
15.7k
    std::error_code ec;
380
15.7k
    std::filesystem::space_info info = std::filesystem::space(path, ec);
381
15.7k
    if (ec) {
382
15.7k
        return localfs_error(
383
15.7k
                ec, fmt::format("failed to get available space for path {}", path.native()));
384
15.7k
    }
385
17
    *capacity = info.capacity;
386
17
    *available = info.available;
387
17
    return Status::OK();
388
15.7k
}
389
390
2
Status LocalFileSystem::copy_path(const Path& src, const Path& dest) {
391
2
    FILESYSTEM_M(copy_path_impl(src, dest));
392
0
}
393
394
2
Status LocalFileSystem::copy_path_impl(const Path& src, const Path& dest) {
395
2
    VLOG_DEBUG << "copy from " << src.native() << " to " << dest.native();
396
2
    std::error_code ec;
397
2
    std::filesystem::copy(src, dest, std::filesystem::copy_options::recursive, ec);
398
2
    if (ec) {
399
0
        return localfs_error(
400
0
                ec, fmt::format("failed to copy from {} to {}", src.native(), dest.native()));
401
0
    }
402
2
    return Status::OK();
403
2
}
404
405
8
bool LocalFileSystem::contain_path(const Path& parent_, const Path& sub_) {
406
8
    Path parent = parent_.lexically_normal();
407
8
    Path sub = sub_.lexically_normal();
408
8
    if (parent == sub) {
409
0
        return true;
410
0
    }
411
412
8
    if (parent.filename() == ".") {
413
0
        parent.remove_filename();
414
0
    }
415
416
    // We're also not interested in the file's name.
417
8
    if (sub.has_filename()) {
418
8
        sub.remove_filename();
419
8
    }
420
    // If dir has more components than file, then file can't possibly reside in dir.
421
8
    auto dir_len = std::distance(parent.begin(), parent.end());
422
8
    auto file_len = std::distance(sub.begin(), sub.end());
423
8
    if (dir_len > file_len) {
424
0
        return false;
425
0
    }
426
8
    auto p_it = parent.begin();
427
8
    auto s_it = sub.begin();
428
24
    for (; p_it != parent.end() && !p_it->string().empty(); ++p_it, ++s_it) {
429
16
        if (!(*p_it == *s_it)) {
430
0
            return false;
431
0
        }
432
16
    }
433
8
    return true;
434
8
}
435
436
8
bool LocalFileSystem::equal_or_sub_path(const Path& parent, const Path& child) {
437
8
    auto parent_path = parent.lexically_normal();
438
8
    auto child_path = child.lexically_normal();
439
8
    auto parent_it = parent_path.begin();
440
8
    auto child_it = child_path.begin();
441
35
    for (; parent_it != parent_path.end() && child_it != child_path.end();
442
28
         ++parent_it, ++child_it) {
443
28
        if (*parent_it != *child_it) {
444
1
            return false;
445
1
        }
446
28
    }
447
7
    return parent_it == parent_path.end();
448
8
}
449
450
246k
const std::shared_ptr<LocalFileSystem>& global_local_filesystem() {
451
246k
    static std::shared_ptr<LocalFileSystem> local_fs(new LocalFileSystem());
452
246k
    return local_fs;
453
246k
}
454
455
Status LocalFileSystem::canonicalize_local_file(const std::string& dir,
456
                                                const std::string& file_path,
457
4
                                                std::string* full_path) {
458
4
    const std::string absolute_path = dir + "/" + file_path;
459
4
    std::string canonical_path;
460
4
    RETURN_IF_ERROR(canonicalize(absolute_path, &canonical_path));
461
4
    if (!contain_path(dir, canonical_path)) {
462
0
        return Status::InvalidArgument("file path is not allowed: {}", canonical_path);
463
0
    }
464
465
4
    *full_path = canonical_path;
466
4
    return Status::OK();
467
4
}
468
469
4
Status LocalFileSystem::safe_glob(const std::string& path, std::vector<FileInfo>* res) {
470
4
    if (path.find("..") != std::string::npos) {
471
1
        return Status::InvalidArgument("can not contain '..' in path");
472
1
    }
473
3
    std::string full_path = config::user_files_secure_path + "/" + path;
474
3
    std::vector<std::string> files;
475
3
    RETURN_IF_ERROR(_glob(full_path, &files));
476
4
    for (auto& file : files) {
477
4
        FileInfo fi;
478
4
        fi.is_file = true;
479
4
        RETURN_IF_ERROR(canonicalize_local_file("", file, &(fi.file_name)));
480
4
        RETURN_IF_ERROR(file_size_impl(fi.file_name, &(fi.file_size)));
481
4
        res->push_back(std::move(fi));
482
4
    }
483
2
    return Status::OK();
484
2
}
485
486
3
Status LocalFileSystem::_glob(const std::string& pattern, std::vector<std::string>* res) {
487
3
    glob_t glob_result;
488
3
    memset(&glob_result, 0, sizeof(glob_result));
489
490
3
    int rc = glob(pattern.c_str(), GLOB_TILDE, NULL, &glob_result);
491
3
    if (rc != 0) {
492
1
        globfree(&glob_result);
493
1
        return Status::InternalError("failed to glob {}: {}", pattern, glob_err_to_str(rc));
494
1
    }
495
496
6
    for (size_t i = 0; i < glob_result.gl_pathc; ++i) {
497
4
        res->push_back(std::string(glob_result.gl_pathv[i]));
498
4
    }
499
500
2
    globfree(&glob_result);
501
2
    return Status::OK();
502
3
}
503
504
36
Status LocalFileSystem::permission(const Path& file, std::filesystem::perms prms) {
505
36
    FILESYSTEM_M(permission_impl(file, prms));
506
0
}
507
508
36
Status LocalFileSystem::permission_impl(const Path& file, std::filesystem::perms prms) {
509
36
    std::error_code ec;
510
36
    std::filesystem::permissions(file, prms, ec);
511
36
    if (ec) {
512
0
        return localfs_error(ec, fmt::format("failed to change file permission {}", file.native()));
513
0
    }
514
36
    return Status::OK();
515
36
}
516
517
273k
Status LocalFileSystem::convert_to_abs_path(const Path& input_path_str, Path& abs_path) {
518
    // valid path include:
519
    //   1. abc/def                         will return abc/def
520
    //   2. /abc/def                        will return /abc/def
521
    //   3. file:/abc/def                   will return /abc/def
522
    //   4. file://<authority>/abc/def      will return /abc/def
523
273k
    std::string path_str = input_path_str;
524
273k
    size_t slash = path_str.find('/');
525
273k
    if (slash == 0) {
526
199k
        abs_path = input_path_str;
527
199k
        return Status::OK();
528
199k
    }
529
530
    // Initialize scheme and authority
531
73.9k
    std::string scheme;
532
73.9k
    size_t start = 0;
533
534
    // Parse URI scheme
535
73.9k
    size_t colon = path_str.find(':');
536
73.9k
    if (colon != std::string::npos && (slash == std::string::npos || colon < slash)) {
537
        // Has a scheme
538
10
        scheme = path_str.substr(0, colon);
539
10
        if (scheme != "file") {
540
3
            return Status::InternalError(
541
3
                    "Only supports `file` type scheme, like 'file:///path', 'file:/path'.");
542
3
        }
543
7
        start = colon + 1;
544
7
    }
545
546
    // Parse URI authority, if any
547
73.9k
    if (path_str.compare(start, 2, "//") == 0 && path_str.length() - start > 2) {
548
        // Has authority
549
        // such as : path_str = "file://authority/abc/def"
550
        // and now : start = 5
551
6
        size_t next_slash = path_str.find('/', start + 2);
552
        // now : next_slash = 16
553
6
        if (next_slash == std::string::npos) {
554
1
            return Status::InternalError(
555
1
                    "This input string only has authority, but has no path information");
556
1
        }
557
        // We will skit authority
558
        // now : start = 16
559
5
        start = next_slash;
560
5
    }
561
562
    // URI path is the rest of the string
563
73.9k
    abs_path = path_str.substr(start);
564
73.9k
    return Status::OK();
565
73.9k
}
566
567
} // namespace doris::io