1 | #include "filesystemHelpers.h" |
2 | |
3 | #include <sys/stat.h> |
4 | #if defined(__linux__) |
5 | # include <cstdio> |
6 | # include <mntent.h> |
7 | #endif |
8 | #include <Poco/File.h> |
9 | #include <Poco/Path.h> |
10 | #include <Poco/Version.h> |
11 | |
12 | namespace DB |
13 | { |
14 | namespace ErrorCodes |
15 | { |
16 | extern const int LOGICAL_ERROR; |
17 | extern const int SYSTEM_ERROR; |
18 | extern const int NOT_IMPLEMENTED; |
19 | } |
20 | |
21 | bool enoughSpaceInDirectory(const std::string & path [[maybe_unused]], size_t data_size [[maybe_unused]]) |
22 | { |
23 | #if POCO_VERSION >= 0x01090000 |
24 | auto free_space = Poco::File(path).freeSpace(); |
25 | if (data_size > free_space) |
26 | return false; |
27 | #endif |
28 | return true; |
29 | } |
30 | |
31 | std::unique_ptr<TemporaryFile> createTemporaryFile(const std::string & path) |
32 | { |
33 | Poco::File(path).createDirectories(); |
34 | |
35 | /// NOTE: std::make_shared cannot use protected constructors |
36 | return std::make_unique<TemporaryFile>(path); |
37 | } |
38 | |
39 | std::filesystem::path getMountPoint(std::filesystem::path absolute_path) |
40 | { |
41 | if (absolute_path.is_relative()) |
42 | throw Exception("Path is relative. It's a bug." , ErrorCodes::LOGICAL_ERROR); |
43 | |
44 | absolute_path = std::filesystem::canonical(absolute_path); |
45 | |
46 | const auto get_device_id = [](const std::filesystem::path & p) |
47 | { |
48 | struct stat st; |
49 | if (stat(p.c_str(), &st)) |
50 | throwFromErrnoWithPath("Cannot stat " + p.string(), p.string(), ErrorCodes::SYSTEM_ERROR); |
51 | return st.st_dev; |
52 | }; |
53 | |
54 | /// If /some/path/to/dir/ and /some/path/to/ have different device id, |
55 | /// then device which contains /some/path/to/dir/filename is mounted to /some/path/to/dir/ |
56 | auto device_id = get_device_id(absolute_path); |
57 | while (absolute_path.has_relative_path()) |
58 | { |
59 | auto parent = absolute_path.parent_path(); |
60 | auto parent_device_id = get_device_id(parent); |
61 | if (device_id != parent_device_id) |
62 | return absolute_path; |
63 | absolute_path = parent; |
64 | device_id = parent_device_id; |
65 | } |
66 | |
67 | return absolute_path; |
68 | } |
69 | |
70 | /// Returns name of filesystem mounted to mount_point |
71 | #if !defined(__linux__) |
72 | [[noreturn]] |
73 | #endif |
74 | String getFilesystemName([[maybe_unused]] const String & mount_point) |
75 | { |
76 | #if defined(__linux__) |
77 | auto mounted_filesystems = setmntent("/etc/mtab" , "r" ); |
78 | if (!mounted_filesystems) |
79 | throw DB::Exception("Cannot open /etc/mtab to get name of filesystem" , ErrorCodes::SYSTEM_ERROR); |
80 | mntent fs_info; |
81 | constexpr size_t buf_size = 4096; /// The same as buffer used for getmntent in glibc. It can happen that it's not enough |
82 | char buf[buf_size]; |
83 | while (getmntent_r(mounted_filesystems, &fs_info, buf, buf_size) && fs_info.mnt_dir != mount_point) |
84 | ; |
85 | endmntent(mounted_filesystems); |
86 | if (fs_info.mnt_dir != mount_point) |
87 | throw DB::Exception("Cannot find name of filesystem by mount point " + mount_point, ErrorCodes::SYSTEM_ERROR); |
88 | return fs_info.mnt_fsname; |
89 | #else |
90 | throw DB::Exception("The function getFilesystemName is supported on Linux only" , ErrorCodes::NOT_IMPLEMENTED); |
91 | #endif |
92 | } |
93 | |
94 | } |
95 | |