From 1a256382f1465865f45d97a138160b992aa5ccd5 Mon Sep 17 00:00:00 2001 From: Colin Walters Date: Wed, 25 Sep 2019 11:52:44 +0000 Subject: [PATCH] Add hidden `coreos-rootfs seal` command All this does is put the immutable bit on the target directory. The intention is to replace this bit to start: https://github.com/coreos/coreos-assembler/blob/8b205bfbb971707382ace76bbb39e46ed3fc560d/src/create_disk.sh#L229 However, the real goal here is to add code in this file to handle redeploying the rootfs for Fedora CoreOS which combines OSTree+Ignition: https://github.com/coreos/fedora-coreos-tracker/issues/94 Basically doing this in proper Rust is going to be a lot nicer than shell script in dracut modules. Among other details, coreutils `mv` doesn't seem to do the right thing for SELinux labels when policy isn't loaded. --- Makefile-rpm-ostree.am | 1 + rust/Cargo.lock | 38 +++++++ rust/Cargo.toml | 1 + rust/src/coreos_rootfs.rs | 119 ++++++++++++++++++++++ rust/src/lib.rs | 2 + src/app/main.c | 4 + src/app/rpmostree-builtin-coreos-rootfs.c | 45 ++++++++ src/app/rpmostree-builtins.h | 1 + tests/vmcheck/test-misc-1.sh | 9 ++ 9 files changed, 220 insertions(+) create mode 100644 rust/src/coreos_rootfs.rs create mode 100644 src/app/rpmostree-builtin-coreos-rootfs.c diff --git a/Makefile-rpm-ostree.am b/Makefile-rpm-ostree.am index d979f339d0..7ef26bbea9 100644 --- a/Makefile-rpm-ostree.am +++ b/Makefile-rpm-ostree.am @@ -39,6 +39,7 @@ rpm_ostree_SOURCES = src/app/main.c \ src/app/rpmostree-pkg-builtins.c \ src/app/rpmostree-builtin-status.c \ src/app/rpmostree-builtin-ex.c \ + src/app/rpmostree-builtin-coreos-rootfs.c \ src/app/rpmostree-builtin-container.c \ src/app/rpmostree-ex-builtin-commit2rojig.c \ src/app/rpmostree-ex-builtin-rojig2commit.c \ diff --git a/rust/Cargo.lock b/rust/Cargo.lock index 7c771a74a4..d8d966f4fd 100644 --- a/rust/Cargo.lock +++ b/rust/Cargo.lock @@ -309,6 +309,14 @@ dependencies = [ "pkg-config 0.3.16 (registry+https://github.com/rust-lang/crates.io-index)", ] +[[package]] +name = "heck" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "unicode-segmentation 1.3.0 (registry+https://github.com/rust-lang/crates.io-index)", +] + [[package]] name = "indicatif" version = "0.11.0" @@ -650,6 +658,7 @@ dependencies = [ "serde_derive 1.0.101 (registry+https://github.com/rust-lang/crates.io-index)", "serde_json 1.0.40 (registry+https://github.com/rust-lang/crates.io-index)", "serde_yaml 0.8.9 (registry+https://github.com/rust-lang/crates.io-index)", + "structopt 0.2.18 (registry+https://github.com/rust-lang/crates.io-index)", "systemd 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)", "tempfile 3.1.0 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -756,6 +765,26 @@ name = "strsim" version = "0.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" +[[package]] +name = "structopt" +version = "0.2.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "clap 2.32.0 (registry+https://github.com/rust-lang/crates.io-index)", + "structopt-derive 0.2.18 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "structopt-derive" +version = "0.2.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "heck 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)", + "proc-macro2 0.4.30 (registry+https://github.com/rust-lang/crates.io-index)", + "quote 0.6.13 (registry+https://github.com/rust-lang/crates.io-index)", + "syn 0.15.44 (registry+https://github.com/rust-lang/crates.io-index)", +] + [[package]] name = "syn" version = "0.15.44" @@ -836,6 +865,11 @@ dependencies = [ "lazy_static 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)", ] +[[package]] +name = "unicode-segmentation" +version = "1.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" + [[package]] name = "unicode-width" version = "0.1.6" @@ -943,6 +977,7 @@ dependencies = [ "checksum glib 0.7.1 (registry+https://github.com/rust-lang/crates.io-index)" = "0c4e8fdc159c196a5dfa53a92929ac4c10c8a6637ffb43951f3fff89c2cd2365" "checksum glib-sys 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)" = "5bda542f3caee39a027638e9644ff89204101ad916fd7370b585ad2c5fc97e61" "checksum gobject-sys 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)" = "23e05a14290d3dc255223cba51db4b0f3da438d5250657996fa99b2a30faf43e" +"checksum heck 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)" = "20564e78d53d2bb135c343b3f47714a56af2061f1c928fdb541dc7b9fdd94205" "checksum indicatif 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)" = "2c60da1c9abea75996b70a931bba6c750730399005b61ccd853cee50ef3d0d0c" "checksum itoa 0.4.4 (registry+https://github.com/rust-lang/crates.io-index)" = "501266b7edd0174f8530248f87f99c88fbe60ca4ef3dd486835b8d8d53136f7f" "checksum lazy_static 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" @@ -996,6 +1031,8 @@ dependencies = [ "checksum smallvec 0.6.10 (registry+https://github.com/rust-lang/crates.io-index)" = "ab606a9c5e214920bb66c458cd7be8ef094f813f20fe77a54cc7dbfff220d4b7" "checksum socket2 0.3.11 (registry+https://github.com/rust-lang/crates.io-index)" = "e8b74de517221a2cb01a53349cf54182acdc31a074727d3079068448c0676d85" "checksum strsim 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)" = "bb4f380125926a99e52bc279241539c018323fab05ad6368b56f93d9369ff550" +"checksum structopt 0.2.18 (registry+https://github.com/rust-lang/crates.io-index)" = "16c2cdbf9cc375f15d1b4141bc48aeef444806655cd0e904207edc8d68d86ed7" +"checksum structopt-derive 0.2.18 (registry+https://github.com/rust-lang/crates.io-index)" = "53010261a84b37689f9ed7d395165029f9cc7abb9f56bbfe86bee2597ed25107" "checksum syn 0.15.44 (registry+https://github.com/rust-lang/crates.io-index)" = "9ca4b3b69a77cbe1ffc9e198781b7acb0c7365a883670e8f1c1bc66fba79a5c5" "checksum syn 1.0.5 (registry+https://github.com/rust-lang/crates.io-index)" = "66850e97125af79138385e9b88339cbcd037e3f28ceab8c5ad98e64f0f1f80bf" "checksum synstructure 0.10.2 (registry+https://github.com/rust-lang/crates.io-index)" = "02353edf96d6e4dc81aea2d8490a7e9db177bf8acb0e951c24940bf866cb313f" @@ -1004,6 +1041,7 @@ dependencies = [ "checksum termios 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)" = "72b620c5ea021d75a735c943269bb07d30c9b77d6ac6b236bc8b5c496ef05625" "checksum textwrap 0.10.0 (registry+https://github.com/rust-lang/crates.io-index)" = "307686869c93e71f94da64286f9a9524c0f308a9e1c87a583de8e9c9039ad3f6" "checksum thread_local 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)" = "c6b53e329000edc2b34dbe8545fd20e55a333362d0a321909685a19bd28c3f1b" +"checksum unicode-segmentation 1.3.0 (registry+https://github.com/rust-lang/crates.io-index)" = "1967f4cdfc355b37fd76d2a954fb2ed3871034eb4f26d60537d88795cfc332a9" "checksum unicode-width 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)" = "7007dbd421b92cc6e28410fe7362e2e0a2503394908f417b68ec8d1c364c4e20" "checksum unicode-xid 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "fc72304796d0818e357ead4e000d19c9c174ab23dc11093ac919054d20a6a7fc" "checksum unicode-xid 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "826e7639553986605ec5979c7dd957c7895e93eabed50ab2ffa7f6128a75097c" diff --git a/rust/Cargo.toml b/rust/Cargo.toml index 8c292deeab..9b0a6bad7f 100644 --- a/rust/Cargo.toml +++ b/rust/Cargo.toml @@ -17,6 +17,7 @@ gio-sys = "0.8.0" glib = "0.7.1" tempfile = "3.0.3" clap = "~2.32" +structopt = "0.2" openat = "0.1.15" openat-ext = "0.1.0" curl = "0.4.14" diff --git a/rust/src/coreos_rootfs.rs b/rust/src/coreos_rootfs.rs new file mode 100644 index 0000000000..a497395144 --- /dev/null +++ b/rust/src/coreos_rootfs.rs @@ -0,0 +1,119 @@ +/* + * Copyright (C) 2018 Red Hat, Inc. + * + * SPDX-License-Identifier: Apache-2.0 OR MIT + */ + +//! # Code for CoreOS rootfs +//! +//! This backs the `rpm-ostree coreos-rootfs` CLI, which is intended as +//! a hidden implmentation detail of CoreOS style systems. The code +//! is here in rpm-ostree as a convenience. + +use std::os::unix::prelude::*; +use failure::{Fallible, ResultExt}; +use structopt::StructOpt; +use openat; +use nix; +use libc; + +/// A reference to a *directory* file descriptor. Unfortunately, +/// the openat crate always uses O_PATH which doesn't support ioctl(). +pub struct RawDirFd(RawFd); + +impl AsRawFd for RawDirFd { + #[inline] + fn as_raw_fd(&self) -> RawFd { + self.0 + } +} + +impl FromRawFd for RawDirFd { + #[inline] + unsafe fn from_raw_fd(fd: RawFd) -> RawDirFd { + RawDirFd(fd) + } +} + +impl Drop for RawDirFd { + fn drop(&mut self) { + let fd = self.0; + unsafe { + libc::close(fd); + } + } +} + +/* From /usr/include/ext2fs/ext2_fs.h */ +const EXT2_IMMUTABLE_FL : libc::c_long = 0x00000010; /* Immutable file */ + +nix::ioctl_read!(ext2_get_flags, b'f', 1, libc::c_long); +nix::ioctl_write_ptr!(ext2_set_flags, b'f', 2, libc::c_long); + +#[derive(Debug, StructOpt)] +struct SealOpts { + /// Path to rootfs + sysroot: String, +} + +#[derive(Debug, StructOpt)] +#[structopt(name = "coreos-rootfs")] +#[structopt(rename_all = "kebab-case")] +enum Opt { + /// Final step after changing a sysroot + Seal(SealOpts), +} + +// taken from openat code +fn to_cstr(path: P) -> std::io::Result { + path.to_path() + .ok_or_else(|| { + std::io::Error::new(std::io::ErrorKind::InvalidInput, + "nul byte in file name") + }) +} + +/// Set the immutable bit +fn seal(opts: &SealOpts) -> Fallible<()> { + let fd = unsafe { + let fd = libc::open(to_cstr(opts.sysroot.as_str())?.as_ref().as_ptr(), libc::O_CLOEXEC | libc::O_DIRECTORY); + if fd < 0 { + Err(std::io::Error::last_os_error())? + } else { + RawDirFd::from_raw_fd(fd) + } + }; + + let mut flags : libc::c_long = 0; + unsafe { ext2_get_flags(fd.as_raw_fd(), &mut flags as *mut libc::c_long)? }; + if flags & EXT2_IMMUTABLE_FL == 0 { + flags |= EXT2_IMMUTABLE_FL; + unsafe { ext2_set_flags(fd.as_raw_fd(), &flags as *const libc::c_long)? }; + } + Ok(()) +} + +/// Main entrypoint +fn coreos_rootfs_main(args: &Vec) -> Fallible<()> { + let opt = Opt::from_iter(args.iter()); + match opt { + Opt::Seal(ref opts) => seal(opts).with_context(|e| format!("Sealing: {}", e.to_string()))?, + }; + Ok(()) +} + +mod ffi { + use super::*; + use glib_sys; + use libc; + + use crate::ffiutil::*; + + #[no_mangle] + pub extern "C" fn ror_coreos_rootfs_entrypoint(argv: *mut *mut libc::c_char, + gerror: *mut *mut glib_sys::GError) -> libc::c_int { + let v: Vec = unsafe { glib::translate::FromGlibPtrContainer::from_glib_none(argv) }; + int_glib_error(coreos_rootfs_main(&v), gerror) + } +} +pub use self::ffi::*; diff --git a/rust/src/lib.rs b/rust/src/lib.rs index d91e7fe150..8c5e937211 100644 --- a/rust/src/lib.rs +++ b/rust/src/lib.rs @@ -12,6 +12,8 @@ mod composepost; pub use self::composepost::*; mod history; pub use self::history::*; +mod coreos_rootfs; +pub use self::coreos_rootfs::*; mod journal; pub use self::journal::*; mod progress; diff --git a/src/app/main.c b/src/app/main.c index 0252c7bedc..13a5904fb7 100644 --- a/src/app/main.c +++ b/src/app/main.c @@ -115,6 +115,10 @@ static RpmOstreeCommand commands[] = { RPM_OSTREE_BUILTIN_FLAG_HIDDEN, "Experimental commands that may change or be removed in the future", rpmostree_builtin_ex }, + { "coreos-rootfs", RPM_OSTREE_BUILTIN_FLAG_LOCAL_CMD | + RPM_OSTREE_BUILTIN_FLAG_REQUIRES_ROOT | + RPM_OSTREE_BUILTIN_FLAG_HIDDEN, + NULL, rpmostree_builtin_coreos_rootfs }, { "start-daemon", RPM_OSTREE_BUILTIN_FLAG_LOCAL_CMD | RPM_OSTREE_BUILTIN_FLAG_REQUIRES_ROOT | RPM_OSTREE_BUILTIN_FLAG_HIDDEN, diff --git a/src/app/rpmostree-builtin-coreos-rootfs.c b/src/app/rpmostree-builtin-coreos-rootfs.c new file mode 100644 index 0000000000..3c0d128aef --- /dev/null +++ b/src/app/rpmostree-builtin-coreos-rootfs.c @@ -0,0 +1,45 @@ +/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- + * + * Copyright (C) 2019 Red Hat, Inc. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published + * by the Free Software Foundation; either version 2 of the licence or (at + * your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General + * Public License along with this library; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place, Suite 330, + * Boston, MA 02111-1307, USA. + */ + +#include "config.h" + +#include +#include +#include + +#include "rpmostree-builtins.h" +#include "rpmostree-libbuiltin.h" +#include "rpmostree-rust.h" + +#include + +gboolean +rpmostree_builtin_coreos_rootfs (int argc, + char **argv, + RpmOstreeCommandInvocation *invocation, + GCancellable *cancellable, + GError **error) +{ + g_autoptr(GPtrArray) args = g_ptr_array_new (); + for (int i = 0; i < argc; i++) + g_ptr_array_add (args, argv[i]); + g_ptr_array_add (args, NULL); + return ror_coreos_rootfs_entrypoint ((char**)args->pdata, error); +} diff --git a/src/app/rpmostree-builtins.h b/src/app/rpmostree-builtins.h index bb6da7d52c..7ab2a28454 100644 --- a/src/app/rpmostree-builtins.h +++ b/src/app/rpmostree-builtins.h @@ -51,6 +51,7 @@ BUILTINPROTO(override); BUILTINPROTO(kargs); BUILTINPROTO(reset); BUILTINPROTO(start_daemon); +BUILTINPROTO(coreos_rootfs); BUILTINPROTO(ex); BUILTINPROTO(finalize_deployment); diff --git a/tests/vmcheck/test-misc-1.sh b/tests/vmcheck/test-misc-1.sh index 8a0a58fdff..2429690710 100755 --- a/tests/vmcheck/test-misc-1.sh +++ b/tests/vmcheck/test-misc-1.sh @@ -102,6 +102,15 @@ fi assert_file_has_content err.txt 'ReloadConfig not allowed for user' echo "ok auth" +# Test coreos-rootfs +vm_shell_inline << EOF + mkdir /var/tmp/coreos-rootfs + rpm-ostree coreos-rootfs seal /var/tmp/coreos-rootfs + lsattr -d /var/tmp/coreos-rootfs + rpm-ostree coreos-rootfs seal /var/tmp/coreos-rootfs +EOF > coreos-rootfs.txt +assert_file_has_content_literal coreos-rootfs.txt '----i-------------- /var/tmp/coreos-rootfs' + # Assert that we can do status as non-root vm_cmd_as testuser rpm-ostree status echo "ok status doesn't require root"