Merge "Symlink application lib directory when on SD card" into gingerbread
This commit is contained in:
@@ -936,3 +936,157 @@ int movefiles()
|
||||
done:
|
||||
return 0;
|
||||
}
|
||||
|
||||
int linklib(const char* dataDir, const char* asecLibDir)
|
||||
{
|
||||
char libdir[PKG_PATH_MAX];
|
||||
struct stat s, libStat;
|
||||
int rc = 0;
|
||||
|
||||
const size_t libdirLen = strlen(dataDir) + strlen(PKG_LIB_POSTFIX);
|
||||
if (libdirLen >= PKG_PATH_MAX) {
|
||||
LOGE("library dir len too large");
|
||||
rc = -1;
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (snprintf(libdir, sizeof(libdir), "%s%s", dataDir, PKG_LIB_POSTFIX) != (ssize_t)libdirLen) {
|
||||
LOGE("library dir not written successfully: %s\n", strerror(errno));
|
||||
rc = -1;
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (stat(dataDir, &s) < 0) return -1;
|
||||
|
||||
if (chown(dataDir, 0, 0) < 0) {
|
||||
LOGE("failed to chown '%s': %s\n", dataDir, strerror(errno));
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (chmod(dataDir, 0700) < 0) {
|
||||
LOGE("failed to chmod '%s': %s\n", dataDir, strerror(errno));
|
||||
rc = -1;
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (lstat(libdir, &libStat) < 0) {
|
||||
LOGE("couldn't stat lib dir: %s\n", strerror(errno));
|
||||
rc = -1;
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (S_ISDIR(libStat.st_mode)) {
|
||||
if (delete_dir_contents(libdir, 1, 0) < 0) {
|
||||
rc = -1;
|
||||
goto out;
|
||||
}
|
||||
} else if (S_ISLNK(libStat.st_mode)) {
|
||||
if (unlink(libdir) < 0) {
|
||||
rc = -1;
|
||||
goto out;
|
||||
}
|
||||
}
|
||||
|
||||
if (symlink(asecLibDir, libdir) < 0) {
|
||||
LOGE("couldn't symlink directory '%s' -> '%s': %s\n", libdir, asecLibDir, strerror(errno));
|
||||
rc = -errno;
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (lchown(libdir, AID_SYSTEM, AID_SYSTEM) < 0) {
|
||||
LOGE("cannot chown dir '%s': %s\n", libdir, strerror(errno));
|
||||
unlink(libdir);
|
||||
rc = -errno;
|
||||
goto out;
|
||||
}
|
||||
|
||||
out:
|
||||
if (chmod(dataDir, s.st_mode) < 0) {
|
||||
LOGE("failed to chmod '%s': %s\n", dataDir, strerror(errno));
|
||||
return -errno;
|
||||
}
|
||||
|
||||
if (chown(dataDir, s.st_uid, s.st_gid) < 0) {
|
||||
LOGE("failed to chown '%s' : %s\n", dataDir, strerror(errno));
|
||||
return -errno;
|
||||
}
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
int unlinklib(const char* dataDir)
|
||||
{
|
||||
char libdir[PKG_PATH_MAX];
|
||||
struct stat s, libStat;
|
||||
int rc = 0;
|
||||
|
||||
const size_t libdirLen = strlen(dataDir) + strlen(PKG_LIB_POSTFIX);
|
||||
if (libdirLen >= PKG_PATH_MAX) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (snprintf(libdir, sizeof(libdir), "%s%s", dataDir, PKG_LIB_POSTFIX) != (ssize_t)libdirLen) {
|
||||
LOGE("library dir not written successfully: %s\n", strerror(errno));
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (stat(dataDir, &s) < 0) {
|
||||
LOGE("couldn't state data dir");
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (chown(dataDir, 0, 0) < 0) {
|
||||
LOGE("failed to chown '%s': %s\n", dataDir, strerror(errno));
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (chmod(dataDir, 0700) < 0) {
|
||||
LOGE("failed to chmod '%s': %s\n", dataDir, strerror(errno));
|
||||
rc = -1;
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (lstat(libdir, &libStat) < 0) {
|
||||
LOGE("couldn't stat lib dir: %s\n", strerror(errno));
|
||||
rc = -1;
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (S_ISDIR(libStat.st_mode)) {
|
||||
if (delete_dir_contents(libdir, 1, 0) < 0) {
|
||||
rc = -1;
|
||||
goto out;
|
||||
}
|
||||
} else if (S_ISLNK(libStat.st_mode)) {
|
||||
if (unlink(libdir) < 0) {
|
||||
rc = -1;
|
||||
goto out;
|
||||
}
|
||||
}
|
||||
|
||||
if (mkdir(libdir, 0755) < 0) {
|
||||
LOGE("cannot create dir '%s': %s\n", libdir, strerror(errno));
|
||||
rc = -errno;
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (chown(libdir, AID_SYSTEM, AID_SYSTEM) < 0) {
|
||||
LOGE("cannot chown dir '%s': %s\n", libdir, strerror(errno));
|
||||
unlink(libdir);
|
||||
rc = -errno;
|
||||
goto out;
|
||||
}
|
||||
|
||||
out:
|
||||
if (chmod(dataDir, s.st_mode) < 0) {
|
||||
LOGE("failed to chmod '%s': %s\n", dataDir, strerror(errno));
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (chown(dataDir, s.st_uid, s.st_gid) < 0) {
|
||||
LOGE("failed to chown '%s' : %s\n", dataDir, strerror(errno));
|
||||
return -1;
|
||||
}
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
@@ -101,6 +101,16 @@ static int do_movefiles(char **arg, char reply[REPLY_MAX])
|
||||
return movefiles();
|
||||
}
|
||||
|
||||
static int do_linklib(char **arg, char reply[REPLY_MAX])
|
||||
{
|
||||
return linklib(arg[0], arg[1]);
|
||||
}
|
||||
|
||||
static int do_unlinklib(char **arg, char reply[REPLY_MAX])
|
||||
{
|
||||
return unlinklib(arg[0]);
|
||||
}
|
||||
|
||||
struct cmdinfo {
|
||||
const char *name;
|
||||
unsigned numargs;
|
||||
@@ -121,6 +131,8 @@ struct cmdinfo cmds[] = {
|
||||
{ "getsize", 4, do_get_size },
|
||||
{ "rmuserdata", 2, do_rm_user_data },
|
||||
{ "movefiles", 0, do_movefiles },
|
||||
{ "linklib", 2, do_linklib },
|
||||
{ "unlinklib", 1, do_unlinklib },
|
||||
};
|
||||
|
||||
static int readx(int s, void *_buf, int count)
|
||||
|
||||
@@ -111,3 +111,5 @@ int get_size(const char *pkgname, const char *apkpath, const char *fwdlock_apkpa
|
||||
int free_cache(int64_t free_size);
|
||||
int dexopt(const char *apk_path, uid_t uid, int is_public);
|
||||
int movefiles();
|
||||
int linklib(const char* target, const char* source);
|
||||
int unlinklib(const char* libPath);
|
||||
|
||||
@@ -45,6 +45,7 @@ import android.util.DisplayMetrics;
|
||||
import android.util.Log;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
|
||||
public class PackageManagerTests extends AndroidTestCase {
|
||||
@@ -378,6 +379,18 @@ public class PackageManagerTests extends AndroidTestCase {
|
||||
assertEquals(publicSrcPath, appInstallPath);
|
||||
assertFalse((info.flags & ApplicationInfo.FLAG_EXTERNAL_STORAGE) != 0);
|
||||
assertTrue(info.nativeLibraryDir.startsWith(dataDir.getPath()));
|
||||
|
||||
// Make sure the native library dir is not a symlink
|
||||
final File nativeLibDir = new File(info.nativeLibraryDir);
|
||||
assertTrue("Native library dir should exist at " + info.nativeLibraryDir,
|
||||
nativeLibDir.exists());
|
||||
try {
|
||||
assertEquals("Native library dir should not be a symlink",
|
||||
info.nativeLibraryDir,
|
||||
nativeLibDir.getCanonicalPath());
|
||||
} catch (IOException e) {
|
||||
fail("Can't read " + nativeLibDir.getPath());
|
||||
}
|
||||
} else if (rLoc == INSTALL_LOC_SD){
|
||||
assertTrue("Application flags (" + info.flags
|
||||
+ ") should contain FLAG_EXTERNAL_STORAGE",
|
||||
@@ -391,6 +404,19 @@ public class PackageManagerTests extends AndroidTestCase {
|
||||
assertTrue("The native library path (" + info.nativeLibraryDir
|
||||
+ ") should start with " + SECURE_CONTAINERS_PREFIX,
|
||||
info.nativeLibraryDir.startsWith(SECURE_CONTAINERS_PREFIX));
|
||||
|
||||
// Make sure the native library in /data/data/<app>/lib is a
|
||||
// symlink to the ASEC
|
||||
final File nativeLibSymLink = new File(info.dataDir, "lib");
|
||||
assertTrue("Native library symlink should exist at " + nativeLibSymLink.getPath(),
|
||||
nativeLibSymLink.exists());
|
||||
try {
|
||||
assertEquals(nativeLibSymLink.getPath() + " should be a symlink to "
|
||||
+ info.nativeLibraryDir, info.nativeLibraryDir, nativeLibSymLink
|
||||
.getCanonicalPath());
|
||||
} catch (IOException e) {
|
||||
fail("Can't read " + nativeLibSymLink.getPath());
|
||||
}
|
||||
} else {
|
||||
// TODO handle error. Install should have failed.
|
||||
fail("Install should have failed");
|
||||
@@ -1406,13 +1432,21 @@ public class PackageManagerTests extends AndroidTestCase {
|
||||
receiver);
|
||||
assertTrue(retCode);
|
||||
ApplicationInfo info = getPm().getApplicationInfo(ip.pkg.packageName, 0);
|
||||
assertNotNull(info);
|
||||
assertNotNull("ApplicationInfo for recently installed application should exist",
|
||||
info);
|
||||
if ((moveFlags & PackageManager.MOVE_INTERNAL) != 0) {
|
||||
assertTrue((info.flags & ApplicationInfo.FLAG_EXTERNAL_STORAGE) == 0);
|
||||
assertTrue(info.nativeLibraryDir.startsWith(info.dataDir));
|
||||
assertTrue("ApplicationInfo.FLAG_EXTERNAL_STORAGE flag should NOT be set",
|
||||
(info.flags & ApplicationInfo.FLAG_EXTERNAL_STORAGE) == 0);
|
||||
assertTrue("ApplicationInfo.nativeLibraryDir should start with " + info.dataDir,
|
||||
info.nativeLibraryDir.startsWith(info.dataDir));
|
||||
} else if ((moveFlags & PackageManager.MOVE_EXTERNAL_MEDIA) != 0){
|
||||
assertTrue((info.flags & ApplicationInfo.FLAG_EXTERNAL_STORAGE) != 0);
|
||||
assertTrue(info.nativeLibraryDir.startsWith(SECURE_CONTAINERS_PREFIX));
|
||||
assertTrue("ApplicationInfo.FLAG_EXTERNAL_STORAGE flag should be set",
|
||||
(info.flags & ApplicationInfo.FLAG_EXTERNAL_STORAGE) != 0);
|
||||
assertTrue("ApplicationInfo.nativeLibraryDir should start with " + SECURE_CONTAINERS_PREFIX,
|
||||
info.nativeLibraryDir.startsWith(SECURE_CONTAINERS_PREFIX));
|
||||
final File nativeLibSymLink = new File(info.dataDir, "lib");
|
||||
assertTrue("The data directory should have a 'lib' symlink that points to the ASEC container",
|
||||
nativeLibSymLink.getCanonicalPath().startsWith(SECURE_CONTAINERS_PREFIX));
|
||||
}
|
||||
}
|
||||
} catch (NameNotFoundException e) {
|
||||
|
||||
@@ -327,4 +327,33 @@ class Installer {
|
||||
public int moveFiles() {
|
||||
return execute("movefiles");
|
||||
}
|
||||
|
||||
public int linkNativeLibraryDirectory(String dataPath, String nativeLibPath) {
|
||||
if (dataPath == null) {
|
||||
Slog.e(TAG, "unlinkNativeLibraryDirectory dataPath is null");
|
||||
return -1;
|
||||
} else if (nativeLibPath == null) {
|
||||
Slog.e(TAG, "unlinkNativeLibraryDirectory nativeLibPath is null");
|
||||
return -1;
|
||||
}
|
||||
|
||||
StringBuilder builder = new StringBuilder("linklib ");
|
||||
builder.append(dataPath);
|
||||
builder.append(' ');
|
||||
builder.append(nativeLibPath);
|
||||
|
||||
return execute(builder.toString());
|
||||
}
|
||||
|
||||
public int unlinkNativeLibraryDirectory(String dataPath) {
|
||||
if (dataPath == null) {
|
||||
Slog.e(TAG, "unlinkNativeLibraryDirectory dataPath is null");
|
||||
return -1;
|
||||
}
|
||||
|
||||
StringBuilder builder = new StringBuilder("unlinklib ");
|
||||
builder.append(dataPath);
|
||||
|
||||
return execute(builder.toString());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3290,7 +3290,11 @@ class PackageManagerService extends IPackageManager.Stub {
|
||||
}
|
||||
} else if (!isExternal(pkg)) {
|
||||
Log.i(TAG, path + " changed; unpacking");
|
||||
mInstaller.unlinkNativeLibraryDirectory(dataPath.getPath());
|
||||
NativeLibraryHelper.copyNativeBinariesLI(scanFile, sharedLibraryDir);
|
||||
} else {
|
||||
mInstaller.linkNativeLibraryDirectory(dataPath.getPath(),
|
||||
pkg.applicationInfo.nativeLibraryDir);
|
||||
}
|
||||
}
|
||||
pkg.mScanPath = path;
|
||||
@@ -5010,10 +5014,6 @@ class PackageManagerService extends IPackageManager.Stub {
|
||||
try { if (out != null) out.close(); } catch (IOException e) {}
|
||||
}
|
||||
|
||||
if (!temp) {
|
||||
NativeLibraryHelper.copyNativeBinariesLI(codeFile, new File(libraryPath));
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
@@ -9894,10 +9894,10 @@ class PackageManagerService extends IPackageManager.Stub {
|
||||
synchronized (mPackages) {
|
||||
PackageParser.Package pkg = mPackages.get(mp.packageName);
|
||||
// Recheck for package again.
|
||||
if (pkg == null ) {
|
||||
Slog.w(TAG, " Package " + mp.packageName +
|
||||
" doesn't exist. Aborting move");
|
||||
returnCode = PackageManager.MOVE_FAILED_DOESNT_EXIST;
|
||||
if (pkg == null) {
|
||||
Slog.w(TAG, " Package " + mp.packageName
|
||||
+ " doesn't exist. Aborting move");
|
||||
returnCode = PackageManager.MOVE_FAILED_DOESNT_EXIST;
|
||||
} else if (!mp.srcArgs.getCodePath().equals(pkg.applicationInfo.sourceDir)) {
|
||||
Slog.w(TAG, "Package " + mp.packageName + " code path changed from " +
|
||||
mp.srcArgs.getCodePath() + " to " + pkg.applicationInfo.sourceDir +
|
||||
@@ -9908,15 +9908,34 @@ class PackageManagerService extends IPackageManager.Stub {
|
||||
final String newCodePath = mp.targetArgs.getCodePath();
|
||||
final String newResPath = mp.targetArgs.getResourcePath();
|
||||
final String newNativePath = mp.targetArgs.getNativeLibraryPath();
|
||||
pkg.mPath = newCodePath;
|
||||
// Move dex files around
|
||||
if (moveDexFilesLI(pkg)
|
||||
!= PackageManager.INSTALL_SUCCEEDED) {
|
||||
// Moving of dex files failed. Set
|
||||
// error code and abort move.
|
||||
pkg.mPath = pkg.mScanPath;
|
||||
returnCode = PackageManager.MOVE_FAILED_INSUFFICIENT_STORAGE;
|
||||
} else {
|
||||
|
||||
if ((mp.flags & PackageManager.INSTALL_EXTERNAL) == 0) {
|
||||
if (mInstaller
|
||||
.unlinkNativeLibraryDirectory(pkg.applicationInfo.dataDir) < 0) {
|
||||
returnCode = PackageManager.MOVE_FAILED_INSUFFICIENT_STORAGE;
|
||||
} else {
|
||||
NativeLibraryHelper.copyNativeBinariesLI(
|
||||
new File(newCodePath), new File(newNativePath));
|
||||
}
|
||||
} else {
|
||||
if (mInstaller.linkNativeLibraryDirectory(
|
||||
pkg.applicationInfo.dataDir, newNativePath) < 0) {
|
||||
returnCode = PackageManager.MOVE_FAILED_INSUFFICIENT_STORAGE;
|
||||
}
|
||||
}
|
||||
|
||||
if (returnCode == PackageManager.MOVE_SUCCEEDED) {
|
||||
pkg.mPath = newCodePath;
|
||||
// Move dex files around
|
||||
if (moveDexFilesLI(pkg) != PackageManager.INSTALL_SUCCEEDED) {
|
||||
// Moving of dex files failed. Set
|
||||
// error code and abort move.
|
||||
pkg.mPath = pkg.mScanPath;
|
||||
returnCode = PackageManager.MOVE_FAILED_INSUFFICIENT_STORAGE;
|
||||
}
|
||||
}
|
||||
|
||||
if (returnCode == PackageManager.MOVE_SUCCEEDED) {
|
||||
pkg.mScanPath = newCodePath;
|
||||
pkg.applicationInfo.sourceDir = newCodePath;
|
||||
pkg.applicationInfo.publicSourceDir = newResPath;
|
||||
|
||||
Reference in New Issue
Block a user