Klemen Kozjek | b065885 | 2017-08-15 13:03:48 +0900 | [diff] [blame] | 1 | // Copyright 2017 The Chromium OS 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 "cros-disks/rename_manager.h" |
| 6 | |
Ben Chan | 445852f | 2017-10-02 23:00:16 -0700 | [diff] [blame] | 7 | #include <linux/capability.h> |
Klemen Kozjek | b065885 | 2017-08-15 13:03:48 +0900 | [diff] [blame] | 8 | |
| 9 | #include <string> |
| 10 | |
Ben Chan | 445852f | 2017-10-02 23:00:16 -0700 | [diff] [blame] | 11 | #include <base/bind.h> |
Klemen Kozjek | b065885 | 2017-08-15 13:03:48 +0900 | [diff] [blame] | 12 | #include <base/files/file_util.h> |
| 13 | #include <base/logging.h> |
Simon Glass | 2b1da09 | 2020-05-21 12:24:16 -0600 | [diff] [blame] | 14 | #include <brillo/process/process.h> |
Klemen Kozjek | b065885 | 2017-08-15 13:03:48 +0900 | [diff] [blame] | 15 | |
Austin Tankiang | 9ccaf68 | 2019-06-06 15:05:55 +1000 | [diff] [blame] | 16 | #include "cros-disks/filesystem_label.h" |
Klemen Kozjek | b065885 | 2017-08-15 13:03:48 +0900 | [diff] [blame] | 17 | #include "cros-disks/platform.h" |
François Degros | 8b4e31e | 2019-07-29 11:39:19 +1000 | [diff] [blame] | 18 | #include "cros-disks/quote.h" |
Klemen Kozjek | b065885 | 2017-08-15 13:03:48 +0900 | [diff] [blame] | 19 | #include "cros-disks/rename_manager_observer_interface.h" |
| 20 | |
Austin Tankiang | 9ccaf68 | 2019-06-06 15:05:55 +1000 | [diff] [blame] | 21 | namespace cros_disks { |
| 22 | |
Klemen Kozjek | b065885 | 2017-08-15 13:03:48 +0900 | [diff] [blame] | 23 | namespace { |
| 24 | |
| 25 | struct RenameParameters { |
| 26 | const char* filesystem_type; |
| 27 | const char* program_path; |
Klemen Kozjek | b065885 | 2017-08-15 13:03:48 +0900 | [diff] [blame] | 28 | const char* rename_group; |
| 29 | }; |
| 30 | |
Klemen Kozjek | b065885 | 2017-08-15 13:03:48 +0900 | [diff] [blame] | 31 | const char kRenameUser[] = "cros-disks"; |
| 32 | |
| 33 | // Supported file systems and their parameters |
| 34 | const RenameParameters kSupportedRenameParameters[] = { |
Austin Tankiang | 9ccaf68 | 2019-06-06 15:05:55 +1000 | [diff] [blame] | 35 | {"vfat", "/usr/sbin/fatlabel", "disk"}, |
Austin Tankiang | df16abb | 2019-06-07 14:04:30 +1000 | [diff] [blame] | 36 | {"exfat", "/usr/sbin/exfatlabel", "fuse-exfat"}, |
| 37 | {"ntfs", "/usr/sbin/ntfslabel", "ntfs-3g"}}; |
Klemen Kozjek | b065885 | 2017-08-15 13:03:48 +0900 | [diff] [blame] | 38 | |
| 39 | const RenameParameters* FindRenameParameters( |
| 40 | const std::string& filesystem_type) { |
| 41 | for (const auto& parameters : kSupportedRenameParameters) { |
| 42 | if (filesystem_type == parameters.filesystem_type) { |
| 43 | return ¶meters; |
| 44 | } |
| 45 | } |
| 46 | |
| 47 | return nullptr; |
| 48 | } |
| 49 | |
Austin Tankiang | 9ccaf68 | 2019-06-06 15:05:55 +1000 | [diff] [blame] | 50 | RenameErrorType LabelErrorToRenameError(LabelErrorType error_code) { |
| 51 | switch (error_code) { |
| 52 | case LabelErrorType::kLabelErrorNone: |
| 53 | return RENAME_ERROR_NONE; |
| 54 | case LabelErrorType::kLabelErrorUnsupportedFilesystem: |
| 55 | return RENAME_ERROR_UNSUPPORTED_FILESYSTEM; |
| 56 | case LabelErrorType::kLabelErrorLongName: |
| 57 | return RENAME_ERROR_LONG_NAME; |
| 58 | case LabelErrorType::kLabelErrorInvalidCharacter: |
| 59 | return RENAME_ERROR_INVALID_CHARACTER; |
| 60 | } |
| 61 | } |
Klemen Kozjek | b065885 | 2017-08-15 13:03:48 +0900 | [diff] [blame] | 62 | |
Austin Tankiang | 9ccaf68 | 2019-06-06 15:05:55 +1000 | [diff] [blame] | 63 | } // namespace |
Klemen Kozjek | b065885 | 2017-08-15 13:03:48 +0900 | [diff] [blame] | 64 | |
Ben Chan | 445852f | 2017-10-02 23:00:16 -0700 | [diff] [blame] | 65 | RenameManager::RenameManager(Platform* platform, |
| 66 | brillo::ProcessReaper* process_reaper) |
| 67 | : platform_(platform), |
| 68 | process_reaper_(process_reaper), |
| 69 | weak_ptr_factory_(this) {} |
Klemen Kozjek | b065885 | 2017-08-15 13:03:48 +0900 | [diff] [blame] | 70 | |
Ben Chan | fc77d71 | 2019-06-20 12:45:56 -0700 | [diff] [blame] | 71 | RenameManager::~RenameManager() = default; |
Klemen Kozjek | b065885 | 2017-08-15 13:03:48 +0900 | [diff] [blame] | 72 | |
Ben Chan | 213c6d9 | 2019-04-10 16:21:52 -0700 | [diff] [blame] | 73 | RenameErrorType RenameManager::StartRenaming( |
| 74 | const std::string& device_path, |
| 75 | const std::string& device_file, |
| 76 | const std::string& volume_name, |
| 77 | const std::string& filesystem_type) { |
Klemen Kozjek | b065885 | 2017-08-15 13:03:48 +0900 | [diff] [blame] | 78 | std::string source_path; |
| 79 | if (!platform_->GetRealPath(device_path, &source_path) || |
| 80 | !CanRename(source_path)) { |
François Degros | 8b4e31e | 2019-07-29 11:39:19 +1000 | [diff] [blame] | 81 | LOG(WARNING) << "Device with path " << quote(device_path) |
| 82 | << " is not allowed for renaming"; |
Klemen Kozjek | b065885 | 2017-08-15 13:03:48 +0900 | [diff] [blame] | 83 | return RENAME_ERROR_DEVICE_NOT_ALLOWED; |
| 84 | } |
| 85 | |
Austin Tankiang | 9ccaf68 | 2019-06-06 15:05:55 +1000 | [diff] [blame] | 86 | LabelErrorType label_error = |
| 87 | ValidateVolumeLabel(volume_name, filesystem_type); |
| 88 | if (label_error != LabelErrorType::kLabelErrorNone) { |
| 89 | return LabelErrorToRenameError(label_error); |
Klemen Kozjek | b065885 | 2017-08-15 13:03:48 +0900 | [diff] [blame] | 90 | } |
| 91 | |
| 92 | const RenameParameters* parameters = FindRenameParameters(filesystem_type); |
| 93 | // Check if tool for renaming exists |
Ben Chan | 213c6d9 | 2019-04-10 16:21:52 -0700 | [diff] [blame] | 94 | if (!base::PathExists(base::FilePath(parameters->program_path))) { |
François Degros | 8b4e31e | 2019-07-29 11:39:19 +1000 | [diff] [blame] | 95 | LOG(WARNING) << "Cannot find a rename program for filesystem " |
| 96 | << quote(filesystem_type); |
Klemen Kozjek | b065885 | 2017-08-15 13:03:48 +0900 | [diff] [blame] | 97 | return RENAME_ERROR_RENAME_PROGRAM_NOT_FOUND; |
| 98 | } |
| 99 | |
Qijiang Fan | 5243904 | 2020-06-17 15:34:38 +0900 | [diff] [blame] | 100 | if (base::Contains(rename_process_, device_path)) { |
François Degros | 8b4e31e | 2019-07-29 11:39:19 +1000 | [diff] [blame] | 101 | LOG(WARNING) << "Device " << quote(device_path) |
| 102 | << " is already being renamed"; |
Klemen Kozjek | b065885 | 2017-08-15 13:03:48 +0900 | [diff] [blame] | 103 | return RENAME_ERROR_DEVICE_BEING_RENAMED; |
| 104 | } |
| 105 | |
| 106 | uid_t rename_user_id; |
| 107 | gid_t rename_group_id; |
| 108 | if (!platform_->GetUserAndGroupId(kRenameUser, &rename_user_id, nullptr) || |
| 109 | !platform_->GetGroupId(parameters->rename_group, &rename_group_id)) { |
François Degros | 8b4e31e | 2019-07-29 11:39:19 +1000 | [diff] [blame] | 110 | LOG(WARNING) << "Cannot find a user with name " << quote(kRenameUser) |
| 111 | << " or a group with name " << quote(parameters->rename_group); |
Klemen Kozjek | b065885 | 2017-08-15 13:03:48 +0900 | [diff] [blame] | 112 | return RENAME_ERROR_INTERNAL; |
| 113 | } |
| 114 | |
Klemen Kozjek | 3fd136b | 2017-09-05 17:00:37 +0900 | [diff] [blame] | 115 | // TODO(klemenko): Further restrict the capabilities |
Klemen Kozjek | b065885 | 2017-08-15 13:03:48 +0900 | [diff] [blame] | 116 | SandboxedProcess* process = &rename_process_[device_path]; |
Klemen Kozjek | b065885 | 2017-08-15 13:03:48 +0900 | [diff] [blame] | 117 | process->SetUserId(rename_user_id); |
| 118 | process->SetGroupId(rename_group_id); |
| 119 | process->SetNoNewPrivileges(); |
| 120 | process->NewMountNamespace(); |
Klemen Kozjek | b065885 | 2017-08-15 13:03:48 +0900 | [diff] [blame] | 121 | process->NewIpcNamespace(); |
| 122 | process->NewNetworkNamespace(); |
Mattias Nissler | 6df1b1b | 2018-10-10 15:26:10 +0200 | [diff] [blame] | 123 | process->SetCapabilities(0); |
Klemen Kozjek | b065885 | 2017-08-15 13:03:48 +0900 | [diff] [blame] | 124 | |
| 125 | process->AddArgument(parameters->program_path); |
| 126 | |
| 127 | // TODO(klemenko): To improve and provide more general solution, the |
| 128 | // per-filesystem argument setup should be parameterized with RenameParameter. |
| 129 | // Construct program-name arguments |
| 130 | // Example: dosfslabel /dev/sdb1 "NEWNAME" |
| 131 | // Example: exfatlabel /dev/sdb1 "NEWNAME" |
Austin Tankiang | df16abb | 2019-06-07 14:04:30 +1000 | [diff] [blame] | 132 | if (filesystem_type == "vfat" || filesystem_type == "exfat" || |
| 133 | filesystem_type == "ntfs") { |
Klemen Kozjek | b065885 | 2017-08-15 13:03:48 +0900 | [diff] [blame] | 134 | process->AddArgument(device_file); |
| 135 | process->AddArgument(volume_name); |
| 136 | } |
| 137 | |
| 138 | if (!process->Start()) { |
François Degros | 8b4e31e | 2019-07-29 11:39:19 +1000 | [diff] [blame] | 139 | LOG(WARNING) << "Cannot start a process for renaming " << quote(device_path) |
| 140 | << " as filesystem " << quote(filesystem_type) |
| 141 | << " and volume name " << quote(volume_name); |
Klemen Kozjek | b065885 | 2017-08-15 13:03:48 +0900 | [diff] [blame] | 142 | rename_process_.erase(device_path); |
| 143 | return RENAME_ERROR_RENAME_PROGRAM_FAILED; |
| 144 | } |
Ben Chan | 445852f | 2017-10-02 23:00:16 -0700 | [diff] [blame] | 145 | |
| 146 | process_reaper_->WatchForChild( |
| 147 | FROM_HERE, process->pid(), |
Anand K Mistry | 44b77d4 | 2019-09-27 11:03:20 +1000 | [diff] [blame] | 148 | base::BindOnce(&RenameManager::OnRenameProcessTerminated, |
| 149 | weak_ptr_factory_.GetWeakPtr(), device_path)); |
Klemen Kozjek | b065885 | 2017-08-15 13:03:48 +0900 | [diff] [blame] | 150 | return RENAME_ERROR_NONE; |
| 151 | } |
| 152 | |
Ben Chan | 213c6d9 | 2019-04-10 16:21:52 -0700 | [diff] [blame] | 153 | void RenameManager::OnRenameProcessTerminated(const std::string& device_path, |
Ben Chan | 445852f | 2017-10-02 23:00:16 -0700 | [diff] [blame] | 154 | const siginfo_t& info) { |
Klemen Kozjek | b065885 | 2017-08-15 13:03:48 +0900 | [diff] [blame] | 155 | rename_process_.erase(device_path); |
Klemen Kozjek | b065885 | 2017-08-15 13:03:48 +0900 | [diff] [blame] | 156 | RenameErrorType error_type = RENAME_ERROR_UNKNOWN; |
Ben Chan | 445852f | 2017-10-02 23:00:16 -0700 | [diff] [blame] | 157 | switch (info.si_code) { |
| 158 | case CLD_EXITED: |
| 159 | if (info.si_status == 0) { |
| 160 | error_type = RENAME_ERROR_NONE; |
François Degros | 8b4e31e | 2019-07-29 11:39:19 +1000 | [diff] [blame] | 161 | LOG(INFO) << "Process " << info.si_pid << " for renaming " |
| 162 | << quote(device_path) << " completed successfully"; |
Ben Chan | 445852f | 2017-10-02 23:00:16 -0700 | [diff] [blame] | 163 | } else { |
| 164 | error_type = RENAME_ERROR_RENAME_PROGRAM_FAILED; |
François Degros | 8b4e31e | 2019-07-29 11:39:19 +1000 | [diff] [blame] | 165 | LOG(ERROR) << "Process " << info.si_pid << " for renaming " |
| 166 | << quote(device_path) << " exited with a status " |
Ben Chan | 445852f | 2017-10-02 23:00:16 -0700 | [diff] [blame] | 167 | << info.si_status; |
| 168 | } |
| 169 | break; |
| 170 | |
| 171 | case CLD_DUMPED: |
| 172 | case CLD_KILLED: |
Klemen Kozjek | b065885 | 2017-08-15 13:03:48 +0900 | [diff] [blame] | 173 | error_type = RENAME_ERROR_RENAME_PROGRAM_FAILED; |
François Degros | 8b4e31e | 2019-07-29 11:39:19 +1000 | [diff] [blame] | 174 | LOG(ERROR) << "Process " << info.si_pid << " for renaming " |
| 175 | << quote(device_path) << " killed by a signal " |
| 176 | << info.si_status; |
Ben Chan | 445852f | 2017-10-02 23:00:16 -0700 | [diff] [blame] | 177 | break; |
| 178 | |
| 179 | default: |
| 180 | break; |
Klemen Kozjek | b065885 | 2017-08-15 13:03:48 +0900 | [diff] [blame] | 181 | } |
François Degros | 8b4e31e | 2019-07-29 11:39:19 +1000 | [diff] [blame] | 182 | |
Klemen Kozjek | b065885 | 2017-08-15 13:03:48 +0900 | [diff] [blame] | 183 | if (observer_) |
| 184 | observer_->OnRenameCompleted(device_path, error_type); |
| 185 | } |
| 186 | |
Klemen Kozjek | b065885 | 2017-08-15 13:03:48 +0900 | [diff] [blame] | 187 | bool RenameManager::CanRename(const std::string& source_path) const { |
| 188 | return base::StartsWith(source_path, "/sys/", base::CompareCase::SENSITIVE) || |
Ben Chan | de0e3f6 | 2017-09-26 06:28:39 -0700 | [diff] [blame] | 189 | base::StartsWith(source_path, "/devices/", |
| 190 | base::CompareCase::SENSITIVE) || |
Klemen Kozjek | b065885 | 2017-08-15 13:03:48 +0900 | [diff] [blame] | 191 | base::StartsWith(source_path, "/dev/", base::CompareCase::SENSITIVE); |
| 192 | } |
| 193 | |
| 194 | } // namespace cros_disks |