Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit e845a1ef authored by Narayan Kamath's avatar Narayan Kamath
Browse files

Move linking and directory creation logic to installd.

bug: 20889739

Change-Id: I1763376a2fbb25f3a3d55f60c1940cdbddcded07
parent 1162744f
Loading
Loading
Loading
Loading
+12 −0
Original line number Diff line number Diff line
@@ -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.
     */
+44 −66
Original line number Diff line number Diff line
@@ -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,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.
@@ -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)) {
@@ -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);
    }