1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
#[macro_use]
extern crate log;

#[macro_use]
extern crate error_chain;

use std::ffi::CString;
use std::path::Path;

pub mod errors {
    error_chain! {
        foreign_links {
            Io(::std::io::Error) #[cfg(unix)];
        }
    }
}

pub(crate) trait LibcString {
    fn as_libc(&self) -> (*const i8, CString);
}

impl LibcString for Path {
    fn as_libc(&self) -> (*const i8, CString) {
        let res = unsafe {
            use std::os::unix::ffi::OsStrExt;

            CString::from_vec_unchecked(self.as_os_str().as_bytes().to_vec())
        };

        (res.as_ptr() as *const i8, res)
    }
}


pub mod fd;
pub mod dir;
pub mod chroot;

#[cfg(test)]
extern crate libc;
#[cfg(test)]
extern crate tempdir;
#[cfg(test)]
extern crate env_logger;

#[cfg(test)]
mod test {
    use std;
    use libc;

    use std::ffi::OsString;
    use std::os::unix::ffi::OsStringExt;

    use tempdir::TempDir;

    pub enum FsItem<'a> {
        Dir(&'a [u8], &'a [FsItem<'a>]),
        File(&'a [u8], &'a str),
        DirLink(&'a [u8], &'a [u8], &'a [u8]),
        FileLink(&'a [u8], &'a [u8], &'a str),
        DeadLink(&'a [u8], &'a [u8]),
        Empty,
    }

    pub fn create_fsitem(dir_fd: &::fd::Fd, item: &FsItem) {
        use std::io::Write;

        match item {
            &FsItem::Dir(name, content) => {
                let path = OsString::from_vec(name.to_vec());

                if name != b"." {
                    dir_fd
                        .mkdirat(&path, 0o777)
                        .expect(&format!("failed to create dir {:?}", name));
                }

                let sub_fd = dir_fd.openat(&path, 0
                                           | libc::O_RDONLY | libc::O_DIRECTORY
                                           | libc::O_CLOEXEC)
                    .expect(&format!("failed to open dir {:?}", path));

                for i in content {
                    create_fsitem(&sub_fd, i);
                }
            }

            &FsItem::File(name, content) => {
                let path = OsString::from_vec(name.to_vec());
                let fs_fd =
                    dir_fd.createat(&path, 0
                                    | libc::O_WRONLY | libc::O_CLOEXEC
                                    | libc::O_EXCL, 0o0666)
                    .expect(&format!("failed to create file {:?}", path));

                fs_fd
                    .into_rawfd()
                    .expect(&format!("failed to unref fd"))
                    .into_file()
                    .expect(&format!("failed to transform file {:?}", path))
                    .write_all(content.as_bytes())
                    .expect(&format!("failed to fill file {:?}", path));
            }

            &FsItem::DirLink(name, dst, _) |
            &FsItem::FileLink(name, dst, _) |
            &FsItem::DeadLink(name, dst) => {
                let path = OsString::from_vec(name.to_vec());
                let target = OsString::from_vec(dst.to_vec());

                dir_fd.symlinkat(&target, &path)
                    .expect(&format!("failed to create {:?} -> {:?} link",
                                     path, target));
            }

            &FsItem::Empty => { },
        }
    }

    pub fn create_fs(tmpdir: &std::path::Path, item: &FsItem) {
        let fd =
            ::fd::Fd::open(&tmpdir, 0
                           | libc::O_RDONLY | libc::O_DIRECTORY
                           | libc::O_CLOEXEC)
            .expect(&format!("failed to open tmpdir {:?}", tmpdir));

        create_fsitem(&fd, item);
    }

    pub fn create_tmpdir() -> TempDir {
        let res = TempDir::new("unix-fd-test")
            .expect("failed to create tmpdir");

        res
    }
}