Skip to content

Commit

Permalink
Add group write
Browse files Browse the repository at this point in the history
  • Loading branch information
JonathonReinhart committed Dec 10, 2023
1 parent 85e9e7b commit 16bd0e4
Show file tree
Hide file tree
Showing 3 changed files with 115 additions and 5 deletions.
65 changes: 62 additions & 3 deletions scubainit-rs/src/groups.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
use std::fs::File;
use std::io::{BufRead, BufReader};
use std::io::{BufRead, BufReader, Write};
use std::path::Path;

#[derive(Debug)]
Expand Down Expand Up @@ -47,6 +47,15 @@ impl GroupEntry {
members: members,
})
}

fn to_line(&self) -> String {
format!("{}:{}:{}:{}",
self.name,
self.passwd,
self.gid,
self.members.join(","),
)
}
}

impl Iterator for GroupFileReader {
Expand All @@ -71,18 +80,68 @@ impl Iterator for GroupFileReader {
}
}

pub struct GroupFileWriter<'a> {
file: &'a File,
}


impl GroupFileWriter<'_> {
pub fn new(file: &File) -> GroupFileWriter {
GroupFileWriter {
file: file,
}
}

pub fn write(&mut self, group: &GroupEntry) -> std::io::Result<()> {
let line = group.to_line() + "\n";
let data = line.as_bytes();
let written = self.file.write(data)?;
if written != data.len() {
return Err(short_write());
}
Ok(())
}
}

fn short_write() -> std::io::Error {
std::io::Error::new(std::io::ErrorKind::Other, "Short write")
}


#[cfg(test)]
mod tests {
use super::*;

macro_rules! vec_of_strings {
// match a list of expressions separated by comma:
($($str:expr),*) => ({
// create a Vec with this list of expressions,
// calling String::from on each:
vec![$(String::from($str),)*] as Vec<String>
});
}

#[test]
fn it_works() -> Result<(), String> {
fn from_line_works() -> Result<(), String> {
let line = "foo:x:1234:moe,larry,shemp";
let result = Group::from_line(line).unwrap();
let result = GroupEntry::from_line(line).unwrap();
assert_eq!(result.name, "foo");
assert_eq!(result.passwd, "x");
assert_eq!(result.gid, 1234);
assert_eq!(result.members.as_slice(), ["moe", "larry", "shemp"]);
Ok(())
}

#[test]
fn to_line_works() -> Result<(), String> {
let grp = GroupEntry {
name: "foo".to_string(),
passwd: "x".to_string(),
gid: 1234,
members: vec_of_strings!["moe", "larry", "shemp"],
};
let line = grp.to_line();
assert_eq!(line, "foo:x:1234:moe,larry,shemp");
Ok(())
}
}
18 changes: 17 additions & 1 deletion scubainit-rs/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -83,6 +83,8 @@ impl UserInfo {
let gid = self.gid;

// Try to find a conflicting group (one matching name or gid).
// TODO: Skip if file is missing
// TODO: Can we open the file once (like fopen(path, "a+"))
let mut reader = groups::GroupFileReader::open(ETC_GROUP)?;
for grp in reader {
let name_matches = grp.name.as_str() == group_name;
Expand All @@ -101,14 +103,25 @@ impl UserInfo {
}
}

todo!("Add /etc/group entry");
// Okay, add group
let grp = groups::GroupEntry {
name: group_name.to_string(),
passwd: INVALID_PASSWORD.to_string(),
gid: gid,
members: Vec::new(),
};
let file = fs::File::options().append(true).create(true).open(ETC_GROUP)?;
let mut writer = groups::GroupFileWriter::new(&file);
Ok(writer.write(&grp)?)
}

pub fn add_user(&self) -> Result<()> {
let user_name = &self.user;
let uid = self.uid;

// Try to find a conflicting user (one matching name or uid).
// TODO: Skip if file is missing
// TODO: Can we open the file once (like fopen(path, "a+"))
let mut reader = passwd::PasswdFileReader::open(ETC_PASSWD)?;
for pwd in reader {
let name_matches = pwd.name.as_str() == user_name;
Expand All @@ -127,13 +140,16 @@ impl UserInfo {
}
}

// Okay, add user
todo!("Add /etc/passwd entry");
}

pub fn add_shadow<P: AsRef<Path>>(&self, path: P) -> Result<()> {
let user_name = &self.user;

// Try to find a conflicting user (one matching name).
// TODO: Skip if file is missing
// TODO: Can we open the file once (like fopen(path, "a+"))
let mut reader = shadow::ShadowFileReader::open(ETC_SHADOW)?;
for sp in reader {
if sp.name.as_str() == user_name {
Expand Down
37 changes: 36 additions & 1 deletion scubainit-rs/tests/groups.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,18 @@
use anyhow::Result;
use scubainit_rs::groups::{GroupFileReader};
use std::io::{Read, Seek};
use tempfile;

use scubainit_rs::groups::{GroupEntry, GroupFileReader, GroupFileWriter};

// TODO: DRY
macro_rules! vec_of_strings {
// match a list of expressions separated by comma:
($($str:expr),*) => ({
// create a Vec with this list of expressions,
// calling String::from on each:
vec![$(String::from($str),)*] as Vec<String>
});
}

#[test]
fn test_group_empty() -> Result<()> {
Expand Down Expand Up @@ -34,3 +47,25 @@ fn test_group1() -> Result<()> {
Ok(())
}

#[test]
fn test_write() -> Result<()> {
let mut file = tempfile::tempfile()?;
let mut writer = GroupFileWriter::new(&file);
let grp = GroupEntry {
name: "foo".to_string(),
passwd: "x".to_string(),
gid: 1234,
members: vec_of_strings!["moe", "larry", "shemp"],
};
writer.write(&grp)?;

file.rewind()?;

let mut buffer = String::with_capacity(128);
file.read_to_string(&mut buffer)?;

assert_eq!(buffer, "foo:x:1234:moe,larry,shemp\n");


Ok(())
}

0 comments on commit 16bd0e4

Please sign in to comment.