Skip to content

Commit

Permalink
Merge pull request #7994 from hrydgard/fileutil-cleanup
Browse files Browse the repository at this point in the history
Remove all uses of stat() on Windows. Buggy on XP with VS 2015.
  • Loading branch information
hrydgard committed Sep 23, 2015
2 parents c2ac933 + 6fcbfab commit 103b73d
Show file tree
Hide file tree
Showing 16 changed files with 181 additions and 265 deletions.
2 changes: 1 addition & 1 deletion Common/ChunkFile.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -169,7 +169,7 @@ CChunkFileReader::Error CChunkFileReader::LoadFile(const std::string& _rFilename
}

// Check file size
const u64 fileSize = File::GetSize(_rFilename);
const u64 fileSize = File::GetFileSize(_rFilename);
static const u64 headerSize = sizeof(SChunkHeader);
if (fileSize < headerSize)
{
Expand Down
2 changes: 0 additions & 2 deletions Common/CommonFuncs.h
Original file line number Diff line number Diff line change
Expand Up @@ -77,8 +77,6 @@ inline u64 __rotr64(u64 x, unsigned int shift){
#define fseeko _fseeki64
#define ftello _ftelli64
#define atoll _atoi64
#define stat64 _stat64
#define fstat64 _fstat64
#define fileno _fileno
#ifndef _XBOX
#if _M_IX86
Expand Down
204 changes: 98 additions & 106 deletions Common/FileUtil.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,9 @@

#if defined(_MSC_VER)
#pragma warning(disable:4091) // workaround bug in VS2015 headers
#ifndef UNICODE
#error Win32 build requires a unicode build
#endif
#endif

#include "FileUtil.h"
Expand Down Expand Up @@ -119,10 +122,8 @@ bool OpenCPPFile(std::fstream & stream, const std::string &filename, std::ios::o

// Remove any ending forward slashes from directory paths
// Modifies argument.
static void StripTailDirSlashes(std::string &fname)
{
if (fname.length() > 1)
{
static void StripTailDirSlashes(std::string &fname) {
if (fname.length() > 1) {
size_t i = fname.length() - 1;
#ifdef _WIN32
if (i == 2 && fname[1] == ':' && fname[2] == '\\')
Expand All @@ -134,82 +135,50 @@ static void StripTailDirSlashes(std::string &fname)
return;
}

// _WIN32 only since std::strings are used everywhere else.
#if defined(_WIN32) && defined(UNICODE)
static void StripTailDirSlashes(std::wstring &fname)
{
if (fname.length() > 1)
{
size_t i = fname.length() - 1;
// Returns true if file filename exists. Will return true on directories.
bool Exists(const std::string &filename) {
std::string fn = filename;
StripTailDirSlashes(fn);

if (i == 2 && fname[1] == ':' && fname[2] == '\\')
return;
#if defined(_WIN32)
std::wstring copy = ConvertUTF8ToWString(fn);

while (wcschr((const wchar_t*)_T(DIR_SEP_CHRS), fname[i]))
fname[i--] = '\0';
}
return;
}
#endif

// Returns true if file filename exists
bool Exists(const std::string &filename)
{
// Make sure Windows will no longer handle critical errors, which means no annoying "No disk" dialog
// Save the old error mode
#ifdef _WIN32
int OldMode = SetErrorMode(SEM_FAILCRITICALERRORS);
#endif

struct stat64 file_info;
#if defined(_WIN32) && defined(UNICODE)
std::wstring copy = ConvertUTF8ToWString(filename);
StripTailDirSlashes(copy);

int result = _wstat64(copy.c_str(), &file_info);
#else
std::string copy(filename);
StripTailDirSlashes(copy);

int result = stat64(copy.c_str(), &file_info);
#endif

// Set the old error mode
#ifdef _WIN32
bool success = GetFileAttributes(copy.c_str()) != INVALID_FILE_ATTRIBUTES;
SetErrorMode(OldMode);
return success;
#else
struct stat64 file_info;
return stat64(fn.c_str(), &file_info) == 0;
#endif

return (result == 0);
}

// Returns true if stat represents a directory
bool IsDirectory(const struct stat64 &file_info)
{
return S_ISDIR(file_info.st_mode);
}

// Returns true if filename is a directory
bool IsDirectory(const std::string &filename)
{
struct stat64 file_info;
#if defined(_WIN32) && defined(UNICODE)
std::wstring copy = ConvertUTF8ToWString(filename);
StripTailDirSlashes(copy);
std::string fn = filename;
StripTailDirSlashes(fn);

int result = _wstat64(copy.c_str(), &file_info);
#if defined(_WIN32)
std::wstring copy = ConvertUTF8ToWString(fn);
DWORD result = GetFileAttributes(copy.c_str());
if (result == INVALID_FILE_ATTRIBUTES) {
WARN_LOG(COMMON, "GetFileAttributes failed on %s: %08x", fn.c_str(), GetLastError());
return false;
}
return (result & FILE_ATTRIBUTE_DIRECTORY) == FILE_ATTRIBUTE_DIRECTORY;
#else
std::string copy(filename);
StripTailDirSlashes(copy);

std::string copy(fn);
struct stat64 file_info;
int result = stat64(copy.c_str(), &file_info);
#endif
if (result < 0) {
WARN_LOG(COMMON, "IsDirectory: stat failed on %s: %s",
filename.c_str(), GetLastErrorMsg());
fn.c_str(), GetLastErrorMsg());
return false;
}

return IsDirectory(file_info);
return S_ISDIR(file_info.st_mode);
#endif
}

// Deletes a given filename, return true on success
Expand Down Expand Up @@ -452,73 +421,96 @@ bool Copy(const std::string &srcFilename, const std::string &destFilename)
#endif
}

bool GetModifTime(const std::string &filename, tm &return_time)
{
memset(&return_time, 0, sizeof(return_time));
if (!Exists(filename))
{
WARN_LOG(COMMON, "GetCreateTime: failed %s: No such file", filename.c_str());
#ifdef _WIN32

static int64_t FiletimeToStatTime(FILETIME ft) {
const int windowsTickResolution = 10000000;
const int64_t secToUnixEpoch = 11644473600LL;
int64_t ticks = ((uint64_t)ft.dwHighDateTime << 32) | ft.dwLowDateTime;
return (int64_t)(ticks / windowsTickResolution - secToUnixEpoch);
}

#endif

// Returns file attributes.
bool GetFileDetails(const std::string &filename, FileDetails *details) {
#ifdef _WIN32
WIN32_FILE_ATTRIBUTE_DATA attr;
if (!GetFileAttributesEx(ConvertUTF8ToWString(filename).c_str(), GetFileExInfoStandard, &attr))
return false;
details->isDirectory = (attr.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) != 0;
details->size = ((u64)attr.nFileSizeHigh << 32) | (u64)attr.nFileSizeLow;
details->atime = FiletimeToStatTime(attr.ftLastAccessTime);
details->mtime = FiletimeToStatTime(attr.ftLastWriteTime);
details->ctime = FiletimeToStatTime(attr.ftCreationTime);
if (attr.dwFileAttributes & FILE_ATTRIBUTE_READONLY) {
details->access = 0444; // Read
} else {
details->access = 0666; // Read/Write
}

if (IsDirectory(filename))
{
WARN_LOG(COMMON, "GetCreateTime: failed %s: is a directory", filename.c_str());
if (attr.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) {
details->access |= 0111; // Execute
}
return true;
#else
if (!Exists(filename)) {
return false;
}

struct stat64 buf;
if (stat64(filename.c_str(), &buf) == 0)
{
INFO_LOG(COMMON, "GetCreateTime: %s: %lld", filename.c_str(), (long long)buf.st_mtime);
localtime_r((time_t*)&buf.st_mtime, &return_time);
if (stat64(filename.c_str(), &buf) == 0) {
details->size = buf.st_size;
details->isDirectory = S_ISDIR(buf.st_mode);
details->atime = buf.st_atime;
details->mtime = buf.st_mtime;
details->ctime = buf.st_ctime;
details->access = buf.st_mode & 0x1ff;
return true;
} else {
return false;
}
#endif
}

ERROR_LOG(COMMON, "GetCreateTime: Stat failed %s: %s", filename.c_str(), GetLastErrorMsg());
return false;
bool GetModifTime(const std::string &filename, tm &return_time) {
memset(&return_time, 0, sizeof(return_time));
FileDetails details;
if (GetFileDetails(filename, &details)) {
time_t t = details.mtime;
localtime_r((time_t*)&t, &return_time);
return true;
} else {
return false;
}
}

// Returns the size of filename (64bit)
u64 GetSize(const std::string &filename)
{
struct stat64 file_info;
// Returns the size of file (64bit)
// TODO: Add a way to return an error.
u64 GetFileSize(const std::string &filename) {
#if defined(_WIN32) && defined(UNICODE)
int result = _wstat64(ConvertUTF8ToWString(filename).c_str(), &file_info);
WIN32_FILE_ATTRIBUTE_DATA attr;
if (!GetFileAttributesEx(ConvertUTF8ToWString(filename).c_str(), GetFileExInfoStandard, &attr))
return 0;
if (attr.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)
return 0;
return ((u64)attr.nFileSizeHigh << 32) | (u64)attr.nFileSizeLow;
#else
struct stat64 file_info;
int result = stat64(filename.c_str(), &file_info);
#endif
if (result != 0)
{
if (result != 0) {
WARN_LOG(COMMON, "GetSize: failed %s: No such file", filename.c_str());
return 0;
}

if (IsDirectory(file_info))
{
if (S_ISDIR(file_info.st_mode)) {
WARN_LOG(COMMON, "GetSize: failed %s: is a directory", filename.c_str());
return 0;
}

DEBUG_LOG(COMMON, "GetSize: %s: %lld", filename.c_str(), (long long)file_info.st_size);
return file_info.st_size;
}

// Overloaded GetSize, accepts file descriptor
u64 GetSize(const int fd)
{
struct stat64 buf;
if (fstat64(fd, &buf) != 0) {
ERROR_LOG(COMMON, "GetSize: stat failed %i: %s",
fd, GetLastErrorMsg());
return 0;
}
return buf.st_size;
#endif
}

// Overloaded GetSize, accepts FILE*
u64 GetSize(FILE *f)
{
u64 GetFileSize(FILE *f) {
// can't use off_t here because it can be 32-bit
u64 pos = ftello(f);
if (fseeko(f, 0, SEEK_END) != 0) {
Expand Down Expand Up @@ -809,7 +801,7 @@ void IOFile::SetHandle(std::FILE* file)
u64 IOFile::GetSize()
{
if (IsOpen())
return File::GetSize(m_file);
return File::GetFileSize(m_file);
else
return 0;
}
Expand Down
19 changes: 14 additions & 5 deletions Common/FileUtil.h
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,15 @@ struct FSTEntry
std::vector<FSTEntry> children;
};

struct FileDetails {
bool isDirectory;
u64 size;
uint64_t atime;
uint64_t mtime;
uint64_t ctime;
uint32_t access; // st_mode & 0x1ff
};

// Mostly to handle utf-8 filenames better on Windows.
FILE *OpenCFile(const std::string &filename, const char *mode);
bool OpenCPPFile(std::fstream & stream, const std::string &filename, std::ios::openmode mode);
Expand All @@ -55,17 +64,17 @@ bool Exists(const std::string &filename);
// Returns true if filename is a directory
bool IsDirectory(const std::string &filename);

// Returns file attributes.
bool GetFileDetails(const std::string &filename, FileDetails *details);

// Returns struct with modification date of file
bool GetModifTime(const std::string &filename, tm &return_time);

// Returns the size of filename (64bit)
u64 GetSize(const std::string &filename);

// Overloaded GetSize, accepts file descriptor
u64 GetSize(const int fd);
u64 GetFileSize(const std::string &filename);

// Overloaded GetSize, accepts FILE*
u64 GetSize(FILE *f);
u64 GetFileSize(FILE *f);

// Returns true if successful, or path already exists.
bool CreateDir(const std::string &filename);
Expand Down
40 changes: 22 additions & 18 deletions Core/FileSystems/DirectoryFileSystem.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -660,7 +660,7 @@ PSPFileInfo DirectoryFileSystem::GetFileInfo(std::string filename) {
x.name = filename;

std::string fullName = GetLocalPath(filename);
if (! File::Exists(fullName)) {
if (!File::Exists(fullName)) {
#if HOST_IS_CASE_SENSITIVE
if (! FixPathCase(basePath,filename, FPC_FILE_MUST_EXIST))
return x;
Expand All @@ -675,21 +675,26 @@ PSPFileInfo DirectoryFileSystem::GetFileInfo(std::string filename) {
x.type = File::IsDirectory(fullName) ? FILETYPE_DIRECTORY : FILETYPE_NORMAL;
x.exists = true;

if (x.type != FILETYPE_DIRECTORY)
{
#ifdef _WIN32
struct _stat64i32 s;
_wstat64i32(ConvertUTF8ToWString(fullName).c_str(), &s);
#else
struct stat s;
stat(fullName.c_str(), &s);
#endif

x.size = File::GetSize(fullName);
x.access = s.st_mode & 0x1FF;
localtime_r((time_t*)&s.st_atime,&x.atime);
localtime_r((time_t*)&s.st_ctime,&x.ctime);
localtime_r((time_t*)&s.st_mtime,&x.mtime);
if (x.type != FILETYPE_DIRECTORY) {
File::FileDetails details;
if (!File::GetFileDetails(fullName, &details)) {
ERROR_LOG(FILESYS, "DirectoryFileSystem::GetFileInfo: GetFileDetails failed: %s", fullName.c_str());
x.size = 0;
x.access = 0;
memset(&x.atime, 0, sizeof(x.atime));
memset(&x.ctime, 0, sizeof(x.ctime));
memset(&x.mtime, 0, sizeof(x.mtime));
} else {
x.size = details.size;
x.access = details.access;
time_t atime = details.atime;
time_t ctime = details.ctime;
time_t mtime = details.mtime;

localtime_r((time_t*)&atime, &x.atime);
localtime_r((time_t*)&ctime, &x.ctime);
localtime_r((time_t*)&mtime, &x.mtime);
}
}

return x;
Expand All @@ -703,8 +708,7 @@ bool DirectoryFileSystem::GetHostPath(const std::string &inpath, std::string &ou
#ifdef _WIN32
#define FILETIME_FROM_UNIX_EPOCH_US 11644473600000000ULL

static void tmFromFiletime(tm &dest, FILETIME &src)
{
static void tmFromFiletime(tm &dest, FILETIME &src) {
u64 from_1601_us = (((u64) src.dwHighDateTime << 32ULL) + (u64) src.dwLowDateTime) / 10ULL;
u64 from_1970_us = from_1601_us - FILETIME_FROM_UNIX_EPOCH_US;

Expand Down
Loading

0 comments on commit 103b73d

Please sign in to comment.