Merge "Populate APK files using hard links (2/n)"

This commit is contained in:
JW Wang
2020-12-04 09:29:33 +00:00
committed by Android (Google) Code Review
4 changed files with 64 additions and 3 deletions

View File

@@ -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);

View File

@@ -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

View File

@@ -104,6 +104,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;
@@ -1088,6 +1089,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

View File

@@ -581,9 +581,18 @@ class Rollback {
ParcelFileDescriptor.MODE_READ_ONLY)) {
final long token = Binder.clearCallingIdentity();
try {
session.write(packageCodePath.getName(), 0,
packageCodePath.length(),
fd);
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);
}