| 文件 | 最后提交记录 | 最后更新时间 |
|---|---|---|
rpm: implement --whatprovides using provide2pkgnames index Problem: - rpm -q --whatprovides bash returned "No package provides this capability" - The provides field contains versioned strings like "bash = 5.2.37-7.oe2509" - Exact match against capability name "bash" failed Solution: Add select_packages_by_whatprovides() function: - Uses provide2pkgnames.rkyv index for efficient lookup - Falls back to scanning installed packages' provides field - Uses extract_provide_name() to parse capability names from versioned strings Usage: epkg -e openeuler busybox rpm -q --whatprovides bash epkg -e openeuler busybox rpm -q --whatprovides 'libc.so.6()(64bit)' epkg -e openeuler busybox rpm -q --whatprovides '/bin/sh' Signed-off-by: Wu Fengguang <wfg@mail.ustc.edu.cn> | 3 个月前 | |
download: show Downloaded -> Unpacking -> Unpacked status for packages Problem/Purpose: The download progress bar only showed "Downloaded" status, without indicating the subsequent unpack phase. Users want to see the full progress through download and unpack. Solution: - Add DownloadFlags::PACKAGE flag to identify package downloads - For PACKAGE downloads: keep progress bar alive after download ends - Show "Downloaded" at download completion - Show "Unpacking" before unpack starts - Show "Unpacked" after unpack completes - For non-PACKAGE downloads (repo metadata, wget): finish immediately with "Downloaded" status Changes: - types.rs: add PACKAGE flag - orchestration.rs: check PACKAGE flag to decide progress bar behavior - install.rs: pass URL through unpack flow, update status messages - manager.rs: add finish_download_task_message() public function Signed-off-by: Wu Fengguang <wfg@mail.ustc.edu.cn> | 3 个月前 | |
libkrun: split build_kernel_args() from build_libkrun_config() Problem/Purpose: build_libkrun_config() was too long (~273 lines), making it hard to read and maintain. The kernel cmdline building logic was the largest block (~165 lines) with multiple platform-specific branches. Solution: - Extract kernel cmdline building logic into build_kernel_args() - New function handles: base cmdline, user args, init_cmd, RUST_LOG, TSI, host OS env vars, init_pwd, virtiofs mounts - build_libkrun_config() now calls build_kernel_args() and focuses on vsock mode setup, guest command, kernel path/format Result: - build_libkrun_config(): 273 lines → ~110 lines - build_kernel_args(): ~170 lines (new) - Cleaner function separation, easier to navigate and modify Signed-off-by: Wu Fengguang <wfg@mail.ustc.edu.cn> | 2 个月前 | |
busybox: use symlink_for_native() for host CLI tools Problem: Busybox applets like cp, ln, tar run on the host system. On Windows, cp.exe/ln.exe should create native Windows symlinks, not virtiofs symlinks (which are only meaningful inside VM). Solution: Change all busybox applets to use symlink_for_native(): - cp.rs: cp -s creates symbolic links - ln.rs: ln -s creates symbolic links - tar.rs: tar extraction creates symlinks - update_alternatives.rs: manages alternative symlinks - systemd_tmpfiles.rs: creates tmpfiles symlinks - deb_systemd_helper.rs: manages systemd unit symlinks - lua/lposix.rs: posix.symlink() Lua binding On Unix, symlink_for_native() is equivalent to symlink(). On Windows, it creates native Windows symlinks (or hardlink/copy fallback). Signed-off-by: Wu Fengguang <wfg@mail.ustc.edu.cn> | 4 个月前 | |
link: fix Windows symlink type detection mismatch Problem: - Symlink 'usr/local/share/man -> ../man' was created as FILE symlink on Windows, but target '../man' is a directory - is_directory_symlink() returned false due to missing FILE_ATTRIBUTE_DIRECTORY flag in the symlink's file attributes - Root cause: tar_extract.rs used entry.unpack() for symlinks which creates them with wrong type on Windows Solution: 1. tar_extract.rs: Add Windows-specific symlink handling in extract_archive_with_policy(): - Collect symlinks during first pass - After collecting all directories, create symlinks with correct type - Use normalize_path_components() to resolve '..' in symlink target paths - Check both package directories and env_root for cross-package symlinks 2. lfs.rs: Add normalize_path_components() to resolve '.' and '..' path components without requiring the path to exist (unlike canonicalize()) 3. link.rs: Remove fallback logic that masked the root cause 4. mirror/url.rs: Move url2legal_filename() here from utils.rs for better locality (only used in URL path conversion) 5. environment.rs: Fix path separator issue in find_command_in_registered_envs() by normalizing bin_dir paths to use backslashes on Windows The key insight is that on Windows: - symlink_dir sets FILE_ATTRIBUTE_DIRECTORY (0x10) - symlink_file does NOT set this flag - The tar crate's entry.unpack() doesn't consider symlink target type Signed-off-by: Wu Fengguang <wfg@mail.ustc.edu.cn> | 4 个月前 | |
resolve: quiet a warning [DEBUG src/resolve/provider.rs:368] [RESOLVO] lookup_packages_by_capability('golang-vet') returning 0 total packages [DEBUG src/resolve/provider.rs:212] [RESOLVO] Capability lookup: skipping arch filtering for 'golang-vet' (keeping 0 packages) [WARN src/resolve/requirement.rs:114] [RESOLVO] Package/capability 'golang-vet' not found, skipping (ignore_missing=true) Signed-off-by: Wu Fengguang <wfg@mail.ustc.edu.cn> | 4 个月前 | |
libkrun: pass terminal size to guest PTY on command start Problem: - libkrun's build_command_request() was missing terminal field - Guest PTY started with default size (24x80) instead of host terminal size - $LINES/$COLUMNS showed wrong values in guest shell - vm/client.rs used Term::size() which returns default (24,80) when unavailable Solution: - Add terminal field to build_command_request() in libkrun/stream.rs - Use Term::size_checked() instead of Term::size() to avoid default values - Only set terminal when actual size can be determined from TTY Files changed: - src/libkrun/stream.rs: add terminal field for PTY mode - src/vm/client.rs: use size_checked() to avoid defaults Signed-off-by: Wu Fengguang <wfg@mail.ustc.edu.cn> | 3 个月前 | |
Dependency resolution major rework epkg install --assume-no now uses 'world + resolvo' to work out install plan, the new code is well tested for major distros. Bump version to 0.2.1 Major Features ============== Constraint-Aware Dependency Resolution - **Introduced Alpine 'world' concenpt for top level package specs with version constraints** (e.g., pkgname=version, pkgname>=version, pkgname~=version) - **Implemented resolvo-based dependency solver** that works across all package formats Local Package and URL Support - **Support for local package files** (.rpm, .deb, .apk, .epkg, .conda, .pkg.tar.xz, .pkg.tar.zst, .tar.bz2) - **Support for remote package URLs** (http://, https://, file://) with automatic downloading Testing Infrastructure - **Data-driven test framework** for dependency solver using YAML/JSON test definitions (cargo test solver_tests::tests -- --nocapture) - **Batch package dependency testing script** (test_depends.py) with random package selection and configurable --os/--batch - **Added --assume-no option** for automatic negative prompts in testing scenarios Dependency Parsing Improvements =============================== Operator Parsing Fixes - **Fixed operator parsing order** in parse_operator_from_start() - now checks multi-character operators (>=, <=, ==) before single-character ones (>, <, =) - **Fixed ">>" and "==" parsing** - now correctly parsed instead of being split into ">" and "=" RPM "with" Operator Support - **Added support for RPM "with" operator** to combine multiple constraints on the same package - **Support for parenthesized and unparenthesized "with" clauses** - **Support for OR expressions in "with" clause left side** with De Morgan's law expansion - **Refactored parse_rpm_requires()** into smaller, focused helper functions Conditional Dependencies - **Fixed "if" operator parsing** in conditional dependencies with proper parentheses handling - **Fixed "and" operator handling** in conditional dependencies by normalizing to commas - **Improved conditional dependency resolution** - only checks installed/selected packages, not repository availability Architecture and Capability Parsing - **Support for RPM architecture specifications** in capability names (e.g., wine-cms(x86-32), wine-cms(x86-64)) - **Arch Linux library alias parsing** - distinguishes between version constraints and library aliases (e.g., libfoo=1.0.0 vs libstk-5.0.0.so=libstk-5.0.0.so-64) - **Support for capability tags** (pkgconfig, crate, python3.13dist, etc.) with proper parsing Version Comparison Fixes ======================== Debian Version Comparison - **Fixed Debian revision comparison** for strings with letters (e.g., "0ubuntu8.6" vs "0") - **Fixed parsing of versions with dashes in revision** (e.g., "1.48.0-2-3") - **Fixed alphabetic revision parsing** (e.g., "2.14.14-z" correctly parsed with revision "z") - **Fixed version comparison when string ends with ~** (tilde has lowest precedence) - **Fixed pre-release detection** - only recognized markers (rc, beta, alpha, pre, dev, snapshot) treated as pre-releases - **Strip local version suffixes** for VersionEqual constraints (everything after last '+') - **Strip trailing ~ suffix** for >= and > constraints in Debian format - **Improved trailing ~ handling** for versions with revisions RPM Version Comparison - **Fixed RPM version comparison** for numeric vs alphabetic segments (numbers > letters in RPM) - **Fixed semantic version comparison** - now compares segment-by-segment instead of concatenating - **Fixed revision comparison** - versions with release > versions without release (e.g., "12.0.0-bp160.1.2" > "12.0.0") - **Fixed version matching** for packages with letter-prefixed build numbers (e.g., "9.8.3-bp160.1.34") - **Compare upstream-only when operand has no release/revision** for RPM/Pacman/Apk formats - **Fixed explicit revision "0" handling** - distinguishes between no revision and explicit "0" APK/Alpine Version Comparison - **Fixed APK explicit revision comparison** (e.g., "6.0-r0" < "6.0") - **Fixed VersionCompatible operator** for APK format to ignore revision suffixes - **Fixed dot-separated pre-release version comparison** (e.g., "48.4" > "48.alpha") Wildcard and Pattern Matching - **Implemented VersionEqualStar and VersionNotEqualStar** wildcard matching (e.g., "6.9.*") - **Support for conda version patterns** (e.g., "3.13" matches "3.13.*" in conda format) - **Support for conda 3-part version matching** and underscore handling Dependency Resolution Improvements ================================== Provider Resolution - **Fixed provider constraint checking** - now checks what providers provide, not provider package version - **Filter provider packages by constraints** before version selection - **Try all providers when constraints fail** - fixed to always check mmio providers - **Fixed provider resolution for version-constrained capabilities** (e.g., "php-composer(doctrine/cache) >= 1.6") - **Split Pacman format provides fields** by whitespace for proper SONAME matching - **Split Alpine pkgconfig provides** by whitespace for multiple provides in single string - **Centralized provides parsing** with format-aware version stripping Version Constraint Checking - **Fixed OR condition handling** in version constraint checking - detects mutually exclusive constraints - **Improved OR group detection** for constraints with different operands - **Fixed VersionGreaterThan constraint with ~~ suffix** for versions with revisions - **Implemented VersionCompatible operator** constraint checking - **Refactored constraint checking logic** to shared functions in version.rs - **Ignore RPM constraints with unexpanded macros** (pattern %{xxx}) Conditional Dependencies - **Fixed conditional dependency resolution** - only checks installed/selected packages, not repository - **Proper handling of skipped conditional dependencies** - marks OR groups as satisfied when all are conditional and none satisfied Direct Package Lookup - **Fixed direct package lookup with version constraints** - filters packages before selecting highest version - **Support for package specs with version constraints** in install command Package Format Support ====================== Conda Support - **Added conda repository support** with noarch repos - **Fixed noarch handling** - set arch to "all" at creation time - **Custom deserializer for noarch field** (handles string and boolean values) - **Improved error logging** throughout conda processing pipeline Arch Linux/Pacman - **Added more repositories** for Arch Linux - **Added zstd decoder support** for .tar.zst files - **Skip usr/lib64 symlink** during package installation to prevent overwriting environment symlink - **Fixed library alias parsing** in requires and provides Alpine/APK - **Parse conflicts from requires field** - conflicts with '!' prefix now properly separated - **Fixed pkgconfig dependency resolution** with version constraints for multiple provides Debian/Ubuntu - **Fixed Debian provide version parsing** with parentheses format (e.g., "libgcc1 (= 1:14.2.0-19)") - **Handle ~~ suffix in version constraints** for Debian Rust packages - **Support for python3:any** capability resolution RPM/OpenSUSE/Fedora/OpenEuler - **Fixed RPM provides parsing** with spaces (e.g., ">= 1.0" format) - **Support for RPM files provides** and font provides - **Support for ksym(default:__SCT__cond_resched) provides** - **Fixed openeuler provides parsing** Error Handling and Logging ========================== Logging Improvements - **Improved error logging** in conda processing with full error chains - **Added debug logging** for provider lookup and constraint checking Bug Fixes ========= Dependency Resolution - **Fixed panic when package obsoleted during dependency resolution** - check if package exists before updating dependencies - **Improved version selection** - prefer packages that don't obsolete existing packages - **Fixed self-obsoletes handling** - flip to VersionNotEqual for self-obsoletes - **De-duplicate provides** to avoid redundant entries - **Remove duplicate pkgkeys** from same package in dependency collection Package Indexing - **Fixed last package not saved to index** in RPM repository processing - call on_new_paragraph() in finalize() - **Filter out trivial entries** from provide2pkgnames.yaml (where key == value) - **Fixed cache path collision** for URLs with same filename - include host and path in cache path Package Installation - **Fixed checksum file path resolution** for downloaded files - use resolve_mirror_path() consistently - **Fixed "Text file busy" errors** during package installation Miscellaneous - **Support for pkgkeys like __glibc__2.41__x86_64** - treat leading __ as part of pkgname - **Fixed epkg info command** - support pkgkey format (pkgname__version__arch) for exact package lookup - **Fixed test framework** - ensure constraints test runs even if simple test fails - **Added path-based filtering** to solver tests Documentation and Tests ======================= - **Added comprehensive test cases** for all major features - **Port initial tests** from apk-tools/test/solver/ - **Added test whitelist** for known working packages - **Improved test organization** with data-driven test framework Signed-off-by: Wu Fengguang <wfg@mail.ustc.edu.cn> | 8 个月前 | |
lfs: enable case sensitivity in pkg unpack and link places Purpose: Ensure case-sensitive semantics for all directories that handle Linux package files (rpm/deb/apk/conda). Solution: Convert create_dir_all() to create_dir_all_with_case_sensitivity() in these locations: - store.rs: final_dir parent directory - link.rs: mirror_dir and mirror_symlink_file - tar_extract.rs: extract_archive_with_policy, extract_archive, unpack_tar_archive, create_package_dirs - deb_pkg.rs: extract_tar - rpm_pkg.rs: extract_rpm_files - apk_pkg.rs: create_apk_trigger_hook - conda_pkg.rs: extract_zstd_tar_stream - aur.rs: extract_aur_source Behavior: Case sensitivity is inherited by subdirectories on Windows. Setting it on parent dirs (env_root, store_tmp_dir, epkg_store) propagates to all child directories created after. Signed-off-by: Wu Fengguang <wfg@mail.ustc.edu.cn> | 4 个月前 | |
apk_repo: fix gzip stream detection by checking full magic header Root Cause Analysis: - find_next_gzip_stream() only checked 2-byte magic (0x1f 0x8b) - This falsely matched bytes at position 214 (0xd6) inside first gzip stream - The bytes "1f 8b 91 8f" at 0xd6 are compressed data, not a gzip header - Real second gzip stream starts at position 751 (0x2ef) with "1f 8b 08" - gzip format requires 3-byte header: ID1=0x1f, ID2=0x8b, CM=0x08 (deflate) - Third byte CM must be 0x08 for valid deflate compression method - Without CM check, any 1f 8b sequence in compressed data triggers false match Solution: - Check all 3 bytes of gzip magic: 0x1f 0x8b 0x08 - Adjust loop bounds to ensure 3 bytes available for comparison Fixes: epkg -e alpine list fails with "invalid gzip header" when processing Alpine APKINDEX.tar.gz concatenated streams | 3 个月前 | |
dirs: centralize path_join and env_root path helpers Add path_join(base, parts) in dirs.rs: one Path::join per path segment so Windows does not mix separators when code used to do .join("a/b"). Use a generic base type P: AsRef<Path> + ?Sized so callers can pass &Path, &PathBuf, etc.; owned PathBuf must be passed as &path_buf at call sites. Introduce/consolidate env_root_* helpers (e.g. env_root_channel_yaml(), env_root_etc_epkg(), env_root_repos_d(), env_root_env_yaml()) and switch call sites away from stringly join chains. Sweep package/store/install/hooks paths to use path_join or component-wise joins (apk/arch/brew/conda/deb/rpm pipelines, busybox, mirror, main, etc.). brew_service: import crate::lfs on Linux; fix Object(obj) / service name bindings for platform-specific branches. init: import PathBuf for paths that construct PathBuf explicitly. Signed-off-by: Wu Fengguang <wfg@mail.ustc.edu.cn> | 4 个月前 | |
dirs: centralize path_join and env_root path helpers Add path_join(base, parts) in dirs.rs: one Path::join per path segment so Windows does not mix separators when code used to do .join("a/b"). Use a generic base type P: AsRef<Path> + ?Sized so callers can pass &Path, &PathBuf, etc.; owned PathBuf must be passed as &path_buf at call sites. Introduce/consolidate env_root_* helpers (e.g. env_root_channel_yaml(), env_root_etc_epkg(), env_root_repos_d(), env_root_env_yaml()) and switch call sites away from stringly join chains. Sweep package/store/install/hooks paths to use path_join or component-wise joins (apk/arch/brew/conda/deb/rpm pipelines, busybox, mirror, main, etc.). brew_service: import crate::lfs on Linux; fix Object(obj) / service name bindings for platform-specific branches. init: import PathBuf for paths that construct PathBuf explicitly. Signed-off-by: Wu Fengguang <wfg@mail.ustc.edu.cn> | 4 个月前 | |
lfs: fix .exists() and fs::metadata() in arch_repo.rs Problem: - Inconsistent use of .exists() and std::fs::metadata() without clear semantics - Host paths (repo_dir, download_path) should use lfs functions Solution: - Add use crate::lfs import - Replace filelists_path.exists() with lfs::exists_on_host() - Replace revise.download_path.exists() with lfs::exists_on_host() - Replace std::fs::metadata(&revise.download_path) with lfs::metadata_on_host() File: - arch_repo.rs: Arch Linux repo processing (host paths) Signed-off-by: Wu Fengguang <wfg@mail.ustc.edu.cn> | 4 个月前 | |
lfs: enable case sensitivity in pkg unpack and link places Purpose: Ensure case-sensitive semantics for all directories that handle Linux package files (rpm/deb/apk/conda). Solution: Convert create_dir_all() to create_dir_all_with_case_sensitivity() in these locations: - store.rs: final_dir parent directory - link.rs: mirror_dir and mirror_symlink_file - tar_extract.rs: extract_archive_with_policy, extract_archive, unpack_tar_archive, create_package_dirs - deb_pkg.rs: extract_tar - rpm_pkg.rs: extract_rpm_files - apk_pkg.rs: create_apk_trigger_hook - conda_pkg.rs: extract_zstd_tar_stream - aur.rs: extract_aur_source Behavior: Case sensitivity is inherited by subdirectories on Windows. Setting it on parent dirs (env_root, store_tmp_dir, epkg_store) propagates to all child directories created after. Signed-off-by: Wu Fengguang <wfg@mail.ustc.edu.cn> | 4 个月前 | |
build: fix all compiler warnings for cross-macos build Problem: - cross-macos build (macOS x86_64 without libkrun feature) generates multiple unused import/variable/function/dead_code warnings Fixes: make cross-macos ARCH=x86_64 warning: unused imports: VmConfig, register_vm_session, unregister_vm_session warning: unreachable expression warning: unused variable: guest_cmd_path warning: variable does not need to be mutable warning: function hash_env_root is never used warning: constant MAX_ID is never used warning: function auto_idmap_specs is never used ... Signed-off-by: Wu Fengguan <wfg@mail.ustc.edu.cn> | 3 个月前 | |
brew_pkg: rewrite dylib ID to opt/ path (like vanilla brew) Problem: install_name_tool failed with headerpad overflow for gcc dylibs because epkg resolved @@HOMEBREW_PREFIX@@/opt/gcc to Cellar path which is longer than placeholder. Analysis of vanilla brew behavior: - Placeholder: @@HOMEBREW_PREFIX@@/opt/gcc/lib/libfoo.dylib (58 chars) - Vanilla brew: /opt/homebrew/opt/gcc/lib/libfoo.dylib (52 chars) - SHORTER - epkg (before): /opt/homebrew/Cellar/gcc/15.2.0_1/lib/... (64 chars) - LONGER The Cellar path is 6 chars longer than placeholder, causing overflow. Vanilla brew uses opt/ path (via symlinks), which is 6 chars shorter. Solution: For @@HOMEBREW_PREFIX@@ placeholders, resolve to env_root/opt/ directly instead of Cellar path. This matches vanilla brew behavior and produces shorter paths that fit in MachO header. Also fix scipy test to install python before running numpy/scipy tests. Signed-off-by: Wu Fengguang <wfg@mail.ustc.edu.cn> | 3 个月前 | |
brew: run post_install inside namespace for correct path resolution Problem: - SSL certificate path mismatch: openssl@3/cert.pem symlink pointed to wrong path because post_install ran on pure HOST OS without namespace - cert.pem was created at HOST path instead of env_root path - Symlink paths like '.LB/etc/ca-certificates/cert.pem' couldn't resolve outside namespace where .LB doesn exist Root Cause Analysis: - post_install was executed with Command::new() on HOST OS directly - No namespace bind mount, so Ruby's file operations went to HOST paths - Other Linux distros' scriptlets (rpm/deb/apt) always run inside namespace - brew post_install should follow same pattern for path consistency Solution: - Use fork_and_execute() instead of Command::new() to run inside namespace - Pass RunOptions with env_vars for HOMEBREW_PREFIX/CELLAR/LIBRARY - Bind mount ensures /home/linuxbrew/.LB -> env_root, paths resolve correctly - cert.pem created at correct location, symlinks work inside namespace Test: epkg --env dev-brew run curl https://curl.se/ca/cacert.pem succeeds with cert.pem correctly created at env_root/etc/ca-certificates/ Signed-off-by: Wu Fengguang <wfg@mail.ustc.edu.cn> | 3 个月前 | |
brew_repo: add uses_from_macos_bounds field to BrewFormula The uses_from_macos_bounds field contains version requirements: - {} - always available on macOS (no version requirement) - {"since": "sequoia"} - only available on macOS 15 or later For Linux bottles, bounds don't matter since Linux doesn't have macOS system libs. All uses_from_macos entries become recommended deps. This field is parsed for completeness and future macOS bottle support. Signed-off-by: Wu Fengguang <wfg@mail.ustc.edu.cn> | 3 个月前 | |
dirs: centralize path_join and env_root path helpers Add path_join(base, parts) in dirs.rs: one Path::join per path segment so Windows does not mix separators when code used to do .join("a/b"). Use a generic base type P: AsRef<Path> + ?Sized so callers can pass &Path, &PathBuf, etc.; owned PathBuf must be passed as &path_buf at call sites. Introduce/consolidate env_root_* helpers (e.g. env_root_channel_yaml(), env_root_etc_epkg(), env_root_repos_d(), env_root_env_yaml()) and switch call sites away from stringly join chains. Sweep package/store/install/hooks paths to use path_join or component-wise joins (apk/arch/brew/conda/deb/rpm pipelines, busybox, mirror, main, etc.). brew_service: import crate::lfs on Linux; fix Object(obj) / service name bindings for platform-specific branches. init: import PathBuf for paths that construct PathBuf explicitly. Signed-off-by: Wu Fengguang <wfg@mail.ustc.edu.cn> | 4 个月前 | |
conda_link: fix Move link type handling for conda packages Problem/Purpose/Background: - For conda packages, link_conda_package() was ignoring plan.link and only using path_type from paths.json for link type decisions - When plan.link=Move (intended for Windows for space efficiency), files with path_type="file" were incorrectly using Reflink (copy) instead of Move, preventing proper store consumption tracking - consumed.json marker was never created for conda packages because link_package_generic() was bypassed, causing: 1. Store appears unconsumed even after files are moved 2. GC might incorrectly remove packages that appear unused 3. Other environments may try to reuse consumed stores Solution: - In link_conda_package(): check if plan.link==Move and handle properly: 1. Call handle_move_link_type() to check if store already consumed 2. Create consumed.json marker before linking files (same pattern as link_package_generic) - In link_file_without_prefix_replacement(): respect plan.link when path_type doesn't specify hardlink/softlink: 1. path_type="hardlink" + can_hardlink → Hardlink (unchanged) 2. path_type="softlink" + can_symlink → Symlink (unchanged) 3. path_type="file" or fallback → use plan.link (Move/Hardlink/etc.) - Make handle_move_link_type() public for reuse in conda_link.rs Usage/Examples: - Windows conda packages with plan.link=Move: 1. Files with path_type="file" are moved from store to env 2. consumed.json is created to mark the store as consumed 3. info/ directory remains with filelist.txt and consumed.json Signed-off-by: Wu Fengguang <wfg@mail.ustc.edu.cn> | 3 个月前 | |
lfs: enable case sensitivity in pkg unpack and link places Purpose: Ensure case-sensitive semantics for all directories that handle Linux package files (rpm/deb/apk/conda). Solution: Convert create_dir_all() to create_dir_all_with_case_sensitivity() in these locations: - store.rs: final_dir parent directory - link.rs: mirror_dir and mirror_symlink_file - tar_extract.rs: extract_archive_with_policy, extract_archive, unpack_tar_archive, create_package_dirs - deb_pkg.rs: extract_tar - rpm_pkg.rs: extract_rpm_files - apk_pkg.rs: create_apk_trigger_hook - conda_pkg.rs: extract_zstd_tar_stream - aur.rs: extract_aur_source Behavior: Case sensitivity is inherited by subdirectories on Windows. Setting it on parent dirs (env_root, store_tmp_dir, epkg_store) propagates to all child directories created after. Signed-off-by: Wu Fengguang <wfg@mail.ustc.edu.cn> | 4 个月前 | |
mmio: add shared serialize_all_indices() for code reuse Problem: - serialize_all_indices in packages_stream.rs and serialize_indices in conda_repo.rs were nearly identical, both calling the same three mmio::serialize_* functions Solution: - Add shared serialize_all_indices() to mmio.rs - Update conda_repo.rs to use mmio::serialize_all_indices() - Inline serialize_all_indices() call in packages_stream.rs::on_finish() - Remove now-redundant serialize_indices() from conda_repo.rs Code reuse achieved: - Both modules now use the same mmio::serialize_all_indices() function - Eliminated ~25 lines of duplicate code Signed-off-by: Wu Fengguang <wfg@mail.ustc.edu.cn> | 3 个月前 | |
refactor: split large modules into focused, single-responsibility modules Major module reorganization to improve maintainability and code organization: Split install.rs (4409 lines) into: - plan.rs: InstallationPlan structure and planning logic - steps.rs: Installation step execution (scriptlets, triggers, hooks) - link.rs: Package file linking (hardlink, symlink, move, runpath) - expose.rs: Package exposure (ebin wrapper creation) - risks.rs: Transaction risk management (renamed from transaction.rs) Split depends.rs (2972 lines) into: - version_constraint.rs: Version constraint checking and validation (renamed from constraint.rs) - provides.rs: Package provides checking and capability matching - conda_requires.rs: Conda-style requirement parsing Split version.rs into: - parse_version.rs: PackageVersion struct and parsing - version_compare.rs: Version comparison routines (renamed from version.rs) - version_constraint.rs: Version constraint checking (moved from constraint.rs) Split parse_requires.rs (2298 lines) into: - conda_requires.rs: Conda requirement parsing - rpm_requires.rs: RPM requirement parsing - parse_requires.rs: Core requirement parsing (reduced) Created new focused modules: - package.rs: Package line/key utilities and filtering - package_cache.rs: Package cache management and indexing - world.rs: World file state management - store.rs: Package store operations - deb_triggers.rs: Debian trigger support Module renames: - transaction.rs -> risks.rs (better reflects RPM transaction risk management) - version.rs -> version_compare.rs (clearer purpose) - constraint.rs -> version_constraint.rs (more descriptive) All modules now have focused responsibilities and clear boundaries, making the codebase easier to navigate and maintain. Signed-off-by: Wu Fengguang <wfg@mail.ustc.edu.cn> | 6 个月前 | |
store, tar_extract, deb_pkg: optimize case sensitivity inheritance Problem: - create_dir_all_with_case_sensitivity() was called on every temp directory - On Windows, setting case sensitivity requires admin privileges - Repeated calls were failing with "Access denied" errors Solution: - Set case sensitivity once on parent directory (unpack/) - Child directories inherit case sensitivity automatically on NTFS - Use simple create_dir_all() for child directories Signed-off-by: Wu Fengguang <wfg@mail.ustc.edu.cn> | 4 个月前 | |
deb_repo: fix by-hash URL path separator on Windows Problem: URL paths like /noble/universe/binary-amd64\by-hash/SHA256/... showed \ instead of / before by-hash on Windows. Root Cause: Path::join() uses OS-specific separator (\ on Windows). When constructing URL paths, the code used: Path::new(&location).parent().unwrap().join(format!("by-hash/...")) This created main/binary-amd64\by-hash/... instead of the correct main/binary-amd64/by-hash/.... Solution: Use string formatting with explicit forward slashes instead of Path::join() for URL path construction. URLs must always use /. Fixes: HTTP 404 errors when downloading Ubuntu repo metadata on Windows. Signed-off-by: Wu Fengguang <wfg@mail.ustc.edu.cn> | 4 个月前 | |
dirs: centralize path_join and env_root path helpers Add path_join(base, parts) in dirs.rs: one Path::join per path segment so Windows does not mix separators when code used to do .join("a/b"). Use a generic base type P: AsRef<Path> + ?Sized so callers can pass &Path, &PathBuf, etc.; owned PathBuf must be passed as &path_buf at call sites. Introduce/consolidate env_root_* helpers (e.g. env_root_channel_yaml(), env_root_etc_epkg(), env_root_repos_d(), env_root_env_yaml()) and switch call sites away from stringly join chains. Sweep package/store/install/hooks paths to use path_join or component-wise joins (apk/arch/brew/conda/deb/rpm pipelines, busybox, mirror, main, etc.). brew_service: import crate::lfs on Linux; fix Object(obj) / service name bindings for platform-specific branches. init: import PathBuf for paths that construct PathBuf explicitly. Signed-off-by: Wu Fengguang <wfg@mail.ustc.edu.cn> | 4 个月前 | |
cross-platform: Linux-format unpack/repos on host; Windows tool CMD shims - store::general_unpack_package(): Deb/Rpm/Apk (and Epkg under Unix) no longer gated on target_os=linux so native epkg can unpack Linux packages on macOS/Windows per VM/scriptlet plan. - repo::sync_from_release_metadata() and process_data(): same for Release/repomd and process_packages_content() paths. - io::load_system_repositories() and merge_channel_defaults_into_repos(): deb/rpm system sources and components merge on all targets. - scriptlets: setup_deb_env_vars(), setup_rpm_env_vars(), setup_apk_env_vars() for all targets (VM runs same maintainer environment). - install: dpkg_db generation when package_format is Deb without linux-only cfg; environment: ensure_triggers_dir() for Deb on any Unix. - epkg::unpack_package() available on all Unix (module remains cfg(unix)). - build.rs: busybox dpkg_trigger moves LINUX_ONLY -> UNIX_ONLY so activate_trigger() is linked on macOS. - main.rs: tool_wrapper always built; risks/gc stay cfg(unix). plan.rs: FilesystemInfo free_space/free_inodes cfg_attr for Windows. rpm_pkg: path normalization for permissions on non-Unix; utils::set_permissions_from_mode() used from create_tool_wrapper(). deb_triggers: cfg(unix) on ensure_triggers_dir() and activate_trigger(). ntfs_ea: allow(dead_code) on included stub. - tool_wrapper: normalize backslashes in detect_installed_tools(); Windows copies templates from assets/tool/cmd_shims/*.cmd (python/ruby/posix_shell) to {tool}.cmd using %~dp0%~n0; get_wrapper_content() falls back to shell-wrapper.sh; docs updated. Signed-off-by: Wu Fengguang <wfg@mail.ustc.edu.cn> | 4 个月前 | |
deinit: detect already-removed state to prevent repeated "remove" output Problem: When running epkg self remove on an already-removed installation, the command would still display "Deinitialization completed successfully" even though nothing was actually done. This happened because shell RC files were added to the plan based solely on file existence, not on whether they contained epkg initialization code. Root Cause: - collect_user_personal_plan() and collect_global_deinit_plan() added shell RC files to plan.shell_rc_files if the files merely existed - plan.is_empty() would return false due to non-empty shell_rc_files - This caused execute_deinit_with_plan() to display the completion message even when no epkg content was present to remove Solution: - Add rc_file_has_epkg() helper function to check for both "# epkg begin" and "# epkg end" markers in RC files - Apply this check when collecting Unix shell RC files and Windows PowerShell profiles for both user and global scopes - Now plan.is_empty() returns true when no actual epkg content exists, and the early return properly skips output Fixes: repeated epkg self remove --assume-yes showing "Deinitialization completed" when already removed Signed-off-by: Wu Fengguang <wfg@mail.ustc.edu.cn> | 3 个月前 | |
depends: downgrade dependency resolution fallback warning to debug Problem: - When installing packages, a warning appeared: "Dependency resolution failed with RECOMMENDS/SUGGESTS: ... Retrying with REQUIRES only." - This warning appeared frequently during normal package installations Root Cause: - The solver tries with RECOMMENDS/SUGGESTS first for better dependency resolution - If that fails (e.g., some recommended packages not available), it falls back to REQUIRES only - This fallback is intentional and expected behavior, not a warning condition Solution: - Downgrade the log level from warn! to debug! since this is a normal fallback behavior - The operation continues successfully after the fallback Fixes: frequent warning during conda package installs on Windows | 4 个月前 | |
vm: document pipe signaling flow and fix epkg_run path for cross-namespace visibility Problem/Purpose: - Session files created in mount namespace were invisible to parent - Unclear why pipe signaling is used vs session file polling - Unclear why QEMU needs pipe but libkrun doesn't Solution: - Change epkg_run from XDG_RUNTIME_DIR to ~/.epkg/run - home_epkg is bind-mounted, ensuring cross-namespace visibility - Child creates session at env_root/home/wfg/.epkg/run/vm-sessions/ - Parent sees it at ~/.epkg/run/vm-sessions/ via bind mount - Document full pipe signaling flow: - Pipe: timing coordination (parent waits for READY) - Session file: discovery (subsequent runs find existing VM) - libkrun: no namespace fork, registers session directly Files changed: - src/dirs.rs: epkg_run always uses ~/.epkg/run - src/models.rs: vm_daemon_ready_fd flow documentation - src/qemu.rs: pipe signaling vs session file explanation - tests/sandbox/test-vm-sandbox.sh: update get_epkg_run_dir() Signed-off-by: Wu Fengguang <wfg@mail.ustc.edu.cn> | 3 个月前 | |
lfs: rename vague symlink_to_file/directory() to explicit symlink_file/dir_for_virtiofs() Problem: The function names symlink_to_file() and symlink_to_directory() are vague - they don't clearly indicate whether they create native Windows symlinks or virtiofs/Linux guest symlinks. Solution: - Rename symlink_to_file() -> symlink_file_for_virtiofs() - Rename symlink_to_directory() -> symlink_dir_for_virtiofs() - Add symlink_file_for_native() / symlink_dir_for_native() for Windows host access - Add symlink_for_virtiofs() for generic symlinks (type detected at runtime) - Add symlink_for_native() for generic native symlinks - Update all callers to use explicit function names based on use case: Virtiofs callers (env symlinks for VM): - environment.rs: env symlinks (bin->usr/bin, lib->usr/lib, etc.) - link.rs: package symlinks - tar_extract.rs: extracted symlinks - expose.rs: node_modules, libexec, interpreter symlinks - dpkg_db.rs: dpkg info symlinks - rpm_pkg.rs: RPM extraction symlinks - repo.rs: filelists symlinks - qemu.rs: log symlinks - mount.rs: device symlinks - utils.rs: scriptlet symlinks - xdesktop.rs: desktop file symlinks - busybox/*: applet, alternative, tar, cp, ln, tmpfiles symlinks Native callers (Windows host access): - init.rs: self-install symlinks (epkg binary, kernel, lib64) - tool_wrapper.rs: config symlinks for mirrors - busybox/mod.rs: Windows target applet symlinks The explicit naming makes it clear at each call site whether the symlink is intended for native Windows access or virtiofs/Linux guest visibility. Signed-off-by: Wu Fengguang <wfg@mail.ustc.edu.cn> | 4 个月前 | |
environment: set mode 755 for public environments to allow traversal Problem/Purpose: - Public environments should be accessible by other users - When running in VM mode, the env root becomes the filesystem root - Mode 700 on root directory blocks all non-root users from traversing Solution: - For public environments (env_config.public = true), set mode 755 - This allows other users to enter and traverse the environment directory - Private environments still use mode 700 for security Note: This fix complements the virtiofs root inode mode override in git/libkrun passthrough.rs which handles VM root filesystem traversal. Signed-off-by: Wu Fengguang <wfg@mail.ustc.edu.cn> | 2 个月前 | |
init: fix Windows junction detection and add --force option Problem: - On Windows, junctions (directory reparse points) are not detected by std::fs::metadata.file_type().is_symlink() - This caused setup_epkg_src() to skip creating junction when an incomplete directory existed - 'epkg repo list' returned empty on Windows due to missing assets/repos Solution: - Add lfs::is_symlink() for regular symlink detection - Add lfs::is_symlink_or_junction() for directory link detection on Windows - Junction behaves as directory symlink, but is not detected by is_symlink() - Modify setup_epkg_src() to remove existing directory before creating fresh symlink/junction - Add --force option to 'self install' to allow complete reinstallation Changes: - lfs::is_symlink(): Detect only true symlinks (efficient for files) - lfs::is_symlink_or_junction(): Detect symlinks and Windows junctions (for directory links created by lfs::symlink()) - setup_epkg_src(): Use is_symlink_or_junction() for epkg source directory - install_epkg_with_force(): New function that removes old self env when --force is specified - 'self install --force': Force reinstall epkg source and binaries Files updated to use is_symlink_or_junction() for directory links: - init.rs: epkg_src, epkg_src_symlink - gc.rs: directory size calculation - environment.rs: env_base symlink check - link.rs: usr-merge symlink detection Files kept with is_symlink() for file links: - utils.rs, xdesktop.rs, store.rs, conda_link.rs, download/task.rs, rpm_verify.rs, expose.rs, epkg.rs Root Cause Analysis: - Windows junctions are reparse points with FILE_ATTRIBUTE_REPARSE_POINT but std::fs detects them as directories, not symlinks - lfs::symlink() creates junctions for directories on Windows, which were then not detected by is_symlink() on subsequent runs Fixes: 'epkg repo list' returning empty on Windows Signed-off-by: Wu Fengguang <wfg@mail.ustc.edu.cn> | 4 个月前 | |
expose: add missing #[cfg(unix)] guards for MachO handling Root Cause Analysis: - FileType::MachO case (src/expose.rs:700) called Unix-only functions: - crate::run::is_brew_environment() - gated by #[cfg(unix)] in src/run.rs:1296 - crate::brew_pkg::prefix::preferred() - brew_pkg mod is #[cfg(unix)] in src/main.rs:127 - create_binary_wrapper() - uses Unix-specific exec syscall - These functions are not compiled on Windows target, causing E0433/E0425 errors - Homebrew is Unix-specific (macOS/Linux), so MachO handling is irrelevant on Windows Solution: - Add #[cfg(unix)] guard to FileType::MachO match arm - Add #[cfg(not(unix))] fallback that returns Ok(None) for non-Unix platforms - Add #[cfg(unix)] guard to create_binary_wrapper() to suppress dead_code warning Fixes: make cross-windows error[E0433]: failed to resolve: could not find brew_pkg in the crate root --> src/expose.rs:704:46 error[E0425]: cannot find function is_brew_environment in module crate::run --> src/expose.rs:703:28 Signed-off-by: Wu Fengguang <wfg@mail.ustc.edu.cn> | 3 个月前 | |
gc: skip size calculation for instant display Problem: - gc display_gc_plan() was very slow (minutes) with 600+ packages - It recursively calculated directory sizes for each package - This was unnecessary overhead just to show a plan Solution: - Remove total_size() and get_dir_size() functions - Only show directory counts, not total sizes - gc display is now instant (0.01s instead of minutes) Signed-off-by: Wu Fengguang <wfg@mail.ustc.edu.cn> | 3 个月前 | |
hash: replace sha1 to xor Requirement: to reduce dependency, we want replace sha1 step in the b32encode() code to a compaction step, which will compress the sha256-output-len string into sha1-output-len string, so that the base32 output len will remain the the same(32 bytes). Please propose possible alternatives compress ways first. Proposed Compression Alternatives - Truncation: Simply truncate the SHA256 hash to the first 20 bytes. Pros: Extremely simple and fast. Cons: Reduces the hash space, which may increase the risk of collisions. rust --------------------------------------------------- let truncated_hash = &sha256_hash[..20]; --------------------------------------------------- - XOR Folding: Split the SHA256 hash into two 16-byte halves and XOR them together to produce a 16-byte result. Pad the result to 20 bytes if necessary. Pros: Preserves more entropy than truncation. Cons: Slightly more complex. rust --------------------------------------------------- let half1 = &sha256_hash[..16]; let half2 = &sha256_hash[16..32]; let folded_hash: Vec<u8> = half1.iter().zip(half2.iter()).map(|(a, b)| a ^ b).collect(); --------------------------------------------------- - Modular Arithmetic: Treat the SHA256 hash as a large integer and reduce it modulo a prime number close to 21602160 (the size of SHA1's output space). Pros: Mathematically sound and preserves uniformity. Cons: Computationally expensive. rust --------------------------------------------------- use num_bigint::BigUint; use num_traits::Num; let hash_int = BigUint::from_str_radix(&sha256_hash, 16)?; let prime = BigUint::from_bytes_be(&[0xFF; 20]); // Example prime let compressed_hash = (hash_int % prime).to_bytes_be(); --------------------------------------------------- - Hash-Based Compression: Use a lightweight hash function (e.g., BLAKE3, CRC32) to hash the SHA256 hash into a smaller size. Pros: Preserves entropy and is fast. Cons: Adds dependency on another hash function. rust --------------------------------------------------- use blake3::Hasher; let mut hasher = Hasher::new(); hasher.update(sha256_hash.as_bytes()); let compressed_hash = hasher.finalize().as_bytes()[..20].to_vec(); - Base Conversion: Convert the SHA256 hash to a higher base (e.g., base64) and truncate to the desired length. Pros: Simple and preserves some entropy. Cons: May not be as secure as other methods. rust --------------------------------------------------- use base64::encode; let base64_hash = encode(sha256_hash.as_bytes()); let compressed_hash = &base64_hash[..20]; --------------------------------------------------- Recommended Approach: XOR Folding XOR folding is a good balance between simplicity, performance, and entropy preservation. It ensures that all bits of the SHA256 hash contribute to the final compressed hash, reducing the risk of collisions compared to truncation. Feature Rust Implementation Python Implementation ========================================================================================== SHA256 Hashing sha2::Sha256 hashlib.sha256() XOR Folding Manual byte manipulation Manual byte manipulation Base32 Encoding base32::encode base64.b32encode() Lowercase Output .to_lowercase() .lower() Dependencies crate (sha2, base32) Standard library (hashlib, base64) Signed-off-by: Wu Fengguang <wfg@mail.ustc.edu.cn> | 1 年前 | |
fix build warns Signed-off-by: Wu Fengguang <wfg@mail.ustc.edu.cn> | 4 个月前 | |
history: add comments explaining restore generation semantics Problem: - restore semantics may be confusing: it works on content not gen number - gen and current always increment by 1 after each operation Solution: - Add doc comments to rollback_history() explaining: - Generation 0 is empty state created by env create - Each install/restore creates new generation (current_id + 1) - Restore only works on content, not generation number - restore 0 restores to empty state - Add doc comment to create_new_generation_with_root() Signed-off-by: Wu Fengguang <wfg@mail.ustc.edu.cn> | 3 个月前 | |
hooks: pass matched targets as args for APK hooks, not stdin Root Cause Analysis: - APK hooks with NeedsTargets expect matched file paths as command-line arguments - The trigger script uses "$@" to iterate over matched paths: "for i in "$@"; do" - epkg incorrectly passed matched targets via stdin (Pacman-style) - Trigger script ignored stdin, never received paths → empty loop → no action Solution: - Check package_format == PackageFormat::Apk for hooks with NeedsTargets - APK: pass matched_targets as command-line arguments (extend args) - Pacman: pass matched_targets via stdin (existing behavior) - This aligns with APK trigger behavior: /bin/busybox sh for "$@" iteration Fixes: APK apk-trigger.hook didn't execute busybox applet symlinks creation Trigger received no paths, "do_bb_install=yes" never triggered Signed-off-by: Wu Fengguang <wfg@mail.ustc.edu.cn> | 3 个月前 | |
idmap: attempt to map UID/GID range 0-65535 before falling back to single ID Problem: - systemd_tmpfiles chown fails with EINVAL when target GID (e.g., 997) is not mapped - execute_simple_idmap_for_pid only mapped UID/GID 0 to host values Solution: - Try to map full ID range 0-65535 first: uid_map: "0 <host_uid> 1\n1 1 65535" gid_map: "0 <host_gid> 1\n1 1 65535" - Falls back to single ID mapping if full range fails (normal for unprivileged users) - When /etc/subuid/subgid is configured, full range mapping will succeed Note: Without subuid/subgid, unprivileged users can only map their own UID/GID, so the fallback is expected behavior. This change enables full range mapping when subuid/subgid is properly configured. Signed-off-by: Wu Fengguang <wfg@mail.ustc.edu.cn> | 3 个月前 | |
repo: simplify should_refresh_release_file() This commit refactors the repository index checking logic to be simpler and more efficient by: 1. Remove the NeedConvert release state and associated logic These paths containing "Official" are wrong, hard to determine at this early stage, and not necessary: should_refresh_release_file: RepoIndex.json does not exist: /home/wfg/.cache/epkg/channels/debian-trixie/Official/x86_64/RepoIndex.json, returning NeedConvert should_refresh_release_file: RepoIndex.json does not exist: /home/wfg/.cache/epkg/channels/debian-trixie/Official-updates/x86_64/RepoIndex.json, returning NeedConvert should_refresh_release_file: RepoIndex.json does not exist: /home/wfg/.cache/epkg/channels/debian-trixie/Official-security/x86_64/RepoIndex.json, returning NeedConvert 2. Eliminate RepoIndex.json existence checking - Removed complex & wrong checks for RepoIndex.json file existence and age - Instead, rely on download timestamps and .etag.json files to determine freshness 3. Simplify RepoReleaseItem initialization - Use ..Default::default() for cleaner struct initialization - Remove hardcoded empty values for hash_type, hash, and size fields - Streamline field initialization across different creation sites 4. Consolidate repository freshness checking - Replaced multiple file existence checks with single has_recent_download() function - Function checks both the main file and its .etag.json counterpart - Reduces file system I/O operations 5. Clean up repository processing logic - Removed unused touch_file_mtime() function - Simplified sync_from_package_database() by removing need_convert logic - Made struct definitions more maintainable with Default derivation Signed-off-by: Wu Fengguang <wfg@mail.ustc.edu.cn> | 5 个月前 | |
busybox: add apk/apt/apt-get/dnf/yum package manager compatibility shims Add Linux-only busybox applets that mimic common package manager command interfaces, reusing epkg's existing backend functions: - apk: Alpine apk compatibility (add, del, info, list, search, update, upgrade) - apt/apt-get: Debian/Ubuntu apt compatibility (install, remove, purge, update, upgrade, show, list, search) - dnf/yum: Fedora/RHEL dnf compatibility (install, remove, info, list, search, update, upgrade, provides) Commands are implemented as clap subcommands matching real package manager CLI. Backend calls epkg functions directly: - install_packages() / remove_packages() / upgrade_packages() - search::search_repo_cache() with SearchOptions - info::show_package_info() / list::list_packages_with_scope() show_package_info() and other backend functions handle their own initialization (sync_channel_metadata, load_installed_packages), simplifying all callers. All applets are LINUX_ONLY since they target Linux distribution package workflows. Signed-off-by: Wu Fengguang <wfg@mail.ustc.edu.cn> | 3 个月前 | |
init: fix missing init file in self environment on Windows/macOS Root Cause: - create_epkg_symlink() in environment.rs creates init for VM environments but skips the self environment (line 564) - setup_common_binaries() in init.rs installed epkg-linux-$arch for Windows but didn't create the init file - update_all_env_hardlinks() returned early on non-Linux without updating any environments The init file at usr/bin/init is required for VM boot (kernel cmdline init=/usr/bin/init). Without it, Linux distros in VM panic with: "Kernel panic - not syncing: Requested init /usr/bin/init failed" Changes: 1. setup_common_binaries(): Create init file in self environment on Windows/macOS by hardlinking (or copying) epkg-linux-$arch 2. update_all_env_hardlinks(): Remove early return on Windows/macOS, now properly copies/hardlinks epkg and init to all environments Both places use hardlink first (for space efficiency), falling back to copy if hardlink fails (e.g., across filesystems). Signed-off-by: Wu Fengguang <wfg@mail.ustc.edu.cn> | 3 个月前 | |
install: skip exposure for packages with missing store directory Problem/Purpose: When user runs epkg install python on a package that's already installed but whose store directory is empty (consumed by LinkType::Move or deleted), the system attempts to expose the package and fails with "filelist.txt not found". Root Cause Analysis: - Facts: 1. Store directory C:\Users\aa\.epkg\store\ is completely empty 2. installed-packages.json has pkgline record for python 3. When epkg install python runs, python goes into skipped_reinstalls 4. Because user explicitly requested it, ebin_exposure=true is set 5. expose_packages() tries to expose skipped_reinstalls with ebin_exposure=true 6. expose_package() fails because filelist.txt doesn't exist in empty store - Logical chain: skipped_reinstalls → ebin_exposure=true → expose_packages() → store_fs_dir empty → expose_package() → filelist.txt not found → ERROR Solution: In expose_packages(), before calling expose_package(), check if the store directory exists and contains filelist.txt. If missing, log a warning and skip exposure instead of failing hard. Fixes: reproduce command epkg -e test-conda --assume-yes install --ignore-missing python Error: 0: Failed to expose package python__3.14.4-h653fc63_100_cp314__x86_64 1: filelist.txt not found at C:\Users\aa\.epkg\store\z7nlswq6uf7y4w6pj5g457vinkzk6wso__python__3.14.4-h653fc63_100_cp314__x86_64\info\filelist.txt Signed-off-by: Wu Fengguang <wfg@mail.ustc.edu.cn> | 3 个月前 | |
io: convert DOS paths to Linux guest paths in deserialize_env_config_for Root Cause Analysis: - When Windows host creates env.yaml, env_root is stored as DOS path: "C:\Users\aa\.epkg\envs\alpine" - In Linux VM guest, virtiofs mounts use Linux path format: /mnt/c/Users/aa/.epkg/envs/alpine - Nested epkg in VM guest reads env.yaml and gets DOS path - DOS path doesn't exist in Linux guest filesystem - Result: panic when trying to access C:\Users\.../etc/epkg/channel.yaml Facts: 1. env.yaml env_root: C:\Users\aa\.epkg\envs\alpine (DOS format) 2. virtiofs mount: C:\Users\aa\.epkg -> /mnt/c/Users/aa/.epkg 3. Guest path should be: /mnt/c/Users/aa/.epkg/envs/alpine 4. Previous fix (89622e67) handled -e NAME matching VM guest env 5. This fix handles all env_config reads at source Solution: - Add convert_dos_path_to_linux_guest() in lfs.rs - Converts DOS paths (C:\...) to Linux guest paths (/mnt/c/...) - Only applies on Linux; on Windows, no conversion needed - Apply conversion in deserialize_env_config_for() at source - This ensures env_config.env_root/env_base are always valid paths - Remove redundant conversions from other locations (dirs.rs, io.rs, environment.rs, path.rs, vm/start.rs, vm/stop.rs) Fixes: nested epkg in VM guest fails with DOS path from env.yaml Signed-off-by: Wu Fengguan <wfg@mail.ustc.edu.cn> | 3 个月前 | |
cargo: update crate versions - Upgrade some depends - Downgrade some others to avoid 2024 edition - cargo build --ignore-rust-version to work with old rustc - Fix build errors Now builds fine in openEuler 24.03-LTS (rustc 1.82). Signed-off-by: Wu Fengguang <wfg@mail.ustc.edu.cn> | 5 个月前 | |
tool_wrapper: move config from ~/.config/epkg to ~/.epkg/config Problem/Purpose: - Tool config at ~/.config/epkg/tool required separate virtiofs mount - ~/.epkg is already mounted in VM guest, so config under it needs no extra mount Solution: 1. Change config path from ~/.config/epkg/tool to ~/.epkg/config/tool 2. Update all wrapper scripts to use new path with EPKG_HOME support 3. Remove separate tool_config mount in libkrun/core.rs 4. Update docs and comments for new path Benefits: - Eliminates one virtiofs mount (virtiofs has limited device slots) - Config is under home_epkg which is already mounted - Simpler code, no special handling needed Files changed: - src/tool_wrapper.rs: get_tool_config_dir() path change - assets/tool/wrappers/*: config path + EPKG_HOME fallback - src/libkrun/core.rs: remove tool_config mount code - docs: update path references - src/models.rs, src/run.rs, CHANGELOG.md: comment updates Signed-off-by: Wu Fengguang <wfg@mail.ustc.edu.cn> | 3 个月前 | |
conda_link: fix Move link type handling for conda packages Problem/Purpose/Background: - For conda packages, link_conda_package() was ignoring plan.link and only using path_type from paths.json for link type decisions - When plan.link=Move (intended for Windows for space efficiency), files with path_type="file" were incorrectly using Reflink (copy) instead of Move, preventing proper store consumption tracking - consumed.json marker was never created for conda packages because link_package_generic() was bypassed, causing: 1. Store appears unconsumed even after files are moved 2. GC might incorrectly remove packages that appear unused 3. Other environments may try to reuse consumed stores Solution: - In link_conda_package(): check if plan.link==Move and handle properly: 1. Call handle_move_link_type() to check if store already consumed 2. Create consumed.json marker before linking files (same pattern as link_package_generic) - In link_file_without_prefix_replacement(): respect plan.link when path_type doesn't specify hardlink/softlink: 1. path_type="hardlink" + can_hardlink → Hardlink (unchanged) 2. path_type="softlink" + can_symlink → Symlink (unchanged) 3. path_type="file" or fallback → use plan.link (Move/Hardlink/etc.) - Make handle_move_link_type() public for reuse in conda_link.rs Usage/Examples: - Windows conda packages with plan.link=Move: 1. Files with path_type="file" are moved from store to env 2. consumed.json is created to mark the store as consumed 3. info/ directory remains with filelist.txt and consumed.json Signed-off-by: Wu Fengguang <wfg@mail.ustc.edu.cn> | 3 个月前 | |
list: fix Size column width from 8 to 9 Problem: Size column was too narrow (width=8) for values like "185.24 KB" or "307.18 KB" which have 9 characters, causing misalignment. Solution: Increase Size column width from 8 to 9 to accommodate the maximum format_size() output length: "xxx.xx KB/MB/GB" (9 chars). Fixes: epkg -e test-conda list --installed shows misaligned Size column | 3 个月前 | |
dirs: centralize path_join and env_root path helpers Add path_join(base, parts) in dirs.rs: one Path::join per path segment so Windows does not mix separators when code used to do .join("a/b"). Use a generic base type P: AsRef<Path> + ?Sized so callers can pass &Path, &PathBuf, etc.; owned PathBuf must be passed as &path_buf at call sites. Introduce/consolidate env_root_* helpers (e.g. env_root_channel_yaml(), env_root_etc_epkg(), env_root_repos_d(), env_root_env_yaml()) and switch call sites away from stringly join chains. Sweep package/store/install/hooks paths to use path_join or component-wise joins (apk/arch/brew/conda/deb/rpm pipelines, busybox, mirror, main, etc.). brew_service: import crate::lfs on Linux; fix Object(obj) / service name bindings for platform-specific branches. init: import PathBuf for paths that construct PathBuf explicitly. Signed-off-by: Wu Fengguang <wfg@mail.ustc.edu.cn> | 4 个月前 | |
main: compile mount_specs only for linux or libkrun Root Cause Analysis The mount_specs module is referenced only from namespace.rs (Linux-only) and libkrun/core.rs (libkrun feature). Cross-target macOS x86_64 builds omit both, so every public function in mount_specs.rs was dead code and rustc emitted dead_code warnings. Solution Gate mod mount_specs with cfg(any(target_os = "linux", feature = "libkrun")) so the crate is built only when a caller exists. Fixes - reproduce: make cross-macos ARCH=x86_64 - warning: function build_vm_mount_policy() is never used (and seven similar dead_code warnings from mount_specs.rs) Signed-off-by: Wu Fengguang <wfg@mail.ustc.edu.cn> | 2 个月前 | |
mmio: add shared serialize_all_indices() for code reuse Problem: - serialize_all_indices in packages_stream.rs and serialize_indices in conda_repo.rs were nearly identical, both calling the same three mmio::serialize_* functions Solution: - Add shared serialize_all_indices() to mmio.rs - Update conda_repo.rs to use mmio::serialize_all_indices() - Inline serialize_all_indices() call in packages_stream.rs::on_finish() - Remove now-redundant serialize_indices() from conda_repo.rs Code reuse achieved: - Both modules now use the same mmio::serialize_all_indices() function - Eliminated ~25 lines of duplicate code Signed-off-by: Wu Fengguang <wfg@mail.ustc.edu.cn> | 3 个月前 | |
build: fix Windows cross-compile warnings - Add #[cfg(not(target_os = "windows"))] to shared_store variable definition in build_virtiofs_mount_specs(), since it's only used in the non-Windows code block - Add #[cfg(target_os = "linux")] to ConfigFlags struct and apply_config_flags() function, since they are only used by Linux-only apt/apk/dnf modules - Add #[cfg(not(target_os = "windows"))] to config import in build_virtiofs_mount_specs() to match conditional usage Signed-off-by: Wu Fengguang <wfg@mail.ustc.edu.cn> | 3 个月前 | |
mount: fix bind mount target creation for Env mode paths within env_root Problem: When using 'epkg run --mount /tmp/test-dir' in Env mode, bind mount failed with ENOENT because the target path (e.g., ~/.epkg/envs/.../tmp/test-dir) was not created. The ensure_bind_target_exists() function incorrectly skipped ALL Env mode cases, not just arbitrary host paths. Root Cause Analysis: - ensure_bind_target_exists() lacked env_root parameter - For Env mode, it returned early without checking if target is within env_root - Target paths like ~/.epkg/envs/dev-alpine/tmp/... ARE in env_root - They should be treated like Fs/Vm mode (safe to create), not skipped Solution: - Pass env_root to ensure_bind_target_exists() - For Env mode, check if target.starts_with(env_root) - If within env_root, proceed to create placeholder (like Fs/Vm modes) - If outside env_root, skip (preserve original safety behavior) Fixes: epkg -e dev-alpine run --mount /tmp/epkg-test-ruby -- ruby -e puts 1+1 Failed in child setup: Failed to bind mount /tmp/epkg-test-ruby -> /home/wfg/.epkg/envs/dev-alpine/tmp/epkg-test-ruby: ENOENT: No such file or directory Signed-off-by: Wu Fengguang <wfg@mail.ustc.edu.cn> | 2 个月前 | |
mount_specs: fix VM cwd mount target to use @ prefix for env_root Problem: --io=tty mode failed with "No such file or directory" when executing commands, while --io=stream mode worked (but with wrong cwd=/). Root Cause Analysis: 1. build_vm_mount_policy() generated cwd mount spec as "/c/epkg://c/epkg" 2. The "//" prefix caused target to be parsed as absolute host path "/c/epkg" (not inside env_root/rootfs) 3. Bind mount executed: /c/epkg -> /c/epkg (host namespace path) 4. VM guest accesses rootfs via virtiofs, which only sees rootfs contents and NOT the namespace bind mounts to host paths outside rootfs 5. Result: VM guest cannot see /c/epkg directory, causing exec failure Evidence from df output inside VM: /c/epkg was NOT mounted, only rootfs and standard mounts (/dev, /tmp, /opt/epkg) were visible. Solution: Change target format from "//{cwd}" to "@{cwd}" so that: - "@/c/epkg" is parsed as env_root.join("c/epkg") - Target becomes /home/wfg/.epkg/envs/test-vm-sandbox/c/epkg - Directory created inside rootfs - Bind mount: /c/epkg -> env_root/c/epkg (inside rootfs) - VM guest sees /c/epkg via virtiofs Fixes: epkg -e test-vm-sandbox run --isolate=vm --io=tty bash -c 'sleep 1; echo hi' Error: Failed to execute command: No such file or directory (os error 2) Signed-off-by: Wu Fengguang <wfg@mail.ustc.edu.cn> | 3 个月前 | |
mtree: fix UTF-8 corruption in escape/unescape functions Root Cause Analysis: - unescape_mtree_path() iterated over UTF-8 bytes instead of characters - Line "result.push(bytes[i] as char)" treated each UTF-8 byte as a char - U+F03A (UTF-8: 0xEF 0xB8 0xBA) was corrupted to U+00EF, U+00B8, U+00BA - escape_mtree_path() used "ch as u8" which truncates multi-byte chars - U+F03A was not properly escaped, causing it to be written to filelist.txt - When read back, the UTF-8 bytes were misinterpreted as individual chars Solution: - Changed unescape_mtree_path() to iterate over chars, not bytes - Changed escape_mtree_path() to preserve multi-byte Unicode (codepoints > 0x7F) - Only escape ASCII control characters (0x00-0x1F) and DEL (0x7F) - Multi-byte Unicode characters like U+F03A are now preserved correctly Fixes: Text::CharWidth.3pm.gz files with PUA-encoded ':' characters failing LX symlink creation with error -2147020504 Signed-off-by: Wu Fengguang <wfg@mail.ustc.edu.cn> | 4 个月前 | |
libkrun: use pre-namespace host UID/GID for auto_idmap Problem/Purpose: - epkg creates user namespace before launching VM - Inside namespace, UID becomes 0 (root) - auto_idmap used get_current_uid() which returned 0 - should_auto_map(0) returns false, no squashing applied - Files created in VM appeared with wrong ownership on host Solution: - Capture host_uid AND host_gid in namespace.rs before namespace setup - Pass them via RunOptions to libkrun/core.rs - Use run_options.host_uid/host_gid instead of get_current_uid/get_current_gid - Add debug logging for idmap decision process Fixes: UID/GID mapping when running VM from within user namespace Signed-off-by: Wu Fengguang <wfg@mail.ustc.edu.cn> | 2 个月前 | |
package: use '#' not '~' for ':' in pkgline/pkgkey paths Debian upstream versions may contain '~' (sorting / pre-releases), so replacing ':' with '~' for Windows-safe filenames could corrupt round-trips. RPM also gives semantic meaning to '~' and '^' in EVR segments (rpm-version(7)), so those are unsuitable as escapes. Introduce VERSION_COLON_FILENAME_ESC ('#') with version_for_filename() and version_from_filename(). Document RPM/Debian/Alpine/Conda constraints and why '%' was not chosen (cmd.exe %NAME%, noisy in list descriptions). parse_pkgkey() and pkgkey2version() now decode the version with version_from_filename(), matching parse_pkgline(), so EVR with epoch colon round-trips after format_pkgkey(). Signed-off-by: Wu Fengguang <wfg@mail.ustc.edu.cn> | 4 个月前 | |
risks: fix disk space estimation for files/dirs/links Problem: Disk space estimation was underestimating by up to 15.9% due to three issues in validate_file_conflicts() and validate_before_linking(): 1. Directory detection was broken - checking ends_with('/') on MtreeFileInfo.path which doesn't have trailing slashes, so all entries were counted as files (dir_count was always 0). 2. Symlinks were incorrectly counted in file_count for block alignment overhead. Symlinks don't occupy data blocks - only directory entries. For packages with many symlinks (e.g., breeze-icons with 24K links), this caused significant overestimation of block overhead. 3. info/ directory overhead was underestimated. The fixed 16KB per package assumption was wrong for packages with many files. filelist.txt can be several MB for packages like breeze-icons (4.4 MB for 40K entries). Solution: 1. Use MtreeFileInfo.is_dir()/is_link() methods instead of path string checking for accurate type detection. 2. Return (file_count, dir_count, link_count) tuple to separately track regular files, directories, and symlinks. Only regular files get block alignment overhead. 3. Calculate info/ overhead based on entry count: - Base overhead: 2 KB (package.txt + other metadata) - filelist.txt: ~80 bytes per entry Results: - Estimation error reduced from 15.9% to ~1-3% - Test: breeze-icons (39847 entries, 15610 files, 24091 links) now shows accurate estimation Signed-off-by: Wu Fengguang <wfg@mail.ustc.edu.cn> | 3 个月前 | |
mmio: add shared serialize_all_indices() for code reuse Problem: - serialize_all_indices in packages_stream.rs and serialize_indices in conda_repo.rs were nearly identical, both calling the same three mmio::serialize_* functions Solution: - Add shared serialize_all_indices() to mmio.rs - Update conda_repo.rs to use mmio::serialize_all_indices() - Inline serialize_all_indices() call in packages_stream.rs::on_finish() - Remove now-redundant serialize_indices() from conda_repo.rs Code reuse achieved: - Both modules now use the same mmio::serialize_all_indices() function - Eliminated ~25 lines of duplicate code Signed-off-by: Wu Fengguang <wfg@mail.ustc.edu.cn> | 3 个月前 | |
rpm: split handle_with_operator() and parse_provides / provides helpers rpm_requires: add parse_with_clause_segment() and merge_or_parts_same_pkg() to deduplicate OR-segment parsing for "with" dependencies. parse_provides: split per-format parsers (APK/Pacman, Deb, RPM item + rpm token paren helper, whitespace fallback). provides: extract provide_remainder_has_ignored_comparison_operators() and single_provide_entry_satisfies() from check_provider_satisfies_constraints(). Signed-off-by: Wu Fengguang <wfg@mail.ustc.edu.cn> | 4 个月前 | |
brew: add dylib path rewriting and dependency resolution Problem: - Homebrew bottles contain placeholder dylib paths like @@HOMEBREW_CELLAR@@ and @@HOMEBREW_PREFIX@@ that need to be rewritten for binaries to work on epkg - Brew dependencies were not being parsed, causing packages like oniguruma (jq's dependency) to not be installed Solution: - brew_pkg.rs: add rewrite_dylib_paths() to rewrite Homebrew placeholder paths to @loader_path/../lib/ relative paths - Uses otool -L to detect placeholder paths - Uses install_name_tool -change to rewrite paths - Scans all Mach-O files in fs/bin and fs/lib directories - link.rs: call rewrite_dylib_paths() after linking brew packages - run.rs: set DYLD_LIBRARY_PATH to env/lib for brew packages, allowing dyld to find dependent libraries from other packages - parse_requires.rs: add parse_brew_requires() to parse simple comma-separated brew dependency strings Test: - ENV_NAME=test-brew EPKG_BIN=./target/debug/epkg \ ./tests/cross-platform/channels/brew.sh - jq and oniguruma install and run correctly Signed-off-by: Wu Fengguang <wfg@mail.ustc.edu.cn> | 4 个月前 | |
refactor: split large modules into focused, single-responsibility modules Major module reorganization to improve maintainability and code organization: Split install.rs (4409 lines) into: - plan.rs: InstallationPlan structure and planning logic - steps.rs: Installation step execution (scriptlets, triggers, hooks) - link.rs: Package file linking (hardlink, symlink, move, runpath) - expose.rs: Package exposure (ebin wrapper creation) - risks.rs: Transaction risk management (renamed from transaction.rs) Split depends.rs (2972 lines) into: - version_constraint.rs: Version constraint checking and validation (renamed from constraint.rs) - provides.rs: Package provides checking and capability matching - conda_requires.rs: Conda-style requirement parsing Split version.rs into: - parse_version.rs: PackageVersion struct and parsing - version_compare.rs: Version comparison routines (renamed from version.rs) - version_constraint.rs: Version constraint checking (moved from constraint.rs) Split parse_requires.rs (2298 lines) into: - conda_requires.rs: Conda requirement parsing - rpm_requires.rs: RPM requirement parsing - parse_requires.rs: Core requirement parsing (reduced) Created new focused modules: - package.rs: Package line/key utilities and filtering - package_cache.rs: Package cache management and indexing - world.rs: World file state management - store.rs: Package store operations - deb_triggers.rs: Debian trigger support Module renames: - transaction.rs -> risks.rs (better reflects RPM transaction risk management) - version.rs -> version_compare.rs (clearer purpose) - constraint.rs -> version_constraint.rs (more descriptive) All modules now have focused responsibilities and clear boundaries, making the codebase easier to navigate and maintain. Signed-off-by: Wu Fengguang <wfg@mail.ustc.edu.cn> | 6 个月前 | |
io: convert DOS paths to Linux guest paths in deserialize_env_config_for Root Cause Analysis: - When Windows host creates env.yaml, env_root is stored as DOS path: "C:\Users\aa\.epkg\envs\alpine" - In Linux VM guest, virtiofs mounts use Linux path format: /mnt/c/Users/aa/.epkg/envs/alpine - Nested epkg in VM guest reads env.yaml and gets DOS path - DOS path doesn't exist in Linux guest filesystem - Result: panic when trying to access C:\Users\.../etc/epkg/channel.yaml Facts: 1. env.yaml env_root: C:\Users\aa\.epkg\envs\alpine (DOS format) 2. virtiofs mount: C:\Users\aa\.epkg -> /mnt/c/Users/aa/.epkg 3. Guest path should be: /mnt/c/Users/aa/.epkg/envs/alpine 4. Previous fix (89622e67) handled -e NAME matching VM guest env 5. This fix handles all env_config reads at source Solution: - Add convert_dos_path_to_linux_guest() in lfs.rs - Converts DOS paths (C:\...) to Linux guest paths (/mnt/c/...) - Only applies on Linux; on Windows, no conversion needed - Apply conversion in deserialize_env_config_for() at source - This ensures env_config.env_root/env_base are always valid paths - Remove redundant conversions from other locations (dirs.rs, io.rs, environment.rs, path.rs, vm/start.rs, vm/stop.rs) Fixes: nested epkg in VM guest fails with DOS path from env.yaml Signed-off-by: Wu Fengguan <wfg@mail.ustc.edu.cn> | 3 个月前 | |
risks: move pkgs_in_store to InstallationPlan for reuse Store pkgs_in_store in InstallationPlan instead of computing it separately in identify_pkgs_in_store(). This is filled by fill_pkglines_in_plan() and reused by calculate_plan_sizes() and validate_file_conflicts(). Changes: - Add pkgs_in_store: HashSet<String> to InstallationPlan - Populate it in fill_pkglines_in_plan() when matching packages - Remove identify_pkgs_in_store() function - Update calculate_plan_sizes() and validate_file_conflicts() to use plan.pkgs_in_store Signed-off-by: Wu Fengguang <wfg@mail.ustc.edu.cn> | 3 个月前 | |
repo: repomd XML attr helper; split fork_and_execute_direct PATH; mode_munch helpers rpm_repo: repomd_xml_attr_utf8() for repeated type/href attribute parsing. run: conda_windows_path_env() and msys2_pacman_path_env() (non-Linux Windows). posix: mode_munch_parse_who_bits() and mode_munch_try_octal_prefix(). Signed-off-by: Wu Fengguang <wfg@mail.ustc.edu.cn> | 4 个月前 | |
rpm: split handle_with_operator() and parse_provides / provides helpers rpm_requires: add parse_with_clause_segment() and merge_or_parts_same_pkg() to deduplicate OR-segment parsing for "with" dependencies. parse_provides: split per-format parsers (APK/Pacman, Deb, RPM item + rpm token paren helper, whitespace fallback). provides: extract provide_remainder_has_ignored_comparison_operators() and single_provide_entry_satisfies() from check_provider_satisfies_constraints(). Signed-off-by: Wu Fengguang <wfg@mail.ustc.edu.cn> | 4 个月前 | |
vm: unify lifecycle management with vm_keep_timeout and remove reuse_vm/vm_reuse_connect ## Problem/Purpose The reuse_vm and vm_reuse_connect flags had confusing semantics that made the code hard to understand and maintain. VM lifecycle control was scattered across multiple flags and conditional checks. ## Background/Context Session file is the cross-process coordination mechanism. When a VM session exists, it MUST be reused ("能reuse就必须reuse"). The VM lifecycle is independent from epkg run lifecycle (VM lifecycle >= epkg run). ## Solution Unified VM lifecycle control through vm_keep_timeout (Option<u32>): - None: VM shuts down immediately after command completes - Some(0): VM never times out (persistent) - Some(N) > 0: VM shuts down after N seconds idle Key changes: 1. Changed VmConfig.timeout from u32 to Option<u32> 2. Removed reuse_vm and vm_reuse_connect fields from RunOptions 3. Removed --reuse CLI flag (reuse is now always attempted) 4. Made session file registration UNCONDITIONAL for cross-process discovery 5. Guest daemon decides lifecycle based solely on vm_keep_timeout_secs 6. Removed unused functions: is_vm_reuse_active_for_env, send_command_to_running_qemu_guest ## Usage - epkg run --isolate=vm /bin/ls: VM shuts down immediately (timeout=None) - epkg run --isolate=vm --vm-keep-timeout=30 /bin/ls: VM idle 30s then shutdown - epkg vm start --set timeout=60: VM persistent for 60s idle timeout - epkg vm start --set timeout=0: VM never shuts down until manual stop - Concurrent epkg run automatically discovers and reuses existing session Signed-off-by: Wu Fengguang <wfg@mail.ustc.edu.cn> | 3 个月前 | |
lfs: Win32 PUA path mapping via shared win32_pua_paths.rs Replace the old ~hex filename encoding in lfs.rs with the same WSL/MSYS2-style PUA mapping as libkrun (include! of virtio/fs/windows/win32_pua_paths.rs): sanitize_filename_for_windows(), sanitize_path_for_windows(), decode_*(), and host_path_from_manifest_rel_path() for POSIX manifest strings vs on-disk names. Wire host_path_from_manifest_rel_path() where filelist.txt / mtree paths are joined with store or env roots: mirror_dir(), unlink_package_diff(), unlink_package(), create_ebin_wrappers(), xdesktop helpers, and import_environment_from_file(). Tar/deb/rpm/apk unpack paths use sanitize_path_for_windows() / per-segment sanitization consistently. build.rs: rerun-if-changed for win32_pua_paths.rs (epkg_ntfs_ea path unchanged). Signed-off-by: Wu Fengguang <wfg@mail.ustc.edu.cn> | 4 个月前 | |
repo: enable brew_repo on all platforms, skip brew tests on Windows epkg The brew_repo module only does JSON parsing of formula.json files, which is platform-agnostic. Remove the unnecessary #[cfg(unix)] restrictions to allow brew repository metadata parsing on Windows. For the actual brew package installation (brew_pkg), it remains Unix-only since brew bottles require Unix-specific handling (symlinks, permissions, etc). Also update run.sh to skip brew tests when EPKG_WINDOWS_MODE is set, which happens when running Windows epkg.exe from WSL2 via run-wsl2-windows.sh. Changes: - main.rs: Remove #[cfg(unix)] from brew_repo module - repo.rs: Remove #[cfg(unix)] guards around brew_repo function calls - tests/cross-platform/run.sh: Skip brew when EPKG_WINDOWS_MODE is set Signed-off-by: Wu Fengguang <wfg@mail.ustc.edu.cn> | 3 个月前 | |
risks: allow symlink conflict when targets are identical Problem: Brew GNU packages share symlinks like libexec/gnubin/man -> ../gnuman in multiple packages (e.g., libtool, make). This causes file conflict errors when installing packages that share these common symlinks. Solution: In validate_file_conflicts(), when detecting a symlink conflict: - Check if existing symlink in env has the same target as new symlink - If targets match, allow the conflict (they are equivalent) - Log debug message and continue instead of returning error This allows brew packages to share common GNU symlinks without triggering unnecessary conflict errors. Fixes: elixir installation failing with "File conflict: libexec/gnubin/man" Signed-off-by: Wu Fengguang <wfg@mail.ustc.edu.cn> | 3 个月前 | |
lfs: rename vague symlink_to_file/directory() to explicit symlink_file/dir_for_virtiofs() Problem: The function names symlink_to_file() and symlink_to_directory() are vague - they don't clearly indicate whether they create native Windows symlinks or virtiofs/Linux guest symlinks. Solution: - Rename symlink_to_file() -> symlink_file_for_virtiofs() - Rename symlink_to_directory() -> symlink_dir_for_virtiofs() - Add symlink_file_for_native() / symlink_dir_for_native() for Windows host access - Add symlink_for_virtiofs() for generic symlinks (type detected at runtime) - Add symlink_for_native() for generic native symlinks - Update all callers to use explicit function names based on use case: Virtiofs callers (env symlinks for VM): - environment.rs: env symlinks (bin->usr/bin, lib->usr/lib, etc.) - link.rs: package symlinks - tar_extract.rs: extracted symlinks - expose.rs: node_modules, libexec, interpreter symlinks - dpkg_db.rs: dpkg info symlinks - rpm_pkg.rs: RPM extraction symlinks - repo.rs: filelists symlinks - qemu.rs: log symlinks - mount.rs: device symlinks - utils.rs: scriptlet symlinks - xdesktop.rs: desktop file symlinks - busybox/*: applet, alternative, tar, cp, ln, tmpfiles symlinks Native callers (Windows host access): - init.rs: self-install symlinks (epkg binary, kernel, lib64) - tool_wrapper.rs: config symlinks for mirrors - busybox/mod.rs: Windows target applet symlinks The explicit naming makes it clear at each call site whether the symlink is intended for native Windows access or virtiofs/Linux guest visibility. Signed-off-by: Wu Fengguang <wfg@mail.ustc.edu.cn> | 4 个月前 | |
repo: repomd XML attr helper; split fork_and_execute_direct PATH; mode_munch helpers rpm_repo: repomd_xml_attr_utf8() for repeated type/href attribute parsing. run: conda_windows_path_env() and msys2_pacman_path_env() (non-Linux Windows). posix: mode_munch_parse_who_bits() and mode_munch_try_octal_prefix(). Signed-off-by: Wu Fengguang <wfg@mail.ustc.edu.cn> | 4 个月前 | |
rpm: split handle_with_operator() and parse_provides / provides helpers rpm_requires: add parse_with_clause_segment() and merge_or_parts_same_pkg() to deduplicate OR-segment parsing for "with" dependencies. parse_provides: split per-format parsers (APK/Pacman, Deb, RPM item + rpm token paren helper, whitespace fallback). provides: extract provide_remainder_has_ignored_comparison_operators() and single_provide_entry_satisfies() from check_provider_satisfies_constraints(). Signed-off-by: Wu Fengguang <wfg@mail.ustc.edu.cn> | 4 个月前 | |
dirs: centralize path_join and env_root path helpers Add path_join(base, parts) in dirs.rs: one Path::join per path segment so Windows does not mix separators when code used to do .join("a/b"). Use a generic base type P: AsRef<Path> + ?Sized so callers can pass &Path, &PathBuf, etc.; owned PathBuf must be passed as &path_buf at call sites. Introduce/consolidate env_root_* helpers (e.g. env_root_channel_yaml(), env_root_etc_epkg(), env_root_repos_d(), env_root_env_yaml()) and switch call sites away from stringly join chains. Sweep package/store/install/hooks paths to use path_join or component-wise joins (apk/arch/brew/conda/deb/rpm pipelines, busybox, mirror, main, etc.). brew_service: import crate::lfs on Linux; fix Object(obj) / service name bindings for platform-specific branches. init: import PathBuf for paths that construct PathBuf explicitly. Signed-off-by: Wu Fengguang <wfg@mail.ustc.edu.cn> | 4 个月前 | |
rpm_triggers: add pkg_name and paths to warn logs for better debugging Problem: - WARN logs "Cannot read trigger type tag" and "Missing script for trigger" only showed index numbers, making it hard to identify which package had the problem Solution: - Add pkg_name field to ModernTriggerData struct - Pass package name through extract_trigger_type_strings() function - Update all warn!() calls in process_single_modern_trigger() to include package name and associated trigger paths Before: WARN Cannot read trigger type tag, using default WARN Missing script for trigger index 2, skipping After: WARN Cannot read trigger type tag for package glib2, using default WARN Missing script for trigger index 2 in package info, paths: [/etc/ld.so.conf.d/*.conf], skipping Signed-off-by: Wu Fengguang <wfg@mail.ustc.edu.cn> | 4 个月前 | |
init: fix Windows junction detection and add --force option Problem: - On Windows, junctions (directory reparse points) are not detected by std::fs::metadata.file_type().is_symlink() - This caused setup_epkg_src() to skip creating junction when an incomplete directory existed - 'epkg repo list' returned empty on Windows due to missing assets/repos Solution: - Add lfs::is_symlink() for regular symlink detection - Add lfs::is_symlink_or_junction() for directory link detection on Windows - Junction behaves as directory symlink, but is not detected by is_symlink() - Modify setup_epkg_src() to remove existing directory before creating fresh symlink/junction - Add --force option to 'self install' to allow complete reinstallation Changes: - lfs::is_symlink(): Detect only true symlinks (efficient for files) - lfs::is_symlink_or_junction(): Detect symlinks and Windows junctions (for directory links created by lfs::symlink()) - setup_epkg_src(): Use is_symlink_or_junction() for epkg source directory - install_epkg_with_force(): New function that removes old self env when --force is specified - 'self install --force': Force reinstall epkg source and binaries Files updated to use is_symlink_or_junction() for directory links: - init.rs: epkg_src, epkg_src_symlink - gc.rs: directory size calculation - environment.rs: env_base symlink check - link.rs: usr-merge symlink detection Files kept with is_symlink() for file links: - utils.rs, xdesktop.rs, store.rs, conda_link.rs, download/task.rs, rpm_verify.rs, expose.rs, epkg.rs Root Cause Analysis: - Windows junctions are reparse points with FILE_ATTRIBUTE_REPARSE_POINT but std::fs detects them as directories, not symlinks - lfs::symlink() creates junctions for directories on Windows, which were then not detected by is_symlink() on subsequent runs Fixes: 'epkg repo list' returning empty on Windows Signed-off-by: Wu Fengguang <wfg@mail.ustc.edu.cn> | 4 个月前 | |
libkrun: use pre-namespace host UID/GID for auto_idmap Problem/Purpose: - epkg creates user namespace before launching VM - Inside namespace, UID becomes 0 (root) - auto_idmap used get_current_uid() which returned 0 - should_auto_map(0) returns false, no squashing applied - Files created in VM appeared with wrong ownership on host Solution: - Capture host_uid AND host_gid in namespace.rs before namespace setup - Pass them via RunOptions to libkrun/core.rs - Use run_options.host_uid/host_gid instead of get_current_uid/get_current_gid - Add debug logging for idmap decision process Fixes: UID/GID mapping when running VM from within user namespace Signed-off-by: Wu Fengguang <wfg@mail.ustc.edu.cn> | 2 个月前 | |
scriptlets: fix host_path_to_guest_path() to use path consistency Root Cause Analysis: - host_path_to_guest_path() converted ~/.epkg paths to /root/.epkg - This didn't match virtiofs mount configuration in build_virtiofs_mount_specs() - build_virtiofs_mount_specs() mounts home_epkg at SAME guest path (guest_path = host_path) - Guest VM looked for scriptlet files at wrong path /root/.epkg/... instead of /Users/aa/.epkg/... - Result: "No such file or directory" error when executing scriptlets in VM Solution: - Align host_path_to_guest_path() with virtiofs mount configuration - For paths under env_root: strip prefix to get guest "/" path (env_root mounted at "/") - For paths outside env_root: return SAME host path (virtiofs mounts them at same path) - Follows path consistency rule in docs/zh/architecture/env-path.md Fixes: Alpine package installation in VM mode stalled when executing scriptlets guest_daemon.rs error: "Failed to execute command: No such file or directory" Signed-off-by: Wu Fengguang <wfg@mail.ustc.edu.cn> | 3 个月前 | |
search: add --in, --format, --limit options for package search Purpose: Enhance epkg search to support field-specific matching, custom output formatting, and result limiting for more precise package queries. Solution: - Add --in option: match pattern only in specified comma-separated fields (e.g. --in pkgname,summary). Default: match in all fields. - Add --format option: support dpkg-query style '${field[;width]}' syntax or 'json' for full field output. Default: '${pkgname} - ${summary}' - Add --limit option: limit number of results across all shards Implementation: - search_packages_hashmap(): handles --in/--format/--limit options - search_packages_default(): original simple logic for pkgname+summary - search_packages() dispatches based on options - Parse matched paragraphs into HashMap only when needed - Use global Arc<Mutex<usize>> counter for cross-shard limit enforcement - Handle \t and \n escape sequences in format string Usage examples: epkg search bash --in pkgname # match only in pkgname field epkg search bash --in pkgname,summary # match in pkgname or summary epkg search bash --format '${pkgname}\t${version}' epkg search bash --format json # JSON output with all fields epkg search bash --limit 10 # limit to 10 results Signed-off-by: Wu Fengguang <wfg@mail.ustc.edu.cn> | 3 个月前 | |
Replace fs:: modification calls with lfs:: wrappers in 12 files Convert direct fs:: modification calls in 12 files to use lfs:: wrapper functions. Remove duplicate .context(), .wrap_err_with(), and log::debug/trace calls because lfs:: functions already provide error context and trace logging. Signed-off-by: Wu Fengguang <wfg@mail.ustc.edu.cn> | 5 个月前 | |
mount: add unit tests for parse_mount_spec() covering documented examples Add comprehensive unit tests for parse_mount_spec() covering: - Documented examples from function comment - bind host /usr to sandbox /usr (ro,try) - bind /dev/zero (recursive,silent) - tmpfs mount with mode option - proc filesystem mount - remount read-only operation - Various mount spec forms - env_root substitution (@/path) - pseudo filesystem types (sysfs, devpts, mqueue) - auto-generated source for bind mounts - propagation flags (make-slave, make-rprivate) - JSON format parsing - multiple options combinations - Error cases - relative paths rejection - empty target rejection - invalid syntax handling - Static mount spec constants validation Fixes pre-existing test bug in shebang.rs where test_strip_shebang lacked #[cfg(target_os = "linux")] matching the function's cfg. Signed-off-by: Wu Fengguang <wfg@mail.ustc.edu.cn> | 3 个月前 | |
shell: PowerShell integration and self usr/bin at PATH tail - Add assets/shell/epkg.ps1 (mirror epkg.sh): set EPKG_SHELL=powershell, Invoke-Expression stdout for env path|register|unregister|activate|deactivate|remove, and run epkg env path on load so PATH matches registered envs with self usr/bin last. - init: update_powershell_profile() appends the # epkg begin/end block to Microsoft PowerShell profile paths (~/.config/powershell/ and Windows Documents/PowerShell/); refactor append_epkg_block_to_text_file() shared with bash rc append. - path: append get_env_root(self)/usr/bin once at the end; split inherited PATH with path_sep_os(); print export PATH= or $env:PATH= via shell_emit from EPKG_SHELL. - environment: push_env_var() and deactivate restore scripts are bash or PowerShell; update_path() runs on register/unregister/activate/deactivate on all targets; deactivate file extension .sh or .ps1 from shell_emit::deactivate_script_extension(). - dirs: powershell_profile_paths(); deinit removes the epkg block from existing profiles. - make.sh: copy epkg.ps1 beside epkg.sh for dev self env. Signed-off-by: Wu Fengguang <wfg@mail.ustc.edu.cn> | 4 个月前 | |
dirs: centralize path_join and env_root path helpers Add path_join(base, parts) in dirs.rs: one Path::join per path segment so Windows does not mix separators when code used to do .join("a/b"). Use a generic base type P: AsRef<Path> + ?Sized so callers can pass &Path, &PathBuf, etc.; owned PathBuf must be passed as &path_buf at call sites. Introduce/consolidate env_root_* helpers (e.g. env_root_channel_yaml(), env_root_etc_epkg(), env_root_repos_d(), env_root_env_yaml()) and switch call sites away from stringly join chains. Sweep package/store/install/hooks paths to use path_join or component-wise joins (apk/arch/brew/conda/deb/rpm pipelines, busybox, mirror, main, etc.). brew_service: import crate::lfs on Linux; fix Object(obj) / service name bindings for platform-specific branches. init: import PathBuf for paths that construct PathBuf explicitly. Signed-off-by: Wu Fengguang <wfg@mail.ustc.edu.cn> | 4 个月前 | |
store: return unpacked store path from unpack_package() Purpose: Show the actual unpacked store path in "Unpacked" status message instead of just pkgkey. Solution: - Modify unpack_package() to return (pkgkey, pkgline, store_path) - Update install.rs to use store_path for "Unpacked" message - Update rpm.rs caller to handle new return type Example output change: - Before: "Unpacked gzip__1.14-r2__x86_64" - After: "Unpacked ~/.local/share/epkg/store/gzip__1.14-r2__x86_64" Signed-off-by: Wu Fengguang <wfg@mail.ustc.edu.cn> | 3 个月前 | |
tar_extract: fix self install failure on Windows by avoiding premature env_config() call Root Cause Analysis: - In 'epkg self install' flow, create_environment("self") has not completed yet - extract_tar_gz() calls unpack_tar_archive() with env_root=None - finalize_windows_tar_symlinks() calls get_default_env_root() when env_root_override is None - get_default_env_root() calls env_config() -> deserialize_env_config() - deserialize_env_config() tries to read env.yaml which does not exist yet - This causes the installation to fail with "Environment 'self' may not exist" Solution: - When env_root_override is None in finalize_windows_tar_symlinks(), skip calling get_default_env_root() and use None directly - This avoids triggering env_config() during early initialization Fixes: ./epkg.exe self install Error: C:\\Users\\aa\\.epkg\\envs\\self\\etc\\epkg\\env.yaml: The system cannot find the path specified. (os error 3) Environment 'self' may not exist Signed-off-by: Wu Fengguang <wfg@mail.ustc.edu.cn> | 3 个月前 | |
tool_wrapper: move config from ~/.config/epkg to ~/.epkg/config Problem/Purpose: - Tool config at ~/.config/epkg/tool required separate virtiofs mount - ~/.epkg is already mounted in VM guest, so config under it needs no extra mount Solution: 1. Change config path from ~/.config/epkg/tool to ~/.epkg/config/tool 2. Update all wrapper scripts to use new path with EPKG_HOME support 3. Remove separate tool_config mount in libkrun/core.rs 4. Update docs and comments for new path Benefits: - Eliminates one virtiofs mount (virtiofs has limited device slots) - Config is under home_epkg which is already mounted - Simpler code, no special handling needed Files changed: - src/tool_wrapper.rs: get_tool_config_dir() path change - assets/tool/wrappers/*: config path + EPKG_HOME fallback - src/libkrun/core.rs: remove tool_config mount code - docs: update path references - src/models.rs, src/run.rs, CHANGELOG.md: comment updates Signed-off-by: Wu Fengguang <wfg@mail.ustc.edu.cn> | 3 个月前 | |
transaction: fix duplicate PostTransaction hook execution Root Cause Analysis: - run_hooks(PostTransaction) was called twice: 1. In run_transaction_batch() at line 321 2. In end_transaction() at line 224 - This caused apk-trigger.hook to execute twice, printing duplicate "Running hook" messages - Duplicate execution wasted time and could cause inconsistent state Solution: - Remove the duplicate call in run_transaction_batch() - Keep the call in end_transaction() which handles both PostUnTrans and PostTransaction hooks - end_transaction() is the proper place for transaction-end hooks following RPM order: %posttrans → %postuntrans → PostUnTrans → PostTransaction Fixes: Duplicate "Running hook: apk-trigger.hook" output during Alpine package installation Signed-off-by: Wu Fengguang <wfg@mail.ustc.edu.cn> | 3 个月前 | |
rpm_triggers: complete RPM trigger system and core improvements User-Facing Features - RPM triggers fully implemented: File, package, and transaction triggers now convert to Arch-style .hook files - Priority-based execution: High priority triggers run before postin/preun, low priority after - Enhanced package info: RPM packages now show license, vendor, buildHost, signature, and relocation fields - Better dependency display: RPM dependency constraints use proper operators (<=, >=, <, >, =) - Improved search: Absolute paths like /usr/bin/ls work with relative filelist entries - Streamlined workflows: Local files/URLs handled consistently across install/upgrade commands Fixes & Enhancements - Search path handling: Leading / automatically stripped for Deb/Pacman filelists - Scriptlet execution: Lua scriptlets now run with proper namespace isolation and chdir_to_env_root - Dependency resolution: Channel metadata syncs before resolution for consistent behavior - Redundant operations removed: Unnecessary privilege dropping calls eliminated from CLI commands - Hook file improvements: Unique names with priority encoding, ScriptOrder preserves trigger sequence Design & Architecture - Package initialization standardized: Default trait used consistently across conda_pkg, rpm_pkg, mmio - Trigger extraction unified: Modern (array-based) and legacy formats handled through common interface - Search refactored: Pattern preprocessing centralized in setup_u8_pattern() - CLI flow streamlined: Command execution follows parse → prepare → execute pattern - RPM field mapping: PACKAGE_KEY_MAPPING expanded with direct equality mappings - Dead code cleanup: Unused functions and imports removed Technical Improvements - RPM dependency formatting: Optimized with dependency_flags_to_operator() and macro-based field addition - Hook parsing enhanced: Better error messages, ScriptOrder support added - Script extension detection: Improved interpreter detection with duplicate program element removal - File trigger path processing: Split comma-separated paths, deduplication, and union fallback logic - Metadata extraction: Robust handling of RPM tag arrays (string, u32, i32 formats) - Transaction trigger support: transfiletrigger* types with proper PostTransaction/PreTransaction timing Performance - Reduced allocations: Reusable functions minimize string operations in trigger processing - Optimized dependencies: Macro generates field addition code for consistent RPM dependency handling - Memory efficiency: Default trait initialization avoids redundant field setting Signed-off-by: Wu Fengguang <wfg@mail.ustc.edu.cn> | 5 个月前 | |
build: fix macOS cross-compile (unix helpers, sync) - Move validate_user_group_name() to userdb so groupadd and systemd-sysusers do not depend on the Linux-only systemd_sysusers module. - Compile remove_user_from_group() on all targets; implementation is file-based and needed by deluser on macOS. - Expose resolve_vm_cpus() and resolve_vm_memory_mib() for libkrun on Darwin (logic is not Linux-specific). - sync --data: use libc::fsync() on non-Linux where fdatasync is absent. Fixes: make cross-macos failed with unresolved import systemd_sysusers, missing resolve_vm_cpus()/resolve_vm_memory_mib(), missing remove_user_from_group(), and libc::fdatasync on aarch64-apple-darwin. Signed-off-by: Wu Fengguang <wfg@mail.ustc.edu.cn> | 4 个月前 | |
mount: create cross-platform mount spec strings module Problem/Purpose: - On Linux, epkg added 4 redundant virtiofs mounts when bind mounts already made those paths accessible via root virtiofs - Mount policy code was scattered and platform-specific, causing Windows compilation failures - Fs mode also needed mount spec string functions - symlink resolution and coverage checks were duplicated in libkrun/core.rs Background/Rational: - Linux VM mode (both libkrun and qemu): namespace.rs performs bind mounts (home_epkg, cache, opt_epkg, cwd, modules) into env_root, then VM shares entire env_root via single virtiofs/pass-through-fs - macOS/Windows VM mode: no namespace/bind mount support, needs multiple virtiofs mounts for each directory - Mount spec strings should be unified across all platforms and all VM backends - Symlink resolution and coverage filtering should be shared across execution paths Solution: 1. Create src/mount_specs.rs with cross-platform mount policy functions: - build_vm_mount_policy: unified policy (home_epkg, cache, opt_epkg, cwd, user --mounts, /lib/modules, Windows drives) - add_epkg_mount_specs: core epkg directories - add_epkg_bin_dir_mount: epkg binary directory - windows_drive_mount_specs: WSL2 drive mounts (#[cfg Linux]) - resolve_and_filter_mount_specs: symlink resolution + coverage filtering (#[cfg macOS/Windows]) - parse_mount_spec: parse spec strings (#[cfg macOS/Windows]) - is_path_covered_by: coverage check (#[cfg macOS/Windows]) 2. namespace.rs (Linux): - VM mode: add make-rprivate, then call build_vm_mount_policy - Fs mode: call add_epkg_mount_specs - Result: Linux VM has 0 extra virtiofs mounts 3. libkrun/core.rs: - Linux: returns empty Vec (no extra virtiofs needed) - macOS/Windows: call resolve_and_filter_mount_specs, then build virtiofs mounts - Removed inline do_add_mount closure and duplicate parsing/filtering logic 4. qemu: uses same namespace.rs path, shares same policy 5. stream.rs: conditional write_output function - Linux: uses write_stream_output for O_NONBLOCK handling - macOS/Windows: uses simple write_all 6. df.rs: fix type mismatch (&PathBuf vs PathBuf) Result: - Linux: bind mounts + single root virtiofs (no epkg.vol_N kernel params) - macOS/Windows: multiple virtiofs mounts from unified policy with symlink/coverage handling - Policy code in mount_specs.rs, shared by libkrun, qemu, and Fs mode - Symlink resolution and coverage filtering moved to policy layer - Linux, macOS, Windows all compile successfully Signed-off-by: Wu Fengguang <wfg@mail.ustc.edu.cn> | 3 个月前 | |
cross-platform: fix all build warnings for macOS and Windows Problem: - Windows cross-compile had 18+ warnings about unused code - macOS cross-compile had warnings from our code - Some Unix-only code was not properly guarded Solution: - Add #[cfg(unix)] to Unix-only functions and imports: - scriptlets.rs: ScriptletType, run_scriptlet, etc. - busybox/mod.rs: format_list_columns, visible_width_ansi, etc. - download/aur.rs: AUR_DOMAIN constant - download/file_ops.rs: is_epkg_process() - remove.rs: unlink_package() - conda_link.rs: create_unix_python_entry_point() - Add #[allow(dead_code)] to Unix-only struct fields in plan.rs (InstallationPlan fields used by transaction/hooks/history) - Add #[allow(dead_code)] to version_compare.rs functions (compare, is_version_newer used by Unix-only list.rs) - Add mkfifo to UNIX_ONLY list in build.rs - Fix qemu.rs: resolve_vm_kernel_path() reference after code movement Result: - Linux: 0 warnings - macOS: 0 warnings (1 from external devices crate) - Windows: 0 warnings Signed-off-by: Wu Fengguang <wfg@mail.ustc.edu.cn> | 4 个月前 | |
refactor: split large modules into focused, single-responsibility modules Major module reorganization to improve maintainability and code organization: Split install.rs (4409 lines) into: - plan.rs: InstallationPlan structure and planning logic - steps.rs: Installation step execution (scriptlets, triggers, hooks) - link.rs: Package file linking (hardlink, symlink, move, runpath) - expose.rs: Package exposure (ebin wrapper creation) - risks.rs: Transaction risk management (renamed from transaction.rs) Split depends.rs (2972 lines) into: - version_constraint.rs: Version constraint checking and validation (renamed from constraint.rs) - provides.rs: Package provides checking and capability matching - conda_requires.rs: Conda-style requirement parsing Split version.rs into: - parse_version.rs: PackageVersion struct and parsing - version_compare.rs: Version comparison routines (renamed from version.rs) - version_constraint.rs: Version constraint checking (moved from constraint.rs) Split parse_requires.rs (2298 lines) into: - conda_requires.rs: Conda requirement parsing - rpm_requires.rs: RPM requirement parsing - parse_requires.rs: Core requirement parsing (reduced) Created new focused modules: - package.rs: Package line/key utilities and filtering - package_cache.rs: Package cache management and indexing - world.rs: World file state management - store.rs: Package store operations - deb_triggers.rs: Debian trigger support Module renames: - transaction.rs -> risks.rs (better reflects RPM transaction risk management) - version.rs -> version_compare.rs (clearer purpose) - constraint.rs -> version_constraint.rs (more descriptive) All modules now have focused responsibilities and clear boundaries, making the codebase easier to navigate and maintain. Signed-off-by: Wu Fengguang <wfg@mail.ustc.edu.cn> | 6 个月前 | |
vm: unify lifecycle management with vm_keep_timeout and remove reuse_vm/vm_reuse_connect ## Problem/Purpose The reuse_vm and vm_reuse_connect flags had confusing semantics that made the code hard to understand and maintain. VM lifecycle control was scattered across multiple flags and conditional checks. ## Background/Context Session file is the cross-process coordination mechanism. When a VM session exists, it MUST be reused ("能reuse就必须reuse"). The VM lifecycle is independent from epkg run lifecycle (VM lifecycle >= epkg run). ## Solution Unified VM lifecycle control through vm_keep_timeout (Option<u32>): - None: VM shuts down immediately after command completes - Some(0): VM never times out (persistent) - Some(N) > 0: VM shuts down after N seconds idle Key changes: 1. Changed VmConfig.timeout from u32 to Option<u32> 2. Removed reuse_vm and vm_reuse_connect fields from RunOptions 3. Removed --reuse CLI flag (reuse is now always attempted) 4. Made session file registration UNCONDITIONAL for cross-process discovery 5. Guest daemon decides lifecycle based solely on vm_keep_timeout_secs 6. Removed unused functions: is_vm_reuse_active_for_env, send_command_to_running_qemu_guest ## Usage - epkg run --isolate=vm /bin/ls: VM shuts down immediately (timeout=None) - epkg run --isolate=vm --vm-keep-timeout=30 /bin/ls: VM idle 30s then shutdown - epkg vm start --set timeout=60: VM persistent for 60s idle timeout - epkg vm start --set timeout=0: VM never shuts down until manual stop - Concurrent epkg run automatically discovers and reuses existing session Signed-off-by: Wu Fengguang <wfg@mail.ustc.edu.cn> | 3 个月前 | |
install: load repo metadata before adding essential packages to delta_world add_essential_packages_to_delta_world() uses get_essential_pkgnames(), which reads from the in-memory repodata_indice(). That index is only filled when sync_channel_metadata() runs (revise_repos → create_repository_indexes → create_load_repoindex → populate_repoindex_data). Previously sync_channel_metadata() was only called inside resolve_and_install_packages(), which runs after add_essential_packages_to_delta_world() in install_packages(). So when essentials were added, repodata_indice was still empty, no essential pkgnames were loaded, and delta_world stayed without essentials. Call sync_channel_metadata() in install_packages() when !config().install.no_install_essentials, before add_essential_packages_to_delta_world(), so repo indexes (and shard essential_pkgnames) are loaded first. Add an info log in add_essential_packages_to_delta_world() reporting how many essential names came from the repo and how many were added to delta_world. Fixes: epkg -e debian --assume-no install jq installed only jq (+2 deps, 3 newly installed) and did not pull in essential packages (e.g. coreutils); essential_pkgnames-*.txt were present under the channel cache but add_essential_packages_to_delta_world() added nothing because repodata was not loaded yet. Signed-off-by: Wu Fengguang <wfg@mail.ustc.edu.cn> | 5 个月前 | |
libkrun: reuse VM session for scriptlets/hooks during install Problem: - On macOS/Windows with Linux packages (rpm/deb/apk/arch), each scriptlet creates a NEW VM instead of reusing the existing VM session - This adds ~2-3 seconds overhead per scriptlet due to VM boot time Solution: - Add is_vm_reuse_active_for_env() helper in run.rs (always available) - Centralize VM reuse logic in prepare_run_options_for_command() - All callers (scriptlets/hooks/ldconfig/desktop-updates) automatically inherit VM settings when an active session exists for the same env_root Files changed: - run.rs: Add is_vm_reuse_active_for_env() and integrate in prepare_run_options_for_command() - libkrun.rs: Export internal implementation for run.rs to use - scriptlets.rs/hooks.rs/transaction.rs/xdesktop.rs: Simplified, no longer need manual VM checks Signed-off-by: Wu Fengguang <wfg@mail.ustc.edu.cn> | 4 个月前 |
| 文件 | 最后提交记录 | 最后更新时间 |
|---|---|---|
| 3 个月前 | ||
| 3 个月前 | ||
| 2 个月前 | ||
| 4 个月前 | ||
| 4 个月前 | ||
| 4 个月前 | ||
| 3 个月前 | ||
| 8 个月前 | ||
| 4 个月前 | ||
| 3 个月前 | ||
| 4 个月前 | ||
| 4 个月前 | ||
| 4 个月前 | ||
| 4 个月前 | ||
| 3 个月前 | ||
| 3 个月前 | ||
| 3 个月前 | ||
| 3 个月前 | ||
| 4 个月前 | ||
| 3 个月前 | ||
| 4 个月前 | ||
| 3 个月前 | ||
| 6 个月前 | ||
| 4 个月前 | ||
| 4 个月前 | ||
| 4 个月前 | ||
| 4 个月前 | ||
| 3 个月前 | ||
| 4 个月前 | ||
| 3 个月前 | ||
| 4 个月前 | ||
| 2 个月前 | ||
| 4 个月前 | ||
| 3 个月前 | ||
| 3 个月前 | ||
| 1 年前 | ||
| 4 个月前 | ||
| 3 个月前 | ||
| 3 个月前 | ||
| 3 个月前 | ||
| 5 个月前 | ||
| 3 个月前 | ||
| 3 个月前 | ||
| 3 个月前 | ||
| 3 个月前 | ||
| 5 个月前 | ||
| 3 个月前 | ||
| 3 个月前 | ||
| 3 个月前 | ||
| 4 个月前 | ||
| 2 个月前 | ||
| 3 个月前 | ||
| 3 个月前 | ||
| 2 个月前 | ||
| 3 个月前 | ||
| 4 个月前 | ||
| 2 个月前 | ||
| 4 个月前 | ||
| 3 个月前 | ||
| 3 个月前 | ||
| 4 个月前 | ||
| 4 个月前 | ||
| 6 个月前 | ||
| 3 个月前 | ||
| 3 个月前 | ||
| 4 个月前 | ||
| 4 个月前 | ||
| 3 个月前 | ||
| 4 个月前 | ||
| 3 个月前 | ||
| 3 个月前 | ||
| 4 个月前 | ||
| 4 个月前 | ||
| 4 个月前 | ||
| 4 个月前 | ||
| 4 个月前 | ||
| 4 个月前 | ||
| 2 个月前 | ||
| 3 个月前 | ||
| 3 个月前 | ||
| 5 个月前 | ||
| 3 个月前 | ||
| 4 个月前 | ||
| 4 个月前 | ||
| 3 个月前 | ||
| 3 个月前 | ||
| 3 个月前 | ||
| 3 个月前 | ||
| 5 个月前 | ||
| 4 个月前 | ||
| 3 个月前 | ||
| 4 个月前 | ||
| 6 个月前 | ||
| 3 个月前 | ||
| 5 个月前 | ||
| 4 个月前 |