Ryo Hashimoto | f8936a4 | 2017-08-04 17:41:59 +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 "virtual_file_provider/fuse_main.h" |
| 6 | |
Ryo Hashimoto | 153d2ff | 2017-08-14 15:17:23 +0900 | [diff] [blame] | 7 | #include <algorithm> |
Ryo Hashimoto | f8936a4 | 2017-08-04 17:41:59 +0900 | [diff] [blame] | 8 | #include <string> |
| 9 | #include <utility> |
| 10 | |
| 11 | #include <fuse/fuse.h> |
| 12 | |
Qijiang Fan | 713061e | 2021-03-08 15:45:12 +0900 | [diff] [blame] | 13 | #include <base/check.h> |
| 14 | #include <base/check_op.h> |
Ryo Hashimoto | f8936a4 | 2017-08-04 17:41:59 +0900 | [diff] [blame] | 15 | #include <base/files/scoped_file.h> |
Cherie Cheung | 1d39d93 | 2020-10-24 00:19:34 +0900 | [diff] [blame] | 16 | #include <base/optional.h> |
Ryo Hashimoto | f8936a4 | 2017-08-04 17:41:59 +0900 | [diff] [blame] | 17 | #include <base/posix/eintr_wrapper.h> |
Qijiang Fan | e19d67d | 2020-04-01 08:18:39 +0900 | [diff] [blame] | 18 | #include <base/stl_util.h> |
Cherie Cheung | 1d39d93 | 2020-10-24 00:19:34 +0900 | [diff] [blame] | 19 | #include <base/strings/stringprintf.h> |
Ryo Hashimoto | f8936a4 | 2017-08-04 17:41:59 +0900 | [diff] [blame] | 20 | |
Ryo Hashimoto | daefef0 | 2017-11-01 19:23:10 +0900 | [diff] [blame] | 21 | #include "virtual_file_provider/operation_throttle.h" |
Ryo Hashimoto | f8936a4 | 2017-08-04 17:41:59 +0900 | [diff] [blame] | 22 | #include "virtual_file_provider/util.h" |
| 23 | |
| 24 | namespace virtual_file_provider { |
| 25 | |
| 26 | namespace { |
| 27 | |
| 28 | constexpr char kFileSystemName[] = "virtual-file-provider"; |
| 29 | |
Ryo Hashimoto | daefef0 | 2017-11-01 19:23:10 +0900 | [diff] [blame] | 30 | // Maximum number of operations running at the same time. |
| 31 | constexpr int kMaxOperationCount = 1024; |
| 32 | |
| 33 | struct FusePrivateData { |
| 34 | FuseMainDelegate* delegate; |
| 35 | OperationThrottle* operation_throttle; |
| 36 | }; |
| 37 | |
Ryo Hashimoto | 153d2ff | 2017-08-14 15:17:23 +0900 | [diff] [blame] | 38 | FuseMainDelegate* GetDelegate() { |
Ryo Hashimoto | daefef0 | 2017-11-01 19:23:10 +0900 | [diff] [blame] | 39 | return static_cast<FusePrivateData*>(fuse_get_context()->private_data) |
| 40 | ->delegate; |
| 41 | } |
| 42 | |
| 43 | OperationThrottle* GetOperationThrottle() { |
| 44 | return static_cast<FusePrivateData*>(fuse_get_context()->private_data) |
| 45 | ->operation_throttle; |
Ryo Hashimoto | f8936a4 | 2017-08-04 17:41:59 +0900 | [diff] [blame] | 46 | } |
| 47 | |
| 48 | int GetAttr(const char* path, struct stat* stat) { |
| 49 | // Everything except the root is a file. |
| 50 | if (path == std::string("/")) { |
Cherie Cheung | 1d39d93 | 2020-10-24 00:19:34 +0900 | [diff] [blame] | 51 | stat->st_mode = S_IFDIR | S_IXGRP; |
Ryo Hashimoto | f8936a4 | 2017-08-04 17:41:59 +0900 | [diff] [blame] | 52 | stat->st_nlink = 2; |
| 53 | } else { |
Ryo Hashimoto | 153d2ff | 2017-08-14 15:17:23 +0900 | [diff] [blame] | 54 | DCHECK_EQ('/', path[0]); |
| 55 | // File name is the ID. |
| 56 | std::string id(path + 1); |
| 57 | |
| 58 | const int64_t size = GetDelegate()->GetSize(id); |
| 59 | if (size < 0) { |
| 60 | LOG(ERROR) << "Invalid ID " << id; |
| 61 | return -ENOENT; |
| 62 | } |
Cherie Cheung | 1d39d93 | 2020-10-24 00:19:34 +0900 | [diff] [blame] | 63 | stat->st_mode = S_IFREG | S_IRGRP; |
Ryo Hashimoto | f8936a4 | 2017-08-04 17:41:59 +0900 | [diff] [blame] | 64 | stat->st_nlink = 1; |
Ryo Hashimoto | 153d2ff | 2017-08-14 15:17:23 +0900 | [diff] [blame] | 65 | stat->st_size = size; |
Ryo Hashimoto | f8936a4 | 2017-08-04 17:41:59 +0900 | [diff] [blame] | 66 | } |
| 67 | return 0; |
| 68 | } |
| 69 | |
| 70 | int Open(const char* path, struct fuse_file_info* fi) { |
Cherie Cheung | add59dd | 2020-09-25 00:25:09 +0900 | [diff] [blame] | 71 | DCHECK_EQ('/', path[0]); |
| 72 | // File name is the ID. |
| 73 | const std::string id(path + 1); |
| 74 | |
| 75 | const int64_t file_size = GetDelegate()->GetSize(id); |
| 76 | if (file_size < 0) { |
| 77 | LOG(ERROR) << "Invalid ID " << id; |
| 78 | return -ENOENT; |
| 79 | } |
| 80 | |
Ryo Hashimoto | f8936a4 | 2017-08-04 17:41:59 +0900 | [diff] [blame] | 81 | return 0; |
| 82 | } |
| 83 | |
| 84 | int Read(const char* path, |
| 85 | char* buf, |
| 86 | size_t size, |
| 87 | off_t off, |
| 88 | struct fuse_file_info* fi) { |
Ryo Hashimoto | daefef0 | 2017-11-01 19:23:10 +0900 | [diff] [blame] | 89 | auto operation_reference = GetOperationThrottle()->StartOperation(); |
| 90 | |
Ryo Hashimoto | f8936a4 | 2017-08-04 17:41:59 +0900 | [diff] [blame] | 91 | DCHECK_EQ('/', path[0]); |
| 92 | // File name is the ID. |
| 93 | std::string id(path + 1); |
| 94 | |
Ryo Hashimoto | 153d2ff | 2017-08-14 15:17:23 +0900 | [diff] [blame] | 95 | // Adjust the size to avoid issuing unnecessary read requests. |
| 96 | const int64_t file_size = GetDelegate()->GetSize(id); |
| 97 | if (file_size < 0) { |
| 98 | LOG(ERROR) << "Invalid ID " << id; |
| 99 | return -EIO; |
| 100 | } |
| 101 | size = std::min(static_cast<int64_t>(size), file_size - off); |
| 102 | if (size <= 0) { |
| 103 | return 0; |
| 104 | } |
| 105 | |
Ryo Hashimoto | f8936a4 | 2017-08-04 17:41:59 +0900 | [diff] [blame] | 106 | // Create a pipe to receive data from chrome. By using pipe instead of D-Bus |
| 107 | // to receive data, we can reliably avoid deadlock at read(), provided chrome |
| 108 | // doesn't leak the file descriptor of the write end. |
| 109 | int fds[2] = {-1, -1}; |
| 110 | if (pipe(fds) != 0) { |
| 111 | PLOG(ERROR) << "pipe() failed."; |
| 112 | return -EIO; |
| 113 | } |
| 114 | base::ScopedFD read_end(fds[0]), write_end(fds[1]); |
| 115 | |
| 116 | // Send read request to chrome with the write end of the pipe. |
Ryo Hashimoto | 153d2ff | 2017-08-14 15:17:23 +0900 | [diff] [blame] | 117 | GetDelegate()->HandleReadRequest(id, off, size, std::move(write_end)); |
Ryo Hashimoto | f8936a4 | 2017-08-04 17:41:59 +0900 | [diff] [blame] | 118 | |
| 119 | // Read the data from the read end of the pipe. |
| 120 | size_t result = 0; |
| 121 | while (result < size) { |
| 122 | ssize_t r = HANDLE_EINTR(read(read_end.get(), buf + result, size - result)); |
| 123 | if (r < 0) { |
| 124 | return -EIO; |
| 125 | } |
| 126 | if (r == 0) { |
| 127 | break; |
| 128 | } |
| 129 | result += r; |
| 130 | } |
| 131 | return result; |
| 132 | } |
| 133 | |
| 134 | int Release(const char* path, struct fuse_file_info* fi) { |
Ryo Hashimoto | bcdabfc | 2017-09-20 19:12:39 +0900 | [diff] [blame] | 135 | DCHECK_EQ('/', path[0]); |
| 136 | // File name is the ID. |
| 137 | std::string id(path + 1); |
| 138 | |
Ryo Hashimoto | 153d2ff | 2017-08-14 15:17:23 +0900 | [diff] [blame] | 139 | GetDelegate()->NotifyIdReleased(id); |
Ryo Hashimoto | f8936a4 | 2017-08-04 17:41:59 +0900 | [diff] [blame] | 140 | return 0; |
| 141 | } |
| 142 | |
| 143 | int ReadDir(const char* path, |
| 144 | void* buf, |
| 145 | fuse_fill_dir_t filler, |
| 146 | off_t offset, |
| 147 | struct fuse_file_info* fi) { |
| 148 | filler(buf, ".", nullptr, 0); |
| 149 | filler(buf, "..", nullptr, 0); |
| 150 | return 0; |
| 151 | } |
| 152 | |
| 153 | void* Init(struct fuse_conn_info* conn) { |
| 154 | CHECK(ClearCapabilities()); |
| 155 | // FUSE will overwrite the context's private_data with the return value. |
| 156 | // Just return the current private_data. |
| 157 | return fuse_get_context()->private_data; |
| 158 | } |
| 159 | |
| 160 | } // namespace |
| 161 | |
Cherie Cheung | 1d39d93 | 2020-10-24 00:19:34 +0900 | [diff] [blame] | 162 | int FuseMain(const base::FilePath& mount_path, |
| 163 | FuseMainDelegate* delegate, |
| 164 | base::Optional<uid_t> userId, |
| 165 | base::Optional<gid_t> groupId) { |
| 166 | std::string mount_options = "noexec"; // disallow code execution |
| 167 | if (userId || groupId) { |
| 168 | // allow others to access files |
| 169 | mount_options.append(",allow_other"); |
| 170 | if (userId) { |
| 171 | mount_options.append(base::StringPrintf(",uid=%u", userId.value())); |
| 172 | } |
| 173 | if (groupId) { |
| 174 | mount_options.append(base::StringPrintf(",gid=%u", groupId.value())); |
| 175 | } |
| 176 | } |
Ryo Hashimoto | f8936a4 | 2017-08-04 17:41:59 +0900 | [diff] [blame] | 177 | const char* fuse_argv[] = { |
Cherie Cheung | 1d39d93 | 2020-10-24 00:19:34 +0900 | [diff] [blame] | 178 | kFileSystemName, |
| 179 | mount_path.value().c_str(), |
Ryo Hashimoto | f8936a4 | 2017-08-04 17:41:59 +0900 | [diff] [blame] | 180 | "-f", // "-f" for foreground. |
Cherie Cheung | 1d39d93 | 2020-10-24 00:19:34 +0900 | [diff] [blame] | 181 | "-o", |
| 182 | mount_options.c_str(), |
Ryo Hashimoto | f8936a4 | 2017-08-04 17:41:59 +0900 | [diff] [blame] | 183 | }; |
| 184 | constexpr struct fuse_operations operations = { |
| 185 | .getattr = GetAttr, |
| 186 | .open = Open, |
| 187 | .read = Read, |
| 188 | .release = Release, |
| 189 | .readdir = ReadDir, |
| 190 | .init = Init, |
| 191 | }; |
Ryo Hashimoto | daefef0 | 2017-11-01 19:23:10 +0900 | [diff] [blame] | 192 | OperationThrottle operation_throttle(kMaxOperationCount); |
| 193 | FusePrivateData private_data; |
| 194 | private_data.delegate = delegate; |
| 195 | private_data.operation_throttle = &operation_throttle; |
Qijiang Fan | e19d67d | 2020-04-01 08:18:39 +0900 | [diff] [blame] | 196 | return fuse_main(base::size(fuse_argv), const_cast<char**>(fuse_argv), |
Ryo Hashimoto | daefef0 | 2017-11-01 19:23:10 +0900 | [diff] [blame] | 197 | &operations, &private_data); |
Ryo Hashimoto | f8936a4 | 2017-08-04 17:41:59 +0900 | [diff] [blame] | 198 | } |
| 199 | |
| 200 | } // namespace virtual_file_provider |