1 //! This module contains parser for /proc/PID/mountinfo
2 //!
3 use std;
4 use std::fmt;
5 use std::ffi::{OsStr, OsString};
6 use std::os::unix::ffi::{OsStrExt, OsStringExt};
7 use std::borrow::Cow;
8 use std::error::Error;
9
10 use nix::mount::MsFlags;
11
12 use libc::c_ulong;
13
14 /// Error parsing a single entry of mountinfo file
15 #[derive(Debug)]
16 pub(crate) struct ParseRowError(pub(crate) String);
17
18 impl fmt::Display for ParseRowError {
fmt(&self, f: &mut fmt::Formatter) -> fmt::Result19 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
20 write!(f, "Parse error: {}", self.0)
21 }
22 }
23
24 impl Error for ParseRowError {
description(&self) -> &str25 fn description(&self) -> &str {
26 return &self.0;
27 }
28 }
29
30 /// Mountinfo file parsing error
31 #[derive(Debug)]
32 pub struct ParseError {
33 msg: String,
34 row_num: usize,
35 row: String,
36 }
37
38 impl ParseError {
new(msg: String, row_num: usize, row: String) -> ParseError39 fn new(msg: String, row_num: usize, row: String) -> ParseError {
40 ParseError {
41 msg: msg,
42 row_num: row_num,
43 row: row,
44 }
45 }
46 }
47
48 impl fmt::Display for ParseError {
fmt(&self, f: &mut fmt::Formatter) -> fmt::Result49 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
50 write!(f, "Parse error at line {}: {}\n{}",
51 self.row_num, self.description(), self.row)
52 }
53 }
54
55 impl Error for ParseError {
description(&self) -> &str56 fn description(&self) -> &str {
57 return &self.msg;
58 }
59 }
60
61 /// A parser class for mountinfo file
62 #[derive(Debug)]
63 pub struct Parser<'a> {
64 data: &'a [u8],
65 row_num: usize,
66 exhausted: bool,
67 }
68
69 #[allow(dead_code)]
70 impl<'a> Parser<'a> {
71 /// Create a new parser
72 ///
73 /// `data` should contain whole contents of `mountinfo` file of any process
new(data: &'a [u8]) -> Parser<'a>74 pub fn new(data: &'a [u8]) -> Parser<'a> {
75 Parser {
76 data: data,
77 row_num: 0,
78 exhausted: false,
79 }
80 }
81 }
82
83 /// A single entry returned by mountpoint parser
84 #[allow(missing_docs)] // self-descriptive / described by man page
85 #[derive(Debug)]
86 pub struct MountPoint<'a> {
87 pub mount_id: c_ulong,
88 pub parent_id: c_ulong,
89 pub major: c_ulong,
90 pub minor: c_ulong,
91 pub root: Cow<'a, OsStr>,
92 pub mount_point: Cow<'a, OsStr>,
93 pub mount_options: Cow<'a, OsStr>,
94 // TODO: we might need some enum which will have three states:
95 // empty, single Cow<OsStr> value or a vector Vec<Cow<OsStr>>
96 pub optional_fields: Cow<'a, OsStr>,
97 pub fstype: Cow<'a, OsStr>,
98 pub mount_source: Cow<'a, OsStr>,
99 pub super_options: Cow<'a, OsStr>,
100 }
101
102 impl<'a> MountPoint<'a> {
103 /// Returns flags of the mountpoint as a numeric value
104 ///
105 /// This value matches linux `MsFlags::MS_*` flags as passed into mount syscall
get_flags(&self) -> c_ulong106 pub fn get_flags(&self) -> c_ulong {
107 self.get_mount_flags().bits() as c_ulong
108 }
109
get_mount_flags(&self) -> MsFlags110 pub(crate) fn get_mount_flags(&self) -> MsFlags {
111 let mut flags = MsFlags::empty();
112 for opt in self.mount_options.as_bytes().split(|c| *c == b',') {
113 let opt = OsStr::from_bytes(opt);
114 if opt == OsStr::new("ro") { flags |= MsFlags::MS_RDONLY }
115 else if opt == OsStr::new("nosuid") { flags |= MsFlags::MS_NOSUID }
116 else if opt == OsStr::new("nodev") { flags |= MsFlags::MS_NODEV }
117 else if opt == OsStr::new("noexec") { flags |= MsFlags::MS_NOEXEC }
118 else if opt == OsStr::new("mand") { flags |= MsFlags::MS_MANDLOCK }
119 else if opt == OsStr::new("sync") { flags |= MsFlags::MS_SYNCHRONOUS }
120 else if opt == OsStr::new("dirsync") { flags |= MsFlags::MS_DIRSYNC }
121 else if opt == OsStr::new("noatime") { flags |= MsFlags::MS_NOATIME }
122 else if opt == OsStr::new("nodiratime") { flags |= MsFlags::MS_NODIRATIME }
123 else if opt == OsStr::new("relatime") { flags |= MsFlags::MS_RELATIME }
124 else if opt == OsStr::new("strictatime") { flags |= MsFlags::MS_STRICTATIME }
125 }
126 flags
127 }
128 }
129
130 impl<'a> Iterator for Parser<'a> {
131 type Item = Result<MountPoint<'a>, ParseError>;
132
next(&mut self) -> Option<Self::Item>133 fn next(&mut self) -> Option<Self::Item> {
134 if self.exhausted {
135 return None;
136 }
137
138 loop {
139 match self.data.iter().position(|c| *c == b'\n') {
140 Some(ix) => {
141 self.row_num += 1;
142 let row = &self.data[..ix];
143 self.data = &self.data[ix + 1..];
144 let res = match parse_mount_point(row) {
145 Ok(None) => continue,
146 Ok(Some(v)) => Ok(v),
147 Err(e) => Err(ParseError::new(e.0, self.row_num,
148 String::from_utf8_lossy(row).into_owned())),
149 };
150 return Some(res);
151 },
152 None => {
153 self.exhausted = true;
154 let res = match parse_mount_point(self.data) {
155 Ok(None) => return None,
156 Ok(Some(v)) => Ok(v),
157 Err(e) => Err(ParseError::new(e.0, self.row_num,
158 String::from_utf8_lossy(self.data).into_owned())),
159 };
160 return Some(res);
161 },
162 }
163 }
164 }
165 }
166
parse_mount_point<'a>(row: &'a [u8]) -> Result<Option<MountPoint<'a>>, ParseRowError>167 pub(crate) fn parse_mount_point<'a>(row: &'a [u8])
168 -> Result<Option<MountPoint<'a>>, ParseRowError>
169 {
170 let row = rstrip_cr(&row);
171 if is_comment_line(row) {
172 return Ok(None);
173 }
174
175 let (mount_id, row) = try!(parse_int(row));
176 let (parent_id, row) = try!(parse_int(row));
177 let (major, minor, row) = try!(parse_major_minor(row));
178 let (root, row) = try!(parse_os_str(row));
179 let (mount_point, row) = try!(parse_os_str(row));
180 let (mount_options, row) = try!(parse_os_str(row));
181 let (optional_fields, row) = try!(parse_optional(row));
182 let (fstype, row) = try!(parse_os_str(row));
183 let (mount_source, row) = try!(parse_os_str(row));
184 let (super_options, _) = try!(parse_os_str(row));
185 // TODO: should we ignore extra fields?
186 Ok(Some(MountPoint {
187 mount_id: mount_id,
188 parent_id: parent_id,
189 major: major,
190 minor: minor,
191 root: root,
192 mount_point: mount_point,
193 mount_options: mount_options,
194 optional_fields: optional_fields,
195 fstype: fstype,
196 mount_source: mount_source,
197 super_options: super_options,
198 }))
199 }
200
is_comment_line(row: &[u8]) -> bool201 fn is_comment_line(row: &[u8]) -> bool {
202 if row.is_empty() {
203 return true;
204 }
205 for c in row {
206 if *c == b' ' || *c == b'\t' {
207 continue;
208 }
209 if *c == b'#' {
210 return true;
211 }
212 return false;
213 }
214 return false;
215 }
216
rstrip_cr(row: &[u8]) -> &[u8]217 fn rstrip_cr(row: &[u8]) -> &[u8] {
218 if let Some((&b'\r', tail)) = row.split_last() {
219 tail
220 } else {
221 row
222 }
223 }
224
parse_field<'a>(data: &'a [u8], delimit: &'a [u8]) -> Result<(&'a [u8], &'a [u8]), ParseRowError>225 fn parse_field<'a>(data: &'a [u8], delimit: &'a [u8])
226 -> Result<(&'a [u8], &'a [u8]), ParseRowError>
227 {
228 if data.is_empty() {
229 return Err(ParseRowError(format!("Expected more fields")));
230 }
231 let data = lstrip_whitespaces(data);
232 Ok(split_by(data, delimit))
233 }
234
parse_os_str<'a>(data: &'a [u8]) -> Result<(Cow<'a, OsStr>, &'a [u8]), ParseRowError>235 fn parse_os_str<'a>(data: &'a [u8])
236 -> Result<(Cow<'a, OsStr>, &'a [u8]), ParseRowError>
237 {
238 let (field, tail) = try!(parse_field(data, b" "));
239 Ok((unescape_octals(OsStr::from_bytes(field)), tail))
240 }
241
parse_int(data: &[u8]) -> Result<(c_ulong, &[u8]), ParseRowError>242 fn parse_int(data: &[u8])
243 -> Result<(c_ulong, &[u8]), ParseRowError>
244 {
245 let (field, tail) = try!(parse_field(data, b" "));
246 let v = try!(std::str::from_utf8(field).map_err(|e| {
247 ParseRowError(format!("Cannot parse integer {:?}: {}",
248 String::from_utf8_lossy(field).into_owned(), e))}));
249
250 let v = try!(c_ulong::from_str_radix(v, 10).map_err(|e| {
251 ParseRowError(format!("Cannot parse integer {:?}: {}",
252 String::from_utf8_lossy(field).into_owned(), e))}));
253 Ok((v, tail))
254 }
255
parse_major_minor(data: &[u8]) -> Result<(c_ulong, c_ulong, &[u8]), ParseRowError>256 fn parse_major_minor(data: &[u8])
257 -> Result<(c_ulong, c_ulong, &[u8]), ParseRowError>
258 {
259 let (major_field, data) = try!(parse_field(data, b":"));
260 let (minor_field, tail) = try!(parse_field(data, b" "));
261 let (major, _) = try!(parse_int(major_field));
262 let (minor, _) = try!(parse_int(minor_field));
263 Ok((major, minor, tail))
264 }
265
parse_optional<'a>(data: &'a [u8]) -> Result<(Cow<'a, OsStr>, &'a [u8]), ParseRowError>266 fn parse_optional<'a>(data: &'a [u8])
267 -> Result<(Cow<'a, OsStr>, &'a [u8]), ParseRowError>
268 {
269 let (field, tail) = try!(parse_field(data, b"- "));
270 let field = rstrip_whitespaces(field);
271 Ok((unescape_octals(OsStr::from_bytes(field)), tail))
272 }
273
lstrip_whitespaces(v: &[u8]) -> &[u8]274 fn lstrip_whitespaces(v: &[u8]) -> &[u8] {
275 for (i, c) in v.iter().enumerate() {
276 if *c != b' ' {
277 return &v[i..];
278 }
279 }
280 return &v[0..0];
281 }
282
rstrip_whitespaces(v: &[u8]) -> &[u8]283 fn rstrip_whitespaces(v: &[u8]) -> &[u8] {
284 for (i, c) in v.iter().enumerate().rev() {
285 if *c != b' ' {
286 return &v[..i + 1];
287 }
288 }
289 return &v[0..0];
290 }
291
split_by<'a, 'b>(v: &'a [u8], needle: &'b [u8]) -> (&'a [u8], &'a [u8])292 fn split_by<'a, 'b>(v: &'a [u8], needle: &'b [u8]) -> (&'a [u8], &'a [u8]) {
293 if needle.len() > v.len() {
294 return (&v[0..], &v[0..0]);
295 }
296 let mut i = 0;
297 while i <= v.len() - needle.len() {
298 let (head, tail) = v.split_at(i);
299 if tail.starts_with(needle) {
300 return (head, &tail[needle.len()..]);
301 }
302 i += 1;
303 }
304 return (&v[0..], &v[0..0]);
305 }
306
unescape_octals(s: &OsStr) -> Cow<OsStr>307 fn unescape_octals(s: &OsStr) -> Cow<OsStr> {
308 let (mut i, has_escapes) = {
309 let bytes = s.as_bytes();
310 let mut i = 0;
311 while i < bytes.len() {
312 if is_octal_encoding(&bytes[i..]) {
313 break;
314 }
315 i += 1;
316 }
317 (i, i < bytes.len())
318 };
319 if !has_escapes {
320 return Cow::Borrowed(s);
321 }
322
323 let mut v: Vec<u8> = vec!();
324 let bytes = s.as_bytes();
325 v.extend_from_slice(&bytes[..i]);
326 while i < bytes.len() {
327 if is_octal_encoding(&bytes[i..]) {
328 let c = parse_octal(&bytes[i + 1..]);
329 v.push(c);
330 i += 4;
331 } else {
332 v.push(bytes[i]);
333 i += 1;
334 }
335 }
336 Cow::Owned(OsString::from_vec(v))
337 }
338
is_octal_encoding(v: &[u8]) -> bool339 fn is_octal_encoding(v: &[u8]) -> bool {
340 v.len() >= 4 && v[0] == b'\\'
341 && is_oct(v[1]) && is_oct(v[2]) && is_oct(v[3])
342 }
343
is_oct(c: u8) -> bool344 fn is_oct(c: u8) -> bool {
345 c >= b'0' && c <= b'7'
346 }
347
parse_octal(v: &[u8]) -> u8348 fn parse_octal(v: &[u8]) -> u8 {
349 ((v[0] & 7) << 6) + ((v[1] & 7) << 3) + (v[2] & 7)
350 }
351
352 #[cfg(test)]
353 mod test {
354 use std::path::Path;
355 use std::ffi::OsStr;
356 use std::os::unix::ffi::OsStrExt;
357
358 use nix::mount::MsFlags;
359
360 use super::{Parser, ParseError};
361 use super::{is_octal_encoding, parse_octal, unescape_octals};
362
363 #[test]
test_is_octal_encoding()364 fn test_is_octal_encoding() {
365 assert!(is_octal_encoding(b"\\000"));
366 assert!(is_octal_encoding(b"\\123"));
367 assert!(is_octal_encoding(b"\\777"));
368 assert!(!is_octal_encoding(b""));
369 assert!(!is_octal_encoding(b"\\"));
370 assert!(!is_octal_encoding(b"000"));
371 assert!(!is_octal_encoding(b"\\00"));
372 assert!(!is_octal_encoding(b"\\800"));
373 }
374
375 #[test]
376 fn test_parse_octal() {
377 assert_eq!(parse_octal(b"000"), 0);
378 assert_eq!(parse_octal(b"123"), 83);
379 assert_eq!(parse_octal(b"377"), 255);
380 // mount utility just ignores overflowing
381 assert_eq!(parse_octal(b"777"), 255);
382 }
383
384 #[test]
385 fn test_unescape_octals() {
386 assert_eq!(unescape_octals(OsStr::new("\\000")), OsStr::from_bytes(b"\x00"));
387 assert_eq!(unescape_octals(OsStr::new("\\00")), OsStr::new("\\00"));
388 assert_eq!(unescape_octals(OsStr::new("test\\040data")), OsStr::new("test data"));
389 }
390
391 #[test]
392 fn test_mount_info_parser_proc() {
393 let content = b"19 24 0:4 / /proc rw,nosuid,nodev,noexec,relatime shared:12 - proc proc rw";
394 let mut parser = Parser::new(&content[..]);
395 let mount_point = parser.next().unwrap().unwrap();
396 assert_eq!(mount_point.mount_id, 19);
397 assert_eq!(mount_point.parent_id, 24);
398 assert_eq!(mount_point.major, 0);
399 assert_eq!(mount_point.minor, 4);
400 assert_eq!(mount_point.root, Path::new("/"));
401 assert_eq!(mount_point.mount_point, Path::new("/proc"));
402 assert_eq!(mount_point.mount_options, OsStr::new("rw,nosuid,nodev,noexec,relatime"));
403 assert_eq!(mount_point.optional_fields, OsStr::new("shared:12"));
404 assert_eq!(mount_point.fstype, OsStr::new("proc"));
405 assert_eq!(mount_point.mount_source, OsStr::new("proc"));
406 assert_eq!(mount_point.super_options, OsStr::new("rw"));
407 assert_eq!(mount_point.get_mount_flags(), MsFlags::MS_NOSUID | MsFlags::MS_NODEV | MsFlags::MS_NOEXEC | MsFlags::MS_RELATIME);
408 assert!(parser.next().is_none());
409 }
410
411 #[test]
412 fn test_mount_info_parser_comment() {
413 let content = b"# Test comment\n\
414 \t # Another shifted comment\n\
415 19 24 0:4 / /#proc rw,nosuid,nodev,noexec,relatime shared:12 - proc proc rw";
416 let mut parser = Parser::new(&content[..]);
417 let mount_point = parser.next().unwrap().unwrap();
418 assert_eq!(mount_point.mount_id, 19);
419 assert_eq!(mount_point.parent_id, 24);
420 assert_eq!(mount_point.major, 0);
421 assert_eq!(mount_point.minor, 4);
422 assert_eq!(mount_point.root, Path::new("/"));
423 assert_eq!(mount_point.mount_point, Path::new("/#proc"));
424 assert_eq!(mount_point.mount_options, OsStr::new("rw,nosuid,nodev,noexec,relatime"));
425 assert_eq!(mount_point.optional_fields, OsStr::new("shared:12"));
426 assert_eq!(mount_point.fstype, OsStr::new("proc"));
427 assert_eq!(mount_point.mount_source, OsStr::new("proc"));
428 assert_eq!(mount_point.super_options, OsStr::new("rw"));
429 assert_eq!(mount_point.get_mount_flags(), MsFlags::MS_NOSUID | MsFlags::MS_NODEV | MsFlags::MS_NOEXEC | MsFlags::MS_RELATIME);
430 assert!(parser.next().is_none());
431 }
432
433 #[test]
434 fn test_mount_info_parser_missing_optional_fields() {
435 let content = b"335 294 0:56 / /proc rw,relatime - proc proc rw";
436 let mut parser = Parser::new(&content[..]);
437 let mount_point = parser.next().unwrap().unwrap();
438 assert_eq!(mount_point.mount_id, 335);
439 assert_eq!(mount_point.parent_id, 294);
440 assert_eq!(mount_point.major, 0);
441 assert_eq!(mount_point.minor, 56);
442 assert_eq!(mount_point.root, Path::new("/"));
443 assert_eq!(mount_point.mount_point, Path::new("/proc"));
444 assert_eq!(mount_point.mount_options, OsStr::new("rw,relatime"));
445 assert_eq!(mount_point.optional_fields, OsStr::new(""));
446 assert_eq!(mount_point.fstype, OsStr::new("proc"));
447 assert_eq!(mount_point.mount_source, OsStr::new("proc"));
448 assert_eq!(mount_point.super_options, OsStr::new("rw"));
449 assert_eq!(mount_point.get_mount_flags(), MsFlags::MS_RELATIME);
450 assert!(parser.next().is_none());
451 }
452
453 #[test]
454 fn test_mount_info_parser_more_optional_fields() {
455 let content = b"335 294 0:56 / /proc rw,relatime shared:12 master:1 - proc proc rw";
456 let mut parser = Parser::new(&content[..]);
457 let mount_point = parser.next().unwrap().unwrap();
458 assert_eq!(mount_point.mount_id, 335);
459 assert_eq!(mount_point.parent_id, 294);
460 assert_eq!(mount_point.major, 0);
461 assert_eq!(mount_point.minor, 56);
462 assert_eq!(mount_point.root, Path::new("/"));
463 assert_eq!(mount_point.mount_point, Path::new("/proc"));
464 assert_eq!(mount_point.mount_options, OsStr::new("rw,relatime"));
465 assert_eq!(mount_point.optional_fields, OsStr::new("shared:12 master:1"));
466 assert_eq!(mount_point.fstype, OsStr::new("proc"));
467 assert_eq!(mount_point.mount_source, OsStr::new("proc"));
468 assert_eq!(mount_point.super_options, OsStr::new("rw"));
469 assert_eq!(mount_point.get_mount_flags(), MsFlags::MS_RELATIME);
470 assert!(parser.next().is_none());
471 }
472
473 #[test]
474 fn test_mount_info_parser_escaping() {
475 let content = br"76 24 8:6 / /home/my\040super\011name\012\134 rw,relatime shared:29 - ext4 /dev/sda1 rw,data=ordered";
476 let mut parser = Parser::new(&content[..]);
477 let mount_point = parser.next().unwrap().unwrap();
478 assert_eq!(mount_point.mount_id, 76);
479 assert_eq!(mount_point.parent_id, 24);
480 assert_eq!(mount_point.major, 8);
481 assert_eq!(mount_point.minor, 6);
482 assert_eq!(mount_point.root, Path::new("/"));
483 assert_eq!(mount_point.mount_point, Path::new("/home/my super\tname\n\\"));
484 assert_eq!(mount_point.mount_options, OsStr::new("rw,relatime"));
485 assert_eq!(mount_point.optional_fields, OsStr::new("shared:29"));
486 assert_eq!(mount_point.fstype, OsStr::new("ext4"));
487 assert_eq!(mount_point.mount_source, OsStr::new("/dev/sda1"));
488 assert_eq!(mount_point.super_options, OsStr::new("rw,data=ordered"));
489 assert_eq!(mount_point.get_mount_flags(), MsFlags::MS_RELATIME);
490 assert!(parser.next().is_none());
491 }
492
493 #[test]
494 fn test_mount_info_parser_non_utf8() {
495 let content = b"22 24 0:19 / /\xff rw shared:5 - tmpfs tmpfs rw,mode=755";
496 let mut parser = Parser::new(&content[..]);
497 let mount_point = parser.next().unwrap().unwrap();
498 assert_eq!(mount_point.mount_point, Path::new(OsStr::from_bytes(b"/\xff")));
499 assert_eq!(mount_point.mount_options, OsStr::new("rw"));
500 assert_eq!(mount_point.fstype, OsStr::new("tmpfs"));
501 assert_eq!(mount_point.mount_source, OsStr::new("tmpfs"));
502 assert_eq!(mount_point.get_mount_flags(), MsFlags::empty());
503 assert!(parser.next().is_none());
504 }
505
506 #[test]
507 fn test_mount_info_parser_crlf() {
508 let content = b"26 20 0:21 / /tmp rw shared:4 - tmpfs tmpfs rw\r\n\
509 \n\
510 \r\n\
511 27 22 0:22 / /tmp rw,nosuid,nodev shared:6 - tmpfs tmpfs rw\r";
512 let mut parser = Parser::new(&content[..]);
513 let mount_point = parser.next().unwrap().unwrap();
514 assert_eq!(mount_point.mount_point, Path::new("/tmp"));
515 assert_eq!(mount_point.mount_options, OsStr::new("rw"));
516 assert_eq!(mount_point.super_options, OsStr::new("rw"));
517 assert_eq!(mount_point.get_mount_flags(), MsFlags::empty());
518 let mount_point = parser.next().unwrap().unwrap();
519 assert_eq!(mount_point.mount_point, Path::new("/tmp"));
520 assert_eq!(mount_point.mount_options, OsStr::new("rw,nosuid,nodev"));
521 assert_eq!(mount_point.super_options, OsStr::new("rw"));
522 assert_eq!(mount_point.get_mount_flags(), MsFlags::MS_NOSUID | MsFlags::MS_NODEV);
523 assert!(parser.next().is_none());
524 }
525
526 #[test]
527 fn test_mount_info_parser_incomplete_row() {
528 let content = b"19 24 0:4 / /proc rw,relatime shared:12 - proc proc";
529 let mut parser = Parser::new(&content[..]);
530 let mount_info_res = parser.next().unwrap();
531 assert!(mount_info_res.is_err());
532 match mount_info_res {
533 Err(ParseError {ref msg, ..}) => {
534 assert_eq!(msg, "Expected more fields");
535 },
536 _ => panic!("Expected incomplete row error")
537 }
538 assert!(parser.next().is_none());
539 }
540
541 #[test]
542 fn test_mount_info_parser_invalid_int() {
543 let content = b"19 24b 0:4 / /proc rw,relatime - proc proc rw";
544 let mut parser = Parser::new(&content[..]);
545 let mount_info_res = parser.next().unwrap();
546 assert!(mount_info_res.is_err());
547 match mount_info_res {
548 Err(ParseError {ref msg, ..}) => {
549 assert!(msg.starts_with("Cannot parse integer \"24b\":"));
550 },
551 _ => panic!("Expected invalid row error")
552 }
553 assert!(parser.next().is_none());
554 }
555
556 #[test]
test_mount_info_parser_overflowed_int()557 fn test_mount_info_parser_overflowed_int() {
558 let content = b"111111111111111111111";
559 let mut parser = Parser::new(&content[..]);
560 let mount_info_res = parser.next().unwrap();
561 assert!(mount_info_res.is_err());
562 match mount_info_res {
563 Err(ParseError {ref msg, ..}) => {
564 assert!(msg.starts_with("Cannot parse integer \"111111111111111111111\""));
565 },
566 _ => panic!("Expected invalid row error")
567 }
568 assert!(parser.next().is_none());
569 }
570
571 #[test]
test_mount_info_parser_invalid_escape()572 fn test_mount_info_parser_invalid_escape() {
573 let content = b"19 24 0:4 / /proc\\1 rw,relatime - proc proc rw";
574 let mut parser = Parser::new(&content[..]);
575 let mount_point = parser.next().unwrap().unwrap();
576 assert_eq!(mount_point.mount_point, Path::new("/proc\\1"));
577 assert!(parser.next().is_none());
578 }
579
580 #[test]
test_mount_info_parser_overflowed_escape()581 fn test_mount_info_parser_overflowed_escape() {
582 let content = b"19 24 0:4 / /proc\\400 rw,nosuid,nodev,noexec,relatime - proc proc rw";
583 let mut parser = Parser::new(&content[..]);
584 let mount_point = parser.next().unwrap().unwrap();
585 assert_eq!(mount_point.mount_point, Path::new(OsStr::from_bytes(b"/proc\x00")));
586 assert!(parser.next().is_none());
587 }
588 }
589