Get basic package installation implemented, with several TODOS left.
This commit is contained in:
parent
f4e87ee500
commit
13dff6bd96
6
Cargo.lock
generated
6
Cargo.lock
generated
@ -282,9 +282,9 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "crossbeam-channel"
|
name = "crossbeam-channel"
|
||||||
version = "0.5.7"
|
version = "0.5.8"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "cf2b3e8478797446514c91ef04bafcb59faba183e621ad488df88983cc14128c"
|
checksum = "a33c2bf77f2df06183c3aa30d1e96c0695a313d4f9c453cc3762a6db39f99200"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"cfg-if",
|
"cfg-if",
|
||||||
"crossbeam-utils",
|
"crossbeam-utils",
|
||||||
@ -560,7 +560,7 @@ dependencies = [
|
|||||||
[[package]]
|
[[package]]
|
||||||
name = "hpk-package"
|
name = "hpk-package"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
source = "git+https://git.hitchhiker-linux.org/jeang3nie/hpk-package.git#acbdf2d99284b69a48601f3aeb0a56f295872a4c"
|
source = "git+https://git.hitchhiker-linux.org/jeang3nie/hpk-package.git#00683722d64a9c2f619e6c5378ab7b571b32ecc8"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"chrono",
|
"chrono",
|
||||||
"deku",
|
"deku",
|
||||||
|
@ -16,6 +16,8 @@ There is no top level directory in a package archive. Files intended to go into
|
|||||||
```Sh
|
```Sh
|
||||||
.
|
.
|
||||||
├── package.ron
|
├── package.ron
|
||||||
|
├── appdata.xml
|
||||||
|
├── postinstall.sh
|
||||||
├── etc
|
├── etc
|
||||||
│ └── hpk
|
│ └── hpk
|
||||||
│ └── conf.ron.new
|
│ └── conf.ron.new
|
||||||
@ -33,7 +35,6 @@ There is no top level directory in a package archive. Files intended to go into
|
|||||||
└── man8
|
└── man8
|
||||||
└── hpk-package-format.8
|
└── hpk-package-format.8
|
||||||
|
|
||||||
11 directories, 7 files
|
|
||||||
```
|
```
|
||||||
Files inside of *etc* and *var* are expected to be configurable by the system
|
Files inside of *etc* and *var* are expected to be configurable by the system
|
||||||
administrator or may change at runtime, respectively, and so are not overwritten
|
administrator or may change at runtime, respectively, and so are not overwritten
|
||||||
@ -50,7 +51,7 @@ to start at boot or other related tasks.
|
|||||||
The file *package.ron* contains all of the required metadata about a package, such
|
The file *package.ron* contains all of the required metadata about a package, such
|
||||||
as a list of files to be installed, their checksums, runtime dependencies etc.
|
as a list of files to be installed, their checksums, runtime dependencies etc.
|
||||||
|
|
||||||
## package.ron
|
## package.ron [required]
|
||||||
An example *package.ron*
|
An example *package.ron*
|
||||||
```Ron
|
```Ron
|
||||||
Package(
|
Package(
|
||||||
@ -95,7 +96,6 @@ Package(
|
|||||||
gid: Some(42),
|
gid: Some(42),
|
||||||
),
|
),
|
||||||
]),
|
]),
|
||||||
post_install: Some("bar -u baz")
|
|
||||||
)
|
)
|
||||||
```
|
```
|
||||||
### Descriptions of the fields
|
### Descriptions of the fields
|
||||||
@ -116,3 +116,15 @@ Package(
|
|||||||
do not already exist
|
do not already exist
|
||||||
- post_install: an optional Posix shell script (minus shebang line) to be run
|
- post_install: an optional Posix shell script (minus shebang line) to be run
|
||||||
after installing the package
|
after installing the package
|
||||||
|
|
||||||
|
## appdata.xml [optional]
|
||||||
|
This is an [appstream](https://www.freedesktop.org/wiki/Distributions/AppStream/) metadata
|
||||||
|
file used primarily by software stores or graphical package managers. If the source
|
||||||
|
distribution includes this file, it should be included in the package. If the source
|
||||||
|
distribution does not contain this file, then it can be omitted or an appropriate file
|
||||||
|
generated at the packager's discretion.
|
||||||
|
|
||||||
|
## postinstall.sh [optional]
|
||||||
|
This is a POSIX shell compatible script to be run in order to finish installation. Heavy
|
||||||
|
use of this capability is discouraged. If the desired action could be provided by hooks
|
||||||
|
[see src/hooks/mod.rs] then please file a bug report to request the feature be added.
|
||||||
|
@ -25,6 +25,7 @@ pub enum Message {
|
|||||||
Failure(String),
|
Failure(String),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Creates a package archive
|
||||||
pub struct Creator {
|
pub struct Creator {
|
||||||
path: PathBuf,
|
path: PathBuf,
|
||||||
entries: Vec<PathBuf>,
|
entries: Vec<PathBuf>,
|
||||||
|
@ -26,7 +26,11 @@ pub struct Update {
|
|||||||
|
|
||||||
impl fmt::Display for Update {
|
impl fmt::Display for Update {
|
||||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||||
write!(f, "{}-{}_{}_{}", self.name, self.version, self.arch, self.release)
|
write!(
|
||||||
|
f,
|
||||||
|
"{}-{}_{}_{}",
|
||||||
|
self.name, self.version, self.arch, self.release
|
||||||
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,6 +1,24 @@
|
|||||||
use std::{io, path::{PathBuf, Path}, ffi::OsStr, sync::mpsc::Sender};
|
#![allow(dead_code)]
|
||||||
pub use error::InstallError;
|
pub use error::InstallError;
|
||||||
use hpk_package::{Package, User, Group};
|
use hpk_package::{
|
||||||
|
sha2::{Digest, Sha256},
|
||||||
|
tar::Archive,
|
||||||
|
Entry, Group, Package, User,
|
||||||
|
};
|
||||||
|
use rayon::prelude::{IntoParallelRefIterator, ParallelIterator};
|
||||||
|
use std::{
|
||||||
|
ffi::OsStr,
|
||||||
|
fmt::Write as _,
|
||||||
|
fs::{self, DirBuilder, File},
|
||||||
|
io::{self, BufWriter, Write},
|
||||||
|
os::{
|
||||||
|
self,
|
||||||
|
unix::{fs::DirBuilderExt, prelude::OpenOptionsExt},
|
||||||
|
},
|
||||||
|
path::{Path, PathBuf},
|
||||||
|
sync::{mpsc::Sender, Mutex},
|
||||||
|
};
|
||||||
|
use zstd::Decoder;
|
||||||
|
|
||||||
pub struct Link {
|
pub struct Link {
|
||||||
pub path: PathBuf,
|
pub path: PathBuf,
|
||||||
@ -8,6 +26,8 @@ pub struct Link {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub enum InstallMessage {
|
pub enum InstallMessage {
|
||||||
|
ArchiveRead,
|
||||||
|
ArchiveLen(usize),
|
||||||
FileInstalled(PathBuf),
|
FileInstalled(PathBuf),
|
||||||
DirectoryCreated(PathBuf),
|
DirectoryCreated(PathBuf),
|
||||||
LinkCreated(Link),
|
LinkCreated(Link),
|
||||||
@ -17,6 +37,8 @@ pub enum InstallMessage {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub struct Installer<T: io::Read> {
|
pub struct Installer<T: io::Read> {
|
||||||
|
/// The filesystem root under which to install this package.
|
||||||
|
/// Normally this is "/".
|
||||||
path: PathBuf,
|
path: PathBuf,
|
||||||
package: Package,
|
package: Package,
|
||||||
reader: T,
|
reader: T,
|
||||||
@ -25,22 +47,146 @@ pub struct Installer<T: io::Read> {
|
|||||||
impl<T: io::Read> Installer<T> {
|
impl<T: io::Read> Installer<T> {
|
||||||
pub fn new<P: AsRef<OsStr>>(path: P, package: Package, reader: T) -> Self {
|
pub fn new<P: AsRef<OsStr>>(path: P, package: Package, reader: T) -> Self {
|
||||||
let path = Path::new(&path).to_path_buf();
|
let path = Path::new(&path).to_path_buf();
|
||||||
Self { path, package, reader }
|
Self {
|
||||||
|
path,
|
||||||
|
package,
|
||||||
|
reader,
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn install(&mut self, sender: Sender<InstallMessage>) -> Result<(), Box<InstallError>> {
|
pub fn install(self, sender: Sender<InstallMessage>) -> Result<(), InstallError> {
|
||||||
unimplemented!();
|
let reader = Decoder::new(self.reader)?;
|
||||||
|
let mut archive = Archive::read(reader)?;
|
||||||
|
sender.send(InstallMessage::ArchiveRead)?;
|
||||||
|
let pr_node = match archive.pop("package.ron") {
|
||||||
|
Some(node) => node,
|
||||||
|
None => return Err(InstallError::MissingManifest),
|
||||||
|
};
|
||||||
|
let pinstall = archive.pop("postinstall.sh");
|
||||||
|
let appstream = archive.pop("appdata.xml");
|
||||||
|
let len = archive.nodes.len();
|
||||||
|
sender.send(InstallMessage::ArchiveLen(len))?;
|
||||||
|
let mut db_pkgdir = crate::get_dbdir(Some(self.path.clone()));
|
||||||
|
db_pkgdir.push(&self.package.name);
|
||||||
|
if !db_pkgdir.exists() {
|
||||||
|
fs::create_dir_all(&db_pkgdir)?;
|
||||||
|
}
|
||||||
|
let mut db_file = db_pkgdir.clone();
|
||||||
|
db_file.push("package.ron");
|
||||||
|
if db_file.exists() {
|
||||||
|
fs::remove_file(&db_file)?;
|
||||||
|
}
|
||||||
|
let fd = File::create(&db_file)?;
|
||||||
|
let writer = BufWriter::new(fd);
|
||||||
|
pr_node.write(writer)?;
|
||||||
|
let path = &self.path;
|
||||||
|
let package = &self.package;
|
||||||
|
let sender = Mutex::new(sender);
|
||||||
|
archive.nodes.par_iter().try_for_each(|node| {
|
||||||
|
let mut path = path.clone();
|
||||||
|
let fpath = node.header.file_path()?;
|
||||||
|
let entry = package
|
||||||
|
.plist
|
||||||
|
.entries
|
||||||
|
.iter()
|
||||||
|
.find(|&x| match x {
|
||||||
|
Entry::File {
|
||||||
|
path,
|
||||||
|
sha256sum: _,
|
||||||
|
mode: _,
|
||||||
|
size: _,
|
||||||
|
} => path == &fpath,
|
||||||
|
Entry::Link { path, target: _ } => path == &fpath,
|
||||||
|
Entry::Directory { path, mode: _ } => path == &fpath,
|
||||||
|
})
|
||||||
|
.unwrap();
|
||||||
|
path = path.join(&fpath);
|
||||||
|
if let Some(parent) = path.parent() {
|
||||||
|
if !parent.exists() {
|
||||||
|
fs::create_dir_all(&parent)?;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if path.exists() {
|
||||||
|
if path.is_dir() {
|
||||||
|
fs::remove_dir(&path)?;
|
||||||
|
} else if path.is_symlink() || path.is_file() {
|
||||||
|
fs::remove_file(&path)?;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
match entry {
|
||||||
|
Entry::Directory { path: _, mode } => {
|
||||||
|
DirBuilder::new().mode(*mode).create(&path)?;
|
||||||
|
sender
|
||||||
|
.lock()
|
||||||
|
.unwrap()
|
||||||
|
.send(InstallMessage::DirectoryCreated(path))?;
|
||||||
|
}
|
||||||
|
Entry::Link { path: _, target } => {
|
||||||
|
os::unix::fs::symlink(&path, &target)?;
|
||||||
|
sender
|
||||||
|
.lock()
|
||||||
|
.unwrap()
|
||||||
|
.send(InstallMessage::LinkCreated(Link {
|
||||||
|
path,
|
||||||
|
target: target.clone(),
|
||||||
|
}))?;
|
||||||
|
}
|
||||||
|
Entry::File {
|
||||||
|
path: _,
|
||||||
|
sha256sum,
|
||||||
|
mode,
|
||||||
|
size: _,
|
||||||
|
} => {
|
||||||
|
let mut buf = vec![];
|
||||||
|
node.clone().write(&mut buf)?;
|
||||||
|
let mut sum = String::new();
|
||||||
|
let mut hasher = Sha256::new();
|
||||||
|
hasher.update(&buf);
|
||||||
|
let res = hasher.finalize();
|
||||||
|
for c in res {
|
||||||
|
write!(sum, "{c:02x}")?;
|
||||||
|
}
|
||||||
|
if sha256sum != &sum {
|
||||||
|
return Err(InstallError::ChecksumMismatch);
|
||||||
|
}
|
||||||
|
let fd = File::options().mode(*mode).write(true).open(&path)?;
|
||||||
|
let mut writer = BufWriter::new(fd);
|
||||||
|
writer.write_all(&buf)?;
|
||||||
|
sender
|
||||||
|
.lock()
|
||||||
|
.unwrap()
|
||||||
|
.send(InstallMessage::FileInstalled(path))?;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Ok::<(), InstallError>(())
|
||||||
|
})?;
|
||||||
|
let sender = sender.into_inner()?;
|
||||||
|
if let Some(node) = appstream {
|
||||||
|
let mut appdatafile = db_pkgdir;
|
||||||
|
appdatafile.push("appdata.xml");
|
||||||
|
let fd = File::open(appdatafile)?;
|
||||||
|
let writer = BufWriter::new(fd);
|
||||||
|
node.write(writer)?;
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
mod error {
|
mod error {
|
||||||
use std::{io, fmt, error::Error};
|
|
||||||
use hpk_package::tar;
|
use hpk_package::tar;
|
||||||
|
use std::{error::Error, fmt, io, sync::{mpsc::{SendError, Sender}, PoisonError}};
|
||||||
|
|
||||||
|
use super::InstallMessage;
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub enum InstallError {
|
pub enum InstallError {
|
||||||
|
Fmt(fmt::Error),
|
||||||
IO(io::Error),
|
IO(io::Error),
|
||||||
|
SendError(SendError<InstallMessage>),
|
||||||
Tar(tar::Error),
|
Tar(tar::Error),
|
||||||
|
MissingManifest,
|
||||||
|
MutexError,
|
||||||
|
ChecksumMismatch,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl fmt::Display for InstallError {
|
impl fmt::Display for InstallError {
|
||||||
@ -52,15 +198,42 @@ mod error {
|
|||||||
impl Error for InstallError {
|
impl Error for InstallError {
|
||||||
fn source(&self) -> Option<&(dyn Error + 'static)> {
|
fn source(&self) -> Option<&(dyn Error + 'static)> {
|
||||||
match self {
|
match self {
|
||||||
|
Self::Fmt(e) => Some(e),
|
||||||
Self::IO(e) => Some(e),
|
Self::IO(e) => Some(e),
|
||||||
Self::Tar(e) => Some(e),
|
Self::Tar(e) => Some(e),
|
||||||
|
Self::SendError(e) => Some(e),
|
||||||
|
_ => None,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl From<fmt::Error> for InstallError {
|
||||||
|
fn from(value: fmt::Error) -> Self {
|
||||||
|
Self::Fmt(value)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl From<io::Error> for InstallError {
|
impl From<io::Error> for InstallError {
|
||||||
fn from(value: io::Error) -> Self {
|
fn from(value: io::Error) -> Self {
|
||||||
Self::IO(value)
|
Self::IO(value)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl From<PoisonError<Sender<InstallMessage>>> for InstallError {
|
||||||
|
fn from(_value: PoisonError<Sender<InstallMessage>>) -> Self {
|
||||||
|
Self::MutexError
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<SendError<InstallMessage>> for InstallError {
|
||||||
|
fn from(value: SendError<InstallMessage>) -> Self {
|
||||||
|
Self::SendError(value)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<hpk_package::tar::Error> for InstallError {
|
||||||
|
fn from(value: hpk_package::tar::Error) -> Self {
|
||||||
|
Self::Tar(value)
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user