1 /*
2  * Copyright (C) 2019 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specic language governing permissions and
14  * limitations under the License.
15  */
16 
17 #include "MediaProviderWrapper.h"
18 #include "libfuse_jni/ReaddirHelper.h"
19 
20 #include <android-base/logging.h>
21 #include <android-base/properties.h>
22 #include <jni.h>
23 #include <nativehelper/scoped_local_ref.h>
24 #include <nativehelper/scoped_primitive_array.h>
25 #include <nativehelper/scoped_utf_chars.h>
26 
27 #include <pthread.h>
28 
29 #include <mutex>
30 #include <unordered_map>
31 
32 namespace mediaprovider {
33 namespace fuse {
34 using android::base::GetBoolProperty;
35 using std::string;
36 
37 namespace {
38 
39 constexpr const char* kPropRedactionEnabled = "persist.sys.fuse.redaction-enabled";
40 
41 constexpr uid_t ROOT_UID = 0;
42 constexpr uid_t SHELL_UID = 2000;
43 
44 /** Private helper functions **/
45 
shouldBypassMediaProvider(uid_t uid)46 inline bool shouldBypassMediaProvider(uid_t uid) {
47     return uid == SHELL_UID || uid == ROOT_UID;
48 }
49 
CheckForJniException(JNIEnv * env)50 static bool CheckForJniException(JNIEnv* env) {
51     if (env->ExceptionCheck()) {
52         env->ExceptionDescribe();
53         env->ExceptionClear();
54         return true;
55     }
56     return false;
57 }
58 
getRedactionInfoInternal(JNIEnv * env,jobject media_provider_object,jmethodID mid_get_redaction_ranges,uid_t uid,pid_t tid,const string & path)59 std::unique_ptr<RedactionInfo> getRedactionInfoInternal(JNIEnv* env, jobject media_provider_object,
60                                                         jmethodID mid_get_redaction_ranges,
61                                                         uid_t uid, pid_t tid, const string& path) {
62     ScopedLocalRef<jstring> j_path(env, env->NewStringUTF(path.c_str()));
63     ScopedLongArrayRO redaction_ranges(
64             env, static_cast<jlongArray>(env->CallObjectMethod(
65                          media_provider_object, mid_get_redaction_ranges, j_path.get(), uid, tid)));
66 
67     if (CheckForJniException(env)) {
68         return nullptr;
69     }
70 
71     std::unique_ptr<RedactionInfo> ri;
72     if (redaction_ranges.size() % 2) {
73         LOG(ERROR) << "Error while calculating redaction ranges: array length is uneven";
74     } else if (redaction_ranges.size() > 0) {
75         ri = std::make_unique<RedactionInfo>(redaction_ranges.size() / 2, redaction_ranges.get());
76     } else {
77         // No ranges to redact
78         ri = std::make_unique<RedactionInfo>();
79     }
80 
81     return ri;
82 }
83 
insertFileInternal(JNIEnv * env,jobject media_provider_object,jmethodID mid_insert_file,const string & path,uid_t uid)84 int insertFileInternal(JNIEnv* env, jobject media_provider_object, jmethodID mid_insert_file,
85                        const string& path, uid_t uid) {
86     ScopedLocalRef<jstring> j_path(env, env->NewStringUTF(path.c_str()));
87     int res = env->CallIntMethod(media_provider_object, mid_insert_file, j_path.get(), uid);
88 
89     if (CheckForJniException(env)) {
90         return EFAULT;
91     }
92     return res;
93 }
94 
deleteFileInternal(JNIEnv * env,jobject media_provider_object,jmethodID mid_delete_file,const string & path,uid_t uid)95 int deleteFileInternal(JNIEnv* env, jobject media_provider_object, jmethodID mid_delete_file,
96                        const string& path, uid_t uid) {
97     ScopedLocalRef<jstring> j_path(env, env->NewStringUTF(path.c_str()));
98     int res = env->CallIntMethod(media_provider_object, mid_delete_file, j_path.get(), uid);
99 
100     if (CheckForJniException(env)) {
101         return EFAULT;
102     }
103     return res;
104 }
105 
isOpenAllowedInternal(JNIEnv * env,jobject media_provider_object,jmethodID mid_is_open_allowed,const string & path,uid_t uid,bool for_write)106 int isOpenAllowedInternal(JNIEnv* env, jobject media_provider_object, jmethodID mid_is_open_allowed,
107                           const string& path, uid_t uid, bool for_write) {
108     ScopedLocalRef<jstring> j_path(env, env->NewStringUTF(path.c_str()));
109     int res = env->CallIntMethod(media_provider_object, mid_is_open_allowed, j_path.get(), uid,
110                                  for_write);
111 
112     if (CheckForJniException(env)) {
113         return EFAULT;
114     }
115     return res;
116 }
117 
scanFileInternal(JNIEnv * env,jobject media_provider_object,jmethodID mid_scan_file,const string & path)118 void scanFileInternal(JNIEnv* env, jobject media_provider_object, jmethodID mid_scan_file,
119                       const string& path) {
120     ScopedLocalRef<jstring> j_path(env, env->NewStringUTF(path.c_str()));
121     env->CallVoidMethod(media_provider_object, mid_scan_file, j_path.get());
122     CheckForJniException(env);
123 }
124 
isMkdirOrRmdirAllowedInternal(JNIEnv * env,jobject media_provider_object,jmethodID mid_is_mkdir_or_rmdir_allowed,const string & path,uid_t uid,bool forCreate)125 int isMkdirOrRmdirAllowedInternal(JNIEnv* env, jobject media_provider_object,
126                                   jmethodID mid_is_mkdir_or_rmdir_allowed, const string& path,
127                                   uid_t uid, bool forCreate) {
128     ScopedLocalRef<jstring> j_path(env, env->NewStringUTF(path.c_str()));
129     int res = env->CallIntMethod(media_provider_object, mid_is_mkdir_or_rmdir_allowed, j_path.get(),
130                                  uid, forCreate);
131 
132     if (CheckForJniException(env)) {
133         return EFAULT;
134     }
135     return res;
136 }
137 
isOpendirAllowedInternal(JNIEnv * env,jobject media_provider_object,jmethodID mid_is_opendir_allowed,const string & path,uid_t uid,bool forWrite)138 int isOpendirAllowedInternal(JNIEnv* env, jobject media_provider_object,
139                              jmethodID mid_is_opendir_allowed, const string& path, uid_t uid,
140                              bool forWrite) {
141     ScopedLocalRef<jstring> j_path(env, env->NewStringUTF(path.c_str()));
142     int res = env->CallIntMethod(media_provider_object, mid_is_opendir_allowed, j_path.get(), uid,
143                                  forWrite);
144 
145     if (CheckForJniException(env)) {
146         return EFAULT;
147     }
148     return res;
149 }
150 
isUidForPackageInternal(JNIEnv * env,jobject media_provider_object,jmethodID mid_is_uid_for_package,const string & pkg,uid_t uid)151 bool isUidForPackageInternal(JNIEnv* env, jobject media_provider_object,
152                              jmethodID mid_is_uid_for_package, const string& pkg, uid_t uid) {
153     ScopedLocalRef<jstring> j_pkg(env, env->NewStringUTF(pkg.c_str()));
154     bool res = env->CallBooleanMethod(media_provider_object, mid_is_uid_for_package, j_pkg.get(),
155             uid);
156 
157     if (CheckForJniException(env)) {
158         return false;
159     }
160     return res;
161 }
162 
getFilesInDirectoryInternal(JNIEnv * env,jobject media_provider_object,jmethodID mid_get_files_in_dir,uid_t uid,const string & path)163 std::vector<std::shared_ptr<DirectoryEntry>> getFilesInDirectoryInternal(
164         JNIEnv* env, jobject media_provider_object, jmethodID mid_get_files_in_dir, uid_t uid,
165         const string& path) {
166     std::vector<std::shared_ptr<DirectoryEntry>> directory_entries;
167     ScopedLocalRef<jstring> j_path(env, env->NewStringUTF(path.c_str()));
168 
169     ScopedLocalRef<jobjectArray> files_list(
170             env, static_cast<jobjectArray>(env->CallObjectMethod(
171                          media_provider_object, mid_get_files_in_dir, j_path.get(), uid)));
172 
173     if (CheckForJniException(env)) {
174         directory_entries.push_back(std::make_shared<DirectoryEntry>("", EFAULT));
175         return directory_entries;
176     }
177 
178     int de_count = env->GetArrayLength(files_list.get());
179     if (de_count == 1) {
180         ScopedLocalRef<jstring> j_d_name(env,
181                                          (jstring)env->GetObjectArrayElement(files_list.get(), 0));
182         ScopedUtfChars d_name(env, j_d_name.get());
183         if (d_name.c_str() == nullptr) {
184             LOG(ERROR) << "Error reading file name returned from MediaProvider at index " << 0;
185             directory_entries.push_back(std::make_shared<DirectoryEntry>("", EFAULT));
186             return directory_entries;
187         } else if (d_name.c_str()[0] == '\0') {
188             // Calling package has no storage permissions.
189             directory_entries.push_back(std::make_shared<DirectoryEntry>("", EPERM));
190             return directory_entries;
191         }
192     }
193 
194     for (int i = 0; i < de_count; i++) {
195         ScopedLocalRef<jstring> j_d_name(env,
196                                          (jstring)env->GetObjectArrayElement(files_list.get(), i));
197         ScopedUtfChars d_name(env, j_d_name.get());
198 
199         if (d_name.c_str() == nullptr) {
200             LOG(ERROR) << "Error reading file name returned from MediaProvider at index " << i;
201             directory_entries.resize(0);
202             directory_entries.push_back(std::make_shared<DirectoryEntry>("", EFAULT));
203             break;
204         }
205         directory_entries.push_back(std::make_shared<DirectoryEntry>(d_name.c_str(), DT_REG));
206     }
207     return directory_entries;
208 }
209 
renameInternal(JNIEnv * env,jobject media_provider_object,jmethodID mid_rename,const string & old_path,const string & new_path,uid_t uid)210 int renameInternal(JNIEnv* env, jobject media_provider_object, jmethodID mid_rename,
211                    const string& old_path, const string& new_path, uid_t uid) {
212     ScopedLocalRef<jstring> j_old_path(env, env->NewStringUTF(old_path.c_str()));
213     ScopedLocalRef<jstring> j_new_path(env, env->NewStringUTF(new_path.c_str()));
214     int res = env->CallIntMethod(media_provider_object, mid_rename, j_old_path.get(),
215                                  j_new_path.get(), uid);
216 
217     if (CheckForJniException(env)) {
218         return EFAULT;
219     }
220     return res;
221 }
222 
onFileCreatedInternal(JNIEnv * env,jobject media_provider_object,jmethodID mid_on_file_created,const string & path)223 void onFileCreatedInternal(JNIEnv* env, jobject media_provider_object,
224                            jmethodID mid_on_file_created, const string& path) {
225     ScopedLocalRef<jstring> j_path(env, env->NewStringUTF(path.c_str()));
226 
227     env->CallVoidMethod(media_provider_object, mid_on_file_created, j_path.get());
228     CheckForJniException(env);
229     return;
230 }
231 
232 }  // namespace
233 /*****************************************************************************************/
234 /******************************* Public API Implementation *******************************/
235 /*****************************************************************************************/
236 
237 JavaVM* MediaProviderWrapper::gJavaVm = nullptr;
238 pthread_key_t MediaProviderWrapper::gJniEnvKey;
239 
OneTimeInit(JavaVM * vm)240 void MediaProviderWrapper::OneTimeInit(JavaVM* vm) {
241     gJavaVm = vm;
242     CHECK(gJavaVm != nullptr);
243 
244     pthread_key_create(&MediaProviderWrapper::gJniEnvKey,
245                        MediaProviderWrapper::DetachThreadFunction);
246 }
247 
MediaProviderWrapper(JNIEnv * env,jobject media_provider)248 MediaProviderWrapper::MediaProviderWrapper(JNIEnv* env, jobject media_provider) {
249     if (!media_provider) {
250         LOG(FATAL) << "MediaProvider is null!";
251     }
252 
253     media_provider_object_ = reinterpret_cast<jobject>(env->NewGlobalRef(media_provider));
254     media_provider_class_ = env->FindClass("com/android/providers/media/MediaProvider");
255     if (!media_provider_class_) {
256         LOG(FATAL) << "Could not find class MediaProvider";
257     }
258     media_provider_class_ = reinterpret_cast<jclass>(env->NewGlobalRef(media_provider_class_));
259 
260     // Cache methods - Before calling a method, make sure you cache it here
261     mid_get_redaction_ranges_ = CacheMethod(env, "getRedactionRanges", "(Ljava/lang/String;II)[J",
262                                             /*is_static*/ false);
263     mid_insert_file_ = CacheMethod(env, "insertFileIfNecessary", "(Ljava/lang/String;I)I",
264                                    /*is_static*/ false);
265     mid_delete_file_ = CacheMethod(env, "deleteFile", "(Ljava/lang/String;I)I", /*is_static*/ false);
266     mid_is_open_allowed_ = CacheMethod(env, "isOpenAllowed", "(Ljava/lang/String;IZ)I",
267                                        /*is_static*/ false);
268     mid_scan_file_ = CacheMethod(env, "scanFile", "(Ljava/lang/String;)V",
269                                  /*is_static*/ false);
270     mid_is_mkdir_or_rmdir_allowed_ = CacheMethod(env, "isDirectoryCreationOrDeletionAllowed",
271                                                  "(Ljava/lang/String;IZ)I", /*is_static*/ false);
272     mid_is_opendir_allowed_ = CacheMethod(env, "isOpendirAllowed", "(Ljava/lang/String;IZ)I",
273                                           /*is_static*/ false);
274     mid_get_files_in_dir_ =
275             CacheMethod(env, "getFilesInDirectory", "(Ljava/lang/String;I)[Ljava/lang/String;",
276                         /*is_static*/ false);
277     mid_rename_ = CacheMethod(env, "rename", "(Ljava/lang/String;Ljava/lang/String;I)I",
278                               /*is_static*/ false);
279     mid_is_uid_for_package_ = CacheMethod(env, "isUidForPackage", "(Ljava/lang/String;I)Z",
280                               /*is_static*/ false);
281     mid_on_file_created_ = CacheMethod(env, "onFileCreated", "(Ljava/lang/String;)V",
282                                        /*is_static*/ false);
283 }
284 
~MediaProviderWrapper()285 MediaProviderWrapper::~MediaProviderWrapper() {
286     JNIEnv* env = MaybeAttachCurrentThread();
287     env->DeleteGlobalRef(media_provider_object_);
288     env->DeleteGlobalRef(media_provider_class_);
289 }
290 
GetRedactionInfo(const string & path,uid_t uid,pid_t tid)291 std::unique_ptr<RedactionInfo> MediaProviderWrapper::GetRedactionInfo(const string& path, uid_t uid,
292                                                                       pid_t tid) {
293     if (shouldBypassMediaProvider(uid) || !GetBoolProperty(kPropRedactionEnabled, true)) {
294         return std::make_unique<RedactionInfo>();
295     }
296 
297     // Default value in case JNI thread was being terminated, causes the read to fail.
298     std::unique_ptr<RedactionInfo> res = nullptr;
299 
300     JNIEnv* env = MaybeAttachCurrentThread();
301     auto ri = getRedactionInfoInternal(env, media_provider_object_, mid_get_redaction_ranges_, uid,
302                                        tid, path);
303     res = std::move(ri);
304 
305     return res;
306 }
307 
InsertFile(const string & path,uid_t uid)308 int MediaProviderWrapper::InsertFile(const string& path, uid_t uid) {
309     if (uid == ROOT_UID) {
310         return 0;
311     }
312 
313     JNIEnv* env = MaybeAttachCurrentThread();
314     return insertFileInternal(env, media_provider_object_, mid_insert_file_, path, uid);
315 }
316 
DeleteFile(const string & path,uid_t uid)317 int MediaProviderWrapper::DeleteFile(const string& path, uid_t uid) {
318     if (uid == ROOT_UID) {
319         int res = unlink(path.c_str());
320         return res;
321     }
322 
323     JNIEnv* env = MaybeAttachCurrentThread();
324     return deleteFileInternal(env, media_provider_object_, mid_delete_file_, path, uid);
325 }
326 
IsOpenAllowed(const string & path,uid_t uid,bool for_write)327 int MediaProviderWrapper::IsOpenAllowed(const string& path, uid_t uid, bool for_write) {
328     if (shouldBypassMediaProvider(uid)) {
329         return 0;
330     }
331 
332     JNIEnv* env = MaybeAttachCurrentThread();
333     return isOpenAllowedInternal(env, media_provider_object_, mid_is_open_allowed_, path, uid,
334                                  for_write);
335 }
336 
ScanFile(const string & path)337 void MediaProviderWrapper::ScanFile(const string& path) {
338     JNIEnv* env = MaybeAttachCurrentThread();
339     scanFileInternal(env, media_provider_object_, mid_scan_file_, path);
340 }
341 
IsCreatingDirAllowed(const string & path,uid_t uid)342 int MediaProviderWrapper::IsCreatingDirAllowed(const string& path, uid_t uid) {
343     if (shouldBypassMediaProvider(uid)) {
344         return 0;
345     }
346 
347     JNIEnv* env = MaybeAttachCurrentThread();
348     return isMkdirOrRmdirAllowedInternal(env, media_provider_object_,
349                                          mid_is_mkdir_or_rmdir_allowed_, path, uid,
350                                          /*forCreate*/ true);
351 }
352 
IsDeletingDirAllowed(const string & path,uid_t uid)353 int MediaProviderWrapper::IsDeletingDirAllowed(const string& path, uid_t uid) {
354     if (shouldBypassMediaProvider(uid)) {
355         return 0;
356     }
357 
358     JNIEnv* env = MaybeAttachCurrentThread();
359     return isMkdirOrRmdirAllowedInternal(env, media_provider_object_,
360                                          mid_is_mkdir_or_rmdir_allowed_, path, uid,
361                                          /*forCreate*/ false);
362 }
363 
GetDirectoryEntries(uid_t uid,const string & path,DIR * dirp)364 std::vector<std::shared_ptr<DirectoryEntry>> MediaProviderWrapper::GetDirectoryEntries(
365         uid_t uid, const string& path, DIR* dirp) {
366     // Default value in case JNI thread was being terminated
367     std::vector<std::shared_ptr<DirectoryEntry>> res;
368     if (shouldBypassMediaProvider(uid)) {
369         addDirectoryEntriesFromLowerFs(dirp, /* filter */ nullptr, &res);
370         return res;
371     }
372 
373     JNIEnv* env = MaybeAttachCurrentThread();
374     res = getFilesInDirectoryInternal(env, media_provider_object_, mid_get_files_in_dir_, uid, path);
375 
376     const int res_size = res.size();
377     if (res_size && res[0]->d_name[0] == '/') {
378         // Path is unknown to MediaProvider, get files and directories from lower file system.
379         res.resize(0);
380         addDirectoryEntriesFromLowerFs(dirp, /* filter */ nullptr, &res);
381     } else if (res_size == 0 || !res[0]->d_name.empty()) {
382         // add directory names from lower file system.
383         addDirectoryEntriesFromLowerFs(dirp, /* filter */ &isDirectory, &res);
384     }
385     return res;
386 }
387 
IsOpendirAllowed(const string & path,uid_t uid,bool forWrite)388 int MediaProviderWrapper::IsOpendirAllowed(const string& path, uid_t uid, bool forWrite) {
389     if (shouldBypassMediaProvider(uid)) {
390         return 0;
391     }
392 
393     JNIEnv* env = MaybeAttachCurrentThread();
394     return isOpendirAllowedInternal(env, media_provider_object_, mid_is_opendir_allowed_, path, uid,
395                                     forWrite);
396 }
397 
IsUidForPackage(const string & pkg,uid_t uid)398 bool MediaProviderWrapper::IsUidForPackage(const string& pkg, uid_t uid) {
399     if (shouldBypassMediaProvider(uid)) {
400         return true;
401     }
402 
403     JNIEnv* env = MaybeAttachCurrentThread();
404     return isUidForPackageInternal(env, media_provider_object_, mid_is_uid_for_package_, pkg, uid);
405 }
406 
Rename(const string & old_path,const string & new_path,uid_t uid)407 int MediaProviderWrapper::Rename(const string& old_path, const string& new_path, uid_t uid) {
408     // Rename from SHELL_UID should go through MediaProvider to update database rows, so only bypass
409     // MediaProvider for ROOT_UID.
410     if (uid == ROOT_UID) {
411         int res = rename(old_path.c_str(), new_path.c_str());
412         if (res != 0) res = -errno;
413         return res;
414     }
415 
416     JNIEnv* env = MaybeAttachCurrentThread();
417     return renameInternal(env, media_provider_object_, mid_rename_, old_path, new_path, uid);
418 }
419 
OnFileCreated(const string & path)420 void MediaProviderWrapper::OnFileCreated(const string& path) {
421     JNIEnv* env = MaybeAttachCurrentThread();
422 
423     return onFileCreatedInternal(env, media_provider_object_, mid_on_file_created_, path);
424 }
425 
426 /*****************************************************************************************/
427 /******************************** Private member functions *******************************/
428 /*****************************************************************************************/
429 
430 /**
431  * Finds MediaProvider method and adds it to methods map so it can be quickly called later.
432  */
CacheMethod(JNIEnv * env,const char method_name[],const char signature[],bool is_static)433 jmethodID MediaProviderWrapper::CacheMethod(JNIEnv* env, const char method_name[],
434                                             const char signature[], bool is_static) {
435     jmethodID mid;
436     string actual_method_name(method_name);
437     actual_method_name.append("ForFuse");
438     if (is_static) {
439         mid = env->GetStaticMethodID(media_provider_class_, actual_method_name.c_str(), signature);
440     } else {
441         mid = env->GetMethodID(media_provider_class_, actual_method_name.c_str(), signature);
442     }
443     if (!mid) {
444         // SHOULD NOT HAPPEN!
445         LOG(FATAL) << "Error caching method: " << method_name << signature;
446     }
447     return mid;
448 }
449 
DetachThreadFunction(void * unused)450 void MediaProviderWrapper::DetachThreadFunction(void* unused) {
451     int detach = gJavaVm->DetachCurrentThread();
452     CHECK_EQ(detach, 0);
453 }
454 
MaybeAttachCurrentThread()455 JNIEnv* MediaProviderWrapper::MaybeAttachCurrentThread() {
456     // We could use pthread_getspecific here as that's likely quicker but
457     // that would result in wrong behaviour for threads that don't need to
458     // be attached (e.g, those that were created in managed code).
459     JNIEnv* env = nullptr;
460     if (gJavaVm->GetEnv(reinterpret_cast<void**>(&env), JNI_VERSION_1_4) == JNI_OK) {
461         return env;
462     }
463 
464     // This thread is currently unattached, so it must not have any TLS
465     // value. Note that we don't really care about the actual value we store
466     // in TLS -- we only care about the value destructor being called, which
467     // will happen as long as the key is not null.
468     CHECK(pthread_getspecific(gJniEnvKey) == nullptr);
469     CHECK_EQ(gJavaVm->AttachCurrentThread(&env, nullptr), 0);
470     CHECK(env != nullptr);
471 
472     pthread_setspecific(gJniEnvKey, env);
473     return env;
474 }
475 
476 }  // namespace fuse
477 }  // namespace mediaprovider
478