1#![allow(clippy::unwrap_or_default)]
9
10pub mod async_types;
11#[cfg(feature = "alloc")]
12pub mod build_package;
13#[cfg(feature = "alloc")]
14pub mod build_system;
15pub mod compliance;
16pub mod database;
17pub mod delta;
18pub mod ecosystem;
19pub mod format;
20pub mod manifest;
21pub mod plugin;
22pub mod ports;
23#[cfg(feature = "alloc")]
24pub mod repo_server;
25pub mod repository;
26pub mod reproducible;
27pub mod resolver;
28pub mod sdk;
29pub mod statistics;
30pub mod testing;
31pub mod toml_parser;
32
33use alloc::{collections::BTreeMap, string::String, vec, vec::Vec};
34
35use spin::Mutex;
36
37use crate::error::KernelError;
38
39pub type PackageId = String;
41
42#[derive(Debug, Clone, Hash, PartialEq, Eq, PartialOrd, Ord)]
44pub struct Version {
45 pub major: u32,
46 pub minor: u32,
47 pub patch: u32,
48}
49
50impl Version {
51 pub fn new(major: u32, minor: u32, patch: u32) -> Self {
52 Self {
53 major,
54 minor,
55 patch,
56 }
57 }
58}
59
60#[derive(Debug, Clone)]
62pub struct PackageMetadata {
63 pub name: String,
64 pub version: Version,
65 pub author: String,
66 pub description: String,
67 pub license: String,
68 pub dependencies: Vec<Dependency>,
69 pub conflicts: Vec<String>,
71}
72
73#[derive(Debug, Clone)]
75pub struct Dependency {
76 pub name: String,
77 pub version_req: String,
78}
79
80pub type PkgResult<T> = Result<T, KernelError>;
82
83#[derive(Debug, Clone, Default)]
85pub struct InstallOptions {
86 pub force_unsigned: bool,
89 pub expected_hash: Option<[u8; 32]>,
93}
94
95#[derive(Debug, Clone)]
97pub struct PackageTransaction {
98 operations: Vec<TransactionOp>,
100 snapshot: BTreeMap<PackageId, PackageMetadata>,
102}
103
104#[derive(Debug, Clone)]
106pub enum TransactionOp {
107 Install(PackageId, Version),
109 Remove(PackageId),
111}
112
113impl PackageTransaction {
114 fn new(current_installed: &BTreeMap<PackageId, PackageMetadata>) -> Self {
115 Self {
116 operations: Vec::new(),
117 snapshot: current_installed.clone(),
118 }
119 }
120}
121
122pub struct PackageManager {
124 installed: BTreeMap<PackageId, PackageMetadata>,
126 resolver: resolver::DependencyResolver,
128 repositories: Vec<repository::Repository>,
130 signature_policy: format::SignaturePolicy,
132 trusted_keys: format::TrustedKeyRing,
134 database: database::PackageDatabase,
136 file_manifest: manifest::FileManifest,
138 transaction: Option<PackageTransaction>,
140}
141
142impl PackageManager {
143 pub fn new() -> Self {
144 Self {
145 installed: BTreeMap::new(),
146 resolver: resolver::DependencyResolver::new(),
147 repositories: Vec::new(),
148 signature_policy: format::SignaturePolicy::default(),
149 trusted_keys: format::TrustedKeyRing::default(),
150 database: database::PackageDatabase::default(),
151 file_manifest: manifest::FileManifest::default(),
152 transaction: None,
153 }
154 }
155
156 pub fn signature_policy(&self) -> &format::SignaturePolicy {
158 &self.signature_policy
159 }
160
161 pub fn set_signature_policy(&mut self, policy: format::SignaturePolicy) {
163 self.signature_policy = policy;
164 }
165
166 pub fn trusted_keys_mut(&mut self) -> &mut format::TrustedKeyRing {
168 &mut self.trusted_keys
169 }
170
171 pub fn add_repository(&mut self, repo: repository::Repository) {
173 self.repositories.push(repo);
174 }
175
176 pub fn install(&mut self, name: String, version_req: String) -> PkgResult<()> {
181 self.install_with_options(name, version_req, InstallOptions::default())
182 }
183
184 pub fn install_with_options(
190 &mut self,
191 name: String,
192 version_req: String,
193 options: InstallOptions,
194 ) -> PkgResult<()> {
195 let dep = Dependency {
197 name: name.clone(),
198 version_req: version_req.clone(),
199 };
200
201 let packages = self
203 .resolver
204 .resolve(&[dep])
205 .map_err(|_e| KernelError::InvalidState {
206 expected: "resolvable dependencies",
207 actual: "dependency resolution failed",
208 })?;
209
210 for (pkg_id, version) in packages {
212 if self.installed.contains_key(&pkg_id) {
213 continue;
215 }
216
217 let package_data = self.download_package(&pkg_id, &version)?;
219
220 if let Some(expected) = options.expected_hash {
222 let actual = crate::crypto::hash::sha256(&package_data);
223 if actual.as_bytes() != &expected {
224 crate::println!(
225 "[PKG] REJECT {}: package hash does not match repository index",
226 pkg_id
227 );
228 return Err(KernelError::PermissionDenied {
229 operation: "verify package hash",
230 });
231 }
232 }
233
234 match self.verify_package(&package_data) {
236 Ok(()) => {}
237 Err(e) if options.force_unsigned => {
238 crate::println!(
239 "[PKG] WARNING: --force-unsigned: skipping signature failure for {}: {:?}",
240 pkg_id,
241 e
242 );
243 }
244 Err(e) => return Err(e),
245 }
246
247 self.install_package(pkg_id.clone(), version.clone(), package_data)?;
249
250 if let Some(txn) = &mut self.transaction {
252 txn.operations
253 .push(TransactionOp::Install(pkg_id.clone(), version.clone()));
254 }
255
256 crate::println!(
257 "[PKG] Installed {} {}.{}.{}",
258 pkg_id,
259 version.major,
260 version.minor,
261 version.patch
262 );
263 }
264
265 Ok(())
266 }
267
268 pub fn remove(&mut self, package_id: &PackageId) -> PkgResult<()> {
270 if !self.installed.contains_key(package_id) {
271 return Err(KernelError::NotFound {
272 resource: "package",
273 id: 0,
274 });
275 }
276
277 let dependents = self.find_dependents(package_id);
279 if !dependents.is_empty() {
280 return Err(KernelError::InvalidState {
281 expected: "no reverse dependencies",
282 actual: "package is required by other packages",
283 });
284 }
285
286 if let Some(txn) = &mut self.transaction {
288 txn.operations
289 .push(TransactionOp::Remove(package_id.clone()));
290 }
291
292 self.installed.remove(package_id);
294
295 crate::println!("[PKG] Removed {}", package_id);
296
297 Ok(())
298 }
299
300 pub fn remove_preserving_configs(&mut self, package_id: &PackageId) -> PkgResult<()> {
305 if !self.installed.contains_key(package_id) {
306 return Err(KernelError::NotFound {
307 resource: "package",
308 id: 0,
309 });
310 }
311
312 let dependents = self.find_dependents(package_id);
314 if !dependents.is_empty() {
315 return Err(KernelError::InvalidState {
316 expected: "no reverse dependencies",
317 actual: "package is required by other packages",
318 });
319 }
320
321 let config_files = self.database.list_config_files(package_id);
323 for config in config_files {
324 if config.is_user_modified {
325 crate::println!(
326 "[PKG] Preserving modified config: {} -> {}.bak",
327 config.path,
328 config.path
329 );
330 }
331 }
332
333 if let Some(txn) = &mut self.transaction {
335 txn.operations
336 .push(TransactionOp::Remove(package_id.clone()));
337 }
338
339 self.installed.remove(package_id);
341 crate::println!("[PKG] Removed {} (configs preserved)", package_id);
342
343 Ok(())
344 }
345
346 pub fn remove_orphans(&mut self) -> PkgResult<Vec<PackageId>> {
350 let orphans = self.database.find_orphans();
351 let mut removed = Vec::new();
352
353 for orphan in &orphans {
354 if !self.installed.contains_key(orphan) {
356 continue;
357 }
358 if self.remove(orphan).is_ok() {
359 removed.push(orphan.clone());
360 }
361 }
362
363 if !removed.is_empty() {
364 crate::println!("[PKG] Removed {} orphan package(s)", removed.len());
365 }
366
367 Ok(removed)
368 }
369
370 pub fn list_installed(&self) -> Vec<(PackageId, Version)> {
372 self.installed
373 .iter()
374 .map(|(id, meta)| (id.clone(), meta.version.clone()))
375 .collect()
376 }
377
378 pub fn is_installed(&self, package_id: &PackageId) -> bool {
380 self.installed.contains_key(package_id)
381 }
382
383 pub fn get_metadata(&self, package_id: &PackageId) -> Option<&PackageMetadata> {
385 self.installed.get(package_id)
386 }
387
388 pub fn update(&mut self) -> PkgResult<()> {
390 for repo in &self.repositories {
391 let packages = repo.fetch_package_list();
392
393 for pkg in packages {
394 self.resolver.register_package(
395 pkg.name.clone(),
396 pkg.version.clone(),
397 pkg.dependencies.clone(),
398 pkg.conflicts.clone(),
399 );
400 }
401 }
402
403 Ok(())
404 }
405
406 fn download_package(&self, package_id: &PackageId, _version: &Version) -> PkgResult<Vec<u8>> {
408 for repo in &self.repositories {
409 if let Some(data) = repo.download_package(package_id) {
410 return Ok(data);
411 }
412 }
413
414 Err(KernelError::NotFound {
415 resource: "package in repositories",
416 id: 0,
417 })
418 }
419
420 fn verify_package(&self, package_data: &[u8]) -> PkgResult<()> {
426 use format::{PackageHeader, PackageSignatures, TrustLevel, VPKG_MAGIC};
427
428 use crate::crypto::asymmetric;
429
430 if !self.signature_policy.require_signatures {
432 crate::println!("[PKG] Signature verification skipped (policy: not required)");
433 return Ok(());
434 }
435
436 if package_data.len() < 64 {
438 return Err(KernelError::InvalidArgument {
439 name: "package_data",
440 value: "too_short",
441 });
442 }
443
444 let header =
451 unsafe { core::ptr::read_unaligned(package_data.as_ptr() as *const PackageHeader) };
452
453 if header.magic != VPKG_MAGIC {
455 return Err(KernelError::InvalidArgument {
456 name: "package_magic",
457 value: "invalid",
458 });
459 }
460
461 if !header.validate() {
463 return Err(KernelError::InvalidArgument {
464 name: "package_version",
465 value: "unsupported",
466 });
467 }
468
469 let sig_offset = header.signature_offset as usize;
471 let sig_size = header.signature_size as usize;
472
473 if sig_offset + sig_size > package_data.len() {
474 return Err(KernelError::InvalidArgument {
475 name: "signature_offset",
476 value: "out_of_bounds",
477 });
478 }
479
480 let sig_data = &package_data[sig_offset..sig_offset + sig_size];
481 let signatures =
482 PackageSignatures::from_bytes(sig_data).map_err(|_| KernelError::InvalidArgument {
483 name: "signatures",
484 value: "parse_failed",
485 })?;
486
487 let content_start = header.metadata_offset as usize;
489 let content_end = header.signature_offset as usize;
490 if content_start > content_end || content_end > package_data.len() {
491 return Err(KernelError::InvalidArgument {
492 name: "content_range",
493 value: "out_of_bounds",
494 });
495 }
496 let content_to_verify = &package_data[content_start..content_end];
497
498 let ed25519_sig =
501 asymmetric::Signature::from_bytes(&signatures.ed25519_sig).map_err(|_| {
502 KernelError::InvalidArgument {
503 name: "ed25519_signature",
504 value: "malformed",
505 }
506 })?;
507
508 let mut ed25519_valid = false;
509 let mut matched_trust_level = TrustLevel::Untrusted;
510
511 for trusted in self.trusted_keys.keys() {
512 let vk = match asymmetric::VerifyingKey::from_bytes(&trusted.public_key) {
513 Ok(vk) => vk,
514 Err(_) => continue,
515 };
516
517 match vk.verify(content_to_verify, &ed25519_sig) {
518 Ok(true) => {
519 ed25519_valid = true;
520 matched_trust_level = trusted.trust_level;
521 break;
522 }
523 _ => continue,
524 }
525 }
526
527 if !ed25519_valid {
528 crate::println!("[PKG] REJECT: Ed25519 signature verification failed");
529 return Err(KernelError::PermissionDenied {
530 operation: "verify Ed25519 signature",
531 });
532 }
533
534 if matched_trust_level < self.signature_policy.minimum_trust_level {
536 crate::println!("[PKG] REJECT: signing key trust level insufficient");
537 return Err(KernelError::PermissionDenied {
538 operation: "verify signing key trust level",
539 });
540 }
541
542 if self.signature_policy.require_post_quantum {
544 let dilithium_valid = verify_dilithium_signature(
545 content_to_verify,
546 &signatures.dilithium_sig,
547 &get_trusted_dilithium_pubkey(),
548 );
549
550 if !dilithium_valid {
551 crate::println!("[PKG] REJECT: Dilithium signature verification failed");
552 return Err(KernelError::PermissionDenied {
553 operation: "verify Dilithium signature",
554 });
555 }
556 }
557
558 crate::println!(
559 "[PKG] Package signature verification passed (Ed25519{})",
560 if self.signature_policy.require_post_quantum {
561 " + Dilithium"
562 } else {
563 ""
564 }
565 );
566
567 Ok(())
568 }
569
570 fn install_package(
572 &mut self,
573 package_id: PackageId,
574 _version: Version,
575 package_data: Vec<u8>,
576 ) -> PkgResult<()> {
577 use format::{Compression, PackageHeader};
578
579 if package_data.len() < 64 {
581 return Err(KernelError::InvalidArgument {
582 name: "package_data",
583 value: "too_short",
584 });
585 }
586
587 let header =
592 unsafe { core::ptr::read_unaligned(package_data.as_ptr() as *const PackageHeader) };
593
594 let meta_offset = header.metadata_offset as usize;
596 let meta_size = header.metadata_size as usize;
597
598 if meta_offset + meta_size > package_data.len() {
599 return Err(KernelError::InvalidArgument {
600 name: "metadata_offset",
601 value: "out_of_bounds",
602 });
603 }
604
605 let meta_bytes = &package_data[meta_offset..meta_offset + meta_size];
606 let metadata = parse_package_metadata(meta_bytes)?;
607
608 let content_offset = header.content_offset as usize;
610 let content_size = header.content_size as usize;
611
612 if content_offset + content_size > package_data.len() {
613 return Err(KernelError::InvalidArgument {
614 name: "content_offset",
615 value: "out_of_bounds",
616 });
617 }
618
619 let content_bytes = &package_data[content_offset..content_offset + content_size];
620
621 let compression = header.get_compression().unwrap_or(Compression::None);
623 let decompressed = format::decompress(content_bytes, compression).map_err(|_| {
624 KernelError::InvalidArgument {
625 name: "compression",
626 value: "decompression_failed",
627 }
628 })?;
629
630 extract_package_files(&package_id, &decompressed)?;
632
633 self.installed.insert(package_id.clone(), metadata);
635
636 crate::println!(
637 "[PKG] Package {} successfully extracted and installed",
638 package_id
639 );
640
641 Ok(())
642 }
643
644 fn find_dependents(&self, package_id: &PackageId) -> Vec<PackageId> {
646 self.installed
647 .iter()
648 .filter(|(_, meta)| meta.dependencies.iter().any(|dep| &dep.name == package_id))
649 .map(|(id, _)| id.clone())
650 .collect()
651 }
652
653 pub fn begin_transaction(&mut self) -> PkgResult<()> {
664 if self.transaction.is_some() {
665 return Err(KernelError::InvalidState {
666 expected: "no active transaction",
667 actual: "transaction already in progress",
668 });
669 }
670 self.transaction = Some(PackageTransaction::new(&self.installed));
671 crate::println!("[PKG] Transaction started");
672 Ok(())
673 }
674
675 pub fn commit_transaction(&mut self) -> PkgResult<()> {
677 let txn = self.transaction.take().ok_or(KernelError::InvalidState {
678 expected: "active transaction",
679 actual: "no transaction in progress",
680 })?;
681
682 let _op_count = txn.operations.len();
683 crate::println!("[PKG] Transaction committed ({} operations)", _op_count);
684
685 if let Err(_e) = self.database.save() {
687 crate::println!("[PKG] Warning: failed to persist database: {:?}", _e);
688 }
689 Ok(())
690 }
691
692 pub fn rollback_transaction(&mut self) -> PkgResult<()> {
694 let txn = self.transaction.take().ok_or(KernelError::InvalidState {
695 expected: "active transaction",
696 actual: "no transaction in progress",
697 })?;
698
699 self.installed = txn.snapshot;
700 crate::println!("[PKG] Transaction rolled back");
701 Ok(())
702 }
703
704 pub fn upgrade(&mut self, package_id: &str) -> PkgResult<()> {
710 if !self.installed.contains_key(package_id) {
711 return Err(KernelError::NotFound {
712 resource: "installed package",
713 id: 0,
714 });
715 }
716
717 let current_version = self
718 .installed
719 .get(package_id)
720 .map(|m| m.version.clone())
721 .ok_or(KernelError::NotFound {
722 resource: "package",
723 id: 0,
724 })?;
725
726 let latest = self.resolver.latest_version(package_id);
728 match latest {
729 Some(v) if v > current_version => {
730 crate::println!(
731 "[PKG] Upgrading {} from {}.{}.{} to {}.{}.{}",
732 package_id,
733 current_version.major,
734 current_version.minor,
735 current_version.patch,
736 v.major,
737 v.minor,
738 v.patch
739 );
740 let version_req = alloc::format!("{}.{}.{}", v.major, v.minor, v.patch);
741 self.installed.remove(package_id);
743 self.install(String::from(package_id), version_req)?;
744 }
745 _ => {
746 crate::println!("[PKG] {} is already at the latest version", package_id);
747 }
748 }
749
750 Ok(())
751 }
752
753 pub fn upgrade_all(&mut self) -> PkgResult<usize> {
755 let installed_names: Vec<PackageId> = self.installed.keys().cloned().collect();
756 let mut upgraded = 0;
757
758 for name in &installed_names {
759 let current = self.installed.get(name).map(|m| m.version.clone());
760 let latest = self.resolver.latest_version(name);
761
762 if let (Some(cur), Some(lat)) = (current, latest) {
763 if lat > cur {
764 self.upgrade(name)?;
765 upgraded += 1;
766 }
767 }
768 }
769
770 crate::println!("[PKG] Upgraded {} package(s)", upgraded);
771 Ok(upgraded)
772 }
773
774 pub fn search(&self, query: &str) -> Vec<(PackageId, Version)> {
780 self.resolver.search(query)
781 }
782
783 pub fn get_package_info(&self, package_id: &str) -> Option<PackageMetadata> {
785 if let Some(meta) = self.installed.get(package_id) {
787 return Some(meta.clone());
788 }
789 self.resolver.get_package_metadata(package_id)
791 }
792
793 pub fn file_manifest(&self) -> &manifest::FileManifest {
795 &self.file_manifest
796 }
797
798 pub fn file_manifest_mut(&mut self) -> &mut manifest::FileManifest {
800 &mut self.file_manifest
801 }
802
803 pub fn database(&self) -> &database::PackageDatabase {
805 &self.database
806 }
807
808 pub fn database_mut(&mut self) -> &mut database::PackageDatabase {
810 &mut self.database
811 }
812}
813
814impl Default for PackageManager {
815 fn default() -> Self {
816 Self::new()
817 }
818}
819
820static PACKAGE_MANAGER: Mutex<Option<PackageManager>> = Mutex::new(None);
822
823pub fn init() {
825 *PACKAGE_MANAGER.lock() = Some(PackageManager::new());
826 crate::println!("[PKG] Package management system initialized");
827}
828
829pub fn with_package_manager<R, F: FnOnce(&mut PackageManager) -> R>(f: F) -> Option<R> {
831 PACKAGE_MANAGER.lock().as_mut().map(f)
832}
833
834fn get_trusted_dilithium_pubkey() -> Vec<u8> {
848 use crate::crypto::pq_params::dilithium::level3::PUBLIC_KEY_SIZE;
849
850 let mut key = vec![0u8; PUBLIC_KEY_SIZE];
854
855 let seed: [u8; 32] = [
858 0x56, 0x65, 0x72, 0x69, 0x64, 0x69, 0x61, 0x6e, 0x4f, 0x53, 0x2d, 0x50, 0x4b, 0x47, 0x2d, 0x4b, 0x45, 0x59, 0x2d, 0x53, 0x45, 0x45, 0x44, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, ];
863
864 for (i, key_byte) in key.iter_mut().enumerate() {
867 let counter = i as u8;
869 let idx = i % 32;
870 *key_byte = seed[idx].wrapping_add(counter).wrapping_mul(0x6D);
871 }
872
873 key[0] = 0x4D; key[1] = 0x44; key[2] = 0x36; key[3] = 0x35; key
880}
881
882fn verify_dilithium_signature(message: &[u8], signature: &[u8], public_key: &[u8]) -> bool {
888 match crate::security::dilithium::verify(public_key, message, signature) {
891 Ok(valid) => valid,
892 Err(_e) => {
893 crate::println!("[PKG] Dilithium verification error: {:?}", _e);
894 false
895 }
896 }
897}
898
899fn parse_package_metadata(data: &[u8]) -> PkgResult<PackageMetadata> {
905 let json_str = core::str::from_utf8(data).map_err(|_| KernelError::InvalidArgument {
909 name: "metadata",
910 value: "not_utf8",
911 })?;
912
913 let name = extract_json_string(json_str, "name").unwrap_or_else(|| String::from("unknown"));
915 let version_str =
916 extract_json_string(json_str, "version").unwrap_or_else(|| String::from("0.0.0"));
917 let author = extract_json_string(json_str, "author").unwrap_or_else(|| String::from("unknown"));
918 let description =
919 extract_json_string(json_str, "description").unwrap_or_else(|| String::from(""));
920 let license = extract_json_string(json_str, "license").unwrap_or_else(|| String::from(""));
921
922 let version = parse_version(&version_str);
924
925 let dependencies = extract_json_array(json_str, "dependencies")
927 .map(|deps| {
928 deps.into_iter()
929 .filter_map(|dep_str| {
930 let parts: Vec<&str> = dep_str.split('@').collect();
931 if !parts.is_empty() {
932 Some(Dependency {
933 name: String::from(parts[0]),
934 version_req: if parts.len() > 1 {
935 String::from(parts[1])
936 } else {
937 String::from("*")
938 },
939 })
940 } else {
941 None
942 }
943 })
944 .collect()
945 })
946 .unwrap_or_else(Vec::new);
947
948 Ok(PackageMetadata {
949 name,
950 version,
951 author,
952 description,
953 license,
954 dependencies,
955 conflicts: Vec::new(),
956 })
957}
958
959fn extract_json_string(json: &str, key: &str) -> Option<String> {
961 let pattern = alloc::format!("\"{}\":\"", key);
962 if let Some(start) = json.find(&pattern) {
963 let value_start = start + pattern.len();
964 if let Some(end) = json[value_start..].find('"') {
965 return Some(String::from(&json[value_start..value_start + end]));
966 }
967 }
968 None
969}
970
971fn extract_json_array(json: &str, key: &str) -> Option<Vec<String>> {
973 let pattern = alloc::format!("\"{}\":[", key);
974 if let Some(start) = json.find(&pattern) {
975 let array_start = start + pattern.len();
976 if let Some(end) = json[array_start..].find(']') {
977 let array_str = &json[array_start..array_start + end];
978 let items: Vec<String> = array_str
979 .split(',')
980 .map(|s: &str| String::from(s.trim().trim_matches('"')))
981 .filter(|s: &String| !s.is_empty())
982 .collect();
983 return Some(items);
984 }
985 }
986 None
987}
988
989fn parse_version(version_str: &str) -> Version {
991 let parts: Vec<&str> = version_str.split('.').collect();
992
993 let major = parts.first().and_then(|s| s.parse().ok()).unwrap_or(0);
994 let minor = parts.get(1).and_then(|s| s.parse().ok()).unwrap_or(0);
995 let patch = parts.get(2).and_then(|s| s.parse().ok()).unwrap_or(0);
996
997 Version {
998 major,
999 minor,
1000 patch,
1001 }
1002}
1003
1004#[allow(dead_code)] struct PackageFileEntry {
1014 path: String,
1015 size: u64,
1016 mode: u32,
1017 data_offset: usize,
1018}
1019
1020fn extract_package_files(package_id: &str, data: &[u8]) -> PkgResult<()> {
1022 if data.len() < 4 {
1032 return Err(KernelError::InvalidArgument {
1033 name: "package_content",
1034 value: "too_short",
1035 });
1036 }
1037
1038 let num_files = u32::from_le_bytes([data[0], data[1], data[2], data[3]]) as usize;
1039 let mut pos = 4;
1040
1041 let install_base = alloc::format!("/usr/local/packages/{}", package_id);
1042
1043 #[cfg_attr(not(target_arch = "x86_64"), allow(unused_variables))]
1044 for file_idx in 0..num_files {
1045 if pos + 2 > data.len() {
1047 return Err(KernelError::InvalidArgument {
1048 name: "file_entry",
1049 value: "truncated_path_len",
1050 });
1051 }
1052 let path_len = u16::from_le_bytes([data[pos], data[pos + 1]]) as usize;
1053 pos += 2;
1054
1055 if pos + path_len > data.len() {
1057 return Err(KernelError::InvalidArgument {
1058 name: "file_entry",
1059 value: "truncated_path",
1060 });
1061 }
1062 let path = core::str::from_utf8(&data[pos..pos + path_len]).map_err(|_| {
1063 KernelError::InvalidArgument {
1064 name: "file_path",
1065 value: "not_utf8",
1066 }
1067 })?;
1068 pos += path_len;
1069
1070 if pos + 8 > data.len() {
1072 return Err(KernelError::InvalidArgument {
1073 name: "file_entry",
1074 value: "truncated_size",
1075 });
1076 }
1077 let file_size = u64::from_le_bytes([
1078 data[pos],
1079 data[pos + 1],
1080 data[pos + 2],
1081 data[pos + 3],
1082 data[pos + 4],
1083 data[pos + 5],
1084 data[pos + 6],
1085 data[pos + 7],
1086 ]) as usize;
1087 pos += 8;
1088
1089 if pos + 4 > data.len() {
1091 return Err(KernelError::InvalidArgument {
1092 name: "file_entry",
1093 value: "truncated_mode",
1094 });
1095 }
1096 let file_mode =
1097 u32::from_le_bytes([data[pos], data[pos + 1], data[pos + 2], data[pos + 3]]);
1098 pos += 4;
1099
1100 if pos + file_size > data.len() {
1102 return Err(KernelError::InvalidArgument {
1103 name: "file_entry",
1104 value: "truncated_data",
1105 });
1106 }
1107 let file_data = &data[pos..pos + file_size];
1108 pos += file_size;
1109
1110 let full_path = alloc::format!("{}/{}", install_base, path);
1112 install_file(&full_path, file_data, file_mode)?;
1113
1114 crate::println!(
1115 "[PKG] Extracted file {}/{}: {} ({} bytes, mode {:o})",
1116 file_idx + 1,
1117 num_files,
1118 path,
1119 file_size,
1120 file_mode
1121 );
1122 }
1123
1124 Ok(())
1125}
1126
1127fn install_file(path: &str, data: &[u8], mode: u32) -> PkgResult<()> {
1129 if let Some(parent_end) = path.rfind('/') {
1131 let parent_path = &path[..parent_end];
1132 create_directories(parent_path)?;
1133 }
1134
1135 #[cfg(feature = "alloc")]
1138 {
1139 use crate::fs::{get_vfs, OpenFlags, Permissions};
1140
1141 if let Some(parent_end) = path.rfind('/') {
1143 if parent_end > 0 {
1144 let parent = &path[..parent_end];
1145 let perms = Permissions::from_mode(0o755);
1146 let _ = get_vfs().write().mkdir(parent, perms);
1148 }
1149 }
1150
1151 let flags = OpenFlags::read_write();
1154 match get_vfs().read().open(path, flags) {
1155 Ok(node) => {
1156 if let Err(_e) = node.write(0, data) {
1158 crate::println!("[PKG] Warning: Could not write to file {}", path);
1159 }
1160 }
1161 Err(_e) => {
1162 crate::println!(
1164 "[PKG] Warning: Could not create file {} (VFS limitation)",
1165 path
1166 );
1167 }
1168 }
1169 let _ = mode; }
1171
1172 #[cfg(not(feature = "alloc"))]
1173 {
1174 let _ = (path, data, mode);
1176 }
1177
1178 Ok(())
1179}
1180
1181#[cfg(test)]
1182mod tests {
1183 #[allow(unused_imports)]
1184 use alloc::vec;
1185
1186 use super::*;
1187
1188 #[test]
1191 fn test_version_new() {
1192 let v = Version::new(1, 2, 3);
1193 assert_eq!(v.major, 1);
1194 assert_eq!(v.minor, 2);
1195 assert_eq!(v.patch, 3);
1196 }
1197
1198 #[test]
1199 fn test_version_ordering() {
1200 let v1 = Version::new(1, 0, 0);
1201 let v2 = Version::new(1, 0, 1);
1202 let v3 = Version::new(1, 1, 0);
1203 let v4 = Version::new(2, 0, 0);
1204 assert!(v1 < v2);
1205 assert!(v2 < v3);
1206 assert!(v3 < v4);
1207 assert_eq!(v1, Version::new(1, 0, 0));
1208 }
1209
1210 #[test]
1211 fn test_version_clone_and_eq() {
1212 let v1 = Version::new(5, 10, 15);
1213 let v2 = v1.clone();
1214 assert_eq!(v1, v2);
1215 }
1216
1217 #[test]
1220 fn test_parse_version_full() {
1221 let v = parse_version("1.2.3");
1222 assert_eq!(v, Version::new(1, 2, 3));
1223 }
1224
1225 #[test]
1226 fn test_parse_version_partial() {
1227 let v = parse_version("5.10");
1228 assert_eq!(v, Version::new(5, 10, 0));
1229 }
1230
1231 #[test]
1232 fn test_parse_version_major_only() {
1233 let v = parse_version("7");
1234 assert_eq!(v, Version::new(7, 0, 0));
1235 }
1236
1237 #[test]
1238 fn test_parse_version_invalid() {
1239 let v = parse_version("abc");
1240 assert_eq!(v, Version::new(0, 0, 0));
1241 }
1242
1243 #[test]
1244 fn test_parse_version_empty() {
1245 let v = parse_version("");
1246 assert_eq!(v, Version::new(0, 0, 0));
1247 }
1248
1249 #[test]
1252 fn test_extract_json_string_found() {
1253 let json = r#"{"name":"hello","version":"1.0.0"}"#;
1254 assert_eq!(
1255 extract_json_string(json, "name"),
1256 Some(String::from("hello"))
1257 );
1258 assert_eq!(
1259 extract_json_string(json, "version"),
1260 Some(String::from("1.0.0"))
1261 );
1262 }
1263
1264 #[test]
1265 fn test_extract_json_string_not_found() {
1266 let json = r#"{"name":"hello"}"#;
1267 assert_eq!(extract_json_string(json, "missing"), None);
1268 }
1269
1270 #[test]
1273 fn test_extract_json_array_found() {
1274 let json = r#"{"items":["a","b","c"]}"#;
1275 let arr = extract_json_array(json, "items").unwrap();
1276 assert_eq!(arr.len(), 3);
1277 assert_eq!(arr[0], "a");
1278 assert_eq!(arr[1], "b");
1279 assert_eq!(arr[2], "c");
1280 }
1281
1282 #[test]
1283 fn test_extract_json_array_not_found() {
1284 let json = r#"{"name":"test"}"#;
1285 assert!(extract_json_array(json, "items").is_none());
1286 }
1287
1288 #[test]
1289 fn test_extract_json_array_empty() {
1290 let json = r#"{"items":[]}"#;
1291 let arr = extract_json_array(json, "items").unwrap();
1292 assert!(arr.is_empty());
1293 }
1294
1295 #[test]
1298 fn test_parse_package_metadata_valid() {
1299 let json = br#"{"name":"test-pkg","version":"1.2.3","author":"dev","description":"a package","license":"MIT"}"#;
1300 let meta = parse_package_metadata(json).unwrap();
1301 assert_eq!(meta.name, "test-pkg");
1302 assert_eq!(meta.version, Version::new(1, 2, 3));
1303 assert_eq!(meta.author, "dev");
1304 assert_eq!(meta.description, "a package");
1305 assert_eq!(meta.license, "MIT");
1306 }
1307
1308 #[test]
1309 fn test_parse_package_metadata_minimal() {
1310 let json = br#"{}"#;
1311 let meta = parse_package_metadata(json).unwrap();
1312 assert_eq!(meta.name, "unknown");
1313 assert_eq!(meta.version, Version::new(0, 0, 0));
1314 }
1315
1316 #[test]
1317 fn test_parse_package_metadata_invalid_utf8() {
1318 let data: &[u8] = &[0xFF, 0xFE];
1319 assert!(parse_package_metadata(data).is_err());
1320 }
1321
1322 #[test]
1323 fn test_parse_package_metadata_with_deps() {
1324 let json = br#"{"name":"app","version":"1.0.0","dependencies":["lib@>=1.0.0","core"]}"#;
1325 let meta = parse_package_metadata(json).unwrap();
1326 assert_eq!(meta.dependencies.len(), 2);
1327 assert_eq!(meta.dependencies[0].name, "lib");
1328 assert_eq!(meta.dependencies[0].version_req, ">=1.0.0");
1329 assert_eq!(meta.dependencies[1].name, "core");
1330 assert_eq!(meta.dependencies[1].version_req, "*");
1331 }
1332
1333 #[test]
1336 fn test_install_options_default() {
1337 let opts = InstallOptions::default();
1338 assert!(!opts.force_unsigned);
1339 assert!(opts.expected_hash.is_none());
1340 }
1341
1342 #[test]
1345 fn test_package_manager_new() {
1346 let pm = PackageManager::new();
1347 assert!(pm.list_installed().is_empty());
1348 assert!(!pm.is_installed(&String::from("test")));
1349 }
1350
1351 #[test]
1352 fn test_package_manager_default() {
1353 let pm = PackageManager::default();
1354 assert!(pm.list_installed().is_empty());
1355 }
1356
1357 #[test]
1358 fn test_package_manager_remove_not_installed() {
1359 let mut pm = PackageManager::new();
1360 let result = pm.remove(&String::from("nonexistent"));
1361 assert!(result.is_err());
1362 }
1363
1364 #[test]
1365 fn test_package_manager_get_metadata_none() {
1366 let pm = PackageManager::new();
1367 assert!(pm.get_metadata(&String::from("missing")).is_none());
1368 }
1369
1370 #[test]
1373 fn test_transaction_begin_commit() {
1374 let mut pm = PackageManager::new();
1375 assert!(pm.begin_transaction().is_ok());
1376 assert!(pm.commit_transaction().is_ok());
1377 }
1378
1379 #[test]
1380 fn test_transaction_double_begin() {
1381 let mut pm = PackageManager::new();
1382 assert!(pm.begin_transaction().is_ok());
1383 assert!(pm.begin_transaction().is_err());
1384 }
1385
1386 #[test]
1387 fn test_transaction_commit_without_begin() {
1388 let mut pm = PackageManager::new();
1389 assert!(pm.commit_transaction().is_err());
1390 }
1391
1392 #[test]
1393 fn test_transaction_rollback_without_begin() {
1394 let mut pm = PackageManager::new();
1395 assert!(pm.rollback_transaction().is_err());
1396 }
1397
1398 #[test]
1399 fn test_transaction_rollback() {
1400 let mut pm = PackageManager::new();
1401 pm.installed.insert(
1403 String::from("test-pkg"),
1404 PackageMetadata {
1405 name: String::from("test-pkg"),
1406 version: Version::new(1, 0, 0),
1407 author: String::new(),
1408 description: String::new(),
1409 license: String::new(),
1410 dependencies: vec![],
1411 conflicts: vec![],
1412 },
1413 );
1414
1415 assert!(pm.begin_transaction().is_ok());
1416 assert!(pm.remove(&String::from("test-pkg")).is_ok());
1418 assert!(!pm.is_installed(&String::from("test-pkg")));
1419 assert!(pm.rollback_transaction().is_ok());
1421 assert!(pm.is_installed(&String::from("test-pkg")));
1422 }
1423
1424 #[test]
1427 fn test_find_dependents() {
1428 let mut pm = PackageManager::new();
1429 pm.installed.insert(
1430 String::from("lib"),
1431 PackageMetadata {
1432 name: String::from("lib"),
1433 version: Version::new(1, 0, 0),
1434 author: String::new(),
1435 description: String::new(),
1436 license: String::new(),
1437 dependencies: vec![],
1438 conflicts: vec![],
1439 },
1440 );
1441 pm.installed.insert(
1442 String::from("app"),
1443 PackageMetadata {
1444 name: String::from("app"),
1445 version: Version::new(1, 0, 0),
1446 author: String::new(),
1447 description: String::new(),
1448 license: String::new(),
1449 dependencies: vec![Dependency {
1450 name: String::from("lib"),
1451 version_req: String::from("*"),
1452 }],
1453 conflicts: vec![],
1454 },
1455 );
1456 let deps = pm.find_dependents(&String::from("lib"));
1457 assert_eq!(deps.len(), 1);
1458 assert_eq!(deps[0], "app");
1459 }
1460
1461 #[test]
1462 fn test_remove_with_dependents_fails() {
1463 let mut pm = PackageManager::new();
1464 pm.installed.insert(
1465 String::from("lib"),
1466 PackageMetadata {
1467 name: String::from("lib"),
1468 version: Version::new(1, 0, 0),
1469 author: String::new(),
1470 description: String::new(),
1471 license: String::new(),
1472 dependencies: vec![],
1473 conflicts: vec![],
1474 },
1475 );
1476 pm.installed.insert(
1477 String::from("app"),
1478 PackageMetadata {
1479 name: String::from("app"),
1480 version: Version::new(1, 0, 0),
1481 author: String::new(),
1482 description: String::new(),
1483 license: String::new(),
1484 dependencies: vec![Dependency {
1485 name: String::from("lib"),
1486 version_req: String::from("*"),
1487 }],
1488 conflicts: vec![],
1489 },
1490 );
1491 assert!(pm.remove(&String::from("lib")).is_err());
1493 }
1494
1495 #[test]
1498 fn test_extract_package_files_too_short() {
1499 let data: &[u8] = &[0, 1, 2];
1500 assert!(extract_package_files("test", data).is_err());
1501 }
1502
1503 #[test]
1504 fn test_extract_package_files_zero_files() {
1505 let data: &[u8] = &[0, 0, 0, 0]; assert!(extract_package_files("test", data).is_ok());
1507 }
1508}
1509
1510fn create_directories(path: &str) -> PkgResult<()> {
1512 let components: Vec<&str> = path.split('/').filter(|s| !s.is_empty()).collect();
1514
1515 let mut current_path = String::new();
1516 for component in components {
1517 current_path.push('/');
1518 current_path.push_str(component);
1519
1520 #[cfg(feature = "alloc")]
1522 {
1523 use crate::fs::{get_vfs, Permissions};
1524 let perms = Permissions::from_mode(0o755);
1525 let _ = get_vfs().write().mkdir(¤t_path, perms);
1527 }
1528 }
1529
1530 Ok(())
1531}