AtomicFile: Add size() and remove() methods

Signed-off-by: Shaun Ruffell <sruffell@sruffell.net>
This commit is contained in:
Shaun Ruffell 2020-06-06 20:41:45 -05:00 committed by lauft
parent cea2e5d13f
commit 0a4644bfc7
3 changed files with 71 additions and 8 deletions

View file

@ -26,6 +26,8 @@
#include <csignal>
#include <stdio.h>
#include <string.h>
#include <errno.h>
#include <cassert>
#include <iostream>
#include <vector>
@ -59,7 +61,9 @@ struct AtomicFile::impl
bool open ();
void close ();
size_t size () const;
void truncate ();
void remove ();
void read (std::string& content);
void read (std::vector <std::string>& lines);
void append (const std::string& content);
@ -150,6 +154,18 @@ void AtomicFile::impl::close ()
}
}
////////////////////////////////////////////////////////////////////////////////
size_t AtomicFile::impl::size () const
{
struct stat s;
const char *filename = (is_temp_active) ? temp_file._data.c_str () : real_file._data.c_str ();
if (stat (filename, &s))
{
throw format ("stat error {1}: {2}", errno, strerror (errno));
}
return s.st_size;
}
////////////////////////////////////////////////////////////////////////////////
void AtomicFile::impl::truncate ()
{
@ -165,6 +181,21 @@ void AtomicFile::impl::truncate ()
}
}
////////////////////////////////////////////////////////////////////////////////
void AtomicFile::impl::remove ()
{
try
{
temp_file.remove ();
is_temp_active = true;
}
catch (...)
{
allow_atomics = false;
throw;
}
}
////////////////////////////////////////////////////////////////////////////////
void AtomicFile::impl::read (std::string& content)
{
@ -233,11 +264,19 @@ void AtomicFile::impl::finalize ()
{
if (is_temp_active && impl::allow_atomics)
{
debug (format ("Moving '{1}' -> '{2}'", temp_file._data, real_file._data));
if (std::rename (temp_file._data.c_str (), real_file._data.c_str ()))
if (temp_file.exists ())
{
throw format("Failed copying '{1}' to '{2}'. Database corruption possible.",
temp_file._data, real_file._data);
debug (format ("Moving '{1}' -> '{2}'", temp_file._data, real_file._data));
if (std::rename (temp_file._data.c_str (), real_file._data.c_str ()))
{
throw format("Failed copying '{1}' to '{2}'. Database corruption possible.",
temp_file._data, real_file._data);
}
}
else
{
debug (format ("Removing '{1}'", real_file._data));
std::remove (real_file._data.c_str ());
}
is_temp_active = false;
}
@ -322,12 +361,24 @@ void AtomicFile::close ()
pimpl->close ();
}
////////////////////////////////////////////////////////////////////////////////
size_t AtomicFile::size () const
{
return pimpl->size ();
}
////////////////////////////////////////////////////////////////////////////////
void AtomicFile::truncate ()
{
pimpl->truncate ();
}
////////////////////////////////////////////////////////////////////////////////
void AtomicFile::remove ()
{
pimpl->remove ();
}
////////////////////////////////////////////////////////////////////////////////
void AtomicFile::read (std::string& content)
{

View file

@ -48,7 +48,9 @@ public:
bool open ();
void close ();
void remove ();
void truncate ();
size_t size () const;
void read (std::string& content);
void read (std::vector <std::string>& lines);
void append (const std::string& content);

View file

@ -24,6 +24,7 @@
//
////////////////////////////////////////////////////////////////////////////////
#include <cassert>
#include <sstream>
#include <memory>
#include <string>
@ -451,18 +452,27 @@ int test (UnitTest& t)
t.fail (test_name);
t.diag (std::string ("Expected '" + expected + "' read '" + contents + "'"));
}
AtomicFile::reset ();
{
AtomicFile test ("test");
tempDir.clear ();
Path test("test");
AtomicFile file(test);
file.truncate ();
assert (! test.exists ());
AtomicFile::finalize_all ();
assert (test.exists ());
file.remove ();
t.is (test.exists (), true, "File not removed before finalize");
AtomicFile::finalize_all ();
t.is (test.exists (), false, "File is removed after finalize");
}
AtomicFile::reset ();
return 0;
}
int main (int, char**)
{
UnitTest t (20);
UnitTest t (22);
try
{
int ret = test (t);