Sleds/gluster_status/FileSystemTest.cpp

185 lines
5.3 KiB
C++

/*
Copyright (c) 2014 IOnU Security Inc. All rights reserved
Created February 2014 by Kendrick Webster
FileSystemTest.cpp - implementation for FileSystemTest.h
*/
#include <unistd.h>
#include <fstream>
#include "main.h"
#include "UdpIpc.h"
#include "Random.h"
#include "FileSystemTest.h"
using namespace ionu::random;
using namespace ionu::hash;
using namespace ionu::udp_ipc;
using namespace ionu::network;
namespace
{
constexpr size_t HASH_SIZE = 16; // 16 base64 chars, 96 bits
std::string error_logged;
std::string error_with_context;
}
static void setErrorContext(std::string where)
{
where += ": ";
where += error_logged;
error_with_context = where;
}
#define log_and_set_error(...) logprintf_save_string(error_logged, Log::error, __VA_ARGS__)
//-----------------------------------------------------------------------------------
// Local functions corresponding to module interface functions ...
// these run in a fork()'d process, then send results to the daemon via UdpIpc
//-----------------------------------------------------------------------------------
static void createTestFile(const std::string& name, size_t size)
{
logprintf(Log::debug2, "creating test file \"%s\", size(%lu)", name.c_str(), size);
std::string contents = GetRandomString(size);
FILE* f = fopen(name.c_str(), "wb");
if (NULL == f)
{
log_and_set_error("fopen: %s", strerror(errno));
failed:
setErrorContext("Creating file");
SendToSelf("create_file_result:FAIL:", error_with_context);
return;
}
size_t size_written = fwrite(contents.c_str(), 1, size, f);
if (size != size_written)
{
log_and_set_error("fwrite: %s", strerror(errno));
logprintf(Log::error, "fwrite returned %lu, size attempted = %lu", size_written, size);
failed__close_f:
if (0 != fclose(f))
{
log_and_set_error("fclose: %s", strerror(errno));
}
goto failed;
}
if (0 != fflush(f))
{
log_and_set_error("fflush: %s", strerror(errno));
goto failed__close_f;
}
if (0 != fsync(fileno(f)))
{
log_and_set_error("fsync: %s", strerror(errno));
goto failed__close_f;
}
if (0 != fclose(f))
{
log_and_set_error("fclose: %s", strerror(errno));
goto failed;
}
CHash h(contents);
std::string hash = h.GetString(HASH_SIZE);
logprintf(Log::debug3, "test file created, hash = \"%s\"", hash.c_str());
SendToSelf("create_file_result:PASS:", hash);
}
static void checkTestFile(const std::string& name, const std::string& hash, CAddress reply_to)
{
bool local = PORT_LOOPBACK == reply_to.port;
logprintf(Log::debug2, "checking test file \"%s\" (created %s) against hash \"%s\"",
name.c_str(), local ? "locally" : "remotely", hash.c_str());
std::ifstream in(name, std::ios::in | std::ios::binary);
if (!in)
{
log_and_set_error("ifstream ctor: %s", strerror(errno));
failed:
setErrorContext(local ? "Reading file (local)" : "Reading file (remote)");
SendTo(reply_to, "check_file_result:FAIL:", error_with_context);
return;
}
in.seekg(0, std::ios::end);
if (!in)
{
seekg_failed:
log_and_set_error("ifstream seekg: %s", strerror(errno));
goto failed;
}
std::string contents;
contents.resize(in.tellg());
in.seekg(0, std::ios::beg);
if (!in)
{
goto seekg_failed;
}
in.read(&contents[0], contents.size());
if (!in)
{
log_and_set_error("ifstream read: %s", strerror(errno));
goto failed;
}
in.close();
if (!in)
{
log_and_set_error("ifstream close: %s", strerror(errno));
goto failed;
}
CHash h(contents);
if (!h.IsString(hash))
{
log_and_set_error("hash mismatch: file contents read (%d bytes) differ from file created", contents.size());
goto failed;
}
logprintf(Log::debug3, "file hash verified");
SendTo(reply_to, "check_file_result:PASS");
}
static void deleteTestFile(const std::string& name)
{
logprintf(Log::debug2, "deleting test file \"%s\"", name.c_str());
if (0 == unlink(name.c_str()))
{
logprintf(Log::debug3, "file unlinked");
SendToSelf("delete_file_result:PASS");
}
else
{
log_and_set_error("unlink: %s", strerror(errno));
setErrorContext("Deleting file");
SendToSelf("delete_file_result:FAIL:", error_with_context);
}
}
//-----------------------------------------------------------------------------------
// Module interface functions ...
// these fork() a child to do the actual work
//-----------------------------------------------------------------------------------
#define FORK(f) \
pid_t pid = fork(); \
if (pid < 0) {perrorExit("fork");} \
if (pid > 0) {return;} \
f; \
SendToSelf("child_exit:", std::to_string(getpid())); \
exit(EXIT_SUCCESS)
void ionu::filesystem_test::CreateTestFile(const std::string& name, size_t size)
{
FORK(createTestFile(name, size));
}
void ionu::filesystem_test::CheckTestFile(const std::string& name, const std::string& hash, CAddress reply_to)
{
FORK(checkTestFile(name, hash, reply_to));
}
void ionu::filesystem_test::DeleteTestFile(const std::string& name)
{
FORK(deleteTestFile(name));
}