185 lines
5.3 KiB
C++
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));
|
|
}
|