From 2ae9d11b7c0fd54e0b5298fb4fcf17b6ee9d5c91 Mon Sep 17 00:00:00 2001 From: Reid Spencer Date: Sat, 7 Apr 2007 18:52:17 +0000 Subject: [PATCH] For PR1291: Implement the PathWithStatus class and its use throughout lib/System. git-svn-id: https://llvm.org/svn/llvm-project/llvm/trunk@35742 91177308-0d34-0410-b5e6-96231b3b80d8 --- include/llvm/System/MappedFile.h | 2 +- include/llvm/System/Path.h | 113 ++++++++++++++++++++++++++++++--------- lib/System/Unix/Path.inc | 89 +++++++++++++++--------------- lib/System/Unix/Signals.inc | 12 +++-- lib/System/Win32/Path.inc | 30 +++++------ 5 files changed, 157 insertions(+), 89 deletions(-) diff --git a/include/llvm/System/MappedFile.h b/include/llvm/System/MappedFile.h index f82d6fe9429..6276bc3b2cd 100644 --- a/include/llvm/System/MappedFile.h +++ b/include/llvm/System/MappedFile.h @@ -152,7 +152,7 @@ namespace sys { /// @name Data /// @{ private: - sys::Path path_; ///< Path to the file. + sys::PathWithStatus path_; ///< Path to the file. int options_; ///< Options used to create the mapping void* base_; ///< Pointer to the base memory address mutable MappedFileInfo* info_; ///< Platform specific info for the mapping diff --git a/include/llvm/System/Path.h b/include/llvm/System/Path.h index e2519853c91..69c1b483070 100644 --- a/include/llvm/System/Path.h +++ b/include/llvm/System/Path.h @@ -29,9 +29,9 @@ namespace sys { /// platform independent and eliminates many of the unix-specific fields. /// However, to support llvm-ar, the mode, user, and group fields are /// retained. These pertain to unix security and may not have a meaningful - /// value on non-Unix platforms. However, the fileSize and modTime fields - /// should always be applicable on all platforms. The structure is - /// filled in by the Path::getFileStatus method. + /// value on non-Unix platforms. However, the other fields fields should + /// always be applicable on all platforms. The structure is filled in by + /// the PathWithStatus class. /// @brief File status structure class FileStatus { public: @@ -164,16 +164,15 @@ namespace sys { /// provided so that they can be used to indicate null or error results in /// other lib/System functionality. /// @brief Construct an empty (and invalid) path. - Path() : path(), status(0) {} - ~Path() { delete status; } - Path(const Path &that) : path(that.path), status(0) {} + Path() : path() {} + Path(const Path &that) : path(that.path) {} /// This constructor will accept a std::string as a path. No checking is /// done on this path to determine if it is valid. To determine validity /// of the path, use the isValid method. /// @param p The path to assign. /// @brief Construct a Path from a string. - explicit Path(const std::string& p) : path(p), status(0) {} + explicit Path(const std::string& p) : path(p) {} /// @} /// @name Operators @@ -184,9 +183,6 @@ namespace sys { /// @brief Assignment Operator Path &operator=(const Path &that) { path = that.path; - if (status) - delete status; - status = 0; return *this; } @@ -230,8 +226,8 @@ namespace sys { /// This function determines if the contents of the path name are empty. /// That is, the path name has a zero length. This does NOT determine if /// if the file is empty. To get the length of the file itself, Use the - /// getFileStatus() method and then the getSize() on the returned - /// FileStatus object + /// PathWithStatus::getFileStatus() method and then the getSize() method + /// on the returned FileStatus object. /// @returns true iff the path is empty. /// @brief Determines if the path name is empty (invalid). bool isEmpty() const { return path.empty(); } @@ -361,17 +357,6 @@ namespace sys { std::string* ErrMsg ///< Optional place to return an error message. ) const; - /// This function returns status information about the file. The type of - /// path (file or directory) is updated to reflect the actual contents - /// of the file system. - /// @returns 0 on failure, with Error explaining why (if non-zero) - /// @returns a pointer to a FileStatus structure on success. - /// @brief Get file status. - const FileStatus *getFileStatus( - bool forceUpdate = false, ///< Force an update from the file system - std::string *Error = 0 ///< Optional place to return an error msg. - ) const; - /// @} /// @name Path Mutators /// @{ @@ -527,9 +512,85 @@ namespace sys { /// @} /// @name Data /// @{ - private: + protected: mutable std::string path; ///< Storage for the path name. - mutable FileStatus *status; ///< Status information. + + /// @} + }; + + /// This class is identical to Path class except it allows you to obtain the + /// file status of the Path as well. The reason for the distinction is one of + /// efficiency. First, the file status requires additional space and the space + /// is incorporated directly into PathWithStatus without an additional malloc. + /// Second, obtaining status information is an expensive operation on most + /// operating systems so we want to be careful and explicity about where we + /// allow this operation in LLVM. + /// @brief Path with file status class. + class PathWithStatus : public Path { + /// @name Constructors + /// @{ + public: + /// @brief Default constructor + PathWithStatus() : Path(), status(), fsIsValid(false) {} + + /// @brief Copy constructor + PathWithStatus(const PathWithStatus &that) + : Path(static_cast(that)), status(that.status), + fsIsValid(that.fsIsValid) {} + + /// This constructor allows construction from a Path object + /// @brief Path constructor + PathWithStatus(const Path &other) + : Path(other), status(), fsIsValid(false) {} + + /// This constructor will accept a std::string as a path. No checking is + /// done on this path to determine if it is valid. To determine validity + /// of the path, use the isValid method. + /// @param p The path to assign. + /// @brief Construct a Path from a string. + explicit PathWithStatus(const std::string& p) + : Path(p), status(), fsIsValid(false) {} + + /// Makes a copy of \p that to \p this. + /// @returns \p this + /// @brief Assignment Operator + PathWithStatus &operator=(const PathWithStatus &that) { + static_cast(*this) = static_cast(that); + status = that.status; + fsIsValid = that.fsIsValid; + return *this; + } + + /// Makes a copy of \p that to \p this. + /// @returns \p this + /// @brief Assignment Operator + PathWithStatus &operator=(const Path &that) { + static_cast(*this) = static_cast(that); + fsIsValid = false; + return *this; + } + + /// @} + /// @name Methods + /// @{ + public: + /// This function returns status information about the file. The type of + /// path (file or directory) is updated to reflect the actual contents + /// of the file system. + /// @returns 0 on failure, with Error explaining why (if non-zero) + /// @returns a pointer to a FileStatus structure on success. + /// @brief Get file status. + const FileStatus *getFileStatus( + bool forceUpdate = false, ///< Force an update from the file system + std::string *Error = 0 ///< Optional place to return an error msg. + ) const; + + /// @} + /// @name Data + /// @{ + private: + mutable FileStatus status; ///< Status information. + mutable bool fsIsValid; ///< Whether we've obtained it or not /// @} }; @@ -556,7 +617,9 @@ namespace sys { bool CopyFile(const Path& Dest, const Path& Src, std::string* ErrMsg); } + std::ostream& operator<<(std::ostream& strm, const sys::Path& aPath); +std::ostream& operator<<(std::ostream& strm, const sys::PathWithStatus& aPath); } diff --git a/lib/System/Unix/Path.inc b/lib/System/Unix/Path.inc index 6ceffcdfe31..53906d99cec 100644 --- a/lib/System/Unix/Path.inc +++ b/lib/System/Unix/Path.inc @@ -333,10 +333,10 @@ bool Path::canExecute() const { if (0 != access(path.c_str(), R_OK | X_OK )) return false; - if (const FileStatus *fs = getFileStatus(true, 0)) { - if (!S_ISREG(fs->mode)) - return false; - } else + struct stat buf; + if (0 != stat(path.c_str(), &buf)) + return false; + if (!S_ISREG(buf.st_mode)) return false; return true; } @@ -363,26 +363,25 @@ Path::getLast() const { return path.substr(pos+1); } -const FileStatus* -Path::getFileStatus(bool update, std::string *ErrStr) const { - if (status == 0 || update) { +const FileStatus * +PathWithStatus::getFileStatus(bool update, std::string *ErrStr) const { + if (!fsIsValid || update) { struct stat buf; if (0 != stat(path.c_str(), &buf)) { MakeErrMsg(ErrStr, path + ": can't get status of file"); return 0; } - if (status == 0) - status = new FileStatus; - status->fileSize = buf.st_size; - status->modTime.fromEpochTime(buf.st_mtime); - status->mode = buf.st_mode; - status->user = buf.st_uid; - status->group = buf.st_gid; - status->uniqueID = uint64_t(buf.st_ino); - status->isDir = S_ISDIR(buf.st_mode); - status->isFile = S_ISREG(buf.st_mode); + status.fileSize = buf.st_size; + status.modTime.fromEpochTime(buf.st_mtime); + status.mode = buf.st_mode; + status.user = buf.st_uid; + status.group = buf.st_gid; + status.uniqueID = uint64_t(buf.st_ino); + status.isDir = S_ISDIR(buf.st_mode); + status.isFile = S_ISREG(buf.st_mode); + fsIsValid = true; } - return status; + return &status; } static bool AddPermissionBits(const Path &File, int bits) { @@ -394,14 +393,13 @@ static bool AddPermissionBits(const Path &File, int bits) { umask(mask); // Restore the umask. // Get the file's current mode. - if (const FileStatus *fs = File.getFileStatus()) { - // Change the file to have whichever permissions bits from 'bits' - // that the umask would not disable. - if ((chmod(File.c_str(), (fs->getMode() | (bits & ~mask)))) == -1) - return false; - } else + struct stat buf; + if (0 != stat(File.toString().c_str(), &buf)) return false; - + // Change the file to have whichever permissions bits from 'bits' + // that the umask would not disable. + if ((chmod(File.c_str(), (buf.st_mode | (bits & ~mask)))) == -1) + return false; return true; } @@ -594,25 +592,28 @@ Path::createTemporaryFileOnDisk(bool reuse_current, std::string* ErrMsg) { bool Path::eraseFromDisk(bool remove_contents, std::string *ErrStr) const { - FileStatus Status; - if (const FileStatus *Status = getFileStatus(false, ErrStr)) { - // Note: this check catches strange situations. In all cases, LLVM should - // only be involved in the creation and deletion of regular files. This - // check ensures that what we're trying to erase is a regular file. It - // effectively prevents LLVM from erasing things like /dev/null, any block - // special file, or other things that aren't "regular" files. - if (Status->isFile) { - if (unlink(path.c_str()) != 0) - return MakeErrMsg(ErrStr, path + ": can't destroy file"); - return false; - } - - if (!Status->isDir) { - if (ErrStr) *ErrStr = "not a file or directory"; - return true; - } - } else + // Get the status so we can determin if its a file or directory + struct stat buf; + if (0 != stat(path.c_str(), &buf)) { + MakeErrMsg(ErrStr, path + ": can't get status of file"); return true; + } + + // Note: this check catches strange situations. In all cases, LLVM should + // only be involved in the creation and deletion of regular files. This + // check ensures that what we're trying to erase is a regular file. It + // effectively prevents LLVM from erasing things like /dev/null, any block + // special file, or other things that aren't "regular" files. + if (S_ISREG(buf.st_mode)) { + if (unlink(path.c_str()) != 0) + return MakeErrMsg(ErrStr, path + ": can't destroy file"); + return false; + } + + if (!S_ISDIR(buf.st_mode)) { + if (ErrStr) *ErrStr = "not a file or directory"; + return true; + } if (remove_contents) { // Recursively descend the directory to remove its contents. @@ -644,7 +645,7 @@ Path::renamePathOnDisk(const Path& newName, std::string* ErrMsg) { return false; } -bool +bool Path::setStatusInfoOnDisk(const FileStatus &si, std::string *ErrStr) const { struct utimbuf utb; utb.actime = si.modTime.toPosixTime(); diff --git a/lib/System/Unix/Signals.inc b/lib/System/Unix/Signals.inc index a471b95a999..d1493a26c9b 100644 --- a/lib/System/Unix/Signals.inc +++ b/lib/System/Unix/Signals.inc @@ -21,6 +21,9 @@ #if HAVE_SIGNAL_H #include #endif +#if HAVE_SYS_STAT_H +#include +#endif using namespace llvm; namespace { @@ -168,10 +171,13 @@ bool sys::RemoveFileOnSignal(const sys::Path &Filename, std::string* ErrMsg) { // RemoveDirectoryOnSignal - The public API bool sys::RemoveDirectoryOnSignal(const sys::Path& path, std::string* ErrMsg) { // Not a directory? - const sys::FileStatus *Status = path.getFileStatus(false, ErrMsg); - if (!Status) + struct stat buf; + if (0 != stat(path.c_str(), &buf)) { + MakeErrMsg(ErrMsg, path.toString() + ": can't get status of file"); return true; - if (!Status->isDir) { + } + + if (!S_ISDIR(buf.st_mode)) { if (ErrMsg) *ErrMsg = path.toString() + " is not a directory"; return true; diff --git a/lib/System/Win32/Path.inc b/lib/System/Win32/Path.inc index 57d75358b81..937bb51fa63 100644 --- a/lib/System/Win32/Path.inc +++ b/lib/System/Win32/Path.inc @@ -307,8 +307,8 @@ Path::getLast() const { } const FileStatus * -Path::getFileStatus(bool update, std::string *ErrStr) const { - if (status == 0 || update) { +PathWithStatus::getFileStatus(bool update, std::string *ErrStr) const { + if (!fsIsValid || update) { WIN32_FILE_ATTRIBUTE_DATA fi; if (!GetFileAttributesEx(path.c_str(), GetFileExInfoStandard, &fi)) { MakeErrMsg(ErrStr, "getStatusInfo():" + std::string(path) + @@ -316,30 +316,28 @@ Path::getFileStatus(bool update, std::string *ErrStr) const { return 0; } - if (status == 0) - status = new FileStatus; + status.fileSize = fi.nFileSizeHigh; + status.fileSize <<= sizeof(fi.nFileSizeHigh)*8; + status.fileSize += fi.nFileSizeLow; - status->fileSize = fi.nFileSizeHigh; - status->fileSize <<= sizeof(fi.nFileSizeHigh)*8; - status->fileSize += fi.nFileSizeLow; - - status->mode = fi.dwFileAttributes & FILE_ATTRIBUTE_READONLY ? 0555 : 0777; - status->user = 9999; // Not applicable to Windows, so... - status->group = 9999; // Not applicable to Windows, so... + status.mode = fi.dwFileAttributes & FILE_ATTRIBUTE_READONLY ? 0555 : 0777; + status.user = 9999; // Not applicable to Windows, so... + status.group = 9999; // Not applicable to Windows, so... // FIXME: this is only unique if the file is accessed by the same file path. // How do we do this for C:\dir\file and ..\dir\file ? Unix has inode // numbers, but the concept doesn't exist in Windows. - status->uniqueID = 0; + status.uniqueID = 0; for (unsigned i = 0; i < path.length(); ++i) - status->uniqueID += path[i]; + status.uniqueID += path[i]; __int64 ft = *reinterpret_cast<__int64*>(&fi.ftLastWriteTime); - status->modTime.fromWin32Time(ft); + status.modTime.fromWin32Time(ft); - status->isDir = fi.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY; + status.isDir = fi.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY; + fsIsValid = true; } - return status; + return &status; } bool Path::makeReadableOnDisk(std::string* ErrMsg) { -- 2.11.0