Correct module remote file implementation

This commit is contained in:
LoveSy 2023-01-08 15:25:23 +08:00 committed by LoveSy
parent 5e3d4d6559
commit 4c3f8d95db
5 changed files with 115 additions and 31 deletions

View File

@ -299,12 +299,22 @@ public class LSPosedContext extends XposedContext {
@Override
public boolean moveSharedPreferencesFrom(Context sourceContext, String name) {
throw new AbstractMethodError();
if (name == null) throw new IllegalArgumentException("name must not be null");
if (name.startsWith("remote://")) {
throw new IllegalArgumentException("Moving remote preferences is not supported");
} else {
return mBase.moveSharedPreferencesFrom(sourceContext, name);
}
}
@Override
public boolean deleteSharedPreferences(String name) {
throw new AbstractMethodError();
if (name == null) throw new IllegalArgumentException("name must not be null");
if (name.startsWith("remote://")) {
throw new IllegalArgumentException("Read only implementation");
} else {
return mBase.deleteSharedPreferences(name);
}
}
@Override
@ -325,7 +335,7 @@ public class LSPosedContext extends XposedContext {
public FileOutputStream openFileOutput(String name, int mode) throws FileNotFoundException {
if (name == null) throw new IllegalArgumentException("name must not be null");
if (name.startsWith("remote://")) {
throw new FileNotFoundException("Read only implementation");
throw new IllegalArgumentException("Read only implementation");
} else {
return mBase.openFileOutput(name, mode);
}
@ -333,12 +343,22 @@ public class LSPosedContext extends XposedContext {
@Override
public boolean deleteFile(String name) {
throw new AbstractMethodError();
if (name == null) throw new IllegalArgumentException("name must not be null");
if (name.startsWith("remote://")) {
throw new IllegalArgumentException("Read only implementation");
} else {
return mBase.deleteFile(name);
}
}
@Override
public File getFileStreamPath(String name) {
return mBase.getFileStreamPath(name);
if (name == null) throw new IllegalArgumentException("name must not be null");
if (name.startsWith("remote://")) {
throw new IllegalArgumentException("Getting remote file path is not supported");
} else {
return mBase.getFileStreamPath(name);
}
}
@Override
@ -427,32 +447,64 @@ public class LSPosedContext extends XposedContext {
@Override
public SQLiteDatabase openOrCreateDatabase(String name, int mode, SQLiteDatabase.CursorFactory factory) {
throw new AbstractMethodError();
if (name == null) throw new IllegalArgumentException("name must not be null");
if (name.startsWith("remote://")) {
return openOrCreateDatabase(name, mode, factory, null);
} else {
return mBase.openOrCreateDatabase(name, mode, factory);
}
}
@Override
public SQLiteDatabase openOrCreateDatabase(String name, int mode, SQLiteDatabase.CursorFactory factory, @Nullable DatabaseErrorHandler errorHandler) {
throw new AbstractMethodError();
if (name == null) throw new IllegalArgumentException("name must not be null");
if (name.startsWith("remote://")) {
throw new IllegalArgumentException("Opening remote database is not supported");
} else {
return mBase.openOrCreateDatabase(name, mode, factory, errorHandler);
}
}
@Override
public boolean moveDatabaseFrom(Context sourceContext, String name) {
throw new AbstractMethodError();
if (name == null) throw new IllegalArgumentException("name must not be null");
if (name.startsWith("remote://")) {
throw new IllegalArgumentException("Moving remote database is not supported");
} else {
return mBase.moveDatabaseFrom(sourceContext, name);
}
}
@Override
public boolean deleteDatabase(String name) {
throw new AbstractMethodError();
if (name == null) throw new IllegalArgumentException("name must not be null");
if (name.startsWith("remote://")) {
throw new IllegalArgumentException("Read only implementation");
} else {
return mBase.deleteDatabase(name);
}
}
@Override
public File getDatabasePath(String name) {
throw new AbstractMethodError();
if (name == null) throw new IllegalArgumentException("name must not be null");
if (name.startsWith("remote://")) {
throw new IllegalArgumentException("Getting remote database path is not supported");
} else {
return mBase.getDatabasePath(name);
}
}
@Override
public String[] databaseList() {
throw new AbstractMethodError();
var remoteFiles = new String[0]; // TODO
var localFiles = mBase.databaseList();
var files = new String[remoteFiles.length + localFiles.length];
for (int i = 0; i < remoteFiles.length; i++) {
files[i] = "remote://" + remoteFiles[i];
}
System.arraycopy(localFiles, 0, files, remoteFiles.length, localFiles.length);
return files;
}
@Override

View File

@ -26,6 +26,7 @@ import android.content.res.AssetManager;
import android.content.res.Resources;
import android.os.ParcelFileDescriptor;
import android.os.Process;
import android.os.RemoteException;
import android.os.SELinux;
import android.os.SharedMemory;
import android.system.ErrnoException;
@ -428,17 +429,27 @@ public class ConfigFileManager {
return preloadDex;
}
static Path resolveModulePath(String packageName, String path) throws IOException {
var requestPath = Paths.get(path);
if (requestPath.isAbsolute()) {
throw new IOException("path must be relative");
static void ensureValidPath(String path) throws RemoteException {
if (path == null || path.indexOf(File.separatorChar) >= 0 || ".".equals(path) || "..".equals(path)) {
throw new RemoteException("Invalid path: " + path);
}
var moduleDir = modulePath.resolve(packageName).normalize();
var absolutePath = moduleDir.resolve(requestPath).normalize();
if (!absolutePath.startsWith(moduleDir)) {
throw new IOException("path must be in module dir");
}
static Path resolveModuleDir(String packageName, String dir, int userId, int uid) throws IOException {
var path = modulePath.resolve(String.valueOf(userId)).resolve(packageName).resolve(dir).normalize();
if (uid != -1) {
if (!path.toFile().mkdirs()) {
throw new IOException("Can not create " + dir + " for " + packageName);
}
SELinux.setFileContext(path.toString(), "u:object_r::s0");
try {
Os.chown(path.toString(), uid, uid);
Os.chmod(path.toString(), 0755);
} catch (ErrnoException e) {
throw new IOException(e);
}
}
return absolutePath;
return path;
}
private static class FileLocker {

View File

@ -9,6 +9,7 @@ import android.os.RemoteException;
import org.lsposed.lspd.models.Module;
import java.io.File;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
@ -44,9 +45,11 @@ public class LSPInjectedModuleService extends ILSPInjectedModuleService.Stub {
@Override
public ParcelFileDescriptor openRemoteFile(String path) throws RemoteException {
ConfigFileManager.ensureValidPath(path);
var userId = Binder.getCallingUid() / PER_USER_RANGE;
try {
var absolutePath = ConfigFileManager.resolveModulePath(loadedModule.packageName, path);
return ParcelFileDescriptor.open(absolutePath.toFile(), ParcelFileDescriptor.MODE_READ_ONLY);
var dirPath = ConfigFileManager.resolveModuleDir(loadedModule.packageName, "files", userId, -1);
return ParcelFileDescriptor.open(dirPath.resolve(path).toFile(), ParcelFileDescriptor.MODE_READ_ONLY);
} catch (Throwable e) {
throw new RemoteException(e.getMessage());
}
@ -55,8 +58,11 @@ public class LSPInjectedModuleService extends ILSPInjectedModuleService.Stub {
@Override
public String[] getRemoteFileList() throws RemoteException {
try {
var absolutePath = ConfigFileManager.resolveModulePath(loadedModule.packageName, ".");
return absolutePath.toFile().list();
var userId = Binder.getCallingUid() / PER_USER_RANGE;
var dir = ConfigFileManager.resolveModuleDir(loadedModule.packageName, "files", userId, -1);
var files = dir.toFile().list();
return files == null ? new String[0] : files;
} catch (Throwable e) {
throw new RemoteException(e.getMessage());
}

View File

@ -35,6 +35,7 @@ import androidx.annotation.NonNull;
import org.lsposed.daemon.BuildConfig;
import org.lsposed.lspd.models.Module;
import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
@ -224,12 +225,11 @@ public class LSPModuleService extends IXposedService.Stub {
@Override
public ParcelFileDescriptor openRemoteFile(String path, int mode) throws RemoteException {
var userId = ensureModule();
ConfigFileManager.ensureValidPath(path);
try {
var absolutePath = ConfigFileManager.resolveModulePath(loadedModule.packageName, path);
if (!absolutePath.getParent().toFile().mkdirs()) {
throw new IOException("failed to create parent dir");
}
return ParcelFileDescriptor.open(absolutePath.toFile(), mode);
var dir = ConfigFileManager.resolveModuleDir(loadedModule.packageName, "files", userId, Binder.getCallingUid());
return ParcelFileDescriptor.open(dir.resolve(path).toFile(), mode);
} catch (Throwable e) {
throw new RemoteException(e.getMessage());
}
@ -237,9 +237,23 @@ public class LSPModuleService extends IXposedService.Stub {
@Override
public boolean deleteRemoteFile(String path) throws RemoteException {
var userId = ensureModule();
ConfigFileManager.ensureValidPath(path);
try {
var absolutePath = ConfigFileManager.resolveModulePath(loadedModule.packageName, path);
return absolutePath.toFile().delete();
var dir = ConfigFileManager.resolveModuleDir(loadedModule.packageName, "files", userId, Binder.getCallingUid());
return dir.resolve(path).toFile().delete();
} catch (Throwable e) {
throw new RemoteException(e.getMessage());
}
}
@Override
public String[] listRemoteFiles() throws RemoteException {
var userId = ensureModule();
try {
var dir = ConfigFileManager.resolveModuleDir(loadedModule.packageName, "files", userId, Binder.getCallingUid());
var files = dir.toFile().list();
return files == null ? new String[0] : files;
} catch (Throwable e) {
throw new RemoteException(e.getMessage());
}

View File

@ -33,4 +33,5 @@ interface IXposedService {
// remote file utilities
ParcelFileDescriptor openRemoteFile(String path, int mode) = 30;
boolean deleteRemoteFile(String path) = 31;
String[] listRemoteFiles() = 32;
}