summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--.gitignore2
-rw-r--r--Cargo.toml5
-rw-r--r--README.md8
-rw-r--r--src/errno.rs314
-rw-r--r--src/lib.rs12
-rw-r--r--src/mount.rs107
-rw-r--r--src/sched.rs80
-rw-r--r--src/syscall.rs16
-rw-r--r--src/unistd.rs69
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(())
+}