Turn on utf8 flag with necessary to support non-ASCII file names on certain file systems.

BUG=chromium-os:15358
TEST=Checked that non-ASCII file names are shown correctly in file browser.

Change-Id: Idce832dc75c6d9e052be79e4f30a3c07fb56dbb2
Reviewed-on: http://gerrit.chromium.org/gerrit/3265
Reviewed-by: Darin Petkov <petkov@chromium.org>
Reviewed-by: Ben Chan <benchan@chromium.org>
Tested-by: Ben Chan <benchan@chromium.org>
diff --git a/disk-manager.cc b/disk-manager.cc
index e9d57f3..55c1710 100644
--- a/disk-manager.cc
+++ b/disk-manager.cc
@@ -6,6 +6,7 @@
 #include "disk.h"
 #include "udev-device.h"
 
+#include <base/basictypes.h>
 #include <base/logging.h>
 #include <base/scoped_ptr.h>
 #include <base/string_number_conversions.h>
@@ -17,7 +18,6 @@
 #include <unistd.h>
 #include <algorithm>
 #include <fstream>
-#include <sstream>
 #include <vector>
 
 
@@ -39,11 +39,13 @@
 static const char kFallbackMountPath[] = "/media/disk";
 static const unsigned kMaxNumMountTrials = 10000;
 static const unsigned kFallbackPasswordBufferSize = 16384;
+static const char* const kFilesystemsWithUTF8Flag[] = {
+  "vfat", "ntfs", "iso9660", "udf",
+};
 static const char* const kFilesystemsWithMountUserAndGroupId[] = {
   "fat", "vfat", "msdos", "ntfs",
   "iso9660", "udf",
   "hfs", "hfsplus", "hpfs",
-  NULL
 };
 
 DiskManager::DiskManager()
@@ -358,31 +360,51 @@
   return true;
 }
 
-bool DiskManager::IsMountUserAndGroupIdSupportedByFilesystem(
-    const std::string& filesystem_type) const {
-  for (const char* const* filesystem = kFilesystemsWithMountUserAndGroupId;
-      *filesystem; ++filesystem) {
-    if (strcmp(filesystem_type.c_str(), *filesystem) == 0) {
+bool DiskManager::IsStringInStringArray(const std::string& str,
+    const char* const* str_array, size_t str_array_len) const {
+  for (size_t i = 0; i < str_array_len; ++i) {
+    if (str == str_array[i]) {
       return true;
     }
   }
   return false;
 }
 
+bool DiskManager::IsMountUserAndGroupIdSupportedByFilesystem(
+    const std::string& filesystem_type) const {
+  return IsStringInStringArray(filesystem_type,
+      kFilesystemsWithMountUserAndGroupId,
+      arraysize(kFilesystemsWithMountUserAndGroupId));
+}
+
+bool DiskManager::IsUTF8FlagSupportedByFilesystem(
+    const std::string& filesystem_type) const {
+  return IsStringInStringArray(filesystem_type,
+      kFilesystemsWithUTF8Flag,
+      arraysize(kFilesystemsWithUTF8Flag));
+}
+
 std::string DiskManager::ModifyMountOptionsForFilesystem(
     const std::string& filesystem_type, const std::string& options) const {
-  std::stringstream extended_options(options);
+  std::vector<std::string> extended_options;
+  if (!options.empty()) {
+    extended_options.push_back(options);
+  }
   if (IsMountUserAndGroupIdSupportedByFilesystem(filesystem_type)) {
     uid_t uid;
     gid_t gid;
     if (GetUserAndGroupId(kMountDefaultUser, &uid, &gid)) {
-      if (!options.empty()) {
-        extended_options << ",";
-      }
-      extended_options << "uid=" << uid << ",gid=" << gid;
+      extended_options.push_back(StringPrintf("uid=%d", uid));
+      extended_options.push_back(StringPrintf("gid=%d", gid));
     }
   }
-  return extended_options.str();
+  if (IsUTF8FlagSupportedByFilesystem(filesystem_type)) {
+    extended_options.push_back("utf8");
+  }
+  if (filesystem_type == "vfat") {
+    extended_options.push_back("shortname=mixed");
+  }
+  return JoinString(extended_options, ',');
 }
 
 bool DiskManager::DoMount(const std::string& device_file,
diff --git a/disk-manager.h b/disk-manager.h
index 96c8732..eef2de6 100644
--- a/disk-manager.h
+++ b/disk-manager.h
@@ -59,10 +59,18 @@
   // /proc/filesystems.
   std::vector<std::string> GetFilesystems(std::istream& stream) const;
 
+  // Returns true if a string is found in an array of strings.
+  bool IsStringInStringArray(const std::string& str,
+      const char* const* str_array, size_t str_array_len) const;
+
   // Checks if a filesystem supports user ID and group ID in mount options.
   bool IsMountUserAndGroupIdSupportedByFilesystem(
       const std::string& filesystem_type) const;
 
+  // Checks if a filesystem supports the utf8 flag in mount options.
+  bool IsUTF8FlagSupportedByFilesystem(
+      const std::string& filesystem_type) const;
+
   // Gets the user ID and group ID for a given username.
   bool GetUserAndGroupId(const std::string& username,
       uid_t *uid, gid_t *gid) const;
diff --git a/disk-manager_unittest.cc b/disk-manager_unittest.cc
index 9866c2a..15ae749 100644
--- a/disk-manager_unittest.cc
+++ b/disk-manager_unittest.cc
@@ -3,6 +3,7 @@
 // found in the LICENSE file.
 
 #include <base/logging.h>
+#include <base/string_util.h>
 #include <gtest/gtest.h>
 
 #include <sys/mount.h>
@@ -94,6 +95,20 @@
   }
 }
 
+TEST_F(DiskManagerTest, IsStringInStringArray) {
+  DiskManager manager;
+  const char* str_array[] = {
+    "this", "is", "a", "string", "array"
+  };
+  size_t len = arraysize(str_array);
+  EXPECT_TRUE(manager.IsStringInStringArray("this", str_array, len));
+  EXPECT_TRUE(manager.IsStringInStringArray("is", str_array, len));
+  EXPECT_TRUE(manager.IsStringInStringArray("a", str_array, len));
+  EXPECT_TRUE(manager.IsStringInStringArray("string", str_array, len));
+  EXPECT_TRUE(manager.IsStringInStringArray("array", str_array, len));
+  EXPECT_FALSE(manager.IsStringInStringArray("nonexistent", str_array, len));
+}
+
 TEST_F(DiskManagerTest, IsMountUserAndGroupIdSupportedByFilesystem) {
   DiskManager manager;
   EXPECT_FALSE(manager.IsMountUserAndGroupIdSupportedByFilesystem("ext2"));
@@ -110,6 +125,59 @@
   EXPECT_TRUE(manager.IsMountUserAndGroupIdSupportedByFilesystem("hpfs"));
 }
 
+TEST_F(DiskManagerTest, IsUTF8FlagSupportedByFilesystem) {
+  DiskManager manager;
+  EXPECT_FALSE(manager.IsUTF8FlagSupportedByFilesystem("ext2"));
+  EXPECT_FALSE(manager.IsUTF8FlagSupportedByFilesystem("ext3"));
+  EXPECT_FALSE(manager.IsUTF8FlagSupportedByFilesystem("ext4"));
+  EXPECT_FALSE(manager.IsUTF8FlagSupportedByFilesystem("fat"));
+  EXPECT_TRUE(manager.IsUTF8FlagSupportedByFilesystem("vfat"));
+  EXPECT_FALSE(manager.IsUTF8FlagSupportedByFilesystem("msdos"));
+  EXPECT_TRUE(manager.IsUTF8FlagSupportedByFilesystem("ntfs"));
+  EXPECT_TRUE(manager.IsUTF8FlagSupportedByFilesystem("iso9660"));
+  EXPECT_TRUE(manager.IsUTF8FlagSupportedByFilesystem("udf"));
+  EXPECT_FALSE(manager.IsUTF8FlagSupportedByFilesystem("hfs"));
+  EXPECT_FALSE(manager.IsUTF8FlagSupportedByFilesystem("hfsplus"));
+  EXPECT_FALSE(manager.IsUTF8FlagSupportedByFilesystem("hpfs"));
+}
+
+TEST_F(DiskManagerTest, ModifyMountOptionsForFilesystem) {
+  DiskManager manager;
+  uid_t uid;
+  gid_t gid;
+  bool found_uid_gid = manager.GetUserAndGroupId("chronos", &uid, &gid);
+
+  std::string expected_options, modified_options;
+
+  // Test the case when both the uid/gid, utf8 and shortname options are added
+  expected_options = (found_uid_gid) ?
+    StringPrintf("ro,uid=%d,gid=%d,utf8,shortname=mixed", uid, gid) :
+    "ro,utf8,shortname=mixed";
+  modified_options = manager.ModifyMountOptionsForFilesystem("vfat", "ro");
+  EXPECT_EQ(expected_options, modified_options);
+
+  // Test the case when both the uid/gid and utf8 options are added
+  expected_options = (found_uid_gid) ?
+    StringPrintf("ro,uid=%d,gid=%d,utf8", uid, gid) : "ro,utf8";
+  modified_options = manager.ModifyMountOptionsForFilesystem("udf", "ro");
+  EXPECT_EQ(expected_options, modified_options);
+
+  // Test the case when only the uid/gid option is added
+  expected_options = (found_uid_gid) ?
+    StringPrintf("ro,uid=%d,gid=%d", uid, gid) : "ro";
+  modified_options = manager.ModifyMountOptionsForFilesystem("fat", "ro");
+  EXPECT_EQ(expected_options, modified_options);
+
+  // Test the case when no option is added
+  expected_options = "ro";
+  modified_options = manager.ModifyMountOptionsForFilesystem("ext2", "ro");
+  EXPECT_EQ(expected_options, modified_options);
+
+  expected_options = "";
+  modified_options = manager.ModifyMountOptionsForFilesystem("ext2", "");
+  EXPECT_EQ(expected_options, modified_options);
+}
+
 TEST_F(DiskManagerTest, GetUserAndGroupIdForRoot) {
   DiskManager manager;
   uid_t uid;