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
12namespace DB
13{
14namespace ErrorCodes
15{
16 extern const int LOGICAL_ERROR;
17 extern const int SYSTEM_ERROR;
18 extern const int NOT_IMPLEMENTED;
19}
20
21bool 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
31std::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
39std::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
74String 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