* add support for idmapped mounts to start-sdk

* misc fixes

* misc fixes

* add default to textarea

* fix iptables masquerade rule

* fix textarea types

* more fixes

* better logging for rsync

* fix tty size

* fix wg conf generation for android

* disable file mounts on dependencies

* mostly there, some styling issues (#3069)

* mostly there, some styling issues

* fix: address comments (#3070)

* fix: address comments

* fix: fix

* show SSL for any address with secure protocol and ssl added

* better sorting and messaging

---------

Co-authored-by: Alex Inkin <alexander@inkin.ru>

* fixes for nextcloud

* allow sidebar navigation during service state traansitions

* wip: x-forwarded headers

* implement x-forwarded-for proxy

* lowercase domain names and fix warning popover bug

* fix http2 websockets

* fix websocket retry behavior

* add arch filters to s9pk pack

* use docker for start-cli install

* add version range to package signer on registry

* fix rcs < 0

* fix user information parsing

* refactor service interface getters

* disable idmaps

* build fixes

* update docker login action

* streamline build

* add start-cli workflow

* rename

* riscv64gc

* fix ui packing

* no default features on cli

* make cli depend on GIT_HASH

* more build fixes

* more build fixes

* interpolate arch within dockerfile

* fix tests

* add launch ui to service page plus other small improvements (#3075)

* add launch ui to service page plus other small improvements

* revert translation disable

* add spinner to service list if service is health and loading

* chore: some visual tune up

* chore: update Taiga UI

---------

Co-authored-by: waterplea <alexander@inkin.ru>

* fix backups

* feat: use arm hosted runners and don't fail when apt package does not exist (#3076)

---------

Co-authored-by: Matt Hill <mattnine@protonmail.com>
Co-authored-by: Shadowy Super Coder <musashidisciple@proton.me>
Co-authored-by: Matt Hill <MattDHill@users.noreply.github.com>
Co-authored-by: Alex Inkin <alexander@inkin.ru>
Co-authored-by: Remco Ros <remcoros@live.nl>
This commit is contained in:
Aiden McClelland
2025-12-15 13:30:50 -07:00
committed by GitHub
parent b945243d1a
commit 0430e0f930
148 changed files with 2572 additions and 1761 deletions

View File

@@ -19,6 +19,11 @@ pub enum FileType {
Directory,
Infer,
}
impl Default for FileType {
fn default() -> Self {
FileType::Directory
}
}
pub struct Bind<Src: AsRef<Path>> {
src: Src,

View File

@@ -2,34 +2,86 @@ use std::ffi::OsStr;
use std::fmt::Display;
use std::os::unix::fs::MetadataExt;
use std::path::Path;
use std::str::FromStr;
use clap::Parser;
use clap::builder::ValueParserFactory;
use digest::generic_array::GenericArray;
use digest::{Digest, OutputSizeUser};
use models::FromStrParser;
use serde::{Deserialize, Serialize};
use sha2::Sha256;
use tokio::process::Command;
use ts_rs::TS;
use super::{FileSystem, MountType};
use crate::disk::mount::filesystem::default_mount_command;
use super::FileSystem;
use crate::prelude::*;
use crate::util::Invoke;
#[derive(Clone, Copy, Debug, Deserialize, Serialize, Parser, TS)]
#[serde(rename_all = "camelCase")]
pub struct IdMap {
pub from_id: u32,
pub to_id: u32,
pub range: u32,
}
impl IdMap {
pub fn stack(a: Vec<IdMap>, b: Vec<IdMap>) -> Vec<IdMap> {
let mut res = Vec::with_capacity(a.len() + b.len());
res.extend_from_slice(&a);
for mut b in b {
for a in &a {
if a.from_id <= b.to_id && a.from_id + a.range > b.to_id {
b.to_id += a.to_id;
}
}
res.push(b);
}
res
}
}
impl FromStr for IdMap {
type Err = Error;
fn from_str(s: &str) -> Result<Self, Self::Err> {
let split = s.splitn(3, ":").collect::<Vec<_>>();
if let Some([u, k, r]) = split.get(0..3) {
Ok(Self {
from_id: u.parse()?,
to_id: k.parse()?,
range: r.parse()?,
})
} else if let Some([u, k]) = split.get(0..2) {
Ok(Self {
from_id: u.parse()?,
to_id: k.parse()?,
range: 1,
})
} else {
Err(Error::new(
eyre!("{s} is not a valid idmap"),
ErrorKind::ParseNumber,
))
}
}
}
impl ValueParserFactory for IdMap {
type Parser = FromStrParser<IdMap>;
fn value_parser() -> Self::Parser {
<Self::Parser>::new()
}
}
#[derive(Debug, Deserialize, Serialize)]
#[serde(rename_all = "camelCase")]
pub struct IdMapped<Fs: FileSystem> {
filesystem: Fs,
from_id: u32,
to_id: u32,
range: u32,
idmap: Vec<IdMap>,
}
impl<Fs: FileSystem> IdMapped<Fs> {
pub fn new(filesystem: Fs, from_id: u32, to_id: u32, range: u32) -> Self {
Self {
filesystem,
from_id,
to_id,
range,
}
pub fn new(filesystem: Fs, idmap: Vec<IdMap>) -> Self {
Self { filesystem, idmap }
}
}
impl<Fs: FileSystem> FileSystem for IdMapped<Fs> {
@@ -44,12 +96,17 @@ impl<Fs: FileSystem> FileSystem for IdMapped<Fs> {
.mount_options()
.into_iter()
.map(|a| Box::new(a) as Box<dyn Display>)
.chain(std::iter::once(Box::new(lazy_format!(
"X-mount.idmap=b:{}:{}:{}",
self.from_id,
self.to_id,
self.range,
)) as Box<dyn Display>))
.chain(if self.idmap.is_empty() {
None
} else {
use std::fmt::Write;
let mut option = "X-mount.idmap=".to_owned();
for i in &self.idmap {
write!(&mut option, "b:{}:{}:{} ", i.from_id, i.to_id, i.range).unwrap();
}
Some(Box::new(option) as Box<dyn Display>)
})
}
async fn source(&self) -> Result<Option<impl AsRef<Path>>, Error> {
self.filesystem.source().await
@@ -57,26 +114,28 @@ impl<Fs: FileSystem> FileSystem for IdMapped<Fs> {
async fn pre_mount(&self, mountpoint: &Path) -> Result<(), Error> {
self.filesystem.pre_mount(mountpoint).await?;
let info = tokio::fs::metadata(mountpoint).await?;
let uid_in_range = self.from_id <= info.uid() && self.from_id + self.range > info.uid();
let gid_in_range = self.from_id <= info.gid() && self.from_id + self.range > info.gid();
if uid_in_range || gid_in_range {
Command::new("chown")
.arg(format!(
"{uid}:{gid}",
uid = if uid_in_range {
self.to_id + info.uid() - self.from_id
} else {
info.uid()
},
gid = if gid_in_range {
self.to_id + info.gid() - self.from_id
} else {
info.gid()
},
))
.arg(&mountpoint)
.invoke(crate::ErrorKind::Filesystem)
.await?;
for i in &self.idmap {
let uid_in_range = i.from_id <= info.uid() && i.from_id + i.range > info.uid();
let gid_in_range = i.from_id <= info.gid() && i.from_id + i.range > info.gid();
if uid_in_range || gid_in_range {
Command::new("chown")
.arg(format!(
"{uid}:{gid}",
uid = if uid_in_range {
i.to_id + info.uid() - i.from_id
} else {
info.uid()
},
gid = if gid_in_range {
i.to_id + info.gid() - i.from_id
} else {
info.gid()
},
))
.arg(&mountpoint)
.invoke(crate::ErrorKind::Filesystem)
.await?;
}
}
Ok(())
}
@@ -86,9 +145,12 @@ impl<Fs: FileSystem> FileSystem for IdMapped<Fs> {
let mut sha = Sha256::new();
sha.update("IdMapped");
sha.update(self.filesystem.source_hash().await?);
sha.update(u32::to_be_bytes(self.from_id));
sha.update(u32::to_be_bytes(self.to_id));
sha.update(u32::to_be_bytes(self.range));
sha.update(usize::to_be_bytes(self.idmap.len()));
for i in &self.idmap {
sha.update(u32::to_be_bytes(i.from_id));
sha.update(u32::to_be_bytes(i.to_id));
sha.update(u32::to_be_bytes(i.range));
}
Ok(sha.finalize())
}
}