xref: /aosp_15_r20/system/vold/model/EmulatedVolume.cpp (revision f40fafd4c6c2594924d919feffc1a1fd6e3b30f3)
1 /*
2  * Copyright (C) 2015 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 specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 #include "EmulatedVolume.h"
18 
19 #include "AppFuseUtil.h"
20 #include "Utils.h"
21 #include "VolumeBase.h"
22 #include "VolumeManager.h"
23 
24 #include <android-base/logging.h>
25 #include <android-base/properties.h>
26 #include <android-base/scopeguard.h>
27 #include <android-base/stringprintf.h>
28 #include <cutils/fs.h>
29 #include <private/android_filesystem_config.h>
30 #include <utils/Timers.h>
31 
32 #include <fcntl.h>
33 #include <stdlib.h>
34 #include <sys/mount.h>
35 #include <sys/stat.h>
36 #include <sys/sysmacros.h>
37 #include <sys/types.h>
38 #include <sys/wait.h>
39 
40 using android::base::StringPrintf;
41 
42 namespace android {
43 namespace vold {
44 
45 static const char* kSdcardFsPath = "/system/bin/sdcard";
46 
EmulatedVolume(const std::string & rawPath,int userId)47 EmulatedVolume::EmulatedVolume(const std::string& rawPath, int userId)
48     : VolumeBase(Type::kEmulated) {
49     setId(StringPrintf("emulated;%u", userId));
50     mRawPath = rawPath;
51     mLabel = "emulated";
52     mFuseMounted = false;
53     mUseSdcardFs = IsSdcardfsUsed();
54     mAppDataIsolationEnabled = base::GetBoolProperty(kVoldAppDataIsolationEnabled, false);
55 }
56 
EmulatedVolume(const std::string & rawPath,dev_t device,const std::string & fsUuid,int userId)57 EmulatedVolume::EmulatedVolume(const std::string& rawPath, dev_t device, const std::string& fsUuid,
58                                int userId)
59     : VolumeBase(Type::kEmulated) {
60     setId(StringPrintf("emulated:%u,%u;%u", major(device), minor(device), userId));
61     mRawPath = rawPath;
62     mLabel = fsUuid;
63     mFuseMounted = false;
64     mUseSdcardFs = IsSdcardfsUsed();
65     mAppDataIsolationEnabled = base::GetBoolProperty(kVoldAppDataIsolationEnabled, false);
66 }
67 
~EmulatedVolume()68 EmulatedVolume::~EmulatedVolume() {}
69 
getLabel() const70 std::string EmulatedVolume::getLabel() const {
71     // We could have migrated storage to an adopted private volume, so always
72     // call primary storage "emulated" to avoid media rescans.
73     if (getMountFlags() & MountFlags::kPrimary) {
74         return "emulated";
75     } else {
76         return mLabel;
77     }
78 }
79 
80 // Creates a bind mount from source to target
doFuseBindMount(const std::string & source,const std::string & target,std::list<std::string> & pathsToUnmount)81 static status_t doFuseBindMount(const std::string& source, const std::string& target,
82                                 std::list<std::string>& pathsToUnmount) {
83     LOG(INFO) << "Bind mounting " << source << " on " << target;
84     auto status = BindMount(source, target);
85     if (status != OK) {
86         return status;
87     }
88     LOG(INFO) << "Bind mounted " << source << " on " << target;
89     pathsToUnmount.push_front(target);
90     return OK;
91 }
92 
93 // Bind mounts the volume 'volume' onto this volume.
bindMountVolume(const EmulatedVolume & volume,std::list<std::string> & pathsToUnmount)94 status_t EmulatedVolume::bindMountVolume(const EmulatedVolume& volume,
95                                          std::list<std::string>& pathsToUnmount) {
96     int myUserId = getMountUserId();
97     int volumeUserId = volume.getMountUserId();
98     std::string label = volume.getLabel();
99 
100     // eg /mnt/user/10/emulated/10
101     std::string srcUserPath = GetFuseMountPathForUser(volumeUserId, label);
102     std::string srcPath = StringPrintf("%s/%d", srcUserPath.c_str(), volumeUserId);
103     // eg /mnt/user/0/emulated/10
104     std::string dstUserPath = GetFuseMountPathForUser(myUserId, label);
105     std::string dstPath = StringPrintf("%s/%d", dstUserPath.c_str(), volumeUserId);
106 
107     auto status = doFuseBindMount(srcPath, dstPath, pathsToUnmount);
108     if (status == OK) {
109         // Store the mount path, so we can unmount it when this volume goes away
110         mSharedStorageMountPath = dstPath;
111     }
112 
113     return status;
114 }
115 
mountFuseBindMounts()116 status_t EmulatedVolume::mountFuseBindMounts() {
117     std::string androidSource;
118     std::string label = getLabel();
119     int userId = getMountUserId();
120     std::list<std::string> pathsToUnmount;
121 
122     auto unmounter = [&]() {
123         LOG(INFO) << "mountFuseBindMounts() unmount scope_guard running";
124         for (const auto& path : pathsToUnmount) {
125             LOG(INFO) << "Unmounting " << path;
126             auto status = UnmountTree(path);
127             if (status != OK) {
128                 LOG(INFO) << "Failed to unmount " << path;
129             } else {
130                 LOG(INFO) << "Unmounted " << path;
131             }
132         }
133     };
134     auto unmount_guard = android::base::make_scope_guard(unmounter);
135 
136     if (mUseSdcardFs) {
137         androidSource = StringPrintf("/mnt/runtime/default/%s/%d/Android", label.c_str(), userId);
138     } else {
139         androidSource = StringPrintf("/%s/%d/Android", mRawPath.c_str(), userId);
140     }
141 
142     status_t status = OK;
143     // Zygote will unmount these dirs if app data isolation is enabled, so apps
144     // cannot access these dirs directly.
145     std::string androidDataSource = StringPrintf("%s/data", androidSource.c_str());
146     std::string androidDataTarget(
147             StringPrintf("/mnt/user/%d/%s/%d/Android/data", userId, label.c_str(), userId));
148     status = doFuseBindMount(androidDataSource, androidDataTarget, pathsToUnmount);
149     if (status != OK) {
150         return status;
151     }
152 
153     std::string androidObbSource = StringPrintf("%s/obb", androidSource.c_str());
154     std::string androidObbTarget(
155             StringPrintf("/mnt/user/%d/%s/%d/Android/obb", userId, label.c_str(), userId));
156     status = doFuseBindMount(androidObbSource, androidObbTarget, pathsToUnmount);
157     if (status != OK) {
158         return status;
159     }
160 
161     // Installers get the same view as all other apps, with the sole exception that the
162     // OBB dirs (Android/obb) are writable to them. On sdcardfs devices, this requires
163     // a special bind mount, since app-private and OBB dirs share the same GID, but we
164     // only want to give access to the latter.
165     if (mUseSdcardFs) {
166         std::string obbSource(StringPrintf("/mnt/runtime/write/%s/%d/Android/obb",
167                 label.c_str(), userId));
168         std::string obbInstallerTarget(StringPrintf("/mnt/installer/%d/%s/%d/Android/obb",
169                 userId, label.c_str(), userId));
170 
171         status = doFuseBindMount(obbSource, obbInstallerTarget, pathsToUnmount);
172         if (status != OK) {
173             return status;
174         }
175     }
176 
177     // For users that share their volume with another user (eg a clone
178     // profile), the current mount setup can cause page cache inconsistency
179     // issues.  Let's say this is user 10, and the user it shares storage with
180     // is user 0.
181     // Then:
182     // * The FUSE daemon for user 0 serves /mnt/user/0
183     // * The FUSE daemon for user 10 serves /mnt/user/10
184     // The emulated volume for user 10 would be located at two paths:
185     // /mnt/user/0/emulated/10
186     // /mnt/user/10/emulated/10
187     // Since these paths refer to the same files but are served by different FUSE
188     // daemons, this can result in page cache inconsistency issues. To prevent this,
189     // bind mount the relevant paths for the involved users:
190     // 1. /mnt/user/10/emulated/10 =B=> /mnt/user/0/emulated/10
191     // 2. /mnt/user/0/emulated/0 =B=> /mnt/user/10/emulated/0
192     //
193     // This will ensure that any access to the volume for a specific user always
194     // goes through a single FUSE daemon.
195     userid_t sharedStorageUserId = VolumeManager::Instance()->getSharedStorageUser(userId);
196     if (sharedStorageUserId != USER_UNKNOWN) {
197         auto filter_fn = [&](const VolumeBase& vol) {
198             if (vol.getState() != VolumeBase::State::kMounted) {
199                 // The volume must be mounted
200                 return false;
201             }
202             if (vol.getType() != VolumeBase::Type::kEmulated) {
203                 return false;
204             }
205             if (vol.getMountUserId() != sharedStorageUserId) {
206                 return false;
207             }
208             if ((vol.getMountFlags() & MountFlags::kPrimary) == 0) {
209                 // We only care about the primary emulated volume, so not a private
210                 // volume with an emulated volume stacked on top.
211                 return false;
212             }
213             return true;
214         };
215         auto vol = VolumeManager::Instance()->findVolumeWithFilter(filter_fn);
216         if (vol != nullptr) {
217             auto sharedVol = static_cast<EmulatedVolume*>(vol.get());
218             // Bind mount this volume in the other user's primary volume
219             status = sharedVol->bindMountVolume(*this, pathsToUnmount);
220             if (status != OK) {
221                 return status;
222             }
223             // And vice-versa
224             status = bindMountVolume(*sharedVol, pathsToUnmount);
225             if (status != OK) {
226                 return status;
227             }
228         }
229     }
230     unmount_guard.Disable();
231     return OK;
232 }
233 
unmountFuseBindMounts()234 status_t EmulatedVolume::unmountFuseBindMounts() {
235     std::string label = getLabel();
236     int userId = getMountUserId();
237 
238     if (!mSharedStorageMountPath.empty()) {
239         LOG(INFO) << "Unmounting " << mSharedStorageMountPath;
240         auto status = UnmountTree(mSharedStorageMountPath);
241         if (status != OK) {
242             LOG(ERROR) << "Failed to unmount " << mSharedStorageMountPath;
243         }
244         mSharedStorageMountPath = "";
245     }
246     if (mUseSdcardFs || mAppDataIsolationEnabled) {
247         std::string installerTarget(
248                 StringPrintf("/mnt/installer/%d/%s/%d/Android/obb", userId, label.c_str(), userId));
249         LOG(INFO) << "Unmounting " << installerTarget;
250         auto status = UnmountTree(installerTarget);
251         if (status != OK) {
252             LOG(ERROR) << "Failed to unmount " << installerTarget;
253             // Intentional continue to try to unmount the other bind mount
254         }
255     }
256     if (mAppDataIsolationEnabled) {
257         std::string obbTarget( StringPrintf("/mnt/androidwritable/%d/%s/%d/Android/obb",
258                 userId, label.c_str(), userId));
259         LOG(INFO) << "Unmounting " << obbTarget;
260         auto status = UnmountTree(obbTarget);
261         if (status != OK) {
262             LOG(ERROR) << "Failed to unmount " << obbTarget;
263             // Intentional continue to try to unmount the other bind mount
264         }
265         std::string dataTarget(StringPrintf("/mnt/androidwritable/%d/%s/%d/Android/data",
266                 userId, label.c_str(), userId));
267         LOG(INFO) << "Unmounting " << dataTarget;
268         status = UnmountTree(dataTarget);
269         if (status != OK) {
270             LOG(ERROR) << "Failed to unmount " << dataTarget;
271             // Intentional continue to try to unmount the other bind mount
272         }
273     }
274 
275     // When app data isolation is enabled, kill all apps that obb/ is mounted, otherwise we should
276     // umount the whole Android/ dir.
277     if (mAppDataIsolationEnabled) {
278         std::string appObbDir(StringPrintf("%s/%d/Android/obb", getPath().c_str(), userId));
279         // Here we assume obb/data dirs is mounted as tmpfs, then it must be caused by
280         // app data isolation.
281         KillProcessesWithTmpfsMountPrefix(appObbDir);
282     }
283 
284     // Always unmount data and obb dirs as they are mounted to lowerfs for speeding up access.
285     std::string androidDataTarget(
286             StringPrintf("/mnt/user/%d/%s/%d/Android/data", userId, label.c_str(), userId));
287 
288     LOG(INFO) << "Unmounting " << androidDataTarget;
289     auto status = UnmountTree(androidDataTarget);
290     if (status != OK) {
291         return status;
292     }
293     LOG(INFO) << "Unmounted " << androidDataTarget;
294 
295     std::string androidObbTarget(
296             StringPrintf("/mnt/user/%d/%s/%d/Android/obb", userId, label.c_str(), userId));
297 
298     LOG(INFO) << "Unmounting " << androidObbTarget;
299     status = UnmountTree(androidObbTarget);
300     if (status != OK) {
301         return status;
302     }
303     LOG(INFO) << "Unmounted " << androidObbTarget;
304     return OK;
305 }
306 
unmountSdcardFs()307 status_t EmulatedVolume::unmountSdcardFs() {
308     if (!mUseSdcardFs || getMountUserId() != 0) {
309         // For sdcardfs, only unmount for user 0, since user 0 will always be running
310         // and the paths don't change for different users.
311         return OK;
312     }
313 
314     ForceUnmount(mSdcardFsDefault);
315     ForceUnmount(mSdcardFsRead);
316     ForceUnmount(mSdcardFsWrite);
317     ForceUnmount(mSdcardFsFull);
318 
319     rmdir(mSdcardFsDefault.c_str());
320     rmdir(mSdcardFsRead.c_str());
321     rmdir(mSdcardFsWrite.c_str());
322     rmdir(mSdcardFsFull.c_str());
323 
324     mSdcardFsDefault.clear();
325     mSdcardFsRead.clear();
326     mSdcardFsWrite.clear();
327     mSdcardFsFull.clear();
328 
329     return OK;
330 }
331 
doMount()332 status_t EmulatedVolume::doMount() {
333     std::string label = getLabel();
334     bool isVisible = isVisibleForWrite();
335 
336     mSdcardFsDefault = StringPrintf("/mnt/runtime/default/%s", label.c_str());
337     mSdcardFsRead = StringPrintf("/mnt/runtime/read/%s", label.c_str());
338     mSdcardFsWrite = StringPrintf("/mnt/runtime/write/%s", label.c_str());
339     mSdcardFsFull = StringPrintf("/mnt/runtime/full/%s", label.c_str());
340 
341     setInternalPath(mRawPath);
342     setPath(StringPrintf("/storage/%s", label.c_str()));
343 
344     if (fs_prepare_dir(mSdcardFsDefault.c_str(), 0700, AID_ROOT, AID_ROOT) ||
345         fs_prepare_dir(mSdcardFsRead.c_str(), 0700, AID_ROOT, AID_ROOT) ||
346         fs_prepare_dir(mSdcardFsWrite.c_str(), 0700, AID_ROOT, AID_ROOT) ||
347         fs_prepare_dir(mSdcardFsFull.c_str(), 0700, AID_ROOT, AID_ROOT)) {
348         PLOG(ERROR) << getId() << " failed to create mount points";
349         return -errno;
350     }
351 
352     dev_t before = GetDevice(mSdcardFsFull);
353 
354     // Mount sdcardfs regardless of FUSE, since we need it to bind-mount on top of the
355     // FUSE volume for various reasons.
356     if (mUseSdcardFs && getMountUserId() == 0) {
357         LOG(INFO) << "Executing sdcardfs";
358         int sdcardFsPid;
359         if (!(sdcardFsPid = fork())) {
360             // clang-format off
361             if (execl(kSdcardFsPath, kSdcardFsPath,
362                     "-u", "1023", // AID_MEDIA_RW
363                     "-g", "1023", // AID_MEDIA_RW
364                     "-m",
365                     "-w",
366                     "-G",
367                     "-i",
368                     "-o",
369                     mRawPath.c_str(),
370                     label.c_str(),
371                     NULL)) {
372                 // clang-format on
373                 PLOG(ERROR) << "Failed to exec";
374             }
375 
376             LOG(ERROR) << "sdcardfs exiting";
377             _exit(1);
378         }
379 
380         if (sdcardFsPid == -1) {
381             PLOG(ERROR) << getId() << " failed to fork";
382             return -errno;
383         }
384 
385         nsecs_t start = systemTime(SYSTEM_TIME_BOOTTIME);
386         while (before == GetDevice(mSdcardFsFull)) {
387             LOG(DEBUG) << "Waiting for sdcardfs to spin up...";
388             usleep(50000);  // 50ms
389 
390             nsecs_t now = systemTime(SYSTEM_TIME_BOOTTIME);
391             if (nanoseconds_to_milliseconds(now - start) > 5000) {
392                 LOG(WARNING) << "Timed out while waiting for sdcardfs to spin up";
393                 return -ETIMEDOUT;
394             }
395         }
396         /* sdcardfs will have exited already. The filesystem will still be running */
397         TEMP_FAILURE_RETRY(waitpid(sdcardFsPid, nullptr, 0));
398         sdcardFsPid = 0;
399     }
400 
401     if (isVisible) {
402         // Make sure we unmount sdcardfs if we bail out with an error below
403         auto sdcardfs_unmounter = [&]() {
404             LOG(INFO) << "sdcardfs_unmounter scope_guard running";
405             unmountSdcardFs();
406         };
407         auto sdcardfs_guard = android::base::make_scope_guard(sdcardfs_unmounter);
408 
409         LOG(INFO) << "Mounting emulated fuse volume";
410         android::base::unique_fd fd;
411         int user_id = getMountUserId();
412         auto volumeRoot = getRootPath();
413 
414         // Make sure Android/ dirs exist for bind mounting
415         status_t res = PrepareAndroidDirs(volumeRoot);
416         if (res != OK) {
417             LOG(ERROR) << "Failed to prepare Android/ directories";
418             return res;
419         }
420 
421         res = MountUserFuse(user_id, getInternalPath(), label, &fd);
422         if (res != 0) {
423             PLOG(ERROR) << "Failed to mount emulated fuse volume";
424             return res;
425         }
426 
427         mFuseMounted = true;
428         auto fuse_unmounter = [&]() {
429             LOG(INFO) << "fuse_unmounter scope_guard running";
430             fd.reset();
431             if (UnmountUserFuse(user_id, getInternalPath(), label) != OK) {
432                 PLOG(INFO) << "UnmountUserFuse failed on emulated fuse volume";
433             }
434             mFuseMounted = false;
435         };
436         auto fuse_guard = android::base::make_scope_guard(fuse_unmounter);
437 
438         auto callback = getMountCallback();
439         if (callback) {
440             bool is_ready = false;
441             callback->onVolumeChecking(std::move(fd), getPath(), getInternalPath(), &is_ready);
442             if (!is_ready) {
443                 return -EIO;
444             }
445         }
446 
447         if (!IsFuseBpfEnabled()) {
448             // Only do the bind-mounts when we know for sure the FUSE daemon can resolve the path.
449             res = mountFuseBindMounts();
450             if (res != OK) {
451                 return res;
452             }
453         }
454 
455         ConfigureReadAheadForFuse(GetFuseMountPathForUser(user_id, label), 256u);
456 
457         // By default, FUSE has a max_dirty ratio of 1%. This means that out of
458         // all dirty pages in the system, only 1% is allowed to belong to any
459         // FUSE filesystem. The reason this is in place is that FUSE
460         // filesystems shouldn't be trusted by default; a FUSE filesystem could
461         // take up say 100% of dirty pages, and subsequently refuse to write
462         // them back to storage.  The kernel will then apply rate-limiting, and
463         // block other tasks from writing.  For this particular FUSE filesystem
464         // however, we trust the implementation, because it is a part of the
465         // Android platform. So use the default ratio of 100%.
466         //
467         // The reason we're setting this is that there's a suspicion that the
468         // kernel starts rate-limiting the FUSE filesystem under extreme
469         // memory pressure scenarios. While the kernel will only rate limit if
470         // the writeback can't keep up with the write rate, under extreme
471         // memory pressure the write rate may dip as well, in which case FUSE
472         // writes to a 1% max_ratio filesystem are throttled to an extreme amount.
473         //
474         // To prevent this, just give FUSE 40% max_ratio, meaning it can take
475         // up to 40% of all dirty pages in the system.
476         ConfigureMaxDirtyRatioForFuse(GetFuseMountPathForUser(user_id, label), 40u);
477 
478         // All mounts where successful, disable scope guards
479         sdcardfs_guard.Disable();
480         fuse_guard.Disable();
481     }
482 
483     return OK;
484 }
485 
doUnmount()486 status_t EmulatedVolume::doUnmount() {
487     int userId = getMountUserId();
488 
489     // Kill all processes using the filesystem before we unmount it. If we
490     // unmount the filesystem first, most file system operations will return
491     // ENOTCONN until the unmount completes. This is an exotic and unusual
492     // error code and might cause broken behaviour in applications.
493     if (mFuseMounted) {
494         // For FUSE specifically, we have an emulated volume per user, so only kill
495         // processes using files from this particular user.
496         std::string user_path(StringPrintf("%s/%d", getPath().c_str(), getMountUserId()));
497         LOG(INFO) << "Killing all processes referencing " << user_path;
498         KillProcessesUsingPath(user_path);
499     } else {
500         KillProcessesUsingPath(getPath());
501     }
502 
503     if (mFuseMounted) {
504         std::string label = getLabel();
505 
506         if (!IsFuseBpfEnabled()) {
507             // Ignoring unmount return status because we do want to try to
508             // unmount the rest cleanly.
509             unmountFuseBindMounts();
510         }
511 
512         if (UnmountUserFuse(userId, getInternalPath(), label) != OK) {
513             PLOG(INFO) << "UnmountUserFuse failed on emulated fuse volume";
514             return -errno;
515         }
516 
517         mFuseMounted = false;
518     }
519 
520     return unmountSdcardFs();
521 }
522 
getRootPath() const523 std::string EmulatedVolume::getRootPath() const {
524     int user_id = getMountUserId();
525     std::string volumeRoot = StringPrintf("%s/%d", getInternalPath().c_str(), user_id);
526 
527     return volumeRoot;
528 }
529 
530 }  // namespace vold
531 }  // namespace android
532