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
This commit is contained in:
@@ -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);
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user