Clean backup helper code

This commit is contained in:
Martchus 2018-02-04 23:55:52 +01:00
parent 6a62ef7fc2
commit 25d164a5ae
1 changed files with 39 additions and 45 deletions

View File

@ -27,7 +27,7 @@ namespace Media {
* \brief Helps to create and restore backup files when rewriting * \brief Helps to create and restore backup files when rewriting
* files to apply changed tag information. * files to apply changed tag information.
* *
* Methods in this namespace are internally used eg. in implementations of AbstractContainer::internalMake(). * Methods in this namespace are internally used eg. in implementations of AbstractContainer::internalMakeFile().
*/ */
namespace BackupHelper { namespace BackupHelper {
@ -37,6 +37,8 @@ namespace BackupHelper {
* *
* Setting this value allows creation of backup files in a custom location * Setting this value allows creation of backup files in a custom location
* instead of the directory of the file being modified. * instead of the directory of the file being modified.
*
* \todo Add this as member variable to MediaFileInfo to avoid global.
*/ */
string &backupDirectory() string &backupDirectory()
{ {
@ -80,9 +82,9 @@ void restoreOriginalFileFromBackupFile(const std::string &originalPath, const st
} }
// remove original file and restore backup // remove original file and restore backup
std::remove(originalPath.c_str()); std::remove(originalPath.c_str());
if(std::rename(backupPath.c_str(), originalPath.c_str()) != 0) { // restore backup if(std::rename(backupPath.c_str(), originalPath.c_str())) {
// unable to move the file // can't rename/move the file (maybe backup dir on another partition) -> make a copy instead
try { // to copy try {
// need to open all streams again // need to open all streams again
backupStream.exceptions(ios_base::failbit | ios_base::badbit); backupStream.exceptions(ios_base::failbit | ios_base::badbit);
originalStream.exceptions(ios_base::failbit | ios_base::badbit); originalStream.exceptions(ios_base::failbit | ios_base::badbit);
@ -97,6 +99,14 @@ void restoreOriginalFileFromBackupFile(const std::string &originalPath, const st
} }
} }
/*!
* \brief Returns whether the specified \a path is relative.
*/
static bool isRelative(const std::string &path)
{
return path.empty() || (path.front() != '/' && (path.size() < 2 || path[1] != ':'));
}
/*! /*!
* \brief Creates a backup file for the specified file. * \brief Creates a backup file for the specified file.
* \param originalPath Specifies the path of the file to be backuped. * \param originalPath Specifies the path of the file to be backuped.
@ -122,11 +132,12 @@ void restoreOriginalFileFromBackupFile(const std::string &originalPath, const st
*/ */
void createBackupFile(const std::string &originalPath, std::string &backupPath, NativeFileStream &originalStream, NativeFileStream &backupStream) void createBackupFile(const std::string &originalPath, std::string &backupPath, NativeFileStream &originalStream, NativeFileStream &backupStream)
{ {
// determine dirs
const auto &backupDir(backupDirectory());
const auto backupDirRelative(isRelative(backupDir));
const auto originalDir(backupDirRelative ? BasicFileInfo::containingDirectory(originalPath) : string());
// determine the backup path // determine the backup path
const string &backupDir = backupDirectory();
#ifndef PLATFORM_WINDOWS
struct stat backupStat;
#endif
for(unsigned int i = 0; ; ++i) { for(unsigned int i = 0; ; ++i) {
if(backupDir.empty()) { if(backupDir.empty()) {
if(i) { if(i) {
@ -135,63 +146,44 @@ void createBackupFile(const std::string &originalPath, std::string &backupPath,
backupPath = originalPath + ".bak"; backupPath = originalPath + ".bak";
} }
} else { } else {
const string fileName = BasicFileInfo::fileName(originalPath, i); const auto fileName(BasicFileInfo::fileName(originalPath, i));
if(i) { if(i) {
const string ext = BasicFileInfo::extension(originalPath); const auto ext(BasicFileInfo::extension(originalPath));
if(backupDir.at(0) != '/' && (backupDir.size() < 2 || backupDir.at(1) != ':')) { if(backupDirRelative) {
// backupDir is a relative path backupPath = originalDir % '/' % backupDir % '/' % fileName % '.' % i + ext;
backupPath = BasicFileInfo::containingDirectory(originalPath);
backupPath += '/';
backupPath += backupDir;
backupPath += '/';
backupPath += fileName;
backupPath += '.';
backupPath += numberToString(i);
backupPath += ext;
} else { } else {
// backupDir is an absolute path backupPath = backupDir % '/' % fileName % '.' % i + ext;
backupPath = backupDir;
backupPath += '/';
backupPath += fileName;
backupPath += '.';
backupPath += numberToString(i);
backupPath += ext;
} }
} else { } else {
if(backupDir.at(0) != '/' && (backupDir.size() < 2 || backupDir.at(1) != ':')) { if(backupDirRelative) {
// backupDir is a relative path backupPath = originalDir % '/' % backupDir % '/' + fileName;
backupPath = BasicFileInfo::containingDirectory(originalPath);
backupPath += '/';
backupPath += backupDir;
backupPath += '/';
backupPath += fileName;
} else { } else {
// backupDir is an absolute path backupPath = backupDir % '/' + fileName;
backupPath = backupDir;
backupPath += '/';
backupPath += fileName;
} }
} }
} }
// test whether the backup file already exists
// test whether the backup path is still unused; otherwise continue loop
#ifdef PLATFORM_WINDOWS #ifdef PLATFORM_WINDOWS
if(GetFileAttributes(backupPath.c_str()) == INVALID_FILE_ATTRIBUTES) { if(GetFileAttributes(backupPath.c_str()) == INVALID_FILE_ATTRIBUTES) {
#else #else
struct stat backupStat;
if(stat(backupPath.c_str(), &backupStat)) { if(stat(backupPath.c_str(), &backupStat)) {
#endif #endif
break; break;
} // else: the backup file already exists -> find another file name }
} }
// ensure original file is closed // ensure original file is closed
if(originalStream.is_open()) { if(originalStream.is_open()) {
originalStream.close(); originalStream.close();
} }
// rename original file // rename original file
if(std::rename(originalPath.c_str(), backupPath.c_str()) != 0) { if(std::rename(originalPath.c_str(), backupPath.c_str())) {
// can't rename/move the file // can't rename/move the file (maybe backup dir on another partition) -> make a copy instead
try { // to copy try {
backupStream.exceptions(ios_base::failbit | ios_base::badbit); backupStream.exceptions(ios_base::failbit | ios_base::badbit);
originalStream.exceptions(ios_base::failbit | ios_base::badbit); originalStream.exceptions(ios_base::failbit | ios_base::badbit);
// ensure backupStream is opened as write-only // ensure backupStream is opened as write-only
@ -210,8 +202,10 @@ void createBackupFile(const std::string &originalPath, std::string &backupPath,
throwIoFailure("Unable to rename original file before rewriting it."); throwIoFailure("Unable to rename original file before rewriting it.");
} }
} }
// manage streams
try { try {
// ensure there is not file associated with the originalStream object // ensure there is no file associated with the originalStream object
if(originalStream.is_open()) { if(originalStream.is_open()) {
originalStream.close(); originalStream.close();
} }
@ -226,7 +220,7 @@ void createBackupFile(const std::string &originalPath, std::string &backupPath,
catchIoFailure(); catchIoFailure();
// can't open the new file // can't open the new file
// -> try to re-rename backup file in the error case to restore previous state // -> try to re-rename backup file in the error case to restore previous state
if(std::rename(backupPath.c_str(), originalPath.c_str()) != 0) { if(std::rename(backupPath.c_str(), originalPath.c_str())) {
throwIoFailure(("Unable to restore original file from backup file \"" % backupPath + "\" after failure.").data()); throwIoFailure(("Unable to restore original file from backup file \"" % backupPath + "\" after failure.").data());
} else { } else {
throwIoFailure("Unable to open backup file."); throwIoFailure("Unable to open backup file.");