|
41 | 41 | import org.lsposed.lspd.models.PreLoadedApk; |
42 | 42 | import org.lsposed.lspd.util.InstallerVerifier; |
43 | 43 | import org.lsposed.lspd.util.Utils; |
| 44 | +import static org.lsposed.lspd.service.LSPModuleService.FILES_DIR; |
44 | 45 |
|
45 | 46 | import java.io.BufferedReader; |
46 | 47 | import java.io.File; |
|
60 | 61 | import java.nio.file.OpenOption; |
61 | 62 | import java.nio.file.Path; |
62 | 63 | import java.nio.file.Paths; |
| 64 | +import java.nio.file.DirectoryStream; |
| 65 | +import java.nio.file.FileVisitOption; |
63 | 66 | import java.nio.file.SimpleFileVisitor; |
64 | 67 | import java.nio.file.StandardOpenOption; |
65 | 68 | import java.nio.file.attribute.BasicFileAttributes; |
|
70 | 73 | import java.util.HashSet; |
71 | 74 | import java.util.List; |
72 | 75 | import java.util.Locale; |
| 76 | +import java.util.regex.Pattern; |
73 | 77 | import java.util.zip.Deflater; |
74 | 78 | import java.util.zip.ZipEntry; |
75 | 79 | import java.util.zip.ZipFile; |
@@ -455,19 +459,154 @@ static void ensureModuleFilePath(String path) throws RemoteException { |
455 | 459 | static Path resolveModuleDir(String packageName, String dir, int userId, int uid) throws IOException { |
456 | 460 | var path = modulePath.resolve(String.valueOf(userId)).resolve(packageName).resolve(dir).normalize(); |
457 | 461 | if (uid != -1) { |
458 | | - if (path.toFile().mkdirs()) { |
| 462 | + var directory = path.toFile(); |
| 463 | + |
| 464 | + if (directory.mkdirs()) { |
459 | 465 | try { |
460 | 466 | SELinux.setFileContext(path.toString(), "u:object_r:xposed_file:s0"); |
461 | 467 | Os.chown(path.toString(), uid, uid); |
462 | 468 | Os.chmod(path.toString(), 0755); |
463 | 469 | } catch (ErrnoException e) { |
464 | 470 | throw new IOException(e); |
465 | 471 | } |
| 472 | + } else if (directory.isDirectory()) { |
| 473 | + try { |
| 474 | + Files.walkFileTree(path, new SimpleFileVisitor<>() { |
| 475 | + @Override |
| 476 | + public FileVisitResult preVisitDirectory(Path dir, BasicFileAttributes attrs) { |
| 477 | + try { |
| 478 | + SELinux.setFileContext(dir.toString(), "u:object_r:xposed_file:s0"); |
| 479 | + Os.chown(dir.toString(), uid, uid); |
| 480 | + Os.chmod(dir.toString(), 0755); |
| 481 | + } catch (Throwable e) { |
| 482 | + Log.w(TAG, "Failed to secure directory: " + dir + " " + Log.getStackTraceString(e)); |
| 483 | + } |
| 484 | + return FileVisitResult.CONTINUE; |
| 485 | + } |
| 486 | + |
| 487 | + @Override |
| 488 | + public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) { |
| 489 | + try { |
| 490 | + SELinux.setFileContext(file.toString(), "u:object_r:xposed_file:s0"); |
| 491 | + Os.chown(file.toString(), uid, uid); |
| 492 | + Os.chmod(file.toString(), 0660); |
| 493 | + } catch (Throwable e) { |
| 494 | + Log.w(TAG, "Failed to secure file: " + file + " " + Log.getStackTraceString(e)); |
| 495 | + } |
| 496 | + return FileVisitResult.CONTINUE; |
| 497 | + } |
| 498 | + }); |
| 499 | + |
| 500 | + SELinux.setFileContext(path.toString(), "u:object_r:xposed_file:s0"); |
| 501 | + Os.chown(path.toString(), uid, uid); |
| 502 | + Os.chmod(path.toString(), 0755); |
| 503 | + } catch (Throwable e) { |
| 504 | + throw new IOException(e); |
| 505 | + } |
466 | 506 | } |
467 | 507 | } |
468 | 508 | return path; |
469 | 509 | } |
470 | 510 |
|
| 511 | + static void fixModulesDirContext() throws IOException { |
| 512 | + var directory = modulePath.toFile(); |
| 513 | + if (directory.mkdirs()) { |
| 514 | + try { |
| 515 | + SELinux.setFileContext(directory.toString(), "u:object_r:system_file:s0"); |
| 516 | + Os.chown(directory.toString(), 0, 0); |
| 517 | + Os.chmod(directory.toString(), 0777); |
| 518 | + } catch (ErrnoException e) { |
| 519 | + throw new IOException(e); |
| 520 | + } |
| 521 | + } else if (directory.isDirectory()) { |
| 522 | + try { |
| 523 | + // Recursively set context, uid, gid and mode |
| 524 | + // to avoid issues caused by incorrect file context. |
| 525 | + // /data/adb/lspd/modules/<userId>/<packageName>/files/<files> |
| 526 | + // directory = /data/adb/lspd/modules |
| 527 | + // <userId> = u:object_r:system_file:s0 777 root root |
| 528 | + // <packageName> = u:object_r:system_file:s0 777 root root |
| 529 | + // files = u:object_r:xposed_file:s0 755 root root |
| 530 | + // <files> = u:object_r:xposed_file:s0 660 root root |
| 531 | + // Recursively walk through all subdirectories |
| 532 | + Files.walkFileTree(modulePath, new SimpleFileVisitor<Path>() { |
| 533 | + @Override |
| 534 | + public FileVisitResult preVisitDirectory(Path dir, BasicFileAttributes attrs) { |
| 535 | + try { |
| 536 | + String pathStr = dir.toString(); |
| 537 | + |
| 538 | + // Get relative path from modulePath |
| 539 | + Path relative = modulePath.relativize(dir); |
| 540 | + String[] parts = relative.toString().split(Pattern.quote(File.separator)); |
| 541 | + |
| 542 | + if (parts.length == 1) { |
| 543 | + // <userId> dir (e.g., "0") |
| 544 | + SELinux.setFileContext(pathStr, "u:object_r:system_file:s0"); |
| 545 | + Os.chown(pathStr, 0, 0); |
| 546 | + Os.chmod(pathStr, 0777); |
| 547 | + } else if (parts.length == 2) { |
| 548 | + // <packageName> dir (e.g., "com.example.module") |
| 549 | + SELinux.setFileContext(pathStr, "u:object_r:system_file:s0"); |
| 550 | + Os.chown(pathStr, 0, 0); |
| 551 | + Os.chmod(pathStr, 0777); |
| 552 | + } else if (parts.length >= 3 && parts[2].equals(FILES_DIR)) { |
| 553 | + // Inside `files/` or deeper |
| 554 | + if (parts.length == 3) { |
| 555 | + // The `files` directory itself |
| 556 | + SELinux.setFileContext(pathStr, "u:object_r:xposed_file:s0"); |
| 557 | + Os.chown(pathStr, 0, 0); |
| 558 | + Os.chmod(pathStr, 0755); |
| 559 | + } else { |
| 560 | + // Subdirectories or files under `files/` |
| 561 | + SELinux.setFileContext(pathStr, "u:object_r:xposed_file:s0"); |
| 562 | + Os.chown(pathStr, 0, 0); |
| 563 | + // Dirs: 0755, Files: 0660 |
| 564 | + if (Files.isDirectory(dir)) { |
| 565 | + Os.chmod(pathStr, 0755); |
| 566 | + } else { |
| 567 | + Os.chmod(pathStr, 0660); |
| 568 | + } |
| 569 | + } |
| 570 | + } else { |
| 571 | + // Other dirs (not in files/) — keep system context |
| 572 | + SELinux.setFileContext(pathStr, "u:object_r:system_file:s0"); |
| 573 | + Os.chown(pathStr, 0, 0); |
| 574 | + Os.chmod(pathStr, 0777); |
| 575 | + } |
| 576 | + } catch (Exception e) { |
| 577 | + Log.w(TAG, "Failed to secure directory: " + dir + " " + Log.getStackTraceString(e)); |
| 578 | + } |
| 579 | + return FileVisitResult.CONTINUE; |
| 580 | + } |
| 581 | + |
| 582 | + @Override |
| 583 | + public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) { |
| 584 | + try { |
| 585 | + String pathStr = file.toString(); |
| 586 | + Path relative = modulePath.relativize(file); |
| 587 | + String[] parts = relative.toString().split(Pattern.quote(File.separator)); |
| 588 | + |
| 589 | + if (parts.length >= 3 && parts[2].equals("files")) { |
| 590 | + SELinux.setFileContext(pathStr, "u:object_r:xposed_file:s0"); |
| 591 | + Os.chown(pathStr, 0, 0); |
| 592 | + Os.chmod(pathStr, 0660); |
| 593 | + } else { |
| 594 | + SELinux.setFileContext(pathStr, "u:object_r:system_file:s0"); |
| 595 | + Os.chown(pathStr, 0, 0); |
| 596 | + Os.chmod(pathStr, 0644); |
| 597 | + } |
| 598 | + } catch (Exception e) { |
| 599 | + Log.w(TAG, "Failed to secure file: " + file + " " + Log.getStackTraceString(e)); |
| 600 | + } |
| 601 | + return FileVisitResult.CONTINUE; |
| 602 | + } |
| 603 | + }); |
| 604 | + } catch (Throwable e) { |
| 605 | + throw new IOException(e); |
| 606 | + } |
| 607 | + } |
| 608 | + } |
| 609 | + |
471 | 610 | private static class FileLocker { |
472 | 611 | private final FileChannel lockChannel; |
473 | 612 | private final FileLock locker; |
|
0 commit comments