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

Commit 9415ba0a authored by JW Wang's avatar JW Wang
Browse files

Populate APK files using hard links (2/n)

This is an effort to improve the storage usage of RollbackManager.

When committing a rollback, the apk/apex files will be deleted after
the install session is committed successfully. We can use hard links
to effectively transfer the files to PackageInstaller to avoid copy.

Bug: 168562373
Test: atest RollbackTest StagedRollbackTest

Change-Id: I749a17d769d1845bb0f9b34c8757b69b40b516f9
parent d6395c51
Loading
Loading
Loading
Loading
+1 −0
Original line number Diff line number Diff line
@@ -33,6 +33,7 @@ interface IPackageInstallerSession {
    ParcelFileDescriptor openRead(String name);

    void write(String name, long offsetBytes, long lengthBytes, in ParcelFileDescriptor fd);
    void stageViaHardLink(String target);

    void addChecksums(String name, in Checksum[] checksums);

+25 −0
Original line number Diff line number Diff line
@@ -1048,6 +1048,31 @@ public class PackageInstaller {
            }
        }

        /**
         * Populate an APK file by creating a hard link to avoid the need to copy.
         * <p>
         * Note this API is used by RollbackManager only and can only be called from system_server.
         * {@code target} will be relabeled if link is created successfully. RollbackManager has
         * to delete {@code target} when the session is committed successfully to avoid SELinux
         * label conflicts.
         * <p>
         * Note No more bytes should be written to the file once the link is created successfully.
         *
         * @param target the path of the link target
         *
         * @hide
         */
        public void stageViaHardLink(String target) throws IOException {
            try {
                mSession.stageViaHardLink(target);
            } catch (RuntimeException e) {
                ExceptionUtils.maybeUnwrapIOException(e);
                throw e;
            } catch (RemoteException e) {
                throw e.rethrowFromSystemServer();
            }
        }

        /**
         * Ensure that any outstanding data for given stream has been committed
         * to disk. This is only valid for streams returned from
+26 −0
Original line number Diff line number Diff line
@@ -110,6 +110,7 @@ import android.os.ParcelableException;
import android.os.Process;
import android.os.RemoteException;
import android.os.RevocableFileDescriptor;
import android.os.SELinux;
import android.os.SystemProperties;
import android.os.UserHandle;
import android.os.incremental.IStorageHealthListener;
@@ -1092,6 +1093,31 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub {
        }
    }

    @Override
    public void stageViaHardLink(String path) {
        final int callingUid = Binder.getCallingUid();
        if (callingUid != Process.SYSTEM_UID) {
            throw new SecurityException("link() can only be run by the system");
        }

        try {
            final File target = new File(path);
            final File source = new File(stageDir, target.getName());
            try {
                Os.link(path, source.getAbsolutePath());
                // Grant READ access for APK to be read successfully
                Os.chmod(source.getAbsolutePath(), 0644);
            } catch (ErrnoException e) {
                e.rethrowAsIOException();
            }
            if (!SELinux.restorecon(source)) {
                throw new IOException("Can't relabel file: " + source);
            }
        } catch (IOException e) {
            throw ExceptionUtils.wrap(e);
        }
    }

    private ParcelFileDescriptor openTargetInternal(String path, int flags, int mode)
            throws IOException, ErrnoException {
        // TODO: this should delegate to DCS so the system process avoids
+12 −3
Original line number Diff line number Diff line
@@ -581,9 +581,18 @@ class Rollback {
                            ParcelFileDescriptor.MODE_READ_ONLY)) {
                        final long token = Binder.clearCallingIdentity();
                        try {
                            boolean fallbackToCopy = false;
                            try {
                                // Populate apk/apex files using hard links to avoid copy
                                session.stageViaHardLink(packageCodePath.getAbsolutePath());
                            } catch (Exception ignore) {
                                fallbackToCopy = true;
                            }
                            if (fallbackToCopy) {
                                session.write(packageCodePath.getName(), 0,
                                        packageCodePath.length(),
                                        fd);
                            }
                        } finally {
                            Binder.restoreCallingIdentity(token);
                        }