blob: c3939b69ff9b4eabea334af691f26b09357d8212 [file] [log] [blame]
Ryo Hashimotof8936a42017-08-04 17:41:59 +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 "virtual_file_provider/fuse_main.h"
6
Ryo Hashimoto153d2ff2017-08-14 15:17:23 +09007#include <algorithm>
Ryo Hashimotof8936a42017-08-04 17:41:59 +09008#include <string>
9#include <utility>
10
11#include <fuse/fuse.h>
12
Qijiang Fan713061e2021-03-08 15:45:12 +090013#include <base/check.h>
14#include <base/check_op.h>
Ryo Hashimotof8936a42017-08-04 17:41:59 +090015#include <base/files/scoped_file.h>
Cherie Cheung1d39d932020-10-24 00:19:34 +090016#include <base/optional.h>
Ryo Hashimotof8936a42017-08-04 17:41:59 +090017#include <base/posix/eintr_wrapper.h>
Qijiang Fane19d67d2020-04-01 08:18:39 +090018#include <base/stl_util.h>
Cherie Cheung1d39d932020-10-24 00:19:34 +090019#include <base/strings/stringprintf.h>
Ryo Hashimotof8936a42017-08-04 17:41:59 +090020
Ryo Hashimotodaefef02017-11-01 19:23:10 +090021#include "virtual_file_provider/operation_throttle.h"
Ryo Hashimotof8936a42017-08-04 17:41:59 +090022#include "virtual_file_provider/util.h"
23
24namespace virtual_file_provider {
25
26namespace {
27
28constexpr char kFileSystemName[] = "virtual-file-provider";
29
Ryo Hashimotodaefef02017-11-01 19:23:10 +090030// Maximum number of operations running at the same time.
31constexpr int kMaxOperationCount = 1024;
32
33struct FusePrivateData {
34 FuseMainDelegate* delegate;
35 OperationThrottle* operation_throttle;
36};
37
Ryo Hashimoto153d2ff2017-08-14 15:17:23 +090038FuseMainDelegate* GetDelegate() {
Ryo Hashimotodaefef02017-11-01 19:23:10 +090039 return static_cast<FusePrivateData*>(fuse_get_context()->private_data)
40 ->delegate;
41}
42
43OperationThrottle* GetOperationThrottle() {
44 return static_cast<FusePrivateData*>(fuse_get_context()->private_data)
45 ->operation_throttle;
Ryo Hashimotof8936a42017-08-04 17:41:59 +090046}
47
48int GetAttr(const char* path, struct stat* stat) {
49 // Everything except the root is a file.
50 if (path == std::string("/")) {
Cherie Cheung1d39d932020-10-24 00:19:34 +090051 stat->st_mode = S_IFDIR | S_IXGRP;
Ryo Hashimotof8936a42017-08-04 17:41:59 +090052 stat->st_nlink = 2;
53 } else {
Ryo Hashimoto153d2ff2017-08-14 15:17:23 +090054 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 Cheung1d39d932020-10-24 00:19:34 +090063 stat->st_mode = S_IFREG | S_IRGRP;
Ryo Hashimotof8936a42017-08-04 17:41:59 +090064 stat->st_nlink = 1;
Ryo Hashimoto153d2ff2017-08-14 15:17:23 +090065 stat->st_size = size;
Ryo Hashimotof8936a42017-08-04 17:41:59 +090066 }
67 return 0;
68}
69
70int Open(const char* path, struct fuse_file_info* fi) {
Cherie Cheungadd59dd2020-09-25 00:25:09 +090071 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 Hashimotof8936a42017-08-04 17:41:59 +090081 return 0;
82}
83
84int Read(const char* path,
85 char* buf,
86 size_t size,
87 off_t off,
88 struct fuse_file_info* fi) {
Ryo Hashimotodaefef02017-11-01 19:23:10 +090089 auto operation_reference = GetOperationThrottle()->StartOperation();
90
Ryo Hashimotof8936a42017-08-04 17:41:59 +090091 DCHECK_EQ('/', path[0]);
92 // File name is the ID.
93 std::string id(path + 1);
94
Ryo Hashimoto153d2ff2017-08-14 15:17:23 +090095 // 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 Hashimotof8936a42017-08-04 17:41:59 +0900106 // 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 Hashimoto153d2ff2017-08-14 15:17:23 +0900117 GetDelegate()->HandleReadRequest(id, off, size, std::move(write_end));
Ryo Hashimotof8936a42017-08-04 17:41:59 +0900118
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
134int Release(const char* path, struct fuse_file_info* fi) {
Ryo Hashimotobcdabfc2017-09-20 19:12:39 +0900135 DCHECK_EQ('/', path[0]);
136 // File name is the ID.
137 std::string id(path + 1);
138
Ryo Hashimoto153d2ff2017-08-14 15:17:23 +0900139 GetDelegate()->NotifyIdReleased(id);
Ryo Hashimotof8936a42017-08-04 17:41:59 +0900140 return 0;
141}
142
143int 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
153void* 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 Cheung1d39d932020-10-24 00:19:34 +0900162int 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 Hashimotof8936a42017-08-04 17:41:59 +0900177 const char* fuse_argv[] = {
Cherie Cheung1d39d932020-10-24 00:19:34 +0900178 kFileSystemName,
179 mount_path.value().c_str(),
Ryo Hashimotof8936a42017-08-04 17:41:59 +0900180 "-f", // "-f" for foreground.
Cherie Cheung1d39d932020-10-24 00:19:34 +0900181 "-o",
182 mount_options.c_str(),
Ryo Hashimotof8936a42017-08-04 17:41:59 +0900183 };
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 Hashimotodaefef02017-11-01 19:23:10 +0900192 OperationThrottle operation_throttle(kMaxOperationCount);
193 FusePrivateData private_data;
194 private_data.delegate = delegate;
195 private_data.operation_throttle = &operation_throttle;
Qijiang Fane19d67d2020-04-01 08:18:39 +0900196 return fuse_main(base::size(fuse_argv), const_cast<char**>(fuse_argv),
Ryo Hashimotodaefef02017-11-01 19:23:10 +0900197 &operations, &private_data);
Ryo Hashimotof8936a42017-08-04 17:41:59 +0900198}
199
200} // namespace virtual_file_provider