crash-reporter: Avoid writing through symlinks.

BUG=7987
TEST=bvts

Change-Id: I875adeb5073936e790beb93f6a223a1642131cbd

Review URL: http://codereview.chromium.org/4603001
diff --git a/crash_reporter/crash_collector_test.cc b/crash_reporter/crash_collector_test.cc
index 859653b..bf3a9dd 100644
--- a/crash_reporter/crash_collector_test.cc
+++ b/crash_reporter/crash_collector_test.cc
@@ -8,9 +8,16 @@
 #include "base/string_util.h"
 #include "crash-reporter/crash_collector.h"
 #include "crash-reporter/system_logging_mock.h"
+#include "crash-reporter/test_helpers.h"
 #include "gflags/gflags.h"
 #include "gtest/gtest.h"
 
+// This test assumes the following standard binaries are installed.
+static const char kBinBash[] = "/bin/bash";
+static const char kBinCp[] = "/bin/cp";
+static const char kBinEcho[] = "/bin/echo";
+static const char kBinFalse[] = "/bin/false";
+
 void CountCrash() {
   ADD_FAILURE();
 }
@@ -48,6 +55,81 @@
   ASSERT_TRUE(&logging_ == collector_.logger_);
 }
 
+TEST_F(CrashCollectorTest, WriteNewFile) {
+  FilePath test_file = test_dir_.Append("test_new");
+  const char kBuffer[] = "buffer";
+  EXPECT_EQ(strlen(kBuffer),
+            collector_.WriteNewFile(test_file,
+                                    kBuffer,
+                                    strlen(kBuffer)));
+  EXPECT_LT(collector_.WriteNewFile(test_file,
+                                    kBuffer,
+                                    strlen(kBuffer)), 0);
+}
+
+TEST_F(CrashCollectorTest, ForkExecAndPipe) {
+  std::vector<const char *> args;
+  char output_file[] = "test/fork_out";
+
+  // Test basic call with stdout.
+  args.clear();
+  args.push_back(kBinEcho);
+  args.push_back("hello world");
+  EXPECT_EQ(0, collector_.ForkExecAndPipe(args, output_file));
+  ExpectFileEquals("hello world\n", output_file);
+  EXPECT_EQ("", logging_.log());
+
+  // Test non-zero return value
+  logging_.clear();
+  args.clear();
+  args.push_back(kBinFalse);
+  EXPECT_EQ(1, collector_.ForkExecAndPipe(args, output_file));
+  ExpectFileEquals("", output_file);
+  EXPECT_EQ("", logging_.log());
+
+  // Test bad output_file.
+  EXPECT_EQ(127, collector_.ForkExecAndPipe(args, "/bad/path"));
+
+  // Test bad executable.
+  logging_.clear();
+  args.clear();
+  args.push_back("false");
+  EXPECT_EQ(127, collector_.ForkExecAndPipe(args, output_file));
+
+  // Test stderr captured.
+  std::string contents;
+  logging_.clear();
+  args.clear();
+  args.push_back(kBinCp);
+  EXPECT_EQ(1, collector_.ForkExecAndPipe(args, output_file));
+  EXPECT_TRUE(file_util::ReadFileToString(FilePath(output_file),
+                                                   &contents));
+  EXPECT_NE(std::string::npos, contents.find("missing file operand"));
+  EXPECT_EQ("", logging_.log());
+
+  // NULL parameter.
+  logging_.clear();
+  args.clear();
+  args.push_back(NULL);
+  EXPECT_EQ(-1, collector_.ForkExecAndPipe(args, output_file));
+  EXPECT_NE(std::string::npos,
+            logging_.log().find("Bad parameter"));
+
+  // No parameters.
+  args.clear();
+  EXPECT_EQ(127, collector_.ForkExecAndPipe(args, output_file));
+
+  // Segmentation faulting process.
+  logging_.clear();
+  args.clear();
+  args.push_back(kBinBash);
+  args.push_back("-c");
+  args.push_back("kill -SEGV $$");
+  EXPECT_EQ(-1, collector_.ForkExecAndPipe(args, output_file));
+  EXPECT_NE(std::string::npos,
+            logging_.log().find("Process did not exit normally"));
+}
+
 TEST_F(CrashCollectorTest, Sanitize) {
   EXPECT_EQ("chrome", collector_.Sanitize("chrome"));
   EXPECT_EQ("CHROME", collector_.Sanitize("CHROME"));
@@ -231,7 +313,8 @@
 }
 
 TEST_F(CrashCollectorTest, MetaData) {
-  FilePath meta_file = test_dir_.Append("generated.meta");
+  const char kMetaFileBasename[] = "generated.meta";
+  FilePath meta_file = test_dir_.Append(kMetaFileBasename);
   FilePath lsb_release = test_dir_.Append("lsb-release");
   FilePath payload_file = test_dir_.Append("payload-file");
   std::string contents;
@@ -247,12 +330,43 @@
   collector_.AddCrashMetaData("foo", "bar");
   collector_.WriteCrashMetaData(meta_file, "kernel", payload_file.value());
   EXPECT_TRUE(file_util::ReadFileToString(meta_file, &contents));
-  EXPECT_EQ("foo=bar\n"
-            "exec_name=kernel\n"
-            "ver=version\n"
-            "payload=test/payload-file\n"
-            "payload_size=3\n"
-            "done=1\n", contents);
+  const char kExpectedMeta[] =
+      "foo=bar\n"
+      "exec_name=kernel\n"
+      "ver=version\n"
+      "payload=test/payload-file\n"
+      "payload_size=3\n"
+      "done=1\n";
+  EXPECT_EQ(kExpectedMeta, contents);
+
+  // Test target of symlink is not overwritten.
+  payload_file = test_dir_.Append("payload2-file");
+  ASSERT_TRUE(
+      file_util::WriteFile(payload_file,
+                           kPayload, strlen(kPayload)));
+  FilePath meta_symlink_path = test_dir_.Append("symlink.meta");
+  ASSERT_EQ(0,
+            symlink(kMetaFileBasename,
+                    meta_symlink_path.value().c_str()));
+  ASSERT_TRUE(file_util::PathExists(meta_symlink_path));
+  logging_.clear();
+  collector_.WriteCrashMetaData(meta_symlink_path,
+                                "kernel",
+                                payload_file.value());
+  // Target metadata contents sould have stayed the same.
+  contents.clear();
+  EXPECT_TRUE(file_util::ReadFileToString(meta_file, &contents));
+  EXPECT_EQ(kExpectedMeta, contents);
+  EXPECT_NE(std::string::npos, logging_.log().find("Unable to write"));
+
+  // Test target of dangling symlink is not created.
+  file_util::Delete(meta_file, false);
+  ASSERT_FALSE(file_util::PathExists(meta_file));
+  logging_.clear();
+  collector_.WriteCrashMetaData(meta_symlink_path, "kernel",
+                                payload_file.value());
+  EXPECT_FALSE(file_util::PathExists(meta_file));
+  EXPECT_NE(std::string::npos, logging_.log().find("Unable to write"));
 }
 
 int main(int argc, char **argv) {