2016-05-31 19:20:38 +00:00
|
|
|
/*
|
|
|
|
* Copyright (c) 2014-present, Facebook, Inc.
|
|
|
|
* All rights reserved.
|
|
|
|
*
|
|
|
|
* This source code is licensed under the BSD-style license found in the
|
|
|
|
* LICENSE file in the root directory of this source tree. An additional grant
|
|
|
|
* of patent rights can be found in the PATENTS file in the same directory.
|
|
|
|
*
|
|
|
|
*/
|
|
|
|
|
|
|
|
#include <gtest/gtest.h>
|
|
|
|
|
|
|
|
#include <boost/filesystem.hpp>
|
|
|
|
|
|
|
|
#include <osquery/core.h>
|
|
|
|
|
|
|
|
#include "osquery/filesystem/fileops.h"
|
2016-09-12 16:46:52 +00:00
|
|
|
#include "osquery/tests/test_util.h"
|
2016-05-31 19:20:38 +00:00
|
|
|
|
|
|
|
namespace fs = boost::filesystem;
|
|
|
|
|
|
|
|
namespace osquery {
|
|
|
|
|
|
|
|
class FileOpsTests : public testing::Test {
|
|
|
|
protected:
|
2016-09-12 16:46:52 +00:00
|
|
|
void SetUp() override {
|
|
|
|
createMockFileStructure();
|
|
|
|
}
|
2016-05-31 19:20:38 +00:00
|
|
|
|
2016-09-12 16:46:52 +00:00
|
|
|
void TearDown() override {
|
|
|
|
tearDownMockFileStructure();
|
|
|
|
}
|
2016-05-31 19:20:38 +00:00
|
|
|
|
|
|
|
bool globResultsMatch(const std::vector<std::string>& results,
|
|
|
|
std::vector<fs::path>& expected) {
|
|
|
|
if (results.size() == expected.size()) {
|
|
|
|
size_t i = 0;
|
|
|
|
for (auto const& path : results) {
|
|
|
|
if (path != expected[i].make_preferred().string()) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
i++;
|
|
|
|
}
|
|
|
|
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
class TempFile {
|
|
|
|
public:
|
|
|
|
TempFile()
|
|
|
|
: path_((fs::temp_directory_path() / fs::unique_path())
|
|
|
|
.make_preferred()
|
|
|
|
.string()) {}
|
2016-06-02 22:46:55 +00:00
|
|
|
|
2016-05-31 19:20:38 +00:00
|
|
|
~TempFile() {
|
|
|
|
if (fs::exists(path_)) {
|
|
|
|
fs::remove(path_);
|
|
|
|
}
|
|
|
|
}
|
2016-06-02 22:46:55 +00:00
|
|
|
|
2016-09-12 16:46:52 +00:00
|
|
|
const std::string& path() const {
|
|
|
|
return path_;
|
|
|
|
}
|
2016-06-02 22:46:55 +00:00
|
|
|
|
2016-05-31 19:20:38 +00:00
|
|
|
private:
|
|
|
|
std::string path_;
|
|
|
|
};
|
|
|
|
|
|
|
|
TEST_F(FileOpsTests, test_openFile) {
|
|
|
|
TempFile tmp_file;
|
|
|
|
std::string path = tmp_file.path();
|
|
|
|
|
|
|
|
{
|
|
|
|
PlatformFile fd(path, PF_OPEN_EXISTING | PF_READ);
|
|
|
|
EXPECT_FALSE(fd.isValid());
|
|
|
|
}
|
2016-06-02 22:46:55 +00:00
|
|
|
|
2016-05-31 19:20:38 +00:00
|
|
|
{
|
|
|
|
PlatformFile fd(path, PF_CREATE_NEW | PF_WRITE);
|
|
|
|
EXPECT_TRUE(fd.isValid());
|
|
|
|
}
|
|
|
|
|
|
|
|
{
|
|
|
|
PlatformFile fd(path, PF_CREATE_NEW | PF_READ);
|
|
|
|
EXPECT_FALSE(fd.isValid());
|
|
|
|
}
|
|
|
|
|
|
|
|
fs::remove(path);
|
|
|
|
|
|
|
|
{
|
|
|
|
PlatformFile fd(path, PF_CREATE_ALWAYS | PF_READ);
|
|
|
|
EXPECT_TRUE(fd.isValid());
|
|
|
|
}
|
|
|
|
|
|
|
|
{
|
|
|
|
PlatformFile fd(path, PF_CREATE_ALWAYS | PF_READ);
|
|
|
|
EXPECT_TRUE(fd.isValid());
|
|
|
|
}
|
|
|
|
|
|
|
|
{
|
|
|
|
PlatformFile fd(path, PF_OPEN_EXISTING | PF_READ);
|
|
|
|
EXPECT_TRUE(fd.isValid());
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
TEST_F(FileOpsTests, test_fileIo) {
|
|
|
|
TempFile tmp_file;
|
|
|
|
std::string path = tmp_file.path();
|
2016-06-02 22:46:55 +00:00
|
|
|
|
|
|
|
const char* expected_read = "AAAABBBBCCCCDDDD";
|
|
|
|
const ssize_t expected_read_len = ::strlen(expected_read);
|
|
|
|
const ssize_t expected_write_len = ::strlen(expected_read);
|
|
|
|
const size_t expected_buf_size = ::strlen(expected_read);
|
2016-05-31 19:20:38 +00:00
|
|
|
|
|
|
|
{
|
|
|
|
PlatformFile fd(path, PF_CREATE_NEW | PF_WRITE);
|
|
|
|
EXPECT_TRUE(fd.isValid());
|
2016-06-02 22:46:55 +00:00
|
|
|
EXPECT_EQ(expected_write_len, fd.write(expected_read, expected_read_len));
|
2016-05-31 19:20:38 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
{
|
|
|
|
std::vector<char> buf(expected_read_len);
|
|
|
|
PlatformFile fd(path, PF_OPEN_EXISTING | PF_READ);
|
|
|
|
EXPECT_TRUE(fd.isValid());
|
2016-06-07 21:08:50 +00:00
|
|
|
EXPECT_FALSE(fd.isSpecialFile());
|
2016-06-30 19:32:26 +00:00
|
|
|
EXPECT_EQ(expected_read_len, fd.read(buf.data(), expected_read_len));
|
2016-06-02 22:46:55 +00:00
|
|
|
EXPECT_EQ(expected_buf_size, buf.size());
|
|
|
|
for (ssize_t i = 0; i < expected_read_len; i++) {
|
2016-05-31 19:20:38 +00:00
|
|
|
EXPECT_EQ(expected_read[i], buf[i]);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
TEST_F(FileOpsTests, test_asyncIo) {
|
|
|
|
TempFile tmp_file;
|
|
|
|
std::string path = tmp_file.path();
|
|
|
|
|
2016-06-02 22:46:55 +00:00
|
|
|
const char* expected = "AAAABBBBCCCCDDDDEEEEFFFFGGGG";
|
|
|
|
const ssize_t expected_len = ::strlen(expected);
|
2016-05-31 19:20:38 +00:00
|
|
|
|
|
|
|
{
|
|
|
|
PlatformFile fd(path, PF_CREATE_NEW | PF_WRITE | PF_NONBLOCK);
|
|
|
|
EXPECT_TRUE(fd.isValid());
|
|
|
|
EXPECT_EQ(expected_len, fd.write(expected, expected_len));
|
|
|
|
}
|
|
|
|
|
|
|
|
{
|
|
|
|
PlatformFile fd(path, PF_OPEN_EXISTING | PF_READ | PF_NONBLOCK);
|
|
|
|
EXPECT_TRUE(fd.isValid());
|
2016-06-07 21:08:50 +00:00
|
|
|
EXPECT_FALSE(fd.isSpecialFile());
|
2016-05-31 19:20:38 +00:00
|
|
|
|
|
|
|
std::vector<char> buf(expected_len);
|
2016-06-30 19:32:26 +00:00
|
|
|
EXPECT_EQ(expected_len, fd.read(buf.data(), expected_len));
|
|
|
|
EXPECT_EQ(0, ::memcmp(expected, buf.data(), expected_len));
|
2016-05-31 19:20:38 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
{
|
|
|
|
PlatformFile fd(path, PF_OPEN_EXISTING | PF_READ | PF_NONBLOCK);
|
|
|
|
EXPECT_TRUE(fd.isValid());
|
2016-06-07 21:08:50 +00:00
|
|
|
EXPECT_FALSE(fd.isSpecialFile());
|
2016-05-31 19:20:38 +00:00
|
|
|
|
|
|
|
std::vector<char> buf(expected_len);
|
2016-06-30 19:32:26 +00:00
|
|
|
char* ptr = buf.data();
|
2016-05-31 19:20:38 +00:00
|
|
|
ssize_t part_bytes = 0;
|
2016-06-02 22:46:55 +00:00
|
|
|
int iterations = 0;
|
2016-05-31 19:20:38 +00:00
|
|
|
do {
|
|
|
|
part_bytes = fd.read(ptr, 4);
|
|
|
|
if (part_bytes > 0) {
|
|
|
|
ptr += part_bytes;
|
|
|
|
iterations++;
|
|
|
|
}
|
|
|
|
} while (part_bytes > 0);
|
|
|
|
|
|
|
|
EXPECT_EQ(7, iterations);
|
2016-06-30 19:32:26 +00:00
|
|
|
EXPECT_EQ(0, ::memcmp(expected, buf.data(), expected_len));
|
2016-05-31 19:20:38 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
TEST_F(FileOpsTests, test_seekFile) {
|
|
|
|
TempFile tmp_file;
|
|
|
|
std::string path = tmp_file.path();
|
|
|
|
|
2016-06-02 22:46:55 +00:00
|
|
|
const char* expected = "AABBBBAACCCAAAAADDDDAAAAAAAA";
|
|
|
|
const ssize_t expected_len = ::strlen(expected);
|
2016-09-12 16:46:52 +00:00
|
|
|
ssize_t expected_offs;
|
2016-05-31 19:20:38 +00:00
|
|
|
|
|
|
|
{
|
|
|
|
PlatformFile fd(path, PF_CREATE_ALWAYS | PF_WRITE);
|
|
|
|
EXPECT_TRUE(fd.isValid());
|
2016-06-02 22:46:55 +00:00
|
|
|
EXPECT_EQ(expected_len,
|
|
|
|
fd.write("AAAAAAAAAAAAAAAAAAAAAAAAAAAA", expected_len));
|
2016-05-31 19:20:38 +00:00
|
|
|
}
|
|
|
|
|
2016-06-02 22:46:55 +00:00
|
|
|
// Cast to the proper type, off_t
|
|
|
|
expected_offs = expected_len - 12;
|
|
|
|
|
2016-05-31 19:20:38 +00:00
|
|
|
{
|
|
|
|
PlatformFile fd(path, PF_OPEN_EXISTING | PF_WRITE);
|
|
|
|
EXPECT_TRUE(fd.isValid());
|
2016-06-02 22:46:55 +00:00
|
|
|
|
|
|
|
EXPECT_EQ(expected_offs, fd.seek(-12, PF_SEEK_END));
|
2016-05-31 19:20:38 +00:00
|
|
|
EXPECT_EQ(4, fd.write("DDDD", 4));
|
|
|
|
|
|
|
|
EXPECT_EQ(2, fd.seek(2, PF_SEEK_BEGIN));
|
|
|
|
EXPECT_EQ(4, fd.write("BBBB", 4));
|
|
|
|
|
|
|
|
EXPECT_EQ(8, fd.seek(2, PF_SEEK_CURRENT));
|
|
|
|
EXPECT_EQ(3, fd.write("CCC", 3));
|
|
|
|
}
|
|
|
|
|
|
|
|
{
|
|
|
|
std::vector<char> buffer(expected_len);
|
|
|
|
|
|
|
|
PlatformFile fd(path, PF_OPEN_EXISTING | PF_READ);
|
|
|
|
EXPECT_TRUE(fd.isValid());
|
|
|
|
|
2016-06-30 19:32:26 +00:00
|
|
|
EXPECT_EQ(expected_len, fd.read(buffer.data(), expected_len));
|
|
|
|
EXPECT_EQ(0, ::memcmp(buffer.data(), expected, expected_len));
|
2016-05-31 19:20:38 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
TEST_F(FileOpsTests, test_glob) {
|
|
|
|
{
|
2016-09-12 16:46:52 +00:00
|
|
|
std::vector<fs::path> expected{kFakeDirectory + "/door.txt",
|
|
|
|
kFakeDirectory + "/root.txt",
|
|
|
|
kFakeDirectory + "/root2.txt",
|
|
|
|
kFakeDirectory + "/roto.txt"};
|
2016-05-31 19:20:38 +00:00
|
|
|
auto result = platformGlob(kFakeDirectory + "/*.txt");
|
|
|
|
EXPECT_TRUE(globResultsMatch(result, expected));
|
|
|
|
}
|
|
|
|
|
|
|
|
{
|
2016-09-12 16:46:52 +00:00
|
|
|
std::vector<fs::path> expected{kFakeDirectory + "/deep1/",
|
|
|
|
kFakeDirectory + "/deep11/",
|
|
|
|
kFakeDirectory + "/door.txt",
|
|
|
|
kFakeDirectory + "/root.txt",
|
|
|
|
kFakeDirectory + "/root2.txt",
|
2016-10-14 17:23:37 +00:00
|
|
|
kFakeDirectory + "/roto.txt",
|
|
|
|
kFakeDirectory + "/toplevel/"};
|
2016-05-31 19:20:38 +00:00
|
|
|
auto result = platformGlob(kFakeDirectory + "/*");
|
|
|
|
EXPECT_TRUE(globResultsMatch(result, expected));
|
|
|
|
}
|
|
|
|
|
|
|
|
{
|
2016-06-02 22:46:55 +00:00
|
|
|
std::vector<fs::path> expected{kFakeDirectory + "/deep1/deep2/",
|
|
|
|
kFakeDirectory + "/deep1/level1.txt",
|
|
|
|
kFakeDirectory + "/deep11/deep2/",
|
|
|
|
kFakeDirectory + "/deep11/level1.txt",
|
2016-10-14 17:23:37 +00:00
|
|
|
kFakeDirectory + "/deep11/not_bash",
|
|
|
|
kFakeDirectory + "/toplevel/secondlevel1/",
|
|
|
|
kFakeDirectory + "/toplevel/secondlevel2/",
|
|
|
|
kFakeDirectory + "/toplevel/secondlevel3/"};
|
2016-05-31 19:20:38 +00:00
|
|
|
auto result = platformGlob(kFakeDirectory + "/*/*");
|
|
|
|
EXPECT_TRUE(globResultsMatch(result, expected));
|
|
|
|
}
|
|
|
|
|
|
|
|
{
|
2016-10-14 17:23:37 +00:00
|
|
|
std::vector<fs::path> expected{
|
|
|
|
kFakeDirectory + "/deep1/deep2/level2.txt",
|
|
|
|
kFakeDirectory + "/deep11/deep2/deep3/",
|
|
|
|
kFakeDirectory + "/deep11/deep2/level2.txt",
|
|
|
|
kFakeDirectory + "/toplevel/secondlevel3/thirdlevel1/",
|
|
|
|
};
|
2016-05-31 19:20:38 +00:00
|
|
|
auto result = platformGlob(kFakeDirectory + "/*/*/*");
|
|
|
|
EXPECT_TRUE(globResultsMatch(result, expected));
|
|
|
|
}
|
|
|
|
|
|
|
|
{
|
2016-06-02 22:46:55 +00:00
|
|
|
std::vector<fs::path> expected{kFakeDirectory + "/deep11/deep2/deep3/",
|
|
|
|
kFakeDirectory + "/deep11/deep2/level2.txt"};
|
2016-05-31 19:20:38 +00:00
|
|
|
auto result = platformGlob(kFakeDirectory + "/*11/*/*");
|
|
|
|
EXPECT_TRUE(globResultsMatch(result, expected));
|
|
|
|
}
|
|
|
|
|
|
|
|
{
|
2016-06-02 22:46:55 +00:00
|
|
|
std::vector<fs::path> expected{kFakeDirectory + "/deep1/",
|
|
|
|
kFakeDirectory + "/root.txt"};
|
2016-05-31 19:20:38 +00:00
|
|
|
auto result = platformGlob(kFakeDirectory + "/{deep,root}{1,.txt}");
|
|
|
|
EXPECT_TRUE(globResultsMatch(result, expected));
|
|
|
|
}
|
|
|
|
|
|
|
|
{
|
2016-06-02 22:46:55 +00:00
|
|
|
std::vector<fs::path> expected{kFakeDirectory + "/deep1/deep2/level2.txt",
|
|
|
|
kFakeDirectory + "/deep11/deep2/deep3/",
|
|
|
|
kFakeDirectory + "/deep11/deep2/level2.txt"};
|
2016-05-31 19:20:38 +00:00
|
|
|
auto result = platformGlob(kFakeDirectory + "/*/deep2/*");
|
|
|
|
EXPECT_TRUE(globResultsMatch(result, expected));
|
|
|
|
}
|
|
|
|
|
|
|
|
{
|
2016-10-14 17:23:37 +00:00
|
|
|
std::vector<fs::path> expected;
|
|
|
|
if (isPlatform(PlatformType::TYPE_WINDOWS)) {
|
|
|
|
expected = {kFakeDirectory + "/deep1/deep2/",
|
|
|
|
kFakeDirectory + "/deep1/level1.txt",
|
|
|
|
kFakeDirectory + "/deep11/deep2/",
|
|
|
|
kFakeDirectory + "/deep11/level1.txt",
|
|
|
|
kFakeDirectory + "/deep11/not_bash"};
|
|
|
|
} else {
|
|
|
|
expected = {kFakeDirectory + "/deep1/deep2/",
|
|
|
|
kFakeDirectory + "/deep11/deep2/",
|
|
|
|
kFakeDirectory + "/deep1/level1.txt",
|
|
|
|
kFakeDirectory + "/deep11/level1.txt",
|
|
|
|
kFakeDirectory + "/deep11/not_bash"};
|
|
|
|
}
|
|
|
|
|
2016-06-02 22:46:55 +00:00
|
|
|
auto result =
|
|
|
|
platformGlob(kFakeDirectory + "/*/{deep2,level1,not_bash}{,.txt}");
|
2016-05-31 19:20:38 +00:00
|
|
|
EXPECT_TRUE(globResultsMatch(result, expected));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|