mod.rs 6.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253
  1. //! pwd implementation for relibc
  2. use core::ptr;
  3. use c_str::CStr;
  4. use header::{errno, fcntl};
  5. use platform;
  6. use platform::types::*;
  7. use platform::Sys;
  8. use platform::{Line, RawFile, RawLineBuffer};
  9. #[repr(C)]
  10. pub struct passwd {
  11. pw_name: *mut c_char,
  12. pw_passwd: *mut c_char,
  13. pw_uid: uid_t,
  14. pw_gid: gid_t,
  15. pw_gecos: *mut c_char,
  16. pw_dir: *mut c_char,
  17. pw_shell: *mut c_char,
  18. }
  19. static mut PASSWD_BUF: *mut c_char = ptr::null_mut();
  20. static mut PASSWD: passwd = passwd {
  21. pw_name: ptr::null_mut(),
  22. pw_passwd: ptr::null_mut(),
  23. pw_uid: 0,
  24. pw_gid: 0,
  25. pw_gecos: ptr::null_mut(),
  26. pw_dir: ptr::null_mut(),
  27. pw_shell: ptr::null_mut(),
  28. };
  29. enum OptionPasswd {
  30. Error,
  31. NotFound,
  32. Found(*mut c_char),
  33. }
  34. fn pwd_lookup<F>(
  35. out: *mut passwd,
  36. alloc: Option<(*mut c_char, size_t)>,
  37. mut callback: F,
  38. ) -> OptionPasswd
  39. where
  40. // TODO F: FnMut(impl Iterator<Item = &[u8]>) -> bool
  41. F: FnMut(&[&[u8]]) -> bool,
  42. {
  43. let file = match RawFile::open(
  44. unsafe { CStr::from_bytes_with_nul_unchecked(b"/etc/passwd\0") },
  45. fcntl::O_RDONLY,
  46. 0,
  47. ) {
  48. Ok(file) => file,
  49. Err(_) => return OptionPasswd::Error,
  50. };
  51. let mut rlb = RawLineBuffer::new(*file);
  52. loop {
  53. let line = match rlb.next() {
  54. Line::Error => return OptionPasswd::Error,
  55. Line::EOF => return OptionPasswd::NotFound,
  56. Line::Some(line) => line,
  57. };
  58. // Parse into passwd
  59. let mut parts: [&[u8]; 7] = [&[]; 7];
  60. for (i, part) in line.splitn(7, |b| *b == b':').enumerate() {
  61. parts[i] = part;
  62. }
  63. if !callback(&parts) {
  64. // TODO when nll becomes a thing:
  65. // buf.drain(..newline + 1);
  66. continue;
  67. }
  68. let len = parts
  69. .iter()
  70. .enumerate()
  71. .filter(|(i, _)| *i != 2 && *i != 3)
  72. .map(|(_, part)| part.len() + 1)
  73. .sum();
  74. if alloc.map(|(_, s)| len > s as usize).unwrap_or(false) {
  75. unsafe {
  76. platform::errno = errno::ERANGE;
  77. }
  78. return OptionPasswd::Error;
  79. }
  80. let alloc = match alloc {
  81. Some((alloc, _)) => alloc,
  82. None => unsafe { platform::alloc(len) as *mut c_char },
  83. };
  84. // _ prefix so it won't complain about the trailing
  85. // _off += <thing>
  86. // in the macro that is never read
  87. let mut _off = 0;
  88. let mut parts = parts.into_iter();
  89. macro_rules! copy_into {
  90. ($entry:expr) => {
  91. debug_assert!(_off as usize <= len);
  92. let src = parts.next().unwrap_or(&(&[] as &[u8])); // this is madness
  93. let dst = unsafe { alloc.offset(_off) };
  94. for (i, c) in src.iter().enumerate() {
  95. unsafe {
  96. *dst.offset(i as isize) = *c as c_char;
  97. }
  98. }
  99. unsafe {
  100. *dst.offset(src.len() as isize) = 0;
  101. $entry = dst;
  102. }
  103. _off += src.len() as isize + 1;
  104. };
  105. ($entry:expr,parse) => {
  106. unsafe {
  107. $entry = parts
  108. .next()
  109. .and_then(|part| core::str::from_utf8(part).ok())
  110. .and_then(|part| part.parse().ok())
  111. .unwrap_or(0);
  112. }
  113. };
  114. }
  115. copy_into!((*out).pw_name);
  116. copy_into!((*out).pw_passwd);
  117. copy_into!((*out).pw_uid, parse);
  118. copy_into!((*out).pw_gid, parse);
  119. copy_into!((*out).pw_gecos);
  120. copy_into!((*out).pw_dir);
  121. copy_into!((*out).pw_shell);
  122. return OptionPasswd::Found(alloc);
  123. }
  124. }
  125. #[no_mangle]
  126. pub extern "C" fn getpwnam_r(
  127. name: *const c_char,
  128. out: *mut passwd,
  129. buf: *mut c_char,
  130. size: size_t,
  131. result: *mut *mut passwd,
  132. ) -> c_int {
  133. match pwd_lookup(out, Some((buf, size)), |parts| {
  134. let part = parts.get(0).unwrap_or(&(&[] as &[u8]));
  135. for (i, c) in part.iter().enumerate() {
  136. // /etc/passwd should not contain any NUL bytes in the middle
  137. // of entries, but if this happens, it can't possibly match the
  138. // search query since it's NUL terminated.
  139. if *c == 0 || unsafe { *name.offset(i as isize) } != *c as c_char {
  140. return false;
  141. }
  142. }
  143. true
  144. }) {
  145. OptionPasswd::Error => unsafe {
  146. *result = ptr::null_mut();
  147. -1
  148. },
  149. OptionPasswd::NotFound => unsafe {
  150. *result = ptr::null_mut();
  151. 0
  152. },
  153. OptionPasswd::Found(_) => unsafe {
  154. *result = out;
  155. 0
  156. },
  157. }
  158. }
  159. #[no_mangle]
  160. pub extern "C" fn getpwuid_r(
  161. uid: uid_t,
  162. out: *mut passwd,
  163. buf: *mut c_char,
  164. size: size_t,
  165. result: *mut *mut passwd,
  166. ) -> c_int {
  167. match pwd_lookup(out, Some((buf, size)), |parts| {
  168. let part = parts
  169. .get(2)
  170. .and_then(|part| core::str::from_utf8(part).ok())
  171. .and_then(|part| part.parse().ok());
  172. part == Some(uid)
  173. }) {
  174. OptionPasswd::Error => unsafe {
  175. *result = ptr::null_mut();
  176. -1
  177. },
  178. OptionPasswd::NotFound => unsafe {
  179. *result = ptr::null_mut();
  180. 0
  181. },
  182. OptionPasswd::Found(_) => unsafe {
  183. *result = out;
  184. 0
  185. },
  186. }
  187. }
  188. #[no_mangle]
  189. pub extern "C" fn getpwnam(name: *const c_char) -> *mut passwd {
  190. match pwd_lookup(unsafe { &mut PASSWD }, None, |parts| {
  191. let part = parts.get(0).unwrap_or(&(&[] as &[u8]));
  192. for (i, c) in part.iter().enumerate() {
  193. // /etc/passwd should not contain any NUL bytes in the middle
  194. // of entries, but if this happens, it can't possibly match the
  195. // search query since it's NUL terminated.
  196. if *c == 0 || unsafe { *name.offset(i as isize) } != *c as c_char {
  197. return false;
  198. }
  199. }
  200. true
  201. }) {
  202. OptionPasswd::Error => ptr::null_mut(),
  203. OptionPasswd::NotFound => ptr::null_mut(),
  204. OptionPasswd::Found(buf) => unsafe {
  205. PASSWD_BUF = buf;
  206. &mut PASSWD
  207. },
  208. }
  209. }
  210. #[no_mangle]
  211. pub extern "C" fn getpwuid(uid: uid_t) -> *mut passwd {
  212. match pwd_lookup(unsafe { &mut PASSWD }, None, |parts| {
  213. let part = parts
  214. .get(2)
  215. .and_then(|part| core::str::from_utf8(part).ok())
  216. .and_then(|part| part.parse().ok());
  217. part == Some(uid)
  218. }) {
  219. OptionPasswd::Error => ptr::null_mut(),
  220. OptionPasswd::NotFound => ptr::null_mut(),
  221. OptionPasswd::Found(buf) => unsafe {
  222. if PASSWD_BUF != ptr::null_mut() {
  223. platform::free(PASSWD_BUF as *mut c_void);
  224. }
  225. PASSWD_BUF = buf;
  226. &mut PASSWD
  227. },
  228. }
  229. }