diff --git a/src/dnsd/scheme.rs b/src/dnsd/scheme.rs index d32e562736..0c19472ae5 100644 --- a/src/dnsd/scheme.rs +++ b/src/dnsd/scheme.rs @@ -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; } diff --git a/src/smolnetd/scheme/netcfg/mod.rs b/src/smolnetd/scheme/netcfg/mod.rs index a77a1a5d20..03a1b716c3 100644 --- a/src/smolnetd/scheme/netcfg/mod.rs +++ b/src/smolnetd/scheme/netcfg/mod.rs @@ -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, 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, 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, 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, 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, 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, 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, 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>; struct NetCfgFile { path: String, - cfg_node: CfgNodeRef, + is_dir: bool, + is_writable: bool, + is_readable: bool, + node_writer: Option>, read_buf: Vec, write_buf: Vec, 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 { 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 { + 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)) + } + } } diff --git a/src/smolnetd/scheme/netcfg/nodes.rs b/src/smolnetd/scheme/netcfg/nodes.rs index 4b9fe17401..398a4e7c38 100644 --- a/src/smolnetd/scheme/netcfg/nodes.rs +++ b/src/smolnetd/scheme/netcfg/nodes.rs @@ -5,6 +5,54 @@ use syscall::Result as SyscallResult; pub type CfgNodeRef = Rc>; +pub trait NodeWriter { + fn write_line(&mut self, &str) -> SyscallResult<()> { + Ok(()) + } + + fn commit(&mut self) -> SyscallResult<()> { + Ok(()) + } +} + +pub struct SimpleWriter +where + WL: 'static + Fn(&mut T, &str) -> SyscallResult<()>, + C: 'static + Fn(&mut T) -> SyscallResult<()>, +{ + data: T, + write_line: WL, + commit: C, +} + +impl NodeWriter for SimpleWriter +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 SimpleWriter +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 { + 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 { - Ok(0) + fn open(&self, _file: &str) -> Option { + None } - fn open(&self, _file: &str) -> Option { + fn new_writer(&self) -> Option> { None } } @@ -56,21 +104,17 @@ where } } -pub struct WONode +pub struct WONode where - F: Fn(&str) -> SyscallResult, + W: 'static + Fn() -> Box, { - write_fun: F, + new_writer: W, } -impl CfgNode for WONode +impl CfgNode for WONode where - F: Fn(&str) -> SyscallResult, + W: 'static + Fn() -> Box, { - fn write(&self, buf: &str) -> SyscallResult { - (self.write_fun)(buf) - } - fn is_readable(&self) -> bool { false } @@ -78,53 +122,57 @@ where fn is_writable(&self) -> bool { true } -} -impl WONode -where - F: 'static + Fn(&str) -> SyscallResult, -{ - pub fn new_ref(write_fun: F) -> CfgNodeRef { - Rc::new(RefCell::new(WONode { write_fun })) + fn new_writer(&self) -> Option> { + Some((self.new_writer)()) } } -pub struct RWNode +impl WONode where - F: Fn() -> String, - G: Fn(&str) -> SyscallResult, + W: 'static + Fn() -> Box, { - read_fun: F, - write_fun: G, + pub fn new_ref(new_writer: W) -> CfgNodeRef { + Rc::new(RefCell::new(WONode { new_writer })) + } } -impl CfgNode for RWNode +pub struct RWNode where F: Fn() -> String, - G: Fn(&str) -> SyscallResult, + W: 'static + Fn() -> Box, +{ + read_fun: F, + new_writer: W, +} + +impl CfgNode for RWNode +where + F: Fn() -> String, + W: 'static + Fn() -> Box, { fn read(&self) -> String { (self.read_fun)() } - fn write(&self, buf: &str) -> SyscallResult { - (self.write_fun)(buf) - } - fn is_writable(&self) -> bool { true } + + fn new_writer(&self) -> Option> { + Some((self.new_writer)()) + } } -impl RWNode +impl RWNode where F: 'static + Fn() -> String, - G: 'static + Fn(&str) -> SyscallResult, + W: 'static + Fn() -> Box, { - 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 { + 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 { + 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)* }),* $(,)*) => {