cryptohome: don't clean up stale mount at first time

There wouldn't have any stale mount when the cryptohome first start up.
Didn't need to do the clean up.

BUG=chromium:879797
TEST=FEATURES=test emerge-soraka cryptohome
TEST=tast run $DUT hwsec.Cryptohome*
TEST=Manually enroll, login, logout on soraka

Change-Id: I70118694b7f407fa76a96a192c05771f80d736e7
Reviewed-on: https://chromium-review.googlesource.com/c/chromiumos/platform2/+/2498165
Tested-by: joe Chou <yich@google.com>
Commit-Queue: joe Chou <yich@google.com>
Reviewed-by: Leo Lai <cylai@google.com>
diff --git a/cryptohome/userdataauth.cc b/cryptohome/userdataauth.cc
index 813b3cc..87775b2 100644
--- a/cryptohome/userdataauth.cc
+++ b/cryptohome/userdataauth.cc
@@ -45,6 +45,7 @@
 namespace cryptohome {
 
 constexpr char kMountThreadName[] = "MountThread";
+constexpr char kNotFirstBootFilePath[] = "/run/cryptohome/not_first_boot";
 
 namespace {
 // Some utility functions used by UserDataAuth.
@@ -270,13 +271,17 @@
     mount_thread_.StartWithOptions(options);
   }
 
-  // Clean up any unreferenced mountpoints at startup.
-  PostTaskToMountThread(FROM_HERE,
-                        base::BindOnce(
-                            [](UserDataAuth* userdataauth) {
-                              userdataauth->CleanUpStaleMounts(false);
-                            },
-                            base::Unretained(this)));
+  if (platform_->FileExists(base::FilePath(kNotFirstBootFilePath))) {
+    // Clean up any unreferenced mountpoints at startup.
+    PostTaskToMountThread(FROM_HERE,
+                          base::BindOnce(
+                              [](UserDataAuth* userdataauth) {
+                                userdataauth->CleanUpStaleMounts(false);
+                              },
+                              base::Unretained(this)));
+  } else {
+    platform_->TouchFileDurable(base::FilePath(kNotFirstBootFilePath));
+  }
 
   // We expect |tpm_| and |tpm_init_| to be available by this point.
   DCHECK(tpm_ && tpm_init_);
diff --git a/cryptohome/userdataauth_unittest.cc b/cryptohome/userdataauth_unittest.cc
index 5d11caa..ccbf38e 100644
--- a/cryptohome/userdataauth_unittest.cc
+++ b/cryptohome/userdataauth_unittest.cc
@@ -1564,6 +1564,7 @@
   MockMount* mount = new MockMount();
   EXPECT_CALL(mount_factory, New()).WillOnce(Return(mount));
   userdataauth_->set_mount_factory(&mount_factory);
+  EXPECT_CALL(platform_, FileExists(_)).WillOnce(Return(true));
   EXPECT_CALL(platform_, GetMountsBySourcePrefix(_, _)).WillOnce(Return(false));
   EXPECT_CALL(platform_, GetAttachedLoopDevices())
       .WillRepeatedly(Return(std::vector<Platform::LoopDevice>()));
@@ -1648,6 +1649,100 @@
   EXPECT_TRUE(userdataauth_->CleanUpStaleMounts(false));
 }
 
+TEST_F(UserDataAuthTest,
+       CleanUpStale_FilledMap_NoOpenFiles_ShadowOnly_FirstBoot) {
+  // Checks that when we have a bunch of stale shadow mounts, some active
+  // mounts, and no open filehandles, all inactive mounts are unmounted.
+
+  // ownership handed off to the Service MountMap
+  MockMountFactory mount_factory;
+  MockMount* mount = new MockMount();
+  EXPECT_CALL(mount_factory, New()).WillOnce(Return(mount));
+  userdataauth_->set_mount_factory(&mount_factory);
+  EXPECT_CALL(platform_, FileExists(_)).WillOnce(Return(false));
+  EXPECT_CALL(platform_, GetMountsBySourcePrefix(_, _)).Times(0);
+  EXPECT_CALL(platform_, GetAttachedLoopDevices()).Times(0);
+  EXPECT_CALL(platform_, GetLoopDeviceMounts(_)).Times(0);
+  ASSERT_TRUE(userdataauth_->Initialize());
+
+  EXPECT_CALL(lockbox_, FinalizeBoot());
+  EXPECT_CALL(*mount, Init(&platform_, &crypto_, _)).WillOnce(Return(true));
+  EXPECT_CALL(homedirs_, CryptohomeExists(_)).WillOnce(Return(true));
+  EXPECT_CALL(*mount, MountCryptohome(_, _, _, _)).WillOnce(Return(true));
+  EXPECT_CALL(*mount, UpdateCurrentUserActivityTimestamp(_, _))
+      .WillOnce(Return(true));
+  EXPECT_CALL(platform_, GetMountsBySourcePrefix(_, _)).WillOnce(Return(false));
+  EXPECT_CALL(platform_, GetAttachedLoopDevices())
+      .WillRepeatedly(Return(std::vector<Platform::LoopDevice>()));
+  EXPECT_CALL(platform_, GetLoopDeviceMounts(_)).WillOnce(Return(false));
+
+  user_data_auth::MountRequest mount_req;
+  mount_req.mutable_account()->set_account_id("foo@bar.net");
+  mount_req.mutable_authorization()->mutable_key()->set_secret("key");
+  mount_req.mutable_authorization()->mutable_key()->mutable_data()->set_label(
+      "password");
+  mount_req.mutable_create()->set_copy_authorization_key(true);
+  bool mount_done = false;
+  userdataauth_->DoMount(
+      mount_req,
+      base::Bind(
+          [](bool* mount_done_ptr, const user_data_auth::MountReply& reply) {
+            EXPECT_EQ(user_data_auth::CRYPTOHOME_ERROR_NOT_SET, reply.error());
+            *mount_done_ptr = true;
+          },
+          base::Unretained(&mount_done)));
+  ASSERT_EQ(TRUE, mount_done);
+
+  EXPECT_CALL(platform_, GetMountsBySourcePrefix(_, _))
+      .Times(4)
+      .WillRepeatedly(Invoke(StaleShadowMounts));
+  EXPECT_CALL(platform_, GetAttachedLoopDevices())
+      .WillRepeatedly(Return(std::vector<Platform::LoopDevice>()));
+  EXPECT_CALL(platform_, GetLoopDeviceMounts(_)).WillOnce(Return(false));
+  EXPECT_CALL(
+      platform_,
+      EnumerateDirectoryEntries(
+          FilePath(kEphemeralCryptohomeDir).Append(kSparseFileDir), _, _))
+      .WillOnce(Return(false));
+  // Only 5 look ups: user/1 and root/1 are owned, children of these
+  // directories are excluded.
+  EXPECT_CALL(platform_, GetProcessesWithOpenFiles(_, _)).Times(5);
+
+  EXPECT_CALL(*mount, OwnsMountPoint(_)).WillRepeatedly(Return(false));
+  EXPECT_CALL(*mount, OwnsMountPoint(FilePath("/home/user/1")))
+      .WillOnce(Return(true));
+  EXPECT_CALL(*mount, OwnsMountPoint(FilePath("/home/root/1")))
+      .WillOnce(Return(true));
+
+  EXPECT_CALL(platform_,
+              Unmount(Property(&FilePath::value, EndsWith("/0")), true, _))
+      .Times(2)
+      .WillRepeatedly(Return(true));
+  EXPECT_CALL(platform_, Unmount(FilePath("/home/chronos/user"), true, _))
+      .WillOnce(Return(true));
+  EXPECT_CALL(platform_, Unmount(Property(&FilePath::value,
+                                          EndsWith("user/MyFiles/Downloads")),
+                                 true, _))
+      .WillOnce(Return(true));
+  EXPECT_CALL(platform_, Unmount(FilePath("/daemon-store/server/a"), true, _))
+      .WillOnce(Return(true));
+
+  std::vector<std::string> fake_token_list;
+  fake_token_list.push_back("/home/chronos/user/token");
+  fake_token_list.push_back("/home/user/1/token");
+  fake_token_list.push_back("/home/root/1/token");
+  EXPECT_CALL(chaps_client_, GetTokenList(_, _))
+      .WillRepeatedly(DoAll(SetArgPointee<1>(fake_token_list), Return(true)));
+
+  EXPECT_CALL(chaps_client_,
+              UnloadToken(_, FilePath("/home/chronos/user/token")))
+      .Times(1);
+
+  // Expect that CleanUpStaleMounts() tells us it skipped mounts since 1 is
+  // still logged in.
+  EXPECT_TRUE(userdataauth_->CleanUpStaleMounts(false));
+}
+
 TEST_F(UserDataAuthTest, StartMigrateToDircryptoValidity) {
   constexpr char kUsername1[] = "foo@gmail.com";