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