blob: 1410c699a1b7c6e6f3fde2ab67f6f77f07b0c128 [file] [log] [blame]
Klemen Kozjekb0658852017-08-15 13:03:48 +09001// 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 Chan445852f2017-10-02 23:00:16 -07007#include <linux/capability.h>
Klemen Kozjekb0658852017-08-15 13:03:48 +09008
9#include <string>
10
Ben Chan445852f2017-10-02 23:00:16 -070011#include <base/bind.h>
Klemen Kozjekb0658852017-08-15 13:03:48 +090012#include <base/files/file_util.h>
13#include <base/logging.h>
Simon Glass2b1da092020-05-21 12:24:16 -060014#include <brillo/process/process.h>
Klemen Kozjekb0658852017-08-15 13:03:48 +090015
Austin Tankiang9ccaf682019-06-06 15:05:55 +100016#include "cros-disks/filesystem_label.h"
Klemen Kozjekb0658852017-08-15 13:03:48 +090017#include "cros-disks/platform.h"
François Degros8b4e31e2019-07-29 11:39:19 +100018#include "cros-disks/quote.h"
Klemen Kozjekb0658852017-08-15 13:03:48 +090019#include "cros-disks/rename_manager_observer_interface.h"
20
Austin Tankiang9ccaf682019-06-06 15:05:55 +100021namespace cros_disks {
22
Klemen Kozjekb0658852017-08-15 13:03:48 +090023namespace {
24
25struct RenameParameters {
26 const char* filesystem_type;
27 const char* program_path;
Klemen Kozjekb0658852017-08-15 13:03:48 +090028 const char* rename_group;
29};
30
Klemen Kozjekb0658852017-08-15 13:03:48 +090031const char kRenameUser[] = "cros-disks";
32
33// Supported file systems and their parameters
34const RenameParameters kSupportedRenameParameters[] = {
Austin Tankiang9ccaf682019-06-06 15:05:55 +100035 {"vfat", "/usr/sbin/fatlabel", "disk"},
Austin Tankiangdf16abb2019-06-07 14:04:30 +100036 {"exfat", "/usr/sbin/exfatlabel", "fuse-exfat"},
37 {"ntfs", "/usr/sbin/ntfslabel", "ntfs-3g"}};
Klemen Kozjekb0658852017-08-15 13:03:48 +090038
39const RenameParameters* FindRenameParameters(
40 const std::string& filesystem_type) {
41 for (const auto& parameters : kSupportedRenameParameters) {
42 if (filesystem_type == parameters.filesystem_type) {
43 return &parameters;
44 }
45 }
46
47 return nullptr;
48}
49
Austin Tankiang9ccaf682019-06-06 15:05:55 +100050RenameErrorType 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 Kozjekb0658852017-08-15 13:03:48 +090062
Austin Tankiang9ccaf682019-06-06 15:05:55 +100063} // namespace
Klemen Kozjekb0658852017-08-15 13:03:48 +090064
Ben Chan445852f2017-10-02 23:00:16 -070065RenameManager::RenameManager(Platform* platform,
66 brillo::ProcessReaper* process_reaper)
67 : platform_(platform),
68 process_reaper_(process_reaper),
69 weak_ptr_factory_(this) {}
Klemen Kozjekb0658852017-08-15 13:03:48 +090070
Ben Chanfc77d712019-06-20 12:45:56 -070071RenameManager::~RenameManager() = default;
Klemen Kozjekb0658852017-08-15 13:03:48 +090072
Ben Chan213c6d92019-04-10 16:21:52 -070073RenameErrorType 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 Kozjekb0658852017-08-15 13:03:48 +090078 std::string source_path;
79 if (!platform_->GetRealPath(device_path, &source_path) ||
80 !CanRename(source_path)) {
François Degros8b4e31e2019-07-29 11:39:19 +100081 LOG(WARNING) << "Device with path " << quote(device_path)
82 << " is not allowed for renaming";
Klemen Kozjekb0658852017-08-15 13:03:48 +090083 return RENAME_ERROR_DEVICE_NOT_ALLOWED;
84 }
85
Austin Tankiang9ccaf682019-06-06 15:05:55 +100086 LabelErrorType label_error =
87 ValidateVolumeLabel(volume_name, filesystem_type);
88 if (label_error != LabelErrorType::kLabelErrorNone) {
89 return LabelErrorToRenameError(label_error);
Klemen Kozjekb0658852017-08-15 13:03:48 +090090 }
91
92 const RenameParameters* parameters = FindRenameParameters(filesystem_type);
93 // Check if tool for renaming exists
Ben Chan213c6d92019-04-10 16:21:52 -070094 if (!base::PathExists(base::FilePath(parameters->program_path))) {
François Degros8b4e31e2019-07-29 11:39:19 +100095 LOG(WARNING) << "Cannot find a rename program for filesystem "
96 << quote(filesystem_type);
Klemen Kozjekb0658852017-08-15 13:03:48 +090097 return RENAME_ERROR_RENAME_PROGRAM_NOT_FOUND;
98 }
99
Qijiang Fan52439042020-06-17 15:34:38 +0900100 if (base::Contains(rename_process_, device_path)) {
François Degros8b4e31e2019-07-29 11:39:19 +1000101 LOG(WARNING) << "Device " << quote(device_path)
102 << " is already being renamed";
Klemen Kozjekb0658852017-08-15 13:03:48 +0900103 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 Degros8b4e31e2019-07-29 11:39:19 +1000110 LOG(WARNING) << "Cannot find a user with name " << quote(kRenameUser)
111 << " or a group with name " << quote(parameters->rename_group);
Klemen Kozjekb0658852017-08-15 13:03:48 +0900112 return RENAME_ERROR_INTERNAL;
113 }
114
Klemen Kozjek3fd136b2017-09-05 17:00:37 +0900115 // TODO(klemenko): Further restrict the capabilities
Klemen Kozjekb0658852017-08-15 13:03:48 +0900116 SandboxedProcess* process = &rename_process_[device_path];
Klemen Kozjekb0658852017-08-15 13:03:48 +0900117 process->SetUserId(rename_user_id);
118 process->SetGroupId(rename_group_id);
119 process->SetNoNewPrivileges();
120 process->NewMountNamespace();
Klemen Kozjekb0658852017-08-15 13:03:48 +0900121 process->NewIpcNamespace();
122 process->NewNetworkNamespace();
Mattias Nissler6df1b1b2018-10-10 15:26:10 +0200123 process->SetCapabilities(0);
Klemen Kozjekb0658852017-08-15 13:03:48 +0900124
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 Tankiangdf16abb2019-06-07 14:04:30 +1000132 if (filesystem_type == "vfat" || filesystem_type == "exfat" ||
133 filesystem_type == "ntfs") {
Klemen Kozjekb0658852017-08-15 13:03:48 +0900134 process->AddArgument(device_file);
135 process->AddArgument(volume_name);
136 }
137
138 if (!process->Start()) {
François Degros8b4e31e2019-07-29 11:39:19 +1000139 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 Kozjekb0658852017-08-15 13:03:48 +0900142 rename_process_.erase(device_path);
143 return RENAME_ERROR_RENAME_PROGRAM_FAILED;
144 }
Ben Chan445852f2017-10-02 23:00:16 -0700145
146 process_reaper_->WatchForChild(
147 FROM_HERE, process->pid(),
Anand K Mistry44b77d42019-09-27 11:03:20 +1000148 base::BindOnce(&RenameManager::OnRenameProcessTerminated,
149 weak_ptr_factory_.GetWeakPtr(), device_path));
Klemen Kozjekb0658852017-08-15 13:03:48 +0900150 return RENAME_ERROR_NONE;
151}
152
Ben Chan213c6d92019-04-10 16:21:52 -0700153void RenameManager::OnRenameProcessTerminated(const std::string& device_path,
Ben Chan445852f2017-10-02 23:00:16 -0700154 const siginfo_t& info) {
Klemen Kozjekb0658852017-08-15 13:03:48 +0900155 rename_process_.erase(device_path);
Klemen Kozjekb0658852017-08-15 13:03:48 +0900156 RenameErrorType error_type = RENAME_ERROR_UNKNOWN;
Ben Chan445852f2017-10-02 23:00:16 -0700157 switch (info.si_code) {
158 case CLD_EXITED:
159 if (info.si_status == 0) {
160 error_type = RENAME_ERROR_NONE;
François Degros8b4e31e2019-07-29 11:39:19 +1000161 LOG(INFO) << "Process " << info.si_pid << " for renaming "
162 << quote(device_path) << " completed successfully";
Ben Chan445852f2017-10-02 23:00:16 -0700163 } else {
164 error_type = RENAME_ERROR_RENAME_PROGRAM_FAILED;
François Degros8b4e31e2019-07-29 11:39:19 +1000165 LOG(ERROR) << "Process " << info.si_pid << " for renaming "
166 << quote(device_path) << " exited with a status "
Ben Chan445852f2017-10-02 23:00:16 -0700167 << info.si_status;
168 }
169 break;
170
171 case CLD_DUMPED:
172 case CLD_KILLED:
Klemen Kozjekb0658852017-08-15 13:03:48 +0900173 error_type = RENAME_ERROR_RENAME_PROGRAM_FAILED;
François Degros8b4e31e2019-07-29 11:39:19 +1000174 LOG(ERROR) << "Process " << info.si_pid << " for renaming "
175 << quote(device_path) << " killed by a signal "
176 << info.si_status;
Ben Chan445852f2017-10-02 23:00:16 -0700177 break;
178
179 default:
180 break;
Klemen Kozjekb0658852017-08-15 13:03:48 +0900181 }
François Degros8b4e31e2019-07-29 11:39:19 +1000182
Klemen Kozjekb0658852017-08-15 13:03:48 +0900183 if (observer_)
184 observer_->OnRenameCompleted(device_path, error_type);
185}
186
Klemen Kozjekb0658852017-08-15 13:03:48 +0900187bool RenameManager::CanRename(const std::string& source_path) const {
188 return base::StartsWith(source_path, "/sys/", base::CompareCase::SENSITIVE) ||
Ben Chande0e3f62017-09-26 06:28:39 -0700189 base::StartsWith(source_path, "/devices/",
190 base::CompareCase::SENSITIVE) ||
Klemen Kozjekb0658852017-08-15 13:03:48 +0900191 base::StartsWith(source_path, "/dev/", base::CompareCase::SENSITIVE);
192}
193
194} // namespace cros_disks