summaryrefslogtreecommitdiff
path: root/zen/file_handling.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'zen/file_handling.cpp')
-rw-r--r--zen/file_handling.cpp128
1 files changed, 34 insertions, 94 deletions
diff --git a/zen/file_handling.cpp b/zen/file_handling.cpp
index 103a39f7..3f8d5bbd 100644
--- a/zen/file_handling.cpp
+++ b/zen/file_handling.cpp
@@ -45,74 +45,44 @@
using namespace zen;
-warn_static("remove after test")
-namespace
-{
-void writeSysErrorIfNeeded(std::wstring* sysErrorMsg, const wchar_t* functionName, ErrorCode lastError)
-{
- if (sysErrorMsg)
- {
- //skip uninteresting error codes:
-#ifdef ZEN_WIN
- if (lastError == ERROR_FILE_NOT_FOUND ||
- lastError == ERROR_PATH_NOT_FOUND) return;
- //lastError == ERROR_BAD_NETPATH || //e.g. for a path like: \\192.168.1.1\test
- //lastError == ERROR_NETNAME_DELETED;
-
-#elif defined ZEN_LINUX || defined ZEN_MAC
- if (lastError == ENOENT) return;
-#endif
- *sysErrorMsg = formatSystemError(functionName, lastError);
- }
-}
-}
-
-
-bool zen::fileExists(const Zstring& filename, std::wstring* sysErrorMsg)
+bool zen::fileExists(const Zstring& filename)
{
//symbolic links (broken or not) are also treated as existing files!
#ifdef ZEN_WIN
- const wchar_t functionName[] = L"GetFileAttributes";
const DWORD attr = ::GetFileAttributes(applyLongPathPrefix(filename).c_str());
if (attr != INVALID_FILE_ATTRIBUTES)
return (attr & FILE_ATTRIBUTE_DIRECTORY) == 0; //returns true for (file-)symlinks also
#elif defined ZEN_LINUX || defined ZEN_MAC
- const wchar_t functionName[] = L"lstat";
struct ::stat fileInfo = {};
- if (::lstat(filename.c_str(), &fileInfo) == 0)
- return S_ISREG(fileInfo.st_mode) || S_ISLNK(fileInfo.st_mode); //in Linux a symbolic link is neither file nor directory
+ if (::stat(filename.c_str(), &fileInfo) == 0) //follow symlinks!
+ return S_ISREG(fileInfo.st_mode);
#endif
- writeSysErrorIfNeeded(sysErrorMsg, functionName, getLastError());
return false;
}
-bool zen::dirExists(const Zstring& dirname, std::wstring* sysErrorMsg)
+bool zen::dirExists(const Zstring& dirname)
{
//symbolic links (broken or not) are also treated as existing directories!
#ifdef ZEN_WIN
- const wchar_t functionName[] = L"GetFileAttributes";
const DWORD attr = ::GetFileAttributes(applyLongPathPrefix(dirname).c_str());
if (attr != INVALID_FILE_ATTRIBUTES)
return (attr & FILE_ATTRIBUTE_DIRECTORY) != 0; //returns true for (dir-)symlinks also
#elif defined ZEN_LINUX || defined ZEN_MAC
- const wchar_t functionName[] = L"lstat";
struct ::stat dirInfo = {};
- if (::lstat(dirname.c_str(), &dirInfo) == 0)
- return S_ISDIR(dirInfo.st_mode) || S_ISLNK(dirInfo.st_mode); //in Linux a symbolic link is neither file nor directory
+ if (::stat(dirname.c_str(), &dirInfo) == 0) //follow symlinks!
+ return S_ISDIR(dirInfo.st_mode);
#endif
- writeSysErrorIfNeeded(sysErrorMsg, functionName, getLastError());
return false;
}
-bool zen::symlinkExists(const Zstring& linkname, std::wstring* sysErrorMsg)
+bool zen::symlinkExists(const Zstring& linkname)
{
#ifdef ZEN_WIN
- const wchar_t functionName[] = L"FindFirstFile";
WIN32_FIND_DATA linkInfo = {};
const HANDLE searchHandle = ::FindFirstFile(applyLongPathPrefix(linkname).c_str(), &linkInfo);
if (searchHandle != INVALID_HANDLE_VALUE)
@@ -122,20 +92,17 @@ bool zen::symlinkExists(const Zstring& linkname, std::wstring* sysErrorMsg)
}
#elif defined ZEN_LINUX || defined ZEN_MAC
- const wchar_t functionName[] = L"lstat";
struct ::stat linkInfo = {};
if (::lstat(linkname.c_str(), &linkInfo) == 0)
return S_ISLNK(linkInfo.st_mode);
#endif
- writeSysErrorIfNeeded(sysErrorMsg, functionName, getLastError());
return false;
}
-bool zen::somethingExists(const Zstring& objname, std::wstring* sysErrorMsg)
+bool zen::somethingExists(const Zstring& objname)
{
#ifdef ZEN_WIN
- const wchar_t functionName[] = L"GetFileAttributes";
const DWORD attr = ::GetFileAttributes(applyLongPathPrefix(objname).c_str());
if (attr != INVALID_FILE_ATTRIBUTES)
return true;
@@ -143,35 +110,14 @@ bool zen::somethingExists(const Zstring& objname, std::wstring* sysErrorMsg)
return true;
#elif defined ZEN_LINUX || defined ZEN_MAC
- const wchar_t functionName[] = L"lstat";
struct ::stat fileInfo = {};
if (::lstat(objname.c_str(), &fileInfo) == 0)
return true;
#endif
- writeSysErrorIfNeeded(sysErrorMsg, functionName, getLastError());
return false;
}
-SymLinkType zen::getSymlinkType(const Zstring& linkname) //throw()
-{
- assert(symlinkExists(linkname));
-#ifdef ZEN_WIN
- const DWORD attr = ::GetFileAttributes(applyLongPathPrefix(linkname).c_str());
- if (attr == INVALID_FILE_ATTRIBUTES)
- return SYMLINK_TYPE_UNKNOWN;
- return (attr & FILE_ATTRIBUTE_DIRECTORY) ? SYMLINK_TYPE_DIR : SYMLINK_TYPE_FILE;
-
-#elif defined ZEN_LINUX || defined ZEN_MAC
- //S_ISDIR and S_ISLNK are mutually exclusive on Linux => explicitly need to follow link
- struct ::stat fileInfo = {};
- if (::stat(linkname.c_str(), &fileInfo) != 0)
- return SYMLINK_TYPE_UNKNOWN;
- return S_ISDIR(fileInfo.st_mode) ? SYMLINK_TYPE_DIR : SYMLINK_TYPE_FILE;
-#endif
-}
-
-
namespace
{
#ifdef ZEN_WIN
@@ -531,7 +477,7 @@ public:
{
renameFile_sub(unrelatedFileParked, unrelatedFile); //throw FileError, ErrorDifferentVolume
}
- catch (...) {}
+ catch (FileError&) {}
}
private:
Zstring unrelatedFile;
@@ -580,17 +526,10 @@ public:
}
virtual HandleLink onSymlink(const Zchar* shortName, const Zstring& fullName, const SymlinkInfo& details)
{
- switch (getSymlinkType(fullName))
- {
- case SYMLINK_TYPE_DIR:
- dirs_.push_back(shortName);
- break;
-
- case SYMLINK_TYPE_FILE:
- case SYMLINK_TYPE_UNKNOWN:
- files_.push_back(shortName);
- break;
- }
+ if (dirExists(fullName)) //dir symlink
+ dirs_.push_back(shortName);
+ else //file symlink, broken symlink
+ files_.push_back(shortName);
return LINK_SKIP;
}
virtual TraverseCallback* onDir(const Zchar* shortName, const Zstring& fullName)
@@ -918,7 +857,7 @@ void zen::setFileTime(const Zstring& filename, const Int64& modTime, ProcSymlink
}
}
#ifndef NDEBUG //dst hack: verify data written
- if (dst::isFatDrive(filename) && !dirExists(filename)) //throw()
+ if (dst::isFatDrive(filename) && fileExists(filename)) //throw()
{
FILETIME creationTimeDbg = {};
FILETIME lastWriteTimeDbg = {};
@@ -1282,13 +1221,13 @@ void zen::makeDirectory(const Zstring& directory, bool failIfExists) //throw Fil
}
catch (const FileError&)
{
- if (dirExists(directory)) //a file system race-condition!
+ /*
+ could there be situations where a directory/network path exists,
+ but creation fails with error different than "ErrorTargetExisting"??
+ - creation of C:\ fails with ERROR_ACCESS_DENIED rather than ERROR_ALREADY_EXISTS
+ */
+ if (somethingExists(directory)) //a file system race-condition! don't use dirExists() => harmonize with ErrorTargetExisting!
{
- /*
- could there be situations where a directory/network path exists,
- but creation fails with error different than "ErrorTargetExisting"??
- - creation of C:\ fails with ERROR_ACCESS_DENIED rather than ERROR_ALREADY_EXISTS
- */
assert(false);
if (failIfExists)
throw; //do NOT convert to ErrorTargetExisting: if "failIfExists", not getting a ErrorTargetExisting *atomically* is unexpected!
@@ -1313,7 +1252,7 @@ void zen::makeDirectoryPlain(const Zstring& directory, //throw FileError, ErrorT
{
dirTmp += FILE_NAME_SEPARATOR; //we do not support "C:" to represent a relative path!
- const ErrorCode lastError = dirExists(dirTmp) ? ERROR_ALREADY_EXISTS : ERROR_PATH_NOT_FOUND;
+ const ErrorCode lastError = somethingExists(dirTmp) ? ERROR_ALREADY_EXISTS : ERROR_PATH_NOT_FOUND; //don't use dirExists() => harmonize with ErrorTargetExisting!
const std::wstring errorMsg = replaceCpy(_("Cannot create directory %x."), L"%x", fmtFileName(dirTmp));
const std::wstring errorDescr = formatSystemError(L"CreateDirectory", lastError);
@@ -1429,7 +1368,7 @@ void zen::makeDirectoryPlain(const Zstring& directory, //throw FileError, ErrorT
}
#endif
- zen::ScopeGuard guardNewDir = zen::makeGuard([&] { try { removeDirectory(directory); } catch (...) {} }); //ensure cleanup:
+ zen::ScopeGuard guardNewDir = zen::makeGuard([&] { try { removeDirectory(directory); } catch (FileError&) {} }); //ensure cleanup:
//enforce copying file permissions: it's advertized on GUI...
if (copyFilePermissions)
@@ -1475,12 +1414,12 @@ void zen::copySymlink(const Zstring& sourceLink, const Zstring& targetLink, bool
{
#ifdef ZEN_WIN
if (isDirLink)
- removeDirectory(targetLink);
+ removeDirectory(targetLink); //throw FileError
else
#endif
- removeFile(targetLink);
+ removeFile(targetLink); //throw FileError
}
- catch (...) {}
+ catch (FileError&) {}
});
//file times: essential for sync'ing a symlink: enforce this! (don't just try!)
@@ -1676,7 +1615,7 @@ void copyFileWindowsSparse(const Zstring& sourceFile,
throw FileError(errorMsg, errorDescr);
}
- ScopeGuard guardTarget = makeGuard([&] { try { removeFile(targetFile); } catch (...) {} }); //transactional behavior: guard just after opening target and before managing hFileOut
+ ScopeGuard guardTarget = makeGuard([&] { try { removeFile(targetFile); } catch (FileError&) {} }); //transactional behavior: guard just after opening target and before managing hFileOut
ZEN_ON_SCOPE_EXIT(::CloseHandle(hFileTarget));
//----------------------------------------------------------------------
@@ -2018,7 +1957,7 @@ void copyFileWindowsDefault(const Zstring& sourceFile,
try { activatePrivilege(SE_RESTORE_NAME); }
catch (const FileError&) {}
- zen::ScopeGuard guardTarget = zen::makeGuard([&] { try { removeFile(targetFile); } catch (...) {} });
+ zen::ScopeGuard guardTarget = zen::makeGuard([&] { try { removeFile(targetFile); } catch (FileError&) {} });
//transactional behavior: guard just before starting copy, we don't trust ::CopyFileEx(), do we? ;)
DWORD copyFlags = COPY_FILE_FAIL_IF_EXISTS;
@@ -2091,7 +2030,7 @@ void copyFileWindowsDefault(const Zstring& sourceFile,
//note: ERROR_INVALID_PARAMETER can also occur when copying to a SharePoint server or MS SkyDrive and the target filename is of a restricted type.
}
- catch (...) {}
+ catch (FileError&) {}
throw FileError(errorMsg, errorDescr);
}
@@ -2165,7 +2104,7 @@ void copyFileLinuxMac(const Zstring& sourceFile,
if (::fstat(fileIn.getDescriptor(), &sourceInfo) != 0) //read file attributes from source
throw FileError(replaceCpy(_("Cannot read file attributes of %x."), L"%x", fmtFileName(sourceFile)), formatSystemError(L"fstat", getLastError()));
- zen::ScopeGuard guardTarget = zen::makeGuard([&] { try { removeFile(targetFile); } catch (...) {} }); //transactional behavior: place guard before lifetime of FileOutput
+ zen::ScopeGuard guardTarget = zen::makeGuard([&] { try { removeFile(targetFile); } catch (FileError&) {} }); //transactional behavior: place guard before lifetime of FileOutput
try
{
//create targetFile and open it for writing
@@ -2269,7 +2208,6 @@ void zen::copyFile(const Zstring& sourceFile, //throw FileError, ErrorTargetPath
if (transactionalCopy)
{
Zstring temporary = targetFile + zen::TEMP_FILE_ENDING; //use temporary file until a correct date has been set
- zen::ScopeGuard guardTempFile = zen::makeGuard([&] { try { removeFile(temporary); } catch (...) {} }); //transactional behavior: ensure cleanup (e.g. network drop) -> ref to temporary[!]
//raw file copy
try
@@ -2286,9 +2224,12 @@ void zen::copyFile(const Zstring& sourceFile, //throw FileError, ErrorTargetPath
copyFileSelectOs(sourceFile, temporary, callback, sourceAttr); //throw FileError
}
+ //transactional behavior: ensure cleanup; not needed before copyFileSelectOs() which is already transactional
+ zen::ScopeGuard guardTempFile = zen::makeGuard([&] { try { removeFile(temporary); } catch (FileError&) {} });
+
//have target file deleted (after read access on source and target has been confirmed) => allow for almost transactional overwrite
if (callback)
- callback->deleteTargetFile(targetFile);
+ callback->deleteTargetFile(targetFile); //throw X
//rename temporary file:
//perf: this call is REALLY expensive on unbuffered volumes! ~40% performance decrease on FAT USB stick!
@@ -2319,7 +2260,6 @@ void zen::copyFile(const Zstring& sourceFile, //throw FileError, ErrorTargetPath
}
else
{
- //have target file deleted
if (callback) callback->deleteTargetFile(targetFile);
copyFileSelectOs(sourceFile, targetFile, callback, sourceAttr); //throw FileError: ErrorTargetPathMissing, ErrorTargetExisting, ErrorFileLocked
@@ -2335,7 +2275,7 @@ void zen::copyFile(const Zstring& sourceFile, //throw FileError, ErrorTargetPath
//file permissions
if (copyFilePermissions)
{
- zen::ScopeGuard guardTargetFile = zen::makeGuard([&] { try { removeFile(targetFile); } catch (...) {}});
+ zen::ScopeGuard guardTargetFile = zen::makeGuard([&] { try { removeFile(targetFile); } catch (FileError&) {}});
copyObjectPermissions(sourceFile, targetFile, SYMLINK_FOLLOW); //throw FileError
bgstack15