diff options
-rw-r--r-- | .gitignore | 2 | ||||
-rw-r--r-- | Cargo.toml | 5 | ||||
-rw-r--r-- | README.md | 8 | ||||
-rw-r--r-- | src/errno.rs | 314 | ||||
-rw-r--r-- | src/lib.rs | 12 | ||||
-rw-r--r-- | src/mount.rs | 107 | ||||
-rw-r--r-- | src/sched.rs | 80 | ||||
-rw-r--r-- | src/syscall.rs | 16 | ||||
-rw-r--r-- | src/unistd.rs | 69 |
9 files changed, 613 insertions, 0 deletions
diff --git a/.gitignore b/.gitignore new file mode 100644 index 00000000..1e7caa9e --- /dev/null +++ b/.gitignore @@ -0,0 +1,2 @@ +Cargo.lock +target/ diff --git a/Cargo.toml b/Cargo.toml new file mode 100644 index 00000000..a36bca66 --- /dev/null +++ b/Cargo.toml @@ -0,0 +1,5 @@ +[package] + +name = "linux" +version = "0.0.1" +authors = ["Carl Lerche <me@carllerche.com>"] diff --git a/README.md b/README.md new file mode 100644 index 00000000..fe6496be --- /dev/null +++ b/README.md @@ -0,0 +1,8 @@ +# Rust bindings to Linux APIs + +The goal is to provide rust friendly bindings to Linux APIs. This is +very much a work and progress and I am treating it as where I put +bindings that I need vs. spreading them around the various libs that I +work on. + +Of course, PRs welcome :) diff --git a/src/errno.rs b/src/errno.rs new file mode 100644 index 00000000..e71b4e80 --- /dev/null +++ b/src/errno.rs @@ -0,0 +1,314 @@ +#![cfg(target_os = "linux")] + +use std::os::errno; +use std::num::from_int; +use libc::c_int; + +pub type SysResult<T> = Result<T, SysError>; + +#[deriving(Clone)] +pub struct SysError { + pub kind: Errno, + pub desc: &'static str +} + +impl SysError { + pub fn last() -> SysError { + match from_int(errno()) { + Some(no) => SysError::new(no), + _ => SysError::new(UnknownErrno) + } + } + + pub fn new(kind: Errno) -> SysError { + SysError { + kind: kind, + desc: desc(kind) + } + } +} + +#[inline] +pub fn from_ffi(res: c_int) -> SysResult<()> { + if res != 0 { + return Err(SysError::last()); + } + + Ok(()) +} + +#[deriving(Show, Clone, PartialEq, FromPrimitive)] +pub enum Errno { + UnknownErrno = 0, + EPERM = 1, + ENOENT = 2, + ESRCH = 3, + EINTR = 4, + EIO = 5, + ENXIO = 6, + E2BIG = 7, + ENOEXEC = 8, + EBADF = 9, + ECHILD = 10, + EAGAIN = 11, + ENOMEM = 12, + EACCES = 13, + EFAULT = 14, + ENOTBLK = 15, + EBUSY = 16, + EEXIST = 17, + EXDEV = 18, + ENODEV = 19, + ENOTDIR = 20, + EISDIR = 21, + EINVAL = 22, + ENFILE = 23, + EMFILE = 24, + ENOTTY = 25, + ETXTBSY = 26, + EFBIG = 27, + ENOSPC = 28, + ESPIPE = 29, + EROFS = 30, + EMLINK = 31, + EPIPE = 32, + EDOM = 33, + ERANGE = 34, + EDEADLK = 35, + ENAMETOOLONG = 36, + ENOLCK = 37, + ENOSYS = 38, + ENOTEMPTY = 39, + ELOOP = 40, + ENOMSG = 42, + EIDRM = 43, + ECHRNG = 44, + EL2NSYNC = 45, + EL3HLT = 46, + EL3RST = 47, + ELNRNG = 48, + EUNATCH = 49, + ENOCSI = 50, + EL2HLT = 51, + EBADE = 52, + EBADR = 53, + EXFULL = 54, + ENOANO = 55, + EBADRQC = 56, + EBADSLT = 57, + EBFONT = 59, + ENOSTR = 60, + ENODATA = 61, + ETIME = 62, + ENOSR = 63, + ENONET = 64, + ENOPKG = 65, + EREMOTE = 66, + ENOLINK = 67, + EADV = 68, + ESRMNT = 69, + ECOMM = 70, + EPROTO = 71, + EMULTIHOP = 72, + EDOTDOT = 73, + EBADMSG = 74, + EOVERFLOW = 75, + ENOTUNIQ = 76, + EBADFD = 77, + EREMCHG = 78, + ELIBACC = 79, + ELIBBAD = 80, + ELIBSCN = 81, + ELIBMAX = 82, + ELIBEXEC = 83, + EILSEQ = 84, + ERESTART = 85, + ESTRPIPE = 86, + EUSERS = 87, + ENOTSOCK = 88, + EDESTADDRREQ = 89, + EMSGSIZE = 90, + EPROTOTYPE = 91, + ENOPROTOOPT = 92, + EPROTONOSUPPORT = 93, + ESOCKTNOSUPPORT = 94, + EOPNOTSUPP = 95, + EPFNOSUPPORT = 96, + EAFNOSUPPORT = 97, + EADDRINUSE = 98, + EADDRNOTAVAIL = 99, + ENETDOWN = 100, + ENETUNREACH = 101, + ENETRESET = 102, + ECONNABORTED = 103, + ECONNRESET = 104, + ENOBUFS = 105, + EISCONN = 106, + ENOTCONN = 107, + ESHUTDOWN = 108, + ETOOMANYREFS = 109, + ETIMEDOUT = 110, + ECONNREFUSED = 111, + EHOSTDOWN = 112, + EHOSTUNREACH = 113, + EALREADY = 114, + EINPROGRESS = 115, + ESTALE = 116, + EUCLEAN = 117, + ENOTNAM = 118, + ENAVAIL = 119, + EISNAM = 120, + EREMOTEIO = 121, + EDQUOT = 122, + ENOMEDIUM = 123, + EMEDIUMTYPE = 124, + ECANCELED = 125, + ENOKEY = 126, + EKEYEXPIRED = 127, + EKEYREVOKED = 128, + EKEYREJECTED = 129, + EOWNERDEAD = 130, + ENOTRECOVERABLE = 131, + ERFKILL = 132, + EHWPOISON = 133, +} + +pub static EWOULDBLOCK: Errno = EAGAIN; +pub static EDEADLOCK: Errno = EDEADLK; + +fn desc(kind: Errno) -> &'static str { + match kind { + UnknownErrno => "Unknown errno", + EPERM => "Operation not permitted", + ENOENT => "No such file or directory", + ESRCH => "No such process", + EINTR => "Interrupted system call", + EIO => "I/O error", + ENXIO => "No such device or address", + E2BIG => "Argument list too long", + ENOEXEC => "Exec format error", + EBADF => "Bad file number", + ECHILD => "No child processes", + EAGAIN => "Try again", + ENOMEM => "Out of memory", + EACCES => "Permission denied", + EFAULT => "Bad address", + ENOTBLK => "Block device required", + EBUSY => "Device or resource busy", + EEXIST => "File exists", + EXDEV => "Cross-device link", + ENODEV => "No such device", + ENOTDIR => "Not a directory", + EISDIR => "Is a directory", + EINVAL => "Invalid argument", + ENFILE => "File table overflow", + EMFILE => "Too many open files", + ENOTTY => "Not a typewriter", + ETXTBSY => "Text file busy", + EFBIG => "File too large", + ENOSPC => "No space left on device", + ESPIPE => "Illegal seek", + EROFS => "Read-only file system", + EMLINK => "Too many links", + EPIPE => "Broken pipe", + EDOM => "Math argument out of domain of func", + ERANGE => "Math result not representable", + EDEADLK => "Resource deadlock would occur", + ENAMETOOLONG => "File name too long", + ENOLCK => "No record locks available", + ENOSYS => "Function not implemented", + ENOTEMPTY => "Directory not empty", + ELOOP => "Too many symbolic links encountered", + ENOMSG => "No message of desired type", + EIDRM => "Identifier removed", + ECHRNG => "Channel number out of range", + EL2NSYNC => "Level 2 not synchronized", + EL3HLT => "Level 3 halted", + EL3RST => "Level 3 reset", + ELNRNG => "Link number out of range", + EUNATCH => "Protocol driver not attached", + ENOCSI => "No CSI structure available", + EL2HLT => "Level 2 halted", + EBADE => "Invalid exchange", + EBADR => "Invalid request descriptor", + EXFULL => "Exchange full", + ENOANO => "No anode", + EBADRQC => "Invalid request code", + EBADSLT => "Invalid slot", + EBFONT => "Bad font file format", + ENOSTR => "Device not a stream", + ENODATA => "No data available", + ETIME => "Timer expired", + ENOSR => "Out of streams resources", + ENONET => "Machine is not on the network", + ENOPKG => "Package not installed", + EREMOTE => "Object is remote", + ENOLINK => "Link has been severed", + EADV => "Advertise error", + ESRMNT => "Srmount error", + ECOMM => "Communication error on send", + EPROTO => "Protocol error", + EMULTIHOP => "Multihop attempted", + EDOTDOT => "RFS specific error", + EBADMSG => "Not a data message", + EOVERFLOW => "Value too large for defined data type", + ENOTUNIQ => "Name not unique on network", + EBADFD => "File descriptor in bad state", + EREMCHG => "Remote address changed", + ELIBACC => "Can not acces a needed shared library", + ELIBBAD => "Accessing a corrupted shared library", + ELIBSCN => ".lib section in a.out corrupted", + ELIBMAX => "Attempting to link in too many shared libraries", + ELIBEXEC => "Cannot exec a shared library directly", + EILSEQ => "Illegal byte sequence", + ERESTART => "Interrupted system call should be restarted", + ESTRPIPE => "Streams pipe error", + EUSERS => "Too many users", + ENOTSOCK => "Socket operation on non-socket", + EDESTADDRREQ => "Destination address required", + EMSGSIZE => "Message too long", + EPROTOTYPE => "Protocol wrong type for socket", + ENOPROTOOPT => "Protocol not available", + EPROTONOSUPPORT => "Protocol not supported", + ESOCKTNOSUPPORT => "Socket type not supported", + EOPNOTSUPP => "Operation not supported on transport endpoint", + EPFNOSUPPORT => "Protocol family not supported", + EAFNOSUPPORT => "Address family not supported by protocol", + EADDRINUSE => "Address already in use", + EADDRNOTAVAIL => "Cannot assign requested address", + ENETDOWN => "Network is down", + ENETUNREACH => "Network is unreachable", + ENETRESET => "Network dropped connection because of reset", + ECONNABORTED => "Software caused connection abort", + ECONNRESET => "Connection reset by peer", + ENOBUFS => "No buffer space available", + EISCONN => "Transport endpoint is already connected", + ENOTCONN => "Transport endpoint is not connected", + ESHUTDOWN => "Cannot send after transport endpoint shutdown", + ETOOMANYREFS => "Too many references: cannot splice", + ETIMEDOUT => "Connection timed out", + ECONNREFUSED => "Connection refused", + EHOSTDOWN => "Host is down", + EHOSTUNREACH => "No route to host", + EALREADY => "Operation already in progress", + EINPROGRESS => "Operation now in progress", + ESTALE => "Stale file handle", + EUCLEAN => "Structure needs cleaning", + ENOTNAM => "Not a XENIX named type file", + ENAVAIL => "No XENIX semaphores available", + EISNAM => "Is a named type file", + EREMOTEIO => "Remote I/O error", + EDQUOT => "Quota exceeded", + ENOMEDIUM => "No medium found", + EMEDIUMTYPE => "Wrong medium type", + ECANCELED => "Operation canceled", + ENOKEY => "Required key not available", + EKEYEXPIRED => "Key has expired", + EKEYREVOKED => "Key has been revoked", + EKEYREJECTED => "Key was rejected by service", + EOWNERDEAD => "Owner died", + ENOTRECOVERABLE => "State not recoverable", + ERFKILL => "Operation not possible due to RF-kill", + EHWPOISON => "Memory page has hardware error" + } +} diff --git a/src/lib.rs b/src/lib.rs new file mode 100644 index 00000000..dcfc32bc --- /dev/null +++ b/src/lib.rs @@ -0,0 +1,12 @@ +#![crate_name = "linux"] +#![feature(globs)] + +extern crate libc; + +pub use errno::{SysResult, SysError}; + +pub mod errno; +pub mod mount; +pub mod sched; +pub mod syscall; +pub mod unistd; diff --git a/src/mount.rs b/src/mount.rs new file mode 100644 index 00000000..a82f5876 --- /dev/null +++ b/src/mount.rs @@ -0,0 +1,107 @@ +use std::ptr; +use libc::{c_ulong, c_int, c_void}; +use errno::{SysResult, from_ffi}; + +bitflags!( + flags MsFlags: c_ulong { + static MS_RDONLY = 1 << 0, // Mount read-only + static MS_NOSUID = 1 << 1, // Ignore suid and sgid bits + static MS_NODEV = 1 << 2, // Disallow access to device special files + static MS_NOEXEC = 1 << 3, // Disallow program execution + static MS_SYNCHRONOUS = 1 << 4, // Writes are synced at once + static MS_REMOUNT = 1 << 5, // Alter flags of a mounted FS + static MS_MANDLOCK = 1 << 6, // Allow mandatory locks on a FS + static MS_DIRSYNC = 1 << 7, // Directory modifications are synchronous + static MS_NOATIME = 1 << 10, // Do not update access times + static MS_NODIRATIME = 1 << 11, // Do not update directory access times + static MS_BIND = 1 << 12, // Linux 2.4.0 - Bind directory at different place + static MS_MOVE = 1 << 13, + static MS_REC = 1 << 14, + static MS_VERBOSE = 1 << 15, // Deprecated + static MS_SILENT = 1 << 15, + static MS_POSIXACL = 1 << 16, + static MS_UNBINDABLE = 1 << 17, + static MS_PRIVATE = 1 << 18, + static MS_SLAVE = 1 << 19, + static MS_SHARED = 1 << 20, + static MS_RELATIME = 1 << 21, + static MS_KERNMOUNT = 1 << 22, + static MS_I_VERSION = 1 << 23, + static MS_STRICTATIME = 1 << 24, + static MS_NOSEC = 1 << 28, + static MS_BORN = 1 << 29, + static MS_ACTIVE = 1 << 30, + static MS_NOUSER = 1 << 31, + static MS_RMT_MASK = MS_RDONLY.bits + | MS_SYNCHRONOUS.bits + | MS_MANDLOCK.bits + | MS_I_VERSION.bits, + static MS_MGC_VAL = 0xC0ED0000, + static MS_MGC_MSK = 0xffff0000 + } +) + +bitflags!( + flags MntFlags: c_int { + static MNT_FORCE = 1 << 0, + static MNT_DETATCH = 1 << 1, + static MNT_EXPIRE = 1 << 2 + } +) + +mod ffi { + use libc::{c_char, c_int, c_void, c_ulong}; + + extern { + pub fn mount( + source: *const c_char, + target: *const c_char, + fstype: *const c_char, + flags: c_ulong, + data: *const c_void) -> c_int; + + pub fn umount(target: *const c_char) -> c_int; + + pub fn umount2(target: *const c_char, flags: c_int) -> c_int; + } +} + +pub fn mount<S1: ToCStr, S2: ToCStr, S3: ToCStr, S4: ToCStr>( + source: Option<S1>, + target: S2, + fstype: Option<S3>, + flags: MsFlags, + data: Option<S4>) -> SysResult<()> { + + let source = source.map(|s| s.to_c_str()); + let target = target.to_c_str(); + let fstype = fstype.map(|s| s.to_c_str()); + let data = data.map(|s| s.to_c_str()); + + let res = unsafe { + ffi::mount( + source.map(|s| s.as_ptr()).unwrap_or(ptr::null()), + target.as_ptr(), + fstype.map(|s| s.as_ptr()).unwrap_or(ptr::null()), + flags.bits, + data.map(|s| s.as_ptr() as *const c_void).unwrap_or(ptr::null())) + }; + + from_ffi(res) +} + +pub fn umount<S: ToCStr>(target: S) -> SysResult<()> { + let target = target.to_c_str(); + + let res = unsafe { ffi::umount(target.as_ptr()) }; + + from_ffi(res) +} + +pub fn umount2<S: ToCStr>(target: S, flags: MntFlags) -> SysResult<()> { + let target = target.to_c_str(); + + let res = unsafe { ffi::umount2(target.as_ptr(), flags.bits) }; + + from_ffi(res) +} diff --git a/src/sched.rs b/src/sched.rs new file mode 100644 index 00000000..91cd04f5 --- /dev/null +++ b/src/sched.rs @@ -0,0 +1,80 @@ +#![cfg(target_os = "linux")] + +use std::mem; +use libc::{c_int, c_uint, c_void}; +use super::{SysResult, SysError}; + +pub type CloneFlags = c_uint; + +pub static CLONE_VM: CloneFlags = 0x00000100; +pub static CLONE_FS: CloneFlags = 0x00000200; +pub static CLONE_FILES: CloneFlags = 0x00000400; +pub static CLONE_SIGHAND: CloneFlags = 0x00000800; +pub static CLONE_PTRACE: CloneFlags = 0x00002000; +pub static CLONE_VFORK: CloneFlags = 0x00004000; +pub static CLONE_PARENT: CloneFlags = 0x00008000; +pub static CLONE_THREAD: CloneFlags = 0x00010000; +pub static CLONE_NEWNS: CloneFlags = 0x00020000; +pub static CLONE_SYSVSEM: CloneFlags = 0x00040000; +pub static CLONE_SETTLS: CloneFlags = 0x00080000; +pub static CLONE_PARENT_SETTID: CloneFlags = 0x00100000; +pub static CLONE_CHILD_CLEARTID: CloneFlags = 0x00200000; +pub static CLONE_DETACHED: CloneFlags = 0x00400000; +pub static CLONE_UNTRACED: CloneFlags = 0x00800000; +pub static CLONE_CHILD_SETTID: CloneFlags = 0x01000000; +pub static CLONE_NEWUTS: CloneFlags = 0x04000000; +pub static CLONE_NEWIPC: CloneFlags = 0x08000000; +pub static CLONE_NEWUSER: CloneFlags = 0x10000000; +pub static CLONE_NEWPID: CloneFlags = 0x20000000; +pub static CLONE_NEWNET: CloneFlags = 0x40000000; +pub static CLONE_IO: CloneFlags = 0x80000000; + +pub type CloneCb<'a> = ||:'a -> int; + +mod ffi { + use libc::{c_void, c_int}; + + type CloneCb = extern fn (data: *const super::CloneCb) -> c_int; + + extern { + // create a child process + // doc: http://man7.org/linux/man-pages/man2/clone.2.html + pub fn clone( + cb: *const CloneCb, + child_stack: *mut c_void, + flags: super::CloneFlags, + arg: *mut super::CloneCb, + ...) -> c_int; + + // + pub fn unshare(flags: super::CloneFlags) -> c_int; + } +} + +pub fn clone(mut cb: CloneCb, stack: &mut [u8], flags: CloneFlags) -> SysResult<()> { + extern "C" fn callback(data: *mut CloneCb) -> c_int { + let cb: &mut CloneCb = unsafe { &mut *data }; + (*cb)() as c_int + } + + let res = unsafe { + let ptr = stack.as_mut_ptr().offset(stack.len() as int); + ffi::clone(mem::transmute(callback), ptr as *mut c_void, flags, &mut cb) + }; + + if res != 0 { + return Err(SysError::last()); + } + + Ok(()) +} + +pub fn unshare(flags: CloneFlags) -> SysResult<()> { + let res = unsafe { ffi::unshare(flags) }; + + if res != 0 { + return Err(SysError::last()); + } + + Ok(()) +} diff --git a/src/syscall.rs b/src/syscall.rs new file mode 100644 index 00000000..85f6e730 --- /dev/null +++ b/src/syscall.rs @@ -0,0 +1,16 @@ +use libc::c_int; + +pub use self::arch::*; + +#[cfg(target_arch = "x86_64")] +mod arch { + use libc::c_long; + + pub type Syscall = c_long; + + pub static SysPivotRoot: Syscall = 155; +} + +extern { + pub fn syscall(num: Syscall, ...) -> c_int; +} diff --git a/src/unistd.rs b/src/unistd.rs new file mode 100644 index 00000000..4a0f2abe --- /dev/null +++ b/src/unistd.rs @@ -0,0 +1,69 @@ +use std::ptr; +use std::c_str::{CString, ToCStr}; +use libc::{c_char}; +use syscall::{syscall, SysPivotRoot}; +use {SysResult, SysError}; + +mod ffi { + use libc::{c_char, c_int}; + + extern { + // change working directory + // doc: http://man7.org/linux/man-pages/man2/chdir.2.html + pub fn chdir(path: *const c_char) -> c_int; + + // execute program + // doc: http://man7.org/linux/man-pages/man2/execve.2.html + pub fn execve(filename: *const c_char, argv: *const *const c_char, envp: *const *const c_char) -> c_int; + } +} + +pub fn chdir<S: ToCStr>(path: S) -> SysResult<()> { + let path = path.to_c_str(); + let res = unsafe { ffi::chdir(path.as_ptr()) }; + + if res != 0 { + return Err(SysError::last()); + } + + return Ok(()) +} + +pub fn execve<S: ToCStr, S1: ToCStr, I1: Iterator<S1>, S2: ToCStr, I2: Iterator<S2>>( + filename: S, args: I1, env: I2) -> SysResult<()> { + + let args: Vec<CString> = args.map(|s| s.to_c_str()).collect(); + let env: Vec<CString> = env.map(|s| s.to_c_str()).collect(); + + let mut args_p: Vec<*const c_char> = args.iter().map(|s| s.as_ptr()).collect(); + args_p.push(ptr::null()); + + let mut env_p: Vec<*const c_char> = env.iter().map(|s| s.as_ptr()).collect(); + env_p.push(ptr::null()); + + let res = unsafe { + ffi::execve(filename.to_c_str().as_ptr(), args_p.as_ptr(), env_p.as_ptr()) + }; + + if res != 0 { + return Err(SysError::last()); + } + + // Should never reach here + Ok(()) +} + +pub fn pivot_root<S1: ToCStr, S2: ToCStr>(new_root: S1, put_old: S2) -> SysResult<()> { + let new_root = new_root.to_c_str(); + let put_old = put_old.to_c_str(); + + let res = unsafe { + syscall(SysPivotRoot, new_root.as_ptr(), put_old.as_ptr()) + }; + + if res == 0 { + return Err(SysError::last()); + } + + Ok(()) +} |