Add in ext4 support for ASEC containers

Now forward locked applications will be in ASEC containers both internal
to the system and externally.

This change adds support for putting applications in ext4-based ASECs.

Change-Id: I8d6765b72dd2606e429c067b47a2dbcaa8bef37d
This commit is contained in:
Kenny Root 2012-04-03 17:23:01 -07:00
parent 8cd47b4c3b
commit 344ca10856
12 changed files with 552 additions and 90 deletions

View file

@ -10,6 +10,7 @@ common_src_files := \
DirectVolume.cpp \
logwrapper.c \
Process.cpp \
Ext4.cpp \
Fat.cpp \
Loop.cpp \
Devmapper.cpp \

1
Asec.h
View file

@ -33,6 +33,7 @@ struct asec_superblock {
unsigned char c_chain;
#define ASEC_SB_C_OPTS_NONE 0
#define ASEC_SB_C_OPTS_EXT4 1
unsigned char c_opts;
#define ASEC_SB_C_MODE_NONE 0

View file

@ -261,6 +261,44 @@ CommandListener::AsecCmd::AsecCmd() :
VoldCommand("asec") {
}
void CommandListener::AsecCmd::listAsecsInDirectory(SocketClient *cli, const char *directory) {
DIR *d = opendir(directory);
if (!d) {
cli->sendMsg(ResponseCode::OperationFailed, "Failed to open asec dir", true);
return;
}
size_t dirent_len = offsetof(struct dirent, d_name) +
pathconf(directory, _PC_NAME_MAX) + 1;
struct dirent *dent = (struct dirent *) malloc(dirent_len);
if (dent == NULL) {
cli->sendMsg(ResponseCode::OperationFailed, "Failed to allocate memory", true);
return;
}
struct dirent *result;
while (!readdir_r(d, dent, &result) && result != NULL) {
if (dent->d_name[0] == '.')
continue;
if (dent->d_type != DT_REG)
continue;
size_t name_len = strlen(dent->d_name);
if (name_len > 5 && name_len < 260 &&
!strcmp(&dent->d_name[name_len - 5], ".asec")) {
char id[255];
memset(id, 0, sizeof(id));
strlcpy(id, dent->d_name, name_len - 5);
cli->sendMsg(ResponseCode::AsecListResult, id, false);
}
}
closedir(d);
free(dent);
}
int CommandListener::AsecCmd::runCommand(SocketClient *cli,
int argc, char **argv) {
if (argc < 2) {
@ -273,35 +311,21 @@ int CommandListener::AsecCmd::runCommand(SocketClient *cli,
if (!strcmp(argv[1], "list")) {
dumpArgs(argc, argv, -1);
DIR *d = opendir(Volume::SEC_ASECDIR);
if (!d) {
cli->sendMsg(ResponseCode::OperationFailed, "Failed to open asec dir", true);
return 0;
}
struct dirent *dent;
while ((dent = readdir(d))) {
if (dent->d_name[0] == '.')
continue;
if (!strcmp(&dent->d_name[strlen(dent->d_name)-5], ".asec")) {
char id[255];
memset(id, 0, sizeof(id));
strncpy(id, dent->d_name, strlen(dent->d_name) -5);
cli->sendMsg(ResponseCode::AsecListResult, id, false);
}
}
closedir(d);
listAsecsInDirectory(cli, Volume::SEC_ASECDIR_EXT);
listAsecsInDirectory(cli, Volume::SEC_ASECDIR_INT);
} else if (!strcmp(argv[1], "create")) {
dumpArgs(argc, argv, 5);
if (argc != 7) {
if (argc != 8) {
cli->sendMsg(ResponseCode::CommandSyntaxError,
"Usage: asec create <container-id> <size_mb> <fstype> <key> <ownerUid>", false);
"Usage: asec create <container-id> <size_mb> <fstype> <key> <ownerUid> "
"<isExternal>", false);
return 0;
}
unsigned int numSectors = (atoi(argv[3]) * (1024 * 1024)) / 512;
rc = vm->createAsec(argv[2], numSectors, argv[4], argv[5], atoi(argv[6]));
const bool isExternal = (atoi(argv[7]) == 1);
rc = vm->createAsec(argv[2], numSectors, argv[4], argv[5], atoi(argv[6]), isExternal);
} else if (!strcmp(argv[1], "finalize")) {
dumpArgs(argc, argv, -1);
if (argc != 3) {
@ -309,6 +333,21 @@ int CommandListener::AsecCmd::runCommand(SocketClient *cli,
return 0;
}
rc = vm->finalizeAsec(argv[2]);
} else if (!strcmp(argv[1], "fixperms")) {
dumpArgs(argc, argv, -1);
if (argc != 5) {
cli->sendMsg(ResponseCode::CommandSyntaxError, "Usage: asec fixperms <container-id> <gid> <filename>", false);
return 0;
}
char *endptr;
gid_t gid = (gid_t) strtoul(argv[3], &endptr, 10);
if (*endptr != '\0') {
cli->sendMsg(ResponseCode::CommandSyntaxError, "Usage: asec fixperms <container-id> <gid> <filename>", false);
return 0;
}
rc = vm->fixupAsecPermissions(argv[2], gid, argv[4]);
} else if (!strcmp(argv[1], "destroy")) {
dumpArgs(argc, argv, -1);
if (argc < 3) {

View file

@ -47,6 +47,8 @@ private:
AsecCmd();
virtual ~AsecCmd() {}
int runCommand(SocketClient *c, int argc, char ** argv);
private:
void listAsecsInDirectory(SocketClient *c, const char *directory);
};
class ObbCmd : public VoldCommand {

90
Ext4.cpp Normal file
View file

@ -0,0 +1,90 @@
/*
* Copyright (C) 2012 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.
*/
#include <stdio.h>
#include <stdlib.h>
#include <fcntl.h>
#include <unistd.h>
#include <errno.h>
#include <string.h>
#include <dirent.h>
#include <errno.h>
#include <fcntl.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <sys/mman.h>
#include <sys/mount.h>
#include <linux/kdev_t.h>
#include <linux/fs.h>
#define LOG_TAG "Vold"
#include <cutils/log.h>
#include <cutils/properties.h>
#include "Ext4.h"
#define MKEXT4FS_PATH "/system/bin/make_ext4fs";
extern "C" int logwrap(int argc, const char **argv, int background);
int Ext4::doMount(const char *fsPath, const char *mountPoint, bool ro, bool remount,
bool executable) {
int rc;
unsigned long flags;
flags = MS_NOATIME | MS_NODEV | MS_NOSUID | MS_DIRSYNC;
flags |= (executable ? 0 : MS_NOEXEC);
flags |= (ro ? MS_RDONLY : 0);
flags |= (remount ? MS_REMOUNT : 0);
rc = mount(fsPath, mountPoint, "ext4", flags, NULL);
if (rc && errno == EROFS) {
SLOGE("%s appears to be a read only filesystem - retrying mount RO", fsPath);
flags |= MS_RDONLY;
rc = mount(fsPath, mountPoint, "ext4", flags, NULL);
}
return rc;
}
int Ext4::format(const char *fsPath) {
int fd;
const char *args[4];
int rc;
args[0] = MKEXT4FS_PATH;
args[1] = "-J";
args[2] = fsPath;
args[3] = NULL;
rc = logwrap(3, args, 1);
if (rc == 0) {
SLOGI("Filesystem (ext4) formatted OK");
return 0;
} else {
SLOGE("Format (ext4) failed (unknown exit code %d)", rc);
errno = EIO;
return -1;
}
return 0;
}

29
Ext4.h Normal file
View file

@ -0,0 +1,29 @@
/*
* Copyright (C) 2012 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 _EXT4_H
#define _EXT4_H
#include <unistd.h>
class Ext4 {
public:
static int doMount(const char *fsPath, const char *mountPoint, bool ro, bool remount,
bool executable);
static int format(const char *fsPath);
};
#endif

View file

@ -21,6 +21,7 @@
#include <errno.h>
#include <string.h>
#include <sys/mount.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/ioctl.h>
@ -33,6 +34,7 @@
#include <sysutils/SocketClient.h>
#include "Loop.h"
#include "Asec.h"
int Loop::dumpState(SocketClient *c) {
int i;
@ -246,3 +248,45 @@ int Loop::createImageFile(const char *file, unsigned int numSectors) {
close(fd);
return 0;
}
int Loop::lookupInfo(const char *loopDevice, struct asec_superblock *sb, unsigned int *nr_sec) {
int fd;
struct asec_superblock buffer;
if ((fd = open(loopDevice, O_RDONLY)) < 0) {
SLOGE("Failed to open loopdevice (%s)", strerror(errno));
destroyByDevice(loopDevice);
return -1;
}
if (ioctl(fd, BLKGETSIZE, nr_sec)) {
SLOGE("Failed to get loop size (%s)", strerror(errno));
destroyByDevice(loopDevice);
close(fd);
return -1;
}
/*
* Try to read superblock.
*/
memset(&buffer, 0, sizeof(struct asec_superblock));
if (lseek(fd, ((*nr_sec - 1) * 512), SEEK_SET) < 0) {
SLOGE("lseek failed (%s)", strerror(errno));
close(fd);
destroyByDevice(loopDevice);
return -1;
}
if (read(fd, &buffer, sizeof(struct asec_superblock)) != sizeof(struct asec_superblock)) {
SLOGE("superblock read failed (%s)", strerror(errno));
close(fd);
destroyByDevice(loopDevice);
return -1;
}
close(fd);
/*
* Superblock successfully read. Copy to caller's struct.
*/
memcpy(sb, &buffer, sizeof(struct asec_superblock));
return 0;
}

1
Loop.h
View file

@ -27,6 +27,7 @@ public:
static const int LOOP_MAX = 4096;
public:
static int lookupActive(const char *id, char *buffer, size_t len);
static int lookupInfo(const char *loopDevice, struct asec_superblock *sb, unsigned int *nr_sec);
static int create(const char *id, const char *loopFile, char *loopDeviceBuffer, size_t len);
static int destroyByDevice(const char *loopDevice);
static int destroyByFile(const char *loopFile);

View file

@ -69,10 +69,14 @@ const char *Volume::SEC_STGDIR = "/mnt/secure/staging";
const char *Volume::SEC_STG_SECIMGDIR = "/mnt/secure/staging/.android_secure";
/*
* Path to where *only* root can access asec imagefiles
* Path to external storage where *only* root can access ASEC image files
*/
const char *Volume::SEC_ASECDIR = "/mnt/secure/asec";
const char *Volume::SEC_ASECDIR_EXT = "/mnt/secure/asec";
/*
* Path to internal storage where *only* root can access ASEC image files
*/
const char *Volume::SEC_ASECDIR_INT = "/data/app-asec";
/*
* Path to where secure containers are mounted
*/
@ -504,9 +508,9 @@ int Volume::createBindMounts() {
* Bind mount /mnt/secure/staging/android_secure -> /mnt/secure/asec so we'll
* have a root only accessable mountpoint for it.
*/
if (mount(SEC_STG_SECIMGDIR, SEC_ASECDIR, "", MS_BIND, NULL)) {
if (mount(SEC_STG_SECIMGDIR, SEC_ASECDIR_EXT, "", MS_BIND, NULL)) {
SLOGE("Failed to bind mount points %s -> %s (%s)",
SEC_STG_SECIMGDIR, SEC_ASECDIR, strerror(errno));
SEC_STG_SECIMGDIR, SEC_ASECDIR_EXT, strerror(errno));
return -1;
}
@ -630,8 +634,8 @@ int Volume::unmountVol(bool force, bool revert) {
* the previously obscured directory.
*/
if (doUnmount(Volume::SEC_ASECDIR, force)) {
SLOGE("Failed to remove bindmount on %s (%s)", SEC_ASECDIR, strerror(errno));
if (doUnmount(Volume::SEC_ASECDIR_EXT, force)) {
SLOGE("Failed to remove bindmount on %s (%s)", SEC_ASECDIR_EXT, strerror(errno));
goto fail_remount_tmpfs;
}
@ -663,7 +667,7 @@ int Volume::unmountVol(bool force, bool revert) {
* Failure handling - try to restore everything back the way it was
*/
fail_recreate_bindmount:
if (mount(SEC_STG_SECIMGDIR, SEC_ASECDIR, "", MS_BIND, NULL)) {
if (mount(SEC_STG_SECIMGDIR, SEC_ASECDIR_EXT, "", MS_BIND, NULL)) {
SLOGE("Failed to restore bindmount after failure! - Storage will appear offline!");
goto out_nomedia;
}

View file

@ -41,7 +41,8 @@ public:
static const char *SECDIR;
static const char *SEC_STGDIR;
static const char *SEC_STG_SECIMGDIR;
static const char *SEC_ASECDIR;
static const char *SEC_ASECDIR_EXT;
static const char *SEC_ASECDIR_INT;
static const char *ASECDIR;
static const char *LOOPDIR;

View file

@ -19,6 +19,8 @@
#include <string.h>
#include <errno.h>
#include <fcntl.h>
#include <fts.h>
#include <unistd.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <sys/mount.h>
@ -33,10 +35,13 @@
#include <sysutils/NetlinkEvent.h>
#include <private/android_filesystem_config.h>
#include "VolumeManager.h"
#include "DirectVolume.h"
#include "ResponseCode.h"
#include "Loop.h"
#include "Ext4.h"
#include "Fat.h"
#include "Devmapper.h"
#include "Process.h"
@ -197,7 +202,11 @@ int VolumeManager::getObbMountPath(const char *sourceFile, char *mountPath, int
int VolumeManager::getAsecMountPath(const char *id, char *buffer, int maxlen) {
char asecFileName[255];
snprintf(asecFileName, sizeof(asecFileName), "%s/%s.asec", Volume::SEC_ASECDIR, id);
if (findAsec(id, asecFileName, sizeof(asecFileName))) {
SLOGE("Couldn't find ASEC %s", id);
return -1;
}
memset(buffer, 0, maxlen);
if (access(asecFileName, F_OK)) {
@ -211,7 +220,11 @@ int VolumeManager::getAsecMountPath(const char *id, char *buffer, int maxlen) {
int VolumeManager::getAsecFilesystemPath(const char *id, char *buffer, int maxlen) {
char asecFileName[255];
snprintf(asecFileName, sizeof(asecFileName), "%s/%s.asec", Volume::SEC_ASECDIR, id);
if (findAsec(id, asecFileName, sizeof(asecFileName))) {
SLOGE("Couldn't find ASEC %s", id);
return -1;
}
memset(buffer, 0, maxlen);
if (access(asecFileName, F_OK)) {
@ -223,11 +236,24 @@ int VolumeManager::getAsecFilesystemPath(const char *id, char *buffer, int maxle
return 0;
}
int VolumeManager::createAsec(const char *id, unsigned int numSectors,
const char *fstype, const char *key, int ownerUid) {
int VolumeManager::createAsec(const char *id, unsigned int numSectors, const char *fstype,
const char *key, const int ownerUid, bool isExternal) {
struct asec_superblock sb;
memset(&sb, 0, sizeof(sb));
const bool wantFilesystem = strcmp(fstype, "none");
bool usingExt4 = false;
if (wantFilesystem) {
usingExt4 = !strcmp(fstype, "ext4");
if (usingExt4) {
sb.c_opts |= ASEC_SB_C_OPTS_EXT4;
} else if (strcmp(fstype, "fat")) {
SLOGE("Invalid filesystem type %s", fstype);
errno = EINVAL;
return -1;
}
}
sb.magic = ASEC_SB_MAGIC;
sb.ver = ASEC_SB_VER;
@ -244,11 +270,21 @@ int VolumeManager::createAsec(const char *id, unsigned int numSectors,
}
char asecFileName[255];
snprintf(asecFileName, sizeof(asecFileName), "%s/%s.asec", Volume::SEC_ASECDIR, id);
if (!findAsec(id, asecFileName, sizeof(asecFileName))) {
SLOGE("ASEC file '%s' currently exists - destroy it first! (%s)",
asecFileName, strerror(errno));
errno = EADDRINUSE;
return -1;
}
const char *asecDir = isExternal ? Volume::SEC_ASECDIR_EXT : Volume::SEC_ASECDIR_INT;
snprintf(asecFileName, sizeof(asecFileName), "%s/%s.asec", asecDir, id);
if (!access(asecFileName, F_OK)) {
SLOGE("ASEC file '%s' currently exists - destroy it first! (%s)",
asecFileName, strerror(errno));
asecFileName, strerror(errno));
errno = EADDRINUSE;
return -1;
}
@ -340,13 +376,16 @@ int VolumeManager::createAsec(const char *id, unsigned int numSectors,
}
close(sbfd);
if (strcmp(fstype, "none")) {
if (strcmp(fstype, "fat")) {
SLOGW("Unknown fstype '%s' specified for container", fstype);
if (wantFilesystem) {
int formatStatus;
if (usingExt4) {
formatStatus = Ext4::format(dmDevice);
} else {
formatStatus = Fat::format(dmDevice, numImgSectors);
}
if (Fat::format(dmDevice, numImgSectors)) {
SLOGE("ASEC FAT format failed (%s)", strerror(errno));
if (formatStatus < 0) {
SLOGE("ASEC fs format failed (%s)", strerror(errno));
if (cleanupDm) {
Devmapper::destroy(idHash);
}
@ -354,10 +393,11 @@ int VolumeManager::createAsec(const char *id, unsigned int numSectors,
unlink(asecFileName);
return -1;
}
char mountPoint[255];
snprintf(mountPoint, sizeof(mountPoint), "%s/%s", Volume::ASECDIR, id);
if (mkdir(mountPoint, 0777)) {
if (mkdir(mountPoint, 0000)) {
if (errno != EEXIST) {
SLOGE("Mountpoint creation failed (%s)", strerror(errno));
if (cleanupDm) {
@ -369,8 +409,15 @@ int VolumeManager::createAsec(const char *id, unsigned int numSectors,
}
}
if (Fat::doMount(dmDevice, mountPoint, false, false, false, ownerUid,
0, 0000, false)) {
int mountStatus;
if (usingExt4) {
mountStatus = Ext4::doMount(dmDevice, mountPoint, false, false, false);
} else {
mountStatus = Fat::doMount(dmDevice, mountPoint, false, false, false, ownerUid, 0, 0000,
false);
}
if (mountStatus) {
SLOGE("ASEC FAT mount failed (%s)", strerror(errno));
if (cleanupDm) {
Devmapper::destroy(idHash);
@ -379,6 +426,17 @@ int VolumeManager::createAsec(const char *id, unsigned int numSectors,
unlink(asecFileName);
return -1;
}
if (usingExt4) {
int dirfd = open(mountPoint, O_DIRECTORY);
if (dirfd >= 0) {
if (fchown(dirfd, ownerUid, AID_SYSTEM)
|| fchmod(dirfd, S_IRUSR | S_IWUSR | S_IXUSR | S_ISGID | S_IRGRP | S_IXGRP)) {
SLOGI("Cannot chown/chmod new ASEC mount point %s", mountPoint);
}
close(dirfd);
}
}
} else {
SLOGI("Created raw secure container %s (no filesystem)", id);
}
@ -392,7 +450,10 @@ int VolumeManager::finalizeAsec(const char *id) {
char loopDevice[255];
char mountPoint[255];
snprintf(asecFileName, sizeof(asecFileName), "%s/%s.asec", Volume::SEC_ASECDIR, id);
if (findAsec(id, asecFileName, sizeof(asecFileName))) {
SLOGE("Couldn't find ASEC %s", id);
return -1;
}
char idHash[33];
if (!asecHash(id, idHash, sizeof(idHash))) {
@ -405,9 +466,23 @@ int VolumeManager::finalizeAsec(const char *id) {
return -1;
}
unsigned int nr_sec = 0;
struct asec_superblock sb;
if (Loop::lookupInfo(loopDevice, &sb, &nr_sec)) {
return -1;
}
snprintf(mountPoint, sizeof(mountPoint), "%s/%s", Volume::ASECDIR, id);
// XXX:
if (Fat::doMount(loopDevice, mountPoint, true, true, true, 0, 0, 0227, false)) {
int result = 0;
if (sb.c_opts & ASEC_SB_C_OPTS_EXT4) {
result = Ext4::doMount(loopDevice, mountPoint, true, true, true);
} else {
result = Fat::doMount(loopDevice, mountPoint, true, true, true, 0, 0, 0227, false);
}
if (result) {
SLOGE("ASEC finalize mount failed (%s)", strerror(errno));
return -1;
}
@ -418,13 +493,131 @@ int VolumeManager::finalizeAsec(const char *id) {
return 0;
}
int VolumeManager::fixupAsecPermissions(const char *id, gid_t gid, const char* filename) {
char asecFileName[255];
char loopDevice[255];
char mountPoint[255];
if (gid < AID_APP) {
SLOGE("Group ID is not in application range");
return -1;
}
if (findAsec(id, asecFileName, sizeof(asecFileName))) {
SLOGE("Couldn't find ASEC %s", id);
return -1;
}
char idHash[33];
if (!asecHash(id, idHash, sizeof(idHash))) {
SLOGE("Hash of '%s' failed (%s)", id, strerror(errno));
return -1;
}
if (Loop::lookupActive(idHash, loopDevice, sizeof(loopDevice))) {
SLOGE("Unable fix permissions during lookup on %s (%s)", id, strerror(errno));
return -1;
}
unsigned int nr_sec = 0;
struct asec_superblock sb;
if (Loop::lookupInfo(loopDevice, &sb, &nr_sec)) {
return -1;
}
snprintf(mountPoint, sizeof(mountPoint), "%s/%s", Volume::ASECDIR, id);
int result = 0;
if ((sb.c_opts & ASEC_SB_C_OPTS_EXT4) == 0) {
return 0;
}
int ret = Ext4::doMount(loopDevice, mountPoint,
false /* read-only */,
true /* remount */,
false /* executable */);
if (ret) {
SLOGE("Unable remount to fix permissions for %s (%s)", id, strerror(errno));
return -1;
}
char *paths[] = { mountPoint, NULL };
FTS *fts = fts_open(paths, FTS_PHYSICAL | FTS_NOCHDIR | FTS_XDEV, NULL);
if (fts) {
// Traverse the entire hierarchy and chown to system UID.
for (FTSENT *ftsent = fts_read(fts); ftsent != NULL; ftsent = fts_read(fts)) {
// We don't care about the lost+found directory.
if (!strcmp(ftsent->fts_name, "lost+found")) {
continue;
}
/*
* There can only be one file marked as private right now.
* This should be more robust, but it satisfies the requirements
* we have for right now.
*/
const bool privateFile = !strcmp(ftsent->fts_name, filename);
int fd = open(ftsent->fts_accpath, O_NOFOLLOW);
if (fd < 0) {
SLOGE("Couldn't open file %s: %s", ftsent->fts_accpath, strerror(errno));
result = -1;
continue;
}
result |= fchown(fd, AID_SYSTEM, privateFile? gid : AID_SYSTEM);
if (ftsent->fts_info & FTS_D) {
result |= fchmod(fd, 0711);
} else {
result |= fchmod(fd, privateFile ? 0640 : 0644);
}
close(fd);
}
fts_close(fts);
// Finally make the directory readable by everyone.
int dirfd = open(mountPoint, O_DIRECTORY);
if (dirfd < 0 || fchmod(dirfd, 0755)) {
SLOGE("Couldn't change owner of existing directory %s: %s", mountPoint, strerror(errno));
result |= -1;
}
close(dirfd);
} else {
result |= -1;
}
result |= Ext4::doMount(loopDevice, mountPoint,
true /* read-only */,
true /* remount */,
true /* execute */);
if (result) {
SLOGE("ASEC fix permissions failed (%s)", strerror(errno));
return -1;
}
if (mDebug) {
SLOGD("ASEC %s permissions fixed", id);
}
return 0;
}
int VolumeManager::renameAsec(const char *id1, const char *id2) {
char *asecFilename1;
char asecFilename1[255];
char *asecFilename2;
char mountPoint[255];
asprintf(&asecFilename1, "%s/%s.asec", Volume::SEC_ASECDIR, id1);
asprintf(&asecFilename2, "%s/%s.asec", Volume::SEC_ASECDIR, id2);
const char *dir;
if (findAsec(id1, asecFilename1, sizeof(asecFilename1), &dir)) {
SLOGE("Couldn't find ASEC %s", id1);
return -1;
}
asprintf(&asecFilename2, "%s/%s.asec", dir, id2);
snprintf(mountPoint, sizeof(mountPoint), "%s/%s", Volume::ASECDIR, id1);
if (isMountpointMounted(mountPoint)) {
@ -451,12 +644,10 @@ int VolumeManager::renameAsec(const char *id1, const char *id2) {
goto out_err;
}
free(asecFilename1);
free(asecFilename2);
return 0;
out_err:
free(asecFilename1);
free(asecFilename2);
return -1;
}
@ -467,7 +658,11 @@ int VolumeManager::unmountAsec(const char *id, bool force) {
char asecFileName[255];
char mountPoint[255];
snprintf(asecFileName, sizeof(asecFileName), "%s/%s.asec", Volume::SEC_ASECDIR, id);
if (findAsec(id, asecFileName, sizeof(asecFileName))) {
SLOGE("Couldn't find ASEC %s", id);
return -1;
}
snprintf(mountPoint, sizeof(mountPoint), "%s/%s", Volume::ASECDIR, id);
char idHash[33];
@ -579,7 +774,11 @@ int VolumeManager::destroyAsec(const char *id, bool force) {
char asecFileName[255];
char mountPoint[255];
snprintf(asecFileName, sizeof(asecFileName), "%s/%s.asec", Volume::SEC_ASECDIR, id);
if (findAsec(id, asecFileName, sizeof(asecFileName))) {
SLOGE("Couldn't find ASEC %s", id);
return -1;
}
snprintf(mountPoint, sizeof(mountPoint), "%s/%s", Volume::ASECDIR, id);
if (isMountpointMounted(mountPoint)) {
@ -603,11 +802,70 @@ int VolumeManager::destroyAsec(const char *id, bool force) {
return 0;
}
bool VolumeManager::isAsecInDirectory(const char *dir, const char *asecName) const {
int dirfd = open(dir, O_DIRECTORY);
if (dirfd < 0) {
SLOGE("Couldn't open internal ASEC dir (%s)", strerror(errno));
return -1;
}
bool ret = false;
if (!faccessat(dirfd, asecName, F_OK, AT_SYMLINK_NOFOLLOW)) {
ret = true;
}
close(dirfd);
return ret;
}
int VolumeManager::findAsec(const char *id, char *asecPath, size_t asecPathLen,
const char **directory) const {
int dirfd, fd;
const int idLen = strlen(id);
char *asecName;
if (asprintf(&asecName, "%s.asec", id) < 0) {
SLOGE("Couldn't allocate string to write ASEC name");
return -1;
}
const char *dir;
if (isAsecInDirectory(Volume::SEC_ASECDIR_INT, asecName)) {
dir = Volume::SEC_ASECDIR_INT;
} else if (isAsecInDirectory(Volume::SEC_ASECDIR_EXT, asecName)) {
dir = Volume::SEC_ASECDIR_EXT;
} else {
free(asecName);
return -1;
}
if (directory != NULL) {
*directory = dir;
}
if (asecPath != NULL) {
int written = snprintf(asecPath, asecPathLen, "%s/%s", dir, asecName);
if (written < 0 || static_cast<size_t>(written) >= asecPathLen) {
free(asecName);
return -1;
}
}
free(asecName);
return 0;
}
int VolumeManager::mountAsec(const char *id, const char *key, int ownerUid) {
char asecFileName[255];
char mountPoint[255];
snprintf(asecFileName, sizeof(asecFileName), "%s/%s.asec", Volume::SEC_ASECDIR, id);
if (findAsec(id, asecFileName, sizeof(asecFileName))) {
SLOGE("Couldn't find ASEC %s", id);
return -1;
}
snprintf(mountPoint, sizeof(mountPoint), "%s/%s", Volume::ASECDIR, id);
if (isMountpointMounted(mountPoint)) {
@ -641,39 +899,11 @@ int VolumeManager::mountAsec(const char *id, const char *key, int ownerUid) {
bool cleanupDm = false;
int fd;
unsigned int nr_sec = 0;
if ((fd = open(loopDevice, O_RDWR)) < 0) {
SLOGE("Failed to open loopdevice (%s)", strerror(errno));
Loop::destroyByDevice(loopDevice);
return -1;
}
if (ioctl(fd, BLKGETSIZE, &nr_sec)) {
SLOGE("Failed to get loop size (%s)", strerror(errno));
Loop::destroyByDevice(loopDevice);
close(fd);
return -1;
}
/*
* Validate superblock
*/
struct asec_superblock sb;
memset(&sb, 0, sizeof(sb));
if (lseek(fd, ((nr_sec-1) * 512), SEEK_SET) < 0) {
SLOGE("lseek failed (%s)", strerror(errno));
close(fd);
Loop::destroyByDevice(loopDevice);
return -1;
}
if (read(fd, &sb, sizeof(sb)) != sizeof(sb)) {
SLOGE("superblock read failed (%s)", strerror(errno));
close(fd);
Loop::destroyByDevice(loopDevice);
return -1;
}
close(fd);
if (Loop::lookupInfo(loopDevice, &sb, &nr_sec)) {
return -1;
}
if (mDebug) {
SLOGD("Container sb magic/ver (%.8x/%.2x)", sb.magic, sb.ver);
@ -707,7 +937,7 @@ int VolumeManager::mountAsec(const char *id, const char *key, int ownerUid) {
strcpy(dmDevice, loopDevice);
}
if (mkdir(mountPoint, 0777)) {
if (mkdir(mountPoint, 0000)) {
if (errno != EEXIST) {
SLOGE("Mountpoint creation failed (%s)", strerror(errno));
if (cleanupDm) {
@ -718,9 +948,14 @@ int VolumeManager::mountAsec(const char *id, const char *key, int ownerUid) {
}
}
if (Fat::doMount(dmDevice, mountPoint, true, false, true, ownerUid, 0,
0222, false)) {
// 0227, false)) {
int result;
if (sb.c_opts & ASEC_SB_C_OPTS_EXT4) {
result = Ext4::doMount(dmDevice, mountPoint, true, false, true);
} else {
result = Fat::doMount(dmDevice, mountPoint, true, false, true, ownerUid, 0, 0222, false);
}
if (result) {
SLOGE("ASEC mount failed (%s)", strerror(errno));
if (cleanupDm) {
Devmapper::destroy(idHash);

View file

@ -87,9 +87,23 @@ public:
void disableVolumeManager(void) { mVolManagerDisabled = 1; }
/* ASEC */
int findAsec(const char *id, char *asecPath = NULL, size_t asecPathLen = 0,
const char **directory = NULL) const;
int createAsec(const char *id, unsigned numSectors, const char *fstype,
const char *key, int ownerUid);
const char *key, const int ownerUid, bool isExternal);
int finalizeAsec(const char *id);
/**
* Fixes ASEC permissions on a filesystem that has owners and permissions.
* This currently means EXT4-based ASEC containers.
*
* There is a single file that can be marked as "private" and will not have
* world-readable permission. The group for that file will be set to the gid
* supplied.
*
* Returns 0 on success.
*/
int fixupAsecPermissions(const char *id, gid_t gid, const char* privateFilename);
int destroyAsec(const char *id, bool force);
int mountAsec(const char *id, const char *key, int ownerUid);
int unmountAsec(const char *id, bool force);
@ -127,6 +141,7 @@ private:
VolumeManager();
void readInitialState();
bool isMountpointMounted(const char *mp);
bool isAsecInDirectory(const char *dir, const char *asec) const;
};
extern "C" {