/* * Phusion Passenger - https://www.phusionpassenger.com/ * Copyright (c) 2010-2018 Phusion Holding B.V. * * "Passenger", "Phusion Passenger" and "Union Station" are registered * trademarks of Phusion Holding B.V. * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal * in the Software without restriction, including without limitation the rights * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. */ #ifndef _PASSENGER_FILE_TOOLS_FILE_MANIP_H_ #define _PASSENGER_FILE_TOOLS_FILE_MANIP_H_ #include #include #include #include #include #include #include namespace boost { class mutex; } namespace Passenger { using namespace std; // All functions in this file allow non-NULL-terminated StaticStrings. class CachedFileStat; static const uid_t USER_NOT_GIVEN = (uid_t) -1; static const gid_t GROUP_NOT_GIVEN = (gid_t) -1; /** Enumeration which indicates what kind of file a file is. */ typedef enum { /** The file doesn't exist. */ FT_NONEXISTANT, /** A regular file or a symlink to a regular file. */ FT_REGULAR, /** A directory. */ FT_DIRECTORY, /** Something else, e.g. a pipe or a socket. */ FT_OTHER } FileType; /** * Given a filename, FileGuard will unlink the file in its destructor, unless * commit() was called. Used in file operation functions that don't want to * leave behind half-finished files after error conditions. */ struct FileGuard { string filename; bool committed; FileGuard(const StaticString &filename); ~FileGuard(); void commit(); }; /** * Check whether the specified file exists. * * @param filename The filename to check. * @param cstat A CachedFileStat object, if you want to use cached statting. * @param cstatMutex A mutex for locking cstat while this function uses it. * Makes this function thread-safe. May be NULL. * @param throttleRate A throttle rate for cstat. Only applicable if cstat is not NULL. * @return Whether the file exists. * @throws FileSystemException Unable to check because of a filesystem error. * @throws TimeRetrievalException * @throws boost::thread_interrupted */ bool fileExists(const StaticString &filename, CachedFileStat *cstat = 0, boost::mutex *cstatMutex = NULL, unsigned int throttleRate = 0); /** * Check whether the specified directory exists. * * @param dirname The path to check. * @param cstat A CachedFileStat object, if you want to use cached statting. * @param cstatMutex A mutex for locking cstat while this function uses it. * Makes this function thread-safe. May be NULL. * @param throttleRate A throttle rate for cstat. Only applicable if cstat is not NULL. * @return Whether the directory exists. * @throws FileSystemException Unable to check because of a filesystem error. * @throws TimeRetrievalException * @throws boost::thread_interrupted */ bool dirExists(const StaticString &dirname, CachedFileStat *cstat = 0, boost::mutex *cstatMutex = NULL, unsigned int throttleRate = 0); /** * Check whether 'filename' exists and what kind of file it is. * * @param filename The filename to check. It MUST be NULL-terminated. * @param cstat A CachedFileStat object, if you want to use cached statting. * @param cstatMutex A mutex for locking cstat while this function uses it. * Makes this function thread-safe. May be NULL. * @param throttleRate A throttle rate for cstat. Only applicable if cstat is not NULL. * @return The file type. * @throws FileSystemException Unable to check because of a filesystem error. * @throws TimeRetrievalException * @throws boost::thread_interrupted * @ingroup Support */ FileType getFileType(const StaticString &filename, CachedFileStat *cstat = 0, boost::mutex *cstatMutex = NULL, unsigned int throttleRate = 0); /** * Create the given file with the given contents, permissions and ownership. * This function does not leave behind junk files: if the ownership cannot be set * or if not all data can be written then then the file will be deleted. * * @param filename The file to create. * @param contents The contents to write to the file. * @param permissions The desired file permissions. * @param owner The desired file owner. Specify USER_NOT_GIVEN if you want to use the current * process's owner as the file owner. * @param group The desired file group. Specify GROUP_NOT_GIVEN if you want to use the current * process's group as the file group. * @param overwrite Whether to overwrite the file if it exists. If set to false * and the file exists then nothing will happen. * @throws FileSystemException Something went wrong. */ void createFile(const string &filename, const StaticString &contents, mode_t permissions = S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH, uid_t owner = USER_NOT_GIVEN, gid_t group = GROUP_NOT_GIVEN, bool overwrite = true, const char *callerFile = NULL, unsigned int callerLine = 0); /** * Read all data from the given file until EOF. * This function is "unsafe" in the sense that it lacks the security * checks implemented by `safeReadFile()`. Read the docs for that function * for more information. * * @throws SystemException */ string unsafeReadFile(const string &path); /** * Read all data from the given file until EOF. * * - `dirfd` is a file descriptor of the directory that contains the file * you want read from. * - `basename` is the basename of the file you want to read from. `basename` * may thus not contain slashes. * - `maxSize` is the maximum number of bytes you want to read. * * Returns a pair `(contents, eof)`. * * - `contents` is the read file contents, which is at most `maxSize` bytes. * - `eof` indicates whether the entire file has been read. If false, then it * means the amount of data is larger than `maxSize`. * * This function is "safe" in following sense: * * - It mitigates symbolic link attacks. `open(path)` may not be safe for * processes running as root, because if a user controls any parts of the * path then the user can swap one of the parent directories, or the file * itself, with a symlink. This causes us to read an arbitrary file. * * This function mitigates this attack by requiring a `dirfd` and by opening * the file with O_NOFOLLOW. The caller must ensure that `dirfd` is created * at a time when it knows that no user controls any parts of the path to * that directory. * * However, this mitigation does mean that safeReadFile() *cannot be used to * read from a symlink*! * * - It mitigates DoS attacks through non-regular files which may block the * reader, like FIFOs or block devices. For example if the path refers to a * FIFO which a user created, and the user never opens the FIFO on the other * side, then the open can block indefinitely. * * This function mitigates this attack by opening the file with O_NONBLOCK. * * - It mitigates DoS attacks by creating a very large file. Since we slurp * the entire file into memory, it is a good idea if we impose a limit * on how much data we read. * * @throws ArgumentException * @throws SystemException */ pair safeReadFile(int dirfd, const string &basename, size_t maxSize); /** * Create the directory at the given path, creating intermediate directories * if necessary. The created directories' permissions are exactly as specified * by the 'mode' parameter (i.e. the umask will be ignored). You can specify * this directory's owner and group through the 'owner' and 'group' parameters. * A value of USER_NOT_GIVEN for 'owner' and/or GROUP_NOT_GIVEN 'group' means * that the owner/group should not be changed. * * If 'path' already exists, then nothing will happen. * * @param mode A mode string, as supported by parseModeString(). * @throws FileSystemException Something went wrong. * @throws InvalidModeStringException The mode string cannot be parsed. */ void makeDirTree(const string &path, const StaticString &mode = "u=rwx,g=,o=", uid_t owner = USER_NOT_GIVEN, gid_t group = GROUP_NOT_GIVEN); /** * Remove an entire directory tree recursively. If the directory doesn't exist then this * function does nothing. * * @throws RuntimeException Something went wrong. */ void removeDirTree(const string &path); } // namespace Passenger #endif /* _PASSENGER_FILE_TOOLS_FILE_MANIP_H_ */