summaryrefslogtreecommitdiffstats
path: root/native_client_sdk
diff options
context:
space:
mode:
authorbinji@chromium.org <binji@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2014-05-23 16:58:53 +0000
committerbinji@chromium.org <binji@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2014-05-23 16:58:53 +0000
commitf52f024639003dab5459a01f1927518ef2a345c9 (patch)
tree8abb82b40338fc2c18b3b1bcca157842533c1587 /native_client_sdk
parent45cba27eb5ab9f37429a412cf616f23ec974fd18 (diff)
downloadchromium_src-f52f024639003dab5459a01f1927518ef2a345c9.zip
chromium_src-f52f024639003dab5459a01f1927518ef2a345c9.tar.gz
chromium_src-f52f024639003dab5459a01f1927518ef2a345c9.tar.bz2
[NaCl SDK] Add realpath(3) implementation.
This really should be in nacl-newlib, but it's easier to land here now and remove it later when it lands in newlib and rolls into the SDK. This implementation does not support symlinks, but neither does nacl_io currently. BUG=none R=sbc@chromium.org Review URL: https://codereview.chromium.org/295933009 git-svn-id: svn://svn.chromium.org/chrome/trunk/src@272551 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'native_client_sdk')
-rw-r--r--native_client_sdk/src/libraries/nacl_io/include/stdlib.h17
-rw-r--r--native_client_sdk/src/libraries/nacl_io/library.dsc23
-rw-r--r--native_client_sdk/src/libraries/nacl_io/syscalls/chdir.c2
-rw-r--r--native_client_sdk/src/libraries/nacl_io/syscalls/mkdir.c6
-rw-r--r--native_client_sdk/src/libraries/nacl_io/syscalls/realpath.c131
-rw-r--r--native_client_sdk/src/tests/nacl_io_test/example.dsc1
-rw-r--r--native_client_sdk/src/tests/nacl_io_test/syscalls_test.cc112
7 files changed, 281 insertions, 11 deletions
diff --git a/native_client_sdk/src/libraries/nacl_io/include/stdlib.h b/native_client_sdk/src/libraries/nacl_io/include/stdlib.h
new file mode 100644
index 0000000..5ffae74
--- /dev/null
+++ b/native_client_sdk/src/libraries/nacl_io/include/stdlib.h
@@ -0,0 +1,17 @@
+/* Copyright (c) 2014 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. */
+
+#ifndef LIBRARIES_NACL_IO_INCLUDE_STDLIB_H_
+#define LIBRARIES_NACL_IO_INCLUDE_STDLIB_H_
+
+#include <sys/cdefs.h>
+#include_next <stdlib.h>
+
+__BEGIN_DECLS
+
+char* realpath(const char* path, char* resolved_path);
+
+__END_DECLS
+
+#endif
diff --git a/native_client_sdk/src/libraries/nacl_io/library.dsc b/native_client_sdk/src/libraries/nacl_io/library.dsc
index 4676004..a56cb64 100644
--- a/native_client_sdk/src/libraries/nacl_io/library.dsc
+++ b/native_client_sdk/src/libraries/nacl_io/library.dsc
@@ -68,9 +68,9 @@
"syscalls/bind.c",
"syscalls/cfgetispeed.c",
"syscalls/cfgetospeed.c",
- "syscalls/cfsetspeed.c",
"syscalls/cfsetispeed.c",
"syscalls/cfsetospeed.c",
+ "syscalls/cfsetspeed.c",
"syscalls/chdir.c",
"syscalls/chmod.c",
"syscalls/chown.c",
@@ -118,19 +118,13 @@
"syscalls/pipe.c",
"syscalls/poll.c",
"syscalls/readlink.c",
- "syscalls/rmdir.c",
+ "syscalls/realpath.c",
"syscalls/recv.c",
"syscalls/recvfrom.c",
"syscalls/recvmsg.c",
"syscalls/remove.c",
"syscalls/rename.c",
- "syscalls/tcdrain.c",
- "syscalls/tcflow.c",
- "syscalls/tcflush.c",
- "syscalls/tcgetattr.c",
- "syscalls/tcsendbreak.c",
- "syscalls/tcsetattr.c",
- "syscalls/truncate.c",
+ "syscalls/rmdir.c",
"syscalls/select.c",
"syscalls/send.c",
"syscalls/sendmsg.c",
@@ -151,9 +145,16 @@
"syscalls/socket.c",
"syscalls/socketpair.c",
"syscalls/symlink.c",
- "syscalls/unlink.c",
+ "syscalls/tcdrain.c",
+ "syscalls/tcflow.c",
+ "syscalls/tcflush.c",
+ "syscalls/tcgetattr.c",
+ "syscalls/tcsendbreak.c",
+ "syscalls/tcsetattr.c",
+ "syscalls/truncate.c",
"syscalls/umount.c",
"syscalls/uname.c",
+ "syscalls/unlink.c",
"syscalls/utime.c",
"syscalls/utimes.c",
],
@@ -241,6 +242,7 @@
"netinet/tcp.h",
"netinet6/in6.h",
"poll.h",
+ "stdlib.h",
"sys/ioctl.h",
"sys/mount.h",
"sys/poll.h",
@@ -261,6 +263,7 @@
"netinet/tcp.h",
"netinet6/in6.h",
"poll.h",
+ "stdlib.h",
"sys/ioctl.h",
"sys/mount.h",
"sys/poll.h",
diff --git a/native_client_sdk/src/libraries/nacl_io/syscalls/chdir.c b/native_client_sdk/src/libraries/nacl_io/syscalls/chdir.c
index d45aad7..a7d5c9d 100644
--- a/native_client_sdk/src/libraries/nacl_io/syscalls/chdir.c
+++ b/native_client_sdk/src/libraries/nacl_io/syscalls/chdir.c
@@ -5,7 +5,7 @@
#include "nacl_io/kernel_intercept.h"
#include "nacl_io/kernel_wrap.h"
-#if !defined(__native_client__) || defined(__GLIBC__)
+#if defined(__native_client__) && defined(__GLIBC__)
// GLIBC-only entry point.
// TODO(sbc): remove once this bug gets fixed:
// https://code.google.com/p/nativeclient/issues/detail?id=3709
diff --git a/native_client_sdk/src/libraries/nacl_io/syscalls/mkdir.c b/native_client_sdk/src/libraries/nacl_io/syscalls/mkdir.c
index f4dc8af..cecaa3d 100644
--- a/native_client_sdk/src/libraries/nacl_io/syscalls/mkdir.c
+++ b/native_client_sdk/src/libraries/nacl_io/syscalls/mkdir.c
@@ -5,6 +5,12 @@
#include "nacl_io/kernel_intercept.h"
#include "nacl_io/kernel_wrap.h"
+#if defined(__native_client__)
+// Don't wrap mkdir on host builds. This allows us to test against the real
+// "mkdir" on Linux standalone builds.
+
int mkdir(const char* pathname, mode_t mode) {
return ki_mkdir(pathname, mode);
}
+
+#endif
diff --git a/native_client_sdk/src/libraries/nacl_io/syscalls/realpath.c b/native_client_sdk/src/libraries/nacl_io/syscalls/realpath.c
new file mode 100644
index 0000000..9451243
--- /dev/null
+++ b/native_client_sdk/src/libraries/nacl_io/syscalls/realpath.c
@@ -0,0 +1,131 @@
+/* Copyright 2013 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 <assert.h>
+#include <stdlib.h>
+#include <errno.h>
+#include <limits.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/stat.h>
+#include <unistd.h>
+
+#include "sdk_util/macros.h"
+
+EXTERN_C_BEGIN
+
+#if defined(__native_client__)
+
+// TODO(binji): glibc has realpath, but it fails for all tests. Investigate.
+
+char* realpath(const char* path, char* resolved_path) {
+ if (path == NULL) {
+ errno = EINVAL;
+ return NULL;
+ }
+
+ int needs_free = 0;
+ if (resolved_path == NULL) {
+ resolved_path = (char*)malloc(PATH_MAX);
+ needs_free = 1;
+ }
+
+ struct stat statbuf;
+ const char* in = path;
+ char* out = resolved_path;
+ char* out_end = resolved_path + PATH_MAX - 1;
+ int done = 0;
+
+ *out = 0;
+
+ if (*in == '/') {
+ // Absolute path.
+ strcat(out, "/");
+ in++;
+ out++;
+ } else {
+ // Relative path.
+ if (getcwd(out, out_end - out) == NULL)
+ goto fail;
+
+ out += strlen(out);
+ }
+
+ if (stat(resolved_path, &statbuf) != 0)
+ goto fail;
+
+ while (!done) {
+ const char* next_slash = strchr(in, '/');
+ size_t namelen;
+ const char* next_in;
+ if (next_slash) {
+ namelen = next_slash - in;
+ next_in = next_slash + 1;
+ } else {
+ namelen = strlen(in);
+ next_in = in + namelen; // Move to the '\0'
+ done = 1;
+ }
+
+ if (namelen == 0) {
+ // Empty name, do nothing.
+ } else if (namelen == 1 && strncmp(in, ".", 1) == 0) {
+ // Current directory, do nothing.
+ } else if (namelen == 2 && strncmp(in, "..", 2) == 0) {
+ // Parent directory, find previous slash in resolved_path.
+ char* prev_slash = strrchr(resolved_path, '/');
+ assert(prev_slash != NULL);
+
+ out = prev_slash;
+ if (prev_slash == resolved_path) {
+ // Moved to the root. Keep the slash.
+ ++out;
+ }
+
+ *out = 0;
+ } else {
+ // Append a slash if not at root.
+ if (out != resolved_path + 1) {
+ if (out + 1 > out_end) {
+ errno = ENAMETOOLONG;
+ goto fail;
+ }
+
+ strncat(out, "/", namelen);
+ out++;
+ }
+
+ if (out + namelen > out_end) {
+ errno = ENAMETOOLONG;
+ goto fail;
+ }
+
+ strncat(out, in, namelen);
+ out += namelen;
+ }
+
+ in = next_in;
+
+ if (stat(resolved_path, &statbuf) != 0)
+ goto fail;
+
+ // If there is more to the path, then the current path must be a directory.
+ if (!done && !S_ISDIR(statbuf.st_mode)) {
+ errno = ENOTDIR;
+ goto fail;
+ }
+ }
+
+ return resolved_path;
+
+fail:
+ if (needs_free) {
+ free(resolved_path);
+ }
+ return NULL;
+}
+
+EXTERN_C_END
+
+#endif // defined(__native_client__)
diff --git a/native_client_sdk/src/tests/nacl_io_test/example.dsc b/native_client_sdk/src/tests/nacl_io_test/example.dsc
index 15c0f47..76278a3 100644
--- a/native_client_sdk/src/tests/nacl_io_test/example.dsc
+++ b/native_client_sdk/src/tests/nacl_io_test/example.dsc
@@ -53,6 +53,7 @@
'mock_node.cc',
'mock_node.h',
'mock_util.h',
+ 'syscalls_test.cc',
'path_test.cc',
'pepper_interface_mock.cc',
'pepper_interface_mock.h',
diff --git a/native_client_sdk/src/tests/nacl_io_test/syscalls_test.cc b/native_client_sdk/src/tests/nacl_io_test/syscalls_test.cc
new file mode 100644
index 0000000..a50ba47
--- /dev/null
+++ b/native_client_sdk/src/tests/nacl_io_test/syscalls_test.cc
@@ -0,0 +1,112 @@
+// Copyright (c) 2014 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 <limits.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <sys/stat.h>
+
+#include "gtest/gtest.h"
+
+#include "nacl_io/kernel_intercept.h"
+#include "nacl_io/kernel_proxy.h"
+
+using namespace nacl_io;
+
+namespace {
+
+class SyscallsTest : public ::testing::Test {
+ public:
+ SyscallsTest() {}
+
+ void SetUp() {
+ ASSERT_EQ(0, ki_push_state_for_testing());
+ ASSERT_EQ(0, ki_init(&kp_));
+ // Unmount the passthrough FS and mount a memfs.
+ EXPECT_EQ(0, kp_.umount("/"));
+ EXPECT_EQ(0, kp_.mount("", "/", "memfs", 0, NULL));
+ }
+
+ void TearDown() { ki_uninit(); }
+
+ protected:
+ KernelProxy kp_;
+};
+
+} // namespace
+
+#if defined(__native_client__) || defined(STANDALONE)
+
+// The Linux standalone test is unique in that it calls the real Linux
+// functions (realpath, mkdir, chdir, etc.), not the nacl_io functions. This is
+// done to show that the tests match the behavior for a real implementation.
+
+TEST_F(SyscallsTest, Realpath) {
+ char buffer[PATH_MAX];
+ int result;
+
+#if defined(__native_client__)
+ ASSERT_EQ(0, mkdir("/tmp", S_IREAD | S_IWRITE));
+#endif
+
+ result = mkdir("/tmp/bar", S_IREAD | S_IWRITE);
+#if defined(__native_client__)
+ ASSERT_EQ(0, result);
+#else
+ if (result == -1) {
+ ASSERT_EQ(EEXIST, errno);
+ } else {
+ ASSERT_EQ(0, result);
+ }
+#endif
+
+ int fd = open("/tmp/file", O_CREAT | O_RDWR, 0644);
+ ASSERT_GT(fd, -1);
+ ASSERT_EQ(0, close(fd));
+
+ // Test absolute paths.
+ EXPECT_STREQ("/", realpath("/", buffer));
+ EXPECT_STREQ("/", realpath("/tmp/..", buffer));
+ EXPECT_STREQ("/tmp", realpath("/tmp", buffer));
+ EXPECT_STREQ("/tmp", realpath("/tmp/", buffer));
+ EXPECT_STREQ("/tmp", realpath("/tmp/bar/..", buffer));
+ EXPECT_STREQ("/tmp", realpath("/tmp/bar/../bar/../../tmp", buffer));
+ EXPECT_STREQ("/tmp", realpath("/tmp/././", buffer));
+ EXPECT_STREQ("/tmp", realpath("///tmp", buffer));
+ EXPECT_STREQ("/tmp/bar", realpath("/tmp/bar", buffer));
+
+ EXPECT_EQ(NULL, realpath("/blah", buffer));
+ EXPECT_EQ(ENOENT, errno);
+
+ EXPECT_EQ(NULL, realpath("/blah/blah", buffer));
+ EXPECT_EQ(ENOENT, errno);
+
+ EXPECT_EQ(NULL, realpath("/tmp/baz/..", buffer));
+ EXPECT_EQ(ENOENT, errno);
+
+ EXPECT_EQ(NULL, realpath("/tmp/file/", buffer));
+ EXPECT_EQ(ENOTDIR, errno);
+
+ EXPECT_EQ(NULL, realpath(NULL, buffer));
+ EXPECT_EQ(EINVAL, errno);
+
+ // Test relative paths.
+ EXPECT_EQ(0, chdir("/tmp"));
+
+ EXPECT_STREQ("/", realpath("..", buffer));
+ EXPECT_STREQ("/tmp", realpath(".", buffer));
+ EXPECT_STREQ("/tmp", realpath("./", buffer));
+ EXPECT_STREQ("/tmp", realpath("bar/..", buffer));
+ EXPECT_STREQ("/tmp", realpath("bar/../../tmp", buffer));
+ EXPECT_STREQ("/tmp", realpath(".///", buffer));
+ EXPECT_STREQ("/tmp/bar", realpath("bar", buffer));
+
+ // Test when resolved_path is allocated.
+ char* allocated = realpath("/tmp", NULL);
+ EXPECT_STREQ("/tmp", allocated);
+ free(allocated);
+}
+
+#endif // defined(__native_client__) || defined(STANDALONE)