5 #include <c++utilities/conversion/stringbuilder.h>
6 #include <c++utilities/conversion/stringconversion.h>
8 #ifdef PLATFORM_WINDOWS
32 namespace BackupHelper {
53 const std::string &originalPath,
const std::string &backupPath, NativeFileStream &originalStream, NativeFileStream &backupStream)
56 if (originalStream.is_open()) {
57 originalStream.close();
60 backupStream.exceptions(ios_base::goodbit);
63 backupStream.open(backupPath, ios_base::in | ios_base::binary);
64 if (backupStream.is_open()) {
67 throw std::ios_base::failure(
"Backup/temporary file has not been created.");
70 std::remove(originalPath.c_str());
71 if (std::rename(BasicFileInfo::pathForOpen(backupPath), BasicFileInfo::pathForOpen(originalPath)) == 0) {
77 backupStream.exceptions(ios_base::failbit | ios_base::badbit);
78 originalStream.exceptions(ios_base::failbit | ios_base::badbit);
79 backupStream.open(backupPath, ios_base::in | ios_base::binary);
80 originalStream.open(originalPath, ios_base::out | ios_base::binary);
81 originalStream << backupStream.rdbuf();
82 originalStream.flush();
84 }
catch (
const std::ios_base::failure &failure) {
85 throw std::ios_base::failure(
"Unable to restore original file from backup file \"" % backupPath %
"\" after failure: " + failure.what());
92 static bool isRelative(
const std::string &path)
94 return path.empty() || (path.front() !=
'/' && (path.size() < 2 || path[1] !=
':'));
122 void createBackupFile(
const std::string &backupDir,
const std::string &originalPath, std::string &backupPath, NativeFileStream &originalStream,
123 NativeFileStream &backupStream)
126 const auto backupDirRelative(isRelative(backupDir));
127 const auto originalDir(backupDirRelative ? BasicFileInfo::containingDirectory(originalPath) :
string());
130 for (
unsigned int i = 0;; ++i) {
131 if (backupDir.empty()) {
133 backupPath = originalPath %
'.' % i +
".bak";
135 backupPath = originalPath +
".bak";
138 const auto fileName(BasicFileInfo::fileName(originalPath, i));
140 const auto ext(BasicFileInfo::extension(originalPath));
141 if (backupDirRelative) {
142 backupPath = originalDir %
'/' % backupDir %
'/' % fileName %
'.' % i + ext;
144 backupPath = backupDir %
'/' % fileName %
'.' % i + ext;
147 if (backupDirRelative) {
148 backupPath = originalDir %
'/' % backupDir %
'/' + fileName;
150 backupPath = backupDir %
'/' + fileName;
156 #ifdef PLATFORM_WINDOWS
157 if (GetFileAttributes(BasicFileInfo::pathForOpen(backupPath)) == INVALID_FILE_ATTRIBUTES) {
159 struct stat backupStat;
160 if (stat(BasicFileInfo::pathForOpen(backupPath), &backupStat)) {
167 if (originalStream.is_open()) {
168 originalStream.close();
172 if (std::rename(BasicFileInfo::pathForOpen(originalPath), BasicFileInfo::pathForOpen(backupPath))) {
175 backupStream.exceptions(ios_base::failbit | ios_base::badbit);
176 originalStream.exceptions(ios_base::failbit | ios_base::badbit);
178 if (backupStream.is_open()) {
179 backupStream.close();
181 backupStream.open(BasicFileInfo::pathForOpen(backupPath), ios_base::out | ios_base::binary);
183 originalStream.open(BasicFileInfo::pathForOpen(originalPath), ios_base::in | ios_base::binary);
185 backupStream << originalStream.rdbuf();
186 backupStream.flush();
189 }
catch (
const std::ios_base::failure &failure) {
190 throw std::ios_base::failure(argsToString(
"Unable to rename original file before rewriting it: ", failure.what()));
197 if (originalStream.is_open()) {
198 originalStream.close();
201 if (backupStream.is_open()) {
202 backupStream.close();
205 backupStream.exceptions(ios_base::failbit | ios_base::badbit);
206 backupStream.open(BasicFileInfo::pathForOpen(backupPath), ios_base::in | ios_base::binary);
207 }
catch (
const std::ios_base::failure &failure) {
210 if (std::rename(BasicFileInfo::pathForOpen(backupPath), BasicFileInfo::pathForOpen(originalPath))) {
211 throw std::ios_base::failure(
"Unable to restore original file from backup file \"" % backupPath %
"\" after failure: " + failure.what());
213 throw std::ios_base::failure(argsToString(
"Unable to open backup file: ", failure.what()));
238 NativeFileStream &backupStream,
Diagnostics &diag,
const std::string &context)
249 if (!backupPath.empty()) {
251 diag.emplace_back(DiagLevel::Information,
"Rewriting the file to apply changed tag information has been aborted.", context);
254 diag.emplace_back(DiagLevel::Information,
"The original file has been restored.", context);
255 }
catch (
const std::ios_base::failure &failure) {
256 diag.emplace_back(DiagLevel::Critical, failure.what(), context);
259 diag.emplace_back(DiagLevel::Information,
"Applying new tag information has been aborted.", context);
264 if (!backupPath.empty()) {
266 diag.emplace_back(DiagLevel::Critical,
"Rewriting the file to apply changed tag information failed.", context);
269 diag.emplace_back(DiagLevel::Information,
"The original file has been restored.", context);
270 }
catch (
const std::ios_base::failure &failure) {
271 diag.emplace_back(DiagLevel::Critical, failure.what(), context);
274 diag.emplace_back(DiagLevel::Critical,
"Applying new tag information failed.", context);
278 }
catch (
const std::ios_base::failure &) {
279 if (!backupPath.empty()) {
281 diag.emplace_back(DiagLevel::Critical,
"An IO error occurred when rewriting the file to apply changed tag information.", context);
284 diag.emplace_back(DiagLevel::Information,
"The original file has been restored.", context);
285 }
catch (
const std::ios_base::failure &failure) {
286 diag.emplace_back(DiagLevel::Critical, failure.what(), context);
289 diag.emplace_back(DiagLevel::Critical,
"An IO error occurred when applying tag information.", context);