From 4c3f8d95db722410fb5cef0ce78b98dae99dc7d8 Mon Sep 17 00:00:00 2001 From: LoveSy Date: Sun, 8 Jan 2023 15:25:23 +0800 Subject: [PATCH] Correct module remote file implementation --- .../org/lsposed/lspd/impl/LSPosedContext.java | 74 ++++++++++++++++--- .../lspd/service/ConfigFileManager.java | 29 +++++--- .../service/LSPInjectedModuleService.java | 14 +++- .../lspd/service/LSPModuleService.java | 28 +++++-- .../libxposed/service/IXposedService.aidl | 1 + 5 files changed, 115 insertions(+), 31 deletions(-) diff --git a/core/src/main/java/org/lsposed/lspd/impl/LSPosedContext.java b/core/src/main/java/org/lsposed/lspd/impl/LSPosedContext.java index 5132c22f..36826cbd 100644 --- a/core/src/main/java/org/lsposed/lspd/impl/LSPosedContext.java +++ b/core/src/main/java/org/lsposed/lspd/impl/LSPosedContext.java @@ -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 diff --git a/daemon/src/main/java/org/lsposed/lspd/service/ConfigFileManager.java b/daemon/src/main/java/org/lsposed/lspd/service/ConfigFileManager.java index 8ab2223a..ce10da8e 100644 --- a/daemon/src/main/java/org/lsposed/lspd/service/ConfigFileManager.java +++ b/daemon/src/main/java/org/lsposed/lspd/service/ConfigFileManager.java @@ -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 { diff --git a/daemon/src/main/java/org/lsposed/lspd/service/LSPInjectedModuleService.java b/daemon/src/main/java/org/lsposed/lspd/service/LSPInjectedModuleService.java index 1181ff4c..e1ace243 100644 --- a/daemon/src/main/java/org/lsposed/lspd/service/LSPInjectedModuleService.java +++ b/daemon/src/main/java/org/lsposed/lspd/service/LSPInjectedModuleService.java @@ -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()); } diff --git a/daemon/src/main/java/org/lsposed/lspd/service/LSPModuleService.java b/daemon/src/main/java/org/lsposed/lspd/service/LSPModuleService.java index a4568357..7b333c32 100644 --- a/daemon/src/main/java/org/lsposed/lspd/service/LSPModuleService.java +++ b/daemon/src/main/java/org/lsposed/lspd/service/LSPModuleService.java @@ -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()); } diff --git a/libxposed/service/src/main/aidl/io/github/libxposed/service/IXposedService.aidl b/libxposed/service/src/main/aidl/io/github/libxposed/service/IXposedService.aidl index 67e8daed..eff604fc 100644 --- a/libxposed/service/src/main/aidl/io/github/libxposed/service/IXposedService.aidl +++ b/libxposed/service/src/main/aidl/io/github/libxposed/service/IXposedService.aidl @@ -33,4 +33,5 @@ interface IXposedService { // remote file utilities ParcelFileDescriptor openRemoteFile(String path, int mode) = 30; boolean deleteRemoteFile(String path) = 31; + String[] listRemoteFiles() = 32; }