Use realpath() to find the path to the extension unpack dir on posix systems.

Extensions are unpacked by a sandboxed utility process.  The sandbox forbids file access outside the directory the extension will be unpacked in.  If the path to that directory contains a symbolic link, then unpacking will fail because following the link will cause file system access outside the sandbox path.  Use realpath() to get a symlink free path to the directory where the extension will be unpacked.

A similar issue exists on windows, with junctions instead of symlinks.  This will be fixed in another change.

BUG=13044,35198
TEST=FileUtilTest.RealPath

Review URL: http://codereview.chromium.org/2001013

git-svn-id: svn://svn.chromium.org/chrome/trunk/src@47032 0039d316-1c4b-4281-b951-d872f2087c98


CrOS-Libchrome-Original-Commit: 01e2a1fabe5a6d0065f535a9de4bdd98f0640906
diff --git a/base/file_util.h b/base/file_util.h
index a129876..64a91ee 100644
--- a/base/file_util.h
+++ b/base/file_util.h
@@ -279,6 +279,13 @@
 // Returns true if the given path's base name is "..".
 bool IsDotDot(const FilePath& path);
 
+#if defined(OS_POSIX)
+// Set |real_path| to |path| with symbolic links expanded.
+// Windows support (expanding junctions) comming soon:
+// http://crbug.com/13044
+bool RealPath(const FilePath& path, FilePath* real_path);
+#endif
+
 // Used to hold information about a given file path.  See GetFileInfo below.
 struct FileInfo {
   // The size of the file in bytes.  Undefined when is_directory is true.
diff --git a/base/file_util_posix.cc b/base/file_util_posix.cc
index d9cbe09..9db41fe 100644
--- a/base/file_util_posix.cc
+++ b/base/file_util_posix.cc
@@ -14,6 +14,7 @@
 #include <string.h>
 #include <sys/errno.h>
 #include <sys/mman.h>
+#include <sys/param.h>
 #include <sys/stat.h>
 #include <sys/time.h>
 #include <sys/types.h>
@@ -735,6 +736,15 @@
   return find_info.stat.st_mtime >= cutoff_time.ToTimeT();
 }
 
+bool RealPath(const FilePath& path, FilePath* real_path) {
+  FilePath::CharType buf[PATH_MAX];
+  if (!realpath(path.value().c_str(), buf))
+    return false;
+
+  *real_path = FilePath(buf);
+  return true;
+}
+
 #if !defined(OS_MACOSX)
 bool GetTempDir(FilePath* path) {
   const char* tmp = getenv("TMPDIR");
diff --git a/base/file_util_unittest.cc b/base/file_util_unittest.cc
index c496290..efe79c9 100644
--- a/base/file_util_unittest.cc
+++ b/base/file_util_unittest.cc
@@ -387,6 +387,57 @@
   EXPECT_EQ(size_f1 + size_f2 + 3, computed_size);
 }
 
+#if defined(OS_POSIX)
+TEST_F(FileUtilTest, RealPath) {
+  // Get the real test directory, in case some future change to the
+  // test setup makes the path to test_dir_ include a symlink.
+  FilePath real_test_dir;
+  ASSERT_TRUE(file_util::RealPath(test_dir_, &real_test_dir));
+
+  FilePath real_path;
+  ASSERT_TRUE(file_util::RealPath(real_test_dir, &real_path));
+  ASSERT_TRUE(real_test_dir == real_path);
+
+  // Link one file to another.
+  FilePath link_from = real_test_dir.Append(FPL("from_file"));
+  FilePath link_to = real_test_dir.Append(FPL("to_file"));
+  CreateTextFile(link_to, bogus_content);
+
+  ASSERT_EQ(0, symlink(link_to.value().c_str(), link_from.value().c_str()))
+    << "Failed to create file symlink.";
+
+  // Check that RealPath sees the link.
+  ASSERT_TRUE(file_util::RealPath(link_from, &real_path));
+  ASSERT_TRUE(link_to != link_from);
+  ASSERT_TRUE(link_to == real_path);
+
+
+  // Link to a directory.
+  link_from = real_test_dir.Append(FPL("from_dir"));
+  link_to = real_test_dir.Append(FPL("to_dir"));
+  file_util::CreateDirectory(link_to);
+
+  ASSERT_EQ(0, symlink(link_to.value().c_str(), link_from.value().c_str()))
+    << "Failed to create directory symlink.";
+
+  ASSERT_TRUE(file_util::RealPath(link_from, &real_path));
+  ASSERT_TRUE(link_to != link_from);
+  ASSERT_TRUE(link_to == real_path);
+
+
+  // Test that a loop in the links causes RealPath() to return false.
+  link_from = real_test_dir.Append(FPL("link_a"));
+  link_to = real_test_dir.Append(FPL("link_b"));
+  ASSERT_EQ(0, symlink(link_to.value().c_str(), link_from.value().c_str()))
+    << "Failed to create loop symlink a.";
+  ASSERT_EQ(0, symlink(link_from.value().c_str(), link_to.value().c_str()))
+    << "Failed to create loop symlink b.";
+
+  // Infinite loop!
+  ASSERT_FALSE(file_util::RealPath(link_from, &real_path));
+}
+#endif  // defined(OS_POSIX)
+
 TEST_F(FileUtilTest, DeleteNonExistent) {
   FilePath non_existent = test_dir_.AppendASCII("bogus_file_dne.foobar");
   ASSERT_FALSE(file_util::PathExists(non_existent));