libpriv/passwd: move passwd database to Rust

This moves to Rust the in-memory structure holding passwd entries
(users and groups).
This commit is contained in:
Luca BRUNO 2021-01-12 10:06:57 +00:00 committed by OpenShift Merge Robot
parent 803e4db50c
commit bdf8269dfa
9 changed files with 176 additions and 83 deletions

View File

@ -13,6 +13,9 @@ prefix = "ROR"
# Here we exclude entries belonging to the C side which we use on the Rust side and so
# doesn't make sense to re-export.
exclude = ["RpmOstreeOrigin",
"PasswdDB",
"rpmostree_add_group_to_hash",
"rpmostree_add_passwd_to_hash",
"rpmostree_get_repodata_chksum_repr",
"rpmostree_origin_get_live_state" ]

View File

@ -4,6 +4,7 @@
* SPDX-License-Identifier: Apache-2.0 OR MIT
*/
use c_utf8::CUtf8;
use gio_sys;
use glib_sys;
use libc;
@ -120,6 +121,17 @@ pub(crate) fn ref_from_raw_ptr<T>(p: *mut T) -> &'static mut T {
// return a Result (using the std Error).
// TODO: Try upstreaming this into the glib crate?
/// Convert C results (int + GError convention) to anyhow.
pub(crate) fn int_gerror_to_result(res: i32, gerror: *mut glib_sys::GError) -> anyhow::Result<()> {
if res != 0 {
Ok(())
} else {
assert!(!gerror.is_null(), "invalid failure, NULL Gerror");
let err_msg = unsafe { CUtf8::from_ptr((*gerror).message) }?;
anyhow::bail!("{}", err_msg)
}
}
pub(crate) fn error_to_glib<E: Display>(e: &E, gerror: *mut *mut glib_sys::GError) {
if gerror.is_null() {
return;

View File

@ -8,6 +8,7 @@ NOTICE: The C header definitions are canonical, please update those first
then synchronize the entries here.
!*/
use crate::passwd::PasswdDB;
use libdnf_sys::DnfPackage;
// From `libpriv/rpmostree-rpm-util.h`.
@ -18,3 +19,22 @@ extern "C" {
gerror: *mut *mut glib_sys::GError,
) -> libc::c_int;
}
// From `libpriv/rpmostree-passwd-util.h`.
extern "C" {
#[allow(improper_ctypes)]
pub(crate) fn rpmostree_add_passwd_to_hash(
rootfs_dfd: libc::c_int,
path: *const libc::c_char,
db: *mut PasswdDB,
gerror: *mut *mut glib_sys::GError,
) -> libc::c_int;
#[allow(improper_ctypes)]
pub(crate) fn rpmostree_add_group_to_hash(
rootfs_dfd: libc::c_int,
path: *const libc::c_char,
db: *mut PasswdDB,
gerror: *mut *mut glib_sys::GError,
) -> libc::c_int;
}

View File

@ -109,6 +109,20 @@ mod ffi {
// FIXME/cxx make this Option<&str>
fn transaction_apply_live(sysroot: Pin<&mut OstreeSysroot>, target: &str) -> Result<()>;
}
// passwd.rs
extern "Rust" {
fn passwddb_open(rootfs: i32) -> Result<Box<PasswdDB>>;
type PasswdDB;
fn add_user(self: &mut PasswdDB, uid: u32, username: &str);
fn lookup_user(self: &PasswdDB, uid: u32) -> Result<String>;
fn add_group(self: &mut PasswdDB, gid: u32, groupname: &str);
fn lookup_group(self: &PasswdDB, gid: u32) -> Result<String>;
// TODO(lucab): get rid of the two methods below.
fn add_group_content(self: &mut PasswdDB, rootfs: i32, group_path: &str) -> Result<()>;
fn add_passwd_content(self: &mut PasswdDB, rootfs: i32, passwd_path: &str) -> Result<()>;
}
}
mod client;
@ -137,6 +151,8 @@ pub(crate) use self::live::*;
mod origin;
mod ostree_diff;
mod ostree_utils;
mod passwd;
use passwd::{passwddb_open, PasswdDB};
mod progress;
pub use self::progress::*;
mod scripts;

84
rust/src/passwd.rs Normal file
View File

@ -0,0 +1,84 @@
use crate::ffiutil;
use crate::includes::*;
use anyhow::{anyhow, bail};
use c_utf8::CUtf8Buf;
use nix::unistd::{Gid, Uid};
use std::collections::HashMap;
use std::os::unix::io::AsRawFd;
/// Populate a new DB with content from `passwd` and `group` files.
pub(crate) fn passwddb_open(rootfs: i32) -> anyhow::Result<Box<PasswdDB>> {
let fd = ffiutil::ffi_view_openat_dir(rootfs);
PasswdDB::populate_new(&fd).map(|db| Box::new(db))
}
/// Database holding users and groups.
#[derive(Debug, Default)]
pub struct PasswdDB {
users: HashMap<Uid, String>,
groups: HashMap<Gid, String>,
}
impl PasswdDB {
/// Populate a new DB with content from `passwd` and `group` files.
pub fn populate_new(rootfs: &openat::Dir) -> anyhow::Result<Self> {
let mut db = Self::default();
db.add_passwd_content(rootfs.as_raw_fd(), "usr/etc/passwd")?;
db.add_passwd_content(rootfs.as_raw_fd(), "usr/lib/passwd")?;
db.add_group_content(rootfs.as_raw_fd(), "usr/etc/group")?;
db.add_group_content(rootfs.as_raw_fd(), "usr/lib/group")?;
Ok(db)
}
/// Lookup user name by ID.
pub fn lookup_user(&self, uid: u32) -> anyhow::Result<String> {
let key = Uid::from_raw(uid);
self.users
.get(&key)
.cloned()
.ok_or_else(|| anyhow!("failed to find user ID '{}'", uid))
}
/// Lookup group name by ID.
pub fn lookup_group(&self, gid: u32) -> anyhow::Result<String> {
let key = Gid::from_raw(gid);
self.groups
.get(&key)
.cloned()
.ok_or_else(|| anyhow!("failed to find group ID '{}'", gid))
}
/// Add a user ID with the associated name.
pub fn add_user(&mut self, uid: u32, username: &str) {
let id = Uid::from_raw(uid);
self.users.insert(id, username.to_string());
}
/// Add a group ID with the associated name.
pub fn add_group(&mut self, gid: u32, groupname: &str) {
let id = Gid::from_raw(gid);
self.groups.insert(id, groupname.to_string());
}
/// Add content from a `group` file.
pub fn add_group_content(&mut self, rootfs: i32, group_path: &str) -> anyhow::Result<()> {
let c_path: CUtf8Buf = group_path.to_string().into();
let db_ptr = self as *mut Self;
let mut gerror: *mut glib_sys::GError = std::ptr::null_mut();
// TODO(lucab): find a replacement for `fgetgrent` and drop this.
let res =
unsafe { rpmostree_add_group_to_hash(rootfs, c_path.as_ptr(), db_ptr, &mut gerror) };
ffiutil::int_gerror_to_result(res, gerror)
}
/// Add content from a `passwd` file.
pub fn add_passwd_content(&mut self, rootfs: i32, passwd_path: &str) -> anyhow::Result<()> {
let c_path: CUtf8Buf = passwd_path.to_string().into();
let db_ptr = self as *mut Self;
let mut gerror: *mut glib_sys::GError = std::ptr::null_mut();
// TODO(lucab): find a replacement for `fgetpwent` and drop this.
let res =
unsafe { rpmostree_add_passwd_to_hash(rootfs, c_path.as_ptr(), db_ptr, &mut gerror) };
ffiutil::int_gerror_to_result(res, gerror)
}
}

View File

@ -1278,15 +1278,10 @@ rpmostree_passwd_complete_rpm_layering (int rootfs_dfd,
return TRUE;
}
struct RpmOstreePasswdDB
{
GHashTable *users;
GHashTable *groups;
};
static gboolean
add_passwd_to_hash (int rootfs_dfd, const char *path,
GHashTable *users, GError **error)
gboolean
rpmostree_add_passwd_to_hash (int rootfs_dfd, const char *path,
rpmostreecxx::PasswdDB *db,
GError **error)
{
g_autoptr(FILE) src_stream = open_file_stream_read_at (rootfs_dfd, path, error);
if (!src_stream)
@ -1302,15 +1297,20 @@ add_passwd_to_hash (int rootfs_dfd, const char *path,
return glnx_throw_errno_prefix (error, "fgetpwent");
break;
}
g_hash_table_insert (users, GUINT_TO_POINTER (pw->pw_uid), g_strdup (pw->pw_name));
if (pw->pw_name != NULL)
{
std::string username(pw->pw_name);
db->add_user (pw->pw_uid, username);
}
}
return TRUE;
}
static gboolean
add_groups_to_hash (int rootfs_dfd, const char *path,
GHashTable *groups, GError **error)
gboolean
rpmostree_add_group_to_hash (int rootfs_dfd, const char *path,
rpmostreecxx::PasswdDB *db,
GError **error)
{
g_autoptr(FILE) src_stream = open_file_stream_read_at (rootfs_dfd, path, error);
if (!src_stream)
@ -1326,48 +1326,12 @@ add_groups_to_hash (int rootfs_dfd, const char *path,
return glnx_throw_errno_prefix (error, "fgetgrent");
break;
}
g_hash_table_insert (groups, GUINT_TO_POINTER (gr->gr_gid), g_strdup (gr->gr_name));
if (gr->gr_name != NULL)
{
std::string groupname(gr->gr_name);
db->add_group (gr->gr_gid, groupname);
}
}
return TRUE;
}
RpmOstreePasswdDB *
rpmostree_passwddb_open (int rootfs, GCancellable *cancellable, GError **error)
{
g_autoptr(RpmOstreePasswdDB) ret = g_new0 (RpmOstreePasswdDB, 1);
ret->users = g_hash_table_new_full (NULL, NULL, NULL, g_free);
ret->groups = g_hash_table_new_full (NULL, NULL, NULL, g_free);
if (!add_passwd_to_hash (rootfs, "usr/etc/passwd", ret->users, error))
return NULL;
if (!add_passwd_to_hash (rootfs, "usr/lib/passwd", ret->users, error))
return NULL;
if (!add_groups_to_hash (rootfs, "usr/etc/group", ret->groups, error))
return NULL;
if (!add_groups_to_hash (rootfs, "usr/lib/group", ret->groups, error))
return NULL;
return util::move_nullify (ret);
}
const char *
rpmostree_passwddb_lookup_user (RpmOstreePasswdDB *db, uid_t uid)
{
return static_cast<const char*>(g_hash_table_lookup (db->users, GUINT_TO_POINTER (uid)));
}
const char *
rpmostree_passwddb_lookup_group (RpmOstreePasswdDB *db, gid_t gid)
{
return static_cast<const char*>(g_hash_table_lookup (db->groups, GUINT_TO_POINTER (gid)));
}
void
rpmostree_passwddb_free (RpmOstreePasswdDB *db)
{
g_hash_table_unref (db->users);
g_hash_table_unref (db->groups);
g_free (db);
}

View File

@ -25,10 +25,20 @@
#include <sys/types.h>
#include <sys/wait.h>
#include "rpmostree-cxxrs.h"
#include "rpmostree-rust.h"
G_BEGIN_DECLS
gboolean
rpmostree_add_passwd_to_hash (int rootfs_dfd, const char *path,
rpmostreecxx::PasswdDB *db,
GError **error);
gboolean
rpmostree_add_group_to_hash (int rootfs_dfd, const char *path,
rpmostreecxx::PasswdDB *db,
GError **error);
gboolean
rpmostree_check_passwd (OstreeRepo *repo,
int rootfs_dfd,
@ -69,17 +79,6 @@ rpmostree_passwd_compose_prep (int rootfs_dfd,
GCancellable *cancellable,
GError **error);
typedef struct RpmOstreePasswdDB RpmOstreePasswdDB;
RpmOstreePasswdDB *
rpmostree_passwddb_open (int rootfs, GCancellable *cancellable, GError **error);
const char *
rpmostree_passwddb_lookup_user (RpmOstreePasswdDB *db, uid_t uid);
const char *
rpmostree_passwddb_lookup_group (RpmOstreePasswdDB *db, gid_t gid);
void
rpmostree_passwddb_free (RpmOstreePasswdDB *db);
G_DEFINE_AUTOPTR_CLEANUP_FUNC(RpmOstreePasswdDB, rpmostree_passwddb_free)
gboolean
rpmostree_passwd_cleanup (int rootfs_dfd, GCancellable *cancellable, GError **error);

View File

@ -479,12 +479,12 @@ process_kernel_and_initramfs (int rootfs_dfd,
}
static gboolean
convert_var_to_tmpfiles_d_recurse (GOutputStream *tmpfiles_out,
int dfd,
RpmOstreePasswdDB *pwdb,
GString *prefix,
GCancellable *cancellable,
GError **error)
convert_var_to_tmpfiles_d_recurse (GOutputStream *tmpfiles_out,
int dfd,
rpmostreecxx::PasswdDB &pwdb,
GString *prefix,
GCancellable *cancellable,
GError **error)
{
g_auto(GLnxDirFdIterator) dfd_iter = { 0, };
gsize bytes_written;
@ -538,15 +538,11 @@ convert_var_to_tmpfiles_d_recurse (GOutputStream *tmpfiles_out,
struct stat stbuf;
if (!glnx_fstatat (dfd_iter.fd, dent->d_name, &stbuf, AT_SYMLINK_NOFOLLOW, error))
return FALSE;
g_string_append_printf (tmpfiles_d_buf, " 0%02o", stbuf.st_mode & ~S_IFMT);
const char *user = rpmostree_passwddb_lookup_user (pwdb, stbuf.st_uid);
if (!user)
return glnx_throw (error, "Failed to find user '%u' for %s", stbuf.st_uid, dent->d_name);
const char *group = rpmostree_passwddb_lookup_group (pwdb, stbuf.st_gid);
if (!group)
return glnx_throw (error, "Failed to find group '%u' for %s", stbuf.st_gid, dent->d_name);
g_string_append_printf (tmpfiles_d_buf, " %s %s - -", user, group);
auto username = pwdb.lookup_user (stbuf.st_uid);
auto groupname = pwdb.lookup_group (stbuf.st_gid);
g_string_append_printf (tmpfiles_d_buf, " %s %s - -", username.c_str(), groupname.c_str());
if (filetype_c == 'd')
{
@ -595,9 +591,7 @@ convert_var_to_tmpfiles_d (int rootfs_dfd,
{
GLNX_AUTO_PREFIX_ERROR ("Converting /var to tmpfiles.d", error);
g_autoptr(RpmOstreePasswdDB) pwdb = rpmostree_passwddb_open (rootfs_dfd, cancellable, error);
if (!pwdb)
return FALSE;
auto pwdb = rpmostreecxx::passwddb_open (rootfs_dfd);
glnx_autofd int var_dfd = -1;
/* List of files that are known to possibly exist, but in practice
@ -647,7 +641,7 @@ convert_var_to_tmpfiles_d (int rootfs_dfd,
return FALSE;
g_autoptr(GString) prefix = g_string_new ("/var");
if (!convert_var_to_tmpfiles_d_recurse (tmpfiles_out, rootfs_dfd, pwdb, prefix, cancellable, error))
if (!convert_var_to_tmpfiles_d_recurse (tmpfiles_out, rootfs_dfd, *pwdb, prefix, cancellable, error))
return FALSE;
if (!g_output_stream_close (tmpfiles_out, cancellable, error))

View File

@ -23,6 +23,7 @@
#include <ostree.h>
#include "rpmostree-json-parsing.h"
#include "rpmostree-rust.h"
#include "rpmostree-cxxrs.h"
G_BEGIN_DECLS