| // Copyright 2018 The Chromium OS Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| #include "arc/container/appfuse/appfuse_mount.h" |
| |
| #include <fcntl.h> |
| #include <sys/mount.h> |
| |
| #include <utility> |
| |
| #include <base/bind.h> |
| #include <base/files/file_util.h> |
| #include <base/strings/stringprintf.h> |
| |
| namespace arc { |
| namespace appfuse { |
| |
| AppfuseMount::AppfuseMount(const base::FilePath& mount_root, |
| uid_t uid, |
| int mount_id, |
| Delegate* delegate) |
| : mount_root_(mount_root), |
| uid_(uid), |
| mount_id_(mount_id), |
| delegate_(delegate), |
| mount_point_( |
| mount_root.Append(base::StringPrintf("%d_%d", uid, mount_id))), |
| weak_ptr_factory_(this) {} |
| |
| AppfuseMount::~AppfuseMount() { |
| Unmount(); |
| } |
| |
| base::ScopedFD AppfuseMount::Mount() { |
| // Create the mount point directory. |
| if (!base::CreateDirectory(mount_point_)) { |
| PLOG(ERROR) << "Failed to prepare directory " << mount_point_.value(); |
| return base::ScopedFD(); |
| } |
| |
| // Open device FD. |
| base::ScopedFD dev_fuse(HANDLE_EINTR(open("/dev/fuse", O_RDWR | O_CLOEXEC))); |
| if (!dev_fuse.is_valid()) { |
| PLOG(ERROR) << "Failed to open /dev/fuse"; |
| return base::ScopedFD(); |
| } |
| // An Android app runs with its own UID and GID whose values are the same. |
| const gid_t gid = uid_; |
| const auto opts = base::StringPrintf( |
| "fd=%i," |
| "rootmode=40000," |
| "default_permissions," |
| "allow_other," |
| "user_id=%d,group_id=%d," |
| "context=\"u:object_r:app_fuse_file:s0\"," |
| "fscontext=u:object_r:app_fusefs:s0", |
| dev_fuse.get(), uid_, gid); |
| |
| if (mount("/dev/fuse", mount_point_.value().c_str(), "fuse", |
| MS_NOSUID | MS_NODEV | MS_NOEXEC | MS_NOATIME, opts.c_str()) != 0) { |
| PLOG(ERROR) << "Failed to mount " << mount_point_.value(); |
| return base::ScopedFD(); |
| } |
| // OnDataFilterStopped will be called when the data filter stops on an error. |
| data_filter_.set_on_stopped_callback(base::Bind( |
| &AppfuseMount::OnDataFilterStopped, weak_ptr_factory_.GetWeakPtr())); |
| return data_filter_.Start(std::move(dev_fuse)); |
| } |
| |
| bool AppfuseMount::Unmount() { |
| if (umount2(mount_point_.value().c_str(), UMOUNT_NOFOLLOW | MNT_DETACH) != |
| 0 && |
| errno != EINVAL && errno != ENOENT) { |
| PLOG(ERROR) << "Failed to unmount " << mount_point_.value(); |
| return false; |
| } |
| if (!base::DeletePathRecursively(mount_point_)) { |
| PLOG(ERROR) << "Failed to delete " << mount_point_.value(); |
| return false; |
| } |
| return true; |
| } |
| |
| base::ScopedFD AppfuseMount::OpenFile(int file_id, int flags) { |
| base::FilePath path = mount_point_.Append(base::StringPrintf("%d", file_id)); |
| return base::ScopedFD(HANDLE_EINTR(open(path.value().c_str(), flags))); |
| } |
| |
| void AppfuseMount::OnDataFilterStopped() { |
| delegate_->OnAppfuseMountAborted(this); |
| } |
| |
| } // namespace appfuse |
| } // namespace arc |