Disable automatic update checks if booting from a removable device.

BUG=5335
TEST=unit tests, gmerged to hard-drive as well as USB

Review URL: http://codereview.chromium.org/2868105
diff --git a/main.cc b/main.cc
index 9e7a1ef..8165c14 100644
--- a/main.cc
+++ b/main.cc
@@ -49,7 +49,12 @@
 
 void SchedulePeriodicUpdateChecks(UpdateAttempter* update_attempter) {
   if (!utils::IsOfficialBuild()) {
-    LOG(WARNING) << "No periodic update checks on non-official builds.";
+    LOG(WARNING) << "Non-official build: periodic update checks disabled.";
+    return;
+  }
+
+  if (utils::IsRemovableDevice(utils::RootDevice(utils::BootDevice()))) {
+    LOG(WARNING) << "Removable device boot: periodic update checks disabled.";
     return;
   }
 
diff --git a/utils.cc b/utils.cc
index d691739..05acc15 100644
--- a/utils.cc
+++ b/utils.cc
@@ -3,6 +3,7 @@
 // found in the LICENSE file.
 
 #include "update_engine/utils.h"
+
 #include <sys/mount.h>
 #include <sys/stat.h>
 #include <sys/types.h>
@@ -13,7 +14,12 @@
 #include <stdlib.h>
 #include <string.h>
 #include <unistd.h>
+
 #include <algorithm>
+
+#include "base/file_path.h"
+#include "base/file_util.h"
+#include "base/string_util.h"
 #include "chromeos/obsolete_logging.h"
 #include "update_engine/file_writer.h"
 #include "update_engine/omaha_request_params.h"
@@ -207,7 +213,10 @@
 }
 
 string RootDevice(const string& partition_device) {
-  CHECK(!partition_device.empty());
+  FilePath device_path(partition_device);
+  if (device_path.DirName().value() != "/dev") {
+    return "";
+  }
   string::const_iterator it = --partition_device.end();
   for (; it >= partition_device.begin(); --it) {
     if (!isdigit(*it))
@@ -230,6 +239,26 @@
   return string(it + 1, partition_device.end());
 }
 
+string SysfsBlockDevice(const string& device) {
+  FilePath device_path(device);
+  if (device_path.DirName().value() != "/dev") {
+    return "";
+  }
+  return FilePath("/sys/block").Append(device_path.BaseName()).value();
+}
+
+bool IsRemovableDevice(const std::string& device) {
+  string sysfs_block = SysfsBlockDevice(device);
+  string removable;
+  if (sysfs_block.empty() ||
+      !file_util::ReadFileToString(FilePath(sysfs_block).Append("removable"),
+                                   &removable)) {
+    return false;
+  }
+  TrimWhitespaceASCII(removable, TRIM_ALL, &removable);
+  return removable == "1";
+}
+
 std::string ErrnoNumberAsString(int err) {
   char buf[100];
   buf[0] = '\0';
diff --git a/utils.h b/utils.h
index f7f734e..6ce4cf3 100644
--- a/utils.h
+++ b/utils.h
@@ -84,13 +84,24 @@
 bool RecursiveUnlinkDir(const std::string& path);
 
 // Returns the root device for a partition. For example,
-// RootDevice("/dev/sda3") returns "/dev/sda".
+// RootDevice("/dev/sda3") returns "/dev/sda". Returns an empty string
+// if the input device is not of the "/dev/xyz" form.
 std::string RootDevice(const std::string& partition_device);
 
 // Returns the partition number, as a string, of partition_device. For example,
-// PartitionNumber("/dev/sda3") return "3".
+// PartitionNumber("/dev/sda3") returns "3".
 std::string PartitionNumber(const std::string& partition_device);
 
+// Returns the sysfs block device for a root block device. For
+// example, SysfsBlockDevice("/dev/sda") returns
+// "/sys/block/sda". Returns an empty string if the input device is
+// not of the "/dev/xyz" form.
+std::string SysfsBlockDevice(const std::string& device);
+
+// Returns true if the root |device| (e.g., "/dev/sdb") is known to be
+// removable, false otherwise.
+bool IsRemovableDevice(const std::string& device);
+
 // Synchronously mount or unmount a filesystem. Return true on success.
 // Mounts as ext3 with default options.
 bool MountFilesystem(const std::string& device, const std::string& mountpoint,
diff --git a/utils_unittest.cc b/utils_unittest.cc
index b67114a..575d91f 100644
--- a/utils_unittest.cc
+++ b/utils_unittest.cc
@@ -133,6 +133,23 @@
 TEST(UtilsTest, RootDeviceTest) {
   EXPECT_EQ("/dev/sda", utils::RootDevice("/dev/sda3"));
   EXPECT_EQ("/dev/mmc0", utils::RootDevice("/dev/mmc0p3"));
+  EXPECT_EQ("", utils::RootDevice("/dev/foo/bar"));
+  EXPECT_EQ("", utils::RootDevice("/"));
+  EXPECT_EQ("", utils::RootDevice(""));
+}
+
+TEST(UtilsTest, SysfsBlockDeviceTest) {
+  EXPECT_EQ("/sys/block/sda", utils::SysfsBlockDevice("/dev/sda"));
+  EXPECT_EQ("", utils::SysfsBlockDevice("/foo/sda"));
+  EXPECT_EQ("", utils::SysfsBlockDevice("/dev/foo/bar"));
+  EXPECT_EQ("", utils::SysfsBlockDevice("/"));
+  EXPECT_EQ("", utils::SysfsBlockDevice("./"));
+  EXPECT_EQ("", utils::SysfsBlockDevice(""));
+}
+
+TEST(UtilsTest, IsRemovableDeviceTest) {
+  EXPECT_FALSE(utils::IsRemovableDevice(""));
+  EXPECT_FALSE(utils::IsRemovableDevice("/dev/non-existent-device"));
 }
 
 TEST(UtilsTest, PartitionNumberTest) {