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