diff --git a/Cargo.lock b/Cargo.lock index f10348951dd49fbfcb5a767601fb39c40c2bf54e..797f2d9b61fdb92ae23e58a593d6f7587ad333fd 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1062,6 +1062,15 @@ dependencies = [ "libc", ] +[[package]] +name = "machine-id-setup" +version = "0.5.1" +dependencies = [ + "basic", + "log 0.5.1", + "nix 0.24.3", +] + [[package]] name = "macros" version = "0.5.1" diff --git a/Cargo.toml b/Cargo.toml index 6f5246235ac90d8c19d481e4a382d1a559236860..073e30c7f48aaa4ec4d5ee8e9c4fcc8d0515f75e 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -124,6 +124,7 @@ members = [ "exts/switch_root", "exts/hostname_setup", "exts/hwdb", + "exts/machine-id-setup", #internal libraries crates "libs/cmdproto", #external libraries crates diff --git a/core/sysmaster/src/main.rs b/core/sysmaster/src/main.rs index 0f5fd3f4a542d76353c1a50a0e471d4543cd69f2..b87b5aa3f7317750a1a5da63ea9ea70675928d67 100644 --- a/core/sysmaster/src/main.rs +++ b/core/sysmaster/src/main.rs @@ -165,6 +165,7 @@ fn initialize_runtime(self_recovery_enable: bool) -> Result<()> { msg: format!("enable hongmeng resmgr subtree_control failed: {}", e), })?; + machine::machine_id_setup(true, "")?; set_child_reaper(); Ok(()) diff --git a/exts/machine-id-setup/Cargo.toml b/exts/machine-id-setup/Cargo.toml new file mode 100644 index 0000000000000000000000000000000000000000..d61bfe9831d2e93e525f73da0c9579a279edb6ec --- /dev/null +++ b/exts/machine-id-setup/Cargo.toml @@ -0,0 +1,14 @@ +[package] +name = "machine-id-setup" +version = "0.5.1" +edition = "2021" + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[dependencies] +basic = { path = "../../libs/basic", default-features = false, features = [ + "machine", + "id128", +] } +log = { path = "../../libs/log" } +nix = { version = "0.24", default-features = false } diff --git a/exts/machine-id-setup/src/main.rs b/exts/machine-id-setup/src/main.rs new file mode 100644 index 0000000000000000000000000000000000000000..18605eed9107bedb84269c5b4a6b7c29acd2903c --- /dev/null +++ b/exts/machine-id-setup/src/main.rs @@ -0,0 +1,75 @@ +// Copyright (c) 2022 Huawei Technologies Co.,Ltd. All rights reserved. +// +// sysMaster is licensed under Mulan PSL v2. +// You can use this software according to the terms and conditions of the Mulan +// PSL v2. +// You may obtain a copy of Mulan PSL v2 at: +// http://license.coscl.org.cn/MulanPSL2 +// THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY +// KIND, EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO +// NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. +// See the Mulan PSL v2 for more details. + +//! + +use basic::{ + id128_util, + machine::{self, machine_id_setup}, +}; +use nix::{Error, Result}; +use std::path::Path; + +fn check_args(args: &[String]) -> bool { + if args.len() == 1 { + return true; + } + let mut i = 1; + while i < args.len() { + i += 1; + match &args[i] as &str { + "--commit" => continue, + "--print" => continue, + _ => return false, + } + } + true +} + +fn main() -> Result<()> { + log::init_log_to_console("machine-id-setup", log::Level::Info); + let args: Vec = std::env::args().collect(); + let id: String; + + if !check_args(&args) { + log::error!("Invalid args. Support [--commit][--print]"); + return Err(nix::Error::EINVAL); + } + + if args.contains(&String::from("--commit")) { + let etc_machine_id = "/etc/machine-id".to_string(); + machine::machine_id_commit()?; + id = match id128_util::id128_read_by_path( + Path::new(&etc_machine_id), + id128_util::Id128FormatFlag::ID128_FORMAT_PLAIN, + ) { + Ok(id) => id, + Err(e) => { + log::error!("Failed to read machine Id back: {}", e); + return Err(Error::EIO); + } + } + } else { + id = match machine_id_setup(false, "") { + Ok(id) => id, + Err(e) => { + log::error!("Failed to setup machine-id:{}", e); + return Err(e); + } + } + } + + if args.contains(&String::from("--print")) { + println!("{}", id); + } + Ok(()) +} diff --git a/libs/basic/Cargo.toml b/libs/basic/Cargo.toml index e18db5c79cc4c89d24cd2e8581ef1c15c6f1a5f9..4f9f6a34bbeb950aff12e2f94a389716de98420a 100644 --- a/libs/basic/Cargo.toml +++ b/libs/basic/Cargo.toml @@ -63,6 +63,9 @@ full = [ "strbuf", "argv", "exec_util", + "random", + "id128", + "namespace" ] capability = [] @@ -86,10 +89,12 @@ fd = ["stat", "nix/ioctl", "nix/socket"] file = [] fs = ["pathdiff", "rand", "nix/fs", "nix/user"] host = [] +id128 = ["bitflags", "nix/fs"] io = ["nix/poll", "nix/signal"] -machine = [] +machine = ["nix/fs", "nix/dir", "mount", "id128", "namespace"] memory = [] -mount = [] +mount = ["fs"] +namespace = ["nix/process", "nix/fs", "nix/sched", "nix/mount"] naming_scheme = ["bitflags", "nix/net"] network = ["nix/net"] os_release = [] diff --git a/libs/basic/src/fs_util.rs b/libs/basic/src/fs_util.rs index 955a3a523633932dd976a6624cf6024ec40446d4..4a47eb1d91f51aa1cf3fc7e59b8e64c84d381710 100644 --- a/libs/basic/src/fs_util.rs +++ b/libs/basic/src/fs_util.rs @@ -17,6 +17,7 @@ use libc::{fchownat, mode_t, timespec, AT_EMPTY_PATH, S_IFLNK, S_IFMT}; use nix::{ fcntl::{open, readlink, renameat, OFlag}, sys::stat::{fstat, Mode}, + sys::statfs, unistd::{unlinkat, Gid, Uid, UnlinkatFlags}, }; use pathdiff::diff_paths; @@ -651,6 +652,17 @@ impl Default for LookupPaths { } } +/// Check whether $p belongs to $fstype fs +pub fn check_filesystem(p: &Path, fstype: statfs::FsType) -> bool { + let fstp = match statfs::statfs(p) { + Ok(s) => s.filesystem_type(), + Err(_) => { + return false; + } + }; + fstp == fstype +} + #[cfg(test)] mod tests { use super::LookupPaths; diff --git a/libs/basic/src/id128_util.rs b/libs/basic/src/id128_util.rs new file mode 100755 index 0000000000000000000000000000000000000000..103b013ae434a59dc2b426e64f3a36a7477d6b05 --- /dev/null +++ b/libs/basic/src/id128_util.rs @@ -0,0 +1,353 @@ +// Copyright (c) 2022 Huawei Technologies Co.,Ltd. All rights reserved. +// +// sysMaster is licensed under Mulan PSL v2. +// You can use this software according to the terms and conditions of the Mulan +// PSL v2. +// You may obtain a copy of Mulan PSL v2 at: +// http://license.coscl.org.cn/MulanPSL2 +// THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY +// KIND, EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO +// NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. +// See the Mulan PSL v2 for more details. + +//! +use bitflags::bitflags; +use nix::{errno::Errno, fcntl, fcntl::OFlag, sys::stat::Mode, unistd}; +use rand::Rng; +use std::{ + fs, + io::{Error, ErrorKind}, + os::unix::io::RawFd, + path::Path, + str, +}; + +#[derive(PartialEq)] +enum Id128String { + UninitLen = 13, // length of "uninitialized". + UninitLenN = 14, // length of "uninitialized\n". + PlainUuidMAx = 32, // plain UUID without trailing newline + PlainUuidMAxN = 33, // plain UUID with trailing newline + RfcUuidMax = 36, // RFC UUID without trailing newline + RfcUuidMaxN = 37, // RFC UUID with trailing newline + ErrorLen, +} + +impl From for Id128String { + fn from(item: usize) -> Self { + match item { + 13 => Id128String::UninitLen, + 14 => Id128String::UninitLenN, + 32 => Id128String::PlainUuidMAx, + 33 => Id128String::PlainUuidMAxN, + 36 => Id128String::RfcUuidMax, + 37 => Id128String::RfcUuidMaxN, + _ => Id128String::ErrorLen, + } + } +} + +bitflags! { + /// Format of id128 + pub struct Id128FormatFlag : u8{ + /// formatted as 32 hex chars as-is. eg: 12345678901234567890abcdef123456 + const ID128_FORMAT_PLAIN = 1; + /// formatted as 36 character uuid string. eg:12345678-9012-3456-7890-abcdef123456 + const ID128_FORMAT_UUID = 1 << 1; + /// format any type + const ID128_FORMAT_ANY = (1 << 1) | 1; + } +} + +fn id128_plain_is_valid(id128: &[u8]) -> bool { + let mut id128 = id128.to_owned(); + + id128.retain(|&x| x != b'\n'); + if id128.len() != 32 { + return false; + } + for i in id128 { + if (b'0'..=b'9').contains(&i) || (b'A'..=b'F').contains(&i) || (b'a'..=b'f').contains(&i) { + continue; + } + return false; + } + true +} + +fn id128_rfc2plain(id128: &mut Vec) -> bool { + if id128[8] != b'-' || id128[13] != b'-' || id128[18] != b'-' || id128[23] != b'-' { + return false; + } + + id128.retain(|&x| x != b'-'); + true +} + +fn id128_plain2rfc(id128: &mut Vec) -> bool { + if !id128_plain_is_valid(id128) { + return false; + } + + id128.insert(8, b'-'); + id128.insert(13, b'-'); + id128.insert(18, b'-'); + id128.insert(23, b'-'); + + true +} + +fn id128_rfc_is_valid(id128: &[u8]) -> bool { + let mut id128 = id128.to_owned(); + + id128.retain(|&x| x != b'\n'); + + if !id128_rfc2plain(&mut id128) { + return false; + } + + id128_plain_is_valid(&id128) +} + +/// Check id128 is valid? +pub fn id128_is_valid(id128: &[u8]) -> bool { + let mut id128 = id128.to_owned(); + + id128.retain(|&x| x != b'\n'); + match Id128String::from(id128.len()) { + Id128String::PlainUuidMAx => id128_plain_is_valid(&id128), + Id128String::RfcUuidMax => id128_rfc_is_valid(&id128), + _ => false, + } +} + +/// return id128 from $path +pub fn id128_read_by_path(path: &Path, f: Id128FormatFlag) -> std::io::Result { + let mut id128: Vec = fs::read(path)?; + let id128_len = Id128String::from(id128.len()); + + match id128_len { + Id128String::UninitLen | Id128String::UninitLenN => { + return Err(Error::from(ErrorKind::InvalidData)) + } + + Id128String::PlainUuidMAx | Id128String::PlainUuidMAxN => { + if id128_len == Id128String::PlainUuidMAxN { + // end with '\n' + if !id128.ends_with(&[b'\n']) { + return Err(Error::from(ErrorKind::InvalidData)); + } + id128.pop(); + } + + if !f.contains(Id128FormatFlag::ID128_FORMAT_PLAIN) + && !f.contains(Id128FormatFlag::ID128_FORMAT_ANY) + { + return Err(Error::from(ErrorKind::InvalidInput)); + } + } + + Id128String::RfcUuidMax | Id128String::RfcUuidMaxN => { + if id128_len == Id128String::RfcUuidMaxN { + // end with '\n' + if !id128.ends_with(&[b'\n']) { + return Err(Error::from(ErrorKind::InvalidData)); + } + id128.pop(); + } + + if !f.contains(Id128FormatFlag::ID128_FORMAT_UUID) + && !f.contains(Id128FormatFlag::ID128_FORMAT_ANY) + { + return Err(Error::from(ErrorKind::InvalidInput)); + } + } + + _ => return Err(Error::from(ErrorKind::Other)), + } + match id128_is_valid(&id128) { + true => Ok(str::from_utf8(&id128).unwrap().to_string()), + false => Err(Error::from(ErrorKind::InvalidData)), + } +} + +/// write id128 to $p +pub fn id128_write(p: &Path, f_sync: &bool, id128: &str, f: Id128FormatFlag) -> nix::Result<()> { + let mut id128 = id128.to_string().into_bytes(); + let fd: RawFd; + + if !id128_is_valid(&id128) { + return Err(Errno::EINVAL); + } + + if f == Id128FormatFlag::ID128_FORMAT_PLAIN { + id128_rfc2plain(&mut id128); + } else if f == Id128FormatFlag::ID128_FORMAT_UUID { + id128_plain2rfc(&mut id128); + } + + // add trail newline + if !id128.ends_with(&[b'\n']) { + id128.push(b'\n'); + } + + fd = fcntl::open( + p, + OFlag::O_WRONLY | OFlag::O_CREAT | OFlag::O_CLOEXEC | OFlag::O_NOCTTY | OFlag::O_TRUNC, + Mode::S_IRUSR | Mode::S_IRGRP | Mode::S_IROTH, + )?; + unistd::write(fd, &id128)?; + if *f_sync { + unistd::fsync(fd)?; + } + unistd::close(fd)?; + + Ok(()) +} + +/// +pub fn id128_randomize(f: Id128FormatFlag) -> nix::Result { + let mut rng = rand::thread_rng(); + let mut ret: String = String::new(); + + let mut i = 0; + while i < 32 { + let s: u32 = rng.gen_range(0..16); + let hex = format!("{:x}", s); + ret.push_str(&hex); + i += 1; + } + + if f.contains(Id128FormatFlag::ID128_FORMAT_UUID) { + let mut ret_u8 = ret.into_bytes(); + if !id128_plain2rfc(&mut ret_u8) { + return Err(Errno::UnknownErrno); + } + ret = String::from_utf8(ret_u8).unwrap(); + } + Ok(ret) +} + +#[cfg(test)] +mod tests { + use super::*; + use std::{fs, path::Path}; + + #[test] + fn test_id128_read_by_path() { + let p = Path::new("test_id128_read_by_path"); + + fs::write(p, b"12345678901234567890abcdef123456").unwrap(); + assert_eq!( + id128_read_by_path(p, Id128FormatFlag::ID128_FORMAT_PLAIN).unwrap(), + String::from("12345678901234567890abcdef123456") + ); + assert!(id128_read_by_path(p, Id128FormatFlag::ID128_FORMAT_UUID).is_err()); + assert_eq!( + id128_read_by_path(p, Id128FormatFlag::ID128_FORMAT_ANY).unwrap(), + String::from("12345678901234567890abcdef123456") + ); + + fs::write(p, b"12345678901234567890abcdef123456\n").unwrap(); + assert_eq!( + id128_read_by_path(p, Id128FormatFlag::ID128_FORMAT_PLAIN).unwrap(), + String::from("12345678901234567890abcdef123456") + ); + assert!(id128_read_by_path(p, Id128FormatFlag::ID128_FORMAT_UUID).is_err()); + assert_eq!( + id128_read_by_path(p, Id128FormatFlag::ID128_FORMAT_ANY).unwrap(), + String::from("12345678901234567890abcdef123456") + ); + + fs::write(p, b"123e4567-e89b-12b3-a456-426614174000").unwrap(); + assert!(id128_read_by_path(p, Id128FormatFlag::ID128_FORMAT_PLAIN).is_err()); + assert_eq!( + id128_read_by_path(p, Id128FormatFlag::ID128_FORMAT_UUID).unwrap(), + String::from("123e4567-e89b-12b3-a456-426614174000") + ); + assert_eq!( + id128_read_by_path(p, Id128FormatFlag::ID128_FORMAT_ANY).unwrap(), + String::from("123e4567-e89b-12b3-a456-426614174000") + ); + + fs::write(p, b"123e4567-e89b-12b3-a456-426614174000\n").unwrap(); + assert!(id128_read_by_path(p, Id128FormatFlag::ID128_FORMAT_PLAIN).is_err()); + assert_eq!( + id128_read_by_path(p, Id128FormatFlag::ID128_FORMAT_UUID).unwrap(), + String::from("123e4567-e89b-12b3-a456-426614174000") + ); + assert_eq!( + id128_read_by_path(p, Id128FormatFlag::ID128_FORMAT_ANY).unwrap(), + String::from("123e4567-e89b-12b3-a456-426614174000") + ); + + fs::write(p, b"123").unwrap(); + assert!(id128_read_by_path(p, Id128FormatFlag::ID128_FORMAT_PLAIN).is_err()); + assert!(id128_read_by_path(p, Id128FormatFlag::ID128_FORMAT_UUID).is_err()); + assert!(id128_read_by_path(p, Id128FormatFlag::ID128_FORMAT_ANY).is_err()); + + fs::write(p, b"123e-4567e89b-12b3-a456-426614174000").unwrap(); + assert!(id128_read_by_path(p, Id128FormatFlag::ID128_FORMAT_PLAIN).is_err()); + assert!(id128_read_by_path(p, Id128FormatFlag::ID128_FORMAT_UUID).is_err()); + assert!(id128_read_by_path(p, Id128FormatFlag::ID128_FORMAT_ANY).is_err()); + + fs::write(p, b"123e456--e89b-12b3-a456-426614174000").unwrap(); + assert!(id128_read_by_path(p, Id128FormatFlag::ID128_FORMAT_PLAIN).is_err()); + assert!(id128_read_by_path(p, Id128FormatFlag::ID128_FORMAT_UUID).is_err()); + assert!(id128_read_by_path(p, Id128FormatFlag::ID128_FORMAT_ANY).is_err()); + + fs::write(p, b"z2345678901234567890abcdef123456").unwrap(); + assert!(id128_read_by_path(p, Id128FormatFlag::ID128_FORMAT_PLAIN).is_err()); + assert!(id128_read_by_path(p, Id128FormatFlag::ID128_FORMAT_UUID).is_err()); + assert!(id128_read_by_path(p, Id128FormatFlag::ID128_FORMAT_ANY).is_err()); + + fs::remove_file("test_id128_read_by_path").unwrap(); + } + + #[test] + fn test_id128_is_valid() { + assert!(!id128_is_valid(&String::from("123").into_bytes())); + assert!(!id128_is_valid( + &String::from("123123sfa1234sdfasdfqwer").into_bytes() + )); + assert!(!id128_is_valid( + &String::from("12345678901234567890abcdef1234561").into_bytes() + )); + assert!(!id128_is_valid( + &String::from("123e456--e89b-12b3-a456-426614174000").into_bytes() + )); + assert!(!id128_is_valid( + &String::from("123e-4567e89b-12b3-a456-426614174000").into_bytes() + )); + + assert!(id128_is_valid( + &String::from("12345678901234567890abcdef123456").into_bytes() + )); + assert!(id128_is_valid( + &String::from("12345678901234567890abcdef123456\n").into_bytes() + )); + assert!(id128_is_valid( + &String::from("12345678-9012-3456-7890-abcdef123456").into_bytes() + )); + assert!(id128_is_valid( + &String::from("12345678-9012-3456-7890-abcdef123456\n").into_bytes() + )); + } + #[test] + fn test_id128_randomize() { + let mut i = 0; + while i < 10 { + assert!(id128_is_valid( + &id128_randomize(Id128FormatFlag::ID128_FORMAT_PLAIN) + .unwrap() + .into_bytes() + )); + assert!(id128_is_valid( + &id128_randomize(Id128FormatFlag::ID128_FORMAT_UUID) + .unwrap() + .into_bytes() + )); + i += 1; + } + } +} diff --git a/libs/basic/src/lib.rs b/libs/basic/src/lib.rs index 0f40793dd406b6b9d7aef5248557812d50f9db91..a8834b2ca5a2644352e50f056a36852b33101b92 100644 --- a/libs/basic/src/lib.rs +++ b/libs/basic/src/lib.rs @@ -35,6 +35,8 @@ pub mod fd_util; pub mod fs_util; #[cfg(feature = "host")] pub mod host; +#[cfg(feature = "id128")] +pub mod id128_util; #[cfg(feature = "io")] pub mod io_util; #[cfg(feature = "machine")] @@ -45,6 +47,8 @@ pub mod memory; pub mod mount_util; #[cfg(feature = "murmurhash2")] pub mod murmurhash2; +#[cfg(feature = "namespace")] +pub mod namespace_util; #[cfg(feature = "naming_scheme")] pub mod naming_scheme; #[cfg(feature = "network")] diff --git a/libs/basic/src/machine.rs b/libs/basic/src/machine.rs index db59252290e9806a14dc64bf2093e4d61434b92b..7c657ec6104d83b85e33e41f67724d40b1d9d076 100644 --- a/libs/basic/src/machine.rs +++ b/libs/basic/src/machine.rs @@ -11,13 +11,33 @@ // See the Mulan PSL v2 for more details. //! -use nix::sys::statfs; -use nix::unistd::AccessFlags; -use std::fs::File; -use std::io::{BufRead, BufReader}; -use std::os::unix::prelude::MetadataExt; -use std::path::{Path, PathBuf}; -use std::{env, fs}; +use crate::{ + fs_util, + id128_util::{self, Id128FormatFlag}, + mount_util, namespace_util, +}; +use libc::syncfs; +use log; +use nix::{ + dir::Dir, + errno::Errno, + fcntl, + fcntl::OFlag, + mount, sched, + sys::{stat::*, statfs}, + unistd::{self, sync, AccessFlags, Pid}, + Result, +}; +use std::{ + env, fs, + fs::File, + io::{BufRead, BufReader, ErrorKind}, + ops::BitAnd, + os::unix::io::{AsRawFd, RawFd}, + os::unix::prelude::MetadataExt, + path::{Path, PathBuf}, + process, +}; macro_rules! unsafe_set_and_return { ($var:expr, $value:expr) => { @@ -306,6 +326,254 @@ impl Machine { } } +fn syncfs_path(path: &str) -> Result<()> { + if path.is_empty() { + return Err(Errno::EINVAL); + } + + let fd = Dir::open( + path, + OFlag::O_RDONLY | OFlag::O_CLOEXEC | OFlag::O_NONBLOCK, + Mode::empty(), + )?; + match unsafe { syncfs(fd.as_raw_fd()) } { + 0 => Ok(()), + e => Err(Errno::from_i32(e)), + } +} + +/// Write machine-id from memory to disk +pub fn machine_id_commit() -> Result<()> { + #[cfg(target_env = "musl")] + type FsTypeT = libc::c_ulong; + #[cfg(not(target_env = "musl"))] + type FsTypeT = libc::c_long; + + let etc_machine_id = Path::new("/etc/machine-id"); + let id128: String; + let mnt_fd: RawFd; + + syncfs_path("/etc/")?; + syncfs_path("/var/")?; + + sync(); + + if !mount_util::is_mount_point(etc_machine_id) { + log::debug!("{:?} is not a mount point. Nothing to do.", etc_machine_id); + return Ok(()); + } + + if !fs_util::check_filesystem(etc_machine_id, statfs::FsType(libc::TMPFS_MAGIC as FsTypeT)) { + log::error!("{:?} is not on a temporary file system.", etc_machine_id); + return Err(nix::Error::EROFS); + } + + match id128_util::id128_read_by_path(etc_machine_id, Id128FormatFlag::ID128_FORMAT_PLAIN) { + Ok(id128_string) => id128 = id128_string, + Err(e) => { + log::error!( + "We didn't find a valid machine ID in {:?}:{}", + etc_machine_id, + e + ); + return Err(nix::Error::EINVAL); + } + } + + mnt_fd = namespace_util::namespace_open(&Pid::from_raw(0), Path::new(&"mnt".to_string()))?; + + namespace_util::detach_mount_namespace()?; + + if let Err(e) = mount::umount2(etc_machine_id, mount::MntFlags::from_bits(0).unwrap()) { + log::error!("Failed to umount {:?}:{}", etc_machine_id, e); + return Err(e); + } + + id128_util::id128_write( + etc_machine_id, + &true, + &id128, + Id128FormatFlag::ID128_FORMAT_PLAIN, + )?; + + namespace_util::namespace_enter(&mnt_fd, sched::CloneFlags::CLONE_NEWNS)?; + + mount::umount2(etc_machine_id, mount::MntFlags::MNT_DETACH) +} + +fn generate_machine_id() -> Result { + let dbus_machine_id = Path::new("/var/lib/dbus/machine-id"); + + if let Ok(id128) = + id128_util::id128_read_by_path(dbus_machine_id, Id128FormatFlag::ID128_FORMAT_PLAIN) + { + log::info!("Initializing machine ID from D-Bus machine ID (/var/lib/dbus/machine-id)."); + return Ok(id128); + } + + if process::id() == 1 { + if let Ok(id128) = env::var("container_uuid") { + if id128_util::id128_is_valid(&id128.clone().into_bytes()) { + log::info!( + "Initializing machine ID from container UUID (process 1's container_uuid)." + ); + return Ok(id128); + } + } + } else { + let penv = String::from_utf8(fs::read(Path::new("/proc/1/environ")).unwrap()).unwrap(); + let idv: Vec<&str> = penv.split("container_uuid=").collect(); + if idv.len() > 1 { + let id128 = idv[1]; + let idplain: String = id128.chars().take(32).collect(); + let idrfc: String = id128.chars().take(36).collect(); + + if id128_util::id128_is_valid(&idplain.clone().into_bytes()) { + log::info!("Initializing machine ID from environ's container UUID (/proc/1/environ's container_uuid)."); + return Ok(idplain); + } else if id128_util::id128_is_valid(&idrfc.clone().into_bytes()) { + log::info!("Initializing machine ID from environ's container UUID (/proc/1/environ's container_uuid)."); + return Ok(idrfc); + } + } + } + + match id128_util::id128_read_by_path( + Path::new("/sys/class/dmi/id/product_uuid"), + Id128FormatFlag::ID128_FORMAT_UUID, + ) { + Ok(id128) => { + log::info!("Initializing machine ID from VM UUID (/sys/class/dmi/id/product_uuid)."); + return Ok(id128); + } + Err(e) => { + if e.kind() == ErrorKind::NotFound { + if let Ok(id128) = id128_util::id128_read_by_path( + Path::new("/proc/device-tree/vm,uuid"), + Id128FormatFlag::ID128_FORMAT_UUID, + ) { + log::info!("Initializing machine ID from VM UUID (/proc/device-tree/vm,uuid)."); + return Ok(id128); + } + } + } + } + + log::info!("Initializing machine ID from random generator."); + id128_util::id128_randomize(Id128FormatFlag::ID128_FORMAT_PLAIN) +} + +/// +pub fn machine_id_setup(force_transient: bool, machine_id: &str) -> Result { + let etc_machine_id = Path::new("/etc/machine-id"); + let run_machine_id = Path::new("/run/machine-id"); + let mut writable = false; + let mut fd: RawFd = -1; + let ret_id128: String; + + // with umask(0000) + let mut saved_umask = SFlag::from_bits_truncate( + umask(Mode::from_bits_truncate(0o0000)).bits() | SFlag::S_IFMT.bits(), + ); + while saved_umask.contains(SFlag::S_IFMT) { + match fcntl::open( + etc_machine_id, + OFlag::O_RDWR | OFlag::O_CREAT | OFlag::O_NOCTTY | OFlag::O_CLOEXEC, + Mode::from_bits_truncate(0o0444), + ) { + Ok(f) => { + writable = true; + fd = f; + } + Err(e1) => { + match fcntl::open( + etc_machine_id, + OFlag::O_RDONLY | OFlag::O_NOCTTY | OFlag::O_CLOEXEC, + Mode::from_bits_truncate(0o0444), + ) { + Ok(_) => writable = false, + Err(e2) => { + if e1 == Errno::EROFS && e2 == Errno::ENOENT { + log::error!("System cannot boot: Missing /etc/machine/id and /etc is mounted read-only. +Booting up is supported only when: +1) /etc/machine-id exists and is populated. +2) /etc/machine-id exists and is empty +3) /etc/machine-id is missing and /etc is writable."); + } else { + log::error!("Cannot open {:?}:{}", etc_machine_id, e2); + } + return Err(e2); + } + } + } + }; + saved_umask = saved_umask.bitand(SFlag::from_bits_truncate(0o0777)); + } + + if machine_id.is_empty() { + if let Ok(id128) = + id128_util::id128_read_by_path(etc_machine_id, Id128FormatFlag::ID128_FORMAT_PLAIN) + { + return Ok(id128); + } + ret_id128 = generate_machine_id()?; + } else { + ret_id128 = machine_id.to_string(); + } + + if writable { + if force_transient { + if let Err(e) = unistd::write(fd, "uninitialized\n".as_bytes()) { + log::error!("Failed to write uninitialized {:?}:{}", etc_machine_id, e); + return Err(e); + } + } else { + id128_util::id128_write( + etc_machine_id, + &true, + &ret_id128, + Id128FormatFlag::ID128_FORMAT_PLAIN, + )?; + unistd::close(fd)?; + return Ok(ret_id128); + } + } + + unistd::close(fd)?; + + // with umask(0022) + let mut saved_umask = SFlag::from_bits_truncate( + umask(Mode::from_bits_truncate(0o0022)).bits() | SFlag::S_IFMT.bits(), + ); + while saved_umask.contains(SFlag::S_IFMT) { + id128_util::id128_write( + run_machine_id, + &false, + &ret_id128, + Id128FormatFlag::ID128_FORMAT_PLAIN, + )?; + saved_umask = saved_umask.bitand(SFlag::from_bits_truncate(0o0777)); + } + + mount::mount( + Some(run_machine_id), + etc_machine_id, + None::<&str>, + mount::MsFlags::MS_BIND, + None::<&str>, + )?; + + mount::mount( + None::<&str>, + etc_machine_id, + None::<&str>, + mount::MsFlags::MS_BIND | mount::MsFlags::MS_RDONLY | mount::MsFlags::MS_REMOUNT, + None::<&str>, + )?; + + Ok(ret_id128) +} + #[cfg(test)] mod tests { use crate::machine::Machine; diff --git a/libs/basic/src/namespace_util.rs b/libs/basic/src/namespace_util.rs new file mode 100755 index 0000000000000000000000000000000000000000..13ea8b61f160361294359c9b538588faa337d069 --- /dev/null +++ b/libs/basic/src/namespace_util.rs @@ -0,0 +1,86 @@ +// Copyright (c) 2022 Huawei Technologies Co.,Ltd. All rights reserved. +// +// sysMaster is licensed under Mulan PSL v2. +// You can use this software according to the terms and conditions of the Mulan +// PSL v2. +// You may obtain a copy of Mulan PSL v2 at: +// http://license.coscl.org.cn/MulanPSL2 +// THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY +// KIND, EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO +// NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. +// See the Mulan PSL v2 for more details. + +//! + +use nix::{ + errno::Errno, + fcntl::{open, OFlag}, + mount::{mount, MsFlags}, + sched, + sys::stat::Mode, + unistd::{setgroups, setresgid, setresuid, Gid, Pid, Uid}, + Result, +}; +use std::{ + fs, io, + os::unix::io::RawFd, + path::{Path, PathBuf}, +}; + +/// return (/proc/self/ns/${p})'s fd if $pid is 0. +/// return (/proc/${pid}/ns/${p})'s fd if $pid isn't 0. +pub fn namespace_open(pid: &Pid, p: &Path) -> Result { + let proc_path: PathBuf; + if pid.as_raw() == 0 { + let s = format!("/proc/self/ns/{}", p.display()); + proc_path = PathBuf::from(&s); + } else { + let s = format!("/proc/{}/ns/{}", pid.as_raw(), p.display()); + proc_path = PathBuf::from(&s); + } + open( + &proc_path, + OFlag::O_RDONLY | OFlag::O_NOCTTY | OFlag::O_CLOEXEC, + Mode::empty(), + ) +} + +/// Detaches the mount namespace, disabling propagation from our namespace to the host +pub fn detach_mount_namespace() -> Result<()> { + sched::unshare(sched::CloneFlags::CLONE_NEWNS)?; + + mount( + None::<&str>, + "/", + None::<&str>, + MsFlags::MS_SLAVE | MsFlags::MS_REC, + None::<&str>, + ) +} + +fn reset_uid_gid() -> Result<()> { + match fs::read("/proc/self/setgroups") { + Ok(s) => { + if s != "allow".to_string().into_bytes() { + return Ok(()); + } + } + Err(e) => { + if e.kind() != io::ErrorKind::NotFound { + return Err(Errno::EIO); + } + } + }; + + setgroups(&[])?; + + setresgid(Gid::from_raw(0), Gid::from_raw(0), Gid::from_raw(0))?; + + setresuid(Uid::from_raw(0), Uid::from_raw(0), Uid::from_raw(0)) +} + +/// +pub fn namespace_enter(fd: &RawFd, f: sched::CloneFlags) -> Result<()> { + sched::setns(*fd, f)?; + reset_uid_gid() +}