blob: a75660c11543e25fcae9940560dfcb16f9a4c45c [file] [log] [blame]
shafik1e3a2672019-08-16 14:51:55 +01001// Copyright (C) 2019 The Android Open Source Project
2//
3// Licensed under the Apache License, Version 2.0 (the "License");
4// you may not use this file except in compliance with the License.
5// You may obtain a copy of the License at
6//
7// http://www.apache.org/licenses/LICENSE-2.0
8//
9// Unless required by applicable law or agreed to in writing, software
10// distributed under the License is distributed on an "AS IS" BASIS,
11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12// See the License for the specific language governing permissions and
13// limitations under the License.
14
Zimec8d5722019-12-05 07:26:13 +000015#define ATRACE_TAG ATRACE_TAG_APP
shafik1e3a2672019-08-16 14:51:55 +010016#define LOG_TAG "FuseDaemon"
Zim357e3072020-01-15 10:50:01 +000017#define LIBFUSE_LOG_TAG "libfuse"
shafik1e3a2672019-08-16 14:51:55 +010018
19#include "FuseDaemon.h"
20
shafik458d1102019-09-06 18:21:36 +010021#include <android-base/logging.h>
Zim32129b62020-03-11 11:37:16 +000022#include <android-base/properties.h>
Zim724b8ca2019-11-09 09:37:24 +000023#include <android/log.h>
Zimec8d5722019-12-05 07:26:13 +000024#include <android/trace.h>
shafik1e3a2672019-08-16 14:51:55 +010025#include <ctype.h>
26#include <dirent.h>
27#include <errno.h>
28#include <fcntl.h>
shafik8b57cd52019-09-06 10:51:29 +010029#include <fuse_i.h>
Zim724b8ca2019-11-09 09:37:24 +000030#include <fuse_log.h>
shafik8b57cd52019-09-06 10:51:29 +010031#include <fuse_lowlevel.h>
shafik1e3a2672019-08-16 14:51:55 +010032#include <inttypes.h>
33#include <limits.h>
34#include <linux/fuse.h>
shafik1e3a2672019-08-16 14:51:55 +010035#include <stdbool.h>
36#include <stdio.h>
37#include <stdlib.h>
38#include <string.h>
39#include <sys/inotify.h>
shafikb334a612019-09-30 20:38:16 +010040#include <sys/mman.h>
shafik1e3a2672019-08-16 14:51:55 +010041#include <sys/mount.h>
42#include <sys/param.h>
43#include <sys/resource.h>
44#include <sys/stat.h>
45#include <sys/statfs.h>
46#include <sys/statvfs.h>
47#include <sys/time.h>
48#include <sys/types.h>
49#include <sys/uio.h>
50#include <unistd.h>
shafik1e3a2672019-08-16 14:51:55 +010051
shafik8b57cd52019-09-06 10:51:29 +010052#include <iostream>
Narayan Kamath8d76d0c2019-12-31 13:14:50 +000053#include <list>
Paul Lawrence7e4a5a82019-09-27 21:39:49 +020054#include <map>
Narayan Kamath768bea32019-12-27 16:23:23 +000055#include <mutex>
Paul Lawrence7e4a5a82019-09-27 21:39:49 +020056#include <queue>
Zim859db932019-12-13 00:09:17 +000057#include <regex>
Paul Lawrence7e4a5a82019-09-27 21:39:49 +020058#include <thread>
Zim724b8ca2019-11-09 09:37:24 +000059#include <unordered_map>
Narayan Kamath92bf67b2020-01-03 17:49:09 +000060#include <unordered_set>
shafikc3f62672019-08-30 11:15:48 +010061#include <vector>
shafik1e3a2672019-08-16 14:51:55 +010062
shafikc3f62672019-08-30 11:15:48 +010063#include "MediaProviderWrapper.h"
Corina633099b2020-06-17 21:55:33 +010064#include "libfuse_jni/FuseUtils.h"
Sahana Raoa82bd6a2019-10-10 18:10:37 +010065#include "libfuse_jni/ReaddirHelper.h"
shafikc3f62672019-08-30 11:15:48 +010066#include "libfuse_jni/RedactionInfo.h"
Narayan Kamathaef84a12020-01-02 15:20:13 +000067#include "node-inl.h"
shafikc3f62672019-08-30 11:15:48 +010068
Sahana Raoa82bd6a2019-10-10 18:10:37 +010069using mediaprovider::fuse::DirectoryEntry;
Narayan Kamathaef84a12020-01-02 15:20:13 +000070using mediaprovider::fuse::dirhandle;
71using mediaprovider::fuse::handle;
72using mediaprovider::fuse::node;
shafikc3f62672019-08-30 11:15:48 +010073using mediaprovider::fuse::RedactionInfo;
Narayan Kamath8d76d0c2019-12-31 13:14:50 +000074using std::list;
Jeff Sharkey7469b982019-08-28 16:51:02 -060075using std::string;
shafikc3f62672019-08-30 11:15:48 +010076using std::vector;
shafik1e3a2672019-08-16 14:51:55 +010077
Narayan Kamath88203dc2019-08-30 17:19:38 +010078// logging macros to avoid duplication.
Nandana Dutt154cb5d2020-06-04 11:53:31 +010079#define TRACE_NODE(__node, __req) \
80 LOG(VERBOSE) << __FUNCTION__ << " : " << #__node << " = [" << get_name(__node) \
Alessio Balsini328abac2020-11-25 17:49:25 +000081 << "] (uid=" << (__req)->ctx.uid << ") "
shafik1e3a2672019-08-16 14:51:55 +010082
Zimec8d5722019-12-05 07:26:13 +000083#define ATRACE_NAME(name) ScopedTrace ___tracer(name)
84#define ATRACE_CALL() ATRACE_NAME(__FUNCTION__)
85
86class ScopedTrace {
87 public:
Narayan Kamath64b44352019-12-27 17:07:50 +000088 explicit inline ScopedTrace(const char *name) {
Zimec8d5722019-12-05 07:26:13 +000089 ATrace_beginSection(name);
90 }
91
92 inline ~ScopedTrace() {
93 ATrace_endSection();
94 }
95};
96
Zim32129b62020-03-11 11:37:16 +000097const bool IS_OS_DEBUGABLE = android::base::GetIntProperty("ro.debuggable", 0);
98
shafik1e3a2672019-08-16 14:51:55 +010099#define FUSE_UNKNOWN_INO 0xffffffff
100
Ricky Waif40c4022020-04-15 19:00:06 +0100101// Stolen from: android_filesystem_config.h
102#define AID_APP_START 10000
103
shafikb334a612019-09-30 20:38:16 +0100104constexpr size_t MAX_READ_SIZE = 128 * 1024;
Zim859db932019-12-13 00:09:17 +0000105// Stolen from: UserHandle#getUserId
106constexpr int PER_USER_RANGE = 100000;
shafikb334a612019-09-30 20:38:16 +0100107
Martijn Coenen44ef6302020-11-06 10:32:32 +0100108// Stolen from: UserManagerService
109constexpr int MAX_USER_ID = UINT32_MAX / PER_USER_RANGE;
110
Ricky Waif40c4022020-04-15 19:00:06 +0100111// Regex copied from FileUtils.java in MediaProvider, but without media directory.
112const std::regex PATTERN_OWNED_PATH(
Zim78ffeeb2020-09-22 20:15:25 +0100113 "^/storage/[^/]+/(?:[0-9]+/)?Android/(?:data|obb)/([^/]+)(/?.*)?",
114 std::regex_constants::icase);
Ricky Waif40c4022020-04-15 19:00:06 +0100115
Paul Lawrence7e4a5a82019-09-27 21:39:49 +0200116/*
117 * In order to avoid double caching with fuse, call fadvise on the file handles
118 * in the underlying file system. However, if this is done on every read/write,
119 * the fadvises cause a very significant slowdown in tests (specifically fio
120 * seq_write). So call fadvise on the file handles with the most reads/writes
121 * only after a threshold is passed.
122 */
123class FAdviser {
124 public:
125 FAdviser() : thread_(MessageLoop, this), total_size_(0) {}
126
127 ~FAdviser() {
128 SendMessage(Message::quit);
129 thread_.join();
130 }
131
132 void Record(int fd, size_t size) { SendMessage(Message::record, fd, size); }
133
134 void Close(int fd) { SendMessage(Message::close, fd); }
135
136 private:
Paul Lawrence7e4a5a82019-09-27 21:39:49 +0200137 struct Message {
138 enum Type { record, close, quit };
139 Type type;
140 int fd;
141 size_t size;
142 };
143
144 void RecordImpl(int fd, size_t size) {
145 total_size_ += size;
146
147 // Find or create record in files_
148 // Remove record from sizes_ if it exists, adjusting size appropriately
149 auto file = files_.find(fd);
150 if (file != files_.end()) {
151 auto old_size = file->second;
152 size += old_size->first;
153 sizes_.erase(old_size);
154 } else {
155 file = files_.insert(Files::value_type(fd, sizes_.end())).first;
156 }
157
158 // Now (re) insert record in sizes_
159 auto new_size = sizes_.insert(Sizes::value_type(size, fd));
160 file->second = new_size;
161
162 if (total_size_ < threshold_) return;
163
164 LOG(INFO) << "Threshold exceeded - fadvising " << total_size_;
165 while (!sizes_.empty() && total_size_ > target_) {
166 auto size = --sizes_.end();
167 total_size_ -= size->first;
168 posix_fadvise(size->second, 0, 0, POSIX_FADV_DONTNEED);
169 files_.erase(size->second);
170 sizes_.erase(size);
171 }
172 LOG(INFO) << "Threshold now " << total_size_;
173 }
174
175 void CloseImpl(int fd) {
176 auto file = files_.find(fd);
177 if (file == files_.end()) return;
178
179 total_size_ -= file->second->first;
180 sizes_.erase(file->second);
181 files_.erase(file);
182 }
183
184 void MessageLoopImpl() {
185 while (1) {
186 Message message;
187
188 {
189 std::unique_lock<std::mutex> lock(mutex_);
190 cv_.wait(lock, [this] { return !queue_.empty(); });
191 message = queue_.front();
192 queue_.pop();
193 }
194
195 switch (message.type) {
196 case Message::record:
197 RecordImpl(message.fd, message.size);
198 break;
199
200 case Message::close:
201 CloseImpl(message.fd);
202 break;
203
204 case Message::quit:
205 return;
206 }
207 }
208 }
209
210 static int MessageLoop(FAdviser* ptr) {
211 ptr->MessageLoopImpl();
212 return 0;
213 }
214
215 void SendMessage(Message::Type type, int fd = -1, size_t size = 0) {
216 {
217 std::unique_lock<std::mutex> lock(mutex_);
218 Message message = {type, fd, size};
219 queue_.push(message);
220 }
221 cv_.notify_one();
222 }
223
Paul Lawrence7e4a5a82019-09-27 21:39:49 +0200224 std::mutex mutex_;
225 std::condition_variable cv_;
Zim695a1272020-06-15 18:14:08 +0100226 std::queue<Message> queue_;
227 std::thread thread_;
Paul Lawrence7e4a5a82019-09-27 21:39:49 +0200228
229 typedef std::multimap<size_t, int> Sizes;
230 typedef std::map<int, Sizes::iterator> Files;
231
232 Files files_;
233 Sizes sizes_;
234 size_t total_size_;
235
236 const size_t threshold_ = 64 * 1024 * 1024;
237 const size_t target_ = 32 * 1024 * 1024;
238};
239
shafik1e3a2672019-08-16 14:51:55 +0100240/* Single FUSE mount */
241struct fuse {
Narayan Kamathaef84a12020-01-02 15:20:13 +0000242 explicit fuse(const std::string& _path)
Narayan Kamath568f17a2020-02-19 13:45:29 +0000243 : path(_path),
244 tracker(mediaprovider::fuse::NodeTracker(&lock)),
245 root(node::CreateRoot(_path, &lock, &tracker)),
246 mp(0),
Martijn Coenen87133092020-10-14 17:00:28 +0200247 zero_addr(0),
Alessio Balsinif220a962020-07-13 12:01:13 +0100248 disable_dentry_cache(false),
249 passthrough(false) {}
Paul Lawrence7e4a5a82019-09-27 21:39:49 +0200250
Narayan Kamathaef84a12020-01-02 15:20:13 +0000251 inline bool IsRoot(const node* node) const { return node == root; }
shafik1e3a2672019-08-16 14:51:55 +0100252
Zimb2834012020-06-17 13:26:27 +0100253 inline string GetEffectiveRootPath() {
254 if (path.find("/storage/emulated", 0) == 0) {
255 return path + "/" + std::to_string(getuid() / PER_USER_RANGE);
256 }
257 return path;
258 }
259
Narayan Kamathaef84a12020-01-02 15:20:13 +0000260 // Note that these two (FromInode / ToInode) conversion wrappers are required
261 // because fuse_lowlevel_ops documents that the root inode is always one
262 // (see FUSE_ROOT_ID in fuse_lowlevel.h). There are no particular requirements
263 // on any of the other inodes in the FS.
Ricky Wai44670762020-05-01 11:25:28 +0100264 inline node* FromInode(__u64 inode) {
Narayan Kamathaef84a12020-01-02 15:20:13 +0000265 if (inode == FUSE_ROOT_ID) {
266 return root;
267 }
shafik1e3a2672019-08-16 14:51:55 +0100268
Ricky Wai44670762020-05-01 11:25:28 +0100269 return node::FromInode(inode, &tracker);
Narayan Kamathaef84a12020-01-02 15:20:13 +0000270 }
271
272 inline __u64 ToInode(node* node) const {
273 if (IsRoot(node)) {
274 return FUSE_ROOT_ID;
275 }
276
277 return node::ToInode(node);
278 }
279
280 std::recursive_mutex lock;
281 const string path;
Narayan Kamath568f17a2020-02-19 13:45:29 +0000282 // The Inode tracker associated with this FUSE instance.
283 mediaprovider::fuse::NodeTracker tracker;
Narayan Kamathaef84a12020-01-02 15:20:13 +0000284 node* const root;
Zima76c3492020-02-19 01:23:26 +0000285 struct fuse_session* se;
shafikc3f62672019-08-30 11:15:48 +0100286
287 /*
288 * Used to make JNI calls to MediaProvider.
289 * Responsibility of freeing this object falls on corresponding
290 * FuseDaemon object.
291 */
292 mediaprovider::fuse::MediaProviderWrapper* mp;
shafikb334a612019-09-30 20:38:16 +0100293
294 /*
295 * Points to a range of zeroized bytes, used by pf_read to represent redacted ranges.
296 * The memory is read only and should never be modified.
297 */
Narayan Kamathaef84a12020-01-02 15:20:13 +0000298 /* const */ char* zero_addr;
Paul Lawrence7e4a5a82019-09-27 21:39:49 +0200299
300 FAdviser fadviser;
Zimd0435b22020-03-05 13:52:51 +0000301
302 std::atomic_bool* active;
Martijn Coenen87133092020-10-14 17:00:28 +0200303 std::atomic_bool disable_dentry_cache;
Zim148cbe22020-11-17 15:58:29 +0000304 std::atomic_bool passthrough;
shafik1e3a2672019-08-16 14:51:55 +0100305};
306
Zim32129b62020-03-11 11:37:16 +0000307static inline string get_name(node* n) {
308 if (n) {
Nandana Dutt154cb5d2020-06-04 11:53:31 +0100309 std::string name = IS_OS_DEBUGABLE ? "real_path: " + n->BuildPath() + " " : "";
310 name += "node_path: " + n->BuildSafePath();
Zim32129b62020-03-11 11:37:16 +0000311 return name;
312 }
313 return "?";
shafik458d1102019-09-06 18:21:36 +0100314}
315
shafik1e3a2672019-08-16 14:51:55 +0100316static inline __u64 ptr_to_id(void* ptr) {
317 return (__u64)(uintptr_t) ptr;
318}
319
Zimedbe69e2019-12-13 18:49:36 +0000320/*
321 * Set an F_RDLCK or F_WRLCKK on fd with fcntl(2).
322 *
323 * This is called before the MediaProvider returns fd from the lower file
324 * system to an app over the ContentResolver interface. This allows us
325 * check with is_file_locked if any reference to that fd is still open.
326 */
327static int set_file_lock(int fd, bool for_read, const std::string& path) {
328 std::string lock_str = (for_read ? "read" : "write");
Zimedbe69e2019-12-13 18:49:36 +0000329
330 struct flock fl{};
331 fl.l_type = for_read ? F_RDLCK : F_WRLCK;
332 fl.l_whence = SEEK_SET;
333
334 int res = fcntl(fd, F_OFD_SETLK, &fl);
335 if (res) {
Zimuzo Ezeozue67db40c2020-02-21 19:41:33 +0000336 PLOG(WARNING) << "Failed to set lock: " << lock_str;
Zimedbe69e2019-12-13 18:49:36 +0000337 return res;
338 }
Zimedbe69e2019-12-13 18:49:36 +0000339 return res;
340}
341
342/*
343 * Check if an F_RDLCK or F_WRLCK is set on fd with fcntl(2).
344 *
345 * This is used to determine if the MediaProvider has given an fd to the lower fs to an app over
346 * the ContentResolver interface. Before that happens, we always call set_file_lock on the file
347 * allowing us to know if any reference to that fd is still open here.
348 *
349 * Returns true if fd may have a lock, false otherwise
350 */
351static bool is_file_locked(int fd, const std::string& path) {
Zimedbe69e2019-12-13 18:49:36 +0000352 struct flock fl{};
353 fl.l_type = F_WRLCK;
354 fl.l_whence = SEEK_SET;
355
356 int res = fcntl(fd, F_OFD_GETLK, &fl);
357 if (res) {
Zimuzo Ezeozue67db40c2020-02-21 19:41:33 +0000358 PLOG(WARNING) << "Failed to check lock";
Zimedbe69e2019-12-13 18:49:36 +0000359 // Assume worst
360 return true;
361 }
362 bool locked = fl.l_type != F_UNLCK;
Zimedbe69e2019-12-13 18:49:36 +0000363 return locked;
364}
365
shafik1e3a2672019-08-16 14:51:55 +0100366static struct fuse* get_fuse(fuse_req_t req) {
367 return reinterpret_cast<struct fuse*>(fuse_req_userdata(req));
368}
369
Ricky Waif40c4022020-04-15 19:00:06 +0100370static bool is_package_owned_path(const string& path, const string& fuse_path) {
371 if (path.rfind(fuse_path, 0) != 0) {
372 return false;
373 }
374 return std::regex_match(path, PATTERN_OWNED_PATH);
375}
376
Zim33aea102020-06-19 15:49:47 +0100377// See fuse_lowlevel.h fuse_lowlevel_notify_inval_entry for how to call this safetly without
378// deadlocking the kernel
379static void fuse_inval(fuse_session* se, fuse_ino_t parent_ino, fuse_ino_t child_ino,
380 const string& child_name, const string& path) {
Corina633099b2020-06-17 21:55:33 +0100381 if (mediaprovider::fuse::containsMount(path, std::to_string(getuid() / PER_USER_RANGE))) {
Zim33aea102020-06-19 15:49:47 +0100382 LOG(WARNING) << "Ignoring attempt to invalidate dentry for FUSE mounts";
383 return;
384 }
385
386 if (fuse_lowlevel_notify_inval_entry(se, parent_ino, child_name.c_str(), child_name.size())) {
387 // Invalidating the dentry can fail if there's no dcache entry, however, there may still
388 // be cached attributes, so attempt to invalidate those by invalidating the inode
389 fuse_lowlevel_notify_inval_inode(se, child_ino, 0, 0);
390 }
Zim89962312020-04-22 21:21:53 +0100391}
392
Zim867fece2020-09-23 15:23:19 +0100393static double get_attr_timeout(const string& path, bool should_inval, node* node,
394 struct fuse* fuse) {
Martijn Coenen87133092020-10-14 17:00:28 +0200395 if (fuse->disable_dentry_cache || should_inval || is_package_owned_path(path, fuse->path)) {
Zimb2834012020-06-17 13:26:27 +0100396 // We set dentry timeout to 0 for the following reasons:
Martijn Coenen87133092020-10-14 17:00:28 +0200397 // 1. The dentry cache was completely disabled
398 // 2. Case-insensitive lookups need to invalidate other case-insensitive dentry matches
399 // 3. With app data isolation enabled, app A should not guess existence of app B from the
Zimb2834012020-06-17 13:26:27 +0100400 // Android/{data,obb}/<package> paths, hence we prevent the kernel from caching that
401 // information.
402 return 0;
403 }
404 return std::numeric_limits<double>::max();
405}
406
Zim867fece2020-09-23 15:23:19 +0100407static double get_entry_timeout(const string& path, bool should_inval, node* node,
408 struct fuse* fuse) {
409 string media_path = fuse->GetEffectiveRootPath() + "/Android/media";
410 if (path.find(media_path, 0) == 0) {
411 // Installd might delete Android/media/<package> dirs when app data is cleared.
412 // This can leave a stale entry in the kernel dcache, and break subsequent creation of the
413 // dir via FUSE.
414 return 0;
415 }
416 return get_attr_timeout(path, should_inval, node, fuse);
417}
418
419static std::string get_path(node* node) {
420 const string& io_path = node->GetIoPath();
421 return io_path.empty() ? node->BuildPath() : io_path;
422}
423
Narayan Kamathaef84a12020-01-02 15:20:13 +0000424static node* make_node_entry(fuse_req_t req, node* parent, const string& name, const string& path,
425 struct fuse_entry_param* e, int* error_code) {
shafik1e3a2672019-08-16 14:51:55 +0100426 struct fuse* fuse = get_fuse(req);
Martijn Coenenb8ff4eb2019-12-17 09:55:17 +0100427 const struct fuse_ctx* ctx = fuse_req_ctx(req);
Narayan Kamathaef84a12020-01-02 15:20:13 +0000428 node* node;
shafik1e3a2672019-08-16 14:51:55 +0100429
Jeff Sharkey7469b982019-08-28 16:51:02 -0600430 memset(e, 0, sizeof(*e));
shafik1e3a2672019-08-16 14:51:55 +0100431 if (lstat(path.c_str(), &e->attr) < 0) {
Narayan Kamathbbb779a2019-12-31 16:30:11 +0000432 *error_code = errno;
shafik1e3a2672019-08-16 14:51:55 +0100433 return NULL;
434 }
435
Zim33aea102020-06-19 15:49:47 +0100436 bool should_inval = false;
Zimbb7c3762020-09-23 13:50:08 +0100437 bool transforms_complete = true;
438 int transforms = 0;
439 string io_path;
Zim867fece2020-09-23 15:23:19 +0100440
441 if (S_ISREG(e->attr.st_mode)) {
442 // Handle potential file transforms
Biswarup Pal63901f32021-01-07 14:57:23 +0000443 std::unique_ptr<mediaprovider::fuse::FileLookupResult> file_lookup_result =
444 fuse->mp->FileLookup(path, req->ctx.uid);
Zim867fece2020-09-23 15:23:19 +0100445
Biswarup Pal63901f32021-01-07 14:57:23 +0000446 if (!file_lookup_result) {
447 // Fail lookup if we can't fetch FileLookupResult for path
448 LOG(WARNING) << "Failed to fetch FileLookupResult for " << name;
Zim867fece2020-09-23 15:23:19 +0100449 *error_code = ENOENT;
450 return NULL;
451 }
452
Biswarup Pal63901f32021-01-07 14:57:23 +0000453 transforms = file_lookup_result->transforms;
454 io_path = file_lookup_result->io_path;
455 transforms_complete = file_lookup_result->transforms_complete;
456
457 // Update size with io_path size if io_path is not same as path
458 if (!io_path.empty() && (io_path != path) && (lstat(io_path.c_str(), &e->attr) < 0)) {
459 *error_code = errno;
460 return NULL;
461 }
462
Zim867fece2020-09-23 15:23:19 +0100463 // Invalidate if there are any transforms so that we always get a lookup into userspace
464 should_inval = should_inval || transforms;
Zim867fece2020-09-23 15:23:19 +0100465 }
466
Zimed257c12020-10-15 13:47:45 +0100467 node = parent->LookupChildByName(name, true /* acquire */, transforms);
Narayan Kamatheca34252020-02-11 13:08:37 +0000468 if (!node) {
Zimbb7c3762020-09-23 13:50:08 +0100469 node = ::node::Create(parent, name, io_path, transforms_complete, transforms, &fuse->lock,
470 &fuse->tracker);
Corina633099b2020-06-17 21:55:33 +0100471 } else if (!mediaprovider::fuse::containsMount(path, std::to_string(getuid() / PER_USER_RANGE))) {
Zim867fece2020-09-23 15:23:19 +0100472 should_inval = should_inval || node->HasCaseInsensitiveMatch();
Corina633099b2020-06-17 21:55:33 +0100473 // Only invalidate a path if it does not contain mount.
Zim0852ba72020-06-09 11:50:33 +0100474 // Invalidate both names to ensure there's no dentry left in the kernel after the following
475 // operations:
476 // 1) touch foo, touch FOO, unlink *foo*
477 // 2) touch foo, touch FOO, unlink *FOO*
478 // Invalidating lookup_name fixes (1) and invalidating node_name fixes (2)
Zim33aea102020-06-19 15:49:47 +0100479 // |should_inval| invalidates lookup_name by using 0 timeout below and we explicitly
480 // invalidate node_name if different case
481 // Note that we invalidate async otherwise we will deadlock the kernel
482 if (name != node->GetName()) {
Zimf2b47b42020-09-16 14:54:06 +0100483 should_inval = true;
484 // Record that we have made a case insensitive lookup, this allows us invalidate nodes
485 // correctly on subsequent lookups for the case of |node|
486 node->SetCaseInsensitiveMatch();
487
Narayan Kamath8b296f52020-07-29 13:10:17 +0100488 // Make copies of the node name and path so we're not attempting to acquire
489 // any node locks from the invalidation thread. Depending on timing, we may end
490 // up invalidating the wrong inode but that shouldn't result in correctness issues.
491 const fuse_ino_t parent_ino = fuse->ToInode(parent);
492 const fuse_ino_t child_ino = fuse->ToInode(node);
493 const std::string& node_name = node->GetName();
Narayan Kamath8b296f52020-07-29 13:10:17 +0100494 std::thread t([=]() { fuse_inval(fuse->se, parent_ino, child_ino, node_name, path); });
Zim33aea102020-06-19 15:49:47 +0100495 t.detach();
496 }
Narayan Kamathaef84a12020-01-02 15:20:13 +0000497 }
Nandana Dutt154cb5d2020-06-04 11:53:31 +0100498 TRACE_NODE(node, req);
Zim89962312020-04-22 21:21:53 +0100499
Narayan Kamathfc0aa952019-12-31 13:31:49 +0000500 // This FS is not being exported via NFS so just a fixed generation number
501 // for now. If we do need this, we need to increment the generation ID each
502 // time the fuse daemon restarts because that's what it takes for us to
503 // reuse inode numbers.
504 e->generation = 0;
Zim8b2b6f22020-01-22 13:53:59 +0000505 e->ino = fuse->ToInode(node);
Zim867fece2020-09-23 15:23:19 +0100506 e->entry_timeout = get_entry_timeout(path, should_inval, node, fuse);
507 e->attr_timeout = get_attr_timeout(path, should_inval, node, fuse);
shafik1e3a2672019-08-16 14:51:55 +0100508 return node;
509}
510
shafik15e2d612019-10-31 20:10:25 +0000511static inline bool is_requesting_write(int flags) {
512 return flags & (O_WRONLY | O_RDWR);
513}
514
shafik1e3a2672019-08-16 14:51:55 +0100515namespace mediaprovider {
516namespace fuse {
517
518/**
519 * Function implementations
520 *
521 * These implement the various functions in fuse_lowlevel_ops
522 *
523 */
524
525static void pf_init(void* userdata, struct fuse_conn_info* conn) {
Alessio Balsinif220a962020-07-13 12:01:13 +0100526 struct fuse* fuse = reinterpret_cast<struct fuse*>(userdata);
527
Zima9fcd552019-08-29 15:17:04 +0100528 // We don't want a getattr request with every read request
Zim1100f342020-05-15 16:13:00 +0100529 conn->want &= ~FUSE_CAP_AUTO_INVAL_DATA & ~FUSE_CAP_READDIRPLUS_AUTO;
shafik8b57cd52019-09-06 10:51:29 +0100530 unsigned mask = (FUSE_CAP_SPLICE_WRITE | FUSE_CAP_SPLICE_MOVE | FUSE_CAP_SPLICE_READ |
531 FUSE_CAP_ASYNC_READ | FUSE_CAP_ATOMIC_O_TRUNC | FUSE_CAP_WRITEBACK_CACHE |
532 FUSE_CAP_EXPORT_SUPPORT | FUSE_CAP_FLOCK_LOCKS);
Alessio Balsinif220a962020-07-13 12:01:13 +0100533
534 if (fuse->passthrough) {
535 if (conn->capable & FUSE_CAP_PASSTHROUGH) {
Alessio Balsini2afe7f52020-12-09 13:13:11 +0000536#ifdef NO_FUSE_PASSTHROUGH_32BIT
537 // TODO(b/175151591) Linux compatibility layer for FUSE_DEV_IOC_PASSTHROUGH_OPEN
538 LOG(WARNING) << "Passthrough feature not supported on 32-bit userspace";
539 fuse->passthrough = false;
540#else
Alessio Balsinif220a962020-07-13 12:01:13 +0100541 mask |= FUSE_CAP_PASSTHROUGH;
Alessio Balsini2afe7f52020-12-09 13:13:11 +0000542#endif
Alessio Balsinif220a962020-07-13 12:01:13 +0100543 } else {
544 LOG(WARNING) << "Passthrough feature not supported by the kernel";
545 fuse->passthrough = false;
546 }
547 }
548
shafik1e3a2672019-08-16 14:51:55 +0100549 conn->want |= conn->capable & mask;
shafikb334a612019-09-30 20:38:16 +0100550 conn->max_read = MAX_READ_SIZE;
Zimd0435b22020-03-05 13:52:51 +0000551
Zimd0435b22020-03-05 13:52:51 +0000552 fuse->active->store(true, std::memory_order_release);
shafik1e3a2672019-08-16 14:51:55 +0100553}
554
Zim005cd812019-12-17 15:35:51 +0000555static void pf_destroy(void* userdata) {
556 struct fuse* fuse = reinterpret_cast<struct fuse*>(userdata);
557 LOG(INFO) << "DESTROY " << fuse->path;
Narayan Kamathaef84a12020-01-02 15:20:13 +0000558
559 node::DeleteTree(fuse->root);
shafik1e3a2672019-08-16 14:51:55 +0100560}
shafik1e3a2672019-08-16 14:51:55 +0100561
Ricky Wai44670762020-05-01 11:25:28 +0100562// Return true if the path is accessible for that uid.
563static bool is_app_accessible_path(MediaProviderWrapper* mp, const string& path, uid_t uid) {
564 if (uid < AID_APP_START) {
565 return true;
566 }
567
Martijn Coenen71435d52020-06-15 20:23:40 +0200568 if (path == "/storage/emulated") {
569 // Apps should never refer to /storage/emulated - they should be using the user-spcific
570 // subdirs, eg /storage/emulated/0
571 return false;
572 }
573
Ricky Wai44670762020-05-01 11:25:28 +0100574 std::smatch match;
575 if (std::regex_match(path, match, PATTERN_OWNED_PATH)) {
576 const std::string& pkg = match[1];
Abhijeet Kaur077fdd52020-05-28 13:59:31 +0100577 // .nomedia is not a valid package. .nomedia always exists in /Android/data directory,
578 // and it's not an external file/directory of any package
579 if (pkg == ".nomedia") {
580 return true;
581 }
Abhijeet Kaurb3ac2802020-10-07 16:42:42 +0100582 if (!mp->isUidAllowedAccessToDataOrObbPath(uid, path)) {
583 PLOG(WARNING) << "Invalid other package file access from " << uid << "(: " << path;
Ricky Wai44670762020-05-01 11:25:28 +0100584 return false;
585 }
586 }
587 return true;
588}
589
Zim859db932019-12-13 00:09:17 +0000590static std::regex storage_emulated_regex("^\\/storage\\/emulated\\/([0-9]+)");
Narayan Kamathaef84a12020-01-02 15:20:13 +0000591static node* do_lookup(fuse_req_t req, fuse_ino_t parent, const char* name,
592 struct fuse_entry_param* e, int* error_code) {
shafik1e3a2672019-08-16 14:51:55 +0100593 struct fuse* fuse = get_fuse(req);
Ricky Wai44670762020-05-01 11:25:28 +0100594 node* parent_node = fuse->FromInode(parent);
Ricky Waif40c4022020-04-15 19:00:06 +0100595 if (!parent_node) {
596 *error_code = ENOENT;
597 return nullptr;
598 }
Narayan Kamathaef84a12020-01-02 15:20:13 +0000599 string parent_path = parent_node->BuildPath();
Martijn Coenena5f19e82020-06-17 13:01:23 +0200600 // We should always allow lookups on the root, because failing them could cause
601 // bind mounts to be invalidated.
602 if (!fuse->IsRoot(parent_node) && !is_app_accessible_path(fuse->mp, parent_path, req->ctx.uid)) {
Ricky Wai44670762020-05-01 11:25:28 +0100603 *error_code = ENOENT;
604 return nullptr;
605 }
606
Cody Schuffelen338bd1a2020-02-21 17:40:56 +0000607 string child_path = parent_path + "/" + name;
Zim2c5eb692020-02-20 15:47:06 +0000608
Nandana Dutt154cb5d2020-06-04 11:53:31 +0100609 TRACE_NODE(parent_node, req);
Zimuzo Ezeozue67db40c2020-02-21 19:41:33 +0000610
Zim859db932019-12-13 00:09:17 +0000611 std::smatch match;
612 std::regex_search(child_path, match, storage_emulated_regex);
Zim5077a892020-09-08 12:55:12 +0100613
614 // Ensure the FuseDaemon user id matches the user id or cross-user lookups are allowed in
615 // requested path
Zimf0e3cd92020-01-03 00:57:43 +0000616 if (match.size() == 2 && std::to_string(getuid() / PER_USER_RANGE) != match[1].str()) {
Zim5077a892020-09-08 12:55:12 +0100617 // If user id mismatch, check cross-user lookups
Martijn Coenen44ef6302020-11-06 10:32:32 +0100618 long userId = strtol(match[1].str().c_str(), nullptr, 10);
619 if (userId < 0 || userId > MAX_USER_ID ||
620 !fuse->mp->ShouldAllowLookup(req->ctx.uid, userId)) {
Zim5077a892020-09-08 12:55:12 +0100621 *error_code = EACCES;
622 return nullptr;
623 }
Zim859db932019-12-13 00:09:17 +0000624 }
Narayan Kamathbbb779a2019-12-31 16:30:11 +0000625 return make_node_entry(req, parent_node, name, child_path, e, error_code);
shafik1e3a2672019-08-16 14:51:55 +0100626}
627
628static void pf_lookup(fuse_req_t req, fuse_ino_t parent, const char* name) {
Zimec8d5722019-12-05 07:26:13 +0000629 ATRACE_CALL();
shafik1e3a2672019-08-16 14:51:55 +0100630 struct fuse_entry_param e;
631
Narayan Kamathbbb779a2019-12-31 16:30:11 +0000632 int error_code = 0;
633 if (do_lookup(req, parent, name, &e, &error_code)) {
shafik1e3a2672019-08-16 14:51:55 +0100634 fuse_reply_entry(req, &e);
Narayan Kamathbbb779a2019-12-31 16:30:11 +0000635 } else {
636 CHECK(error_code != 0);
637 fuse_reply_err(req, error_code);
638 }
shafik1e3a2672019-08-16 14:51:55 +0100639}
640
Nandana Dutt154cb5d2020-06-04 11:53:31 +0100641static void do_forget(fuse_req_t req, struct fuse* fuse, fuse_ino_t ino, uint64_t nlookup) {
Ricky Wai44670762020-05-01 11:25:28 +0100642 node* node = fuse->FromInode(ino);
Nandana Dutt154cb5d2020-06-04 11:53:31 +0100643 TRACE_NODE(node, req);
shafik1e3a2672019-08-16 14:51:55 +0100644 if (node) {
Narayan Kamathaef84a12020-01-02 15:20:13 +0000645 // This is a narrowing conversion from an unsigned 64bit to a 32bit value. For
646 // some reason we only keep 32 bit refcounts but the kernel issues
647 // forget requests with a 64 bit counter.
Narayan Kamath568f17a2020-02-19 13:45:29 +0000648 node->Release(static_cast<uint32_t>(nlookup));
shafik1e3a2672019-08-16 14:51:55 +0100649 }
650}
651
652static void pf_forget(fuse_req_t req, fuse_ino_t ino, uint64_t nlookup) {
Ricky Wai44670762020-05-01 11:25:28 +0100653 // Always allow to forget so no need to check is_app_accessible_path()
Zimec8d5722019-12-05 07:26:13 +0000654 ATRACE_CALL();
Narayan Kamathaef84a12020-01-02 15:20:13 +0000655 node* node;
shafik1e3a2672019-08-16 14:51:55 +0100656 struct fuse* fuse = get_fuse(req);
657
Nandana Dutt154cb5d2020-06-04 11:53:31 +0100658 do_forget(req, fuse, ino, nlookup);
shafik1e3a2672019-08-16 14:51:55 +0100659 fuse_reply_none(req);
660}
661
662static void pf_forget_multi(fuse_req_t req,
663 size_t count,
664 struct fuse_forget_data* forgets) {
Zimec8d5722019-12-05 07:26:13 +0000665 ATRACE_CALL();
shafik1e3a2672019-08-16 14:51:55 +0100666 struct fuse* fuse = get_fuse(req);
667
Narayan Kamathaef84a12020-01-02 15:20:13 +0000668 for (int i = 0; i < count; i++) {
Nandana Dutt154cb5d2020-06-04 11:53:31 +0100669 do_forget(req, fuse, forgets[i].ino, forgets[i].nlookup);
Narayan Kamath768bea32019-12-27 16:23:23 +0000670 }
shafik1e3a2672019-08-16 14:51:55 +0100671 fuse_reply_none(req);
672}
673
674static void pf_getattr(fuse_req_t req,
675 fuse_ino_t ino,
676 struct fuse_file_info* fi) {
Zimec8d5722019-12-05 07:26:13 +0000677 ATRACE_CALL();
shafik1e3a2672019-08-16 14:51:55 +0100678 struct fuse* fuse = get_fuse(req);
Ricky Wai44670762020-05-01 11:25:28 +0100679 node* node = fuse->FromInode(ino);
Ricky Waif40c4022020-04-15 19:00:06 +0100680 if (!node) {
681 fuse_reply_err(req, ENOENT);
682 return;
683 }
Zim867fece2020-09-23 15:23:19 +0100684 const string& path = get_path(node);
Ricky Wai44670762020-05-01 11:25:28 +0100685 if (!is_app_accessible_path(fuse->mp, path, req->ctx.uid)) {
686 fuse_reply_err(req, ENOENT);
687 return;
688 }
Nandana Dutt154cb5d2020-06-04 11:53:31 +0100689 TRACE_NODE(node, req);
shafik1e3a2672019-08-16 14:51:55 +0100690
Narayan Kamathaef84a12020-01-02 15:20:13 +0000691 struct stat s;
shafik1e3a2672019-08-16 14:51:55 +0100692 memset(&s, 0, sizeof(s));
693 if (lstat(path.c_str(), &s) < 0) {
694 fuse_reply_err(req, errno);
695 } else {
Zim867fece2020-09-23 15:23:19 +0100696 fuse_reply_attr(
697 req, &s,
698 get_attr_timeout(path, node->GetTransforms() || node->HasCaseInsensitiveMatch(),
699 node, fuse));
shafik1e3a2672019-08-16 14:51:55 +0100700 }
701}
702
703static void pf_setattr(fuse_req_t req,
704 fuse_ino_t ino,
705 struct stat* attr,
706 int to_set,
707 struct fuse_file_info* fi) {
Zimec8d5722019-12-05 07:26:13 +0000708 ATRACE_CALL();
shafik1e3a2672019-08-16 14:51:55 +0100709 struct fuse* fuse = get_fuse(req);
Ricky Wai44670762020-05-01 11:25:28 +0100710 node* node = fuse->FromInode(ino);
shafik1e3a2672019-08-16 14:51:55 +0100711 if (!node) {
712 fuse_reply_err(req, ENOENT);
713 return;
714 }
Zim867fece2020-09-23 15:23:19 +0100715 const string& path = get_path(node);
Ricky Wai44670762020-05-01 11:25:28 +0100716 if (!is_app_accessible_path(fuse->mp, path, req->ctx.uid)) {
717 fuse_reply_err(req, ENOENT);
718 return;
719 }
Ricky Waif40c4022020-04-15 19:00:06 +0100720
Zimuzo Ezeozue435bd252020-06-30 20:15:17 +0000721 int fd = -1;
722 if (fi) {
723 // If we have a file_info, setattr was called with an fd so use the fd instead of path
724 handle* h = reinterpret_cast<handle*>(fi->fh);
725 fd = h->fd;
726 } else {
727 const struct fuse_ctx* ctx = fuse_req_ctx(req);
728 int status = fuse->mp->IsOpenAllowed(path, ctx->uid, true);
729 if (status) {
730 fuse_reply_err(req, EACCES);
731 return;
732 }
733 }
734 struct timespec times[2];
Nandana Dutt154cb5d2020-06-04 11:53:31 +0100735 TRACE_NODE(node, req);
shafik1e3a2672019-08-16 14:51:55 +0100736
737 /* XXX: incomplete implementation on purpose.
738 * chmod/chown should NEVER be implemented.*/
739
Zimuzo Ezeozue435bd252020-06-30 20:15:17 +0000740 if ((to_set & FUSE_SET_ATTR_SIZE)) {
741 int res = 0;
742 if (fd == -1) {
743 res = truncate64(path.c_str(), attr->st_size);
744 } else {
745 res = ftruncate64(fd, attr->st_size);
746 }
747
748 if (res < 0) {
749 fuse_reply_err(req, errno);
750 return;
751 }
shafik1e3a2672019-08-16 14:51:55 +0100752 }
753
754 /* Handle changing atime and mtime. If FATTR_ATIME_and FATTR_ATIME_NOW
755 * are both set, then set it to the current time. Else, set it to the
756 * time specified in the request. Same goes for mtime. Use utimensat(2)
757 * as it allows ATIME and MTIME to be changed independently, and has
758 * nanosecond resolution which fuse also has.
759 */
760 if (to_set & (FATTR_ATIME | FATTR_MTIME)) {
761 times[0].tv_nsec = UTIME_OMIT;
762 times[1].tv_nsec = UTIME_OMIT;
763 if (to_set & FATTR_ATIME) {
764 if (to_set & FATTR_ATIME_NOW) {
765 times[0].tv_nsec = UTIME_NOW;
766 } else {
Zim0056c4b2020-04-27 18:56:51 +0100767 times[0] = attr->st_atim;
shafik1e3a2672019-08-16 14:51:55 +0100768 }
769 }
Zim0056c4b2020-04-27 18:56:51 +0100770
shafik1e3a2672019-08-16 14:51:55 +0100771 if (to_set & FATTR_MTIME) {
772 if (to_set & FATTR_MTIME_NOW) {
773 times[1].tv_nsec = UTIME_NOW;
774 } else {
Zim0056c4b2020-04-27 18:56:51 +0100775 times[1] = attr->st_mtim;
shafik1e3a2672019-08-16 14:51:55 +0100776 }
777 }
Zimuzo Ezeozue67db40c2020-02-21 19:41:33 +0000778
Nandana Dutt154cb5d2020-06-04 11:53:31 +0100779 TRACE_NODE(node, req);
Zimuzo Ezeozue435bd252020-06-30 20:15:17 +0000780 int res = 0;
781 if (fd == -1) {
782 res = utimensat(-1, path.c_str(), times, 0);
783 } else {
784 res = futimens(fd, times);
785 }
786
787 if (res < 0) {
shafik1e3a2672019-08-16 14:51:55 +0100788 fuse_reply_err(req, errno);
789 return;
790 }
791 }
792
Zima9fcd552019-08-29 15:17:04 +0100793 lstat(path.c_str(), attr);
Zim867fece2020-09-23 15:23:19 +0100794 fuse_reply_attr(req, attr,
795 get_attr_timeout(path, node->GetTransforms() || node->HasCaseInsensitiveMatch(),
796 node, fuse));
shafik1e3a2672019-08-16 14:51:55 +0100797}
Zimb9730bf2019-11-30 15:10:04 +0000798
799static void pf_canonical_path(fuse_req_t req, fuse_ino_t ino)
shafik1e3a2672019-08-16 14:51:55 +0100800{
Ricky Wai44670762020-05-01 11:25:28 +0100801 struct fuse* fuse = get_fuse(req);
802 node* node = fuse->FromInode(ino);
Zim867fece2020-09-23 15:23:19 +0100803 const string& path = node ? get_path(node) : "";
Zimb9730bf2019-11-30 15:10:04 +0000804
Ricky Wai44670762020-05-01 11:25:28 +0100805 if (node && is_app_accessible_path(fuse->mp, path, req->ctx.uid)) {
Zimb9730bf2019-11-30 15:10:04 +0000806 // TODO(b/147482155): Check that uid has access to |path| and its contents
Ricky Wai44670762020-05-01 11:25:28 +0100807 fuse_reply_canonical_path(req, path.c_str());
Zimb9730bf2019-11-30 15:10:04 +0000808 return;
809 }
810 fuse_reply_err(req, ENOENT);
shafik1e3a2672019-08-16 14:51:55 +0100811}
Zimb9730bf2019-11-30 15:10:04 +0000812
shafik1e3a2672019-08-16 14:51:55 +0100813static void pf_mknod(fuse_req_t req,
814 fuse_ino_t parent,
815 const char* name,
816 mode_t mode,
817 dev_t rdev) {
Zimec8d5722019-12-05 07:26:13 +0000818 ATRACE_CALL();
shafik1e3a2672019-08-16 14:51:55 +0100819 struct fuse* fuse = get_fuse(req);
Ricky Wai44670762020-05-01 11:25:28 +0100820 node* parent_node = fuse->FromInode(parent);
shafik1e3a2672019-08-16 14:51:55 +0100821 if (!parent_node) {
822 fuse_reply_err(req, ENOENT);
823 return;
824 }
Ricky Waif40c4022020-04-15 19:00:06 +0100825 string parent_path = parent_node->BuildPath();
Ricky Wai44670762020-05-01 11:25:28 +0100826 if (!is_app_accessible_path(fuse->mp, parent_path, req->ctx.uid)) {
827 fuse_reply_err(req, ENOENT);
828 return;
829 }
Ricky Waif40c4022020-04-15 19:00:06 +0100830
Nandana Dutt154cb5d2020-06-04 11:53:31 +0100831 TRACE_NODE(parent_node, req);
Ricky Waif40c4022020-04-15 19:00:06 +0100832
Narayan Kamathaef84a12020-01-02 15:20:13 +0000833 const string child_path = parent_path + "/" + name;
shafik1e3a2672019-08-16 14:51:55 +0100834
835 mode = (mode & (~0777)) | 0664;
836 if (mknod(child_path.c_str(), mode, rdev) < 0) {
837 fuse_reply_err(req, errno);
838 return;
839 }
Narayan Kamathbbb779a2019-12-31 16:30:11 +0000840
841 int error_code = 0;
Narayan Kamathaef84a12020-01-02 15:20:13 +0000842 struct fuse_entry_param e;
Narayan Kamathbbb779a2019-12-31 16:30:11 +0000843 if (make_node_entry(req, parent_node, name, child_path, &e, &error_code)) {
shafik1e3a2672019-08-16 14:51:55 +0100844 fuse_reply_entry(req, &e);
Narayan Kamathbbb779a2019-12-31 16:30:11 +0000845 } else {
846 CHECK(error_code != 0);
847 fuse_reply_err(req, error_code);
848 }
shafik1e3a2672019-08-16 14:51:55 +0100849}
850
851static void pf_mkdir(fuse_req_t req,
852 fuse_ino_t parent,
853 const char* name,
854 mode_t mode) {
Zimec8d5722019-12-05 07:26:13 +0000855 ATRACE_CALL();
shafik1e3a2672019-08-16 14:51:55 +0100856 struct fuse* fuse = get_fuse(req);
Ricky Wai44670762020-05-01 11:25:28 +0100857 node* parent_node = fuse->FromInode(parent);
Ricky Waif40c4022020-04-15 19:00:06 +0100858 if (!parent_node) {
859 fuse_reply_err(req, ENOENT);
860 return;
861 }
Ricky Wai44670762020-05-01 11:25:28 +0100862 const struct fuse_ctx* ctx = fuse_req_ctx(req);
Narayan Kamathaef84a12020-01-02 15:20:13 +0000863 const string parent_path = parent_node->BuildPath();
Ricky Wai44670762020-05-01 11:25:28 +0100864 if (!is_app_accessible_path(fuse->mp, parent_path, ctx->uid)) {
865 fuse_reply_err(req, ENOENT);
866 return;
867 }
Narayan Kamath768bea32019-12-27 16:23:23 +0000868
Nandana Dutt154cb5d2020-06-04 11:53:31 +0100869 TRACE_NODE(parent_node, req);
shafik1e3a2672019-08-16 14:51:55 +0100870
Narayan Kamathaef84a12020-01-02 15:20:13 +0000871 const string child_path = parent_path + "/" + name;
shafik1e3a2672019-08-16 14:51:55 +0100872
shafike4fb1462020-01-29 16:25:23 +0000873 int status = fuse->mp->IsCreatingDirAllowed(child_path, ctx->uid);
Narayan Kamathbbb779a2019-12-31 16:30:11 +0000874 if (status) {
875 fuse_reply_err(req, status);
876 return;
877 }
878
shafik1e3a2672019-08-16 14:51:55 +0100879 mode = (mode & (~0777)) | 0775;
Narayan Kamathbbb779a2019-12-31 16:30:11 +0000880 if (mkdir(child_path.c_str(), mode) < 0) {
shafik1e3a2672019-08-16 14:51:55 +0100881 fuse_reply_err(req, errno);
882 return;
883 }
884
Narayan Kamathbbb779a2019-12-31 16:30:11 +0000885 int error_code = 0;
Narayan Kamathaef84a12020-01-02 15:20:13 +0000886 struct fuse_entry_param e;
Narayan Kamathbbb779a2019-12-31 16:30:11 +0000887 if (make_node_entry(req, parent_node, name, child_path, &e, &error_code)) {
shafik1e3a2672019-08-16 14:51:55 +0100888 fuse_reply_entry(req, &e);
Narayan Kamathbbb779a2019-12-31 16:30:11 +0000889 } else {
890 CHECK(error_code != 0);
891 fuse_reply_err(req, error_code);
892 }
shafik1e3a2672019-08-16 14:51:55 +0100893}
894
895static void pf_unlink(fuse_req_t req, fuse_ino_t parent, const char* name) {
Zimec8d5722019-12-05 07:26:13 +0000896 ATRACE_CALL();
shafik1e3a2672019-08-16 14:51:55 +0100897 struct fuse* fuse = get_fuse(req);
Ricky Wai44670762020-05-01 11:25:28 +0100898 node* parent_node = fuse->FromInode(parent);
Ricky Waif40c4022020-04-15 19:00:06 +0100899 if (!parent_node) {
900 fuse_reply_err(req, ENOENT);
901 return;
902 }
Ricky Wai44670762020-05-01 11:25:28 +0100903 const struct fuse_ctx* ctx = fuse_req_ctx(req);
Narayan Kamathaef84a12020-01-02 15:20:13 +0000904 const string parent_path = parent_node->BuildPath();
Ricky Wai44670762020-05-01 11:25:28 +0100905 if (!is_app_accessible_path(fuse->mp, parent_path, ctx->uid)) {
906 fuse_reply_err(req, ENOENT);
907 return;
908 }
shafik1e3a2672019-08-16 14:51:55 +0100909
Nandana Dutt154cb5d2020-06-04 11:53:31 +0100910 TRACE_NODE(parent_node, req);
shafik1e3a2672019-08-16 14:51:55 +0100911
Narayan Kamathaef84a12020-01-02 15:20:13 +0000912 const string child_path = parent_path + "/" + name;
shafik1e3a2672019-08-16 14:51:55 +0100913
shafike4fb1462020-01-29 16:25:23 +0000914 int status = fuse->mp->DeleteFile(child_path, ctx->uid);
Narayan Kamathbbb779a2019-12-31 16:30:11 +0000915 if (status) {
916 fuse_reply_err(req, status);
shafik1e3a2672019-08-16 14:51:55 +0100917 return;
918 }
Narayan Kamath768bea32019-12-27 16:23:23 +0000919
Zim53c2d702020-09-23 12:18:27 +0100920 // TODO(b/169306422): Log each deleted node
921 parent_node->SetDeletedForChild(name);
shafik1e3a2672019-08-16 14:51:55 +0100922 fuse_reply_err(req, 0);
923}
924
925static void pf_rmdir(fuse_req_t req, fuse_ino_t parent, const char* name) {
Zimec8d5722019-12-05 07:26:13 +0000926 ATRACE_CALL();
shafik1e3a2672019-08-16 14:51:55 +0100927 struct fuse* fuse = get_fuse(req);
Ricky Wai44670762020-05-01 11:25:28 +0100928 node* parent_node = fuse->FromInode(parent);
Ricky Waif40c4022020-04-15 19:00:06 +0100929 if (!parent_node) {
930 fuse_reply_err(req, ENOENT);
931 return;
932 }
Narayan Kamathaef84a12020-01-02 15:20:13 +0000933 const string parent_path = parent_node->BuildPath();
Ricky Wai44670762020-05-01 11:25:28 +0100934 if (!is_app_accessible_path(fuse->mp, parent_path, req->ctx.uid)) {
935 fuse_reply_err(req, ENOENT);
936 return;
937 }
Nandana Dutt154cb5d2020-06-04 11:53:31 +0100938 TRACE_NODE(parent_node, req);
shafik1e3a2672019-08-16 14:51:55 +0100939
Narayan Kamathaef84a12020-01-02 15:20:13 +0000940 const string child_path = parent_path + "/" + name;
shafik1e3a2672019-08-16 14:51:55 +0100941
Ricky Wai44670762020-05-01 11:25:28 +0100942 int status = fuse->mp->IsDeletingDirAllowed(child_path, req->ctx.uid);
Narayan Kamathbbb779a2019-12-31 16:30:11 +0000943 if (status) {
944 fuse_reply_err(req, status);
945 return;
946 }
947
948 if (rmdir(child_path.c_str()) < 0) {
shafik1e3a2672019-08-16 14:51:55 +0100949 fuse_reply_err(req, errno);
950 return;
951 }
Narayan Kamath768bea32019-12-27 16:23:23 +0000952
Narayan Kamatheca34252020-02-11 13:08:37 +0000953 node* child_node = parent_node->LookupChildByName(name, false /* acquire */);
Nandana Dutt154cb5d2020-06-04 11:53:31 +0100954 TRACE_NODE(child_node, req);
Narayan Kamathaef84a12020-01-02 15:20:13 +0000955 if (child_node) {
956 child_node->SetDeleted();
shafik1e3a2672019-08-16 14:51:55 +0100957 }
shafik1e3a2672019-08-16 14:51:55 +0100958
959 fuse_reply_err(req, 0);
960}
961/*
962static void pf_symlink(fuse_req_t req, const char* link, fuse_ino_t parent,
963 const char* name)
964{
965 cout << "TODO:" << __func__;
966}
967*/
Sahana Rao2c416032019-12-31 13:41:00 +0000968static int do_rename(fuse_req_t req, fuse_ino_t parent, const char* name, fuse_ino_t new_parent,
969 const char* new_name, unsigned int flags) {
Zimec8d5722019-12-05 07:26:13 +0000970 ATRACE_CALL();
shafik1e3a2672019-08-16 14:51:55 +0100971 struct fuse* fuse = get_fuse(req);
shafik1e3a2672019-08-16 14:51:55 +0100972
Sahana Rao2c416032019-12-31 13:41:00 +0000973 if (flags != 0) {
Sahana Rao2c416032019-12-31 13:41:00 +0000974 return EINVAL;
975 }
Narayan Kamath768bea32019-12-27 16:23:23 +0000976
Ricky Wai44670762020-05-01 11:25:28 +0100977 node* old_parent_node = fuse->FromInode(parent);
Ricky Waif40c4022020-04-15 19:00:06 +0100978 if (!old_parent_node) return ENOENT;
Ricky Wai44670762020-05-01 11:25:28 +0100979 const struct fuse_ctx* ctx = fuse_req_ctx(req);
Narayan Kamathaef84a12020-01-02 15:20:13 +0000980 const string old_parent_path = old_parent_node->BuildPath();
Ricky Wai44670762020-05-01 11:25:28 +0100981 if (!is_app_accessible_path(fuse->mp, old_parent_path, ctx->uid)) {
982 return ENOENT;
983 }
984
985 node* new_parent_node = fuse->FromInode(new_parent);
Ricky Waif40c4022020-04-15 19:00:06 +0100986 if (!new_parent_node) return ENOENT;
Narayan Kamathaef84a12020-01-02 15:20:13 +0000987 const string new_parent_path = new_parent_node->BuildPath();
Ricky Wai44670762020-05-01 11:25:28 +0100988 if (!is_app_accessible_path(fuse->mp, new_parent_path, ctx->uid)) {
989 return ENOENT;
990 }
Narayan Kamath768bea32019-12-27 16:23:23 +0000991
Narayan Kamathaef84a12020-01-02 15:20:13 +0000992 if (!old_parent_node || !new_parent_node) {
993 return ENOENT;
994 } else if (parent == new_parent && name == new_name) {
995 // No rename required.
996 return 0;
shafik1e3a2672019-08-16 14:51:55 +0100997 }
998
Nandana Dutt154cb5d2020-06-04 11:53:31 +0100999 TRACE_NODE(old_parent_node, req);
1000 TRACE_NODE(new_parent_node, req);
shafik1e3a2672019-08-16 14:51:55 +01001001
Zim53c2d702020-09-23 12:18:27 +01001002 const string old_child_path = old_parent_path + "/" + name;
Narayan Kamathaef84a12020-01-02 15:20:13 +00001003 const string new_child_path = new_parent_path + "/" + new_name;
1004
Sahana Rao182ec6b2020-01-03 15:00:46 +00001005 // TODO(b/147408834): Check ENOTEMPTY & EEXIST error conditions before JNI call.
shafike4fb1462020-01-29 16:25:23 +00001006 const int res = fuse->mp->Rename(old_child_path, new_child_path, req->ctx.uid);
Narayan Kamathaef84a12020-01-02 15:20:13 +00001007 // TODO(b/145663158): Lookups can go out of sync if file/directory is actually moved but
1008 // EFAULT/EIO is reported due to JNI exception.
1009 if (res == 0) {
Zim53c2d702020-09-23 12:18:27 +01001010 // TODO(b/169306422): Log each renamed node
1011 old_parent_node->RenameChild(name, new_name, new_parent_node);
shafik1e3a2672019-08-16 14:51:55 +01001012 }
Narayan Kamath768bea32019-12-27 16:23:23 +00001013 return res;
1014}
shafik1e3a2672019-08-16 14:51:55 +01001015
Sahana Rao2c416032019-12-31 13:41:00 +00001016static void pf_rename(fuse_req_t req, fuse_ino_t parent, const char* name, fuse_ino_t new_parent,
1017 const char* new_name, unsigned int flags) {
1018 int res = do_rename(req, parent, name, new_parent, new_name, flags);
shafik1e3a2672019-08-16 14:51:55 +01001019 fuse_reply_err(req, res);
1020}
Narayan Kamath768bea32019-12-27 16:23:23 +00001021
shafik1e3a2672019-08-16 14:51:55 +01001022/*
Sahana Rao2c416032019-12-31 13:41:00 +00001023static void pf_link(fuse_req_t req, fuse_ino_t ino, fuse_ino_t new_parent,
1024 const char* new_name)
shafik1e3a2672019-08-16 14:51:55 +01001025{
1026 cout << "TODO:" << __func__;
1027}
1028*/
1029
Zim9aa6f542020-10-19 15:39:33 +01001030static handle* create_handle_for_node(struct fuse* fuse, const string& path, int fd, uid_t uid,
1031 node* node, const RedactionInfo* ri, int* keep_cache) {
Zimc05c60f2020-03-05 13:02:26 +00001032 std::lock_guard<std::recursive_mutex> guard(fuse->lock);
Zimc05c60f2020-03-05 13:02:26 +00001033
Zim148cbe22020-11-17 15:58:29 +00001034 bool redaction_needed = ri->isRedactionNeeded();
1035 handle* handle = nullptr;
1036
1037 if (fuse->passthrough) {
Zim329ba2c2020-09-16 14:23:26 +01001038 *keep_cache = 1;
Zim148cbe22020-11-17 15:58:29 +00001039 handle = new struct handle(fd, ri, true /* cached */, !redaction_needed /* passthrough */,
1040 uid);
1041 } else {
1042 // Without fuse->passthrough, we don't want to use the FUSE VFS cache in two cases:
1043 // 1. When redaction is needed because app A with EXIF access might access
1044 // a region that should have been redacted for app B without EXIF access, but app B on
1045 // a subsequent read, will be able to see the EXIF data because the read request for
1046 // that region will be served from cache and not get to the FUSE daemon
1047 // 2. When the file has a read or write lock on it. This means that the MediaProvider
1048 // has given an fd to the lower file system to an app. There are two cases where using
1049 // the cache in this case can be a problem:
1050 // a. Writing to a FUSE fd with caching enabled will use the write-back cache and a
1051 // subsequent read from the lower fs fd will not see the write.
1052 // b. Reading from a FUSE fd with caching enabled may not see the latest writes using
1053 // the lower fs fd because those writes did not go through the FUSE layer and reads from
1054 // FUSE after that write may be served from cache
1055 bool has_redacted = node->HasRedactedCache();
1056 bool is_redaction_change =
1057 (redaction_needed && !has_redacted) || (!redaction_needed && has_redacted);
1058 bool is_cached_file_open = node->HasCachedHandle();
1059 bool direct_io = (is_cached_file_open && is_redaction_change) || is_file_locked(fd, path);
1060
1061 if (!is_cached_file_open && is_redaction_change) {
1062 node->SetRedactedCache(redaction_needed);
1063 // Purges stale page cache before open
1064 *keep_cache = 0;
1065 } else {
1066 *keep_cache = 1;
1067 }
1068 handle = new struct handle(fd, ri, !direct_io /* cached */, false /* passthrough */, uid);
Zim329ba2c2020-09-16 14:23:26 +01001069 }
1070
Zim148cbe22020-11-17 15:58:29 +00001071 node->AddHandle(handle);
1072 return handle;
Zimc05c60f2020-03-05 13:02:26 +00001073}
1074
Alessio Balsinif220a962020-07-13 12:01:13 +01001075bool do_passthrough_enable(fuse_req_t req, struct fuse_file_info* fi, unsigned int fd) {
1076 int passthrough_fh = fuse_passthrough_enable(req, fd);
1077
1078 if (passthrough_fh <= 0) {
1079 return false;
1080 }
1081
1082 fi->passthrough_fh = passthrough_fh;
1083 return true;
1084}
1085
shafik1e3a2672019-08-16 14:51:55 +01001086static void pf_open(fuse_req_t req, fuse_ino_t ino, struct fuse_file_info* fi) {
Zimec8d5722019-12-05 07:26:13 +00001087 ATRACE_CALL();
shafik1e3a2672019-08-16 14:51:55 +01001088 struct fuse* fuse = get_fuse(req);
Ricky Wai44670762020-05-01 11:25:28 +01001089 node* node = fuse->FromInode(ino);
shafik1e3a2672019-08-16 14:51:55 +01001090 if (!node) {
1091 fuse_reply_err(req, ENOENT);
1092 return;
1093 }
Ricky Wai44670762020-05-01 11:25:28 +01001094 const struct fuse_ctx* ctx = fuse_req_ctx(req);
Zime9ae6ee2020-11-26 15:38:13 +00001095 const string& io_path = get_path(node);
Zimf7ec8672020-10-28 18:25:45 +00001096 const string& build_path = node->BuildPath();
Zime9ae6ee2020-11-26 15:38:13 +00001097 if (!is_app_accessible_path(fuse->mp, io_path, ctx->uid)) {
Ricky Wai44670762020-05-01 11:25:28 +01001098 fuse_reply_err(req, ENOENT);
1099 return;
1100 }
Ricky Waif40c4022020-04-15 19:00:06 +01001101
Nandana Dutt154cb5d2020-06-04 11:53:31 +01001102 TRACE_NODE(node, req) << (is_requesting_write(fi->flags) ? "write" : "read");
shafik1e3a2672019-08-16 14:51:55 +01001103
shafik15e2d612019-10-31 20:10:25 +00001104 if (fi->flags & O_DIRECT) {
1105 fi->flags &= ~O_DIRECT;
1106 fi->direct_io = true;
1107 }
1108
Zim867fece2020-09-23 15:23:19 +01001109 // TODO: If transform, disallow write
Zimf7ec8672020-10-28 18:25:45 +00001110 // Force permission check with the build path because the MediaProvider database might not be
1111 // aware of the io_path
1112 int status = fuse->mp->IsOpenAllowed(build_path, ctx->uid, is_requesting_write(fi->flags));
Narayan Kamathbbb779a2019-12-31 16:30:11 +00001113 if (status) {
1114 fuse_reply_err(req, status);
1115 return;
1116 }
1117
Martijn Coenen2d7f1be2020-02-15 07:44:12 +01001118 // With the writeback cache enabled, FUSE may generate READ requests even for files that
1119 // were opened O_WRONLY; so make sure we open it O_RDWR instead.
1120 int open_flags = fi->flags;
1121 if (open_flags & O_WRONLY) {
1122 open_flags &= ~O_WRONLY;
1123 open_flags |= O_RDWR;
1124 }
1125
Zim1b14f2f2020-07-03 07:05:53 +01001126 if (open_flags & O_APPEND) {
1127 open_flags &= ~O_APPEND;
1128 }
1129
Zime9ae6ee2020-11-26 15:38:13 +00001130 const int fd = open(io_path.c_str(), open_flags);
Narayan Kamathbbb779a2019-12-31 16:30:11 +00001131 if (fd < 0) {
shafik1e3a2672019-08-16 14:51:55 +01001132 fuse_reply_err(req, errno);
1133 return;
1134 }
shafik15e2d612019-10-31 20:10:25 +00001135
shafikc580b6d2019-12-10 18:45:17 +00001136 // We don't redact if the caller was granted write permission for this file
Narayan Kamathbbb779a2019-12-31 16:30:11 +00001137 std::unique_ptr<RedactionInfo> ri;
shafikc580b6d2019-12-10 18:45:17 +00001138 if (is_requesting_write(fi->flags)) {
Narayan Kamathbbb779a2019-12-31 16:30:11 +00001139 ri = std::make_unique<RedactionInfo>();
shafikc580b6d2019-12-10 18:45:17 +00001140 } else {
Zime9ae6ee2020-11-26 15:38:13 +00001141 ri = fuse->mp->GetRedactionInfo(build_path, io_path, req->ctx.uid, req->ctx.pid);
shafikc580b6d2019-12-10 18:45:17 +00001142 }
1143
Narayan Kamathbbb779a2019-12-31 16:30:11 +00001144 if (!ri) {
1145 close(fd);
1146 fuse_reply_err(req, EFAULT);
shafikc580b6d2019-12-10 18:45:17 +00001147 return;
1148 }
1149
Zim329ba2c2020-09-16 14:23:26 +01001150 int keep_cache = 1;
Zime9ae6ee2020-11-26 15:38:13 +00001151 handle* h = create_handle_for_node(fuse, io_path, fd, req->ctx.uid, node, ri.release(),
1152 &keep_cache);
shafik1e3a2672019-08-16 14:51:55 +01001153 fi->fh = ptr_to_id(h);
Zim329ba2c2020-09-16 14:23:26 +01001154 fi->keep_cache = keep_cache;
Zimc05c60f2020-03-05 13:02:26 +00001155 fi->direct_io = !h->cached;
Alessio Balsinif220a962020-07-13 12:01:13 +01001156
1157 // TODO(b/173190192) ensuring that h->cached must be enabled in order to
1158 // user FUSE passthrough is a conservative rule and might be dropped as
1159 // soon as demonstrated its correctness.
Zim148cbe22020-11-17 15:58:29 +00001160 if (h->passthrough) {
Alessio Balsinif220a962020-07-13 12:01:13 +01001161 if (!do_passthrough_enable(req, fi, fd)) {
Zim148cbe22020-11-17 15:58:29 +00001162 // TODO: Should we crash here so we can find errors easily?
Zime9ae6ee2020-11-26 15:38:13 +00001163 PLOG(ERROR) << "Passthrough OPEN failed for " << io_path;
Zim148cbe22020-11-17 15:58:29 +00001164 fuse_reply_err(req, EFAULT);
1165 return;
Alessio Balsinif220a962020-07-13 12:01:13 +01001166 }
1167 }
1168
shafik1e3a2672019-08-16 14:51:55 +01001169 fuse_reply_open(req, fi);
1170}
1171
shafikc3f62672019-08-30 11:15:48 +01001172static void do_read(fuse_req_t req, size_t size, off_t off, struct fuse_file_info* fi) {
shafika2ae9072019-10-28 12:16:00 +00001173 handle* h = reinterpret_cast<handle*>(fi->fh);
shafik1e3a2672019-08-16 14:51:55 +01001174 struct fuse_bufvec buf = FUSE_BUFVEC_INIT(size);
1175
1176 buf.buf[0].fd = h->fd;
1177 buf.buf[0].pos = off;
1178 buf.buf[0].flags =
1179 (enum fuse_buf_flags) (FUSE_BUF_IS_FD | FUSE_BUF_FD_SEEK);
1180
1181 fuse_reply_data(req, &buf, (enum fuse_buf_copy_flags) 0);
1182}
shafikc3f62672019-08-30 11:15:48 +01001183
shafikc3f62672019-08-30 11:15:48 +01001184/**
1185 * Sets the parameters for a fuse_buf that reads from memory, including flags.
shafikb334a612019-09-30 20:38:16 +01001186 * Makes buf->mem point to an already mapped region of zeroized memory.
1187 * This memory is read only.
shafikc3f62672019-08-30 11:15:48 +01001188 */
shafikb334a612019-09-30 20:38:16 +01001189static void create_mem_fuse_buf(size_t size, fuse_buf* buf, struct fuse* fuse) {
shafikc3f62672019-08-30 11:15:48 +01001190 buf->size = size;
shafikb334a612019-09-30 20:38:16 +01001191 buf->mem = fuse->zero_addr;
shafikc3f62672019-08-30 11:15:48 +01001192 buf->flags = static_cast<fuse_buf_flags>(0 /*read from fuse_buf.mem*/);
1193 buf->pos = -1;
1194 buf->fd = -1;
1195}
1196
1197/**
1198 * Sets the parameters for a fuse_buf that reads from file, including flags.
1199 */
1200static void create_file_fuse_buf(size_t size, off_t pos, int fd, fuse_buf* buf) {
1201 buf->size = size;
1202 buf->fd = fd;
1203 buf->pos = pos;
1204 buf->flags = static_cast<fuse_buf_flags>(FUSE_BUF_IS_FD | FUSE_BUF_FD_SEEK);
1205 buf->mem = nullptr;
1206}
1207
1208static void do_read_with_redaction(fuse_req_t req, size_t size, off_t off, fuse_file_info* fi) {
shafika2ae9072019-10-28 12:16:00 +00001209 handle* h = reinterpret_cast<handle*>(fi->fh);
shafikc3f62672019-08-30 11:15:48 +01001210
Narayan Kamath11700c02020-10-06 09:15:34 +01001211 std::vector<ReadRange> ranges;
1212 h->ri->getReadRanges(off, size, &ranges);
1213
1214 // As an optimization, return early if there are no ranges to redact.
1215 if (ranges.size() == 0) {
shafikc3f62672019-08-30 11:15:48 +01001216 do_read(req, size, off, fi);
1217 return;
1218 }
Narayan Kamath11700c02020-10-06 09:15:34 +01001219
1220 const size_t num_bufs = ranges.size();
shafikc3f62672019-08-30 11:15:48 +01001221 auto bufvec_ptr = std::unique_ptr<fuse_bufvec, decltype(free)*>{
1222 reinterpret_cast<fuse_bufvec*>(
1223 malloc(sizeof(fuse_bufvec) + (num_bufs - 1) * sizeof(fuse_buf))),
1224 free};
1225 fuse_bufvec& bufvec = *bufvec_ptr;
1226
1227 // initialize bufvec
1228 bufvec.count = num_bufs;
1229 bufvec.idx = 0;
1230 bufvec.off = 0;
shafikc3f62672019-08-30 11:15:48 +01001231
shafikc3f62672019-08-30 11:15:48 +01001232 for (int i = 0; i < num_bufs; ++i) {
Narayan Kamath11700c02020-10-06 09:15:34 +01001233 const ReadRange& range = ranges[i];
1234 if (range.is_redaction) {
1235 create_mem_fuse_buf(range.size, &(bufvec.buf[i]), get_fuse(req));
shafikc3f62672019-08-30 11:15:48 +01001236 } else {
Narayan Kamath11700c02020-10-06 09:15:34 +01001237 create_file_fuse_buf(range.size, range.start, h->fd, &(bufvec.buf[i]));
shafikc3f62672019-08-30 11:15:48 +01001238 }
shafikc3f62672019-08-30 11:15:48 +01001239 }
1240
1241 fuse_reply_data(req, &bufvec, static_cast<fuse_buf_copy_flags>(0));
1242}
1243
1244static void pf_read(fuse_req_t req, fuse_ino_t ino, size_t size, off_t off,
1245 struct fuse_file_info* fi) {
Zimec8d5722019-12-05 07:26:13 +00001246 ATRACE_CALL();
shafika2ae9072019-10-28 12:16:00 +00001247 handle* h = reinterpret_cast<handle*>(fi->fh);
shafikc3f62672019-08-30 11:15:48 +01001248 struct fuse* fuse = get_fuse(req);
shafikc3f62672019-08-30 11:15:48 +01001249
Zim867fece2020-09-23 15:23:19 +01001250 node* node = fuse->FromInode(ino);
1251
1252 if (!node->IsTransformsComplete()) {
1253 if (!fuse->mp->Transform(node->BuildPath(), node->GetIoPath(), node->GetTransforms(),
Zim9aa6f542020-10-19 15:39:33 +01001254 h->uid)) {
Zim867fece2020-09-23 15:23:19 +01001255 fuse_reply_err(req, EFAULT);
1256 return;
1257 }
1258 node->SetTransformsComplete();
1259 }
1260
Paul Lawrence7e4a5a82019-09-27 21:39:49 +02001261 fuse->fadviser.Record(h->fd, size);
1262
shafikc3f62672019-08-30 11:15:48 +01001263 if (h->ri->isRedactionNeeded()) {
1264 do_read_with_redaction(req, size, off, fi);
1265 } else {
1266 do_read(req, size, off, fi);
1267 }
1268}
1269
shafik1e3a2672019-08-16 14:51:55 +01001270/*
1271static void pf_write(fuse_req_t req, fuse_ino_t ino, const char* buf,
1272 size_t size, off_t off, struct fuse_file_info* fi)
1273{
1274 cout << "TODO:" << __func__;
1275}
1276*/
1277
1278static void pf_write_buf(fuse_req_t req,
1279 fuse_ino_t ino,
1280 struct fuse_bufvec* bufv,
1281 off_t off,
1282 struct fuse_file_info* fi) {
Zimec8d5722019-12-05 07:26:13 +00001283 ATRACE_CALL();
shafika2ae9072019-10-28 12:16:00 +00001284 handle* h = reinterpret_cast<handle*>(fi->fh);
shafik1e3a2672019-08-16 14:51:55 +01001285 struct fuse_bufvec buf = FUSE_BUFVEC_INIT(fuse_buf_size(bufv));
1286 ssize_t size;
Paul Lawrence7e4a5a82019-09-27 21:39:49 +02001287 struct fuse* fuse = get_fuse(req);
shafik1e3a2672019-08-16 14:51:55 +01001288
1289 buf.buf[0].fd = h->fd;
1290 buf.buf[0].pos = off;
1291 buf.buf[0].flags =
1292 (enum fuse_buf_flags) (FUSE_BUF_IS_FD | FUSE_BUF_FD_SEEK);
1293 size = fuse_buf_copy(&buf, bufv, (enum fuse_buf_copy_flags) 0);
1294
1295 if (size < 0)
1296 fuse_reply_err(req, -size);
Paul Lawrence7e4a5a82019-09-27 21:39:49 +02001297 else {
shafik1e3a2672019-08-16 14:51:55 +01001298 fuse_reply_write(req, size);
Paul Lawrence7e4a5a82019-09-27 21:39:49 +02001299 fuse->fadviser.Record(h->fd, size);
1300 }
shafik1e3a2672019-08-16 14:51:55 +01001301}
1302// Haven't tested this one. Not sure what calls it.
1303#if 0
1304static void pf_copy_file_range(fuse_req_t req, fuse_ino_t ino_in,
1305 off_t off_in, struct fuse_file_info* fi_in,
1306 fuse_ino_t ino_out, off_t off_out,
1307 struct fuse_file_info* fi_out, size_t len,
1308 int flags)
1309{
shafika2ae9072019-10-28 12:16:00 +00001310 handle* h_in = reinterpret_cast<handle *>(fi_in->fh);
1311 handle* h_out = reinterpret_cast<handle *>(fi_out->fh);
shafik1e3a2672019-08-16 14:51:55 +01001312 struct fuse_bufvec buf_in = FUSE_BUFVEC_INIT(len);
1313 struct fuse_bufvec buf_out = FUSE_BUFVEC_INIT(len);
1314 ssize_t size;
1315
1316 buf_in.buf[0].fd = h_in->fd;
1317 buf_in.buf[0].pos = off_in;
1318 buf_in.buf[0].flags = (enum fuse_buf_flags)(FUSE_BUF_IS_FD|FUSE_BUF_FD_SEEK);
1319
1320 buf_out.buf[0].fd = h_out->fd;
1321 buf_out.buf[0].pos = off_out;
1322 buf_out.buf[0].flags = (enum fuse_buf_flags)(FUSE_BUF_IS_FD|FUSE_BUF_FD_SEEK);
1323 size = fuse_buf_copy(&buf_out, &buf_in, (enum fuse_buf_copy_flags) 0);
1324
1325 if (size < 0) {
1326 fuse_reply_err(req, -size);
1327 }
1328
1329 fuse_reply_write(req, size);
1330}
1331#endif
shafik1e3a2672019-08-16 14:51:55 +01001332
1333static void pf_release(fuse_req_t req,
1334 fuse_ino_t ino,
1335 struct fuse_file_info* fi) {
Zimec8d5722019-12-05 07:26:13 +00001336 ATRACE_CALL();
shafik1e3a2672019-08-16 14:51:55 +01001337 struct fuse* fuse = get_fuse(req);
Kyle Tso613b7ab2019-12-24 06:30:00 +00001338
Ricky Wai44670762020-05-01 11:25:28 +01001339 node* node = fuse->FromInode(ino);
Narayan Kamathaef84a12020-01-02 15:20:13 +00001340 handle* h = reinterpret_cast<handle*>(fi->fh);
Nandana Dutt154cb5d2020-06-04 11:53:31 +01001341 TRACE_NODE(node, req);
shafik15e2d612019-10-31 20:10:25 +00001342
Narayan Kamathaef84a12020-01-02 15:20:13 +00001343 fuse->fadviser.Close(h->fd);
Narayan Kamathaef84a12020-01-02 15:20:13 +00001344 if (node) {
1345 node->DestroyHandle(h);
Zimedbe69e2019-12-13 18:49:36 +00001346 }
Narayan Kamath768bea32019-12-27 16:23:23 +00001347
shafik1e3a2672019-08-16 14:51:55 +01001348 fuse_reply_err(req, 0);
1349}
1350
1351static int do_sync_common(int fd, bool datasync) {
1352 int res = datasync ? fdatasync(fd) : fsync(fd);
1353
1354 if (res == -1) return errno;
1355 return 0;
1356}
1357
1358static void pf_fsync(fuse_req_t req,
1359 fuse_ino_t ino,
1360 int datasync,
1361 struct fuse_file_info* fi) {
Zimec8d5722019-12-05 07:26:13 +00001362 ATRACE_CALL();
shafika2ae9072019-10-28 12:16:00 +00001363 handle* h = reinterpret_cast<handle*>(fi->fh);
shafik1e3a2672019-08-16 14:51:55 +01001364 int err = do_sync_common(h->fd, datasync);
1365
1366 fuse_reply_err(req, err);
1367}
1368
1369static void pf_fsyncdir(fuse_req_t req,
1370 fuse_ino_t ino,
1371 int datasync,
1372 struct fuse_file_info* fi) {
Narayan Kamathaef84a12020-01-02 15:20:13 +00001373 dirhandle* h = reinterpret_cast<dirhandle*>(fi->fh);
shafik1e3a2672019-08-16 14:51:55 +01001374 int err = do_sync_common(dirfd(h->d), datasync);
1375
1376 fuse_reply_err(req, err);
1377}
1378
1379static void pf_opendir(fuse_req_t req,
1380 fuse_ino_t ino,
1381 struct fuse_file_info* fi) {
Zimec8d5722019-12-05 07:26:13 +00001382 ATRACE_CALL();
shafik1e3a2672019-08-16 14:51:55 +01001383 struct fuse* fuse = get_fuse(req);
Ricky Wai44670762020-05-01 11:25:28 +01001384 node* node = fuse->FromInode(ino);
shafik1e3a2672019-08-16 14:51:55 +01001385 if (!node) {
1386 fuse_reply_err(req, ENOENT);
1387 return;
1388 }
Ricky Wai44670762020-05-01 11:25:28 +01001389 const struct fuse_ctx* ctx = fuse_req_ctx(req);
Ricky Waif40c4022020-04-15 19:00:06 +01001390 const string path = node->BuildPath();
Ricky Wai44670762020-05-01 11:25:28 +01001391 if (!is_app_accessible_path(fuse->mp, path, ctx->uid)) {
1392 fuse_reply_err(req, ENOENT);
1393 return;
1394 }
Ricky Waif40c4022020-04-15 19:00:06 +01001395
Nandana Dutt154cb5d2020-06-04 11:53:31 +01001396 TRACE_NODE(node, req);
shafik1e3a2672019-08-16 14:51:55 +01001397
Nandana Dutt5fc32012020-06-25 10:55:52 +01001398 int status = fuse->mp->IsOpendirAllowed(path, ctx->uid, /* forWrite */ false);
Narayan Kamathbbb779a2019-12-31 16:30:11 +00001399 if (status) {
1400 fuse_reply_err(req, status);
1401 return;
1402 }
1403
1404 DIR* dir = opendir(path.c_str());
1405 if (!dir) {
shafik1e3a2672019-08-16 14:51:55 +01001406 fuse_reply_err(req, errno);
1407 return;
1408 }
Narayan Kamathbbb779a2019-12-31 16:30:11 +00001409
Narayan Kamathaef84a12020-01-02 15:20:13 +00001410 dirhandle* h = new dirhandle(dir);
1411 node->AddDirHandle(h);
Narayan Kamathbbb779a2019-12-31 16:30:11 +00001412
shafik1e3a2672019-08-16 14:51:55 +01001413 fi->fh = ptr_to_id(h);
1414 fuse_reply_open(req, fi);
1415}
1416
1417#define READDIR_BUF 8192LU
1418
1419static void do_readdir_common(fuse_req_t req,
1420 fuse_ino_t ino,
1421 size_t size,
1422 off_t off,
1423 struct fuse_file_info* fi,
1424 bool plus) {
1425 struct fuse* fuse = get_fuse(req);
Narayan Kamathaef84a12020-01-02 15:20:13 +00001426 dirhandle* h = reinterpret_cast<dirhandle*>(fi->fh);
Jeff Sharkey7469b982019-08-28 16:51:02 -06001427 size_t len = std::min<size_t>(size, READDIR_BUF);
shafik1e3a2672019-08-16 14:51:55 +01001428 char buf[READDIR_BUF];
1429 size_t used = 0;
Sahana Raoa82bd6a2019-10-10 18:10:37 +01001430 std::shared_ptr<DirectoryEntry> de;
1431
shafik1e3a2672019-08-16 14:51:55 +01001432 struct fuse_entry_param e;
Sahana Rao6f9ea982019-09-20 12:21:33 +01001433 size_t entry_size = 0;
shafik1e3a2672019-08-16 14:51:55 +01001434
Ricky Wai44670762020-05-01 11:25:28 +01001435 node* node = fuse->FromInode(ino);
Ricky Waif40c4022020-04-15 19:00:06 +01001436 if (!node) {
1437 fuse_reply_err(req, ENOENT);
1438 return;
1439 }
Narayan Kamathaef84a12020-01-02 15:20:13 +00001440 const string path = node->BuildPath();
Ricky Wai44670762020-05-01 11:25:28 +01001441 if (!is_app_accessible_path(fuse->mp, path, req->ctx.uid)) {
1442 fuse_reply_err(req, ENOENT);
1443 return;
1444 }
Narayan Kamath768bea32019-12-27 16:23:23 +00001445
Nandana Dutt154cb5d2020-06-04 11:53:31 +01001446 TRACE_NODE(node, req);
Sahana Raoa82bd6a2019-10-10 18:10:37 +01001447 // Get all directory entries from MediaProvider on first readdir() call of
1448 // directory handle. h->next_off = 0 indicates that current readdir() call
1449 // is first readdir() call for the directory handle, Avoid multiple JNI calls
1450 // for single directory handle.
1451 if (h->next_off == 0) {
Sahana Rao71693442019-11-13 13:48:07 +00001452 h->de = fuse->mp->GetDirectoryEntries(req->ctx.uid, path, h->d);
shafik1e3a2672019-08-16 14:51:55 +01001453 }
Sahana Raoa82bd6a2019-10-10 18:10:37 +01001454 // If the last entry in the previous readdir() call was rejected due to
1455 // buffer capacity constraints, update directory offset to start from
1456 // previously rejected entry. Directory offset can also change if there was
Sahana Rao71693442019-11-13 13:48:07 +00001457 // a seekdir() on the given directory handle.
Sahana Raoa82bd6a2019-10-10 18:10:37 +01001458 if (off != h->next_off) {
1459 h->next_off = off;
1460 }
1461 const int num_directory_entries = h->de.size();
Sahana Rao71693442019-11-13 13:48:07 +00001462 // Check for errors. Any error/exception occurred while obtaining directory
1463 // entries will be indicated by marking first directory entry name as empty
1464 // string. In the erroneous case corresponding d_type will hold error number.
Sahana Rao53bd1f62019-12-27 20:18:39 +00001465 if (num_directory_entries && h->de[0]->d_name.empty()) {
1466 fuse_reply_err(req, h->de[0]->d_type);
1467 return;
1468 }
Sahana Raoa82bd6a2019-10-10 18:10:37 +01001469
Sahana Rao53bd1f62019-12-27 20:18:39 +00001470 while (h->next_off < num_directory_entries) {
Sahana Raoa82bd6a2019-10-10 18:10:37 +01001471 de = h->de[h->next_off];
Sahana Raoa82bd6a2019-10-10 18:10:37 +01001472 entry_size = 0;
1473 h->next_off++;
shafik1e3a2672019-08-16 14:51:55 +01001474 if (plus) {
Narayan Kamathbbb779a2019-12-31 16:30:11 +00001475 int error_code = 0;
1476 if (do_lookup(req, ino, de->d_name.c_str(), &e, &error_code)) {
Sahana Raoa82bd6a2019-10-10 18:10:37 +01001477 entry_size = fuse_add_direntry_plus(req, buf + used, len - used, de->d_name.c_str(),
1478 &e, h->next_off);
Sahana Rao8a588e72019-12-06 11:32:56 +00001479 } else {
Sahana Rao8d8dbb42019-12-18 12:57:02 +00001480 // Ignore lookup errors on
1481 // 1. non-existing files returned from MediaProvider database.
1482 // 2. path that doesn't match FuseDaemon UID and calling uid.
likaid31e75b2020-12-09 16:14:28 +08001483 if (error_code == ENOENT || error_code == EPERM || error_code == EACCES
1484 || error_code == EIO) continue;
Narayan Kamathbbb779a2019-12-31 16:30:11 +00001485 fuse_reply_err(req, error_code);
shafik1e3a2672019-08-16 14:51:55 +01001486 return;
1487 }
1488 } else {
Zim1100f342020-05-15 16:13:00 +01001489 // This should never happen because we have readdir_plus enabled without adaptive
1490 // readdir_plus, FUSE_CAP_READDIRPLUS_AUTO
1491 LOG(WARNING) << "Handling plain readdir for " << de->d_name << ". Invalid d_ino";
shafik1e3a2672019-08-16 14:51:55 +01001492 e.attr.st_ino = FUSE_UNKNOWN_INO;
Zim1100f342020-05-15 16:13:00 +01001493 e.attr.st_mode = de->d_type << 12;
Sahana Raoa82bd6a2019-10-10 18:10:37 +01001494 entry_size = fuse_add_direntry(req, buf + used, len - used, de->d_name.c_str(), &e.attr,
1495 h->next_off);
shafik1e3a2672019-08-16 14:51:55 +01001496 }
Sahana Rao6f9ea982019-09-20 12:21:33 +01001497 // If buffer in fuse_add_direntry[_plus] is not large enough then
1498 // the entry is not added to buffer but the size of the entry is still
1499 // returned. Check available buffer size + returned entry size is less
1500 // than actual buffer size to confirm entry is added to buffer.
Sahana Rao43927f02019-12-10 22:59:01 +00001501 if (used + entry_size > len) {
1502 // When an entry is rejected, lookup called by readdir_plus will not be tracked by
1503 // kernel. Call forget on the rejected node to decrement the reference count.
Narayan Kamathd6219842019-12-27 17:44:35 +00001504 if (plus) {
Nandana Dutt154cb5d2020-06-04 11:53:31 +01001505 do_forget(req, fuse, e.ino, 1);
Narayan Kamathd6219842019-12-27 17:44:35 +00001506 }
Sahana Rao43927f02019-12-10 22:59:01 +00001507 break;
1508 }
Sahana Rao6f9ea982019-09-20 12:21:33 +01001509 used += entry_size;
shafik1e3a2672019-08-16 14:51:55 +01001510 }
Sahana Rao53bd1f62019-12-27 20:18:39 +00001511 fuse_reply_buf(req, buf, used);
shafik1e3a2672019-08-16 14:51:55 +01001512}
1513
1514static void pf_readdir(fuse_req_t req, fuse_ino_t ino, size_t size, off_t off,
1515 struct fuse_file_info* fi) {
Zimec8d5722019-12-05 07:26:13 +00001516 ATRACE_CALL();
shafik1e3a2672019-08-16 14:51:55 +01001517 do_readdir_common(req, ino, size, off, fi, false);
1518}
1519
1520static void pf_readdirplus(fuse_req_t req,
1521 fuse_ino_t ino,
1522 size_t size,
1523 off_t off,
1524 struct fuse_file_info* fi) {
Zimec8d5722019-12-05 07:26:13 +00001525 ATRACE_CALL();
shafik1e3a2672019-08-16 14:51:55 +01001526 do_readdir_common(req, ino, size, off, fi, true);
1527}
1528
1529static void pf_releasedir(fuse_req_t req,
1530 fuse_ino_t ino,
1531 struct fuse_file_info* fi) {
Zimec8d5722019-12-05 07:26:13 +00001532 ATRACE_CALL();
shafik1e3a2672019-08-16 14:51:55 +01001533 struct fuse* fuse = get_fuse(req);
shafik1e3a2672019-08-16 14:51:55 +01001534
Ricky Wai44670762020-05-01 11:25:28 +01001535 node* node = fuse->FromInode(ino);
1536
Narayan Kamathaef84a12020-01-02 15:20:13 +00001537 dirhandle* h = reinterpret_cast<dirhandle*>(fi->fh);
Nandana Dutt154cb5d2020-06-04 11:53:31 +01001538 TRACE_NODE(node, req);
Narayan Kamathaef84a12020-01-02 15:20:13 +00001539 if (node) {
1540 node->DestroyDirHandle(h);
Zimedbe69e2019-12-13 18:49:36 +00001541 }
Zimedbe69e2019-12-13 18:49:36 +00001542
shafik1e3a2672019-08-16 14:51:55 +01001543 fuse_reply_err(req, 0);
1544}
1545
1546static void pf_statfs(fuse_req_t req, fuse_ino_t ino) {
Zimec8d5722019-12-05 07:26:13 +00001547 ATRACE_CALL();
shafik1e3a2672019-08-16 14:51:55 +01001548 struct statvfs st;
1549 struct fuse* fuse = get_fuse(req);
1550
Narayan Kamathaef84a12020-01-02 15:20:13 +00001551 if (statvfs(fuse->root->GetName().c_str(), &st))
shafik1e3a2672019-08-16 14:51:55 +01001552 fuse_reply_err(req, errno);
1553 else
1554 fuse_reply_statfs(req, &st);
1555}
1556/*
1557static void pf_setxattr(fuse_req_t req, fuse_ino_t ino, const char* name,
1558 const char* value, size_t size, int flags)
1559{
1560 cout << "TODO:" << __func__;
1561}
1562
1563static void pf_getxattr(fuse_req_t req, fuse_ino_t ino, const char* name,
1564 size_t size)
1565{
1566 cout << "TODO:" << __func__;
1567}
1568
1569static void pf_listxattr(fuse_req_t req, fuse_ino_t ino, size_t size)
1570{
1571 cout << "TODO:" << __func__;
1572}
1573
1574static void pf_removexattr(fuse_req_t req, fuse_ino_t ino, const char* name)
1575{
1576 cout << "TODO:" << __func__;
Zima9fcd552019-08-29 15:17:04 +01001577}*/
1578
1579static void pf_access(fuse_req_t req, fuse_ino_t ino, int mask) {
Zimec8d5722019-12-05 07:26:13 +00001580 ATRACE_CALL();
Zima9fcd552019-08-29 15:17:04 +01001581 struct fuse* fuse = get_fuse(req);
Zima9fcd552019-08-29 15:17:04 +01001582
Ricky Wai44670762020-05-01 11:25:28 +01001583 node* node = fuse->FromInode(ino);
Ricky Waif40c4022020-04-15 19:00:06 +01001584 if (!node) {
1585 fuse_reply_err(req, ENOENT);
1586 return;
1587 }
Narayan Kamathaef84a12020-01-02 15:20:13 +00001588 const string path = node->BuildPath();
Martijn Coenen19c1a872020-07-30 19:59:50 +02001589 if (path != "/storage/emulated" && !is_app_accessible_path(fuse->mp, path, req->ctx.uid)) {
Ricky Wai44670762020-05-01 11:25:28 +01001590 fuse_reply_err(req, ENOENT);
1591 return;
1592 }
Nandana Dutt154cb5d2020-06-04 11:53:31 +01001593 TRACE_NODE(node, req);
Zima9fcd552019-08-29 15:17:04 +01001594
Nandana Dutt17555052020-04-09 16:19:57 +01001595 // exists() checks are always allowed.
1596 if (mask == F_OK) {
1597 int res = access(path.c_str(), F_OK);
1598 fuse_reply_err(req, res ? errno : 0);
1599 return;
1600 }
1601 struct stat stat;
1602 if (lstat(path.c_str(), &stat)) {
1603 // File doesn't exist
1604 fuse_reply_err(req, ENOENT);
1605 return;
1606 }
1607
1608 // For read and write permission checks we go to MediaProvider.
1609 int status = 0;
Nandana Dutt5fc32012020-06-25 10:55:52 +01001610 bool for_write = mask & W_OK;
Nandana Dutt17555052020-04-09 16:19:57 +01001611 bool is_directory = S_ISDIR(stat.st_mode);
1612 if (is_directory) {
Martijn Coenen19c1a872020-07-30 19:59:50 +02001613 if (path == "/storage/emulated" && mask == X_OK) {
1614 // Special case for this path: apps should be allowed to enter it,
1615 // but not list directory contents (which would be user numbers).
1616 int res = access(path.c_str(), X_OK);
1617 fuse_reply_err(req, res ? errno : 0);
1618 return;
1619 }
Nandana Dutt5fc32012020-06-25 10:55:52 +01001620 status = fuse->mp->IsOpendirAllowed(path, req->ctx.uid, for_write);
Nandana Dutt17555052020-04-09 16:19:57 +01001621 } else {
1622 if (mask & X_OK) {
1623 // Fuse is mounted with MS_NOEXEC.
1624 fuse_reply_err(req, EACCES);
1625 return;
1626 }
1627
Ricky Wai44670762020-05-01 11:25:28 +01001628 status = fuse->mp->IsOpenAllowed(path, req->ctx.uid, for_write);
Nandana Dutt17555052020-04-09 16:19:57 +01001629 }
1630
1631 fuse_reply_err(req, status);
shafik1e3a2672019-08-16 14:51:55 +01001632}
Zima9fcd552019-08-29 15:17:04 +01001633
shafik1e3a2672019-08-16 14:51:55 +01001634static void pf_create(fuse_req_t req,
1635 fuse_ino_t parent,
1636 const char* name,
1637 mode_t mode,
1638 struct fuse_file_info* fi) {
Zimec8d5722019-12-05 07:26:13 +00001639 ATRACE_CALL();
shafik1e3a2672019-08-16 14:51:55 +01001640 struct fuse* fuse = get_fuse(req);
Ricky Wai44670762020-05-01 11:25:28 +01001641 node* parent_node = fuse->FromInode(parent);
Ricky Waif40c4022020-04-15 19:00:06 +01001642 if (!parent_node) {
1643 fuse_reply_err(req, ENOENT);
1644 return;
1645 }
Narayan Kamathaef84a12020-01-02 15:20:13 +00001646 const string parent_path = parent_node->BuildPath();
Ricky Wai44670762020-05-01 11:25:28 +01001647 if (!is_app_accessible_path(fuse->mp, parent_path, req->ctx.uid)) {
1648 fuse_reply_err(req, ENOENT);
1649 return;
1650 }
shafik1e3a2672019-08-16 14:51:55 +01001651
Nandana Dutt154cb5d2020-06-04 11:53:31 +01001652 TRACE_NODE(parent_node, req);
shafik1e3a2672019-08-16 14:51:55 +01001653
Narayan Kamathaef84a12020-01-02 15:20:13 +00001654 const string child_path = parent_path + "/" + name;
shafik1e3a2672019-08-16 14:51:55 +01001655
Ricky Wai44670762020-05-01 11:25:28 +01001656 int mp_return_code = fuse->mp->InsertFile(child_path.c_str(), req->ctx.uid);
Narayan Kamathbbb779a2019-12-31 16:30:11 +00001657 if (mp_return_code) {
Narayan Kamathbbb779a2019-12-31 16:30:11 +00001658 fuse_reply_err(req, mp_return_code);
shafik1e3a2672019-08-16 14:51:55 +01001659 return;
1660 }
1661
Martijn Coenen2d7f1be2020-02-15 07:44:12 +01001662 // With the writeback cache enabled, FUSE may generate READ requests even for files that
1663 // were opened O_WRONLY; so make sure we open it O_RDWR instead.
1664 int open_flags = fi->flags;
1665 if (open_flags & O_WRONLY) {
1666 open_flags &= ~O_WRONLY;
1667 open_flags |= O_RDWR;
1668 }
1669
Zim1b14f2f2020-07-03 07:05:53 +01001670 if (open_flags & O_APPEND) {
1671 open_flags &= ~O_APPEND;
1672 }
1673
Narayan Kamathbbb779a2019-12-31 16:30:11 +00001674 mode = (mode & (~0777)) | 0664;
Martijn Coenen2d7f1be2020-02-15 07:44:12 +01001675 int fd = open(child_path.c_str(), open_flags, mode);
Narayan Kamathbbb779a2019-12-31 16:30:11 +00001676 if (fd < 0) {
1677 int error_code = errno;
1678 // We've already inserted the file into the MP database before the
1679 // failed open(), so that needs to be rolled back here.
Ricky Wai44670762020-05-01 11:25:28 +01001680 fuse->mp->DeleteFile(child_path.c_str(), req->ctx.uid);
Narayan Kamathbbb779a2019-12-31 16:30:11 +00001681 fuse_reply_err(req, error_code);
1682 return;
1683 }
1684
Narayan Kamathbbb779a2019-12-31 16:30:11 +00001685 int error_code = 0;
Narayan Kamathaef84a12020-01-02 15:20:13 +00001686 struct fuse_entry_param e;
Narayan Kamathbbb779a2019-12-31 16:30:11 +00001687 node* node = make_node_entry(req, parent_node, name, child_path, &e, &error_code);
Nandana Dutt154cb5d2020-06-04 11:53:31 +01001688 TRACE_NODE(node, req);
Zimc05c60f2020-03-05 13:02:26 +00001689 if (!node) {
Narayan Kamathbbb779a2019-12-31 16:30:11 +00001690 CHECK(error_code != 0);
1691 fuse_reply_err(req, error_code);
Zimc05c60f2020-03-05 13:02:26 +00001692 return;
shafik1e3a2672019-08-16 14:51:55 +01001693 }
Zimc05c60f2020-03-05 13:02:26 +00001694
Martijn Coenenaf2d34d2020-06-19 12:52:20 +02001695 // Let MediaProvider know we've created a new file
1696 fuse->mp->OnFileCreated(child_path);
1697
Zimc05c60f2020-03-05 13:02:26 +00001698 // TODO(b/147274248): Assume there will be no EXIF to redact.
1699 // This prevents crashing during reads but can be a security hole if a malicious app opens an fd
1700 // to the file before all the EXIF content is written. We could special case reads before the
1701 // first close after a file has just been created.
Zim329ba2c2020-09-16 14:23:26 +01001702 int keep_cache = 1;
Zim9aa6f542020-10-19 15:39:33 +01001703 handle* h = create_handle_for_node(fuse, child_path, fd, req->ctx.uid, node,
1704 new RedactionInfo(), &keep_cache);
Zimc05c60f2020-03-05 13:02:26 +00001705 fi->fh = ptr_to_id(h);
Zim329ba2c2020-09-16 14:23:26 +01001706 fi->keep_cache = keep_cache;
Zimc05c60f2020-03-05 13:02:26 +00001707 fi->direct_io = !h->cached;
Alessio Balsinif220a962020-07-13 12:01:13 +01001708
1709 // TODO(b/173190192) ensuring that h->cached must be enabled in order to
1710 // user FUSE passthrough is a conservative rule and might be dropped as
1711 // soon as demonstrated its correctness.
Zim148cbe22020-11-17 15:58:29 +00001712 if (h->passthrough) {
Alessio Balsinif220a962020-07-13 12:01:13 +01001713 if (!do_passthrough_enable(req, fi, fd)) {
Zim148cbe22020-11-17 15:58:29 +00001714 PLOG(ERROR) << "Passthrough CREATE failed for " << child_path;
1715 fuse_reply_err(req, EFAULT);
1716 return;
Alessio Balsinif220a962020-07-13 12:01:13 +01001717 }
1718 }
1719
Zimc05c60f2020-03-05 13:02:26 +00001720 fuse_reply_create(req, &e, fi);
shafik1e3a2672019-08-16 14:51:55 +01001721}
1722/*
1723static void pf_getlk(fuse_req_t req, fuse_ino_t ino,
1724 struct fuse_file_info* fi, struct flock* lock)
1725{
1726 cout << "TODO:" << __func__;
1727}
1728
1729static void pf_setlk(fuse_req_t req, fuse_ino_t ino,
1730 struct fuse_file_info* fi,
1731 struct flock* lock, int sleep)
1732{
1733 cout << "TODO:" << __func__;
1734}
1735
1736static void pf_bmap(fuse_req_t req, fuse_ino_t ino, size_t blocksize,
1737 uint64_t idx)
1738{
1739 cout << "TODO:" << __func__;
1740}
1741
1742static void pf_ioctl(fuse_req_t req, fuse_ino_t ino, unsigned int cmd,
1743 void* arg, struct fuse_file_info* fi, unsigned flags,
1744 const void* in_buf, size_t in_bufsz, size_t out_bufsz)
1745{
1746 cout << "TODO:" << __func__;
1747}
1748
1749static void pf_poll(fuse_req_t req, fuse_ino_t ino, struct fuse_file_info* fi,
1750 struct fuse_pollhandle* ph)
1751{
1752 cout << "TODO:" << __func__;
1753}
1754
1755static void pf_retrieve_reply(fuse_req_t req, void* cookie, fuse_ino_t ino,
1756 off_t offset, struct fuse_bufvec* bufv)
1757{
1758 cout << "TODO:" << __func__;
1759}
1760
1761static void pf_flock(fuse_req_t req, fuse_ino_t ino,
1762 struct fuse_file_info* fi, int op)
1763{
1764 cout << "TODO:" << __func__;
1765}
1766
1767static void pf_fallocate(fuse_req_t req, fuse_ino_t ino, int mode,
1768 off_t offset, off_t length, struct fuse_file_info* fi)
1769{
1770 cout << "TODO:" << __func__;
1771}
1772*/
1773
1774static struct fuse_lowlevel_ops ops{
Zimuzo Ezeozue67db40c2020-02-21 19:41:33 +00001775 .init = pf_init, .destroy = pf_destroy, .lookup = pf_lookup, .forget = pf_forget,
1776 .getattr = pf_getattr, .setattr = pf_setattr, .canonical_path = pf_canonical_path,
1777 .mknod = pf_mknod, .mkdir = pf_mkdir, .unlink = pf_unlink, .rmdir = pf_rmdir,
shafik8b57cd52019-09-06 10:51:29 +01001778 /*.symlink = pf_symlink,*/
1779 .rename = pf_rename,
1780 /*.link = pf_link,*/
1781 .open = pf_open, .read = pf_read,
1782 /*.write = pf_write,*/
Alessio Balsini68c295d2020-07-23 14:32:01 +01001783 /*.flush = pf_flush,*/
Zimuzo Ezeozue67db40c2020-02-21 19:41:33 +00001784 .release = pf_release, .fsync = pf_fsync, .opendir = pf_opendir, .readdir = pf_readdir,
1785 .releasedir = pf_releasedir, .fsyncdir = pf_fsyncdir, .statfs = pf_statfs,
shafik8b57cd52019-09-06 10:51:29 +01001786 /*.setxattr = pf_setxattr,
1787 .getxattr = pf_getxattr,
1788 .listxattr = pf_listxattr,
1789 .removexattr = pf_removexattr,*/
1790 .access = pf_access, .create = pf_create,
1791 /*.getlk = pf_getlk,
1792 .setlk = pf_setlk,
1793 .bmap = pf_bmap,
1794 .ioctl = pf_ioctl,
Nick Desaulniers17714b82019-11-05 09:44:35 -08001795 .poll = pf_poll,*/
1796 .write_buf = pf_write_buf,
1797 /*.retrieve_reply = pf_retrieve_reply,*/
1798 .forget_multi = pf_forget_multi,
shafik8b57cd52019-09-06 10:51:29 +01001799 /*.flock = pf_flock,
1800 .fallocate = pf_fallocate,*/
Nick Desaulniers17714b82019-11-05 09:44:35 -08001801 .readdirplus = pf_readdirplus,
1802 /*.copy_file_range = pf_copy_file_range,*/
shafik1e3a2672019-08-16 14:51:55 +01001803};
1804
1805static struct fuse_loop_config config = {
Zim549f9da2020-02-09 13:54:22 +00001806 .clone_fd = 1,
shafik1e3a2672019-08-16 14:51:55 +01001807 .max_idle_threads = 10,
1808};
1809
Zimb353eb42019-12-17 15:29:48 +00001810static std::unordered_map<enum fuse_log_level, enum android_LogPriority> fuse_to_android_loglevel({
1811 {FUSE_LOG_EMERG, ANDROID_LOG_FATAL},
1812 {FUSE_LOG_ALERT, ANDROID_LOG_ERROR},
1813 {FUSE_LOG_CRIT, ANDROID_LOG_ERROR},
1814 {FUSE_LOG_ERR, ANDROID_LOG_ERROR},
1815 {FUSE_LOG_WARNING, ANDROID_LOG_WARN},
1816 {FUSE_LOG_NOTICE, ANDROID_LOG_INFO},
1817 {FUSE_LOG_INFO, ANDROID_LOG_DEBUG},
1818 {FUSE_LOG_DEBUG, ANDROID_LOG_VERBOSE},
1819 });
Zim724b8ca2019-11-09 09:37:24 +00001820
1821static void fuse_logger(enum fuse_log_level level, const char* fmt, va_list ap) {
Zim357e3072020-01-15 10:50:01 +00001822 __android_log_vprint(fuse_to_android_loglevel.at(level), LIBFUSE_LOG_TAG, fmt, ap);
Zim724b8ca2019-11-09 09:37:24 +00001823}
1824
Zimedbe69e2019-12-13 18:49:36 +00001825bool FuseDaemon::ShouldOpenWithFuse(int fd, bool for_read, const std::string& path) {
Zim148cbe22020-11-17 15:58:29 +00001826 if (fuse->passthrough) {
1827 // Always open with FUSE if passthrough is enabled. This avoids the delicate file lock
1828 // acquisition below to ensure VFS cache consistency and doesn't impact filesystem
1829 // performance since read(2)/write(2) happen in the kernel
1830 return true;
1831 }
1832
Zimedbe69e2019-12-13 18:49:36 +00001833 bool use_fuse = false;
1834
1835 if (active.load(std::memory_order_acquire)) {
Zimdc78f532020-03-04 18:46:06 +00001836 std::lock_guard<std::recursive_mutex> guard(fuse->lock);
Narayan Kamathaef84a12020-01-02 15:20:13 +00001837 const node* node = node::LookupAbsolutePath(fuse->root, path);
Zimedbe69e2019-12-13 18:49:36 +00001838 if (node && node->HasCachedHandle()) {
Zimedbe69e2019-12-13 18:49:36 +00001839 use_fuse = true;
1840 } else {
1841 // If we are unable to set a lock, we should use fuse since we can't track
1842 // when all fd references (including dups) are closed. This can happen when
1843 // we try to set a write lock twice on the same file
1844 use_fuse = set_file_lock(fd, for_read, path);
Zimedbe69e2019-12-13 18:49:36 +00001845 }
Zimedbe69e2019-12-13 18:49:36 +00001846 } else {
Zimuzo Ezeozue67db40c2020-02-21 19:41:33 +00001847 LOG(WARNING) << "FUSE daemon is inactive. Cannot open file with FUSE";
Zimedbe69e2019-12-13 18:49:36 +00001848 }
1849
1850 return use_fuse;
1851}
1852
Zima76c3492020-02-19 01:23:26 +00001853void FuseDaemon::InvalidateFuseDentryCache(const std::string& path) {
Zim4d3fa272020-03-06 13:45:19 +00001854 LOG(VERBOSE) << "Invalidating FUSE dentry cache";
Zima76c3492020-02-19 01:23:26 +00001855 if (active.load(std::memory_order_acquire)) {
Zima55568e2020-04-24 22:40:34 +01001856 string name;
1857 fuse_ino_t parent;
Zim33aea102020-06-19 15:49:47 +01001858 fuse_ino_t child;
Zima55568e2020-04-24 22:40:34 +01001859 {
1860 std::lock_guard<std::recursive_mutex> guard(fuse->lock);
1861 const node* node = node::LookupAbsolutePath(fuse->root, path);
1862 if (node) {
1863 name = node->GetName();
Zim33aea102020-06-19 15:49:47 +01001864 child = fuse->ToInode(const_cast<class node*>(node));
Zima55568e2020-04-24 22:40:34 +01001865 parent = fuse->ToInode(node->GetParent());
Zima76c3492020-02-19 01:23:26 +00001866 }
1867 }
Zima55568e2020-04-24 22:40:34 +01001868
Zim33aea102020-06-19 15:49:47 +01001869 if (!name.empty()) {
1870 fuse_inval(fuse->se, parent, child, name, path);
Zima55568e2020-04-24 22:40:34 +01001871 }
Zima76c3492020-02-19 01:23:26 +00001872 } else {
Zimuzo Ezeozue67db40c2020-02-21 19:41:33 +00001873 LOG(WARNING) << "FUSE daemon is inactive. Cannot invalidate dentry";
Zima76c3492020-02-19 01:23:26 +00001874 }
1875}
1876
Zimedbe69e2019-12-13 18:49:36 +00001877FuseDaemon::FuseDaemon(JNIEnv* env, jobject mediaProvider) : mp(env, mediaProvider),
1878 active(false), fuse(nullptr) {}
shafik1e3a2672019-08-16 14:51:55 +01001879
Zimd0435b22020-03-05 13:52:51 +00001880bool FuseDaemon::IsStarted() const {
1881 return active.load(std::memory_order_acquire);
1882}
1883
Martijn Coenen083eb692020-04-24 09:39:58 +02001884void FuseDaemon::Start(android::base::unique_fd fd, const std::string& path) {
Nikita Ioffec33ee1a2020-06-23 21:31:20 +01001885 android::base::SetDefaultTag(LOG_TAG);
1886
shafik1e3a2672019-08-16 14:51:55 +01001887 struct fuse_args args;
1888 struct fuse_cmdline_opts opts;
shafik1e3a2672019-08-16 14:51:55 +01001889
1890 struct stat stat;
shafik1e3a2672019-08-16 14:51:55 +01001891
Zim7c8712d2019-10-03 21:01:26 +01001892 if (lstat(path.c_str(), &stat)) {
Zimuzo Ezeozue67db40c2020-02-21 19:41:33 +00001893 PLOG(ERROR) << "ERROR: failed to stat source " << path;
Zim3e45d9b2019-08-19 21:14:14 +01001894 return;
1895 }
shafik1e3a2672019-08-16 14:51:55 +01001896
Zim3e45d9b2019-08-19 21:14:14 +01001897 if (!S_ISDIR(stat.st_mode)) {
Zimuzo Ezeozue67db40c2020-02-21 19:41:33 +00001898 PLOG(ERROR) << "ERROR: source is not a directory";
Zim3e45d9b2019-08-19 21:14:14 +01001899 return;
1900 }
shafik1e3a2672019-08-16 14:51:55 +01001901
1902 args = FUSE_ARGS_INIT(0, nullptr);
Zim7c8712d2019-10-03 21:01:26 +01001903 if (fuse_opt_add_arg(&args, path.c_str()) || fuse_opt_add_arg(&args, "-odebug") ||
1904 fuse_opt_add_arg(&args, ("-omax_read=" + std::to_string(MAX_READ_SIZE)).c_str())) {
shafik458d1102019-09-06 18:21:36 +01001905 LOG(ERROR) << "ERROR: failed to set options";
shafik1e3a2672019-08-16 14:51:55 +01001906 return;
1907 }
1908
Narayan Kamathaef84a12020-01-02 15:20:13 +00001909 struct fuse fuse_default(path);
shafikc3f62672019-08-30 11:15:48 +01001910 fuse_default.mp = &mp;
Zimedbe69e2019-12-13 18:49:36 +00001911 // fuse_default is stack allocated, but it's safe to save it as an instance variable because
1912 // this method blocks and FuseDaemon#active tells if we are currently blocking
1913 fuse = &fuse_default;
shafikc3f62672019-08-30 11:15:48 +01001914
shafikb334a612019-09-30 20:38:16 +01001915 // Used by pf_read: redacted ranges are represented by zeroized ranges of bytes,
1916 // so we mmap the maximum length of redacted ranges in the beginning and save memory allocations
1917 // on each read.
1918 fuse_default.zero_addr = static_cast<char*>(mmap(
1919 NULL, MAX_READ_SIZE, PROT_READ, MAP_ANONYMOUS | MAP_PRIVATE, /*fd*/ -1, /*off*/ 0));
1920 if (fuse_default.zero_addr == MAP_FAILED) {
1921 LOG(FATAL) << "mmap failed - could not start fuse! errno = " << errno;
1922 }
1923
Zim724b8ca2019-11-09 09:37:24 +00001924 // Custom logging for libfuse
Zim0594b032020-04-29 11:43:43 +01001925 if (android::base::GetBoolProperty("persist.sys.fuse.log", false)) {
1926 fuse_set_log_func(fuse_logger);
1927 }
Zim724b8ca2019-11-09 09:37:24 +00001928
Martijn Coenen87133092020-10-14 17:00:28 +02001929 uid_t userId = getuid() / PER_USER_RANGE;
1930 if (userId != 0 && mp.IsAppCloneUser(userId)) {
1931 // Disable dentry caching for the app clone user
1932 fuse->disable_dentry_cache = true;
1933 }
1934
Alessio Balsinib1c366b2020-11-26 15:15:14 +00001935 fuse->passthrough = android::base::GetBoolProperty("persist.sys.fuse.passthrough.enable", false);
1936 if (fuse->passthrough) {
1937 LOG(INFO) << "Using FUSE passthrough";
1938 }
Alessio Balsinif220a962020-07-13 12:01:13 +01001939
shafik1e3a2672019-08-16 14:51:55 +01001940 struct fuse_session
1941 * se = fuse_session_new(&args, &ops, sizeof(ops), &fuse_default);
Zim7e0d3142019-10-17 21:06:12 +01001942 if (!se) {
1943 PLOG(ERROR) << "Failed to create session ";
1944 return;
1945 }
Zima76c3492020-02-19 01:23:26 +00001946 fuse_default.se = se;
Zimd0435b22020-03-05 13:52:51 +00001947 fuse_default.active = &active;
Martijn Coenen3ec6c062020-04-28 19:09:53 +02001948 se->fd = fd.release(); // libfuse owns the FD now
Zim7c8712d2019-10-03 21:01:26 +01001949 se->mountpoint = strdup(path.c_str());
Zim3e45d9b2019-08-19 21:14:14 +01001950
1951 // Single thread. Useful for debugging
1952 // fuse_session_loop(se);
1953 // Multi-threaded
Zim7e0d3142019-10-17 21:06:12 +01001954 LOG(INFO) << "Starting fuse...";
shafik1e3a2672019-08-16 14:51:55 +01001955 fuse_session_loop_mt(se, &config);
Zimd0435b22020-03-05 13:52:51 +00001956 fuse->active->store(false, std::memory_order_release);
Zim7e0d3142019-10-17 21:06:12 +01001957 LOG(INFO) << "Ending fuse...";
Zim3e45d9b2019-08-19 21:14:14 +01001958
Zim7c8712d2019-10-03 21:01:26 +01001959 if (munmap(fuse_default.zero_addr, MAX_READ_SIZE)) {
1960 PLOG(ERROR) << "munmap failed!";
shafikb334a612019-09-30 20:38:16 +01001961 }
1962
Zim7e0d3142019-10-17 21:06:12 +01001963 fuse_opt_free_args(&args);
1964 fuse_session_destroy(se);
1965 LOG(INFO) << "Ended fuse";
1966 return;
shafik1e3a2672019-08-16 14:51:55 +01001967}
1968} //namespace fuse
shafik8b57cd52019-09-06 10:51:29 +01001969} // namespace mediaprovider