rrun_ssh/
remote_mounts.rs

1use std::collections::hash_map::*;
2use std::env::current_dir;
3use std::path::{Path, PathBuf};
4use std::fs::File;
5use std::io::{BufRead, BufReader, Read};
6use std::io;
7use super::{Location, RemoteLocation};
8
9/// Encapsulates data on which directories have been remotely mounted.
10#[derive(Debug)]
11pub struct RemoteMounts {
12    map: HashMap<PathBuf, RemoteLocation>,
13}
14impl RemoteMounts {
15    /// Loads the contents of an mtab file into a `RemoteMounts` structure.
16    pub fn load_reader<R: Read>(r: R) -> io::Result<RemoteMounts> {
17        let mut line = String::new();
18        let mut reader = BufReader::new(r);
19
20        let mut map = HashMap::new();
21        while try!(reader.read_line(&mut line)) != 0 {
22            {
23                let mut split = line.split_whitespace();
24                let remote = RemoteLocation::new(split.next().unwrap());
25                if let Ok(remote) = remote {
26                    let mount = Path::new(split.next().unwrap()).to_path_buf();
27                    map.insert(mount, remote);
28                }
29            }
30            line.clear();
31        }
32        Ok(RemoteMounts { map: map })
33    }
34
35    /// Loads the contents of `/etc/mtab` into a `RemoteMounts` structure.
36    pub fn load() -> io::Result<RemoteMounts> {
37        Self::load_reader(try!(File::open("/etc/mtab")))
38    }
39
40    /// Determines the location of the given directory.
41    pub fn into_dir(mut self, p: PathBuf) -> Location {
42        let curr_dir = p;
43        let mut check = curr_dir.clone();
44        loop {
45            match self.map.entry(check.clone()) {
46                Entry::Vacant(_) => {
47                    if check.parent().is_none() {
48                        return Location::Local;
49                    }
50                    check.pop();
51                    continue;
52                }
53                Entry::Occupied(entry) => {
54                    let mut remote = entry.remove();
55                    remote.path.push(curr_dir.strip_prefix(&check).unwrap());
56                    return Location::Remote(remote);
57                }
58            }
59        }
60    }
61
62    /// Determines the location of the current directory.
63    pub fn into_current_dir(self) -> io::Result<Location> {
64        Ok(self.into_dir(try!(current_dir())))
65    }
66}
67
68#[cfg(test)]
69mod tests {
70    use std::path::Path;
71    use super::RemoteMounts;
72    use super::super::{Location, RemoteLocation};
73
74    #[test]
75    fn empty_mtab() {
76        let mtab: &[u8] = &[];
77        let dir = Path::new("/").to_path_buf();
78        assert_eq!(RemoteMounts::load_reader(mtab).unwrap().into_dir(dir),
79                   Location::Local);
80    }
81
82    #[test]
83    fn local_mtab() {
84        let mtab: &[u8] = b"\
85proc /proc proc rw,nosuid,nodev,noexec,relatime 0 0
86sys /sys sysfs rw,nosuid,nodev,noexec,relatime 0 0
87dev /dev devtmpfs rw,nosuid,relatime,size=4046352k,nr_inodes=1011588,mode=755 0 0
88run /run tmpfs rw,nosuid,nodev,relatime,mode=755 0 0
89/dev/sda1 / btrfs rw,relatime,compress=lzo,space_cache 0 0
90tmpfs /dev/shm tmpfs rw,nosuid,nodev 0 0
91devpts /dev/pts devpts rw,nosuid,noexec,relatime,gid=5,mode=620,ptmxmode=000 0 0
92tmpfs /sys/fs/cgroup tmpfs ro,nosuid,nodev,noexec,mode=755 0 0
93binfmt_misc /proc/sys/fs/binfmt_misc binfmt_misc rw,relatime 0 0
94tmpfs /tmp tmpfs rw 0 0
95";
96        let dir = Path::new("/").to_path_buf();
97        assert_eq!(RemoteMounts::load_reader(mtab).unwrap().into_dir(dir),
98                   Location::Local);
99    }
100
101    #[test]
102    fn remote_mtab() {
103        let mtab: &[u8] = b"\
104proc /proc proc rw,nosuid,nodev,noexec,relatime 0 0
105sys /sys sysfs rw,nosuid,nodev,noexec,relatime 0 0
106dev /dev devtmpfs rw,nosuid,relatime,size=4046352k,nr_inodes=1011588,mode=755 0 0
107example:/ /media/example fuse.sshfs rw,nosuid,nodev,relatime,user_id=1000,group_id=100 0 0
108run /run tmpfs rw,nosuid,nodev,relatime,mode=755 0 0
109/dev/sda1 / btrfs rw,relatime,compress=lzo,space_cache 0 0
110example.com:/home/user /run/user/1000/media/example fuse.sshfs rw,nosuid,nodev,relatime,\
111user_id=1000,group_id=100 0 0
112tmpfs /dev/shm tmpfs rw,nosuid,nodev 0 0
113devpts /dev/pts devpts rw,nosuid,noexec,relatime,gid=5,mode=620,ptmxmode=000 0 0
114user@example.com:/home/user /mnt fuse.sshfs rw,nosuid,nodev,relatime,user_id=1000,group_id=100 0 0
115tmpfs /sys/fs/cgroup tmpfs ro,nosuid,nodev,noexec,mode=755 0 0
116example.com:/home /mnt fuse.sshfs rw,nosuid,nodev,relatime,user_id=1000,group_id=100 0 0
117binfmt_misc /proc/sys/fs/binfmt_misc binfmt_misc rw,relatime 0 0
118tmpfs /tmp tmpfs rw 0 0
119";
120        let dir1 = Path::new("/").to_path_buf();
121        let dir2 = Path::new("/media/example").to_path_buf();
122        let dir3 = Path::new("/run/media").to_path_buf();
123        let dir4 = Path::new("/mnt/user").to_path_buf();
124        let remote2 = RemoteLocation::new("example:/").unwrap();
125        let remote4 = RemoteLocation::new("example.com:/home/user").unwrap();
126        assert_eq!(RemoteMounts::load_reader(mtab).unwrap().into_dir(dir1),
127                   Location::Local);
128        assert_eq!(RemoteMounts::load_reader(mtab).unwrap().into_dir(dir2),
129                   Location::Remote(remote2));
130        assert_eq!(RemoteMounts::load_reader(mtab).unwrap().into_dir(dir3),
131                   Location::Local);
132        assert_eq!(RemoteMounts::load_reader(mtab).unwrap().into_dir(dir4),
133                   Location::Remote(remote4));
134    }
135}