1// Copyright 2013 The Chromium Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5#include "chrome/browser/sxs_linux.h"
6
7#include <vector>
8
9#include "base/command_line.h"
10#include "base/logging.h"
11#include "base/file_util.h"
12#include "base/files/file_path.h"
13#include "base/files/important_file_writer.h"
14#include "base/path_service.h"
15#include "base/strings/string_split.h"
16#include "base/strings/string_util.h"
17#include "chrome/common/chrome_paths.h"
18#include "chrome/common/chrome_result_codes.h"
19#include "chrome/common/chrome_switches.h"
20#include "chrome/common/chrome_version_info.h"
21#include "content/public/browser/browser_thread.h"
22
23namespace {
24
25const char kChannelsFileName[] = "Channels";
26
27std::string GetChannelMarkForThisExecutable() {
28  std::string product_channel_name;
29  chrome::VersionInfo::Channel product_channel(
30      chrome::VersionInfo::GetChannel());
31  switch (product_channel) {
32    case chrome::VersionInfo::CHANNEL_UNKNOWN: {
33      // Add the channel mark even for Chromium builds (which do not have
34      // channel) to better handle possibility of users using Chromium builds
35      // with their Google Chrome profiles. Include version string modifier
36      // as additional piece of information for debugging (it can't make
37      // a meaningful difference for the code since unknown does not match any
38      // real channel name).
39      std::string version_string_modifier(
40          chrome::VersionInfo::GetVersionStringModifier());
41      product_channel_name = "unknown (" + version_string_modifier + ")";
42      break;
43    }
44    case chrome::VersionInfo::CHANNEL_CANARY:
45      product_channel_name = "canary";
46      break;
47    case chrome::VersionInfo::CHANNEL_DEV:
48      product_channel_name = "dev";
49      break;
50    case chrome::VersionInfo::CHANNEL_BETA:
51      product_channel_name = "beta";
52      break;
53    case chrome::VersionInfo::CHANNEL_STABLE:
54      product_channel_name = "stable";
55      break;
56    // Rely on -Wswitch compiler warning to detect unhandled enum values.
57  }
58
59  return product_channel_name;
60}
61
62bool DoAddChannelMarkToUserDataDir(const base::FilePath& user_data_dir) {
63  std::string product_channel_name(GetChannelMarkForThisExecutable());
64  base::FilePath channels_path(user_data_dir.AppendASCII(kChannelsFileName));
65  std::vector<std::string> user_data_dir_channels;
66
67  // Note: failure to read the channels file is not fatal. It's possible
68  // and legitimate that it doesn't exist, e.g. for new profile or for profile
69  // existing before channel marks have been introduced.
70  std::string channels_contents;
71  if (base::ReadFileToString(channels_path, &channels_contents))
72    base::SplitString(channels_contents, '\n', &user_data_dir_channels);
73
74  if (std::find(user_data_dir_channels.begin(),
75                user_data_dir_channels.end(),
76                product_channel_name) != user_data_dir_channels.end()) {
77    // No need to do further disk writes if our channel mark is already present.
78    return true;
79  }
80
81  user_data_dir_channels.push_back(product_channel_name);
82  return base::ImportantFileWriter::WriteFileAtomically(
83      channels_path,
84      JoinString(user_data_dir_channels, "\n"));
85}
86
87}  // namespace
88
89namespace sxs_linux {
90
91void AddChannelMarkToUserDataDir() {
92  DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::FILE));
93  base::FilePath user_data_dir;
94  if (!PathService::Get(chrome::DIR_USER_DATA, &user_data_dir)) {
95    LOG(ERROR) << "Failed to get user data dir path. The profile will not be "
96               << "automatically migrated for updated Linux packages.";
97    return;
98  }
99
100  if (!DoAddChannelMarkToUserDataDir(user_data_dir)) {
101    LOG(ERROR) << "Failed to add channel mark to the user data dir ("
102               << user_data_dir.value() << "). This profile will not be "
103               << "automatically migrated for updated Linux packages.";
104  }
105}
106
107bool ShouldMigrateUserDataDir() {
108  return CommandLine::ForCurrentProcess()->HasSwitch(
109      switches::kMigrateDataDirForSxS);
110}
111
112int MigrateUserDataDir() {
113  DCHECK(ShouldMigrateUserDataDir());
114
115  base::FilePath source_path;
116  if (!PathService::Get(chrome::DIR_USER_DATA, &source_path)) {
117    LOG(ERROR) << "Failed to get value of chrome::DIR_USER_DATA";
118    return chrome::RESULT_CODE_SXS_MIGRATION_FAILED;
119  }
120
121  base::FilePath channels_path(source_path.AppendASCII(kChannelsFileName));
122
123  std::string channels_contents;
124  if (!base::ReadFileToString(channels_path, &channels_contents)) {
125    LOG(WARNING) << "Failed to read channels file.";
126    return chrome::RESULT_CODE_SXS_MIGRATION_FAILED;
127  }
128
129  std::vector<std::string> user_data_dir_channels;
130  base::SplitString(channels_contents, '\n', &user_data_dir_channels);
131
132  if (user_data_dir_channels.size() != 1) {
133    LOG(WARNING) << "User data dir migration is only possible when the profile "
134                 << "is only used with a single channel.";
135    return chrome::RESULT_CODE_SXS_MIGRATION_FAILED;
136  }
137
138  if (user_data_dir_channels[0] != GetChannelMarkForThisExecutable()) {
139    LOG(WARNING) << "User data dir migration is only possible when the profile "
140                 << "is used with the same channel.";
141    return chrome::RESULT_CODE_SXS_MIGRATION_FAILED;
142  }
143
144  base::FilePath target_path =
145      CommandLine::ForCurrentProcess()->GetSwitchValuePath(
146          switches::kMigrateDataDirForSxS);
147
148  if (!base::Move(source_path, target_path)) {
149    LOG(ERROR) << "Failed to rename '" << source_path.value()
150               << "' to '" << target_path.value() << "'";
151    return chrome::RESULT_CODE_SXS_MIGRATION_FAILED;
152  }
153
154  return content::RESULT_CODE_NORMAL_EXIT;
155}
156
157}  // namespace sxs_linux
158