Loading services/core/java/com/android/server/pm/Installer.java +12 −0 Original line number Diff line number Diff line Loading @@ -454,6 +454,18 @@ public final class Installer extends SystemService { return mInstaller.execute(builder.toString()); } public int linkFile(String relativePath, String fromBase, String toBase) { StringBuilder builder = new StringBuilder("linkfile"); builder.append(' '); builder.append(relativePath); builder.append(' '); builder.append(fromBase); builder.append(' '); builder.append(toBase); return mInstaller.execute(builder.toString()); } /** * Returns true iff. {@code instructionSet} is a valid instruction set. */ Loading services/core/java/com/android/server/pm/PackageInstallerSession.java +44 −66 Original line number Diff line number Diff line Loading @@ -166,6 +166,8 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub { @GuardedBy("mLock") private final List<File> mResolvedInheritedFiles = new ArrayList<>(); @GuardedBy("mLock") private final List<String> mResolvedInstructionSets = new ArrayList<>(); @GuardedBy("mLock") private File mInheritedFilesBase; private final Handler.Callback mHandlerCallback = new Handler.Callback() { Loading Loading @@ -521,7 +523,11 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub { } if (isLinkPossible(fromFiles, toDir)) { createDirsAndLinkFiles(fromFiles, toDir, mInheritedFilesBase); if (!mResolvedInstructionSets.isEmpty()) { final File oatDir = new File(toDir, "oat"); createOatDirs(mResolvedInstructionSets, oatDir); } linkFiles(fromFiles, toDir, mInheritedFilesBase); } else { // TODO: this should delegate to DCS so the system process // avoids holding open FDs into containers. Loading Loading @@ -706,10 +712,11 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub { final File oatDir = new File(packageInstallDir, "oat"); if (oatDir.exists()) { final File[] archSubdirs = oatDir.listFiles(); // Only add "oatDir" if it contains arch specific subdirs. // Keep track of all instruction sets we've seen compiled output for. // If we're linking (and not copying) inherited files, we can recreate the // instruction set hierarchy and link compiled output. if (archSubdirs != null && archSubdirs.length > 0) { mResolvedInheritedFiles.add(oatDir); } final String[] instructionSets = InstructionSets.getAllDexCodeInstructionSets(); for (File archSubDir : archSubdirs) { // Skip any directory that isn't an ISA subdir. Loading @@ -717,15 +724,16 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub { continue; } mResolvedInstructionSets.add(archSubDir.getName()); List<File> oatFiles = Arrays.asList(archSubDir.listFiles()); if (!oatFiles.isEmpty()) { mResolvedInheritedFiles.add(archSubDir); mResolvedInheritedFiles.addAll(oatFiles); } } } } } } private void assertApkConsistent(String tag, ApkLite apk) throws PackageManagerException { if (!mPackageName.equals(apk.packageName)) { Loading Loading @@ -802,71 +810,41 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub { return true; } /** * Reparents the path of {@code file} from {@code oldBase} to {@code newBase}. {@code file} * must necessarily be a subpath of {@code oldBase}. It is an error for {@code file} to have * relative path components such as {@code "."} or {@code ".."}. For example, for we will * reparent {@code /foo/bar/baz} to {@code /foo2/bar/baz} if {@code oldBase} was {@code /foo} * and {@code newBase} was {@code /foo2}. */ private static File reparentPath(File file, File oldBase, File newBase) throws IOException { final String oldBaseStr = oldBase.getAbsolutePath(); private static String getRelativePath(File file, File base) throws IOException { final String pathStr = file.getAbsolutePath(); final String baseStr = base.getAbsolutePath(); // Don't allow relative paths. if (pathStr.contains("/.") ) { throw new IOException("Invalid path (was relative) : " + pathStr); } if (pathStr.startsWith(oldBaseStr)) { final String relative = pathStr.substring(oldBaseStr.length()); return new File(newBase, relative); if (pathStr.startsWith(baseStr)) { return pathStr.substring(baseStr.length()); } throw new IOException("File: " + pathStr + " outside base: " + oldBaseStr); throw new IOException("File: " + pathStr + " outside base: " + baseStr); } /** * Recreates a directory and file structure, specified by a list of files {@code fromFiles} * which are subpaths of {@code fromDir} to {@code toDir}. Directories are created with the * same permissions, and regular files are linked. * * TODO: Move this function to installd so that the system process doesn't have to * manipulate / relabel directories. */ private static void createDirsAndLinkFiles(List<File> fromFiles, File toDir, File fromDir) throws IOException { for (File fromFile : fromFiles) { final File toFile = reparentPath(fromFile, fromDir, toDir); final StructStat stat; try { stat = Os.stat(fromFile.getAbsolutePath()); } catch (ErrnoException e) { throw new IOException("Failed to stat: " + fromFile.getAbsolutePath(), e); private void createOatDirs(List<String> instructionSets, File fromDir) { for (String instructionSet : instructionSets) { mPm.mInstaller.createOatDir(fromDir.getAbsolutePath(), instructionSet); } if (OsConstants.S_ISDIR(stat.st_mode)) { if (LOGD) Slog.d(TAG, "Creating directory " + toFile.getAbsolutePath()); try { Os.mkdir(toFile.getAbsolutePath(), stat.st_mode); } catch (ErrnoException e) { throw new IOException("Failed to create dir: " + toFile.getAbsolutePath(), e); } // We do this to ensure that the oat/ directory is created with the right // label (data_dalvikcache_file) instead of apk_tmpfile. if (!SELinux.restorecon(toFile)) { throw new IOException("Failed to restorecon: " + toFile.getAbsolutePath()); } } else { if (LOGD) Slog.d(TAG, "Linking " + fromFile + " to " + toFile); try { Os.link(fromFile.getAbsolutePath(), toFile.getAbsolutePath()); } catch (ErrnoException e) { throw new IOException("Failed to link " + fromFile + " to " + toFile, e); } private void linkFiles(List<File> fromFiles, File toDir, File fromDir) throws IOException { for (File fromFile : fromFiles) { final String relativePath = getRelativePath(fromFile, fromDir); final int ret = mPm.mInstaller.linkFile(relativePath, fromDir.getAbsolutePath(), toDir.getAbsolutePath()); if (ret < 0) { // installd will log failure details. throw new IOException("failed linkOrCreateDir(" + relativePath + ", " + fromDir + ", " + toDir + ")"); } } Slog.d(TAG, "Linked " + fromFiles.size() + " files into " + toDir); } Loading Loading
services/core/java/com/android/server/pm/Installer.java +12 −0 Original line number Diff line number Diff line Loading @@ -454,6 +454,18 @@ public final class Installer extends SystemService { return mInstaller.execute(builder.toString()); } public int linkFile(String relativePath, String fromBase, String toBase) { StringBuilder builder = new StringBuilder("linkfile"); builder.append(' '); builder.append(relativePath); builder.append(' '); builder.append(fromBase); builder.append(' '); builder.append(toBase); return mInstaller.execute(builder.toString()); } /** * Returns true iff. {@code instructionSet} is a valid instruction set. */ Loading
services/core/java/com/android/server/pm/PackageInstallerSession.java +44 −66 Original line number Diff line number Diff line Loading @@ -166,6 +166,8 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub { @GuardedBy("mLock") private final List<File> mResolvedInheritedFiles = new ArrayList<>(); @GuardedBy("mLock") private final List<String> mResolvedInstructionSets = new ArrayList<>(); @GuardedBy("mLock") private File mInheritedFilesBase; private final Handler.Callback mHandlerCallback = new Handler.Callback() { Loading Loading @@ -521,7 +523,11 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub { } if (isLinkPossible(fromFiles, toDir)) { createDirsAndLinkFiles(fromFiles, toDir, mInheritedFilesBase); if (!mResolvedInstructionSets.isEmpty()) { final File oatDir = new File(toDir, "oat"); createOatDirs(mResolvedInstructionSets, oatDir); } linkFiles(fromFiles, toDir, mInheritedFilesBase); } else { // TODO: this should delegate to DCS so the system process // avoids holding open FDs into containers. Loading Loading @@ -706,10 +712,11 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub { final File oatDir = new File(packageInstallDir, "oat"); if (oatDir.exists()) { final File[] archSubdirs = oatDir.listFiles(); // Only add "oatDir" if it contains arch specific subdirs. // Keep track of all instruction sets we've seen compiled output for. // If we're linking (and not copying) inherited files, we can recreate the // instruction set hierarchy and link compiled output. if (archSubdirs != null && archSubdirs.length > 0) { mResolvedInheritedFiles.add(oatDir); } final String[] instructionSets = InstructionSets.getAllDexCodeInstructionSets(); for (File archSubDir : archSubdirs) { // Skip any directory that isn't an ISA subdir. Loading @@ -717,15 +724,16 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub { continue; } mResolvedInstructionSets.add(archSubDir.getName()); List<File> oatFiles = Arrays.asList(archSubDir.listFiles()); if (!oatFiles.isEmpty()) { mResolvedInheritedFiles.add(archSubDir); mResolvedInheritedFiles.addAll(oatFiles); } } } } } } private void assertApkConsistent(String tag, ApkLite apk) throws PackageManagerException { if (!mPackageName.equals(apk.packageName)) { Loading Loading @@ -802,71 +810,41 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub { return true; } /** * Reparents the path of {@code file} from {@code oldBase} to {@code newBase}. {@code file} * must necessarily be a subpath of {@code oldBase}. It is an error for {@code file} to have * relative path components such as {@code "."} or {@code ".."}. For example, for we will * reparent {@code /foo/bar/baz} to {@code /foo2/bar/baz} if {@code oldBase} was {@code /foo} * and {@code newBase} was {@code /foo2}. */ private static File reparentPath(File file, File oldBase, File newBase) throws IOException { final String oldBaseStr = oldBase.getAbsolutePath(); private static String getRelativePath(File file, File base) throws IOException { final String pathStr = file.getAbsolutePath(); final String baseStr = base.getAbsolutePath(); // Don't allow relative paths. if (pathStr.contains("/.") ) { throw new IOException("Invalid path (was relative) : " + pathStr); } if (pathStr.startsWith(oldBaseStr)) { final String relative = pathStr.substring(oldBaseStr.length()); return new File(newBase, relative); if (pathStr.startsWith(baseStr)) { return pathStr.substring(baseStr.length()); } throw new IOException("File: " + pathStr + " outside base: " + oldBaseStr); throw new IOException("File: " + pathStr + " outside base: " + baseStr); } /** * Recreates a directory and file structure, specified by a list of files {@code fromFiles} * which are subpaths of {@code fromDir} to {@code toDir}. Directories are created with the * same permissions, and regular files are linked. * * TODO: Move this function to installd so that the system process doesn't have to * manipulate / relabel directories. */ private static void createDirsAndLinkFiles(List<File> fromFiles, File toDir, File fromDir) throws IOException { for (File fromFile : fromFiles) { final File toFile = reparentPath(fromFile, fromDir, toDir); final StructStat stat; try { stat = Os.stat(fromFile.getAbsolutePath()); } catch (ErrnoException e) { throw new IOException("Failed to stat: " + fromFile.getAbsolutePath(), e); private void createOatDirs(List<String> instructionSets, File fromDir) { for (String instructionSet : instructionSets) { mPm.mInstaller.createOatDir(fromDir.getAbsolutePath(), instructionSet); } if (OsConstants.S_ISDIR(stat.st_mode)) { if (LOGD) Slog.d(TAG, "Creating directory " + toFile.getAbsolutePath()); try { Os.mkdir(toFile.getAbsolutePath(), stat.st_mode); } catch (ErrnoException e) { throw new IOException("Failed to create dir: " + toFile.getAbsolutePath(), e); } // We do this to ensure that the oat/ directory is created with the right // label (data_dalvikcache_file) instead of apk_tmpfile. if (!SELinux.restorecon(toFile)) { throw new IOException("Failed to restorecon: " + toFile.getAbsolutePath()); } } else { if (LOGD) Slog.d(TAG, "Linking " + fromFile + " to " + toFile); try { Os.link(fromFile.getAbsolutePath(), toFile.getAbsolutePath()); } catch (ErrnoException e) { throw new IOException("Failed to link " + fromFile + " to " + toFile, e); } private void linkFiles(List<File> fromFiles, File toDir, File fromDir) throws IOException { for (File fromFile : fromFiles) { final String relativePath = getRelativePath(fromFile, fromDir); final int ret = mPm.mInstaller.linkFile(relativePath, fromDir.getAbsolutePath(), toDir.getAbsolutePath()); if (ret < 0) { // installd will log failure details. throw new IOException("failed linkOrCreateDir(" + relativePath + ", " + fromDir + ", " + toDir + ")"); } } Slog.d(TAG, "Linked " + fromFiles.size() + " files into " + toDir); } Loading