@ -47,6 +47,7 @@
# include <list>
# include <list>
# include <mutex>
# include <mutex>
# include <regex>
# include <thread>
# include <thread>
# ifndef UMOUNT_NOFOLLOW
# ifndef UMOUNT_NOFOLLOW
@ -74,6 +75,11 @@ static const char* kKeyPath = "/data/misc/vold";
static const char * kProcFilesystems = " /proc/filesystems " ;
static const char * kProcFilesystems = " /proc/filesystems " ;
static const char * kAndroidDir = " /Android/ " ;
static const char * kAppDataDir = " /Android/data/ " ;
static const char * kAppMediaDir = " /Android/media/ " ;
static const char * kAppObbDir = " /Android/obb/ " ;
// Lock used to protect process-level SELinux changes from racing with each
// Lock used to protect process-level SELinux changes from racing with each
// other between multiple threads.
// other between multiple threads.
static std : : mutex kSecurityLock ;
static std : : mutex kSecurityLock ;
@ -161,44 +167,81 @@ int SetQuotaProjectId(const std::string& path, long projectId) {
return ioctl ( fd , FS_IOC_FSSETXATTR , & fsx ) ;
return ioctl ( fd , FS_IOC_FSSETXATTR , & fsx ) ;
}
}
int PrepareAppDirsFromRoot ( std : : string path , std : : string root , mode_t mode , uid_t uid , gid_t gid ) {
int PrepareDirWithProjectId ( const std : : string & path , mode_t mode , uid_t uid , gid_t gid ,
int ret = 0 ;
long projectId ) {
bool isCacheDir = false ;
int ret = fs_prepare_dir ( path . c_str ( ) , mode , uid , gid ) ;
if ( ! StartsWith ( path , root ) ) {
return - 1 ;
}
// Cache directories (eg "/storage/emulated/Android/data/com.foo/cache/") need special treatment
isCacheDir = EndsWith ( root , " /Android/data/ " ) & & EndsWith ( path , " cache/ " ) ;
std : : string to_create_from_root = path . substr ( root . length ( ) ) ;
if ( ret ! = 0 ) {
return ret ;
}
size_t pos = 0 ;
if ( ! IsFilesystemSupported ( " sdcardfs " ) ) {
while ( ( pos = to_create_from_root . find ( ' / ' ) ) ! = std : : string : : npos ) {
ret = SetQuotaProjectId ( path , projectId ) ;
auto component = to_create_from_root . substr ( 0 , pos ) ;
to_create_from_root . erase ( 0 , pos + 1 ) ;
root = root + component + " / " ;
ret = fs_prepare_dir ( root . c_str ( ) , mode , uid , gid ) ;
if ( ret ) {
break ;
}
}
return ret ;
}
static gid_t getAppDirGid ( const std : : string & appDir ) {
gid_t gid = AID_MEDIA_RW ;
if ( ! IsFilesystemSupported ( " sdcardfs " ) ) {
if ( ! IsFilesystemSupported ( " sdcardfs " ) ) {
long projectId ;
if ( appDir = = android : : vold : : kAppDataDir ) {
// All app-specific directories share the same project-ID, except
gid = AID_EXT_DATA_RW ;
// the cache directory
} else if ( appDir = = android : : vold : : kAppObbDir ) {
if ( isCacheDir & & component = = " cache " ) {
gid = AID_EXT_OBB_RW ;
// Note that this also matches paths like:
} else if ( appDir = = android : : vold : : kAppMediaDir ) {
// /Android/data/com.foo/bar/cache/
gid = AID_MEDIA_RW ;
// This is currently safe because we're never asked to create
// such directories.
projectId = uid - AID_APP_START + AID_CACHE_GID_START ;
} else {
} else {
projectId = uid - AID_APP_START + AID_EXT_GID_START ;
gid = AID_MEDIA_RW ;
}
}
ret = SetQuotaProjectId ( root , projectId ) ;
}
}
return gid ;
}
int PrepareAppDirFromRoot ( std : : string path , int appUid ) {
int ret = 0 ;
// Extract various parts of the path to setup correctly
// Sample path:
// /data/media/0/Android/data/com.foo/files
// [1]: path in which to create app-specific dir, eg. /data/media/0/Android/data/
// [2]: the part of [1] starting from /Android, eg. /Android/data/
// [3]: the package name part of the path, eg. com.foo
// [4]: the directory to create within [3], eg files
std : : regex re ( " (^/.*(/Android/(?:data|media|obb|sandbox)/))([^/]+)/([^/]+)?/? " ) ;
std : : smatch match ;
bool is_match = regex_match ( path , match , re ) ;
if ( ! is_match ) {
LOG ( ERROR ) < < " Invalid application directory: " < < path ;
return - EINVAL ;
}
}
uid_t uid = appUid ;
gid_t gid = getAppDirGid ( match . str ( 2 ) ) ;
// mode = 770, plus sticky bit on directory to inherit GID when apps
// create subdirs
mode_t mode = S_IRWXU | S_IRWXG | S_ISGID ;
long projectId = uid - AID_APP_START + AID_EXT_GID_START ;
// First, create the package-path
std : : string package_path = match . str ( 1 ) + match . str ( 3 ) ;
ret = PrepareDirWithProjectId ( package_path , mode , uid , gid , projectId ) ;
if ( ret ) {
return ret ;
return ret ;
}
// Next, create the directory within the package, if needed
if ( match . size ( ) < = 4 ) {
return OK ;
}
if ( match . str ( 4 ) = = " cache " ) {
// All dirs use the "app" project ID, except for the cache dir
projectId = uid - AID_APP_START + AID_CACHE_GID_START ;
}
return PrepareDirWithProjectId ( path . c_str ( ) , mode , uid , gid , projectId ) ;
}
}
status_t PrepareDir ( const std : : string & path , mode_t mode , uid_t uid , gid_t gid ) {
status_t PrepareDir ( const std : : string & path , mode_t mode , uid_t uid , gid_t gid ) {