Ben Chan | 7e6fea0 | 2013-01-17 15:21:01 -0800 | [diff] [blame] | 1 | // Copyright (c) 2013 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 | |
Ben Chan | 5ccd9fe | 2013-11-13 18:28:27 -0800 | [diff] [blame] | 5 | #include "cros-disks/fuse_mounter.h" |
Ben Chan | 7e6fea0 | 2013-01-17 15:21:01 -0800 | [diff] [blame] | 6 | |
Qijiang Fan | 713061e | 2021-03-08 15:45:12 +0900 | [diff] [blame] | 7 | #include <base/check.h> |
| 8 | #include <base/check_op.h> |
| 9 | |
Anand K Mistry | a24c75b | 2020-01-09 17:57:25 +1100 | [diff] [blame] | 10 | // Has to come before linux/fs.h due to conflicting definitions of MS_* |
| 11 | // constants. |
| 12 | #include <sys/mount.h> |
| 13 | |
Evan Green | 9075b84 | 2019-08-09 11:33:05 -0700 | [diff] [blame] | 14 | #include <fcntl.h> |
Ben Chan | 7e6fea0 | 2013-01-17 15:21:01 -0800 | [diff] [blame] | 15 | #include <linux/capability.h> |
Evan Green | 9075b84 | 2019-08-09 11:33:05 -0700 | [diff] [blame] | 16 | #include <linux/fs.h> |
| 17 | #include <sys/ioctl.h> |
Anand K Mistry | 41a2396 | 2019-02-05 11:57:03 +1100 | [diff] [blame] | 18 | #include <sys/stat.h> |
| 19 | #include <sys/types.h> |
| 20 | #include <unistd.h> |
Ben Chan | 7e6fea0 | 2013-01-17 15:21:01 -0800 | [diff] [blame] | 21 | |
François Degros | 5c6d9cb | 2020-07-16 13:44:44 +1000 | [diff] [blame] | 22 | #include <algorithm> |
Sergei Datsenko | 6907a13 | 2019-04-01 11:26:56 +1100 | [diff] [blame] | 23 | #include <memory> |
Ben Chan | 7e6fea0 | 2013-01-17 15:21:01 -0800 | [diff] [blame] | 24 | #include <string> |
Sergei Datsenko | a910bba | 2019-06-18 13:31:59 +1000 | [diff] [blame] | 25 | #include <utility> |
Ben Chan | 7e6fea0 | 2013-01-17 15:21:01 -0800 | [diff] [blame] | 26 | |
Anand K Mistry | 41a2396 | 2019-02-05 11:57:03 +1100 | [diff] [blame] | 27 | #include <base/bind.h> |
| 28 | #include <base/callback_helpers.h> |
| 29 | #include <base/files/file.h> |
Ben Chan | 7e6fea0 | 2013-01-17 15:21:01 -0800 | [diff] [blame] | 30 | #include <base/logging.h> |
Anand K Mistry | cf5135f | 2020-01-21 10:48:54 +1100 | [diff] [blame] | 31 | #include <base/memory/weak_ptr.h> |
François Degros | f926b99 | 2020-08-13 18:17:01 +1000 | [diff] [blame] | 32 | #include <base/stl_util.h> |
Anand K Mistry | 238aae1 | 2019-01-22 15:02:46 +1100 | [diff] [blame] | 33 | #include <base/strings/string_util.h> |
François Degros | f926b99 | 2020-08-13 18:17:01 +1000 | [diff] [blame] | 34 | #include <base/strings/stringprintf.h> |
Simon Glass | 2b1da09 | 2020-05-21 12:24:16 -0600 | [diff] [blame] | 35 | #include <brillo/process/process_reaper.h> |
Ben Chan | 7e6fea0 | 2013-01-17 15:21:01 -0800 | [diff] [blame] | 36 | |
François Degros | bf7bb59 | 2019-07-12 09:49:52 +1000 | [diff] [blame] | 37 | #include "cros-disks/error_logger.h" |
Anand K Mistry | 9f4611e | 2019-12-19 16:06:39 +1100 | [diff] [blame] | 38 | #include "cros-disks/mount_point.h" |
Ben Chan | 7e6fea0 | 2013-01-17 15:21:01 -0800 | [diff] [blame] | 39 | #include "cros-disks/platform.h" |
François Degros | 899487c | 2019-07-12 11:57:52 +1000 | [diff] [blame] | 40 | #include "cros-disks/quote.h" |
Ben Chan | 5ccd9fe | 2013-11-13 18:28:27 -0800 | [diff] [blame] | 41 | #include "cros-disks/sandboxed_process.h" |
Sergei Datsenko | f5553d1 | 2020-11-25 07:51:59 +1100 | [diff] [blame] | 42 | #include "cros-disks/uri.h" |
Ben Chan | 7e6fea0 | 2013-01-17 15:21:01 -0800 | [diff] [blame] | 43 | |
Anand K Mistry | 41a2396 | 2019-02-05 11:57:03 +1100 | [diff] [blame] | 44 | namespace cros_disks { |
| 45 | |
Ben Chan | 7e6fea0 | 2013-01-17 15:21:01 -0800 | [diff] [blame] | 46 | namespace { |
| 47 | |
Anand K Mistry | 238aae1 | 2019-01-22 15:02:46 +1100 | [diff] [blame] | 48 | const char kFuseDeviceFile[] = "/dev/fuse"; |
Derek Basehore | a057a97 | 2021-01-19 18:45:58 -0800 | [diff] [blame] | 49 | const char kBaseFreezerCgroup[] = "/sys/fs/cgroup/freezer"; |
| 50 | const char kCgroupProcsFile[] = "cgroup.procs"; |
Sergei Datsenko | 3928f78 | 2020-12-31 09:14:04 +1100 | [diff] [blame] | 51 | const int kFUSEMountFlags = MS_NODEV | MS_NOSUID | MS_NOEXEC | MS_DIRSYNC; |
Anand K Mistry | 238aae1 | 2019-01-22 15:02:46 +1100 | [diff] [blame] | 52 | |
Anand K Mistry | a24c75b | 2020-01-09 17:57:25 +1100 | [diff] [blame] | 53 | class FUSEMountPoint : public MountPoint { |
| 54 | public: |
Sergei Datsenko | 0ba1203 | 2021-01-07 08:51:14 +1100 | [diff] [blame] | 55 | using MountPoint::MountPoint; |
Anand K Mistry | a24c75b | 2020-01-09 17:57:25 +1100 | [diff] [blame] | 56 | |
Anand K Mistry | cf5135f | 2020-01-21 10:48:54 +1100 | [diff] [blame] | 57 | base::WeakPtr<FUSEMountPoint> GetWeakPtr() { |
| 58 | return weak_factory_.GetWeakPtr(); |
| 59 | } |
| 60 | |
Sergei Datsenko | 199f4f4 | 2020-10-08 10:47:12 +1100 | [diff] [blame] | 61 | static void CleanUpCallback(const base::FilePath& mount_path, |
| 62 | base::WeakPtr<FUSEMountPoint> ptr, |
| 63 | const siginfo_t& info) { |
| 64 | CHECK_EQ(SIGCHLD, info.si_signo); |
| 65 | if (info.si_code != CLD_EXITED) { |
François Degros | c309d93 | 2021-02-04 15:12:30 +1100 | [diff] [blame] | 66 | LOG(WARNING) << "FUSE daemon for " << redact(mount_path) |
Sergei Datsenko | 199f4f4 | 2020-10-08 10:47:12 +1100 | [diff] [blame] | 67 | << " crashed with code " << info.si_code << " and status " |
| 68 | << info.si_status; |
| 69 | } else if (info.si_status != 0) { |
François Degros | c309d93 | 2021-02-04 15:12:30 +1100 | [diff] [blame] | 70 | LOG(WARNING) << "FUSE daemon for " << redact(mount_path) |
Sergei Datsenko | 199f4f4 | 2020-10-08 10:47:12 +1100 | [diff] [blame] | 71 | << " exited with status " << info.si_status; |
| 72 | } else { |
| 73 | LOG(INFO) << "FUSE daemon for " << quote(mount_path) |
| 74 | << " exited normally"; |
| 75 | } |
| 76 | if (!ptr) { |
| 77 | // If the MountPoint instance has been deleted, it was |
| 78 | // already unmounted and cleaned up due to a |
| 79 | // request from the browser (or logout). In this |
| 80 | // case, there's nothing to do. |
| 81 | // TODO(dats): Consolidate this logic into centralized place (likely |
| 82 | // MountPoint base class). |
| 83 | return; |
| 84 | } |
| 85 | ptr->CleanUp(); |
| 86 | } |
| 87 | |
Anand K Mistry | a24c75b | 2020-01-09 17:57:25 +1100 | [diff] [blame] | 88 | private: |
Sergei Datsenko | 199f4f4 | 2020-10-08 10:47:12 +1100 | [diff] [blame] | 89 | void CleanUp() { |
| 90 | MountErrorType unmount_error = Unmount(); |
| 91 | LOG_IF(ERROR, unmount_error != MOUNT_ERROR_NONE) |
François Degros | c309d93 | 2021-02-04 15:12:30 +1100 | [diff] [blame] | 92 | << "Cannot unmount FUSE mount point " << redact(path()) |
Sergei Datsenko | 199f4f4 | 2020-10-08 10:47:12 +1100 | [diff] [blame] | 93 | << " after process exit: " << unmount_error; |
| 94 | |
| 95 | if (!platform_->RemoveEmptyDirectory(path().value())) { |
François Degros | c309d93 | 2021-02-04 15:12:30 +1100 | [diff] [blame] | 96 | PLOG(ERROR) << "Cannot remove FUSE mount point " << redact(path()) |
Sergei Datsenko | 199f4f4 | 2020-10-08 10:47:12 +1100 | [diff] [blame] | 97 | << " after process exit"; |
| 98 | } |
| 99 | } |
| 100 | |
Anand K Mistry | cf5135f | 2020-01-21 10:48:54 +1100 | [diff] [blame] | 101 | base::WeakPtrFactory<FUSEMountPoint> weak_factory_{this}; |
Anand K Mistry | a24c75b | 2020-01-09 17:57:25 +1100 | [diff] [blame] | 102 | }; |
| 103 | |
Evan Green | 9075b84 | 2019-08-09 11:33:05 -0700 | [diff] [blame] | 104 | bool GetPhysicalBlockSize(const std::string& source, int* size) { |
| 105 | base::ScopedFD fd(open(source.c_str(), O_RDONLY | O_CLOEXEC)); |
| 106 | |
| 107 | *size = 0; |
| 108 | if (!fd.is_valid()) { |
| 109 | PLOG(WARNING) << "Couldn't open " << source; |
| 110 | return false; |
| 111 | } |
| 112 | |
| 113 | if (ioctl(fd.get(), BLKPBSZGET, size) < 0) { |
| 114 | PLOG(WARNING) << "Failed to get block size for" << source; |
| 115 | return false; |
| 116 | } |
| 117 | |
| 118 | return true; |
| 119 | } |
| 120 | |
Sergei Datsenko | 199f4f4 | 2020-10-08 10:47:12 +1100 | [diff] [blame] | 121 | } // namespace |
| 122 | |
Sergei Datsenko | add282e | 2020-11-16 15:41:24 +1100 | [diff] [blame] | 123 | FUSESandboxedProcessFactory::FUSESandboxedProcessFactory( |
| 124 | const Platform* platform, |
| 125 | SandboxedExecutable executable, |
| 126 | OwnerUser run_as, |
| 127 | bool has_network_access, |
| 128 | std::vector<gid_t> supplementary_groups, |
| 129 | base::Optional<base::FilePath> mount_namespace) |
| 130 | : platform_(platform), |
| 131 | executable_(std::move(executable.executable)), |
| 132 | seccomp_policy_(std::move(executable.seccomp_policy)), |
| 133 | run_as_(std::move(run_as)), |
| 134 | has_network_access_(has_network_access), |
| 135 | supplementary_groups_(std::move(supplementary_groups)), |
| 136 | mount_namespace_(std::move(mount_namespace)) { |
| 137 | CHECK(executable_.IsAbsolute()); |
| 138 | if (seccomp_policy_) { |
| 139 | CHECK(seccomp_policy_.value().IsAbsolute()); |
| 140 | } |
| 141 | if (mount_namespace_) { |
| 142 | CHECK(mount_namespace_.value().IsAbsolute()); |
| 143 | } |
| 144 | } |
| 145 | |
| 146 | FUSESandboxedProcessFactory::~FUSESandboxedProcessFactory() = default; |
| 147 | |
| 148 | std::unique_ptr<SandboxedProcess> |
| 149 | FUSESandboxedProcessFactory::CreateSandboxedProcess() const { |
| 150 | auto sandbox = std::make_unique<SandboxedProcess>(); |
| 151 | if (!ConfigureSandbox(sandbox.get())) |
| 152 | return nullptr; |
| 153 | return sandbox; |
| 154 | } |
| 155 | |
| 156 | bool FUSESandboxedProcessFactory::ConfigureSandbox( |
| 157 | SandboxedProcess* sandbox) const { |
Derek Basehore | a057a97 | 2021-01-19 18:45:58 -0800 | [diff] [blame] | 158 | base::FilePath cgroup = base::FilePath(kBaseFreezerCgroup) |
| 159 | .Append(executable_.BaseName()) |
| 160 | .Append(kCgroupProcsFile); |
Sergei Datsenko | add282e | 2020-11-16 15:41:24 +1100 | [diff] [blame] | 161 | sandbox->SetCapabilities(0); |
| 162 | sandbox->SetNoNewPrivileges(); |
| 163 | |
| 164 | // The FUSE mount program is put under a new mount namespace, so mounts |
| 165 | // inside that namespace don't normally propagate. |
| 166 | sandbox->NewMountNamespace(); |
| 167 | sandbox->SkipRemountPrivate(); |
| 168 | |
| 169 | // TODO(benchan): Re-enable cgroup namespace when either Chrome OS |
| 170 | // kernel 3.8 supports it or no more supported devices use kernel |
| 171 | // 3.8. |
| 172 | // mount_process.NewCgroupNamespace(); |
| 173 | |
Derek Basehore | a057a97 | 2021-01-19 18:45:58 -0800 | [diff] [blame] | 174 | // Add the sandboxed process to its cgroup that should be setup. Return an |
| 175 | // error if it's not there. |
| 176 | if (!platform_->PathExists(cgroup.value())) { |
| 177 | LOG(ERROR) << "Freezer cgroup, " << cgroup << " is missing"; |
| 178 | return MOUNT_ERROR_INTERNAL; |
| 179 | } |
| 180 | |
| 181 | if (!sandbox->AddToCgroup(cgroup.value())) { |
| 182 | LOG(ERROR) << "Unable to add sandboxed process to cgroup " << cgroup; |
| 183 | return MOUNT_ERROR_INTERNAL; |
| 184 | } |
| 185 | |
Sergei Datsenko | add282e | 2020-11-16 15:41:24 +1100 | [diff] [blame] | 186 | sandbox->NewIpcNamespace(); |
| 187 | |
| 188 | sandbox->NewPidNamespace(); |
| 189 | |
| 190 | // Prepare mounts for pivot_root. |
| 191 | if (!sandbox->SetUpMinimalMounts()) { |
| 192 | LOG(ERROR) << "Cannot set up minijail mounts"; |
| 193 | return false; |
| 194 | } |
| 195 | |
| 196 | // /run is the place where mutable system configs are being kept. |
| 197 | // We don't expose them by default, but to be able to bind them when |
| 198 | // needed /run needs to be writeable. |
| 199 | if (!sandbox->Mount("tmpfs", "/run", "tmpfs", "mode=0755,size=1M")) { |
| 200 | LOG(ERROR) << "Cannot mount /run"; |
| 201 | return false; |
| 202 | } |
| 203 | |
| 204 | if (!has_network_access_) { |
| 205 | sandbox->NewNetworkNamespace(); |
| 206 | } else { |
| 207 | // Network DNS configs are in /run/shill. |
| 208 | if (!sandbox->BindMount("/run/shill", "/run/shill", false, false)) { |
| 209 | LOG(ERROR) << "Cannot bind /run/shill"; |
| 210 | return false; |
| 211 | } |
| 212 | // Hardcoded hosts are mounted into /etc/hosts.d when Crostini is enabled. |
| 213 | if (platform_->PathExists("/etc/hosts.d") && |
| 214 | !sandbox->BindMount("/etc/hosts.d", "/etc/hosts.d", false, false)) { |
| 215 | LOG(ERROR) << "Cannot bind /etc/hosts.d"; |
| 216 | return false; |
| 217 | } |
| 218 | } |
| 219 | |
| 220 | if (!sandbox->EnterPivotRoot()) { |
| 221 | LOG(ERROR) << "Cannot pivot root"; |
| 222 | return false; |
| 223 | } |
| 224 | |
| 225 | if (seccomp_policy_) { |
| 226 | if (!platform_->PathExists(seccomp_policy_.value().value())) { |
| 227 | LOG(ERROR) << "Seccomp policy " << quote(seccomp_policy_.value()) |
| 228 | << " is missing"; |
| 229 | return false; |
| 230 | } |
| 231 | sandbox->LoadSeccompFilterPolicy(seccomp_policy_.value().value()); |
| 232 | } |
| 233 | |
| 234 | sandbox->SetUserId(run_as_.uid); |
| 235 | sandbox->SetGroupId(run_as_.gid); |
| 236 | if (!supplementary_groups_.empty()) { |
| 237 | sandbox->SetSupplementaryGroupIds(supplementary_groups_); |
| 238 | } |
| 239 | |
| 240 | // Enter mount namespace in the sandbox if necessary. |
| 241 | if (mount_namespace_) { |
| 242 | sandbox->EnterExistingMountNamespace(mount_namespace_.value().value()); |
| 243 | } |
| 244 | |
| 245 | if (!platform_->PathExists(executable_.value())) { |
| 246 | LOG(ERROR) << "Cannot find mount program " << quote(executable_); |
| 247 | return false; |
| 248 | } |
| 249 | sandbox->AddArgument(executable_.value()); |
| 250 | |
| 251 | return true; |
| 252 | } |
| 253 | |
Sergei Datsenko | 199f4f4 | 2020-10-08 10:47:12 +1100 | [diff] [blame] | 254 | FUSEMounter::FUSEMounter(const Platform* platform, |
| 255 | brillo::ProcessReaper* process_reaper, |
| 256 | std::string filesystem_type, |
Sergei Datsenko | 1d2cbf8 | 2020-12-08 21:54:42 +1100 | [diff] [blame] | 257 | Config config) |
Sergei Datsenko | 199f4f4 | 2020-10-08 10:47:12 +1100 | [diff] [blame] | 258 | : platform_(platform), |
| 259 | process_reaper_(process_reaper), |
| 260 | filesystem_type_(std::move(filesystem_type)), |
Sergei Datsenko | 1d2cbf8 | 2020-12-08 21:54:42 +1100 | [diff] [blame] | 261 | config_(std::move(config)) {} |
Sergei Datsenko | 199f4f4 | 2020-10-08 10:47:12 +1100 | [diff] [blame] | 262 | |
| 263 | FUSEMounter::~FUSEMounter() = default; |
| 264 | |
| 265 | std::unique_ptr<MountPoint> FUSEMounter::Mount( |
| 266 | const std::string& source, |
| 267 | const base::FilePath& target_path, |
| 268 | std::vector<std::string> params, |
| 269 | MountErrorType* error) const { |
| 270 | // Read-only is the only parameter that has any effect at this layer. |
Sergei Datsenko | 1d2cbf8 | 2020-12-08 21:54:42 +1100 | [diff] [blame] | 271 | const bool read_only = config_.read_only || IsReadOnlyMount(params); |
Sergei Datsenko | 199f4f4 | 2020-10-08 10:47:12 +1100 | [diff] [blame] | 272 | |
| 273 | const base::File fuse_file = base::File( |
| 274 | base::FilePath(kFuseDeviceFile), |
| 275 | base::File::FLAG_OPEN | base::File::FLAG_READ | base::File::FLAG_WRITE); |
| 276 | if (!fuse_file.IsValid()) { |
| 277 | LOG(ERROR) << "Unable to open FUSE device file. Error: " |
| 278 | << fuse_file.error_details() << " " |
| 279 | << base::File::ErrorToString(fuse_file.error_details()); |
| 280 | *error = MOUNT_ERROR_INTERNAL; |
| 281 | return nullptr; |
| 282 | } |
| 283 | |
Sergei Datsenko | 6907a13 | 2019-04-01 11:26:56 +1100 | [diff] [blame] | 284 | // Mount options for FUSE: |
| 285 | // fd - File descriptor for /dev/fuse. |
| 286 | // user_id/group_id - user/group for file access control. Essentially |
| 287 | // bypassed due to allow_other, but still required to be set. |
| 288 | // allow_other - Allows users other than user_id/group_id to access files |
| 289 | // on the file system. By default, FUSE prevents any process other than |
| 290 | // ones running under user_id/group_id to access files, regardless of |
| 291 | // the file's permissions. |
| 292 | // default_permissions - Enforce permission checking. |
| 293 | // rootmode - Mode bits for the root inode. |
| 294 | std::string fuse_mount_options = base::StringPrintf( |
| 295 | "fd=%d,user_id=%u,group_id=%u,allow_other,default_permissions," |
| 296 | "rootmode=%o", |
Sergei Datsenko | add282e | 2020-11-16 15:41:24 +1100 | [diff] [blame] | 297 | fuse_file.GetPlatformFile(), kChronosUID, kChronosAccessGID, S_IFDIR); |
Sergei Datsenko | 6907a13 | 2019-04-01 11:26:56 +1100 | [diff] [blame] | 298 | |
Sergei Datsenko | 6907a13 | 2019-04-01 11:26:56 +1100 | [diff] [blame] | 299 | std::string fuse_type = "fuse"; |
Sergei Datsenko | 199f4f4 | 2020-10-08 10:47:12 +1100 | [diff] [blame] | 300 | std::string source_descr = source; |
Sergei Datsenko | e2d1d0b | 2020-11-18 12:48:13 +1100 | [diff] [blame] | 301 | base::stat_wrapper_t statbuf = {0}; |
| 302 | if (platform_->Lstat(source, &statbuf) && S_ISBLK(statbuf.st_mode)) { |
Evan Green | 9075b84 | 2019-08-09 11:33:05 -0700 | [diff] [blame] | 303 | int blksize = 0; |
| 304 | |
| 305 | // TODO(crbug.com/931500): It's possible that specifying a block size equal |
| 306 | // to the file system cluster size (which might be larger than the physical |
| 307 | // block size) might be more efficient. Data would be needed to see what |
| 308 | // kind of performance benefit, if any, could be gained. At the very least, |
| 309 | // specify the block size of the underlying device. Without this, UFS cards |
| 310 | // with 4k sector size will fail to mount. |
| 311 | if (GetPhysicalBlockSize(source, &blksize) && blksize > 0) |
| 312 | fuse_mount_options.append(base::StringPrintf(",blksize=%d", blksize)); |
| 313 | |
François Degros | 8b4e31e | 2019-07-29 11:39:19 +1000 | [diff] [blame] | 314 | LOG(INFO) << "Source file " << quote(source) |
| 315 | << " is a block device with block size " << blksize; |
Evan Green | 9075b84 | 2019-08-09 11:33:05 -0700 | [diff] [blame] | 316 | |
Sergei Datsenko | 6907a13 | 2019-04-01 11:26:56 +1100 | [diff] [blame] | 317 | fuse_type = "fuseblk"; |
Sergei Datsenko | 199f4f4 | 2020-10-08 10:47:12 +1100 | [diff] [blame] | 318 | } else { |
Sergei Datsenko | e2d1d0b | 2020-11-18 12:48:13 +1100 | [diff] [blame] | 319 | source_descr = "fuse:" + source; |
Sergei Datsenko | 6907a13 | 2019-04-01 11:26:56 +1100 | [diff] [blame] | 320 | } |
François Degros | 0191371 | 2020-09-08 17:33:54 +1000 | [diff] [blame] | 321 | |
Sergei Datsenko | 199f4f4 | 2020-10-08 10:47:12 +1100 | [diff] [blame] | 322 | if (!filesystem_type_.empty()) { |
Sam McNally | 38e5c23 | 2019-04-18 17:09:10 +1000 | [diff] [blame] | 323 | fuse_type += "."; |
Sergei Datsenko | 199f4f4 | 2020-10-08 10:47:12 +1100 | [diff] [blame] | 324 | fuse_type += filesystem_type_; |
Sam McNally | 38e5c23 | 2019-04-18 17:09:10 +1000 | [diff] [blame] | 325 | } |
Sergei Datsenko | 6907a13 | 2019-04-01 11:26:56 +1100 | [diff] [blame] | 326 | |
Sergei Datsenko | 0ba1203 | 2021-01-07 08:51:14 +1100 | [diff] [blame] | 327 | MountPointData data = { |
| 328 | .mount_path = target_path, |
| 329 | .source = source_descr, |
| 330 | .filesystem_type = fuse_type, |
| 331 | .flags = kFUSEMountFlags | (read_only ? MS_RDONLY : 0) | |
| 332 | (config_.nosymfollow ? MS_NOSYMFOLLOW : 0), |
| 333 | .data = fuse_mount_options, |
| 334 | }; |
| 335 | *error = platform_->Mount(data.source, data.mount_path.value(), |
| 336 | data.filesystem_type, data.flags, data.data); |
Sergei Datsenko | 199f4f4 | 2020-10-08 10:47:12 +1100 | [diff] [blame] | 337 | if (*error != MOUNT_ERROR_NONE) { |
| 338 | LOG(ERROR) << "Cannot perform unprivileged FUSE mount: " << *error; |
| 339 | return nullptr; |
| 340 | } |
| 341 | |
| 342 | pid_t pid = |
| 343 | StartDaemon(fuse_file, source, target_path, std::move(params), error); |
| 344 | if (*error != MOUNT_ERROR_NONE || pid == Process::kInvalidProcessId) { |
| 345 | LOG(ERROR) << "FUSE daemon start failure: " << *error; |
| 346 | LOG(INFO) << "FUSE cleanup on start failure for " << quote(target_path); |
| 347 | MountErrorType unmount_error = |
| 348 | platform_->Unmount(target_path.value(), MNT_FORCE | MNT_DETACH); |
| 349 | LOG_IF(ERROR, unmount_error != MOUNT_ERROR_NONE) |
François Degros | c309d93 | 2021-02-04 15:12:30 +1100 | [diff] [blame] | 350 | << "Cannot unmount FUSE mount point " << redact(target_path) |
Sergei Datsenko | 199f4f4 | 2020-10-08 10:47:12 +1100 | [diff] [blame] | 351 | << " after launch failure: " << unmount_error; |
| 352 | return nullptr; |
| 353 | } |
| 354 | |
| 355 | // At this point, the FUSE daemon has successfully started. |
| 356 | std::unique_ptr<FUSEMountPoint> mount_point = |
Sergei Datsenko | 0ba1203 | 2021-01-07 08:51:14 +1100 | [diff] [blame] | 357 | std::make_unique<FUSEMountPoint>(std::move(data), platform_); |
Sergei Datsenko | 199f4f4 | 2020-10-08 10:47:12 +1100 | [diff] [blame] | 358 | |
| 359 | // Add a watcher that cleans up the FUSE mount when the process exits. |
| 360 | // This is defined as in-jail "init" process, denoted by pid(), |
| 361 | // terminates, which happens only when the last process in the jailed PID |
| 362 | // namespace terminates. |
| 363 | process_reaper_->WatchForChild( |
| 364 | FROM_HERE, pid, |
| 365 | base::BindOnce(&FUSEMountPoint::CleanUpCallback, target_path, |
| 366 | mount_point->GetWeakPtr())); |
| 367 | |
| 368 | *error = MOUNT_ERROR_NONE; |
| 369 | return std::move(mount_point); |
Sergei Datsenko | 6907a13 | 2019-04-01 11:26:56 +1100 | [diff] [blame] | 370 | } |
| 371 | |
Sergei Datsenko | 88035aa | 2020-11-15 00:24:01 +1100 | [diff] [blame] | 372 | pid_t FUSEMounter::StartDaemon(const base::File& fuse_file, |
| 373 | const std::string& source, |
| 374 | const base::FilePath& target_path, |
| 375 | std::vector<std::string> params, |
| 376 | MountErrorType* error) const { |
| 377 | auto mount_process = |
| 378 | PrepareSandbox(source, target_path, std::move(params), error); |
| 379 | if (*error != MOUNT_ERROR_NONE) { |
| 380 | return Process::kInvalidProcessId; |
| 381 | } |
| 382 | |
| 383 | mount_process->AddArgument( |
| 384 | base::StringPrintf("/dev/fd/%d", fuse_file.GetPlatformFile())); |
| 385 | |
| 386 | std::vector<std::string> output; |
| 387 | const int return_code = mount_process->Run(&output); |
| 388 | *error = InterpretReturnCode(return_code); |
| 389 | |
| 390 | if (*error != MOUNT_ERROR_NONE) { |
| 391 | const auto& executable = mount_process->arguments()[0]; |
| 392 | if (!output.empty()) { |
| 393 | LOG(ERROR) << "FUSE mount program " << quote(executable) << " outputted " |
| 394 | << output.size() << " lines:"; |
| 395 | for (const std::string& line : output) { |
| 396 | LOG(ERROR) << line; |
| 397 | } |
| 398 | } |
| 399 | LOG(ERROR) << "FUSE mount program " << quote(executable) |
| 400 | << " returned error code " << return_code; |
| 401 | return Process::kInvalidProcessId; |
| 402 | } |
| 403 | |
| 404 | return mount_process->pid(); |
| 405 | } |
| 406 | |
| 407 | MountErrorType FUSEMounter::InterpretReturnCode(int return_code) const { |
| 408 | if (return_code != 0) |
| 409 | return MOUNT_ERROR_MOUNT_PROGRAM_FAILED; |
| 410 | return MOUNT_ERROR_NONE; |
| 411 | } |
| 412 | |
Sergei Datsenko | f5553d1 | 2020-11-25 07:51:59 +1100 | [diff] [blame] | 413 | FUSEMounterHelper::FUSEMounterHelper( |
| 414 | const Platform* platform, |
| 415 | brillo::ProcessReaper* process_reaper, |
| 416 | std::string filesystem_type, |
| 417 | bool nosymfollow, |
| 418 | const SandboxedProcessFactory* sandbox_factory) |
Sergei Datsenko | 1d2cbf8 | 2020-12-08 21:54:42 +1100 | [diff] [blame] | 419 | : FUSEMounter(platform, |
| 420 | process_reaper, |
| 421 | std::move(filesystem_type), |
| 422 | {.nosymfollow = nosymfollow}), |
Sergei Datsenko | f5553d1 | 2020-11-25 07:51:59 +1100 | [diff] [blame] | 423 | sandbox_factory_(sandbox_factory) {} |
| 424 | |
| 425 | FUSEMounterHelper::~FUSEMounterHelper() = default; |
| 426 | |
| 427 | std::unique_ptr<SandboxedProcess> FUSEMounterHelper::PrepareSandbox( |
| 428 | const std::string& source, |
| 429 | const base::FilePath& target_path, |
| 430 | std::vector<std::string> params, |
| 431 | MountErrorType* error) const { |
| 432 | auto sandbox = sandbox_factory_->CreateSandboxedProcess(); |
Sergei Datsenko | b99344b | 2020-12-31 08:35:12 +1100 | [diff] [blame] | 433 | if (!sandbox) { |
| 434 | *error = MOUNT_ERROR_INTERNAL; |
| 435 | return nullptr; |
| 436 | } |
Sergei Datsenko | f5553d1 | 2020-11-25 07:51:59 +1100 | [diff] [blame] | 437 | *error = |
| 438 | ConfigureSandbox(source, target_path, std::move(params), sandbox.get()); |
| 439 | if (*error != MOUNT_ERROR_NONE) { |
| 440 | return nullptr; |
| 441 | } |
| 442 | return sandbox; |
| 443 | } |
| 444 | |
Ben Chan | 7e6fea0 | 2013-01-17 15:21:01 -0800 | [diff] [blame] | 445 | } // namespace cros_disks |