Moved the chaps database directory under /home/root.

This CL also includes code to migrate existing chaps data to the new
location.

BUG=chromium:212630
TEST=unit, manual

Change-Id: I026b21fc10d1df043e1478db9d7d66fc95116011
Reviewed-on: https://gerrit.chromium.org/gerrit/59961
Reviewed-by: Darren Krahn <dkrahn@chromium.org>
Tested-by: Darren Krahn <dkrahn@chromium.org>
Commit-Queue: Darren Krahn <dkrahn@chromium.org>
diff --git a/homedirs.cc b/homedirs.cc
index a794b99..8281a2d 100644
--- a/homedirs.cc
+++ b/homedirs.cc
@@ -555,11 +555,16 @@
 }
 
 namespace {
+  const char *kChapsDaemonName = "chaps";
   const char *kChapsDirName = ".chaps";
   const char *kChapsSaltName = "auth_data_salt";
 }
 
 FilePath HomeDirs::GetChapsTokenDir(const std::string& user) const {
+  return chromeos::cryptohome::home::GetDaemonPath(user, kChapsDaemonName);
+}
+
+FilePath HomeDirs::GetLegacyChapsTokenDir(const std::string& user) const {
   return chromeos::cryptohome::home::GetUserPath(user).Append(kChapsDirName);
 }
 
diff --git a/homedirs.h b/homedirs.h
index b4fda82..326567b 100644
--- a/homedirs.h
+++ b/homedirs.h
@@ -109,6 +109,9 @@
   // Returns the path to the user's chaps token directory.
   virtual FilePath GetChapsTokenDir(const std::string& username) const;
 
+  // Returns the path to the user's legacy chaps token directory.
+  virtual FilePath GetLegacyChapsTokenDir(const std::string& username) const;
+
   // Returns the path to the user's token salt.
   virtual FilePath GetChapsTokenSaltPath(const std::string& username) const;
 
diff --git a/mock_platform.cc b/mock_platform.cc
index cb2993a..defd540 100644
--- a/mock_platform.cc
+++ b/mock_platform.cc
@@ -6,7 +6,8 @@
 
 namespace cryptohome {
 
-MockPlatform::MockPlatform() : mock_enumerator_(new MockFileEnumerator()) {
+MockPlatform::MockPlatform()
+    : mock_enumerator_(new NiceMock<MockFileEnumerator>()) {
   ON_CALL(*this, GetOwnership(_, _, _))
       .WillByDefault(Invoke(this, &MockPlatform::MockGetOwnership));
   ON_CALL(*this, SetOwnership(_, _, _))
diff --git a/mock_platform.h b/mock_platform.h
index 48c8618..56c580a 100644
--- a/mock_platform.h
+++ b/mock_platform.h
@@ -14,6 +14,7 @@
 namespace cryptohome {
 
 using ::testing::_;
+using ::testing::NiceMock;
 using ::testing::Invoke;
 using ::testing::Return;
 
@@ -134,7 +135,7 @@
   MOCK_METHOD0(FirmwareWriteProtected, bool(void));
   MOCK_METHOD1(SyncFile, bool(const std::string&));
 
-  MockFileEnumerator* get_mock_enumerator() { return mock_enumerator_.get(); }
+  MockFileEnumerator* mock_enumerator() { return mock_enumerator_.get(); }
 
  private:
   bool MockGetOwnership(const std::string& path, uid_t* user_id,
@@ -164,7 +165,7 @@
                                         bool recursive,
                                         int file_type) {
     MockFileEnumerator* e = mock_enumerator_.release();
-    mock_enumerator_.reset(new MockFileEnumerator());
+    mock_enumerator_.reset(new NiceMock<MockFileEnumerator>());
     mock_enumerator_->entries_.assign(e->entries_.begin(), e->entries_.end());
     return e;
   }
diff --git a/mount.cc b/mount.cc
index b921f8f..75581d4 100644
--- a/mount.cc
+++ b/mount.cc
@@ -1245,28 +1245,44 @@
 }
 
 bool Mount::CheckChapsDirectory(const std::string& dir,
+                                const std::string& legacy_dir,
                                 bool* permissions_status) {
-  // If the Chaps database directory does not exist, create it.
-  DCHECK(permissions_status);
-  *permissions_status = true;
   // 0750: u + rwx, g + rx
   const mode_t chaps_permissions = S_IRWXU | S_IRGRP | S_IXGRP;
   const mode_t permissions_mask = S_IRWXU | S_IRWXG | S_IRWXO;
+
+  DCHECK(permissions_status);
+  *permissions_status = true;
+  // If the Chaps database directory does not exist, create it.
   if (!platform_->DirectoryExists(dir)) {
-    if (!platform_->CreateDirectory(dir)) {
-      LOG(ERROR) << "Failed to create " << dir;
-      *permissions_status = false;
-      return false;
-    }
-    if (!platform_->SetOwnership(dir, chaps_user_, default_access_group_)) {
-      LOG(ERROR) << "Couldn't set file ownership for " << dir;
-      *permissions_status = false;
-      return false;
-    }
-    if (!platform_->SetPermissions(dir, chaps_permissions)) {
-      LOG(ERROR) << "Couldn't set permissions for " << dir;
-      *permissions_status = false;
-      return false;
+    if (platform_->DirectoryExists(legacy_dir)) {
+      LOG(INFO) << "Moving chaps directory from " << legacy_dir << " to "
+                << dir;
+      if (!platform_->CopyWithPermissions(legacy_dir, dir)) {
+        *permissions_status = false;
+        return false;
+      }
+      if (!platform_->DeleteFile(legacy_dir, true)) {
+        PLOG(WARNING) << "Failed to clean up " << legacy_dir;
+        *permissions_status = false;
+        return false;
+      }
+    } else {
+      if (!platform_->CreateDirectory(dir)) {
+        LOG(ERROR) << "Failed to create " << dir;
+        *permissions_status = false;
+        return false;
+      }
+      if (!platform_->SetOwnership(dir, chaps_user_, default_access_group_)) {
+        LOG(ERROR) << "Couldn't set file ownership for " << dir;
+        *permissions_status = false;
+        return false;
+      }
+      if (!platform_->SetPermissions(dir, chaps_permissions)) {
+        LOG(ERROR) << "Couldn't set permissions for " << dir;
+        *permissions_status = false;
+        return false;
+      }
     }
     return true;
   }
@@ -1310,8 +1326,11 @@
   bool permissions_status = false;
   std::string username = current_user_->username();
   FilePath token_dir = homedirs_->GetChapsTokenDir(username);
+  FilePath legacy_token_dir = homedirs_->GetLegacyChapsTokenDir(username);
 
-  if (!CheckChapsDirectory(token_dir.value(), &permissions_status))
+  if (!CheckChapsDirectory(token_dir.value(),
+                           legacy_token_dir.value(),
+                           &permissions_status))
      return false;
   // We may create a salt file and, if so, we want to restrict access to it.
   ScopedUmask scoped_umask(platform_, kDefaultUmask);
diff --git a/mount.h b/mount.h
index bd48670..779b010 100644
--- a/mount.h
+++ b/mount.h
@@ -507,12 +507,16 @@
   // does not exist, it is created and initialzed with the correct
   // values. If the directory or its attributes cannot be checked,
   // set or created, a fatal error has occured and the function
-  // returns false.
+  // returns false.  If the directory does not exist and a legacy directory
+  // exists, the legacy directory will be moved to the new location.
   //
   // Parameters
   //   dir - directory to check
+  //   legacy_dir - legacy directory location
   //   permissions_check - set to false if permissions, uid or gid is incorrect
-  bool CheckChapsDirectory(const std::string& dir, bool* permissions_check);
+  bool CheckChapsDirectory(const std::string& dir,
+                           const std::string& legacy_dir,
+                           bool* permissions_check);
 
   // Ensures that the device policy is loaded.
   //
@@ -777,6 +781,7 @@
   FRIEND_TEST(MountTest,
               CheckChapsDirectoryCalledWithExistingDirWithFatalError);
   FRIEND_TEST(MountTest, CheckChapsDirectoryCalledWithExistingDir);
+  FRIEND_TEST(MountTest, CheckChapsDirectoryMigration);
 
   DISALLOW_COPY_AND_ASSIGN(Mount);
 };
diff --git a/mount_unittest.cc b/mount_unittest.cc
index a45938d..a7562f5 100644
--- a/mount_unittest.cc
+++ b/mount_unittest.cc
@@ -300,7 +300,8 @@
                             SetArgumentPointee<2>(shared_gid_),
                             Return(true)));
   bool permissions_status = false;
-  EXPECT_TRUE(mount->CheckChapsDirectory("/fake", &permissions_status));
+  EXPECT_TRUE(mount->CheckChapsDirectory("/fake", "/fake_legacy",
+                                         &permissions_status));
   EXPECT_TRUE(permissions_status);
 }
 
@@ -327,7 +328,8 @@
   EXPECT_CALL(platform, GetPermissions("/fake", _))
       .WillRepeatedly(DoAll(SetArgumentPointee<1>(bad_perms), Return(true)));
   bool permissions_status = false;
-  EXPECT_TRUE(mount->CheckChapsDirectory("/fake", &permissions_status));
+  EXPECT_TRUE(mount->CheckChapsDirectory("/fake", "/fake_legacy",
+                                         &permissions_status));
   EXPECT_FALSE(permissions_status);
 }
 
@@ -362,7 +364,8 @@
                             Return(true)));
 
   bool permissions_status = false;
-  EXPECT_TRUE(mount->CheckChapsDirectory("/fake", &permissions_status));
+  EXPECT_TRUE(mount->CheckChapsDirectory("/fake", "/fake_legacy",
+                                         &permissions_status));
   EXPECT_FALSE(permissions_status);
 }
 
@@ -393,7 +396,8 @@
                             SetArgumentPointee<2>(bad_gid),
                             Return(true)));
   bool permissions_status = false;
-  EXPECT_TRUE(mount->CheckChapsDirectory("/fake", &permissions_status));
+  EXPECT_TRUE(mount->CheckChapsDirectory("/fake", "/fake_legacy",
+                                         &permissions_status));
   EXPECT_FALSE(permissions_status);
 }
 
@@ -417,6 +421,8 @@
   EXPECT_TRUE(mount->Init());
   EXPECT_CALL(platform, DirectoryExists("/fake"))
       .WillRepeatedly(Return(false));
+  EXPECT_CALL(platform, DirectoryExists("/fake_legacy"))
+      .WillRepeatedly(Return(false));
   EXPECT_CALL(platform, CreateDirectory("/fake"))
       .WillOnce(Return(false))
       .WillRepeatedly(Return(true));
@@ -428,11 +434,14 @@
   EXPECT_CALL(platform, GetPermissions("/fake", _))
       .WillRepeatedly(Return(false));
   bool permissions_status = false;
-  EXPECT_FALSE(mount->CheckChapsDirectory("/fake", &permissions_status));
+  EXPECT_FALSE(mount->CheckChapsDirectory("/fake", "/fake_legacy",
+                                          &permissions_status));
   EXPECT_FALSE(permissions_status);
-  EXPECT_FALSE(mount->CheckChapsDirectory("/fake", &permissions_status));
+  EXPECT_FALSE(mount->CheckChapsDirectory("/fake", "/fake_legacy",
+                                          &permissions_status));
   EXPECT_FALSE(permissions_status);
-  EXPECT_FALSE(mount->CheckChapsDirectory("/fake", &permissions_status));
+  EXPECT_FALSE(mount->CheckChapsDirectory("/fake", "/fake_legacy",
+                                          &permissions_status));
   EXPECT_FALSE(permissions_status);
 }
 
@@ -462,12 +471,74 @@
   EXPECT_CALL(platform, GetOwnership("/fake", _, _))
       .WillRepeatedly(Return(false));
   bool permissions_status = false;
-  EXPECT_FALSE(mount->CheckChapsDirectory("/fake", &permissions_status));
+  EXPECT_FALSE(mount->CheckChapsDirectory("/fake", "/fake_legacy",
+                                          &permissions_status));
   EXPECT_FALSE(permissions_status);
-  EXPECT_FALSE(mount->CheckChapsDirectory("/fake", &permissions_status));
+  EXPECT_FALSE(mount->CheckChapsDirectory("/fake", "/fake_legacy",
+                                          &permissions_status));
   EXPECT_FALSE(permissions_status);
 }
 
+TEST_F(MountTest, CheckChapsDirectoryMigration) {
+  scoped_refptr<Mount> mount = new Mount();
+  NiceMock<MockPlatform> platform;
+  NiceMock<MockTpm> tpm;
+  mount->crypto()->set_tpm(&tpm);
+  mount->crypto()->set_platform(&platform);
+  mount->set_use_tpm(false);
+  mount->set_platform(&platform);
+
+  // Configure stub methods.
+  EXPECT_CALL(platform, Copy(_, _))
+      .WillRepeatedly(Return(true));
+  EXPECT_CALL(platform, DeleteFile(_, _))
+      .WillRepeatedly(Return(true));
+
+  // Stubs which will trigger the migration code path.
+  EXPECT_CALL(platform, DirectoryExists("/fake"))
+      .WillRepeatedly(Return(false));
+  EXPECT_CALL(platform, DirectoryExists("/fake_legacy"))
+      .WillRepeatedly(Return(true));
+
+  // Configure stat for the base directory.
+  struct stat base_stat = {0};
+  base_stat.st_mode = 040123;
+  base_stat.st_uid = 1;
+  base_stat.st_gid = 2;
+  EXPECT_CALL(platform, Stat(_, _))
+      .WillRepeatedly(DoAll(SetArgumentPointee<1>(base_stat), Return(true)));
+
+  // Configure a fake enumerator.
+  MockFileEnumerator* enumerator = platform.mock_enumerator();
+  enumerator->entries_.push_back("/fake_legacy/test_file1");
+  enumerator->entries_.push_back("test_file2");
+  FileEnumerator::FindInfo find_info1 = {{0}, "/fake_legacy/test_file1"};
+  find_info1.stat.st_mode = 0555;
+  find_info1.stat.st_uid = 3;
+  find_info1.stat.st_gid = 4;
+  FileEnumerator::FindInfo find_info2 = {{0}, "test_file2"};
+  find_info2.stat.st_mode = 0777;
+  find_info2.stat.st_uid = 5;
+  find_info2.stat.st_gid = 6;
+  EXPECT_CALL(*enumerator, GetFindInfo(_))
+      .WillOnce(SetArgumentPointee<0>(find_info1))
+      .WillOnce(SetArgumentPointee<0>(find_info2));
+
+  // These expectations will ensure the ownership and permissions are being
+  // correctly applied after the directory has been moved.
+  EXPECT_CALL(platform, SetOwnership("/fake/test_file1", 3, 4)).Times(1);
+  EXPECT_CALL(platform, SetPermissions("/fake/test_file1", 0555)).Times(1);
+  EXPECT_CALL(platform, SetOwnership("/fake/test_file2", 5, 6)).Times(1);
+  EXPECT_CALL(platform, SetPermissions("/fake/test_file2", 0777)).Times(1);
+  EXPECT_CALL(platform, SetOwnership("/fake", 1, 2)).Times(1);
+  EXPECT_CALL(platform, SetPermissions("/fake", 0123)).Times(1);
+
+  bool permissions_status = false;
+  EXPECT_TRUE(mount->CheckChapsDirectory("/fake", "/fake_legacy",
+                                         &permissions_status));
+  EXPECT_TRUE(permissions_status);
+}
+
 TEST_F(MountTest, CreateCryptohomeTest) {
   InsertTestUsers(&kDefaultUsers[5], 1);
   // creates a cryptohome and tests credentials
diff --git a/platform.cc b/platform.cc
index 9b43439..95f9b55 100644
--- a/platform.cc
+++ b/platform.cc
@@ -43,6 +43,27 @@
 using base::SplitString;
 using std::string;
 
+namespace {
+
+class ScopedPath {
+ public:
+  ScopedPath(cryptohome::Platform* platform, const string& dir)
+      : platform_(platform), dir_(dir) {}
+  ~ScopedPath() {
+    if (!dir_.empty() && !platform_->DeleteFile(dir_, true)) {
+      PLOG(WARNING) << "Failed to clean up " << dir_;
+    }
+  }
+  void release() {
+    dir_.clear();
+  }
+ private:
+  cryptohome::Platform* platform_;
+  string dir_;
+};
+
+}  // namespace
+
 namespace cryptohome {
 
 const int kDefaultMountOptions = MS_NOEXEC | MS_NOSUID | MS_NODEV;
@@ -544,6 +565,71 @@
   return file_util::CopyDirectory(from_path, to_path, true);
 }
 
+bool Platform::CopyWithPermissions(const std::string& from_path,
+                                   const std::string& to_path) {
+  // Save stat for every file.
+  std::vector<FileEnumerator::FindInfo> entry_list;
+  FileEnumerator::FindInfo base_entry_info;
+  base_entry_info.filename = from_path;
+  if (!Stat(from_path, &base_entry_info.stat)) {
+    PLOG(ERROR) << "Failed to stat " << from_path;
+    return false;
+  }
+  entry_list.push_back(base_entry_info);
+  if (FileEnumerator::IsDirectory(base_entry_info)) {
+    int file_types = FileEnumerator::FILES | FileEnumerator::DIRECTORIES;
+    scoped_ptr<FileEnumerator> file_enumerator(
+        GetFileEnumerator(from_path, true, file_types));
+    string entry_path;
+    while (!(entry_path = file_enumerator->Next()).empty()) {
+      FileEnumerator::FindInfo entry_info;
+      file_enumerator->GetFindInfo(&entry_info);
+      // Keep the full path.
+      entry_info.filename = entry_path;
+      entry_list.push_back(entry_info);
+    }
+  }
+
+  if (!Copy(from_path, to_path)) {
+    PLOG(ERROR) << "Failed to copy " << from_path;
+    return false;
+  }
+
+  // If something goes wrong we want to blow away the half-baked path.
+  ScopedPath scoped_new_path(this, to_path);
+
+  // Unfortunately, ownership and permissions are not always retained.
+  // Apply the old ownership / permissions on a per-file basis.
+  const FilePath new_base(to_path);
+  const FilePath old_base(from_path);
+  for (size_t i = 0; i < entry_list.size(); ++i) {
+    FilePath old_path(entry_list[i].filename);
+    FilePath new_path(new_base);
+    if (old_path != old_base) {
+      if (old_path.IsAbsolute()) {
+        DCHECK(old_base.AppendRelativePath(old_path, &new_path));
+      } else {
+        new_path = new_base.Append(old_path);
+      }
+    }
+    if (!SetOwnership(new_path.value(),
+                      entry_list[i].stat.st_uid,
+                      entry_list[i].stat.st_gid)) {
+      PLOG(ERROR) << "Failed to set ownership for " << new_path.value();
+      return false;
+    }
+    const mode_t permissions_mask = 07777;
+    if (!SetPermissions(new_path.value(),
+                        entry_list[i].stat.st_mode & permissions_mask)) {
+      PLOG(ERROR) << "Failed to set permissions for " << new_path.value();
+      return false;
+    }
+  }
+  // The copy is done, keep the new path.
+  scoped_new_path.release();
+  return true;
+}
+
 bool Platform::StatVFS(const std::string& path, struct statvfs* vfs) {
   return statvfs(path.c_str(), vfs) == 0;
 }
diff --git a/platform.h b/platform.h
index 3e317bd..36f987e 100644
--- a/platform.h
+++ b/platform.h
@@ -295,6 +295,10 @@
   // Copies from to to.
   virtual bool Copy(const std::string& from, const std::string& to);
 
+  // Copies and retains permissions and ownership.
+  virtual bool CopyWithPermissions(const std::string& from,
+                                   const std::string& to);
+
   // Moves a given path on the filesystem
   //
   // Parameters