/* vim:set ts=2 sw=2 sts=2 et: */
/**
* \author Marcus Holland-Moritz (github@mhxnet.de)
* \copyright Copyright (c) Marcus Holland-Moritz
*
* This file is part of dwarfs.
*
* dwarfs is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* dwarfs is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with dwarfs. If not, see .
*/
#include
#include
#include
#include
#include
#include "dwarfs/filesystem_v2.h"
#include "dwarfs/fstypes.h"
#include "dwarfs/mmap.h"
#include "mmap_mock.h"
#include "test_helpers.h"
#include "test_logger.h"
using namespace dwarfs;
namespace fs = std::filesystem;
namespace {
auto test_dir = fs::path(TEST_DATA_DIR).make_preferred();
} // namespace
TEST(filesystem, metadata_symlink_win) {
test::test_logger lgr;
test::os_access_mock os;
auto mm = std::make_shared(test_dir / "winlink.dwarfs");
filesystem_v2 fs(lgr, os, mm);
auto i1 = fs.find("link.txt");
auto i2 = fs.find("dir/link.txt");
auto i3 = fs.find("subdir/test.txt");
ASSERT_TRUE(i1);
ASSERT_TRUE(i2);
ASSERT_TRUE(i3);
EXPECT_TRUE(i1->is_symlink());
EXPECT_TRUE(i2->is_symlink());
EXPECT_TRUE(i3->is_regular_file());
// readlink_mode::preferred (default)
{
std::string buf1, buf2;
EXPECT_EQ(0, fs.readlink(*i1, &buf1));
EXPECT_EQ(0, fs.readlink(*i2, &buf2));
#if defined(_WIN32)
EXPECT_EQ("subdir\\test.txt", buf1);
EXPECT_EQ("..\\subdir\\test.txt", buf2);
#else
EXPECT_EQ("subdir/test.txt", buf1);
EXPECT_EQ("../subdir/test.txt", buf2);
#endif
}
{
std::string buffer;
EXPECT_EQ(0, fs.readlink(*i1, &buffer, readlink_mode::raw));
EXPECT_EQ("subdir\\test.txt", buffer);
EXPECT_EQ(0, fs.readlink(*i2, &buffer, readlink_mode::raw));
EXPECT_EQ("..\\subdir\\test.txt", buffer);
}
{
std::string buffer;
EXPECT_EQ(0, fs.readlink(*i1, &buffer, readlink_mode::unix));
EXPECT_EQ("subdir/test.txt", buffer);
EXPECT_EQ(0, fs.readlink(*i2, &buffer, readlink_mode::unix));
EXPECT_EQ("../subdir/test.txt", buffer);
}
// test error case
{
std::string buffer;
EXPECT_EQ(-EINVAL, fs.readlink(*i3, &buffer));
}
// also test expected interface
{
auto r = fs.readlink(*i1, readlink_mode::unix);
EXPECT_TRUE(r);
EXPECT_EQ("subdir/test.txt", *r);
}
{
auto r = fs.readlink(*i3);
EXPECT_FALSE(r);
EXPECT_EQ(-EINVAL, r.error());
}
}
TEST(filesystem, metadata_symlink_unix) {
test::test_logger lgr;
test::os_access_mock os;
auto mm = std::make_shared(test_dir / "unixlink.dwarfs");
filesystem_v2 fs(lgr, os, mm);
auto i1 = fs.find("link.txt");
auto i2 = fs.find("dir/link.txt");
auto i3 = fs.find("subdir/test.txt");
ASSERT_TRUE(i1);
ASSERT_TRUE(i2);
ASSERT_TRUE(i3);
EXPECT_TRUE(i1->is_symlink());
EXPECT_TRUE(i2->is_symlink());
EXPECT_TRUE(i3->is_regular_file());
// readlink_mode::preferred (default)
{
std::string buf1, buf2;
EXPECT_EQ(0, fs.readlink(*i1, &buf1));
EXPECT_EQ(0, fs.readlink(*i2, &buf2));
#if defined(_WIN32)
EXPECT_EQ("subdir\\test.txt", buf1);
EXPECT_EQ("..\\subdir\\test.txt", buf2);
#else
EXPECT_EQ("subdir/test.txt", buf1);
EXPECT_EQ("../subdir/test.txt", buf2);
#endif
}
{
std::string buffer;
EXPECT_EQ(0, fs.readlink(*i1, &buffer, readlink_mode::raw));
EXPECT_EQ("subdir/test.txt", buffer);
EXPECT_EQ(0, fs.readlink(*i2, &buffer, readlink_mode::raw));
EXPECT_EQ("../subdir/test.txt", buffer);
}
{
std::string buffer;
EXPECT_EQ(0, fs.readlink(*i1, &buffer, readlink_mode::unix));
EXPECT_EQ("subdir/test.txt", buffer);
EXPECT_EQ(0, fs.readlink(*i2, &buffer, readlink_mode::unix));
EXPECT_EQ("../subdir/test.txt", buffer);
}
// test error case
{
std::string buffer;
EXPECT_EQ(-EINVAL, fs.readlink(*i3, &buffer));
}
// also test expected interface
{
auto r = fs.readlink(*i1, readlink_mode::unix);
EXPECT_TRUE(r);
EXPECT_EQ("subdir/test.txt", *r);
}
{
auto r = fs.readlink(*i3);
EXPECT_FALSE(r);
EXPECT_EQ(-EINVAL, r.error());
}
}
namespace {
std::string valid_v1_header() {
section_header hdr;
hdr.type = section_type::BLOCK;
hdr.compression = compression_type_v1::NONE;
hdr.length = 1;
std::string buf;
buf.resize(8 + sizeof(hdr));
std::memcpy(buf.data(), "DWARFS\x02\x01", 8);
std::memcpy(buf.data() + 8, &hdr, sizeof(hdr));
return buf;
}
std::string valid_v2_header(uint32_t section_number = 0) {
section_header_v2 hdr;
std::memset(&hdr, 0, sizeof(hdr));
std::memcpy(hdr.magic, "DWARFS", 6);
hdr.major = 2;
hdr.minor = 3;
hdr.number = section_number;
hdr.type = folly::to_underlying(section_type::BLOCK);
hdr.compression = folly::to_underlying(compression_type::NONE);
hdr.length = 1;
std::string buf;
buf.resize(sizeof(hdr));
std::memcpy(buf.data(), &hdr, sizeof(hdr));
return buf;
}
} // namespace
TEST(filesystem, find_image_offset) {
test::test_logger lgr;
test::os_access_mock os;
auto make_fs =
[&](std::string data,
filesystem_options const& opt = {
.image_offset = filesystem_options::IMAGE_OFFSET_AUTO}) {
return filesystem_v2(
lgr, os, std::make_shared(std::move(data)), opt);
};
auto throws_rt_error = [](auto substr) {
return ::testing::ThrowsMessage(
::testing::HasSubstr(substr));
};
auto throws_no_fs_found = throws_rt_error("no filesystem found");
auto throws_no_schema = throws_rt_error("no metadata schema found");
std::string valid_fs;
{
mmap mm(test_dir / "unixlink.dwarfs");
auto data = mm.span();
valid_fs.assign(data.data(), data.size());
}
auto v1_header = valid_v1_header();
auto v2_header = valid_v2_header();
auto prefix = "DWARFS\x02\x02 DWARFS\x02\x02 xxxxxxxxxxxxxxxxxxxxDWARFS\x02";
EXPECT_NO_THROW(make_fs(valid_fs));
EXPECT_NO_THROW(make_fs(prefix + valid_fs));
EXPECT_THAT([&] { make_fs("dwarfs"); }, throws_no_fs_found);
EXPECT_THAT([&] { make_fs(v1_header + "X"); }, throws_no_schema);
EXPECT_THAT([&] { make_fs(v2_header + "X"); }, throws_no_fs_found);
EXPECT_THAT([&] { make_fs(v2_header + "X" + valid_v2_header(1) + "X"); },
throws_no_schema);
EXPECT_THAT([&] { make_fs(prefix); }, throws_no_fs_found);
for (size_t len = 0; len < valid_fs.size() - 1; ++len) {
auto truncated = valid_fs.substr(0, len);
EXPECT_THAT([&] { make_fs(truncated); }, ::testing::Throws())
<< "len=" << len;
EXPECT_THAT([&] { make_fs(prefix + truncated); },
::testing::Throws())
<< "len=" << len;
}
}