Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
70 commits
Select commit Hold shift + click to select a range
10df11d
fix: Added new tests
justpav05 Aug 7, 2026
91e4b33
fix: Removed obsolete 'branch' parameter;
justpav05 Aug 8, 2026
8dde4a8
fix: Added tests for types
justpav05 Aug 8, 2026
9144fa8
fix: Update documentation
justpav05 Aug 8, 2026
c69f2e4
fix: Added database path to the image
justpav05 Aug 8, 2026
efbaa9e
Fix: Fixed the release profile in the Cargo.toml workspace.
justpav05 Aug 8, 2026
77ffaff
fix: Documentation updated
justpav05 Aug 8, 2026
6d7c207
New: Added a function to retrieve current user digests
justpav05 Aug 8, 2026
dc3d544
fix: Separated the first chapter of the documentation;
justpav05 Aug 9, 2026
b15ea2f
fix: Moved Chapter 1 to a new, separate .md file
justpav05 Aug 9, 2026
8bbc9a6
fix: Moved Chapter 2 to a new, separate .md file
justpav05 Aug 9, 2026
18619f4
fix: Added headers for reuse
justpav05 Aug 9, 2026
78ebc22
fix: Moved the third chapter
justpav05 Aug 11, 2026
bb4ce54
fix: Added new missing definitions
justpav05 Aug 11, 2026
6a87811
new: Added a file for managing Cargo
justpav05 Aug 11, 2026
0e7e91b
Fix: Partially ported chapter 5 (up to, but not including, 5.6)
justpav05 Aug 11, 2026
eeeaf90
fix: Added missing definitions
justpav05 Aug 11, 2026
24d7cd6
fix: Moved chapter 6.
justpav05 Aug 11, 2026
baac28e
fix: Added new definitions
justpav05 Aug 11, 2026
1b5e175
fix: Fixed file header containing a duplicate year
justpav05 Aug 11, 2026
c02ed72
fix: Fixed file header containing a duplicate year
justpav05 Aug 11, 2026
22ef966
fix: Chapter 7 moved
justpav05 Aug 11, 2026
4a6d8e6
fix: File corrections due to changes in the repository
justpav05 Aug 11, 2026
26b78d8
fix: Moved missing items
justpav05 Aug 11, 2026
baec92f
fix: Added missing definitions
justpav05 Aug 11, 2026
2127403
fix: Removed old documentation file
justpav05 Aug 11, 2026
6222d83
New: Added a mechanism for the automatic generation and updating of the
justpav05 Aug 11, 2026
1408019
fix: Moved item 4
justpav05 Aug 11, 2026
d8f9847
fix: Added header for reuse
justpav05 Aug 11, 2026
f175f48
fix: Removed unnecessary characters from filenames
justpav05 Aug 11, 2026
c66746d
Fix: Fixed the design section, corrected text inconsistencies, and
justpav05 Aug 11, 2026
b4de78e
fix: Added a new test
justpav05 Aug 12, 2026
04e10bc
fix: Fixed structure visibility
justpav05 Aug 12, 2026
aebaf38
fix: Renamed usr_digest to prefix_digest
justpav05 Aug 12, 2026
965e516
fix: Documentation updated
justpav05 Aug 12, 2026
c93512e
fix: Fixed an old usr_digest reference
justpav05 Aug 12, 2026
58c6ea5
new: Added a function to retrieve the list of deployments
justpav05 Aug 13, 2026
eec539a
New: Added separate diff requests for the `prefix` and `config`
justpav05 Aug 13, 2026
6bc3733
new: Distinguished between `diff` commands for `prefix` and `config`
justpav05 Aug 13, 2026
68f10c9
fix: Fixed formatting
justpav05 Aug 13, 2026
a01395a
fix: Fixed formatting
justpav05 Aug 13, 2026
df20b21
new: External diff functions stabilized
justpav05 Aug 13, 2026
6f9a2d3
fix: Documentation chapters 0 through 5 have been translated
justpav05 Aug 13, 2026
192b789
fix: Added the remaining chapters
justpav05 Aug 13, 2026
d2a9e6b
fix: Removed obsolete documentation file
justpav05 Aug 13, 2026
544cb70
fix: Fixed a line carried over from main
justpav05 Aug 13, 2026
7e82775
fix: fixed incorrect links in documents
justpav05 Aug 13, 2026
5afe672
fix: Fixed names
justpav05 Aug 13, 2026
bbd0d8c
fix: Removed linear function assemble
justpav05 Aug 13, 2026
880dc53
fix: Moved the assemble function
justpav05 Aug 13, 2026
e3f4a2e
fix: Renamed `etc history` to `config history`
justpav05 Aug 13, 2026
b87a3d9
fix: Renamed 'etc' to 'config'
justpav05 Aug 13, 2026
d364edf
New: A new command has been implemented
justpav05 Aug 13, 2026
f9d8046
New: automatic cleanup added for the new type
justpav05 Aug 13, 2026
c2039a2
new: added a new structure for diff commands
justpav05 Aug 13, 2026
1bc8155
new: Implemented two new commands
justpav05 Aug 13, 2026
02d6922
fix: Fixed 'etc' naming in config
justpav05 Aug 13, 2026
c6c2c4b
new: Added a separate type for package kind
justpav05 Aug 13, 2026
61a9515
fix: rename diff kind to file diff kind
justpav05 Aug 13, 2026
18f7bf6
fix: rename diff kind to file diff kind
justpav05 Aug 13, 2026
eec133a
fix: rename diff kind to file diff kind
justpav05 Aug 13, 2026
c5c3658
fix: rename diff kind to file diff kind and add new package diff to use
justpav05 Aug 13, 2026
90d9ae4
fix: fix formating
justpav05 Aug 13, 2026
7384efe
fix: tests moved to a separate file
justpav05 Aug 13, 2026
3b720d8
fix: updated header in README
justpav05 Aug 13, 2026
d2bbad0
fix: updated header in README
justpav05 Aug 13, 2026
a4ad131
fix: updated header in README
justpav05 Aug 13, 2026
aa2bd51
new: new types implemented
justpav05 Aug 13, 2026
786d791
new: implemented the diff command
justpav05 Aug 14, 2026
8d85bbe
Merge remote-tracking branch 'github/main' into merge-main-into-lib-rs
justpav05 Aug 14, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ members = ["user/upac-cli", "user/sign-cli", "lib/macro", "lib/abi", "lib/lib" ,
version = "0.1.5"
edition = "2024"
license = "GPL-3.0-only"
repository = "https://github.com/justpav05/upac"
repository = "https://github.com/SmoothTeam/upac"
authors = ["justpav05 <aksenovpaveldmitrievich@gmail.com>"]

[workspace.dependencies]
Expand Down
10 changes: 5 additions & 5 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
# 📦 Upac

[![GitHub](https://img.shields.io/badge/GitHub-justpav05%2Fupac-181717?logo=github)](https://github.com/justpav05/upac)
[![Version](https://img.shields.io/badge/version-0.1.5-green)](https://github.com/justpav05/upac/releases)
[![REUSE status](https://api.reuse.software/badge/github.com/justpav05/upac)](https://api.reuse.software/info/github.com/justpav05/upac)
[![lib: LGPL-3.0-or-later](https://img.shields.io/badge/lib-LGPL--3.0--or--later-blue.svg)](LICENSES/LGPL-3.0-or-later.txt)
[![cli: GPL-3.0-only](https://img.shields.io/badge/cli-GPL--3.0--only-blue.svg)](LICENSES/GPL-3.0-only.txt)
Links for repositories: [![GitHub](https://img.shields.io/badge/GitHub-SmoothTeam%2Fupac-181717?logo=github)](https://github.com/SmoothTeam/upac) [![Codeberg](https://img.shields.io/badge/Codeberg-justpav05%2Fupac-2185D0?logo=codeberg)](https://codeberg.org/justpav05/upac)

General information: [![Version](https://img.shields.io/badge/version-0.1.5-green)](https://github.com/SmoothTeam/upac/releases) [![REUSE status](https://api.reuse.software/badge/github.com/SmoothTeam/upac)](https://api.reuse.software/info/github.com/SmoothTeam/upac)

Licensing: [![lib: LGPL-3.0-or-later](https://img.shields.io/badge/lib-LGPL--3.0--or--later-blue.svg)](LICENSES/LGPL-3.0-or-later.txt) [![cli: GPL-3.0-only](https://img.shields.io/badge/cli-GPL--3.0--only-blue.svg)](LICENSES/GPL-3.0-only.txt)

> **⚠️ Branch in progress.** This branch (`lib-rs`) is a from-scratch rewrite of upac's core library in Rust, built around [composefs](https://github.com/containers/composefs) instead of OSTree. The FFI/orchestration engine is done; the actual command bodies, the hook system, and packaging are still being implemented — expect gaps and `todo!()`s.

Expand Down
2 changes: 1 addition & 1 deletion SECURITY.md
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ only the latest commit on `main` is supported. Security fixes are not backported

Please use one of the following instead of opening a public issue:

- GitHub's [private vulnerability reporting](https://github.com/justpav05/upac/security/advisories/new)
- GitHub's [private vulnerability reporting](https://github.com/SmoothTeam/upac/security/advisories/new)
(Security tab → "Report a vulnerability")
- Email: aksenovpaveldmitrievich@gmail.com

Expand Down
40 changes: 34 additions & 6 deletions lib/abi/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -18,18 +18,46 @@ pub const ABI_VERSION: u32 = 2;

#[repr(u8)]
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum DiffKind {
pub enum FileDiffKind {
Added = 0,
Removed = 1,
Modified = 2,
}

impl DiffKind {
pub fn from_u8(version: u8) -> Result<DiffKind, ErrorKind> {
impl FileDiffKind {
pub fn from_u8(version: u8) -> Result<FileDiffKind, ErrorKind> {
match version {
0 => Ok(DiffKind::Added),
1 => Ok(DiffKind::Removed),
2 => Ok(DiffKind::Modified),
0 => Ok(FileDiffKind::Added),
1 => Ok(FileDiffKind::Removed),
2 => Ok(FileDiffKind::Modified),
_ => Err(ErrorKind::InvalidEntry),
}
}
}

// A package's own metadata can be Added/Removed/Modified — or unchanged while
// one of its own files changed underneath it (e.g. a hand-edited is_user file),
// which FileDiffKind's three variants can't represent. Kept separate rather
// than adding a fourth variant to FileDiffKind, since every file-level
// consumer (DiffPrefixFileEntry/DiffConfigFileEntry/DiffUntrackedFileEntry) is
// already a complete, correct 3-way split — a package-only concept doesn't
// belong there.
#[repr(u8)]
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum PackageDiffKind {
Added = 0,
Removed = 1,
Modified = 2,
FilesChanged = 3,
}

impl PackageDiffKind {
pub fn from_u8(version: u8) -> Result<PackageDiffKind, ErrorKind> {
match version {
0 => Ok(PackageDiffKind::Added),
1 => Ok(PackageDiffKind::Removed),
2 => Ok(PackageDiffKind::Modified),
3 => Ok(PackageDiffKind::FilesChanged),
_ => Err(ErrorKind::InvalidEntry),
}
}
Expand Down
14 changes: 7 additions & 7 deletions lib/abi/src/request.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ use std::os::raw::c_void;

use upac_macro::CValidate;

use crate::DiffKind;
use crate::FileDiffKind;
use crate::error::ErrorKind;
use crate::hook::{CancelToken, HookMessageFn};
use crate::package::{CPackageInfo, CUnpackedPackage};
Expand Down Expand Up @@ -74,7 +74,7 @@ pub struct CRollbackRequest {
pub base: CRequestBase,

pub tmp_path: CSlice,
pub commit_hash: CSlice,
pub config_digest: CSlice,
}

#[repr(C)]
Expand All @@ -100,7 +100,7 @@ pub struct CFilesRequest {
#[optional]
pub message: CSlice,
pub files: CVec<CSlice>,
pub file_kind: DiffKind,
pub file_kind: FileDiffKind,
pub file_package: *const CPackageInfo,
}

Expand Down Expand Up @@ -154,9 +154,9 @@ pub struct CDiffFilesConfigRequest {
pub base: CRequestBase,

#[optional]
pub from_commit_hash: CSlice,
pub from_config_digest: CSlice,
#[optional]
pub to_commit_hash: CSlice,
pub to_config_digest: CSlice,
}

#[repr(C)]
Expand All @@ -178,9 +178,9 @@ pub struct CDiffRequest {
pub base: CRequestBase,

#[optional]
pub from_commit_hash: CSlice,
pub from_prefix_digest: CSlice,
#[optional]
pub to_commit_hash: CSlice,
pub to_prefix_digest: CSlice,
}

#[repr(C)]
Expand Down
19 changes: 14 additions & 5 deletions lib/abi/src/response.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,18 +5,18 @@

use upac_macro::{CFree, CValidate};

use crate::DiffKind;
use crate::error::ErrorKind;
use crate::memory::{free_cslice, free_cvec_owning};
use crate::package::{CPackageMeta, CVersion};
use crate::types::{CSlice, CVec, check_size};
use crate::{FileDiffKind, PackageDiffKind};

#[repr(C)]
#[derive(CFree)]
pub struct CDiffPackageEntry {
pub struct_size: usize,
pub name: CSlice,
pub kind: DiffKind,
pub kind: PackageDiffKind,
pub version: CVersion,
pub files: CVec<CDiffPrefixFileEntry>,
}
Expand All @@ -27,7 +27,7 @@ pub struct CDiffPrefixFileEntry {
pub struct_size: usize,

pub path: CSlice,
pub kind: DiffKind,
pub kind: FileDiffKind,
pub package_name: CSlice,
pub is_user: bool,
}
Expand All @@ -38,11 +38,20 @@ pub struct CDiffConfigFileEntry {
pub struct_size: usize,

pub path: CSlice,
pub kind: DiffKind,
pub kind: FileDiffKind,
#[optional]
pub package_name: CSlice,
}

#[repr(C)]
#[derive(CFree, CValidate)]
pub struct CDiffUntrackedFileEntry {
pub struct_size: usize,

pub path: CSlice,
pub kind: FileDiffKind,
}

#[repr(C)]
#[derive(CFree, CValidate)]
pub struct CCommitEntry {
Expand Down Expand Up @@ -231,8 +240,8 @@ impl CDiffPackagesResponse {
#[repr(C)]
pub struct CDiffResponse {
pub struct_size: usize,
pub unattached_files: CVec<CDiffPrefixFileEntry>,
pub diff_packages: CVec<CDiffPackageEntry>,
pub unattached_files: CVec<CDiffUntrackedFileEntry>,
}

impl CDiffResponse {
Expand Down
157 changes: 157 additions & 0 deletions lib/lib/src/composefs/diff.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,157 @@
// SPDX-FileCopyrightText: 2026 JustPav
// SPDX-FileCopyrightText: 2026 JustPav
//
// SPDX-License-Identifier: LGPL-3.0-or-later

use std::cmp::Ordering;
use std::path::{Path, PathBuf};

use composefs::tree::{Directory, FileSystem, Inode, Leaf, LeafContent, RegularFile};
use upac_abi::FileDiffKind;

use crate::composefs::repository::ObjectID;

macro_rules! advance_subtree {
($differ:expr, $prefix:expr, $entries:expr, $next:expr, $name:expr, $inode:expr, $side:expr) => {{
$differ.mark_subtree(&$prefix.join($name), $inode, $side);
$next = $entries.next();
}};
}

#[derive(Clone, Copy)]
enum Side {
From,
To,
}

impl Side {
fn kind(self) -> FileDiffKind {
match self {
Side::From => FileDiffKind::Removed,
Side::To => FileDiffKind::Added,
}
}
}

pub struct TreeDiff<'a> {
from_leaves: &'a [Leaf<ObjectID>],
to_leaves: &'a [Leaf<ObjectID>],
changes: Vec<(String, FileDiffKind)>,
}

impl<'a> TreeDiff<'a> {
pub fn run(from: &'a FileSystem<ObjectID>, to: &'a FileSystem<ObjectID>) -> Vec<(String, FileDiffKind)> {
let mut differ = Self {
from_leaves: &from.leaves,
to_leaves: &to.leaves,
changes: Vec::new(),
};

differ.compare_directories(&PathBuf::new(), &from.root, &to.root);

differ.changes
}

fn compare_directories(&mut self, prefix: &Path, from_dir: &Directory<ObjectID>, to_dir: &Directory<ObjectID>) {
let mut from_entries = from_dir.sorted_entries();
let mut to_entries = to_dir.sorted_entries();

let mut from_next = from_entries.next();
let mut to_next = to_entries.next();

loop {
match (from_next, to_next) {
(Some((from_name, from_inode)), Some((to_name, to_inode))) => match from_name.cmp(to_name) {
Ordering::Less => {
advance_subtree!(self, prefix, from_entries, from_next, from_name, from_inode, Side::From)
}
Ordering::Greater => {
advance_subtree!(self, prefix, to_entries, to_next, to_name, to_inode, Side::To)
}
Ordering::Equal => {
self.compare_matched_entry(&prefix.join(from_name), from_inode, to_inode);
from_next = from_entries.next();
to_next = to_entries.next();
}
},
(Some((from_name, from_inode)), None) => {
advance_subtree!(self, prefix, from_entries, from_next, from_name, from_inode, Side::From)
}
(None, Some((to_name, to_inode))) => {
advance_subtree!(self, prefix, to_entries, to_next, to_name, to_inode, Side::To)
}
(None, None) => break,
}
}
}

fn compare_matched_entry(&mut self, path: &Path, from_inode: &Inode<ObjectID>, to_inode: &Inode<ObjectID>) {
match (from_inode, to_inode) {
(Inode::Directory(from_sub), Inode::Directory(to_sub)) => {
self.compare_directories(path, from_sub, to_sub);
}
(Inode::Leaf(from_id, _), Inode::Leaf(to_id, _)) => {
let from_leaf = &self.from_leaves[from_id.0];
let to_leaf = &self.to_leaves[to_id.0];

if Self::is_regular_or_symlink(from_leaf)
&& Self::is_regular_or_symlink(to_leaf)
&& !Self::content_matches(from_leaf, to_leaf)
{
self.changes.push((Self::path_to_string(path), FileDiffKind::Modified));
}
}
(from_inode, to_inode) => {
self.mark_subtree(path, from_inode, Side::From);
self.mark_subtree(path, to_inode, Side::To);
}
}
}

fn mark_subtree(&mut self, path: &Path, inode: &Inode<ObjectID>, side: Side) {
match inode {
Inode::Leaf(id, _) => {
let leaf = &self.leaves(side)[id.0];

if Self::is_regular_or_symlink(leaf) {
self.changes.push((Self::path_to_string(path), side.kind()));
}
}
Inode::Directory(dir) => {
for (name, child) in dir.sorted_entries() {
self.mark_subtree(&path.join(name), child, side);
}
}
}
}

fn leaves(&self, side: Side) -> &'a [Leaf<ObjectID>] {
match side {
Side::From => self.from_leaves,
Side::To => self.to_leaves,
}
}

fn is_regular_or_symlink(leaf: &Leaf<ObjectID>) -> bool {
matches!(leaf.content, LeafContent::Regular(_) | LeafContent::Symlink(_))
}

fn content_matches(from: &Leaf<ObjectID>, to: &Leaf<ObjectID>) -> bool {
match (&from.content, &to.content) {
(
LeafContent::Regular(RegularFile::External(from_id, from_size)),
LeafContent::Regular(RegularFile::External(to_id, to_size)),
) => from_id == to_id && from_size == to_size,
(
LeafContent::Regular(RegularFile::Inline(from_bytes)),
LeafContent::Regular(RegularFile::Inline(to_bytes)),
) => from_bytes == to_bytes,
(LeafContent::Symlink(from_target), LeafContent::Symlink(to_target)) => from_target == to_target,
_ => false,
}
}

fn path_to_string(path: &Path) -> String {
path.to_string_lossy().into_owned()
}
}
1 change: 1 addition & 0 deletions lib/lib/src/composefs/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
//
// SPDX-License-Identifier: LGPL-3.0-or-later

pub mod diff;
pub mod error;
pub mod file;
pub mod repository;
Loading