extern "C" {
#include <sys/param.h>
#include <sys/ioctl.h>
#include <sys/filio.h>
#include <fcntl.h>
}
#include "mockfs.hh"
#include "utils.hh"
using namespace testing;
const static char FULLPATH[] = "mountpoint/foo";
const static char RELPATH[] = "foo";
class Bmap: public FuseTest {
public:
virtual void SetUp() {
m_maxreadahead = UINT32_MAX;
FuseTest::SetUp();
}
void expect_bmap(uint64_t ino, uint64_t lbn, uint32_t blocksize, uint64_t pbn)
{
EXPECT_CALL(*m_mock, process(
ResultOf([=](auto in) {
return (in.header.opcode == FUSE_BMAP &&
in.header.nodeid == ino &&
in.body.bmap.block == lbn &&
in.body.bmap.blocksize == blocksize);
}, Eq(true)),
_)
).WillOnce(Invoke(ReturnImmediate([=](auto i __unused, auto& out) {
SET_OUT_HEADER_LEN(out, bmap);
out.body.bmap.block = pbn;
})));
}
void expect_lookup(const char *relpath, uint64_t ino, off_t size)
{
FuseTest::expect_lookup(relpath, ino, S_IFREG | 0644, size, 1,
UINT64_MAX);
}
};
class BmapEof: public Bmap, public WithParamInterface<int> {};
TEST_F(Bmap, bmap)
{
struct fiobmap2_arg arg;
const off_t filesize = 1 << 30;
int64_t lbn = 100;
int64_t pbn = 12345;
const ino_t ino = 42;
int fd;
expect_lookup(RELPATH, 42, filesize);
expect_open(ino, 0, 1);
expect_bmap(ino, lbn, m_maxbcachebuf, pbn);
fd = open(FULLPATH, O_RDWR);
ASSERT_LE(0, fd) << strerror(errno);
arg.bn = lbn;
arg.runp = -1;
arg.runb = -1;
ASSERT_EQ(0, ioctl(fd, FIOBMAP2, &arg)) << strerror(errno);
EXPECT_EQ(arg.bn, pbn);
EXPECT_LE(arg.runb, lbn);
EXPECT_LE((unsigned long)arg.runb, m_maxreadahead / m_maxbcachebuf);
EXPECT_LE((unsigned long)arg.runb, m_maxphys / m_maxbcachebuf);
EXPECT_GT(arg.runb, 0);
EXPECT_LE(arg.runp, filesize / m_maxbcachebuf - lbn);
EXPECT_LE((unsigned long)arg.runp, m_maxreadahead / m_maxbcachebuf);
EXPECT_LE((unsigned long)arg.runp, m_maxphys / m_maxbcachebuf);
EXPECT_GT(arg.runp, 0);
leak(fd);
}
TEST_F(Bmap, default_)
{
struct fiobmap2_arg arg;
const off_t filesize = 1 << 30;
const ino_t ino = 42;
int64_t lbn;
int fd;
expect_lookup(RELPATH, 42, filesize);
expect_open(ino, 0, 1);
EXPECT_CALL(*m_mock, process(
ResultOf([=](auto in) {
return (in.header.opcode == FUSE_BMAP);
}, Eq(true)),
_)
).WillOnce(Invoke(ReturnErrno(ENOSYS)));
fd = open(FULLPATH, O_RDWR);
ASSERT_LE(0, fd) << strerror(errno);
lbn = 0;
arg.bn = lbn;
arg.runp = -1;
arg.runb = -1;
ASSERT_EQ(0, ioctl(fd, FIOBMAP2, &arg)) << strerror(errno);
EXPECT_EQ(arg.bn, 0);
EXPECT_EQ((unsigned long )arg.runp, m_maxphys / m_maxbcachebuf - 1);
EXPECT_EQ(arg.runb, 0);
lbn = filesize / m_maxbcachebuf / 2;
arg.bn = lbn;
arg.runp = -1;
arg.runb = -1;
ASSERT_EQ(0, ioctl(fd, FIOBMAP2, &arg)) << strerror(errno);
EXPECT_EQ(arg.bn, lbn * m_maxbcachebuf / DEV_BSIZE);
EXPECT_EQ((unsigned long )arg.runp, m_maxphys / m_maxbcachebuf - 1);
EXPECT_EQ((unsigned long )arg.runb, m_maxphys / m_maxbcachebuf - 1);
lbn = filesize / m_maxbcachebuf - 1;
arg.bn = lbn;
arg.runp = -1;
arg.runb = -1;
ASSERT_EQ(0, ioctl(fd, FIOBMAP2, &arg)) << strerror(errno);
EXPECT_EQ(arg.bn, lbn * m_maxbcachebuf / DEV_BSIZE);
EXPECT_EQ(arg.runp, 0);
EXPECT_EQ((unsigned long )arg.runb, m_maxphys / m_maxbcachebuf - 1);
leak(fd);
}
TEST_P(BmapEof, eof)
{
Sequence seq;
const off_t filesize = 2 * m_maxbcachebuf;
const ino_t ino = 42;
mode_t mode = S_IFREG | 0644;
char *buf;
int fd;
int ngetattrs;
ngetattrs = GetParam();
FuseTest::expect_lookup(RELPATH, ino, mode, filesize, 1, 0);
expect_open(ino, 0, 1);
EXPECT_CALL(*m_mock, process(
ResultOf([=](auto in) {
return (in.header.opcode == FUSE_READ &&
in.header.nodeid == ino &&
in.body.read.offset == 0 &&
( in.body.read.size == filesize ||
in.body.read.size == filesize / 2));
}, Eq(true)),
_)
).WillOnce(Invoke(ReturnImmediate([=](auto in, auto& out) {
size_t osize = in.body.read.size;
assert(osize < sizeof(out.body.bytes));
out.header.len = sizeof(struct fuse_out_header) + osize;
bzero(out.body.bytes, osize);
})));
EXPECT_CALL(*m_mock, process(
ResultOf([](auto in) {
return (in.header.opcode == FUSE_GETATTR &&
in.header.nodeid == ino);
}, Eq(true)),
_)
).Times(Between(ngetattrs - 1, ngetattrs))
.InSequence(seq)
.WillRepeatedly(Invoke(ReturnImmediate([=](auto i __unused, auto& out) {
SET_OUT_HEADER_LEN(out, attr);
out.body.attr.attr_valid = 0;
out.body.attr.attr.ino = ino;
out.body.attr.attr.mode = S_IFREG | 0644;
out.body.attr.attr.size = filesize;
})));
EXPECT_CALL(*m_mock, process(
ResultOf([](auto in) {
return (in.header.opcode == FUSE_GETATTR &&
in.header.nodeid == ino);
}, Eq(true)),
_)
).InSequence(seq)
.WillRepeatedly(Invoke(ReturnImmediate([=](auto i __unused, auto& out) {
SET_OUT_HEADER_LEN(out, attr);
out.body.attr.attr_valid = 0;
out.body.attr.attr.ino = ino;
out.body.attr.attr.mode = S_IFREG | 0644;
out.body.attr.attr.size = filesize / 2;
})));
buf = new char[filesize]();
fd = open(FULLPATH, O_RDWR);
ASSERT_LE(0, fd) << strerror(errno);
read(fd, buf, filesize);
delete[] buf;
leak(fd);
}
INSTANTIATE_TEST_SUITE_P(BE, BmapEof,
Values(1, 2, 3)
);