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 | 8 | LocalFileSystem::LocalFileSystem() : FileSystem(FileSystem::TMP_FS_ID, FileSystemType::LOCAL) {} |
54 | | |
55 | 3 | LocalFileSystem::~LocalFileSystem() = default; |
56 | | |
57 | | Status LocalFileSystem::create_file_impl(const Path& file, FileWriterPtr* writer, |
58 | 130k | const FileWriterOptions* opts) { |
59 | 130k | VLOG_DEBUG << "create file: " << file.native() |
60 | 0 | << ", sync_data: " << (opts ? opts->sync_file_data : true); |
61 | 130k | TEST_SYNC_POINT_RETURN_WITH_VALUE("LocalFileSystem::create_file_impl", |
62 | 130k | Status::IOError("inject io error")); |
63 | | // O_TRUNC: if file already exists (last tmp), clear the content |
64 | 130k | int fd = ::open(file.c_str(), O_TRUNC | O_WRONLY | O_CREAT | O_CLOEXEC, 0666); |
65 | 130k | DBUG_EXECUTE_IF("LocalFileSystem.create_file_impl.open_file_failed", { |
66 | | // spare '.testfile' to make bad disk checker happy |
67 | 130k | auto sub_path = dp->param<std::string>("sub_path", ""); |
68 | 130k | if ((sub_path.empty() && file.filename().compare(kTestFilePath)) || |
69 | 130k | (!sub_path.empty() && file.native().find(sub_path) != std::string::npos)) { |
70 | 130k | ::close(fd); |
71 | 130k | fd = -1; |
72 | 130k | errno = EIO; |
73 | 130k | LOG(WARNING) << Status::IOError("debug open io error: {}", file.native()); |
74 | 130k | } |
75 | 130k | }); |
76 | 130k | if (-1 == fd) { |
77 | 0 | return localfs_error(errno, fmt::format("failed to create file {}", file.native())); |
78 | 0 | } |
79 | 130k | bool sync_data = opts != nullptr ? opts->sync_file_data : true; |
80 | 130k | *writer = std::make_unique<LocalFileWriter>(file, fd, sync_data); |
81 | 130k | return Status::OK(); |
82 | 130k | } |
83 | | |
84 | | Status LocalFileSystem::open_file_impl(const Path& file, FileReaderSPtr* reader, |
85 | 133k | const FileReaderOptions* opts) { |
86 | 133k | TEST_SYNC_POINT_RETURN_WITH_VALUE("LocalFileSystem::open_file_impl", |
87 | 133k | Status::IOError("inject io error")); |
88 | 133k | int64_t fsize = opts ? opts->file_size : -1; |
89 | 133k | if (fsize < 0) { |
90 | 132k | RETURN_IF_ERROR(file_size_impl(file, &fsize)); |
91 | 132k | } |
92 | 117k | int fd = -1; |
93 | 117k | RETRY_ON_EINTR(fd, open(file.c_str(), O_RDONLY)); |
94 | 117k | DBUG_EXECUTE_IF("LocalFileSystem.create_file_impl.open_file_failed", { |
95 | | // spare '.testfile' to make bad disk checker happy |
96 | 117k | auto sub_path = dp->param<std::string>("sub_path", ""); |
97 | 117k | if ((sub_path.empty() && file.filename().compare(kTestFilePath)) || |
98 | 117k | (!sub_path.empty() && file.native().find(sub_path) != std::string::npos)) { |
99 | 117k | ::close(fd); |
100 | 117k | fd = -1; |
101 | 117k | errno = EIO; |
102 | 117k | LOG(WARNING) << Status::IOError("debug open io error: {}", file.native()); |
103 | 117k | } |
104 | 117k | }); |
105 | 117k | if (fd < 0) { |
106 | 0 | return localfs_error(errno, fmt::format("failed to open {}", file.native())); |
107 | 0 | } |
108 | 117k | *reader = std::make_shared<LocalFileReader>(file, fsize, fd); |
109 | 117k | return Status::OK(); |
110 | 117k | } |
111 | | |
112 | 240k | Status LocalFileSystem::create_directory_impl(const Path& dir, bool failed_if_exists) { |
113 | 18.4E | VLOG_DEBUG << "create directory: " << dir.native() |
114 | 18.4E | << ", failed_if_exists: " << failed_if_exists; |
115 | 240k | bool exists = true; |
116 | 240k | RETURN_IF_ERROR(exists_impl(dir, &exists)); |
117 | 240k | if (exists && failed_if_exists) { |
118 | 0 | return Status::AlreadyExist("failed to create {}, already exists", dir.native()); |
119 | 0 | } |
120 | 240k | if (!exists) { |
121 | 226k | std::error_code ec; |
122 | 226k | std::filesystem::create_directories(dir, ec); |
123 | 226k | if (ec) { |
124 | 0 | return localfs_error(ec, fmt::format("failed to create {}", dir.native())); |
125 | 0 | } |
126 | 226k | } |
127 | 240k | return Status::OK(); |
128 | 240k | } |
129 | | |
130 | 232k | Status LocalFileSystem::delete_file_impl(const Path& file) { |
131 | 232k | VLOG_DEBUG << "delete file: " << file.native(); |
132 | 232k | bool exists = true; |
133 | 232k | RETURN_IF_ERROR(exists_impl(file, &exists)); |
134 | 232k | if (!exists) { |
135 | 115k | return Status::OK(); |
136 | 115k | } |
137 | 117k | if (!std::filesystem::is_regular_file(file)) { |
138 | 1 | return Status::InternalError("failed to delete {}, not a file", file.native()); |
139 | 1 | } |
140 | 117k | std::error_code ec; |
141 | 117k | std::filesystem::remove(file, ec); |
142 | 117k | if (ec) { |
143 | 0 | return localfs_error(ec, fmt::format("failed to delete {}", file.native())); |
144 | 0 | } |
145 | 117k | return Status::OK(); |
146 | 117k | } |
147 | | |
148 | 172k | Status LocalFileSystem::delete_directory_impl(const Path& dir) { |
149 | 172k | VLOG_DEBUG << "delete directory: " << dir.native(); |
150 | 172k | bool exists = true; |
151 | 172k | RETURN_IF_ERROR(exists_impl(dir, &exists)); |
152 | 172k | if (!exists) { |
153 | 910 | return Status::OK(); |
154 | 910 | } |
155 | 171k | if (!std::filesystem::is_directory(dir)) { |
156 | 1 | return Status::InternalError("failed to delete {}, not a directory", dir.native()); |
157 | 1 | } |
158 | 171k | std::error_code ec; |
159 | 171k | std::filesystem::remove_all(dir, ec); |
160 | 171k | if (ec) { |
161 | 2 | return localfs_error(ec, fmt::format("failed to delete {}", dir.native())); |
162 | 2 | } |
163 | 171k | return Status::OK(); |
164 | 171k | } |
165 | | |
166 | 20 | Status LocalFileSystem::delete_directory_or_file(const Path& path) { |
167 | 20 | FILESYSTEM_M(delete_directory_or_file_impl(path)); |
168 | 0 | } |
169 | | |
170 | 20 | Status LocalFileSystem::delete_directory_or_file_impl(const Path& path) { |
171 | 20 | bool is_dir; |
172 | 20 | RETURN_IF_ERROR(is_directory(path, &is_dir)); |
173 | 18 | if (is_dir) { |
174 | 1 | return delete_directory_impl(path); |
175 | 17 | } else { |
176 | 17 | return delete_file_impl(path); |
177 | 17 | } |
178 | 18 | } |
179 | | |
180 | 0 | Status LocalFileSystem::batch_delete_impl(const std::vector<Path>& files) { |
181 | 0 | for (auto& file : files) { |
182 | 0 | RETURN_IF_ERROR(delete_file_impl(file)); |
183 | 0 | } |
184 | 0 | return Status::OK(); |
185 | 0 | } |
186 | | |
187 | 1.91M | Status LocalFileSystem::exists_impl(const Path& path, bool* res) const { |
188 | 1.91M | std::error_code ec; |
189 | 1.91M | *res = std::filesystem::exists(path, ec); |
190 | 1.91M | if (ec) { |
191 | 0 | return localfs_error(ec, fmt::format("failed to check exists {}", path.native())); |
192 | 0 | } |
193 | 1.91M | return Status::OK(); |
194 | 1.91M | } |
195 | | |
196 | 150k | Status LocalFileSystem::file_size_impl(const Path& file, int64_t* file_size) const { |
197 | 150k | std::error_code ec; |
198 | 150k | *file_size = std::filesystem::file_size(file, ec); |
199 | 150k | if (ec) { |
200 | 15.8k | return localfs_error(ec, fmt::format("failed to get file size {}", file.native())); |
201 | 15.8k | } |
202 | 134k | return Status::OK(); |
203 | 150k | } |
204 | | |
205 | 67.9k | Status LocalFileSystem::directory_size(const Path& dir_path, size_t* dir_size) { |
206 | 67.9k | *dir_size = 0; |
207 | 67.9k | if (std::filesystem::exists(dir_path) && std::filesystem::is_directory(dir_path)) { |
208 | 1.18M | for (const auto& entry : std::filesystem::recursive_directory_iterator(dir_path)) { |
209 | 1.18M | if (std::filesystem::is_regular_file(entry)) { |
210 | 6.19k | *dir_size += std::filesystem::file_size(entry); |
211 | 6.19k | } |
212 | 1.18M | } |
213 | 67.9k | return Status::OK(); |
214 | 67.9k | } |
215 | | // TODO(plat1ko): Use error code according to std::error_code |
216 | 0 | return Status::InternalError("faile to get dir size {}", dir_path.native()); |
217 | 67.9k | } |
218 | | |
219 | | Status LocalFileSystem::list_impl(const Path& dir, bool only_file, std::vector<FileInfo>* files, |
220 | 830k | bool* exists) { |
221 | 830k | RETURN_IF_ERROR(exists_impl(dir, exists)); |
222 | 830k | if (!exists) { |
223 | 0 | return Status::OK(); |
224 | 0 | } |
225 | 830k | std::error_code ec; |
226 | 830k | try { |
227 | 32.1M | for (const auto& entry : std::filesystem::directory_iterator(dir, ec)) { |
228 | 32.1M | if (only_file && !entry.is_regular_file()) { |
229 | 104 | continue; |
230 | 104 | } |
231 | 32.1M | FileInfo file_info; |
232 | 32.1M | file_info.file_name = entry.path().filename(); |
233 | 32.1M | file_info.is_file = entry.is_regular_file(ec); |
234 | 32.1M | if (ec) { |
235 | 0 | break; |
236 | 0 | } |
237 | 32.1M | if (file_info.is_file) { |
238 | 131k | file_info.file_size = entry.file_size(ec); |
239 | 131k | if (ec) { |
240 | 0 | break; |
241 | 0 | } |
242 | 131k | } |
243 | 32.1M | files->push_back(std::move(file_info)); |
244 | 32.1M | } |
245 | 830k | } catch (const std::filesystem::filesystem_error& e) { |
246 | | // although `directory_iterator(dir, ec)` does not throw an exception, |
247 | | // it may throw an exception during iterator++, so we need to catch the exception here |
248 | 0 | return localfs_error(e.code(), fmt::format("failed to list {}, error message: {}", |
249 | 0 | dir.native(), e.what())); |
250 | 0 | } |
251 | 830k | if (ec) { |
252 | 1.56k | return localfs_error(ec, fmt::format("failed to list {}", dir.native())); |
253 | 1.56k | } |
254 | 828k | return Status::OK(); |
255 | 830k | } |
256 | | |
257 | 116k | Status LocalFileSystem::rename_impl(const Path& orig_name, const Path& new_name) { |
258 | 18.4E | VLOG_DEBUG << "rename file: " << orig_name.native() << " to " << new_name.native(); |
259 | 116k | TEST_SYNC_POINT_RETURN_WITH_VALUE("LocalFileSystem::rename", |
260 | 116k | Status::IOError("inject io error")); |
261 | 116k | std::error_code ec; |
262 | 116k | std::filesystem::rename(orig_name, new_name, ec); |
263 | 116k | if (ec) { |
264 | 1 | return localfs_error(ec, fmt::format("failed to rename {} to {}", orig_name.native(), |
265 | 1 | new_name.native())); |
266 | 1 | } |
267 | 116k | return Status::OK(); |
268 | 116k | } |
269 | | |
270 | 77 | Status LocalFileSystem::link_file(const Path& src, const Path& dest) { |
271 | 77 | FILESYSTEM_M(link_file_impl(src, dest)); |
272 | 0 | } |
273 | | |
274 | 77 | Status LocalFileSystem::link_file_impl(const Path& src, const Path& dest) { |
275 | 77 | VLOG_DEBUG << "link file: " << src.native() << " to " << dest.native(); |
276 | 77 | if (::link(src.c_str(), dest.c_str()) != 0) { |
277 | 0 | return localfs_error(errno, fmt::format("failed to create hard link from {} to {}", |
278 | 0 | src.native(), dest.native())); |
279 | 0 | } |
280 | 77 | return Status::OK(); |
281 | 77 | } |
282 | | |
283 | 148 | Status LocalFileSystem::canonicalize(const Path& path, std::string* real_path) { |
284 | 148 | std::error_code ec; |
285 | 148 | Path res = std::filesystem::canonical(path, ec); |
286 | 148 | if (ec) { |
287 | 0 | return localfs_error(ec, fmt::format("failed to canonicalize {}", path.native())); |
288 | 0 | } |
289 | 148 | *real_path = res.string(); |
290 | 148 | return Status::OK(); |
291 | 148 | } |
292 | | |
293 | 29 | Status LocalFileSystem::is_directory(const Path& path, bool* res) { |
294 | 29 | std::error_code ec; |
295 | 29 | *res = std::filesystem::is_directory(path, ec); |
296 | 29 | if (ec) { |
297 | 4 | return localfs_error(ec, fmt::format("failed to canonicalize {}", path.native())); |
298 | 4 | } |
299 | 25 | return Status::OK(); |
300 | 29 | } |
301 | | |
302 | 0 | Status LocalFileSystem::md5sum(const Path& file, std::string* md5sum) { |
303 | 0 | FILESYSTEM_M(md5sum_impl(file, md5sum)); |
304 | 0 | } |
305 | | |
306 | 0 | Status LocalFileSystem::md5sum_impl(const Path& file, std::string* md5sum) { |
307 | 0 | int fd = open(file.c_str(), O_RDONLY); |
308 | 0 | if (fd < 0) { |
309 | 0 | return localfs_error(errno, |
310 | 0 | fmt::format("failed to open file for md5sum {}", file.native())); |
311 | 0 | } |
312 | | |
313 | 0 | struct stat statbuf; |
314 | 0 | if (fstat(fd, &statbuf) < 0) { |
315 | 0 | std::string err = errno_to_str(); |
316 | 0 | close(fd); |
317 | 0 | return localfs_error(errno, fmt::format("failed to stat file {}", file.native())); |
318 | 0 | } |
319 | 0 | size_t file_len = statbuf.st_size; |
320 | 0 | void* buf = mmap(nullptr, file_len, PROT_READ, MAP_SHARED, fd, 0); |
321 | |
|
322 | 0 | unsigned char result[MD5_DIGEST_LENGTH]; |
323 | 0 | MD5((unsigned char*)buf, file_len, result); |
324 | 0 | munmap(buf, file_len); |
325 | |
|
326 | 0 | std::stringstream ss; |
327 | 0 | for (int32_t i = 0; i < MD5_DIGEST_LENGTH; i++) { |
328 | 0 | ss << std::setfill('0') << std::setw(2) << std::hex << (int)result[i]; |
329 | 0 | } |
330 | 0 | ss >> *md5sum; |
331 | |
|
332 | 0 | close(fd); |
333 | 0 | return Status::OK(); |
334 | 0 | } |
335 | | |
336 | | Status LocalFileSystem::iterate_directory(const std::string& dir, |
337 | 908 | const std::function<bool(const FileInfo& file)>& cb) { |
338 | 908 | FILESYSTEM_M(iterate_directory_impl(dir, cb)); |
339 | 0 | } |
340 | | |
341 | | Status LocalFileSystem::iterate_directory_impl( |
342 | 908 | const std::string& dir, const std::function<bool(const FileInfo& file)>& cb) { |
343 | 908 | bool exists = true; |
344 | 908 | std::vector<FileInfo> files; |
345 | 908 | RETURN_IF_ERROR(list_impl(dir, false, &files, &exists)); |
346 | 908 | for (auto& file : files) { |
347 | 69 | if (!cb(file)) { |
348 | 2 | break; |
349 | 2 | } |
350 | 69 | } |
351 | 908 | return Status::OK(); |
352 | 908 | } |
353 | | |
354 | 80.2k | Status LocalFileSystem::get_space_info(const Path& dir, size_t* capacity, size_t* available) { |
355 | 80.2k | FILESYSTEM_M(get_space_info_impl(dir, capacity, available)); |
356 | 0 | } |
357 | | |
358 | 80.2k | Status LocalFileSystem::get_space_info_impl(const Path& path, size_t* capacity, size_t* available) { |
359 | 80.2k | std::error_code ec; |
360 | 80.2k | std::filesystem::space_info info = std::filesystem::space(path, ec); |
361 | 80.2k | if (ec) { |
362 | 8.60k | return localfs_error( |
363 | 8.60k | ec, fmt::format("failed to get available space for path {}", path.native())); |
364 | 8.60k | } |
365 | 71.6k | *capacity = info.capacity; |
366 | 71.6k | *available = info.available; |
367 | 71.6k | return Status::OK(); |
368 | 80.2k | } |
369 | | |
370 | 2 | Status LocalFileSystem::copy_path(const Path& src, const Path& dest) { |
371 | 2 | FILESYSTEM_M(copy_path_impl(src, dest)); |
372 | 0 | } |
373 | | |
374 | 2 | Status LocalFileSystem::copy_path_impl(const Path& src, const Path& dest) { |
375 | 2 | VLOG_DEBUG << "copy from " << src.native() << " to " << dest.native(); |
376 | 2 | std::error_code ec; |
377 | 2 | std::filesystem::copy(src, dest, std::filesystem::copy_options::recursive, ec); |
378 | 2 | if (ec) { |
379 | 0 | return localfs_error( |
380 | 0 | ec, fmt::format("failed to copy from {} to {}", src.native(), dest.native())); |
381 | 0 | } |
382 | 2 | return Status::OK(); |
383 | 2 | } |
384 | | |
385 | 32 | bool LocalFileSystem::contain_path(const Path& parent_, const Path& sub_) { |
386 | 32 | Path parent = parent_.lexically_normal(); |
387 | 32 | Path sub = sub_.lexically_normal(); |
388 | 32 | if (parent == sub) { |
389 | 0 | return true; |
390 | 0 | } |
391 | | |
392 | 32 | if (parent.filename() == ".") { |
393 | 0 | parent.remove_filename(); |
394 | 0 | } |
395 | | |
396 | | // We're also not interested in the file's name. |
397 | 32 | if (sub.has_filename()) { |
398 | 32 | sub.remove_filename(); |
399 | 32 | } |
400 | | // If dir has more components than file, then file can't possibly reside in dir. |
401 | 32 | auto dir_len = std::distance(parent.begin(), parent.end()); |
402 | 32 | auto file_len = std::distance(sub.begin(), sub.end()); |
403 | 32 | if (dir_len > file_len) { |
404 | 0 | return false; |
405 | 0 | } |
406 | 32 | auto p_it = parent.begin(); |
407 | 32 | auto s_it = sub.begin(); |
408 | 48 | for (; p_it != parent.end() && !p_it->string().empty(); ++p_it, ++s_it) { |
409 | 16 | if (!(*p_it == *s_it)) { |
410 | 0 | return false; |
411 | 0 | } |
412 | 16 | } |
413 | 32 | return true; |
414 | 32 | } |
415 | | |
416 | 1.22M | const std::shared_ptr<LocalFileSystem>& global_local_filesystem() { |
417 | 1.22M | static std::shared_ptr<LocalFileSystem> local_fs(new LocalFileSystem()); |
418 | 1.22M | return local_fs; |
419 | 1.22M | } |
420 | | |
421 | | Status LocalFileSystem::canonicalize_local_file(const std::string& dir, |
422 | | const std::string& file_path, |
423 | 28 | std::string* full_path) { |
424 | 28 | const std::string absolute_path = dir + "/" + file_path; |
425 | 28 | std::string canonical_path; |
426 | 28 | RETURN_IF_ERROR(canonicalize(absolute_path, &canonical_path)); |
427 | 28 | if (!contain_path(dir, canonical_path)) { |
428 | 0 | return Status::InvalidArgument("file path is not allowed: {}", canonical_path); |
429 | 0 | } |
430 | | |
431 | 28 | *full_path = canonical_path; |
432 | 28 | return Status::OK(); |
433 | 28 | } |
434 | | |
435 | 31 | Status LocalFileSystem::safe_glob(const std::string& path, std::vector<FileInfo>* res) { |
436 | 31 | if (path.find("..") != std::string::npos) { |
437 | 1 | return Status::InvalidArgument("can not contain '..' in path"); |
438 | 1 | } |
439 | 30 | std::string full_path = config::user_files_secure_path + "/" + path; |
440 | 30 | std::vector<std::string> files; |
441 | 30 | RETURN_IF_ERROR(_glob(full_path, &files)); |
442 | 28 | for (auto& file : files) { |
443 | 28 | FileInfo fi; |
444 | 28 | fi.is_file = true; |
445 | 28 | RETURN_IF_ERROR(canonicalize_local_file("", file, &(fi.file_name))); |
446 | 28 | RETURN_IF_ERROR(file_size_impl(fi.file_name, &(fi.file_size))); |
447 | 28 | res->push_back(std::move(fi)); |
448 | 28 | } |
449 | 24 | return Status::OK(); |
450 | 24 | } |
451 | | |
452 | 30 | Status LocalFileSystem::_glob(const std::string& pattern, std::vector<std::string>* res) { |
453 | 30 | glob_t glob_result; |
454 | 30 | memset(&glob_result, 0, sizeof(glob_result)); |
455 | | |
456 | 30 | int rc = glob(pattern.c_str(), GLOB_TILDE, NULL, &glob_result); |
457 | 30 | if (rc != 0) { |
458 | 6 | globfree(&glob_result); |
459 | 6 | return Status::InternalError("failed to glob {}: {}", pattern, glob_err_to_str(rc)); |
460 | 6 | } |
461 | | |
462 | 52 | for (size_t i = 0; i < glob_result.gl_pathc; ++i) { |
463 | 28 | res->push_back(std::string(glob_result.gl_pathv[i])); |
464 | 28 | } |
465 | | |
466 | 24 | globfree(&glob_result); |
467 | 24 | return Status::OK(); |
468 | 30 | } |
469 | | |
470 | 36 | Status LocalFileSystem::permission(const Path& file, std::filesystem::perms prms) { |
471 | 36 | FILESYSTEM_M(permission_impl(file, prms)); |
472 | 0 | } |
473 | | |
474 | 36 | Status LocalFileSystem::permission_impl(const Path& file, std::filesystem::perms prms) { |
475 | 36 | std::error_code ec; |
476 | 36 | std::filesystem::permissions(file, prms, ec); |
477 | 36 | if (ec) { |
478 | 0 | return localfs_error(ec, fmt::format("failed to change file permission {}", file.native())); |
479 | 0 | } |
480 | 36 | return Status::OK(); |
481 | 36 | } |
482 | | |
483 | 2.45M | Status LocalFileSystem::convert_to_abs_path(const Path& input_path_str, Path& abs_path) { |
484 | | // valid path include: |
485 | | // 1. abc/def will return abc/def |
486 | | // 2. /abc/def will return /abc/def |
487 | | // 3. file:/abc/def will return /abc/def |
488 | | // 4. file://<authority>/abc/def will return /abc/def |
489 | 2.45M | std::string path_str = input_path_str; |
490 | 2.45M | size_t slash = path_str.find('/'); |
491 | 2.45M | if (slash == 0) { |
492 | 2.35M | abs_path = input_path_str; |
493 | 2.35M | return Status::OK(); |
494 | 2.35M | } |
495 | | |
496 | | // Initialize scheme and authority |
497 | 101k | std::string scheme; |
498 | 101k | size_t start = 0; |
499 | | |
500 | | // Parse URI scheme |
501 | 101k | size_t colon = path_str.find(':'); |
502 | 101k | if (colon != std::string::npos && (slash == std::string::npos || colon < slash)) { |
503 | | // Has a scheme |
504 | 10 | scheme = path_str.substr(0, colon); |
505 | 10 | if (scheme != "file") { |
506 | 3 | return Status::InternalError( |
507 | 3 | "Only supports `file` type scheme, like 'file:///path', 'file:/path'."); |
508 | 3 | } |
509 | 7 | start = colon + 1; |
510 | 7 | } |
511 | | |
512 | | // Parse URI authority, if any |
513 | 101k | if (path_str.compare(start, 2, "//") == 0 && path_str.length() - start > 2) { |
514 | | // Has authority |
515 | | // such as : path_str = "file://authority/abc/def" |
516 | | // and now : start = 5 |
517 | 6 | size_t next_slash = path_str.find('/', start + 2); |
518 | | // now : next_slash = 16 |
519 | 6 | if (next_slash == std::string::npos) { |
520 | 1 | return Status::InternalError( |
521 | 1 | "This input string only has authority, but has no path information"); |
522 | 1 | } |
523 | | // We will skit authority |
524 | | // now : start = 16 |
525 | 5 | start = next_slash; |
526 | 5 | } |
527 | | |
528 | | // URI path is the rest of the string |
529 | 101k | abs_path = path_str.substr(start); |
530 | 101k | return Status::OK(); |
531 | 101k | } |
532 | | |
533 | | } // namespace doris::io |