Ben Chan | 1e5a0cb | 2012-03-22 00:41:52 -0700 | [diff] [blame] | 1 | // Copyright (c) 2012 The Chromium OS Authors. All rights reserved. |
Ryan Cairns | ea6505f | 2011-04-10 19:54:53 -0700 | [diff] [blame] | 2 | // Use of this source code is governed by a BSD-style license that can be |
| 3 | // found in the LICENSE file. |
| 4 | |
Ben Chan | 5ccd9fe | 2013-11-13 18:28:27 -0800 | [diff] [blame] | 5 | #include "cros-disks/disk_manager.h" |
Ben Chan | e31d2aa | 2011-06-15 13:52:59 -0700 | [diff] [blame] | 6 | |
Ben Chan | 35cd7ae | 2019-05-02 22:42:37 -0700 | [diff] [blame] | 7 | #include <errno.h> |
Anand K Mistry | 6b19a97 | 2018-09-11 11:14:59 +1000 | [diff] [blame] | 8 | #include <inttypes.h> |
Ryan Cairns | ea6505f | 2011-04-10 19:54:53 -0700 | [diff] [blame] | 9 | #include <libudev.h> |
Ben Chan | e31d2aa | 2011-06-15 13:52:59 -0700 | [diff] [blame] | 10 | #include <string.h> |
Ben Chan | f51ff00 | 2011-04-25 12:41:57 -0700 | [diff] [blame] | 11 | #include <sys/mount.h> |
Anand K Mistry | 6b19a97 | 2018-09-11 11:14:59 +1000 | [diff] [blame] | 12 | #include <time.h> |
Ryan Cairns | ea6505f | 2011-04-10 19:54:53 -0700 | [diff] [blame] | 13 | |
Ben Chan | 5e3ca67 | 2014-08-25 15:53:58 -0700 | [diff] [blame] | 14 | #include <memory> |
Ben Chan | 5e64fd7 | 2016-09-23 15:20:34 -0700 | [diff] [blame] | 15 | #include <utility> |
Ben Chan | 5e3ca67 | 2014-08-25 15:53:58 -0700 | [diff] [blame] | 16 | |
Ben Chan | 86a9cee | 2014-07-14 12:17:12 -0700 | [diff] [blame] | 17 | #include <base/bind.h> |
Qijiang Fan | 713061e | 2021-03-08 15:45:12 +0900 | [diff] [blame] | 18 | #include <base/check.h> |
Ben Chan | bdc3974 | 2011-05-11 17:51:26 -0700 | [diff] [blame] | 19 | #include <base/logging.h> |
Mike Frysinger | 38ae98d | 2012-04-11 12:03:44 -0400 | [diff] [blame] | 20 | #include <base/stl_util.h> |
Anand K Mistry | 2a0147b | 2019-03-21 18:54:03 +1100 | [diff] [blame] | 21 | #include <base/strings/string_piece.h> |
Ben Chan | 97e20d4 | 2014-02-05 18:38:07 -0800 | [diff] [blame] | 22 | #include <base/strings/string_util.h> |
| 23 | #include <base/strings/stringprintf.h> |
Anand K Mistry | 6b19a97 | 2018-09-11 11:14:59 +1000 | [diff] [blame] | 24 | #include <base/time/time.h> |
Ben Chan | bdc3974 | 2011-05-11 17:51:26 -0700 | [diff] [blame] | 25 | |
Sergei Datsenko | 1682189 | 2019-04-05 11:26:38 +1100 | [diff] [blame] | 26 | #include "cros-disks/device_ejector.h" |
| 27 | #include "cros-disks/disk_monitor.h" |
Sergei Datsenko | 18464b9 | 2020-11-05 21:22:46 +1100 | [diff] [blame] | 28 | #include "cros-disks/fuse_mounter.h" |
Ben Chan | be2b4a7 | 2011-11-08 13:42:23 -0800 | [diff] [blame] | 29 | #include "cros-disks/metrics.h" |
Ben Chan | 5ccd9fe | 2013-11-13 18:28:27 -0800 | [diff] [blame] | 30 | #include "cros-disks/mount_options.h" |
Anand K Mistry | c7eba32 | 2020-01-15 17:45:38 +1100 | [diff] [blame] | 31 | #include "cros-disks/mount_point.h" |
Ben Chan | 29be915 | 2011-07-25 14:39:48 -0700 | [diff] [blame] | 32 | #include "cros-disks/platform.h" |
François Degros | 8b4e31e | 2019-07-29 11:39:19 +1000 | [diff] [blame] | 33 | #include "cros-disks/quote.h" |
Ben Chan | 5ccd9fe | 2013-11-13 18:28:27 -0800 | [diff] [blame] | 34 | #include "cros-disks/system_mounter.h" |
Ryan Cairns | ea6505f | 2011-04-10 19:54:53 -0700 | [diff] [blame] | 35 | |
Ben Chan | 86a9cee | 2014-07-14 12:17:12 -0700 | [diff] [blame] | 36 | namespace cros_disks { |
| 37 | |
Sergei Datsenko | 18464b9 | 2020-11-05 21:22:46 +1100 | [diff] [blame] | 38 | namespace { |
Sergei Datsenko | 68cd43a | 2020-11-16 22:57:16 +1100 | [diff] [blame] | 39 | |
Sergei Datsenko | 3928f78 | 2020-12-31 09:14:04 +1100 | [diff] [blame] | 40 | constexpr char kOptionDirSync[] = "dirsync"; |
| 41 | constexpr char kOptionFlush[] = "flush"; |
| 42 | constexpr char kOptionUtf8[] = "utf8"; |
| 43 | |
Sergei Datsenko | 68cd43a | 2020-11-16 22:57:16 +1100 | [diff] [blame] | 44 | // Implementation of FUSEMounter aimed at removable storage with |
| 45 | // exFAT or NTFS filesystems. |
| 46 | class DiskFUSEMounter : public FUSEMounter { |
| 47 | public: |
| 48 | DiskFUSEMounter(const Platform* platform, |
| 49 | brillo::ProcessReaper* reaper, |
| 50 | std::string filesystem_type, |
| 51 | const SandboxedProcessFactory* upstream_factory, |
| 52 | SandboxedExecutable executable, |
| 53 | OwnerUser run_as, |
| 54 | std::vector<std::string> options) |
Sergei Datsenko | 1d2cbf8 | 2020-12-08 21:54:42 +1100 | [diff] [blame] | 55 | : FUSEMounter(platform, reaper, std::move(filesystem_type), {}), |
Sergei Datsenko | 68cd43a | 2020-11-16 22:57:16 +1100 | [diff] [blame] | 56 | upstream_factory_(upstream_factory), |
| 57 | sandbox_factory_(platform, |
| 58 | std::move(executable), |
| 59 | run_as, |
| 60 | false, // no network needed |
| 61 | {}, |
| 62 | {}), |
| 63 | options_(std::move(options)) {} |
| 64 | |
| 65 | private: |
| 66 | // FUSEMounter overrides: |
| 67 | bool CanMount(const std::string& source, |
| 68 | const std::vector<std::string>& params, |
| 69 | base::FilePath* suggested_name) const override { |
| 70 | if (suggested_name) |
| 71 | *suggested_name = base::FilePath("disk"); |
| 72 | return true; |
| 73 | } |
| 74 | |
| 75 | std::unique_ptr<SandboxedProcess> PrepareSandbox( |
| 76 | const std::string& source, |
| 77 | const base::FilePath&, |
| 78 | std::vector<std::string>, |
| 79 | MountErrorType* error) const override { |
| 80 | auto device = base::FilePath(source); |
| 81 | |
| 82 | if (!device.IsAbsolute() || device.ReferencesParent() || |
| 83 | !base::StartsWith(device.value(), "/dev/", |
| 84 | base::CompareCase::SENSITIVE)) { |
| 85 | LOG(ERROR) << "Source path " << quote(device) << " is invalid"; |
| 86 | *error = MOUNT_ERROR_INVALID_ARGUMENT; |
| 87 | return nullptr; |
| 88 | } |
| 89 | |
| 90 | if (!platform()->PathExists(device.value())) { |
| 91 | LOG(ERROR) << "Source path " << quote(device) << " does not exist"; |
| 92 | *error = MOUNT_ERROR_INVALID_DEVICE_PATH; |
| 93 | return nullptr; |
| 94 | } |
| 95 | |
| 96 | // Make sure the FUSE user can read-write to the device. |
| 97 | if (!platform()->SetOwnership(device.value(), getuid(), |
| 98 | sandbox_factory_.run_as().gid) || |
| 99 | !platform()->SetPermissions(source, |
| 100 | S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP)) { |
| 101 | LOG(ERROR) << "Can't set up permissions on " << quote(source); |
| 102 | *error = MOUNT_ERROR_INSUFFICIENT_PERMISSIONS; |
| 103 | return nullptr; |
| 104 | } |
| 105 | |
| 106 | std::unique_ptr<SandboxedProcess> sandbox; |
| 107 | // For tests we use injected factory. |
| 108 | if (upstream_factory_) { |
| 109 | sandbox = upstream_factory_->CreateSandboxedProcess(); |
| 110 | sandbox->AddArgument(sandbox_factory_.executable().value()); |
| 111 | } else { |
| 112 | sandbox = sandbox_factory_.CreateSandboxedProcess(); |
| 113 | } |
| 114 | |
| 115 | // Bind-mount the device into the sandbox. |
| 116 | if (!sandbox->BindMount(device.value(), device.value(), true, false)) { |
| 117 | LOG(ERROR) << "Can't bind the device " << quote(device) |
| 118 | << " into the sandbox"; |
| 119 | *error = MOUNT_ERROR_INTERNAL; |
| 120 | return nullptr; |
| 121 | } |
| 122 | |
| 123 | if (!options_.empty()) { |
Sergei Datsenko | 1bb0bdc | 2021-02-17 11:26:43 +1100 | [diff] [blame] | 124 | std::string options; |
| 125 | if (!JoinParamsIntoOptions(options_, &options)) { |
| 126 | *error = MOUNT_ERROR_INVALID_MOUNT_OPTIONS; |
| 127 | return nullptr; |
| 128 | } |
Sergei Datsenko | 68cd43a | 2020-11-16 22:57:16 +1100 | [diff] [blame] | 129 | sandbox->AddArgument("-o"); |
Sergei Datsenko | 1bb0bdc | 2021-02-17 11:26:43 +1100 | [diff] [blame] | 130 | sandbox->AddArgument(options); |
Sergei Datsenko | 68cd43a | 2020-11-16 22:57:16 +1100 | [diff] [blame] | 131 | } |
| 132 | |
| 133 | sandbox->AddArgument(device.value()); |
| 134 | |
| 135 | *error = MOUNT_ERROR_NONE; |
| 136 | return sandbox; |
| 137 | } |
| 138 | |
| 139 | // Used to inject mocks for testing. |
| 140 | const SandboxedProcessFactory* const upstream_factory_; |
| 141 | |
| 142 | const FUSESandboxedProcessFactory sandbox_factory_; |
| 143 | const std::vector<std::string> options_; |
| 144 | }; |
| 145 | |
| 146 | // Specialization of a system mounter which deals with FAT-specific |
| 147 | // mount options. |
| 148 | class FATMounter : public SystemMounter { |
| 149 | public: |
| 150 | FATMounter(const Platform* platform, std::vector<std::string> options) |
| 151 | : SystemMounter( |
| 152 | platform, "vfat", /* read_only= */ false, std::move(options)) {} |
| 153 | |
| 154 | private: |
| 155 | MountErrorType ParseParams( |
| 156 | std::vector<std::string> params, |
| 157 | std::vector<std::string>* mount_options) const override { |
| 158 | // FAT32 stores times as local time instead of UTC. By default, the vfat |
| 159 | // kernel module will use the kernel's time zone, which is set using |
| 160 | // settimeofday(), to interpret time stamps as local time. However, time |
| 161 | // zones are complicated and generally a user-space concern in modern Linux. |
| 162 | // The man page for {get,set}timeofday comments that the |timezone| fields |
| 163 | // of these functions is obsolete. Chrome OS doesn't appear to set these |
| 164 | // either. Instead, we pass the time offset explicitly as a mount option so |
| 165 | // that the user can see file time stamps as local time. This mirrors what |
| 166 | // the user will see in other operating systems. |
| 167 | time_t now = base::Time::Now().ToTimeT(); |
| 168 | struct tm timestruct; |
| 169 | // The time zone might have changed since cros-disks was started. Force a |
| 170 | // re-read of the time zone to ensure the local time is what the user |
| 171 | // expects. |
| 172 | tzset(); |
| 173 | localtime_r(&now, ×truct); |
| 174 | // tm_gmtoff is a glibc extension. |
| 175 | int64_t offset_minutes = static_cast<int64_t>(timestruct.tm_gmtoff) / 60; |
| 176 | std::string offset_option = |
| 177 | base::StringPrintf("time_offset=%" PRId64, offset_minutes); |
| 178 | |
| 179 | mount_options->push_back(offset_option); |
| 180 | |
| 181 | return SystemMounter::ParseParams(std::move(params), mount_options); |
| 182 | } |
| 183 | }; |
| 184 | |
Sergei Datsenko | 18464b9 | 2020-11-05 21:22:46 +1100 | [diff] [blame] | 185 | } // namespace |
| 186 | |
Anand K Mistry | c7eba32 | 2020-01-15 17:45:38 +1100 | [diff] [blame] | 187 | class DiskManager::EjectingMountPoint : public MountPoint { |
| 188 | public: |
| 189 | EjectingMountPoint(std::unique_ptr<MountPoint> mount_point, |
Sergei Datsenko | 0ba1203 | 2021-01-07 08:51:14 +1100 | [diff] [blame] | 190 | const Platform* platform, |
Anand K Mistry | c7eba32 | 2020-01-15 17:45:38 +1100 | [diff] [blame] | 191 | DiskManager* disk_manager, |
| 192 | const std::string& device_file) |
Sergei Datsenko | 0ba1203 | 2021-01-07 08:51:14 +1100 | [diff] [blame] | 193 | : MountPoint( |
| 194 | { |
| 195 | .mount_path = mount_point->path(), |
| 196 | .source = mount_point->source(), |
| 197 | .filesystem_type = mount_point->fstype(), |
| 198 | .flags = mount_point->flags(), |
| 199 | .data = mount_point->data(), |
| 200 | }, |
| 201 | platform), |
Anand K Mistry | c7eba32 | 2020-01-15 17:45:38 +1100 | [diff] [blame] | 202 | mount_point_(std::move(mount_point)), |
| 203 | disk_manager_(disk_manager), |
| 204 | device_file_(device_file) { |
| 205 | DCHECK(mount_point_); |
| 206 | DCHECK(disk_manager_); |
| 207 | DCHECK(!device_file_.empty()); |
| 208 | } |
| 209 | |
Sergei Datsenko | 50c204d | 2020-11-14 21:18:40 +1100 | [diff] [blame] | 210 | EjectingMountPoint(const EjectingMountPoint&) = delete; |
| 211 | EjectingMountPoint& operator=(const EjectingMountPoint&) = delete; |
| 212 | |
Sergei Datsenko | 0ba1203 | 2021-01-07 08:51:14 +1100 | [diff] [blame] | 213 | ~EjectingMountPoint() override {} |
Anand K Mistry | c7eba32 | 2020-01-15 17:45:38 +1100 | [diff] [blame] | 214 | |
| 215 | void Release() override { |
| 216 | MountPoint::Release(); |
| 217 | mount_point_->Release(); |
| 218 | } |
| 219 | |
| 220 | protected: |
Sergei Datsenko | 0ba1203 | 2021-01-07 08:51:14 +1100 | [diff] [blame] | 221 | MountErrorType UnmountImpl() override { return CleanUp(); } |
| 222 | |
| 223 | MountErrorType RemountImpl(int flags) override { |
| 224 | return mount_point_->Remount(flags & MS_RDONLY); |
| 225 | } |
| 226 | |
| 227 | private: |
| 228 | MountErrorType CleanUp() { |
Anand K Mistry | c7eba32 | 2020-01-15 17:45:38 +1100 | [diff] [blame] | 229 | MountErrorType error = mount_point_->Unmount(); |
Sergei Datsenko | 0ba1203 | 2021-01-07 08:51:14 +1100 | [diff] [blame] | 230 | if (error == MOUNT_ERROR_NONE || error == MOUNT_ERROR_PATH_NOT_MOUNTED) { |
Anand K Mistry | c7eba32 | 2020-01-15 17:45:38 +1100 | [diff] [blame] | 231 | bool success = disk_manager_->EjectDevice(device_file_); |
| 232 | LOG_IF(ERROR, !success) |
| 233 | << "Unable to eject device " << quote(device_file_) |
| 234 | << " for mount path " << quote(path()); |
| 235 | } |
| 236 | return error; |
| 237 | } |
| 238 | |
Anand K Mistry | c7eba32 | 2020-01-15 17:45:38 +1100 | [diff] [blame] | 239 | const std::unique_ptr<MountPoint> mount_point_; |
| 240 | DiskManager* const disk_manager_; |
| 241 | const std::string device_file_; |
Anand K Mistry | c7eba32 | 2020-01-15 17:45:38 +1100 | [diff] [blame] | 242 | }; |
| 243 | |
Ben Chan | 213c6d9 | 2019-04-10 16:21:52 -0700 | [diff] [blame] | 244 | DiskManager::DiskManager(const std::string& mount_root, |
Ben Chan | de0e3f6 | 2017-09-26 06:28:39 -0700 | [diff] [blame] | 245 | Platform* platform, |
| 246 | Metrics* metrics, |
Sergei Datsenko | a910bba | 2019-06-18 13:31:59 +1000 | [diff] [blame] | 247 | brillo::ProcessReaper* process_reaper, |
Sergei Datsenko | 1682189 | 2019-04-05 11:26:38 +1100 | [diff] [blame] | 248 | DiskMonitor* disk_monitor, |
Sergei Datsenko | 68cd43a | 2020-11-16 22:57:16 +1100 | [diff] [blame] | 249 | DeviceEjector* device_ejector, |
| 250 | const SandboxedProcessFactory* test_sandbox_factory) |
Sergei Datsenko | a910bba | 2019-06-18 13:31:59 +1000 | [diff] [blame] | 251 | : MountManager(mount_root, platform, metrics, process_reaper), |
Sergei Datsenko | 1682189 | 2019-04-05 11:26:38 +1100 | [diff] [blame] | 252 | disk_monitor_(disk_monitor), |
Ben Chan | 1e5a0cb | 2012-03-22 00:41:52 -0700 | [diff] [blame] | 253 | device_ejector_(device_ejector), |
Sergei Datsenko | 68cd43a | 2020-11-16 22:57:16 +1100 | [diff] [blame] | 254 | test_sandbox_factory_(test_sandbox_factory), |
Sergei Datsenko | 1682189 | 2019-04-05 11:26:38 +1100 | [diff] [blame] | 255 | eject_device_on_unmount_(true) {} |
Ryan Cairns | ea6505f | 2011-04-10 19:54:53 -0700 | [diff] [blame] | 256 | |
| 257 | DiskManager::~DiskManager() { |
Ben Chan | a4c7506 | 2011-11-11 09:47:55 -0800 | [diff] [blame] | 258 | UnmountAll(); |
Ryan Cairns | ea6505f | 2011-04-10 19:54:53 -0700 | [diff] [blame] | 259 | } |
| 260 | |
Ben Chan | 8dcede8 | 2011-07-25 20:56:13 -0700 | [diff] [blame] | 261 | bool DiskManager::Initialize() { |
Sergei Datsenko | 68cd43a | 2020-11-16 22:57:16 +1100 | [diff] [blame] | 262 | OwnerUser run_as_exfat; |
| 263 | if (!platform()->GetUserAndGroupId("fuse-exfat", &run_as_exfat.uid, |
| 264 | &run_as_exfat.gid)) { |
| 265 | PLOG(ERROR) << "Cannot resolve fuse-exfat user"; |
| 266 | return false; |
| 267 | } |
| 268 | OwnerUser run_as_ntfs; |
| 269 | if (!platform()->GetUserAndGroupId("ntfs-3g", &run_as_ntfs.uid, |
| 270 | &run_as_ntfs.gid)) { |
| 271 | PLOG(ERROR) << "Cannot resolve ntfs-3g user"; |
| 272 | return false; |
| 273 | } |
| 274 | |
| 275 | std::string uid = base::StringPrintf("uid=%d", kChronosUID); |
| 276 | std::string gid = base::StringPrintf("gid=%d", kChronosAccessGID); |
| 277 | |
| 278 | // FAT32 - typical USB stick/SD card filesystem. |
| 279 | mounters_["vfat"] = std::make_unique<FATMounter>( |
Sergei Datsenko | 3928f78 | 2020-12-31 09:14:04 +1100 | [diff] [blame] | 280 | platform(), std::vector<std::string>{kOptionFlush, "shortname=mixed", |
| 281 | kOptionUtf8, uid, gid}); |
Sergei Datsenko | 68cd43a | 2020-11-16 22:57:16 +1100 | [diff] [blame] | 282 | |
| 283 | // Fancier newer version of FAT used for new big SD cards and USB sticks. |
| 284 | mounters_["exfat"] = std::make_unique<DiskFUSEMounter>( |
| 285 | platform(), process_reaper(), "exfat", test_sandbox_factory_, |
| 286 | SandboxedExecutable{base::FilePath("/usr/sbin/mount.exfat-fuse")}, |
Sergei Datsenko | 3928f78 | 2020-12-31 09:14:04 +1100 | [diff] [blame] | 287 | run_as_exfat, std::vector<std::string>{kOptionDirSync, uid, gid}); |
Sergei Datsenko | 68cd43a | 2020-11-16 22:57:16 +1100 | [diff] [blame] | 288 | |
| 289 | // External drives and some big USB sticks would likely have NTFS. |
| 290 | mounters_["ntfs"] = std::make_unique<DiskFUSEMounter>( |
| 291 | platform(), process_reaper(), "ntfs", test_sandbox_factory_, |
| 292 | SandboxedExecutable{base::FilePath("/usr/bin/ntfs-3g")}, run_as_ntfs, |
Sergei Datsenko | 3928f78 | 2020-12-31 09:14:04 +1100 | [diff] [blame] | 293 | std::vector<std::string>{kOptionDirSync, uid, gid}); |
Sergei Datsenko | 68cd43a | 2020-11-16 22:57:16 +1100 | [diff] [blame] | 294 | |
| 295 | // Typical CD/DVD filesystem. Inherently read-only. |
| 296 | mounters_["iso9660"] = std::make_unique<SystemMounter>( |
| 297 | platform(), "iso9660", true, |
Sergei Datsenko | 3928f78 | 2020-12-31 09:14:04 +1100 | [diff] [blame] | 298 | std::vector<std::string>{kOptionUtf8, uid, gid}); |
Sergei Datsenko | 68cd43a | 2020-11-16 22:57:16 +1100 | [diff] [blame] | 299 | |
| 300 | // Newer DVD filesystem. Inherently read-only. |
| 301 | mounters_["udf"] = std::make_unique<SystemMounter>( |
Sergei Datsenko | 3928f78 | 2020-12-31 09:14:04 +1100 | [diff] [blame] | 302 | platform(), "udf", true, std::vector<std::string>{kOptionUtf8, uid, gid}); |
Sergei Datsenko | 68cd43a | 2020-11-16 22:57:16 +1100 | [diff] [blame] | 303 | |
| 304 | // MacOS's HFS+ is not properly/officially supported, but sort of works, |
| 305 | // although with severe limitaions. |
| 306 | mounters_["hfsplus"] = std::make_unique<SystemMounter>( |
| 307 | platform(), "hfsplus", false, std::vector<std::string>{uid, gid}); |
| 308 | |
| 309 | // Have no reasonable explanation why would one have external media |
| 310 | // with a native Linux, filesystem and use CrOS to access it, given |
| 311 | // all the problems and limitations they would face, but for compatibility |
| 312 | // with previous versions we keep it unofficially supported. |
| 313 | mounters_["ext4"] = std::make_unique<SystemMounter>( |
| 314 | platform(), "ext4", false, std::vector<std::string>{}); |
| 315 | mounters_["ext3"] = std::make_unique<SystemMounter>( |
| 316 | platform(), "ext3", false, std::vector<std::string>{}); |
| 317 | mounters_["ext2"] = std::make_unique<SystemMounter>( |
| 318 | platform(), "ext2", false, std::vector<std::string>{}); |
| 319 | |
Ben Chan | 8dcede8 | 2011-07-25 20:56:13 -0700 | [diff] [blame] | 320 | return MountManager::Initialize(); |
| 321 | } |
| 322 | |
Ben Chan | 213c6d9 | 2019-04-10 16:21:52 -0700 | [diff] [blame] | 323 | bool DiskManager::CanMount(const std::string& source_path) const { |
Ben Chan | 8dcede8 | 2011-07-25 20:56:13 -0700 | [diff] [blame] | 324 | // The following paths can be mounted: |
| 325 | // /sys/... |
| 326 | // /devices/... |
| 327 | // /dev/... |
Alex Vakulenko | e50371c | 2016-01-20 16:06:19 -0800 | [diff] [blame] | 328 | return base::StartsWith(source_path, "/sys/", base::CompareCase::SENSITIVE) || |
| 329 | base::StartsWith(source_path, "/devices/", |
| 330 | base::CompareCase::SENSITIVE) || |
| 331 | base::StartsWith(source_path, "/dev/", base::CompareCase::SENSITIVE); |
Ben Chan | 8dcede8 | 2011-07-25 20:56:13 -0700 | [diff] [blame] | 332 | } |
| 333 | |
Anand K Mistry | d0a0523 | 2020-01-24 14:04:18 +1100 | [diff] [blame] | 334 | std::unique_ptr<MountPoint> DiskManager::DoMount( |
Anand K Mistry | c7eba32 | 2020-01-15 17:45:38 +1100 | [diff] [blame] | 335 | const std::string& source_path, |
| 336 | const std::string& filesystem_type, |
| 337 | const std::vector<std::string>& options, |
| 338 | const base::FilePath& mount_path, |
Anand K Mistry | c7eba32 | 2020-01-15 17:45:38 +1100 | [diff] [blame] | 339 | MountErrorType* error) { |
Ben Chan | 8dcede8 | 2011-07-25 20:56:13 -0700 | [diff] [blame] | 340 | CHECK(!source_path.empty()) << "Invalid source path argument"; |
| 341 | CHECK(!mount_path.empty()) << "Invalid mount path argument"; |
| 342 | |
| 343 | Disk disk; |
Sergei Datsenko | 1682189 | 2019-04-05 11:26:38 +1100 | [diff] [blame] | 344 | if (!disk_monitor_->GetDiskByDevicePath(base::FilePath(source_path), &disk)) { |
François Degros | 8b4e31e | 2019-07-29 11:39:19 +1000 | [diff] [blame] | 345 | LOG(ERROR) << quote(source_path) << " is not a valid device"; |
Anand K Mistry | c7eba32 | 2020-01-15 17:45:38 +1100 | [diff] [blame] | 346 | *error = MOUNT_ERROR_INVALID_DEVICE_PATH; |
| 347 | return nullptr; |
Ben Chan | 8dcede8 | 2011-07-25 20:56:13 -0700 | [diff] [blame] | 348 | } |
| 349 | |
Ben Chan | f0f2bcc | 2017-11-10 14:59:55 -0800 | [diff] [blame] | 350 | if (disk.is_on_boot_device) { |
François Degros | 8b4e31e | 2019-07-29 11:39:19 +1000 | [diff] [blame] | 351 | LOG(ERROR) << quote(source_path) |
| 352 | << " is on boot device and not allowed to mount"; |
Anand K Mistry | c7eba32 | 2020-01-15 17:45:38 +1100 | [diff] [blame] | 353 | *error = MOUNT_ERROR_INVALID_DEVICE_PATH; |
| 354 | return nullptr; |
Ben Chan | f0f2bcc | 2017-11-10 14:59:55 -0800 | [diff] [blame] | 355 | } |
| 356 | |
Ben Chan | ff92fa3 | 2017-10-17 16:17:15 -0700 | [diff] [blame] | 357 | if (disk.device_file.empty()) { |
François Degros | 8b4e31e | 2019-07-29 11:39:19 +1000 | [diff] [blame] | 358 | LOG(ERROR) << quote(source_path) << " does not have a device file"; |
Anand K Mistry | c7eba32 | 2020-01-15 17:45:38 +1100 | [diff] [blame] | 359 | *error = MOUNT_ERROR_INVALID_DEVICE_PATH; |
| 360 | return nullptr; |
Ben Chan | 8dcede8 | 2011-07-25 20:56:13 -0700 | [diff] [blame] | 361 | } |
| 362 | |
Sergei Datsenko | 68cd43a | 2020-11-16 22:57:16 +1100 | [diff] [blame] | 363 | if (!platform()->PathExists(disk.device_file)) { |
| 364 | LOG(ERROR) << quote(source_path) << " has device file " |
| 365 | << quote(disk.device_file) << " which is missing"; |
| 366 | *error = MOUNT_ERROR_INVALID_DEVICE_PATH; |
| 367 | return nullptr; |
| 368 | } |
| 369 | |
Ben Chan | 213c6d9 | 2019-04-10 16:21:52 -0700 | [diff] [blame] | 370 | std::string device_filesystem_type = |
Ben Chan | ff92fa3 | 2017-10-17 16:17:15 -0700 | [diff] [blame] | 371 | filesystem_type.empty() ? disk.filesystem_type : filesystem_type; |
| 372 | metrics()->RecordDeviceMediaType(disk.media_type); |
Ben Chan | 930f18d | 2011-11-21 09:18:50 -0800 | [diff] [blame] | 373 | metrics()->RecordFilesystemType(device_filesystem_type); |
Ben Chan | 8dcede8 | 2011-07-25 20:56:13 -0700 | [diff] [blame] | 374 | if (device_filesystem_type.empty()) { |
François Degros | 8b4e31e | 2019-07-29 11:39:19 +1000 | [diff] [blame] | 375 | LOG(ERROR) << "Cannot determine the file system type of device " |
| 376 | << quote(source_path); |
Anand K Mistry | c7eba32 | 2020-01-15 17:45:38 +1100 | [diff] [blame] | 377 | *error = MOUNT_ERROR_UNKNOWN_FILESYSTEM; |
| 378 | return nullptr; |
Ben Chan | 8dcede8 | 2011-07-25 20:56:13 -0700 | [diff] [blame] | 379 | } |
| 380 | |
Sergei Datsenko | 68cd43a | 2020-11-16 22:57:16 +1100 | [diff] [blame] | 381 | auto it = mounters_.find(device_filesystem_type); |
| 382 | if (it == mounters_.end()) { |
| 383 | LOG(ERROR) << "Unsupported file system type " |
| 384 | << quote(device_filesystem_type) << " of device " |
| 385 | << quote(source_path); |
Anand K Mistry | c7eba32 | 2020-01-15 17:45:38 +1100 | [diff] [blame] | 386 | *error = MOUNT_ERROR_UNSUPPORTED_FILESYSTEM; |
| 387 | return nullptr; |
Ben Chan | 8dcede8 | 2011-07-25 20:56:13 -0700 | [diff] [blame] | 388 | } |
| 389 | |
Sergei Datsenko | 68cd43a | 2020-11-16 22:57:16 +1100 | [diff] [blame] | 390 | const Mounter* mounter = it->second.get(); |
| 391 | |
| 392 | auto applied_options = options; |
| 393 | bool media_read_only = disk.is_read_only || disk.IsOpticalDisk(); |
| 394 | if (media_read_only && !IsReadOnlyMount(applied_options)) { |
Sergei Datsenko | 3928f78 | 2020-12-31 09:14:04 +1100 | [diff] [blame] | 395 | applied_options.push_back("ro"); |
Sergei Datsenko | 68cd43a | 2020-11-16 22:57:16 +1100 | [diff] [blame] | 396 | } |
Ben Chan | b5d7122 | 2012-10-03 12:55:16 -0700 | [diff] [blame] | 397 | |
Sergei Datsenko | 199f4f4 | 2020-10-08 10:47:12 +1100 | [diff] [blame] | 398 | std::unique_ptr<MountPoint> mount_point = |
Sergei Datsenko | 68cd43a | 2020-11-16 22:57:16 +1100 | [diff] [blame] | 399 | mounter->Mount(disk.device_file, mount_path, applied_options, error); |
Anand K Mistry | c7eba32 | 2020-01-15 17:45:38 +1100 | [diff] [blame] | 400 | if (*error != MOUNT_ERROR_NONE) { |
| 401 | DCHECK(!mount_point); |
Sergei Datsenko | da54a07 | 2018-05-14 12:51:56 +1000 | [diff] [blame] | 402 | // Try to mount the filesystem read-only if mounting it read-write failed. |
Sergei Datsenko | 68cd43a | 2020-11-16 22:57:16 +1100 | [diff] [blame] | 403 | if (!IsReadOnlyMount(applied_options)) { |
François Degros | 8b4e31e | 2019-07-29 11:39:19 +1000 | [diff] [blame] | 404 | LOG(INFO) << "Trying to mount " << quote(source_path) << " read-only"; |
Sergei Datsenko | 3928f78 | 2020-12-31 09:14:04 +1100 | [diff] [blame] | 405 | applied_options.push_back("ro"); |
Sergei Datsenko | 199f4f4 | 2020-10-08 10:47:12 +1100 | [diff] [blame] | 406 | mount_point = |
Sergei Datsenko | 68cd43a | 2020-11-16 22:57:16 +1100 | [diff] [blame] | 407 | mounter->Mount(disk.device_file, mount_path, applied_options, error); |
Sergei Datsenko | da54a07 | 2018-05-14 12:51:56 +1000 | [diff] [blame] | 408 | } |
| 409 | } |
| 410 | |
Anand K Mistry | c7eba32 | 2020-01-15 17:45:38 +1100 | [diff] [blame] | 411 | if (*error != MOUNT_ERROR_NONE) { |
| 412 | DCHECK(!mount_point); |
| 413 | return nullptr; |
Ben Chan | b5d7122 | 2012-10-03 12:55:16 -0700 | [diff] [blame] | 414 | } |
| 415 | |
Anand K Mistry | c7eba32 | 2020-01-15 17:45:38 +1100 | [diff] [blame] | 416 | return MaybeWrapMountPointForEject(std::move(mount_point), disk); |
Ben Chan | 8dcede8 | 2011-07-25 20:56:13 -0700 | [diff] [blame] | 417 | } |
| 418 | |
Ben Chan | 213c6d9 | 2019-04-10 16:21:52 -0700 | [diff] [blame] | 419 | std::string DiskManager::SuggestMountPath( |
| 420 | const std::string& source_path) const { |
Ben Chan | 8dcede8 | 2011-07-25 20:56:13 -0700 | [diff] [blame] | 421 | Disk disk; |
Sergei Datsenko | 1682189 | 2019-04-05 11:26:38 +1100 | [diff] [blame] | 422 | disk_monitor_->GetDiskByDevicePath(base::FilePath(source_path), &disk); |
Ben Chan | 8dcede8 | 2011-07-25 20:56:13 -0700 | [diff] [blame] | 423 | // If GetDiskByDevicePath fails, disk.GetPresentationName() returns |
| 424 | // the fallback presentation name. |
Anand K Mistry | 09f2db1 | 2019-11-07 17:06:56 +1100 | [diff] [blame] | 425 | return mount_root().Append(disk.GetPresentationName()).value(); |
Ben Chan | 8dcede8 | 2011-07-25 20:56:13 -0700 | [diff] [blame] | 426 | } |
| 427 | |
Ben Chan | f869288 | 2011-08-21 10:15:30 -0700 | [diff] [blame] | 428 | bool DiskManager::ShouldReserveMountPathOnError( |
| 429 | MountErrorType error_type) const { |
Ben Chan | fcb2fc0 | 2011-11-21 09:44:07 -0800 | [diff] [blame] | 430 | return error_type == MOUNT_ERROR_UNKNOWN_FILESYSTEM || |
| 431 | error_type == MOUNT_ERROR_UNSUPPORTED_FILESYSTEM; |
Ben Chan | f869288 | 2011-08-21 10:15:30 -0700 | [diff] [blame] | 432 | } |
| 433 | |
Anand K Mistry | c7eba32 | 2020-01-15 17:45:38 +1100 | [diff] [blame] | 434 | bool DiskManager::EjectDevice(const std::string& device_file) { |
| 435 | if (eject_device_on_unmount_) { |
| 436 | return device_ejector_->Eject(device_file); |
| 437 | } |
Ben Chan | b5d7122 | 2012-10-03 12:55:16 -0700 | [diff] [blame] | 438 | return true; |
| 439 | } |
| 440 | |
Anand K Mistry | c7eba32 | 2020-01-15 17:45:38 +1100 | [diff] [blame] | 441 | std::unique_ptr<MountPoint> DiskManager::MaybeWrapMountPointForEject( |
| 442 | std::unique_ptr<MountPoint> mount_point, const Disk& disk) { |
| 443 | if (!disk.IsOpticalDisk()) { |
| 444 | return mount_point; |
Ben Chan | 1e5a0cb | 2012-03-22 00:41:52 -0700 | [diff] [blame] | 445 | } |
Sergei Datsenko | 0ba1203 | 2021-01-07 08:51:14 +1100 | [diff] [blame] | 446 | return std::make_unique<EjectingMountPoint>( |
| 447 | std::move(mount_point), platform(), this, disk.device_file); |
Ben Chan | 1e5a0cb | 2012-03-22 00:41:52 -0700 | [diff] [blame] | 448 | } |
| 449 | |
| 450 | bool DiskManager::UnmountAll() { |
| 451 | // UnmountAll() is called when a user session ends. We do not want to eject |
| 452 | // devices in that situation and thus set |eject_device_on_unmount_| to |
| 453 | // false temporarily to prevent devices from being ejected upon unmount. |
| 454 | eject_device_on_unmount_ = false; |
| 455 | bool all_unmounted = MountManager::UnmountAll(); |
| 456 | eject_device_on_unmount_ = true; |
| 457 | return all_unmounted; |
| 458 | } |
| 459 | |
Ben Chan | bdc3974 | 2011-05-11 17:51:26 -0700 | [diff] [blame] | 460 | } // namespace cros_disks |