-
Notifications
You must be signed in to change notification settings - Fork 82
/
UndoableUnlink.cc
89 lines (78 loc) · 2.28 KB
/
UndoableUnlink.cc
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
/*
copyright 2018 Paul Dreik
Distributed under GPL v 2.0 or later, at your option.
See LICENSE for further details.
*/
#include "config.h"
// std
#include <iostream>
#include <stdexcept>
// os
#include <unistd.h> //for unlink etc.
// project
#include "EasyRandom.hh"
#include "UndoableUnlink.hh"
UndoableUnlink::UndoableUnlink(const std::string& filename)
: m_filename(filename)
{
// make a random filename, to avoid getting ENAMETOOLONG
// we try to replace the existing filename, instead of appending to it.
// this will fail if the directory name is really long, but there is not
// much to do about that without going into parsing mount points etc.
const auto last_sep = m_filename.find_last_of('/');
if (last_sep == std::string::npos) {
// bare filename - replace it.
m_tempfilename = EasyRandom().makeRandomFileString();
} else {
// found. keep the directory, switch out the filename.
m_tempfilename =
m_filename.substr(0, last_sep + 1) + EasyRandom().makeRandomFileString();
}
// move the file to a temporary name
if (0 != rename(m_filename.c_str(), m_tempfilename.c_str())) {
// failed rename.
std::cerr << "Failed moving " + m_filename + " to a temporary file\n";
m_state = state::FAILED_MOVE_TO_TEMPORARY;
} else {
m_state = state::MOVED_TO_TEMPORARY;
}
}
int
UndoableUnlink::undo()
{
if (m_state != state::MOVED_TO_TEMPORARY) {
throw std::runtime_error(
"api misuse - calling undo() now is a programming error");
}
if (0 != rename(m_tempfilename.c_str(), m_filename.c_str())) {
// failed rename.
m_state = state::FAILED_UNDO;
std::cerr << "Failed moving file from temporary back to " + m_filename +
'\n';
return 1;
}
m_state = state::UNDONE;
return 0;
}
int
UndoableUnlink::unlink()
{
if (m_state != state::MOVED_TO_TEMPORARY) {
throw std::runtime_error(
"api misuse - calling unlink() now is a programming error");
}
if (0 != ::unlink(m_tempfilename.c_str())) {
std::cerr << "Failed unlinking temporary file made from " + m_filename +
'\n';
m_state = state::FAILED_UNLINK;
return 1;
}
m_state = state::UNLINKED;
return 0;
}
UndoableUnlink::~UndoableUnlink()
{
if (m_state == state::MOVED_TO_TEMPORARY) {
undo();
}
}