Merge pull request #17 from batonius/netcfg_commit

Change netcfg to validate on write and commit on fsync
This commit is contained in:
Jeremy Soller
2018-02-03 07:10:00 -07:00
committed by GitHub
3 changed files with 344 additions and 112 deletions
+2 -2
View File
@@ -65,9 +65,9 @@ impl Domains {
pub fn update_nameserver(&mut self) {
if let Ok(mut file) = File::open("netcfg:resolv/nameserver") {
let mut nameserver = String::new();
if let Ok(_) = file.read_to_string(&mut nameserver) {
if file.read_to_string(&mut nameserver).is_ok() {
if let Some(line) = nameserver.lines().next() {
if let Ok(ip) = Ipv4Addr::from_str(&line) {
if let Ok(ip) = Ipv4Addr::from_str(line) {
trace!("Changing nameserver to {}", ip);
self.nameserver = ip;
}
+227 -66
View File
@@ -8,6 +8,7 @@ use std::collections::BTreeMap;
use std::fs::File;
use std::io::{Read, Write};
use std::rc::Rc;
use std::mem;
use std::str::FromStr;
use std::str;
use syscall::data::Stat;
@@ -45,19 +46,29 @@ fn mk_root_node(iface: Interface, notifier: NotifierRef, dns_config: DNSConfigRe
cfg_node!{
"resolv" => {
"nameserver" => {
rw [dns_config, notifier]
rw [dns_config, notifier] (Option<Ipv4Address>, None)
|| {
format!("{}\n", dns_config.borrow().name_server)
}
|name_server| {
let ip = Ipv4Address::from_str(name_server.trim())
.map_err(|_| SyscallError::new(syscall::EINVAL))?;
if !ip.is_unicast() {
return Err(SyscallError::new(syscall::EINVAL));
|cur_value, line| {
if cur_value.is_none() {
let ip = Ipv4Address::from_str(line.trim())
.map_err(|_| SyscallError::new(syscall::EINVAL))?;
if !ip.is_unicast() {
return Err(SyscallError::new(syscall::EINVAL));
}
*cur_value = Some(ip);
Ok(())
} else {
Err(SyscallError::new(syscall::EINVAL))
}
dns_config.borrow_mut().name_server = ip;
notifier.borrow_mut().schedule_notify("resolv/nameserver");
Ok(0)
}
|cur_value| {
if let Some(ip) = *cur_value {
dns_config.borrow_mut().name_server = ip;
notifier.borrow_mut().schedule_notify("resolv/nameserver");
}
Ok(())
}
}
},
@@ -72,49 +83,90 @@ fn mk_root_node(iface: Interface, notifier: NotifierRef, dns_config: DNSConfigRe
}
},
"add" => {
wo [iface, notifier] |routes| {
let default_gw = parse_default_gw(routes)?;
iface.borrow_mut().set_ipv4_gateway(Some(default_gw));
notifier.borrow_mut().schedule_notify("route/list");
Ok(0)
wo [iface, notifier] (Option<Ipv4Address>, None)
|cur_value, line| {
if cur_value.is_none() {
let default_gw = parse_default_gw(line)?;
if !default_gw.is_unicast() {
return Err(SyscallError::new(syscall::EINVAL));
}
*cur_value = Some(default_gw);
Ok(())
} else {
Err(SyscallError::new(syscall::EINVAL))
}
}
|cur_value| {
if let Some(default_gw) = *cur_value {
iface.borrow_mut().set_ipv4_gateway(Some(default_gw));
notifier.borrow_mut().schedule_notify("route/list");
Ok(())
} else {
Err(SyscallError::new(syscall::EINVAL))
}
}
},
"rm" => {
wo [iface, notifier] |routes| {
let default_gw = parse_default_gw(routes)?;
let mut iface = iface.borrow_mut();
if iface.ipv4_gateway() != Some(default_gw) {
return Err(SyscallError::new(syscall::EINVAL));
wo [iface, notifier] (Option<Ipv4Address>, None)
|cur_value, line| {
if cur_value.is_none() {
let default_gw = parse_default_gw(line)?;
if !default_gw.is_unicast() {
return Err(SyscallError::new(syscall::EINVAL));
}
*cur_value = Some(default_gw);
Ok(())
} else {
Err(SyscallError::new(syscall::EINVAL))
}
iface.set_ipv4_gateway(None);
notifier.borrow_mut().schedule_notify("route/list");
Ok(0)
}
}
|cur_value| {
if let Some(default_gw) = *cur_value {
let mut iface = iface.borrow_mut();
if iface.ipv4_gateway() != Some(default_gw) {
return Err(SyscallError::new(syscall::EINVAL));
}
iface.set_ipv4_gateway(None);
notifier.borrow_mut().schedule_notify("route/list");
Ok(())
} else {
Err(SyscallError::new(syscall::EINVAL))
}
}
},
},
"ifaces" => {
"eth0" => {
"mac" => {
rw [iface, notifier]
rw [iface, notifier] (Option<EthernetAddress>, None)
|| {
format!("{}\n", iface.borrow().ethernet_addr())
}
|mac| {
let mac = mac.lines().next()
.ok_or_else(|| SyscallError::new(syscall::EINVAL))?;
let mac = EthernetAddress::from_str(mac).
map_err(|_| SyscallError::new(syscall::EINVAL))?;
if !mac.is_unicast() {
return Err(SyscallError::new(syscall::EINVAL));
|cur_value, line| {
if cur_value.is_none() {
let mac = EthernetAddress::from_str(line).
map_err(|_| SyscallError::new(syscall::EINVAL))?;
if !mac.is_unicast() {
return Err(SyscallError::new(syscall::EINVAL));
}
*cur_value = Some(mac);
Ok(())
} else {
Err(SyscallError::new(syscall::EINVAL))
}
iface.borrow_mut().set_ethernet_addr(mac);
notifier.borrow_mut().schedule_notify("ifaces/eth0/mac");
Ok(0)
}
|cur_value| {
if let Some(mac) = *cur_value {
iface.borrow_mut().set_ethernet_addr(mac);
notifier.borrow_mut().schedule_notify("ifaces/eth0/mac");
}
Ok(())
}
},
"addr" => {
"list" => {
ro [iface] || {
ro [iface]
|| {
let mut ips = String::new();
for cidr in iface.borrow().ip_addrs() {
ips += &format!("{}\n", cidr);
@@ -122,37 +174,71 @@ fn mk_root_node(iface: Interface, notifier: NotifierRef, dns_config: DNSConfigRe
ips
}
},
"set" => {
wo [iface, notifier] (Vec<IpCidr>, Vec::new())
|cur_value, line| {
let cidr = IpCidr::from_str(line)
.map_err(|_| SyscallError::new(syscall::EINVAL))?;
if !cidr.address().is_unicast() {
return Err(SyscallError::new(syscall::EINVAL));
}
cur_value.push(cidr);
Ok(())
}
|cur_value| {
if !cur_value.is_empty() {
let mut iface = iface.borrow_mut();
let mut cidrs = vec![];
mem::swap(cur_value, &mut cidrs);
iface.update_ip_addrs(|s| {
*s = From::from(cidrs);
});
notifier.borrow_mut().schedule_notify("ifaces/eth0/addr/list");
}
Ok(())
}
},
"add" => {
wo [iface, notifier] |input| {
wo [iface, notifier] (Vec<IpCidr>, Vec::new())
|cur_value, line| {
let cidr = IpCidr::from_str(line)
.map_err(|_| SyscallError::new(syscall::EINVAL))?;
if !cidr.address().is_unicast() {
return Err(SyscallError::new(syscall::EINVAL));
}
cur_value.push(cidr);
Ok(())
}
|cur_value| {
let mut iface = iface.borrow_mut();
let mut cidrs = iface.ip_addrs().to_vec();
for cidr in input.lines() {
let cidr = IpCidr::from_str(cidr)
.map_err(|_| SyscallError::new(syscall::EINVAL))?;
if !cidr.address().is_unicast() {
return Err(SyscallError::new(syscall::EINVAL));
}
cidrs.insert(0, cidr);
for cidr in cur_value {
cidrs.insert(0, *cidr);
}
iface.update_ip_addrs(|s| {
*s = From::from(cidrs);
});
notifier.borrow_mut().schedule_notify("ifaces/eth0/addr/list");
Ok(0)
Ok(())
}
},
"rm" => {
wo [iface, notifier] |input| {
wo [iface, notifier] (Vec<IpCidr>, Vec::new())
|cur_value, line| {
let cidr = IpCidr::from_str(line)
.map_err(|_| SyscallError::new(syscall::EINVAL))?;
if !cidr.address().is_unicast() {
return Err(SyscallError::new(syscall::EINVAL));
}
cur_value.push(cidr);
Ok(())
}
|cur_value| {
let mut iface = iface.borrow_mut();
let mut cidrs = iface.ip_addrs().to_vec();
for cidr in input.lines() {
let cidr = IpCidr::from_str(cidr)
.map_err(|_| SyscallError::new(syscall::EINVAL))?;
if !cidr.address().is_unicast() {
return Err(SyscallError::new(syscall::EINVAL));
}
for cidr in cur_value {
let pre_retain_len = cidrs.len();
cidrs.retain(|&c| c != cidr);
cidrs.retain(|&c| c != *cidr);
if pre_retain_len == cidrs.len() {
return Err(SyscallError::new(syscall::EINVAL));
}
@@ -161,7 +247,7 @@ fn mk_root_node(iface: Interface, notifier: NotifierRef, dns_config: DNSConfigRe
*s = From::from(cidrs);
});
notifier.borrow_mut().schedule_notify("ifaces/eth0/addr/list");
Ok(0)
Ok(())
}
},
}
@@ -178,11 +264,59 @@ type DNSConfigRef = Rc<RefCell<DNSConfig>>;
struct NetCfgFile {
path: String,
cfg_node: CfgNodeRef,
is_dir: bool,
is_writable: bool,
is_readable: bool,
node_writer: Option<Box<NodeWriter>>,
read_buf: Vec<u8>,
write_buf: Vec<u8>,
pos: usize,
uid: u32,
done: bool,
}
impl NetCfgFile {
fn commit(&mut self) -> SyscallResult<()> {
if let Some(ref mut node_writer) = self.node_writer {
if !self.write_buf.is_empty() {
let line = str::from_utf8(&self.write_buf)
.or_else(|_| Err(SyscallError::new(syscall::EINVAL)))?;
node_writer.write_line(line)?;
}
node_writer.commit()?;
self.write_buf.clear();
}
Ok(())
}
fn consume_lines(&mut self) -> SyscallResult<()> {
if let Some(ref mut node_writer) = self.node_writer {
let mut swap_with = None;
{
let mut lines = self.write_buf.split(|&c| c == b'\n');
if let Some(mut cur_line) = lines.next() {
let mut consumed = false;
for next_line in lines {
let line = str::from_utf8(cur_line)
.or_else(|_| Err(SyscallError::new(syscall::EINVAL)))?;
trace!("writing line {}", line);
node_writer.write_line(line)?;
cur_line = next_line;
consumed = true;
}
if consumed {
swap_with = Some(From::from(cur_line))
}
}
}
if let Some(ref mut new_vec) = swap_with {
mem::swap(&mut self.write_buf, new_vec);
}
Ok(())
} else {
Err(SyscallError::new(syscall::EBADF))
}
}
}
pub struct NetCfgScheme {
@@ -243,7 +377,8 @@ impl SchemeMut for NetCfgScheme {
.ok_or_else(|| SyscallError::new(syscall::EINVAL))?;
current_node = next_node;
}
let read_buf = Vec::from(current_node.borrow().read());
let current_node = current_node.borrow();
let read_buf = Vec::from(current_node.read());
let fd = self.next_fd;
trace!("open {} {}", fd, path);
self.next_fd += 1;
@@ -251,11 +386,15 @@ impl SchemeMut for NetCfgScheme {
fd,
NetCfgFile {
path: path.to_owned(),
cfg_node: current_node,
is_dir: current_node.is_dir(),
is_writable: current_node.is_writable(),
is_readable: current_node.is_readable(),
node_writer: if current_node.is_writable() { current_node.new_writer() } else { None },
uid,
pos: 0,
read_buf,
write_buf: vec![],
done: false,
},
);
Ok(fd)
@@ -263,13 +402,10 @@ impl SchemeMut for NetCfgScheme {
fn close(&mut self, fd: usize) -> SyscallResult<usize> {
trace!("close {}", fd);
if let Some(file) = self.files.remove(&fd) {
if let Some(mut file) = self.files.remove(&fd) {
self.notifier.borrow_mut().unsubscribe(&file.path, fd);
let node = file.cfg_node.borrow();
if node.is_writable() {
let value = str::from_utf8(&file.write_buf)
.or_else(|_| Err(SyscallError::new(syscall::EINVAL)))?;
node.write(value)
if !file.done {
file.commit().map(|_| 0)
} else {
Ok(0)
}
@@ -283,6 +419,10 @@ impl SchemeMut for NetCfgScheme {
.get_mut(&fd)
.ok_or_else(|| SyscallError::new(syscall::EBADF))?;
if file.done {
return Err(SyscallError::new(syscall::EBADF));
}
if file.uid != 0 {
return Err(SyscallError::new(syscall::EACCES));
}
@@ -290,7 +430,15 @@ impl SchemeMut for NetCfgScheme {
if (WRITE_BUFFER_MAX_SIZE - file.write_buf.len()) < buf.len() {
return Err(SyscallError::new(syscall::EMSGSIZE));
}
file.write_buf.extend_from_slice(buf);
if let Err(e) = file.consume_lines() {
trace!("Failed write {} {}", fd, e);
file.done = true;
return Err(e);
}
Ok(buf.len())
}
@@ -312,17 +460,16 @@ impl SchemeMut for NetCfgScheme {
let file = self.files
.get_mut(&fd)
.ok_or_else(|| SyscallError::new(syscall::EBADF))?;
let cfg_node = file.cfg_node.borrow();
stat.st_mode = if cfg_node.is_dir() {
stat.st_mode = if file.is_dir {
MODE_DIR
} else {
MODE_FILE
};
if cfg_node.is_writable() {
if file.is_writable {
stat.st_mode |= 0o222;
}
if cfg_node.is_readable() {
if file.is_readable {
stat.st_mode |= 0o444;
}
stat.st_uid = 0;
@@ -343,4 +490,18 @@ impl SchemeMut for NetCfgScheme {
}
Ok(fd)
}
fn fsync(&mut self, fd: usize) -> SyscallResult<usize> {
let file = self.files
.get_mut(&fd)
.ok_or_else(|| SyscallError::new(syscall::EBADF))?;
if !file.done {
let res = file.commit().map(|_| 0);
file.done = true;
res
} else {
Err(SyscallError::new(syscall::EBADF))
}
}
}
+115 -44
View File
@@ -5,6 +5,54 @@ use syscall::Result as SyscallResult;
pub type CfgNodeRef = Rc<RefCell<CfgNode>>;
pub trait NodeWriter {
fn write_line(&mut self, &str) -> SyscallResult<()> {
Ok(())
}
fn commit(&mut self) -> SyscallResult<()> {
Ok(())
}
}
pub struct SimpleWriter<T, WL, C>
where
WL: 'static + Fn(&mut T, &str) -> SyscallResult<()>,
C: 'static + Fn(&mut T) -> SyscallResult<()>,
{
data: T,
write_line: WL,
commit: C,
}
impl<T, WL, C> NodeWriter for SimpleWriter<T, WL, C>
where
WL: 'static + Fn(&mut T, &str) -> SyscallResult<()>,
C: 'static + Fn(&mut T) -> SyscallResult<()>,
{
fn write_line(&mut self, line: &str) -> SyscallResult<()> {
(self.write_line)(&mut self.data, line)
}
fn commit(&mut self) -> SyscallResult<()> {
(self.commit)(&mut self.data)
}
}
impl<T, WL, C> SimpleWriter<T, WL, C>
where
WL: 'static + Fn(&mut T, &str) -> SyscallResult<()>,
C: 'static + Fn(&mut T) -> SyscallResult<()>,
{
pub fn new_boxed(data: T, write_line: WL, commit: C) -> Box<Self> {
Box::new(SimpleWriter {
data,
write_line,
commit,
})
}
}
pub trait CfgNode {
fn is_dir(&self) -> bool {
false
@@ -22,11 +70,11 @@ pub trait CfgNode {
String::new()
}
fn write(&self, _buf: &str) -> SyscallResult<usize> {
Ok(0)
fn open(&self, _file: &str) -> Option<CfgNodeRef> {
None
}
fn open(&self, _file: &str) -> Option<CfgNodeRef> {
fn new_writer(&self) -> Option<Box<NodeWriter>> {
None
}
}
@@ -56,21 +104,17 @@ where
}
}
pub struct WONode<F>
pub struct WONode<W>
where
F: Fn(&str) -> SyscallResult<usize>,
W: 'static + Fn() -> Box<NodeWriter>,
{
write_fun: F,
new_writer: W,
}
impl<F> CfgNode for WONode<F>
impl<W> CfgNode for WONode<W>
where
F: Fn(&str) -> SyscallResult<usize>,
W: 'static + Fn() -> Box<NodeWriter>,
{
fn write(&self, buf: &str) -> SyscallResult<usize> {
(self.write_fun)(buf)
}
fn is_readable(&self) -> bool {
false
}
@@ -78,53 +122,57 @@ where
fn is_writable(&self) -> bool {
true
}
}
impl<F> WONode<F>
where
F: 'static + Fn(&str) -> SyscallResult<usize>,
{
pub fn new_ref(write_fun: F) -> CfgNodeRef {
Rc::new(RefCell::new(WONode { write_fun }))
fn new_writer(&self) -> Option<Box<NodeWriter>> {
Some((self.new_writer)())
}
}
pub struct RWNode<F, G>
impl<W> WONode<W>
where
F: Fn() -> String,
G: Fn(&str) -> SyscallResult<usize>,
W: 'static + Fn() -> Box<NodeWriter>,
{
read_fun: F,
write_fun: G,
pub fn new_ref(new_writer: W) -> CfgNodeRef {
Rc::new(RefCell::new(WONode { new_writer }))
}
}
impl<F, G> CfgNode for RWNode<F, G>
pub struct RWNode<F, W>
where
F: Fn() -> String,
G: Fn(&str) -> SyscallResult<usize>,
W: 'static + Fn() -> Box<NodeWriter>,
{
read_fun: F,
new_writer: W,
}
impl<F, W> CfgNode for RWNode<F, W>
where
F: Fn() -> String,
W: 'static + Fn() -> Box<NodeWriter>,
{
fn read(&self) -> String {
(self.read_fun)()
}
fn write(&self, buf: &str) -> SyscallResult<usize> {
(self.write_fun)(buf)
}
fn is_writable(&self) -> bool {
true
}
fn new_writer(&self) -> Option<Box<NodeWriter>> {
Some((self.new_writer)())
}
}
impl<F, G> RWNode<F, G>
impl<F, W> RWNode<F, W>
where
F: 'static + Fn() -> String,
G: 'static + Fn(&str) -> SyscallResult<usize>,
W: 'static + Fn() -> Box<NodeWriter>,
{
pub fn new_ref(read_fun: F, write_fun: G) -> CfgNodeRef {
pub fn new_ref(read_fun: F, new_writer: W) -> CfgNodeRef {
Rc::new(RefCell::new(RWNode {
read_fun,
write_fun,
new_writer,
}))
}
}
@@ -170,23 +218,46 @@ macro_rules! cfg_node {
RONode::new_ref(move|| $b)
}
};
(wo [ $($c:ident),* ] |$i:ident| $b:block ) => {
(wo [ $($c:ident),* ] ( $et:ty , $e:expr ) |$data_i:ident, $line_i:ident|
$write_line:block |$data_i2:ident| $commit:block) => {
{
$(let $c = $c.clone();)*
WONode::new_ref(move |$i: &str| $b)
$(#[allow(unused_variables)] let $c = $c.clone();)*;
let new_writer = move || -> Box<NodeWriter> {
let write_line = {
$(#[allow(unused_variables)] let $c = $c.clone();)*;
move |$data_i: &mut $et, $line_i: &str| $write_line
};
let commit = {
$(#[allow(unused_variables)] let $c = $c.clone();)*;
move |$data_i2: &mut $et| $commit
};
let data: $et = $e;
SimpleWriter::new_boxed(data, write_line, commit)
};
WONode::new_ref(new_writer)
}
};
(rw [ $($c:ident),* ] || $rb:block |$i:ident| $wb:block ) => {
(rw [ $($c:ident),* ] ( $et:ty , $e:expr ) || $read_fun:block |$data_i:ident, $line_i:ident|
$write_line:block |$data_i2:ident| $commit:block) => {
{
let read_fun = {
$(#[allow(unused_variables)] let $c = $c.clone();)*
move || $rb
$(#[allow(unused_variables)] let $c = $c.clone();)*;
move || $read_fun
};
let write_fun = {
$(#[allow(unused_variables)] let $c = $c.clone();)*
move |$i: &str| $wb
$(#[allow(unused_variables)] let $c = $c.clone();)*;
let new_writer = move || -> Box<NodeWriter> {
let write_line = {
$(#[allow(unused_variables)] let $c = $c.clone();)*;
move |$data_i: &mut $et, $line_i: &str| $write_line
};
let commit = {
$(#[allow(unused_variables)] let $c = $c.clone();)*;
move |$data_i2: &mut $et| $commit
};
let data: $et = $e;
SimpleWriter::new_boxed(data, write_line, commit)
};
RWNode::new_ref(read_fun, write_fun)
RWNode::new_ref(read_fun, new_writer)
}
};
($($e:expr => { $($t:tt)* }),* $(,)*) => {