composepost: move rootfs symlinks creation to Rust
This ports the post-processing logic which creates symlinks for several known state directories under /usr.
This commit is contained in:
parent
02b81d7845
commit
ad365df4b0
@ -697,6 +697,70 @@ fn convert_path_to_tmpfiles_d_recurse(
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Walk over the root filesystem and perform some core conversions
|
||||
/// from RPM conventions to OSTree conventions.
|
||||
///
|
||||
/// For example:
|
||||
/// - Symlink /usr/local -> /var/usrlocal
|
||||
/// - Symlink /var/lib/alternatives -> /usr/lib/alternatives
|
||||
/// - Symlink /var/lib/vagrant -> /usr/lib/vagrant
|
||||
#[context("Preparing symlinks in rootfs")]
|
||||
pub fn rootfs_prepare_links(rootfs_dfd: i32) -> CxxResult<()> {
|
||||
let rootfs = crate::ffiutil::ffi_view_openat_dir(rootfs_dfd);
|
||||
|
||||
rootfs
|
||||
.remove_all("usr/local")
|
||||
.context("Removing /usr/local")?;
|
||||
let state_paths = &["usr/lib/alternatives", "usr/lib/vagrant"];
|
||||
for entry in state_paths {
|
||||
rootfs
|
||||
.ensure_dir_all(*entry, 0o0755)
|
||||
.with_context(|| format!("Creating '/{}'", entry))?;
|
||||
}
|
||||
|
||||
let symlinks = &[
|
||||
("../var/usrlocal", "usr/local"),
|
||||
("../../usr/lib/alternatives", "var/lib/alternatives"),
|
||||
("../../usr/lib/vagrant", "var/lib/vagrant"),
|
||||
];
|
||||
for (target, linkpath) in symlinks {
|
||||
ensure_symlink(&rootfs, target, linkpath)?;
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Create a symlink at `linkpath` if it does not exist, pointing to `target`.
|
||||
///
|
||||
/// This is idempotent and does not alter any content already existing at `linkpath`.
|
||||
/// It returns `true` if the symlink has been created, `false` otherwise.
|
||||
#[context("Symlinking '/{}' to empty directory '/{}'", linkpath, target)]
|
||||
fn ensure_symlink(rootfs: &openat::Dir, target: &str, linkpath: &str) -> Result<bool> {
|
||||
use openat::SimpleType;
|
||||
|
||||
if let Some(meta) = rootfs.metadata_optional(linkpath)? {
|
||||
match meta.simple_type() {
|
||||
SimpleType::Symlink => {
|
||||
// We assume linkpath already points to the correct target,
|
||||
// thus this short-circuits in an idempotent way.
|
||||
return Ok(false);
|
||||
}
|
||||
SimpleType::Dir => rootfs.remove_dir(linkpath)?,
|
||||
_ => bail!("Content already exists at link path"),
|
||||
};
|
||||
} else {
|
||||
// For maximum compatibility, create parent directories too. This
|
||||
// is necessary when we're doing layering on top of a base commit,
|
||||
// and the /var will be empty. We should probably consider running
|
||||
// systemd-tmpfiles to setup the temporary /var.
|
||||
rootfs.ensure_dir_all(linkpath, 0o755)?;
|
||||
rootfs.remove_dir(linkpath)?;
|
||||
}
|
||||
|
||||
rootfs.symlink(linkpath, target)?;
|
||||
Ok(true)
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
@ -854,4 +918,30 @@ OSTREE_VERSION='33.4'
|
||||
}
|
||||
assert_eq!(entries.len(), expected.len(), "{:#?}", entries);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_prepare_symlinks() {
|
||||
let temp_rootfs = tempfile::tempdir().unwrap();
|
||||
let rootfs = openat::Dir::open(temp_rootfs.path()).unwrap();
|
||||
rootfs.ensure_dir_all("usr/local", 0o755).unwrap();
|
||||
|
||||
rootfs_prepare_links(rootfs.as_raw_fd()).unwrap();
|
||||
{
|
||||
let usr_dir = rootfs.sub_dir("usr").unwrap();
|
||||
let local_target = usr_dir.read_link("local").unwrap();
|
||||
assert_eq!(local_target.to_str(), Some("../var/usrlocal"));
|
||||
}
|
||||
{
|
||||
let varlib_dir = rootfs.sub_dir("var/lib").unwrap();
|
||||
let varcases = &[
|
||||
("alternatives", "../../usr/lib/alternatives"),
|
||||
("vagrant", "../../usr/lib/vagrant"),
|
||||
];
|
||||
for (linkpath, content) in varcases {
|
||||
let target = varlib_dir.read_link(*linkpath);
|
||||
assert!(target.is_ok(), "/var/lib/{}", linkpath);
|
||||
assert_eq!(target.unwrap().to_str(), Some(*content));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -149,6 +149,7 @@ pub mod ffi {
|
||||
rootfs_dfd: i32,
|
||||
cancellable: Pin<&mut GCancellable>,
|
||||
) -> Result<()>;
|
||||
fn rootfs_prepare_links(rootfs_dfd: i32) -> Result<()>;
|
||||
}
|
||||
|
||||
// A grab-bag of metadata from the deployment's ostree commit
|
||||
|
@ -4321,8 +4321,7 @@ rpmostree_context_assemble (RpmOstreeContext *self,
|
||||
*/
|
||||
if (!glnx_shutil_mkdir_p_at (tmprootfs_dfd, "var/tmp", 0755, cancellable, error))
|
||||
return FALSE;
|
||||
if (!rpmostree_rootfs_prepare_links (tmprootfs_dfd, cancellable, error))
|
||||
return FALSE;
|
||||
rpmostreecxx::rootfs_prepare_links(tmprootfs_dfd);
|
||||
|
||||
gboolean skip_sanity_check = FALSE;
|
||||
g_variant_dict_lookup (self->spec->dict, "skip-sanity-check", "b", &skip_sanity_check);
|
||||
|
@ -611,8 +611,8 @@ rpmostree_postprocess_final (int rootfs_dfd,
|
||||
|
||||
rpmostreecxx::convert_var_to_tmpfiles_d (rootfs_dfd, *cancellable);
|
||||
|
||||
if (!rpmostree_rootfs_prepare_links (rootfs_dfd, cancellable, error))
|
||||
return FALSE;
|
||||
rpmostreecxx::rootfs_prepare_links(rootfs_dfd);
|
||||
|
||||
if (!rpmostree_rootfs_postprocess_common (rootfs_dfd, cancellable, error))
|
||||
return FALSE;
|
||||
|
||||
@ -672,80 +672,6 @@ rpmostree_postprocess_final (int rootfs_dfd,
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
gboolean
|
||||
rpmostree_rootfs_symlink_emptydir_at (int rootfs_fd,
|
||||
const char *dest,
|
||||
const char *src,
|
||||
GError **error)
|
||||
{
|
||||
const char *parent = dirname (strdupa (src));
|
||||
struct stat stbuf;
|
||||
gboolean make_symlink = TRUE;
|
||||
|
||||
/* For maximum compatibility, create parent directories too. This
|
||||
* is necessary when we're doing layering on top of a base commit,
|
||||
* and the /var will be empty. We should probably consider running
|
||||
* systemd-tmpfiles to setup the temporary /var.
|
||||
*/
|
||||
if (parent && strcmp (parent, ".") != 0)
|
||||
{
|
||||
if (!glnx_shutil_mkdir_p_at (rootfs_fd, parent, 0755, NULL, error))
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
if (!glnx_fstatat_allow_noent (rootfs_fd, src, &stbuf, AT_SYMLINK_NOFOLLOW, error))
|
||||
return FALSE;
|
||||
if (errno == 0)
|
||||
{
|
||||
if (S_ISLNK (stbuf.st_mode))
|
||||
make_symlink = FALSE;
|
||||
else if (S_ISDIR (stbuf.st_mode))
|
||||
{
|
||||
if (!glnx_unlinkat (rootfs_fd, src, AT_REMOVEDIR, error))
|
||||
return FALSE;
|
||||
}
|
||||
}
|
||||
|
||||
if (make_symlink)
|
||||
{
|
||||
if (symlinkat (dest, rootfs_fd, src) < 0)
|
||||
return glnx_throw_errno_prefix (error, "Symlinking %s", src);
|
||||
}
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
/**
|
||||
* rpmostree_rootfs_prepare_links:
|
||||
*
|
||||
* Walk over the root filesystem and perform some core conversions
|
||||
* from RPM conventions to OSTree conventions. For example:
|
||||
*
|
||||
* - Symlink /usr/local -> /var/usrlocal
|
||||
* - Symlink /var/lib/alternatives -> /usr/lib/alternatives
|
||||
* - Symlink /var/lib/vagrant -> /usr/lib/vagrant
|
||||
*/
|
||||
gboolean
|
||||
rpmostree_rootfs_prepare_links (int rootfs_fd,
|
||||
GCancellable *cancellable,
|
||||
GError **error)
|
||||
{
|
||||
if (!glnx_shutil_rm_rf_at (rootfs_fd, "usr/local", cancellable, error))
|
||||
return FALSE;
|
||||
if (!rpmostree_rootfs_symlink_emptydir_at (rootfs_fd, "../var/usrlocal", "usr/local", error))
|
||||
return FALSE;
|
||||
|
||||
if (!glnx_shutil_mkdir_p_at (rootfs_fd, "usr/lib/alternatives", 0755, cancellable, error))
|
||||
return FALSE;
|
||||
if (!rpmostree_rootfs_symlink_emptydir_at (rootfs_fd, "../../usr/lib/alternatives", "var/lib/alternatives", error))
|
||||
return FALSE;
|
||||
if (!glnx_shutil_mkdir_p_at (rootfs_fd, "usr/lib/vagrant", 0755, cancellable, error))
|
||||
return FALSE;
|
||||
if (!rpmostree_rootfs_symlink_emptydir_at (rootfs_fd, "../../usr/lib/vagrant", "var/lib/vagrant", error))
|
||||
return FALSE;
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
static gboolean
|
||||
cleanup_leftover_files (int rootfs_fd,
|
||||
const char *subpath,
|
||||
|
@ -26,17 +26,6 @@
|
||||
|
||||
G_BEGIN_DECLS
|
||||
|
||||
gboolean
|
||||
rpmostree_rootfs_symlink_emptydir_at (int rootfs_fd,
|
||||
const char *dest,
|
||||
const char *src,
|
||||
GError **error);
|
||||
|
||||
gboolean
|
||||
rpmostree_rootfs_prepare_links (int rootfs_fd,
|
||||
GCancellable *cancellable,
|
||||
GError **error);
|
||||
|
||||
gboolean
|
||||
rpmostree_cleanup_leftover_rpmdb_files (int rootfs_fd,
|
||||
GCancellable *cancellable,
|
||||
|
Loading…
Reference in New Issue
Block a user