1 /* 2 Copyright (c) 2015, 2020, Oracle and/or its affiliates. All rights reserved. 3 4 This program is free software; you can redistribute it and/or modify 5 it under the terms of the GNU General Public License, version 2.0, 6 as published by the Free Software Foundation. 7 8 This program is also distributed with certain software (including 9 but not limited to OpenSSL) that is licensed under separate terms, 10 as designated in a particular file or component or in included license 11 documentation. The authors of MySQL hereby grant you an additional 12 permission to link the program and your derivative works with the 13 separately licensed software that they have included with MySQL. 14 15 This program is distributed in the hope that it will be useful, 16 but WITHOUT ANY WARRANTY; without even the implied warranty of 17 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 18 GNU General Public License for more details. 19 20 You should have received a copy of the GNU General Public License 21 along with this program; if not, write to the Free Software 22 Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA 23 */ 24 25 #ifndef MYSQL_HARNESS_FILESYSTEM_INCLUDED 26 #define MYSQL_HARNESS_FILESYSTEM_INCLUDED 27 28 #include "harness_export.h" 29 30 #include <memory> 31 #include <ostream> 32 #include <stdexcept> 33 #include <string> 34 35 #ifndef _WIN32 36 #include <fcntl.h> 37 #endif 38 39 #ifdef _WIN32 40 #include <aclapi.h> 41 #endif 42 43 namespace mysql_harness { 44 45 /** 46 * @defgroup Filesystem Platform-independent file system operations 47 * 48 * This module contain platform-independent file system operations. 49 */ 50 51 /** 52 * Class representing a path in a file system. 53 * 54 * @ingroup Filesystem 55 * 56 * Paths are used to access files in the file system and can be either 57 * relative or absolute. Absolute paths have a slash (`/`) first in 58 * the path, otherwise, the path is relative. 59 */ 60 class HARNESS_EXPORT Path { 61 friend std::ostream &operator<<(std::ostream &out, const Path &path) { 62 out << path.path_; 63 return out; 64 } 65 66 public: 67 /** 68 * Enum used to identify file types. 69 */ 70 71 enum class FileType { 72 /** An error occurred when trying to get file type, but it is *not* 73 * that the file was not found. */ 74 STATUS_ERROR, 75 76 /** Empty path was given */ 77 EMPTY_PATH, 78 79 /** The file was not found. */ 80 FILE_NOT_FOUND, 81 82 /** The file is a regular file. */ 83 REGULAR_FILE, 84 85 /** The file is a directory. */ 86 DIRECTORY_FILE, 87 88 /** The file is a symbolic link. */ 89 SYMLINK_FILE, 90 91 /** The file is a block device */ 92 BLOCK_FILE, 93 94 /** The file is a character device */ 95 CHARACTER_FILE, 96 97 /** The file is a FIFO */ 98 FIFO_FILE, 99 100 /** The file is a UNIX socket */ 101 SOCKET_FILE, 102 103 /** The type of the file is unknown, either because it was not 104 * fetched yet or because stat(2) reported something else than the 105 * above. */ 106 TYPE_UNKNOWN, 107 }; 108 109 friend HARNESS_EXPORT std::ostream &operator<<(std::ostream &out, 110 FileType type); 111 112 Path() noexcept; 113 114 /** 115 * Construct a path 116 * 117 * @param path Non-empty string denoting the path. 118 */ 119 /** @overload */ // throws std::invalid_argument 120 Path(const std::string &path); // NOLINT(runtime/explicit) 121 122 /** @overload */ // throws std::invalid_argument 123 Path(const char *path); // NOLINT(runtime/explicit) 124 125 /** 126 * Create a path from directory, basename, and extension. 127 */ 128 static Path make_path(const Path &directory, const std::string &basename, 129 const std::string &extension); 130 131 bool operator==(const Path &rhs) const; 132 bool operator!=(const Path &rhs) const { return !(*this == rhs); } 133 134 /** 135 * Path ordering operator. 136 * 137 * This is mainly used for ordered containers. The paths are ordered 138 * lexicographically. 139 */ 140 bool operator<(const Path &rhs) const; 141 142 /** 143 * Get the file type. 144 * 145 * The file type is normally cached so if the file type under a path 146 * changes it is necessary to force a refresh. 147 * 148 * @param refresh Set to `true` if the file type should be 149 * refreshed, default to `false`. 150 * 151 * @return The type of the file. 152 */ 153 FileType type(bool refresh = false) const; 154 155 /** 156 * Check if the file is a directory. 157 */ 158 bool is_directory() const; 159 160 /** 161 * Check if the file is a regular file. 162 */ 163 bool is_regular() const; 164 165 /** 166 * Check if the path is absolute or not 167 * 168 * The path is considered absolute if it starts with one of: 169 * Unix: '/' 170 * Windows: '/' or '\' or '.:' (where . is any character) 171 * else: 172 * it's considered relative (empty path is also relative in such respect) 173 */ 174 bool is_absolute() const; 175 176 /** 177 * Check if path exists 178 */ 179 bool exists() const; 180 181 /* 182 * @brief Checks if path exists and can be opened for reading. 183 * 184 * @return true if path exists and can be opened for reading, 185 * false otherwise. 186 */ 187 bool is_readable() const; 188 189 /** 190 * Get the directory name of the path. 191 * 192 * This will strip the last component of a path, assuming that the 193 * what remains is a directory name. If the path is a relative path 194 * that do not contain any directory separators, a dot will be 195 * returned (denoting the current directory). 196 * 197 * @note No checking of the components are done, this is just simple 198 * path manipulation. 199 * 200 * @return A new path object representing the directory portion of 201 * the path. 202 */ 203 Path dirname() const; 204 205 /** 206 * Get the basename of the path. 207 * 208 * Return the basename of the path: the path without the directory 209 * portion. 210 * 211 * @note No checking of the components are done, this is just simple 212 * path manipulation. 213 * 214 * @return A new path object representing the basename of the path. 215 * the path. 216 */ 217 Path basename() const; 218 219 /** 220 * Append a path component to the current path. 221 * 222 * This function will append a path component to the path using the 223 * apropriate directory separator. 224 * 225 * @param other Path component to append to the path. 226 */ 227 void append(const Path &other); 228 229 /** 230 * Join two path components to form a new path. 231 * 232 * This function will join the two path components using a 233 * directory separator. 234 * 235 * @note This will return a new `Path` object. If you want to modify 236 * the existing path object, you should use `append` instead. 237 * 238 * @param other Path component to be appended to the path 239 */ 240 Path join(const Path &other) const; 241 242 /** @overload */ join(const char * other)243 Path join(const char *other) const { return join(Path(other)); } 244 245 /** 246 * Returns the canonical form of the path, resolving relative paths. 247 */ 248 Path real_path() const; 249 250 /** 251 * Get a C-string representation to the path. 252 * 253 * @note This will return a pointer to the internal representation 254 * of the path and hence will become a dangling pointer when the 255 * `Path` object is destroyed. 256 * 257 * @return Pointer to a null-terminated C-string. 258 */ c_str()259 const char *c_str() const { return path_.c_str(); } 260 261 /** 262 * Get a string representation of the path. 263 * 264 * @return Instance of std::string containing the path. 265 */ str()266 const std::string &str() const noexcept { return path_; } 267 268 /** 269 * Test if path is set 270 * 271 * @return Test result 272 */ is_set()273 bool is_set() const noexcept { return (type_ != FileType::EMPTY_PATH); } 274 275 /** 276 * Directory separator string. 277 * 278 * @note This is platform-dependent and defined in the apropriate 279 * source file. 280 */ 281 static const char *const directory_separator; 282 283 /** 284 * Root directory string. 285 * 286 * @note This is platform-dependent and defined in the apropriate 287 * source file. 288 */ 289 static const char *const root_directory; 290 291 operator bool() const noexcept { return is_set(); } 292 293 private: 294 void validate_non_empty_path() const; // throws std::invalid_argument 295 296 std::string path_; 297 mutable FileType type_; 298 }; 299 300 /** 301 * Class representing a directory in a file system. 302 * 303 * @ingroup Filesystem 304 * 305 * In addition to being a refinement of `Path`, it also have functions 306 * that make it act like a container of paths and support iterating 307 * over the entries in a directory. 308 * 309 * An example of how it could be used is: 310 * @code 311 * for (auto&& entry: Directory(path)) 312 * std::cout << entry << std::endl; 313 * @endcode 314 */ 315 class HARNESS_EXPORT Directory : public Path { 316 public: 317 /** 318 * Directory iterator for iterating over directory entries. 319 * 320 * A directory iterator is an input iterator. 321 */ 322 class HARNESS_EXPORT DirectoryIterator { 323 friend class Directory; 324 325 public: 326 using value_type = Path; 327 using iterator_category = std::input_iterator_tag; 328 using difference_type = std::ptrdiff_t; 329 using pointer = value_type *; 330 using reference = value_type &; 331 332 DirectoryIterator(const Path &path, 333 const std::string &pattern = std::string()); 334 335 // Create an end iterator 336 DirectoryIterator(); 337 338 /** 339 * Destructor. 340 * 341 * @note We need this *declared* because the default constructor 342 * try to generate a default constructor for shared_ptr on State 343 * below, which does not work since it is not visible. The 344 * destructor need to be *defined* in the corresponding .cc file 345 * since the State type is visible there (but you can use a 346 * default definition). 347 */ 348 ~DirectoryIterator(); 349 350 // We need these since the default move/copy constructor is 351 // deleted when you define a destructor. 352 #if !defined(_MSC_VER) || (_MSC_VER >= 1900) 353 DirectoryIterator(DirectoryIterator &&); 354 DirectoryIterator(const DirectoryIterator &); 355 #endif 356 357 /** Standard iterator operators */ 358 /** @{ */ 359 Path operator*() const; 360 DirectoryIterator &operator++(); 361 Path operator->() { return this->operator*(); } 362 bool operator!=(const DirectoryIterator &other) const; 363 364 // This avoids C2678 (no binary operator found) in MSVC, 365 // MSVC's std::copy implementation (used by TestFilesystem) uses operator== 366 // (while GCC's implementation uses operator!=). 367 bool operator==(const DirectoryIterator &other) const { 368 return !(this->operator!=(other)); 369 } 370 /** @} */ 371 372 private: 373 /** 374 * Path to the root of the directory 375 */ 376 const Path path_; 377 378 /** 379 * Pattern that matches entries iterated over. 380 */ 381 std::string pattern_; 382 383 /* 384 * Platform-dependent container for iterator state. 385 * 386 * The definition of this class is different for different 387 * platforms, meaning that it is not defined here at all but 388 * rather in the corresponding `filesystem-<platform>.cc` file. 389 * 390 * The directory iterator is the most critical piece since it holds 391 * an iteration state for the platform: something that requires 392 * different types on the platforms. 393 */ 394 class State; 395 std::shared_ptr<State> state_; 396 }; 397 398 /** 399 * Construct a directory instance. 400 * 401 * Construct a directory instance in different ways depending on the 402 * version of the constructor used. 403 */ Directory(const std::string & path)404 Directory(const std::string &path) // NOLINT(runtime/explicit) 405 : Path(path) {} // throws std::invalid_argument 406 407 /** @overload */ // throws std::invalid_argument 408 Directory(const Path &path); // NOLINT(runtime/explicit) 409 410 Directory(const Directory &) = default; 411 Directory &operator=(const Directory &) = default; 412 ~Directory(); 413 414 /** 415 * Iterator to first entry. 416 * 417 * @return Returns an iterator pointing to the first entry. 418 */ 419 DirectoryIterator begin(); 420 421 /** 422 * Iterator past-the-end of entries. 423 * 424 * @return Returns an iterator pointing *past-the-end* of the entries. 425 */ 426 DirectoryIterator end(); 427 428 /** 429 * Iterate over entries matching a glob. 430 */ 431 DirectoryIterator glob(const std::string &glob); 432 }; 433 434 //////////////////////////////////////////////////////////////////////////////// 435 // 436 // Utility free functions 437 // 438 //////////////////////////////////////////////////////////////////////////////// 439 440 /** @brief Removes a directory. 441 * 442 * @ingroup Filesystem 443 * 444 * @param dir path of the directory to be removed; this directory must be empty 445 * 446 * @return 0 on success, -1 on error and sets errno 447 */ 448 HARNESS_EXPORT 449 int delete_dir(const std::string &dir) noexcept; 450 451 /** @brief Removes a file. 452 * 453 * @ingroup Filesystem 454 * 455 * @param path of the file to be removed 456 * 457 * @return 0 on success, -1 on error 458 */ 459 HARNESS_EXPORT 460 int delete_file(const std::string &path) noexcept; 461 462 /** @brief Removes directory and all its contents. 463 * 464 * @ingroup Filesystem 465 * 466 * @param dir path of the directory to be removed 467 * 468 * @return 0 on success, -1 on error 469 */ 470 HARNESS_EXPORT 471 int delete_dir_recursive(const std::string &dir) noexcept; 472 473 /** @brief Creates a temporary directory with partially-random name and returns 474 * its path. 475 * 476 * Creates a directory with a name of form {prefix}-{6 random alphanumerals}. 477 * For example, a possible directory name created by a call to 478 * get_tmp_dir("foo") might be: foo-3f9x0z 479 * 480 * Such directory is usually meant to be used as a temporary directory (thus the 481 * "_tmp_" in the name of this function). 482 * 483 * @ingroup Filesystem 484 * 485 * @param name name to be used as a directory name prefix 486 * 487 * @return path to the created directory 488 * 489 * @throws std::runtime_error if operation failed 490 */ 491 HARNESS_EXPORT 492 std::string get_tmp_dir(const std::string &name = "router"); 493 494 // TODO: description 495 // TODO: move to some other place? 496 HARNESS_EXPORT 497 std::string get_plugin_dir(const std::string &runtime_dir); 498 499 HARNESS_EXPORT 500 std::string get_tests_data_dir(const std::string &runtime_dir); 501 502 #ifdef _WIN32 503 /** 504 * Deleter for smart pointers pointing to objects allocated with `std::malloc`. 505 */ 506 template <typename T> 507 class StdFreeDeleter { 508 public: operator()509 void operator()(T *ptr) { std::free(ptr); } 510 }; 511 512 /** 513 * Retrieves file's DACL security descriptor. 514 * 515 * @param[in] file_name File name. 516 * 517 * @return File's DACL security descriptor. 518 * 519 * @throw std::exception Failed to retrieve security descriptor. 520 */ 521 HARNESS_EXPORT std::unique_ptr<SECURITY_DESCRIPTOR, decltype(&free)> 522 get_security_descriptor(const std::string &file_name); 523 524 #endif 525 526 #ifndef _WIN32 527 using perm_mode = mode_t; 528 HARNESS_EXPORT 529 extern const perm_mode kStrictDirectoryPerm; 530 #else 531 using perm_mode = int; 532 HARNESS_EXPORT 533 extern const perm_mode kStrictDirectoryPerm; 534 #endif 535 536 /** @brief Creates a directory 537 * * 538 * @param dir name (or path) of the directory to create 539 * @param mode permission mode for the created directory 540 * @param recursive if true then immitated unix `mkdir -p` recursively 541 * creating parent directories if needed 542 * @retval 0 operation succeeded 543 * @retval -1 operation failed because of wrong parameters 544 * @retval > 0 errno for failure to mkdir() system call 545 */ 546 HARNESS_EXPORT 547 int mkdir(const std::string &dir, perm_mode mode, bool recursive = false); 548 549 /** 550 * Changes file access permissions to be fully accessible by all users. 551 * 552 * On Unix, the function sets file permission mask to 777. 553 * On Windows, Everyone group is granted full access to the file. 554 * 555 * @param[in] file_name File name. 556 * 557 * @throw std::exception Failed to change file permissions. 558 */ 559 void HARNESS_EXPORT make_file_public(const std::string &file_name); 560 561 #ifdef _WIN32 562 /** 563 * Changes file access permissions to be readable by all users. 564 * 565 * On Windows, Everyone group is granted read access to the file. 566 * 567 * @param[in] file_name File name. 568 * 569 * @throw std::exception Failed to change file permissions. 570 */ 571 void make_file_readable_for_everyone(const std::string &file_name); 572 #endif 573 574 /** 575 * Changes file access permissions to be accessible only by a limited set of 576 * users. 577 * 578 * On Unix, the function sets file permission mask to 600. 579 * On Windows, all permissions to this file are removed for Everyone group, 580 * LocalService account gets read (and optionally write) access. 581 * 582 * @param[in] file_name File name. 583 * @param[in] read_only_for_local_service Weather the LocalService user on 584 * Windows should get only the read access (if false will grant write access 585 * too). Not used on non-Windows. 586 * 587 * @throw std::exception Failed to change file permissions. 588 */ 589 void HARNESS_EXPORT 590 make_file_private(const std::string &file_name, 591 const bool read_only_for_local_service = true); 592 593 /** 594 * Changes file access permissions to be read only. 595 * 596 * On Unix, the function sets file permission mask to 555. 597 * On Windows, all permissions to this file are read access only for Everyone 598 * group, LocalService account gets read access. 599 * 600 * @param[in] file_name File name. 601 * 602 * @throw std::exception Failed to change file permissions. 603 */ 604 void HARNESS_EXPORT make_file_readonly(const std::string &file_name); 605 606 /** 607 * Verifies access permissions of a file. 608 * 609 * On Unix systems it throws if file's permissions differ from 600. 610 * On Windows it throws if file can be accessed by Everyone group. 611 * 612 * @param[in] file_name File to be verified. 613 * 614 * @throw std::exception File access rights are too permissive or 615 * an error occurred. 616 * @throw std::system_error OS and/or filesystem doesn't support file 617 * permissions. 618 */ 619 620 void HARNESS_EXPORT check_file_access_rights(const std::string &file_name); 621 622 } // namespace mysql_harness 623 624 #endif /* MYSQL_HARNESS_FILESYSTEM_INCLUDED */ 625