platform_system_vold/model/EmulatedVolume.h
Martijn Coenen 73e3010a25 Support bind mounting volumes into other volume's mountpoint.
With the way the FUSE mount point are currently setup for emulated
volumes, there can be multiple paths that serve the same files on the
lower filesystem; eg

* /mnt/user/0/emulated/0/Android
* /mnt/user/10/emulated/0/Android

both refer to the same file on the lower filesystem:
* /data/media/0/Android

this is normally not a problem, because cross-user file access is not
allowed, and so the FUSE daemon won't serve files for other users.

With clone profiles this is no longer true however, as their volumes
are accessible by each other.

So, it can happen that an app running in clone profile 10 accesses
"/mnt/user/10/emulated/0/Android", which would be served by the FUSE
daemon for the user 10 filesystem.

At the same time, an app running in the owner profile 0 accesses
"mnt/user/0/emulated/0/Android", which would be served by the FUSE
daemon for the user 0 filesystem.

This can cause page cache inconsistencies, because multiple FUSE daemons
can be running on top of the same entries in the lower filesystem.

To prevent this, use bind mounts to make sure that cross-profile
accesses actually end up in the FUSE daemon to which the volume
belongs: "/mnt/user/10/emulated/0" is bind-mounted to
"/mnt/user/0/emulated/0", and vice-versa.

Bug: 228271997
Test: manual
Change-Id: Iefcbc813670628b329a1a5d408b6126b84991e09
2022-12-07 09:01:27 +00:00

87 lines
2.6 KiB
C++

/*
* Copyright (C) 2015 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#ifndef ANDROID_VOLD_EMULATED_VOLUME_H
#define ANDROID_VOLD_EMULATED_VOLUME_H
#include "VolumeBase.h"
#include <cutils/multiuser.h>
namespace android {
namespace vold {
/*
* Shared storage emulated on top of private storage.
*
* Knows how to spawn a sdcardfs daemon to synthesize permissions. ObbVolume
* can be stacked above it.
*
* This volume is always multi-user aware, but is only binds itself to
* users when its primary storage. This volume should never be presented
* as secondary storage, since we're strongly encouraging developers to
* store data local to their app.
*/
class EmulatedVolume : public VolumeBase {
public:
explicit EmulatedVolume(const std::string& rawPath, int userId);
EmulatedVolume(const std::string& rawPath, dev_t device, const std::string& fsUuid, int userId);
virtual ~EmulatedVolume();
std::string getRootPath() const override;
bool isFuseMounted() const { return mFuseMounted; }
protected:
status_t doMount() override;
status_t doUnmount() override;
private:
status_t unmountSdcardFs();
status_t mountFuseBindMounts();
status_t unmountFuseBindMounts();
status_t bindMountVolume(const EmulatedVolume& vol, std::list<std::string>& pathsToUnmount);
std::string getLabel() const;
std::string mRawPath;
std::string mLabel;
std::string mSdcardFsDefault;
std::string mSdcardFsRead;
std::string mSdcardFsWrite;
std::string mSdcardFsFull;
/* Whether we mounted FUSE for this volume */
bool mFuseMounted;
/* Whether the FUSE BPF feature is enabled */
bool mFuseBpfEnabled;
/* Whether to use sdcardfs for this volume */
bool mUseSdcardFs;
/* Whether to use app data isolation is enabled tor this volume */
bool mAppDataIsolationEnabled;
/* Location of bind mount for another profile that shares storage with us */
std::string mSharedStorageMountPath = "";
DISALLOW_COPY_AND_ASSIGN(EmulatedVolume);
};
} // namespace vold
} // namespace android
#endif