You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

file_util.hxx 8.0KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312
  1. /*
  2. * Copyright 2023 Vsevolod Stakhov
  3. *
  4. * Licensed under the Apache License, Version 2.0 (the "License");
  5. * you may not use this file except in compliance with the License.
  6. * You may obtain a copy of the License at
  7. *
  8. * http://www.apache.org/licenses/LICENSE-2.0
  9. *
  10. * Unless required by applicable law or agreed to in writing, software
  11. * distributed under the License is distributed on an "AS IS" BASIS,
  12. * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13. * See the License for the specific language governing permissions and
  14. * limitations under the License.
  15. */
  16. #ifndef RSPAMD_FILE_UTIL_HXX
  17. #define RSPAMD_FILE_UTIL_HXX
  18. #pragma once
  19. #include "config.h"
  20. #include "contrib/expected/expected.hpp"
  21. #include "libutil/cxx/error.hxx"
  22. #include <string>
  23. #include <sys/stat.h>
  24. namespace rspamd::util {
  25. /**
  26. * A simple RAII object to contain a move only file descriptor
  27. * A file is unlocked and closed when not needed
  28. */
  29. struct raii_file {
  30. public:
  31. virtual ~raii_file() noexcept;
  32. static auto open(const char *fname, int flags) -> tl::expected<raii_file, error>;
  33. static auto open(const std::string &fname, int flags) -> tl::expected<raii_file, error>
  34. {
  35. return open(fname.c_str(), flags);
  36. };
  37. static auto create(const char *fname, int flags, int perms) -> tl::expected<raii_file, error>;
  38. static auto create(const std::string &fname, int flags, int perms) -> tl::expected<raii_file, error>
  39. {
  40. return create(fname.c_str(), flags, perms);
  41. };
  42. static auto create_temp(const char *fname, int flags, int perms) -> tl::expected<raii_file, error>;
  43. static auto mkstemp(const char *pattern, int flags, int perms) -> tl::expected<raii_file, error>;
  44. auto get_fd() const -> int
  45. {
  46. return fd;
  47. }
  48. auto get_stat() const -> const struct stat &
  49. {
  50. return st;
  51. };
  52. auto get_size() const -> std::size_t
  53. {
  54. return st.st_size;
  55. };
  56. auto get_name() const -> std::string_view
  57. {
  58. return std::string_view{fname};
  59. }
  60. auto get_dir() const -> std::string_view
  61. {
  62. auto sep_pos = fname.rfind(G_DIR_SEPARATOR);
  63. if (sep_pos == std::string::npos) {
  64. return std::string_view{fname};
  65. }
  66. while (sep_pos >= 1 && fname[sep_pos - 1] == G_DIR_SEPARATOR) {
  67. sep_pos--;
  68. }
  69. return std::string_view{fname.c_str(), sep_pos + 1};
  70. }
  71. auto get_extension() const -> std::string_view
  72. {
  73. auto sep_pos = fname.rfind(G_DIR_SEPARATOR);
  74. if (sep_pos == std::string::npos) {
  75. sep_pos = 0;
  76. }
  77. auto filename = std::string_view{fname.c_str() + sep_pos};
  78. auto dot_pos = filename.find('.');
  79. if (dot_pos == std::string::npos) {
  80. return std::string_view{};
  81. }
  82. else {
  83. return std::string_view{filename.data() + dot_pos + 1, filename.size() - dot_pos - 1};
  84. }
  85. }
  86. raii_file &operator=(raii_file &&other) noexcept
  87. {
  88. std::swap(fd, other.fd);
  89. std::swap(temp, other.temp);
  90. std::swap(fname, other.fname);
  91. std::swap(st, other.st);
  92. return *this;
  93. }
  94. raii_file(raii_file &&other) noexcept
  95. {
  96. *this = std::move(other);
  97. }
  98. /**
  99. * Prevent file from being deleted
  100. * @return
  101. */
  102. auto make_immortal() noexcept
  103. {
  104. temp = false;
  105. }
  106. /**
  107. * Performs fstat on an opened file to refresh internal stat
  108. * @return
  109. */
  110. auto update_stat() noexcept -> bool;
  111. auto is_valid() noexcept -> bool
  112. {
  113. return fd != -1;
  114. }
  115. /* Do not allow copy/default ctor */
  116. const raii_file &operator=(const raii_file &other) = delete;
  117. raii_file() = delete;
  118. raii_file(const raii_file &other) = delete;
  119. protected:
  120. int fd = -1;
  121. bool temp;
  122. std::string fname;
  123. struct stat st;
  124. explicit raii_file(const char *fname, int fd, bool temp);
  125. };
  126. /**
  127. * A simple RAII object to contain a file descriptor with an flock wrap
  128. * A file is unlocked and closed when not needed
  129. */
  130. struct raii_locked_file final : public raii_file {
  131. public:
  132. ~raii_locked_file() noexcept override;
  133. static auto open(const char *fname, int flags) -> tl::expected<raii_locked_file, error>
  134. {
  135. auto locked = raii_file::open(fname, flags).and_then([]<class T>(T &&file) {
  136. return lock_raii_file(std::forward<T>(file));
  137. });
  138. return locked;
  139. }
  140. static auto create(const char *fname, int flags, int perms) -> tl::expected<raii_locked_file, error>
  141. {
  142. auto locked = raii_file::create(fname, flags, perms).and_then([]<class T>(T &&file) {
  143. return lock_raii_file(std::forward<T>(file));
  144. });
  145. return locked;
  146. }
  147. static auto create_temp(const char *fname, int flags, int perms) -> tl::expected<raii_locked_file, error>
  148. {
  149. auto locked = raii_file::create_temp(fname, flags, perms).and_then([]<class T>(T &&file) {
  150. return lock_raii_file(std::forward<T>(file));
  151. });
  152. return locked;
  153. }
  154. static auto mkstemp(const char *pattern, int flags, int perms) -> tl::expected<raii_locked_file, error>
  155. {
  156. auto locked = raii_file::mkstemp(pattern, flags, perms).and_then([]<class T>(T &&file) {
  157. return lock_raii_file(std::forward<T>(file));
  158. });
  159. return locked;
  160. }
  161. raii_locked_file &operator=(raii_locked_file &&other) noexcept
  162. {
  163. std::swap(fd, other.fd);
  164. std::swap(temp, other.temp);
  165. std::swap(fname, other.fname);
  166. std::swap(st, other.st);
  167. return *this;
  168. }
  169. /**
  170. * Unlock a locked file and return back unlocked file transferring ownership.
  171. * A locked file cannot be used after this method.
  172. */
  173. auto unlock() -> raii_file;
  174. raii_locked_file(raii_locked_file &&other) noexcept
  175. : raii_file(static_cast<raii_file &&>(std::move(other)))
  176. {
  177. }
  178. /* Do not allow copy/default ctor */
  179. const raii_locked_file &operator=(const raii_locked_file &other) = delete;
  180. raii_locked_file() = delete;
  181. raii_locked_file(const raii_locked_file &other) = delete;
  182. private:
  183. static auto lock_raii_file(raii_file &&unlocked) -> tl::expected<raii_locked_file, error>;
  184. raii_locked_file(raii_file &&other) noexcept
  185. : raii_file(std::move(other))
  186. {
  187. }
  188. explicit raii_locked_file(const char *fname, int fd, bool temp)
  189. : raii_file(fname, fd, temp)
  190. {
  191. }
  192. };
  193. /**
  194. * A mmap wrapper on top of a locked file
  195. */
  196. struct raii_mmaped_file final {
  197. ~raii_mmaped_file();
  198. static auto mmap_shared(raii_file &&file, int flags, std::int64_t offset = 0) -> tl::expected<raii_mmaped_file, error>;
  199. static auto mmap_shared(const char *fname, int open_flags, int mmap_flags, std::int64_t offset = 0) -> tl::expected<raii_mmaped_file, error>;
  200. // Returns a constant pointer to the underlying map
  201. auto get_map() const -> void *
  202. {
  203. return map;
  204. }
  205. auto get_file() const -> const raii_file &
  206. {
  207. return file;
  208. }
  209. // Passes the ownership of the mmaped memory to the callee
  210. auto steal_map() -> std::tuple<void *, std::size_t>
  211. {
  212. auto ret = std::make_tuple(this->map, map_size);
  213. this->map = nullptr;
  214. return ret;
  215. }
  216. auto get_size() const -> std::size_t
  217. {
  218. return file.get_stat().st_size;
  219. }
  220. raii_mmaped_file &operator=(raii_mmaped_file &&other) noexcept
  221. {
  222. std::swap(map, other.map);
  223. std::swap(map_size, other.map_size);
  224. file = std::move(other.file);
  225. return *this;
  226. }
  227. raii_mmaped_file(raii_mmaped_file &&other) noexcept;
  228. /* Do not allow copy/default ctor */
  229. const raii_mmaped_file &operator=(const raii_mmaped_file &other) = delete;
  230. raii_mmaped_file() = delete;
  231. raii_mmaped_file(const raii_mmaped_file &other) = delete;
  232. private:
  233. /* Is intended to be used with map_shared */
  234. explicit raii_mmaped_file(raii_file &&_file, void *_map, std::size_t sz);
  235. raii_file file;
  236. void *map = nullptr;
  237. std::size_t map_size;
  238. };
  239. /**
  240. * A helper to have a file to write that will be renamed to the
  241. * target file if successful or deleted in the case of failure
  242. */
  243. struct raii_file_sink final {
  244. static auto create(const char *fname, int flags, int perms, const char *suffix = "new")
  245. -> tl::expected<raii_file_sink, error>;
  246. auto write_output() -> bool;
  247. ~raii_file_sink();
  248. auto get_fd() const -> int
  249. {
  250. return file.get_fd();
  251. }
  252. raii_file_sink(raii_file_sink &&other) noexcept;
  253. /* Do not allow copy/default ctor */
  254. const raii_file_sink &operator=(const raii_file_sink &other) = delete;
  255. raii_file_sink() = delete;
  256. raii_file_sink(const raii_file_sink &other) = delete;
  257. private:
  258. explicit raii_file_sink(raii_locked_file &&_file, const char *_output, std::string &&_tmp_fname);
  259. raii_locked_file file;
  260. std::string output_fname;
  261. std::string tmp_fname;
  262. bool success;
  263. };
  264. }// namespace rspamd::util
  265. #endif//RSPAMD_FILE_UTIL_HXX