blob: 1eabdd03e4d23481ce1e61492c0e2184f28b1d26 [file] [log] [blame]
// Copyright (c) 2012 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include <errno.h>
#include <fcntl.h>
#include <pthread.h>
#include <stdio.h>
#include <sys/stat.h>
#include <map>
#include <string>
#include "gmock/gmock.h"
#include "gtest/gtest.h"
#include "mount_mock.h"
#include "mount_node_mock.h"
#include "nacl_io/kernel_intercept.h"
#include "nacl_io/kernel_proxy.h"
#include "nacl_io/mount.h"
#include "nacl_io/mount_mem.h"
#include "nacl_io/osmman.h"
#include "nacl_io/path.h"
#include "nacl_io/typed_mount_factory.h"
using namespace nacl_io;
using namespace sdk_util;
using ::testing::_;
using ::testing::DoAll;
using ::testing::Invoke;
using ::testing::Return;
using ::testing::SaveArg;
using ::testing::SetArgPointee;
using ::testing::StrEq;
using ::testing::WithArgs;
namespace {
class KernelProxyFriend : public KernelProxy {
public:
Mount* RootMount() {
ScopedMount mnt;
Path path;
AcquireMountAndRelPath("/", &mnt, &path);
return mnt.get();
}
};
class KernelProxyTest : public ::testing::Test {
public:
KernelProxyTest() : kp_(new KernelProxyFriend) {
ki_init(kp_);
// Unmount the passthrough FS and mount a memfs.
EXPECT_EQ(0, kp_->umount("/"));
EXPECT_EQ(0, kp_->mount("", "/", "memfs", 0, NULL));
}
~KernelProxyTest() {
ki_uninit();
delete kp_;
}
protected:
KernelProxyFriend* kp_;
};
} // namespace
TEST_F(KernelProxyTest, FileLeak) {
const size_t buffer_size = 1024;
char filename[128];
int file_num;
int garbage[buffer_size];
MountMem* mount = (MountMem*)kp_->RootMount();
ScopedMountNode root;
EXPECT_EQ(0, mount->Open(Path("/"), O_RDONLY, &root));
EXPECT_EQ(0, root->ChildCount());
for (file_num = 0; file_num < 4096; file_num++) {
sprintf(filename, "/foo%i.tmp", file_num++);
FILE* f = fopen(filename, "w");
EXPECT_NE((FILE*)0, f);
EXPECT_EQ(1, root->ChildCount());
EXPECT_EQ(buffer_size, fwrite(garbage, 1, buffer_size, f));
fclose(f);
EXPECT_EQ(0, remove(filename));
}
EXPECT_EQ(0, root->ChildCount());
}
TEST_F(KernelProxyTest, WorkingDirectory) {
char text[1024];
text[0] = 0;
ki_getcwd(text, sizeof(text));
EXPECT_STREQ("/", text);
char* alloc = ki_getwd(NULL);
EXPECT_EQ((char*)NULL, alloc);
EXPECT_EQ(EFAULT, errno);
text[0] = 0;
alloc = ki_getwd(text);
EXPECT_STREQ("/", alloc);
EXPECT_EQ(-1, ki_chdir("/foo"));
EXPECT_EQ(ENOENT, errno);
EXPECT_EQ(0, ki_chdir("/"));
EXPECT_EQ(0, ki_mkdir("/foo", S_IREAD | S_IWRITE));
EXPECT_EQ(-1, ki_mkdir("/foo", S_IREAD | S_IWRITE));
EXPECT_EQ(EEXIST, errno);
memset(text, 0, sizeof(text));
EXPECT_EQ(0, ki_chdir("foo"));
EXPECT_EQ(text, ki_getcwd(text, sizeof(text)));
EXPECT_STREQ("/foo", text);
memset(text, 0, sizeof(text));
EXPECT_EQ(-1, ki_chdir("foo"));
EXPECT_EQ(ENOENT, errno);
EXPECT_EQ(0, ki_chdir(".."));
EXPECT_EQ(0, ki_chdir("/foo"));
EXPECT_EQ(text, ki_getcwd(text, sizeof(text)));
EXPECT_STREQ("/foo", text);
}
TEST_F(KernelProxyTest, MemMountIO) {
char text[1024];
int fd1, fd2, fd3;
int len;
// Fail to delete non existant "/foo"
EXPECT_EQ(-1, ki_rmdir("/foo"));
EXPECT_EQ(ENOENT, errno);
// Create "/foo"
EXPECT_EQ(0, ki_mkdir("/foo", S_IREAD | S_IWRITE));
EXPECT_EQ(-1, ki_mkdir("/foo", S_IREAD | S_IWRITE));
EXPECT_EQ(EEXIST, errno);
// Delete "/foo"
EXPECT_EQ(0, ki_rmdir("/foo"));
// Recreate "/foo"
EXPECT_EQ(0, ki_mkdir("/foo", S_IREAD | S_IWRITE));
// Fail to open "/foo/bar"
EXPECT_EQ(-1, ki_open("/foo/bar", O_RDONLY));
EXPECT_EQ(ENOENT, errno);
// Create bar "/foo/bar"
fd1 = ki_open("/foo/bar", O_RDONLY | O_CREAT);
EXPECT_NE(-1, fd1);
// Open (optionally create) bar "/foo/bar"
fd2 = ki_open("/foo/bar", O_RDONLY | O_CREAT);
EXPECT_NE(-1, fd2);
// Fail to exclusively create bar "/foo/bar"
EXPECT_EQ(-1, ki_open("/foo/bar", O_RDONLY | O_CREAT | O_EXCL));
EXPECT_EQ(EEXIST, errno);
// Write hello and world to same node with different descriptors
// so that we overwrite each other
EXPECT_EQ(5, ki_write(fd2, "WORLD", 5));
EXPECT_EQ(5, ki_write(fd1, "HELLO", 5));
fd3 = ki_open("/foo/bar", O_WRONLY);
EXPECT_NE(-1, fd3);
len = ki_read(fd3, text, sizeof(text));
if (len > 0)
text[len] = 0;
EXPECT_EQ(5, len);
EXPECT_STREQ("HELLO", text);
EXPECT_EQ(0, ki_close(fd1));
EXPECT_EQ(0, ki_close(fd2));
fd1 = ki_open("/foo/bar", O_WRONLY | O_APPEND);
EXPECT_NE(-1, fd1);
EXPECT_EQ(5, ki_write(fd1, "WORLD", 5));
len = ki_read(fd3, text, sizeof(text));
if (len >= 0)
text[len] = 0;
EXPECT_EQ(5, len);
EXPECT_STREQ("WORLD", text);
fd2 = ki_open("/foo/bar", O_RDONLY);
EXPECT_NE(-1, fd2);
len = ki_read(fd2, text, sizeof(text));
if (len > 0)
text[len] = 0;
EXPECT_EQ(10, len);
EXPECT_STREQ("HELLOWORLD", text);
}
TEST_F(KernelProxyTest, MemMountLseek) {
int fd = ki_open("/foo", O_CREAT | O_RDWR);
EXPECT_EQ(9, ki_write(fd, "Some text", 9));
EXPECT_EQ(9, ki_lseek(fd, 0, SEEK_CUR));
EXPECT_EQ(9, ki_lseek(fd, 0, SEEK_END));
EXPECT_EQ(-1, ki_lseek(fd, -1, SEEK_SET));
EXPECT_EQ(EINVAL, errno);
// Seek past end of file.
EXPECT_EQ(13, ki_lseek(fd, 13, SEEK_SET));
char buffer[4];
memset(&buffer[0], 0xfe, 4);
EXPECT_EQ(9, ki_lseek(fd, -4, SEEK_END));
EXPECT_EQ(9, ki_lseek(fd, 0, SEEK_CUR));
EXPECT_EQ(4, ki_read(fd, &buffer[0], 4));
EXPECT_EQ(0, memcmp("\0\0\0\0", buffer, 4));
}
TEST_F(KernelProxyTest, CloseTwice) {
int fd = ki_open("/foo", O_CREAT | O_RDWR);
EXPECT_EQ(9, ki_write(fd, "Some text", 9));
int fd2 = ki_dup(fd);
EXPECT_NE(-1, fd2);
EXPECT_EQ(0, ki_close(fd));
EXPECT_EQ(0, ki_close(fd2));
}
TEST_F(KernelProxyTest, MemMountDup) {
int fd = ki_open("/foo", O_CREAT | O_RDWR);
int dup_fd = ki_dup(fd);
EXPECT_NE(-1, dup_fd);
EXPECT_EQ(9, ki_write(fd, "Some text", 9));
EXPECT_EQ(9, ki_lseek(fd, 0, SEEK_CUR));
EXPECT_EQ(9, ki_lseek(dup_fd, 0, SEEK_CUR));
int dup2_fd = 123;
EXPECT_EQ(dup2_fd, ki_dup2(fd, dup2_fd));
EXPECT_EQ(9, ki_lseek(dup2_fd, 0, SEEK_CUR));
int new_fd = ki_open("/bar", O_CREAT | O_RDWR);
EXPECT_EQ(fd, ki_dup2(new_fd, fd));
// fd, new_fd -> "/bar"
// dup_fd, dup2_fd -> "/foo"
// We should still be able to write to dup_fd (i.e. it should not be closed).
EXPECT_EQ(4, ki_write(dup_fd, "more", 4));
EXPECT_EQ(0, ki_close(dup2_fd));
// fd, new_fd -> "/bar"
// dup_fd -> "/foo"
EXPECT_EQ(dup_fd, ki_dup2(fd, dup_fd));
// fd, new_fd, dup_fd -> "/bar"
}
namespace {
StringMap_t g_StringMap;
class MountMockInit : public MountMem {
public:
virtual Error Init(int dev, StringMap_t& args, PepperInterface* ppapi) {
g_StringMap = args;
if (args.find("false") != args.end())
return EINVAL;
return 0;
}
friend class TypedMountFactory<MountMockInit>;
};
class KernelProxyMountMock : public KernelProxy {
virtual void Init(PepperInterface* ppapi) {
KernelProxy::Init(NULL);
factories_["initfs"] = new TypedMountFactory<MountMockInit>;
}
};
class KernelProxyMountTest : public ::testing::Test {
public:
KernelProxyMountTest() : kp_(new KernelProxyMountMock) { ki_init(kp_); }
~KernelProxyMountTest() {
ki_uninit();
delete kp_;
}
private:
KernelProxy* kp_;
};
} // namespace
TEST_F(KernelProxyMountTest, MountInit) {
int res1 = ki_mount("/", "/mnt1", "initfs", 0, "false,foo=bar");
EXPECT_EQ("bar", g_StringMap["foo"]);
EXPECT_EQ(-1, res1);
EXPECT_EQ(EINVAL, errno);
int res2 = ki_mount("/", "/mnt2", "initfs", 0, "true,bar=foo,x=y");
EXPECT_NE(-1, res2);
EXPECT_EQ("y", g_StringMap["x"]);
}
namespace {
int g_MMapCount = 0;
class MountNodeMockMMap : public MountNode {
public:
MountNodeMockMMap(Mount* mount) : MountNode(mount), node_mmap_count_(0) {
EXPECT_EQ(0, Init(0));
}
virtual Error MMap(void* addr,
size_t length,
int prot,
int flags,
size_t offset,
void** out_addr) {
node_mmap_count_++;
switch (g_MMapCount++) {
case 0:
*out_addr = reinterpret_cast<void*>(0x1000);
break;
case 1:
*out_addr = reinterpret_cast<void*>(0x2000);
break;
case 2:
*out_addr = reinterpret_cast<void*>(0x3000);
break;
default:
return EPERM;
}
return 0;
}
private:
int node_mmap_count_;
};
class MountMockMMap : public Mount {
public:
virtual Error Access(const Path& path, int a_mode) { return 0; }
virtual Error Open(const Path& path, int mode, ScopedMountNode* out_node) {
out_node->reset(new MountNodeMockMMap(this));
return 0;
}
virtual Error OpenResource(const Path& path, ScopedMountNode* out_node) {
out_node->reset(NULL);
return ENOSYS;
}
virtual Error Unlink(const Path& path) { return ENOSYS; }
virtual Error Mkdir(const Path& path, int permissions) { return ENOSYS; }
virtual Error Rmdir(const Path& path) { return ENOSYS; }
virtual Error Remove(const Path& path) { return ENOSYS; }
friend class TypedMountFactory<MountMockMMap>;
};
class KernelProxyMockMMap : public KernelProxy {
virtual void Init(PepperInterface* ppapi) {
KernelProxy::Init(NULL);
factories_["mmapfs"] = new TypedMountFactory<MountMockMMap>;
}
};
class KernelProxyMMapTest : public ::testing::Test {
public:
KernelProxyMMapTest() : kp_(new KernelProxyMockMMap) { ki_init(kp_); }
~KernelProxyMMapTest() {
ki_uninit();
delete kp_;
}
private:
KernelProxy* kp_;
};
} // namespace
TEST_F(KernelProxyMMapTest, MMap) {
EXPECT_EQ(0, ki_umount("/"));
EXPECT_EQ(0, ki_mount("", "/", "mmapfs", 0, NULL));
int fd = ki_open("/file", O_RDWR | O_CREAT);
EXPECT_NE(-1, fd);
void* addr1 = ki_mmap(NULL, 0x800, PROT_READ, MAP_PRIVATE, fd, 0);
EXPECT_EQ(reinterpret_cast<void*>(0x1000), addr1);
EXPECT_EQ(1, g_MMapCount);
void* addr2 = ki_mmap(NULL, 0x800, PROT_READ, MAP_PRIVATE, fd, 0);
EXPECT_EQ(reinterpret_cast<void*>(0x2000), addr2);
EXPECT_EQ(2, g_MMapCount);
void* addr3 = ki_mmap(NULL, 0x800, PROT_READ, MAP_PRIVATE, fd, 0);
EXPECT_EQ(reinterpret_cast<void*>(0x3000), addr3);
EXPECT_EQ(3, g_MMapCount);
ki_close(fd);
// We no longer track mmap'd regions, so munmap is a no-op.
EXPECT_EQ(0, ki_munmap(reinterpret_cast<void*>(0x1000), 0x2800));
// We don't track regions, so the mmap count hasn't changed.
EXPECT_EQ(3, g_MMapCount);
}
namespace {
class SingletonMountFactory : public MountFactory {
public:
SingletonMountFactory(const ScopedMount& mount) : mount_(mount) {}
virtual Error CreateMount(int dev,
StringMap_t& args,
PepperInterface* ppapi,
ScopedMount* out_mount) {
*out_mount = mount_;
return 0;
}
private:
ScopedMount mount_;
};
class KernelProxyError : public KernelProxy {
public:
KernelProxyError() : mnt_(new MountMock) {}
virtual void Init(PepperInterface* ppapi) {
KernelProxy::Init(ppapi);
factories_["testfs"] = new SingletonMountFactory(mnt_);
EXPECT_CALL(*mnt_, Destroy()).Times(1);
}
ScopedRef<MountMock> mnt() { return mnt_; }
private:
ScopedRef<MountMock> mnt_;
};
class KernelProxyErrorTest : public ::testing::Test {
public:
KernelProxyErrorTest() : kp_(new KernelProxyError) {
ki_init(kp_);
// Unmount the passthrough FS and mount a testfs.
EXPECT_EQ(0, kp_->umount("/"));
EXPECT_EQ(0, kp_->mount("", "/", "testfs", 0, NULL));
}
~KernelProxyErrorTest() {
ki_uninit();
delete kp_;
}
ScopedRef<MountMock> mnt() { return kp_->mnt(); }
private:
KernelProxyError* kp_;
};
} // namespace
TEST_F(KernelProxyErrorTest, WriteError) {
ScopedRef<MountMock> mock_mnt(mnt());
ScopedRef<MountNodeMock> mock_node(new MountNodeMock(&*mock_mnt));
EXPECT_CALL(*mock_mnt, Open(_, _, _))
.WillOnce(DoAll(SetArgPointee<2>(mock_node), Return(0)));
EXPECT_CALL(*mock_node, Write(_, _, _, _))
.WillOnce(DoAll(SetArgPointee<3>(0), // Wrote 0 bytes.
Return(1234))); // Returned error 1234.
EXPECT_CALL(*mock_node, Destroy()).Times(1);
int fd = ki_open("/dummy", O_WRONLY);
EXPECT_NE(0, fd);
char buf[20];
EXPECT_EQ(-1, ki_write(fd, &buf[0], 20));
// The Mount should be able to return whatever error it wants and have it
// propagate through.
EXPECT_EQ(1234, errno);
}
TEST_F(KernelProxyErrorTest, ReadError) {
ScopedRef<MountMock> mock_mnt(mnt());
ScopedRef<MountNodeMock> mock_node(new MountNodeMock(&*mock_mnt));
EXPECT_CALL(*mock_mnt, Open(_, _, _))
.WillOnce(DoAll(SetArgPointee<2>(mock_node), Return(0)));
EXPECT_CALL(*mock_node, Read(_, _, _, _))
.WillOnce(DoAll(SetArgPointee<3>(0), // Read 0 bytes.
Return(1234))); // Returned error 1234.
EXPECT_CALL(*mock_node, Destroy()).Times(1);
int fd = ki_open("/dummy", O_RDONLY);
EXPECT_NE(0, fd);
char buf[20];
EXPECT_EQ(-1, ki_read(fd, &buf[0], 20));
// The Mount should be able to return whatever error it wants and have it
// propagate through.
EXPECT_EQ(1234, errno);
}