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
This commit is contained in:
Reid Spencer 2007-04-07 18:52:17 +00:00
parent e9ed4452bc
commit 2ae9d11b7c
5 changed files with 157 additions and 89 deletions

View File

@ -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

View File

@ -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<const Path&>(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<Path&>(*this) = static_cast<const Path&>(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<Path&>(*this) = static_cast<const Path&>(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);
}

View File

@ -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();

View File

@ -21,6 +21,9 @@
#if HAVE_SIGNAL_H
#include <signal.h>
#endif
#if HAVE_SYS_STAT_H
#include <sys/stat.h>
#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;

View File

@ -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) {