@ -80,6 +80,7 @@ using android::vold::BindMount;
using android : : vold : : CreateDir ;
using android : : vold : : DeleteDirContents ;
using android : : vold : : DeleteDirContentsAndDir ;
using android : : vold : : EnsureDirExists ;
using android : : vold : : IsFilesystemSupported ;
using android : : vold : : PrepareAndroidDirs ;
using android : : vold : : PrepareAppDirFromRoot ;
@ -104,6 +105,8 @@ static const unsigned int kMajorBlockMmc = 179;
static const unsigned int kMajorBlockExperimentalMin = 240 ;
static const unsigned int kMajorBlockExperimentalMax = 254 ;
using ScanProcCallback = bool ( * ) ( uid_t uid , pid_t pid , int nsFd , const char * name , void * params ) ;
VolumeManager * VolumeManager : : sInstance = NULL ;
VolumeManager * VolumeManager : : Instance ( ) {
@ -534,9 +537,9 @@ int VolumeManager::setPrimary(const std::shared_ptr<android::vold::VolumeBase>&
// TODO: Get rid of this guesswork altogether and instead exec a process
// immediately after fork to do our bindding for us.
static bool childProcess ( const char * storageSource , const char * userSource , int nsFd ,
struct dirent * d e) {
const char * nam e) {
if ( setns ( nsFd , CLONE_NEWNS ) ! = 0 ) {
async_safe_format_log ( ANDROID_LOG_ERROR , " vold " , " Failed to setns for %s :%s " , de- > d_ name,
async_safe_format_log ( ANDROID_LOG_ERROR , " vold " , " Failed to setns for %s :%s " , name,
strerror ( errno ) ) ;
return false ;
}
@ -553,59 +556,82 @@ static bool childProcess(const char* storageSource, const char* userSource, int
if ( TEMP_FAILURE_RETRY ( mount ( storageSource , " /storage " , NULL , MS_BIND | MS_REC , NULL ) ) = = - 1 ) {
async_safe_format_log ( ANDROID_LOG_ERROR , " vold " , " Failed to mount %s for %s :%s " ,
storageSource , de- > d_ name, strerror ( errno ) ) ;
storageSource , name, strerror ( errno ) ) ;
return false ;
}
if ( TEMP_FAILURE_RETRY ( mount ( NULL , " /storage " , NULL , MS_REC | MS_SLAVE , NULL ) ) = = - 1 ) {
async_safe_format_log ( ANDROID_LOG_ERROR , " vold " ,
" Failed to set MS_SLAVE to /storage for %s :%s " , de- > d_ name,
" Failed to set MS_SLAVE to /storage for %s :%s " , name,
strerror ( errno ) ) ;
return false ;
}
if ( TEMP_FAILURE_RETRY ( mount ( userSource , " /storage/self " , NULL , MS_BIND , NULL ) ) = = - 1 ) {
async_safe_format_log ( ANDROID_LOG_ERROR , " vold " , " Failed to mount %s for %s :%s " ,
userSource , de- > d_ name, strerror ( errno ) ) ;
userSource , name, strerror ( errno ) ) ;
return false ;
}
return true ;
}
int VolumeManager : : remountUid ( uid_t uid , int32_t mountMode ) {
if ( GetBoolProperty ( android : : vold : : kPropFuse , false ) ) {
// TODO(135341433): Implement fuse specific logic.
return 0 ;
}
std : : string mode ;
// Fork the process and remount storage
bool forkAndRemountChild ( uid_t uid , pid_t pid , int nsFd , const char * name , void * params ) {
int32_t mountMode = * static_cast < int32_t * > ( params ) ;
std : : string userSource ;
std : : string storageSource ;
pid_t child ;
// Need to fix these paths to account for when sdcardfs is gone
switch ( mountMode ) {
case VoldNativeService : : REMOUNT_MODE_NONE :
mode = " none " ;
break ;
return true ;
case VoldNativeService : : REMOUNT_MODE_DEFAULT :
mode = " default" ;
storageSource = " /mnt/runtime/ default" ;
break ;
case VoldNativeService : : REMOUNT_MODE_READ :
mode = " read" ;
storageSource = " /mnt/runtime/ read" ;
break ;
case VoldNativeService : : REMOUNT_MODE_WRITE :
case VoldNativeService : : REMOUNT_MODE_LEGACY :
case VoldNativeService : : REMOUNT_MODE_INSTALLER :
mode = " write" ;
storageSource = " /mnt/runtime/ write" ;
break ;
case VoldNativeService : : REMOUNT_MODE_FULL :
mode = " full" ;
storageSource = " /mnt/runtime/ full" ;
break ;
case VoldNativeService : : REMOUNT_MODE_PASS_THROUGH :
mode = " pass_through " ;
break ;
return true ;
default :
PLOG ( ERROR ) < < " Unknown mode " < < std : : to_string ( mountMode ) ;
return - 1 ;
return false ;
}
LOG ( DEBUG ) < < " Remounting " < < uid < < " as " < < storageSource ;
// Fork a child to mount user-specific symlink helper into place
userSource = StringPrintf ( " /mnt/user/%d " , multiuser_get_user_id ( uid ) ) ;
if ( ! ( child = fork ( ) ) ) {
if ( childProcess ( storageSource . c_str ( ) , userSource . c_str ( ) , nsFd , name ) ) {
_exit ( 0 ) ;
} else {
_exit ( 1 ) ;
}
}
LOG ( DEBUG ) < < " Remounting " < < uid < < " as mode " < < mode ;
if ( child = = - 1 ) {
PLOG ( ERROR ) < < " Failed to fork " ;
return false ;
} else {
TEMP_FAILURE_RETRY ( waitpid ( child , nullptr , 0 ) ) ;
}
return true ;
}
// Helper function to scan all processes in /proc and call the callback if:
// 1). pid belongs to an app process
// 2). If input uid is 0 or it matches the process uid
// 3). If userId is not -1 or userId matches the process userId
bool scanProcProcesses ( uid_t uid , userid_t userId , ScanProcCallback callback , void * params ) {
DIR * dir ;
struct dirent * de ;
std : : string rootName ;
@ -613,24 +639,22 @@ int VolumeManager::remountUid(uid_t uid, int32_t mountMode) {
int pidFd ;
int nsFd ;
struct stat sb ;
pid_t child ;
std : : string userSource ;
std : : string storageSource ;
static bool apexUpdatable = android : : sysprop : : ApexProperties : : updatable ( ) . value_or ( false ) ;
if ( ! ( dir = opendir ( " /proc " ) ) ) {
PLOG( ERROR ) < < " Failed to opendir " ;
return - 1 ;
async_safe_format_log( ANDROID_LOG_ERROR , " vold " , " Failed to opendir " ) ;
return false ;
}
// Figure out root namespace to compare against below
if ( ! android : : vold : : Readlinkat ( dirfd ( dir ) , " 1/ns/mnt " , & rootName ) ) {
PLOG( ERROR ) < < " Failed to read root namespace " ;
async_safe_format_log( ANDROID_LOG_ERROR , " vold " , " Failed to read root namespace " ) ;
closedir ( dir ) ;
return - 1 ;
return false ;
}
async_safe_format_log ( ANDROID_LOG_INFO , " vold " , " Start scanning all processes " ) ;
// Poke through all running PIDs look for apps running as UID
while ( ( de = readdir ( dir ) ) ) {
pid_t pid ;
@ -645,21 +669,23 @@ int VolumeManager::remountUid(uid_t uid, int32_t mountMode) {
goto next ;
}
if ( fstat ( pidFd , & sb ) ! = 0 ) {
PLOG( WARNING ) < < " Failed to stat " < < de - > d_name ;
async_safe_format_log( ANDROID_LOG_ERROR , " vold " , " Failed to stat %s " , de - > d_name ) ;
goto next ;
}
if ( sb . st_uid ! = uid ) {
if ( uid ! = 0 & & sb . st_uid ! = uid ) {
goto next ;
}
if ( userId ! = static_cast < userid_t > ( - 1 ) & & multiuser_get_user_id ( sb . st_uid ) ! = userId ) {
goto next ;
}
// Matches so far, but refuse to touch if in root namespace
LOG ( DEBUG ) < < " Found matching PID " < < de - > d_name ;
if ( ! android : : vold : : Readlinkat ( pidFd , " ns/mnt " , & pidName ) ) {
PLOG ( WARNING ) < < " Failed to read namespace for " < < de - > d_name ;
async_safe_format_log ( ANDROID_LOG_ERROR , " vold " ,
" Failed to read namespacefor %s " , de - > d_name ) ;
goto next ;
}
if ( rootName = = pidName ) {
LOG ( WARNING ) < < " Skipping due to root namespace " ;
goto next ;
}
@ -674,11 +700,9 @@ int VolumeManager::remountUid(uid_t uid, int32_t mountMode) {
// non-Java process whose UID is < AID_APP_START. (The UID condition
// is required to not filter out child processes spawned by apps.)
if ( ! android : : vold : : Readlinkat ( pidFd , " exe " , & exeName ) ) {
PLOG ( WARNING ) < < " Failed to read exe name for " < < de - > d_name ;
goto next ;
}
if ( ! StartsWith ( exeName , " /system/bin/app_process " ) & & sb . st_uid < AID_APP_START ) {
LOG ( WARNING ) < < " Skipping due to native system process " ;
goto next ;
}
}
@ -687,49 +711,218 @@ int VolumeManager::remountUid(uid_t uid, int32_t mountMode) {
// NOLINTNEXTLINE(android-cloexec-open): Deliberately not O_CLOEXEC
nsFd = openat ( pidFd , " ns/mnt " , O_RDONLY ) ;
if ( nsFd < 0 ) {
PLOG ( WARNING ) < < " Failed to open namespace for " < < de - > d_name ;
async_safe_format_log ( ANDROID_LOG_ERROR , " vold " ,
" Failed to open namespace for %s " , de - > d_name ) ;
goto next ;
}
if ( mode = = " default " ) {
storageSource = " /mnt/runtime/default " ;
} else if ( mode = = " read " ) {
storageSource = " /mnt/runtime/read " ;
} else if ( mode = = " write " ) {
storageSource = " /mnt/runtime/write " ;
} else if ( mode = = " full " ) {
storageSource = " /mnt/runtime/full " ;
} else {
// Sane default of no storage visible. No need to fork a child
// to remount uid.
goto next ;
if ( ! callback ( sb . st_uid , pid , nsFd , de - > d_name , params ) ) {
async_safe_format_log ( ANDROID_LOG_ERROR , " vold " , " Failed in callback " ) ;
}
// Mount user-specific symlink helper into place
userSource = StringPrintf ( " /mnt/user/%d " , multiuser_get_user_id ( uid ) ) ;
if ( ! ( child = fork ( ) ) ) {
if ( childProcess ( storageSource . c_str ( ) , userSource . c_str ( ) , nsFd , de ) ) {
_exit ( 0 ) ;
} else {
_exit ( 1 ) ;
}
next :
close ( nsFd ) ;
close ( pidFd ) ;
}
closedir ( dir ) ;
async_safe_format_log ( ANDROID_LOG_INFO , " vold " , " Finished scanning all processes " ) ;
return true ;
}
int VolumeManager : : remountUid ( uid_t uid , int32_t mountMode ) {
if ( GetBoolProperty ( android : : vold : : kPropFuse , false ) ) {
// TODO(135341433): Implement fuse specific logic.
return 0 ;
}
return scanProcProcesses ( uid , static_cast < userid_t > ( - 1 ) ,
forkAndRemountChild , & mountMode ) ? 0 : - 1 ;
}
// Bind mount obb dir for an app if necessary.
// How it works:
// 1). Check if a pid is an app uid and not the FuseDaemon, if not then return.
// 2). Get the mounts for that pid.
// 3). If obb is already mounted then return, otherwise we need to mount obb for this pid.
// 4). Get all packages and uid mounted for jit profile. These packages are all packages with
// same uid or whitelisted apps.
// 5a). If there's no package, it means it's not a process running app data isolation, so
// just bind mount Android/obb dir.
// 5b). Otherwise, for each package, create obb dir if it's not created and bind mount it.
// TODO: Should we get some reliable data from system server instead of scanning /proc ?
static bool bindMountAppObbDir ( uid_t uid , pid_t pid , int nsFd , const char * name , void * params ) {
if ( uid < AID_APP_START | | uid > AID_APP_END ) {
return true ;
}
if ( android : : vold : : IsFuseDaemon ( pid ) ) {
return true ;
}
async_safe_format_log ( ANDROID_LOG_ERROR , " vold " , " Start mounting obb for uid:%d, pid:%d " , uid ,
pid ) ;
userid_t userId = multiuser_get_user_id ( uid ) ;
if ( setns ( nsFd , CLONE_NEWNS ) ! = 0 ) {
async_safe_format_log ( ANDROID_LOG_ERROR , " vold " , " Failed to setns %s " , strerror ( errno ) ) ;
return false ;
}
std : : string profiles_path ( StringPrintf ( " /data/misc/profiles/cur/%d/ " , userId ) ) ;
// We search both .../obb and .../obb/$PKG paths here.
std : : string obb_path ( StringPrintf ( " /storage/emulated/%d/Android/obb " , userId ) ) ;
int profiles_path_len = profiles_path . length ( ) ;
int obb_path_len = obb_path . length ( ) ;
// TODO: Refactor the code as a util function so we can reuse the mount parsing code.
std : : string mounts_file ( StringPrintf ( " /proc/%d/mounts " , pid ) ) ;
auto fp = std : : unique_ptr < FILE , int ( * ) ( FILE * ) > (
setmntent ( mounts_file . c_str ( ) , " r " ) , endmntent ) ;
if ( ! fp ) {
async_safe_format_log ( ANDROID_LOG_ERROR , " vold " , " Error opening %s: %s " ,
mounts_file . c_str ( ) , strerror ( errno ) ) ;
return false ;
}
// Check if obb directory is mounted, and get all packages of mounted app data directory.
bool obb_mounted = false ;
std : : vector < std : : string > pkg_name_list ;
mntent * mentry ;
while ( ( mentry = getmntent ( fp . get ( ) ) ) ! = nullptr ) {
if ( strncmp ( mentry - > mnt_dir , profiles_path . c_str ( ) , profiles_path_len ) = = 0 ) {
pkg_name_list . push_back ( std : : string ( mentry - > mnt_dir + profiles_path_len ) ) ;
}
if ( strncmp ( mentry - > mnt_dir , obb_path . c_str ( ) , obb_path_len ) = = 0 ) {
obb_mounted = true ;
}
}
if ( child = = - 1 ) {
PLOG ( ERROR ) < < " Failed to fork " ;
goto next ;
// Obb mounted in zygote already, so skip it
if ( obb_mounted ) {
return true ;
}
// Ensure obb parent directory exists
std : : string obbSource ;
if ( IsFilesystemSupported ( " sdcardfs " ) ) {
obbSource = StringPrintf ( " /mnt/runtime/default/emulated/%d/Android/obb " , userId ) ;
} else {
obbSource = StringPrintf ( " /mnt/pass_through/%d/emulated/%d/Android/obb " , userId , userId ) ;
}
std : : string obbTarget ( StringPrintf ( " /storage/emulated/%d/Android/obb " , userId ) ) ;
auto status = EnsureDirExists ( obbSource , 0771 , AID_MEDIA_RW , AID_MEDIA_RW ) ;
if ( status ! = OK ) {
async_safe_format_log ( ANDROID_LOG_ERROR , " vold " , " Failed to create dir %s %s " ,
obbSource . c_str ( ) , strerror ( - status ) ) ;
return false ;
}
// It means app data isolation is not applied to this, so we can just bind the whole obb
// directory instead.
if ( pkg_name_list . empty ( ) ) {
async_safe_format_log ( ANDROID_LOG_INFO , " vold " ,
" Bind mounting whole obb directory for pid %d " , pid ) ;
status = BindMount ( obbSource , obbTarget ) ;
if ( status ! = OK ) {
async_safe_format_log ( ANDROID_LOG_ERROR , " vold " , " Failed to mount %s %s %s " ,
obbSource . c_str ( ) , obbTarget . c_str ( ) , strerror ( - status ) ) ;
return false ;
}
return true ;
}
// Bind mount each app's obb directory
for ( const auto & pkg_name : pkg_name_list ) {
std : : string appObbSource ;
if ( IsFilesystemSupported ( " sdcardfs " ) ) {
appObbSource = StringPrintf ( " /mnt/runtime/default/emulated/%d/Android/obb/%s " ,
userId , pkg_name . c_str ( ) ) ;
} else {
TEMP_FAILURE_RETRY ( waitpid ( child , nullptr , 0 ) ) ;
appObbSource = StringPrintf ( " /mnt/pass_through/%d/emulated/%d/Android/obb/%s " ,
userId , userId , pkg_name . c_str ( ) ) ;
}
std : : string appObbTarget ( StringPrintf ( " /storage/emulated/%d/Android/obb/%s " ,
userId , pkg_name . c_str ( ) ) ) ;
status = EnsureDirExists ( appObbSource , 0770 , uid , AID_MEDIA_RW ) ;
if ( status ! = OK ) {
async_safe_format_log ( ANDROID_LOG_INFO , " vold " , " Failed to ensure dir %s exists " ,
appObbSource . c_str ( ) ) ;
continue ;
}
async_safe_format_log ( ANDROID_LOG_INFO , " vold " ,
" Bind mounting app obb directory(%s) for pid %d " , pkg_name . c_str ( ) ,
pid ) ;
status = BindMount ( appObbSource , appObbTarget ) ;
if ( status ! = OK ) {
async_safe_format_log ( ANDROID_LOG_ERROR , " vold " , " Failed to mount %s %s %s " ,
obbSource . c_str ( ) , obbTarget . c_str ( ) , strerror ( - status ) ) ;
continue ;
}
}
return true ;
}
next :
close ( nsFd ) ;
close ( pidFd ) ;
int VolumeManager : : remountAppObb ( userid_t userId ) {
if ( ! GetBoolProperty ( android : : vold : : kPropFuse , false ) ) {
return 0 ;
}
LOG ( INFO ) < < " Start remounting app obb " ;
pid_t child ;
if ( ! ( child = fork ( ) ) ) {
// Child process
if ( daemon ( 0 , 0 ) = = - 1 ) {
PLOG ( FATAL ) < < " Cannot create daemon " ;
}
// TODO(149548518): Refactor the code so minimize the work after fork to prevent deadlock.
if ( scanProcProcesses ( 0 , userId , bindMountAppObbDir , nullptr ) ) {
// As some forked zygote processes may not setuid and recognized as an app yet, sleep
// 3s and try again to catch 'em all.
usleep ( 3 * 1000 * 1000 ) ; // 3s
async_safe_format_log ( ANDROID_LOG_ERROR , " vold " , " Retry remounting app obb " ) ;
scanProcProcesses ( 0 , userId , bindMountAppObbDir , nullptr ) ;
_exit ( 0 ) ;
} else {
_exit ( 1 ) ;
}
}
if ( child = = - 1 ) {
PLOG ( ERROR ) < < " Failed to fork " ;
return - 1 ;
} else if ( child = = 0 ) {
// Parent
int stat_loc ;
for ( ; ; ) {
if ( waitpid ( child , & stat_loc , 0 ) ! = - 1 | | errno ! = EINTR ) {
break ;
}
}
}
closedir ( dir ) ;
return 0 ;
}
bool VolumeManager : : updateFuseMountedProperty ( ) {
if ( mFuseMountedUsers . size ( ) = = 0 ) {
android : : base : : SetProperty ( " vold.fuse_running_users " , " " ) ;
return true ;
}
std : : stringstream stream ;
char const * sep = " " ;
for ( const auto & userId : mFuseMountedUsers ) {
stream < < sep ;
stream < < userId ;
sep = " , " ;
}
return android : : base : : SetProperty ( " vold.fuse_running_users " , stream . str ( ) ) ;
}
bool VolumeManager : : addFuseMountedUser ( userid_t userId ) {
mFuseMountedUsers . insert ( userId ) ;
return updateFuseMountedProperty ( ) ;
}
bool VolumeManager : : removeFuseMountedUser ( userid_t userId ) {
mFuseMountedUsers . erase ( userId ) ;
return updateFuseMountedProperty ( ) ;
}
int VolumeManager : : reset ( ) {
// Tear down all existing disks/volumes and start from a blank slate so
// newly connected framework hears all events.
@ -745,6 +938,8 @@ int VolumeManager::reset() {
updateVirtualDisk ( ) ;
mAddedUsers . clear ( ) ;
mStartedUsers . clear ( ) ;
mFuseMountedUsers . clear ( ) ;
updateFuseMountedProperty ( ) ;
return 0 ;
}
@ -764,6 +959,8 @@ int VolumeManager::shutdown() {
mInternalEmulatedVolumes . clear ( ) ;
mDisks . clear ( ) ;
mPendingDisks . clear ( ) ;
mFuseMountedUsers . clear ( ) ;
updateFuseMountedProperty ( ) ;
android : : vold : : sSleepOnUnmount = true ;
return 0 ;
}