Commit cdf90496 authored by matthewturk@gmail.com's avatar matthewturk@gmail.com

[NaCl SDK] Map active fds to absolute paths.

This implements both fchdir and adds an absolute path to the
Descriptor_t struct.  Typically on Linux systems this information can be
obtained by querying the /proc file system, but in absence of that, we
can track it ourselves.  For pipes and sockets, the path is the empty
string.

By enabling this behavior, fchdir can also be implemented.  This also
returns expected errno values from fchdir.  This opens up the ability to
do relative path operations like openat.  While these currently can be
executed in user code in a race condition prone way, they could be moved
into the KernelProxy, where the absolute paths can be both accessed and
resolved with AcquireHandleAndPath.

R=binji@chromium.org, sbc@chromium.org
BUG=

Review URL: https://codereview.chromium.org/99203014

git-svn-id: svn://svn.chromium.org/chrome/trunk/src@244267 0039d316-1c4b-4281-b951-d872f2087c98
parent 2219b756
...@@ -179,11 +179,30 @@ Error KernelObject::AcquireHandle(int fd, ScopedKernelHandle* out_handle) { ...@@ -179,11 +179,30 @@ Error KernelObject::AcquireHandle(int fd, ScopedKernelHandle* out_handle) {
return EBADF; return EBADF;
} }
int KernelObject::AllocateFD(const ScopedKernelHandle& handle) { Error KernelObject::AcquireHandleAndPath(int fd, ScopedKernelHandle* out_handle,
std::string* out_path){
out_handle->reset(NULL);
AUTO_LOCK(handle_lock_);
if (fd < 0 || fd >= static_cast<int>(handle_map_.size()))
return EBADF;
*out_handle = handle_map_[fd].handle;
if (!out_handle)
return EBADF;
*out_path = handle_map_[fd].path;
return 0;
}
int KernelObject::AllocateFD(const ScopedKernelHandle& handle,
const std::string& path) {
AUTO_LOCK(handle_lock_); AUTO_LOCK(handle_lock_);
int id; int id;
Descriptor_t descriptor(handle); std::string abs_path = GetAbsParts(path).Join();
Descriptor_t descriptor(handle, abs_path);
// If we can recycle and FD, use that first // If we can recycle and FD, use that first
if (free_fds_.size()) { if (free_fds_.size()) {
...@@ -196,10 +215,12 @@ int KernelObject::AllocateFD(const ScopedKernelHandle& handle) { ...@@ -196,10 +215,12 @@ int KernelObject::AllocateFD(const ScopedKernelHandle& handle) {
id = handle_map_.size(); id = handle_map_.size();
handle_map_.push_back(descriptor); handle_map_.push_back(descriptor);
} }
return id; return id;
} }
void KernelObject::FreeAndReassignFD(int fd, const ScopedKernelHandle& handle) { void KernelObject::FreeAndReassignFD(int fd, const ScopedKernelHandle& handle,
const std::string& path) {
if (NULL == handle) { if (NULL == handle) {
FreeFD(fd); FreeFD(fd);
} else { } else {
...@@ -209,7 +230,8 @@ void KernelObject::FreeAndReassignFD(int fd, const ScopedKernelHandle& handle) { ...@@ -209,7 +230,8 @@ void KernelObject::FreeAndReassignFD(int fd, const ScopedKernelHandle& handle) {
if (fd >= (int)handle_map_.size()) if (fd >= (int)handle_map_.size())
handle_map_.resize(fd + 1); handle_map_.resize(fd + 1);
handle_map_[fd] = Descriptor_t(handle); // This path will be from an existing handle, and absolute.
handle_map_[fd] = Descriptor_t(handle, path);
} }
} }
...@@ -221,6 +243,7 @@ void KernelObject::FreeFD(int fd) { ...@@ -221,6 +243,7 @@ void KernelObject::FreeFD(int fd) {
// Force lower numbered FD to be available first. // Force lower numbered FD to be available first.
std::push_heap(free_fds_.begin(), free_fds_.end(), std::greater<int>()); std::push_heap(free_fds_.begin(), free_fds_.end(), std::greater<int>());
//
} }
} // namespace nacl_io } // namespace nacl_io
...@@ -31,10 +31,13 @@ class KernelObject { ...@@ -31,10 +31,13 @@ class KernelObject {
public: public:
struct Descriptor_t { struct Descriptor_t {
Descriptor_t() : flags(0) {} Descriptor_t() : flags(0) {}
explicit Descriptor_t(const ScopedKernelHandle& h) : handle(h), flags(0) {} explicit Descriptor_t(const ScopedKernelHandle& h,
const std::string& open_path)
: handle(h), flags(0), path(open_path) {}
ScopedKernelHandle handle; ScopedKernelHandle handle;
int flags; int flags;
std::string path;
}; };
typedef std::vector<Descriptor_t> HandleMap_t; typedef std::vector<Descriptor_t> HandleMap_t;
typedef std::map<std::string, ScopedFilesystem> FsMap_t; typedef std::map<std::string, ScopedFilesystem> FsMap_t;
...@@ -71,14 +74,18 @@ class KernelObject { ...@@ -71,14 +74,18 @@ class KernelObject {
// Convert from FD to KernelHandle, and acquire the handle. // Convert from FD to KernelHandle, and acquire the handle.
// Assumes |out_handle| is non-NULL. // Assumes |out_handle| is non-NULL.
Error AcquireHandle(int fd, ScopedKernelHandle* out_handle); Error AcquireHandle(int fd, ScopedKernelHandle* out_handle);
Error AcquireHandleAndPath(int fd, ScopedKernelHandle *out_handle,
std::string* out_path);
// Allocate a new fd and assign the handle to it, while // Allocate a new fd and assign the handle to it, while
// ref counting the handle and associated filesystem. // ref counting the handle and associated filesystem.
// Assumes |handle| is non-NULL; // Assumes |handle| is non-NULL;
int AllocateFD(const ScopedKernelHandle& handle); int AllocateFD(const ScopedKernelHandle& handle,
const std::string& path=std::string());
// Assumes |handle| is non-NULL; // Assumes |handle| is non-NULL;
void FreeAndReassignFD(int fd, const ScopedKernelHandle& handle); void FreeAndReassignFD(int fd, const ScopedKernelHandle& handle,
const std::string& path);
void FreeFD(int fd); void FreeFD(int fd);
// Returns or sets CWD // Returns or sets CWD
......
...@@ -172,7 +172,7 @@ int KernelProxy::open_resource(const char* path) { ...@@ -172,7 +172,7 @@ int KernelProxy::open_resource(const char* path) {
return -1; return -1;
} }
return AllocateFD(handle); return AllocateFD(handle, path);
} }
int KernelProxy::open(const char* path, int open_flags) { int KernelProxy::open(const char* path, int open_flags) {
...@@ -192,7 +192,7 @@ int KernelProxy::open(const char* path, int open_flags) { ...@@ -192,7 +192,7 @@ int KernelProxy::open(const char* path, int open_flags) {
return -1; return -1;
} }
return AllocateFD(handle); return AllocateFD(handle, path);
} }
int KernelProxy::pipe(int pipefds[2]) { int KernelProxy::pipe(int pipefds[2]) {
...@@ -233,13 +233,13 @@ int KernelProxy::close(int fd) { ...@@ -233,13 +233,13 @@ int KernelProxy::close(int fd) {
int KernelProxy::dup(int oldfd) { int KernelProxy::dup(int oldfd) {
ScopedKernelHandle handle; ScopedKernelHandle handle;
Error error = AcquireHandle(oldfd, &handle); std::string path;
Error error = AcquireHandleAndPath(oldfd, &handle, &path);
if (error) { if (error) {
errno = error; errno = error;
return -1; return -1;
} }
return AllocateFD(handle, path);
return AllocateFD(handle);
} }
int KernelProxy::dup2(int oldfd, int newfd) { int KernelProxy::dup2(int oldfd, int newfd) {
...@@ -248,13 +248,14 @@ int KernelProxy::dup2(int oldfd, int newfd) { ...@@ -248,13 +248,14 @@ int KernelProxy::dup2(int oldfd, int newfd) {
return newfd; return newfd;
ScopedKernelHandle old_handle; ScopedKernelHandle old_handle;
Error error = AcquireHandle(oldfd, &old_handle); std::string old_path;
Error error = AcquireHandleAndPath(oldfd, &old_handle, &old_path);
if (error) { if (error) {
errno = error; errno = error;
return -1; return -1;
} }
FreeAndReassignFD(newfd, old_handle); FreeAndReassignFD(newfd, old_handle, old_path);
return newfd; return newfd;
} }
...@@ -509,8 +510,31 @@ int KernelProxy::getdents(int fd, void* buf, unsigned int count) { ...@@ -509,8 +510,31 @@ int KernelProxy::getdents(int fd, void* buf, unsigned int count) {
} }
int KernelProxy::fchdir(int fd) { int KernelProxy::fchdir(int fd) {
errno = ENOSYS; ScopedKernelHandle handle;
return -1; std::string path;
Error error = AcquireHandleAndPath(fd, &handle, &path);
if (error) {
errno = error;
return -1;
}
if (!handle->node()->IsaDir()) {
errno = ENOTDIR;
return -1;
}
if (path.empty()) {
errno = EBADF;
return -1;
}
error = SetCWD(path);
if (error) {
// errno is return value from SetCWD
errno = error;
return -1;
}
return 0;
} }
int KernelProxy::ftruncate(int fd, off_t length) { int KernelProxy::ftruncate(int fd, off_t length) {
......
...@@ -95,13 +95,13 @@ TEST_F(KernelObjectTest, Referencing) { ...@@ -95,13 +95,13 @@ TEST_F(KernelObjectTest, Referencing) {
// Allocating an FD should cause the KernelProxy to ref the handle and // Allocating an FD should cause the KernelProxy to ref the handle and
// the node and filesystem should be unchanged. // the node and filesystem should be unchanged.
int fd1 = proxy.AllocateFD(handle_a); int fd1 = proxy.AllocateFD(handle_a, "/example");
EXPECT_EQ(3, handle_a->RefCount()); EXPECT_EQ(3, handle_a->RefCount());
EXPECT_EQ(2, fs->RefCount()); EXPECT_EQ(2, fs->RefCount());
EXPECT_EQ(2, node->RefCount()); EXPECT_EQ(2, node->RefCount());
// If we "dup" the handle, we should bump the ref count on the handle // If we "dup" the handle, we should bump the ref count on the handle
int fd2 = proxy.AllocateFD(handle_b); int fd2 = proxy.AllocateFD(handle_b, "");
EXPECT_EQ(4, handle_a->RefCount()); EXPECT_EQ(4, handle_a->RefCount());
EXPECT_EQ(2, fs->RefCount()); EXPECT_EQ(2, fs->RefCount());
EXPECT_EQ(2, node->RefCount()); EXPECT_EQ(2, node->RefCount());
...@@ -165,16 +165,17 @@ TEST_F(KernelObjectTest, FreeAndReassignFD) { ...@@ -165,16 +165,17 @@ TEST_F(KernelObjectTest, FreeAndReassignFD) {
EXPECT_EQ(2, node->RefCount()); EXPECT_EQ(2, node->RefCount());
EXPECT_EQ(1, raw_handle->RefCount()); EXPECT_EQ(1, raw_handle->RefCount());
proxy.AllocateFD(handle); proxy.AllocateFD(handle, "/example");
EXPECT_EQ(2, fs->RefCount()); EXPECT_EQ(2, fs->RefCount());
EXPECT_EQ(2, node->RefCount()); EXPECT_EQ(2, node->RefCount());
EXPECT_EQ(2, raw_handle->RefCount()); EXPECT_EQ(2, raw_handle->RefCount());
proxy.FreeAndReassignFD(5, handle); proxy.FreeAndReassignFD(5, handle, "/example");
EXPECT_EQ(2, fs->RefCount()); EXPECT_EQ(2, fs->RefCount());
EXPECT_EQ(2, node->RefCount()); EXPECT_EQ(2, node->RefCount());
EXPECT_EQ(3, raw_handle->RefCount()); EXPECT_EQ(3, raw_handle->RefCount());
handle.reset(); handle.reset();
EXPECT_EQ(2, raw_handle->RefCount()); EXPECT_EQ(2, raw_handle->RefCount());
......
...@@ -282,6 +282,62 @@ TEST_F(KernelProxyTest, WorkingDirectory) { ...@@ -282,6 +282,62 @@ TEST_F(KernelProxyTest, WorkingDirectory) {
EXPECT_STREQ("/foo", text); EXPECT_STREQ("/foo", text);
} }
TEST_F(KernelProxyTest, FDPathMapping) {
char text[1024];
int fd1, fd2, fd3, fd4, fd5;
EXPECT_EQ(0, ki_mkdir("/foo", S_IREAD | S_IWRITE));
EXPECT_EQ(0, ki_mkdir("/foo/bar", S_IREAD | S_IWRITE));
EXPECT_EQ(0, ki_mkdir("/example", S_IREAD | S_IWRITE));
ki_chdir("/foo");
fd1 = ki_open("/example", O_RDONLY);
EXPECT_NE(-1, fd1);
EXPECT_EQ(ki_fchdir(fd1), 0);
EXPECT_EQ(text, ki_getcwd(text, sizeof(text)));
EXPECT_STREQ("/example", text);
EXPECT_EQ(0, ki_chdir("/foo"));
fd2 = ki_open("../example", O_RDONLY);
EXPECT_NE(-1, fd2);
EXPECT_EQ(0, ki_fchdir(fd2));
EXPECT_EQ(text, ki_getcwd(text, sizeof(text)));
EXPECT_STREQ("/example", text);
EXPECT_EQ(0, ki_chdir("/foo"));
fd3 = ki_open("../test", O_CREAT | O_RDWR);
EXPECT_NE(-1, fd3);
EXPECT_EQ(-1, ki_fchdir(fd3));
EXPECT_EQ(ENOTDIR, errno);
EXPECT_EQ(0, ki_chdir("/foo"));
fd4 = ki_open("bar", O_RDONLY);
EXPECT_EQ(0, ki_fchdir(fd4));
EXPECT_EQ(text, ki_getcwd(text, sizeof(text)));
EXPECT_STREQ("/foo/bar", text);
EXPECT_EQ(0, ki_chdir("/example"));
EXPECT_EQ(0, ki_fchdir(fd4));
EXPECT_EQ(text, ki_getcwd(text, sizeof(text)));
EXPECT_STREQ("/foo/bar", text);
EXPECT_EQ(0, ki_chdir("/example"));
fd5 = ki_dup(fd4);
ASSERT_GT(fd5, -1);
ASSERT_NE(fd4, fd5);
EXPECT_EQ(0, ki_fchdir(fd5));
EXPECT_EQ(text, ki_getcwd(text, sizeof(text)));
EXPECT_STREQ("/foo/bar", text);
fd5 = 123;
EXPECT_EQ(0, ki_chdir("/example"));
EXPECT_EQ(fd5, ki_dup2(fd4, fd5));
EXPECT_EQ(0, ki_fchdir(fd5));
EXPECT_EQ(text, ki_getcwd(text, sizeof(text)));
EXPECT_STREQ("/foo/bar", text);
}
TEST_F(KernelProxyTest, MemMountIO) { TEST_F(KernelProxyTest, MemMountIO) {
char text[1024]; char text[1024];
int fd1, fd2, fd3; int fd1, fd2, fd3;
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment