OSDN Git Service

Move linking and directory creation logic to installd.
authorNarayan Kamath <narayan@google.com>
Fri, 5 Jun 2015 10:59:26 +0000 (11:59 +0100)
committerNarayan Kamath <narayan@google.com>
Tue, 9 Jun 2015 10:36:37 +0000 (10:36 +0000)
bug: 20889739

Change-Id: I1763376a2fbb25f3a3d55f60c1940cdbddcded07

services/core/java/com/android/server/pm/Installer.java
services/core/java/com/android/server/pm/PackageInstallerSession.java

index fb98d94..ef7be30 100644 (file)
@@ -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.
      */
index 1cec750..b5ef3b7 100644 (file)
@@ -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() {
@@ -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.
@@ -706,21 +712,23 @@ 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.
-                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.
-                    if (!ArrayUtils.contains(instructionSets, archSubDir.getName())) {
-                        continue;
-                    }
 
-                    List<File> oatFiles = Arrays.asList(archSubDir.listFiles());
-                    if (!oatFiles.isEmpty()) {
-                        mResolvedInheritedFiles.add(archSubDir);
-                        mResolvedInheritedFiles.addAll(oatFiles);
+                // 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) {
+                    final String[] instructionSets = InstructionSets.getAllDexCodeInstructionSets();
+                    for (File archSubDir : archSubdirs) {
+                        // Skip any directory that isn't an ISA subdir.
+                        if (!ArrayUtils.contains(instructionSets, archSubDir.getName())) {
+                            continue;
+                        }
+
+                        mResolvedInstructionSets.add(archSubDir.getName());
+                        List<File> oatFiles = Arrays.asList(archSubDir.listFiles());
+                        if (!oatFiles.isEmpty()) {
+                            mResolvedInheritedFiles.addAll(oatFiles);
+                        }
                     }
                 }
             }
@@ -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)
+    private void createOatDirs(List<String> instructionSets, File fromDir) {
+        for (String instructionSet : instructionSets) {
+            mPm.mInstaller.createOatDir(fromDir.getAbsolutePath(), instructionSet);
+        }
+    }
+
+    private void linkFiles(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);
-            }
-
-            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);
-                }
+            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);
     }