#![allow(unused)]
use ::core::{
ffi::{c_int, c_void},
ptr::{NonNull, null_mut},
};
use ::libc::{
CMSG_DATA, CMSG_FIRSTHDR, CMSG_LEN, CMSG_NXTHDR, CMSG_SPACE, EAGAIN, EBADMSG, EINTR, EINVAL,
EMSGSIZE, ENOBUFS, ERANGE, SCM_RIGHTS, SOL_SOCKET, abort, c_uchar, calloc, close, cmsghdr, free,
iovec, malloc, memcpy, memmove, memset, msghdr, readv, recvmsg, sendmsg, writev,
};
use super::imsg::{IBUF_READ_SIZE, ibuf, ibufqueue, msgbuf, readhdr_fn};
use super::queue::{
tailq_first, tailq_foreach, tailq_init, tailq_insert_tail, tailq_next, tailq_remove,
};
use super::{freezero, recallocarray::recallocarray};
use crate::errno;
const IOV_MAX: usize = 1024;
const IBUF_FD_MARK_ON_STACK: c_int = -2;
pub unsafe fn ibuf_open(len: usize) -> *mut ibuf {
unsafe {
if len == 0 {
errno!() = EINVAL;
return null_mut();
}
let buf: *mut ibuf = calloc(1, size_of::<ibuf>()) as *mut ibuf;
if buf.is_null() {
return null_mut();
}
(*buf).buf = calloc(len, 1) as *mut c_uchar;
if (*buf).buf.is_null() {
free(buf as *mut c_void);
return null_mut();
}
(*buf).max = len;
(*buf).size = len;
(*buf).fd = -1;
buf
}
}
pub unsafe fn ibuf_dynamic(len: usize, max: usize) -> *mut ibuf {
unsafe {
if max == 0 || max < len {
errno!() = EINVAL;
return null_mut();
}
let buf: *mut ibuf = calloc(1, size_of::<ibuf>()) as *mut ibuf;
if buf.is_null() {
return null_mut();
}
if len > 0 {
(*buf).buf = calloc(len, 1) as *mut c_uchar;
if (*buf).buf.is_null() {
free(buf as *mut c_void);
return null_mut();
}
}
(*buf).size = len;
(*buf).max = max;
(*buf).fd = -1;
buf
}
}
pub unsafe fn ibuf_realloc(buf: *mut ibuf, len: usize) -> i32 {
unsafe {
if len > usize::MAX - (*buf).wpos || (*buf).wpos + len > (*buf).max {
errno!() = ERANGE;
return -1;
}
let b = recallocarray((*buf).buf as *mut c_void, (*buf).size, (*buf).wpos + len, 1);
if b.is_null() {
return -1;
}
(*buf).buf = b as *mut u8;
(*buf).size = (*buf).wpos + len;
0
}
}
pub unsafe fn ibuf_reserve(buf: *mut ibuf, len: usize) -> *mut c_void {
unsafe {
if len > usize::MAX - (*buf).wpos {
errno!() = ERANGE;
return null_mut();
}
if (*buf).fd == IBUF_FD_MARK_ON_STACK {
errno!() = EINVAL;
return null_mut();
}
if (*buf).wpos + len > (*buf).size && ibuf_realloc(buf, len) == -1 {
return null_mut();
}
let b = (*buf).buf.add((*buf).wpos);
(*buf).wpos += len;
b as *mut c_void
}
}
pub unsafe fn ibuf_add(buf: *mut ibuf, data: *const c_void, len: usize) -> i32 {
unsafe {
let b = ibuf_reserve(buf, len);
if b.is_null() {
return -1;
}
memcpy(b, data, len);
0
}
}
pub unsafe fn ibuf_add_ibuf(buf: *mut ibuf, from: *const ibuf) -> c_int {
unsafe { ibuf_add(buf, ibuf_data(from), ibuf_size(from)) }
}
pub unsafe fn ibuf_add_buf(buf: *mut ibuf, from: *const ibuf) -> c_int {
unsafe { ibuf_add_ibuf(buf, from) }
}
pub unsafe fn ibuf_add_n8(buf: *mut ibuf, value: u64) -> c_int {
unsafe {
if value > u8::MAX as u64 {
errno!() = EINVAL;
return -1;
}
let v = value;
ibuf_add(buf, &raw const v as _, size_of::<u8>())
}
}
pub unsafe fn ibuf_add_n16(buf: *mut ibuf, value: u64) -> c_int {
unsafe {
if value > u16::MAX as u64 {
errno!() = EINVAL;
return -1;
}
let v = (value as u16).to_be();
ibuf_add(buf, &raw const v as _, size_of::<u16>())
}
}
pub unsafe fn ibuf_add_n32(buf: *mut ibuf, value: u64) -> c_int {
unsafe {
if value > u32::MAX as u64 {
errno!() = EINVAL;
return -1;
}
let v = (value as u32).to_be();
ibuf_add(buf, &raw const v as _, size_of::<u32>())
}
}
pub unsafe fn ibuf_add_n64(buf: *mut ibuf, value: u64) -> c_int {
unsafe {
let v = value.to_be();
ibuf_add(buf, &raw const v as _, size_of::<u64>())
}
}
pub unsafe fn ibuf_add_h16(buf: *mut ibuf, value: u64) -> c_int {
unsafe {
if value > u16::MAX as u64 {
errno!() = EINVAL;
return -1;
}
let v = value as u16;
ibuf_add(buf, &raw const v as _, size_of::<u16>())
}
}
pub unsafe fn ibuf_add_h32(buf: *mut ibuf, value: u64) -> c_int {
unsafe {
if value > u32::MAX as u64 {
errno!() = EINVAL;
return -1;
}
let v = value as u32;
ibuf_add(buf, &raw const v as _, size_of::<u32>())
}
}
pub unsafe fn ibuf_add_h64(buf: *mut ibuf, value: u64) -> c_int {
unsafe { ibuf_add(buf, &raw const value as *const c_void, size_of::<u64>()) }
}
pub unsafe fn ibuf_add_zero(buf: *mut ibuf, len: usize) -> c_int {
unsafe {
let b: *mut c_void = ibuf_reserve(buf, len);
if b.is_null() {
return -1;
}
memset(b, 0, len);
0
}
}
pub unsafe fn ibuf_seek(buf: *mut ibuf, pos: usize, len: usize) -> *mut c_void {
unsafe {
if ibuf_size(buf) < pos || usize::MAX - pos < len || ibuf_size(buf) < pos + len {
errno!() = ERANGE;
return null_mut();
}
(*buf).buf.add((*buf).rpos + pos) as _
}
}
pub unsafe fn ibuf_set(buf: *mut ibuf, pos: usize, data: *const c_void, len: usize) -> c_int {
unsafe {
let b = ibuf_seek(buf, pos, len);
if b.is_null() {
return -1;
}
memcpy(b, data, len);
0
}
}
pub unsafe fn ibuf_set_n8(buf: *mut ibuf, pos: usize, value: u64) -> c_int {
unsafe {
if value > u8::MAX as u64 {
errno!() = EINVAL;
return -1;
}
let v = value as u8;
ibuf_set(buf, pos, &raw const v as *const c_void, size_of::<u8>())
}
}
pub unsafe fn ibuf_set_n16(buf: *mut ibuf, pos: usize, value: u64) -> c_int {
unsafe {
if value > u16::MAX as u64 {
errno!() = EINVAL;
return -1;
}
let v = u16::to_be(value as u16);
ibuf_set(buf, pos, &raw const v as *const c_void, size_of::<u16>())
}
}
pub unsafe fn ibuf_set_n32(buf: *mut ibuf, pos: usize, value: u64) -> c_int {
unsafe {
if value > u32::MAX as u64 {
errno!() = EINVAL;
return -1;
}
let v = u32::to_be(value as u32);
ibuf_set(buf, pos, &raw const v as *const c_void, size_of::<u32>())
}
}
pub unsafe fn ibuf_set_n64(buf: *mut ibuf, pos: usize, value: u64) -> c_int {
unsafe {
let v = u64::to_be(value);
ibuf_set(buf, pos, &raw const v as *const c_void, size_of::<u64>())
}
}
pub unsafe fn ibuf_set_h16(buf: *mut ibuf, pos: usize, value: u64) -> c_int {
unsafe {
if value > u16::MAX as u64 {
errno!() = EINVAL;
return -1;
}
let v = value as u16;
ibuf_set(buf, pos, &raw const v as *const c_void, size_of::<u16>())
}
}
pub unsafe fn ibuf_set_h32(buf: *mut ibuf, pos: usize, value: u64) -> c_int {
unsafe {
if value > u32::MAX as u64 {
errno!() = EINVAL;
return -1;
}
let v = value as u32;
ibuf_set(buf, pos, &raw const v as *const c_void, size_of::<u32>())
}
}
pub unsafe fn ibuf_set_h64(buf: *mut ibuf, pos: usize, value: u64) -> c_int {
unsafe {
ibuf_set(
buf,
pos,
&raw const value as *const c_void,
size_of::<u64>(),
)
}
}
pub unsafe fn ibuf_data(buf: *const ibuf) -> *mut c_void {
unsafe { (*buf).buf.add((*buf).rpos) as *mut c_void }
}
pub unsafe fn ibuf_size(buf: *const ibuf) -> usize {
unsafe { (*buf).wpos - (*buf).rpos }
}
pub unsafe fn ibuf_left(buf: *const ibuf) -> usize {
unsafe {
if (*buf).fd == IBUF_FD_MARK_ON_STACK {
return 0;
}
(*buf).max - (*buf).wpos
}
}
pub unsafe fn ibuf_truncate(buf: *mut ibuf, len: usize) -> c_int {
unsafe {
if ibuf_size(buf) >= len {
(*buf).wpos = (*buf).rpos + len;
return 0;
}
if (*buf).fd == IBUF_FD_MARK_ON_STACK {
errno!() = ERANGE;
return -1;
}
ibuf_add_zero(buf, len - ibuf_size(buf))
}
}
pub unsafe fn ibuf_rewind(buf: *mut ibuf) {
unsafe {
(*buf).rpos = 0;
}
}
pub unsafe fn ibuf_close(msgbuf: *mut msgbuf, buf: *mut ibuf) {
unsafe {
ibufq_push(&raw mut (*msgbuf).bufs, buf);
}
}
pub unsafe fn ibuf_from_buffer(buf: *mut ibuf, data: *mut c_void, len: usize) {
unsafe {
memset(buf as _, 0, size_of::<ibuf>());
(*buf).buf = data as _;
(*buf).wpos = len;
(*buf).size = len;
(*buf).fd = IBUF_FD_MARK_ON_STACK;
}
}
pub unsafe fn ibuf_from_ibuf(buf: *mut ibuf, from: *const ibuf) {
unsafe {
ibuf_from_buffer(buf, ibuf_data(from), ibuf_size(from));
}
}
pub unsafe fn ibuf_get(buf: *mut ibuf, data: *mut c_void, len: usize) -> c_int {
unsafe {
if ibuf_size(buf) < len {
errno!() = EBADMSG;
return -1;
}
memcpy(data, ibuf_data(buf), len);
(*buf).rpos += len;
0
}
}
pub unsafe fn ibuf_get_ibuf(buf: *mut ibuf, len: usize, new: *mut ibuf) -> c_int {
unsafe {
if ibuf_size(buf) < len {
errno!() = EBADMSG;
return -1;
}
ibuf_from_buffer(new, ibuf_data(buf), len);
(*buf).rpos += len;
0
}
}
pub unsafe fn ibuf_get_n8(buf: *mut ibuf, value: *mut u8) -> c_int {
unsafe { ibuf_get(buf, value as _, size_of::<u8>()) }
}
pub unsafe fn ibuf_get_n16(buf: *mut ibuf, value: *mut u16) -> c_int {
unsafe {
let rv = ibuf_get(buf, value as _, size_of::<u16>());
*value = u16::from_be(*value);
rv
}
}
pub unsafe fn ibuf_get_n32(buf: *mut ibuf, value: *mut u32) -> c_int {
unsafe {
let rv = ibuf_get(buf, value as _, size_of::<u32>());
*value = u32::from_be(*value);
rv
}
}
pub unsafe fn ibuf_get_n64(buf: *mut ibuf, value: *mut u64) -> c_int {
unsafe {
let rv = ibuf_get(buf, value as _, size_of::<u64>());
*value = u64::from_be(*value);
rv
}
}
pub unsafe fn ibuf_get_h16(buf: *mut ibuf, value: *mut u16) -> c_int {
unsafe { ibuf_get(buf, value as _, size_of::<u16>()) }
}
pub unsafe fn ibuf_get_h32(buf: *mut ibuf, value: *mut u32) -> c_int {
unsafe { ibuf_get(buf, value as _, size_of::<u32>()) }
}
pub unsafe fn ibuf_get_h64(buf: *mut ibuf, value: *mut u64) -> c_int {
unsafe { ibuf_get(buf, value as _, size_of::<u64>()) }
}
pub unsafe fn ibuf_skip(buf: *mut ibuf, len: usize) -> c_int {
unsafe {
if ibuf_size(buf) < len {
errno!() = EBADMSG;
return -1;
}
(*buf).rpos += len;
0
}
}
pub unsafe fn ibuf_set_maxsize(buf: *mut ibuf, max: usize) -> c_int {
unsafe {
if (*buf).fd == IBUF_FD_MARK_ON_STACK {
errno!() = EINVAL;
return -1;
}
if max > (*buf).max {
errno!() = ERANGE;
return -1;
}
(*buf).max = max;
0
}
}
pub unsafe fn ibuf_free(buf: *mut ibuf) {
unsafe {
let save_errno = errno!();
if buf.is_null() {
return;
}
if (*buf).fd == IBUF_FD_MARK_ON_STACK {
abort();
}
if (*buf).fd >= 0 {
close((*buf).fd);
}
freezero((*buf).buf.cast(), (*buf).size);
free(buf as *mut c_void);
errno!() = save_errno;
}
}
pub unsafe fn ibuf_fd_avail(buf: *mut ibuf) -> c_int {
unsafe { ((*buf).fd >= 0) as c_int }
}
pub unsafe fn ibuf_fd_get(buf: *mut ibuf) -> c_int {
unsafe {
if (*buf).fd < 0 {
return -1;
}
let fd = (*buf).fd;
(*buf).fd = -1;
fd
}
}
pub unsafe fn ibuf_fd_set(buf: *mut ibuf, fd: c_int) {
unsafe {
if (*buf).fd == IBUF_FD_MARK_ON_STACK {
abort();
}
if (*buf).fd >= 0 {
close((*buf).fd);
}
(*buf).fd = -1;
if fd >= 0 {
(*buf).fd = fd;
}
}
}
pub unsafe fn msgbuf_new() -> *mut msgbuf {
unsafe {
let m = calloc(1, size_of::<msgbuf>()) as *mut msgbuf;
if m.is_null() {
return null_mut();
}
ibufq_init(&raw mut (*m).bufs);
ibufq_init(&raw mut (*m).rbufs);
m
}
}
pub unsafe fn msgbuf_new_reader(
hdrsz: usize,
readhdr: readhdr_fn,
arg: *mut c_void,
) -> *mut msgbuf {
unsafe {
if hdrsz == 0 || hdrsz > IBUF_READ_SIZE / 2 {
errno!() = EINVAL;
return null_mut();
}
let buf = malloc(IBUF_READ_SIZE) as *mut c_uchar;
if buf.is_null() {
return null_mut();
}
let msgbuf = msgbuf_new();
if msgbuf.is_null() {
free(buf as *mut c_void);
return null_mut();
}
(*msgbuf).rbuf = buf;
(*msgbuf).hdrsize = hdrsz;
(*msgbuf).readhdr = readhdr;
(*msgbuf).rarg = arg;
msgbuf
}
}
pub unsafe fn msgbuf_free(msgbuf: *mut msgbuf) {
unsafe {
if msgbuf.is_null() {
return;
}
msgbuf_clear(msgbuf);
free((*msgbuf).rbuf as *mut c_void);
free(msgbuf as *mut c_void);
}
}
pub unsafe fn msgbuf_queuelen(msgbuf: *mut msgbuf) -> u32 {
unsafe { ibufq_queuelen(&raw mut (*msgbuf).bufs) }
}
pub unsafe fn msgbuf_clear(msgbuf: *mut msgbuf) {
unsafe {
ibufq_flush(&raw mut (*msgbuf).bufs);
ibufq_flush(&raw mut (*msgbuf).rbufs);
(*msgbuf).roff = 0;
ibuf_free((*msgbuf).rpmsg);
(*msgbuf).rpmsg = null_mut();
}
}
pub unsafe fn msgbuf_get(msgbuf: *mut msgbuf) -> *mut ibuf {
unsafe { ibufq_pop(&raw mut (*msgbuf).rbufs) }
}
pub unsafe fn msgbuf_concat(msgbuf: *mut msgbuf, from: *mut ibufqueue) {
unsafe { ibufq_concat(&raw mut (*msgbuf).bufs, from) }
}
pub unsafe fn ibuf_write(fd: c_int, msgbuf: *mut msgbuf) -> c_int {
unsafe {
let mut iov: [iovec; IOV_MAX] = std::mem::zeroed();
let mut i: u32 = 0;
for buf in tailq_foreach(&raw mut (*msgbuf).bufs.bufs).map(NonNull::as_ptr) {
if i as usize >= IOV_MAX {
break;
}
iov[i as usize].iov_base = ibuf_data(buf);
iov[i as usize].iov_len = ibuf_size(buf);
i += 1;
}
if i == 0 {
return 0; }
let mut n: isize;
'again: loop {
n = writev(fd, iov.as_ptr(), i as i32);
if n == -1 {
if errno!() == EINTR {
continue 'again;
}
if errno!() == EAGAIN || errno!() == ENOBUFS {
return 0;
}
return -1;
}
break 'again;
}
msgbuf_drain(msgbuf, n as usize);
0
}
}
pub unsafe fn msgbuf_write(fd: c_int, msgbuf: *mut msgbuf) -> c_int {
unsafe {
let mut iov: [iovec; IOV_MAX] = std::mem::zeroed();
let mut buf0: *mut ibuf = null_mut();
let mut i: u32 = 0;
let mut msg: msghdr = std::mem::zeroed();
let mut cmsgbuf: cmsgbuf = std::mem::zeroed();
union cmsgbuf {
_hdr: cmsghdr,
buf: [u8; unsafe { CMSG_SPACE(size_of::<c_int>() as _) as usize }],
}
for buf in tailq_foreach(&raw mut (*msgbuf).bufs.bufs).map(NonNull::as_ptr) {
if i as usize >= IOV_MAX {
break;
}
if i > 0 && (*buf).fd != -1 {
break;
}
iov[i as usize].iov_base = ibuf_data(buf);
iov[i as usize].iov_len = ibuf_size(buf);
i += 1;
if (*buf).fd != -1 {
buf0 = buf;
}
}
if i == 0 {
return 0; }
msg.msg_iov = iov.as_mut_ptr();
msg.msg_iovlen = i.try_into().unwrap();
if !buf0.is_null() {
msg.msg_control = &raw mut cmsgbuf.buf as _;
msg.msg_controllen = size_of_val(&cmsgbuf.buf) as _;
let cmsg = CMSG_FIRSTHDR(&raw const msg);
(*cmsg).cmsg_len = CMSG_LEN(size_of::<c_int>() as u32) as _;
(*cmsg).cmsg_level = SOL_SOCKET;
(*cmsg).cmsg_type = SCM_RIGHTS;
*CMSG_DATA(cmsg).cast() = (*buf0).fd;
}
let mut n: isize;
'again: loop {
n = sendmsg(fd, &raw const msg, 0);
if n == -1 {
if errno!() == EINTR {
continue 'again;
}
if errno!() == EAGAIN || errno!() == ENOBUFS {
return 0;
}
return -1;
}
break 'again;
}
if !buf0.is_null() {
close((*buf0).fd);
(*buf0).fd = -1;
}
msgbuf_drain(msgbuf, n as usize);
0
}
}
unsafe fn ibuf_read_process(msgbuf: *mut msgbuf, mut fd: c_int) -> c_int {
unsafe {
let mut rbuf: ibuf = std::mem::zeroed();
let mut msg: ibuf = std::mem::zeroed();
ibuf_from_buffer(&raw mut rbuf, (*msgbuf).rbuf.cast(), (*msgbuf).roff);
let ok = 'process: {
loop {
if (*msgbuf).rpmsg.is_null() {
if ibuf_size(&raw const rbuf) < (*msgbuf).hdrsize {
break; }
ibuf_from_buffer(&raw mut msg, ibuf_data(&raw const rbuf), (*msgbuf).hdrsize);
(*msgbuf).rpmsg =
((*msgbuf).readhdr.unwrap())(&raw mut msg, (*msgbuf).rarg, &raw mut fd);
if (*msgbuf).rpmsg.is_null() {
break 'process false; }
}
let sz = if ibuf_left((*msgbuf).rpmsg) <= ibuf_size(&raw const rbuf) {
ibuf_left((*msgbuf).rpmsg)
} else {
ibuf_size(&raw const rbuf)
};
if ibuf_get_ibuf(&raw mut rbuf, sz, &raw mut msg) == -1
|| ibuf_add_ibuf((*msgbuf).rpmsg, &raw const msg) == -1
{
break 'process false; }
if ibuf_left((*msgbuf).rpmsg) == 0 {
ibufq_push(&raw mut (*msgbuf).rbufs, (*msgbuf).rpmsg);
(*msgbuf).rpmsg = null_mut();
}
if ibuf_size(&raw const rbuf) == 0 {
break; }
}
true
};
if ok {
if ibuf_size(&raw const rbuf) > 0 {
memmove(
(*msgbuf).rbuf.cast(),
ibuf_data(&raw const rbuf),
ibuf_size(&raw const rbuf),
);
}
(*msgbuf).roff = ibuf_size(&raw const rbuf);
}
if fd != -1 {
close(fd);
}
if ok { 1 } else { -1 }
}
}
pub unsafe fn ibuf_read(fd: c_int, msgbuf: *mut msgbuf) -> c_int {
unsafe {
if (*msgbuf).rbuf.is_null() {
errno!() = EINVAL;
return -1;
}
let mut iov: iovec = std::mem::zeroed();
iov.iov_base = (*msgbuf).rbuf.add((*msgbuf).roff).cast();
iov.iov_len = IBUF_READ_SIZE - (*msgbuf).roff;
let mut n: isize;
'again: loop {
n = readv(fd, &raw const iov, 1);
if n == -1 {
if errno!() == EINTR {
continue 'again;
}
if errno!() == EAGAIN {
return 1; }
return -1;
}
break 'again;
}
if n == 0 {
return 0; }
(*msgbuf).roff += n as usize;
ibuf_read_process(msgbuf, -1)
}
}
pub unsafe fn msgbuf_read(fd: c_int, msgbuf: *mut msgbuf) -> c_int {
unsafe {
let mut msg: msghdr = std::mem::zeroed();
let mut cmsgbuf: cmsgbuf = std::mem::zeroed();
union cmsgbuf {
_hdr: cmsghdr,
buf: [u8; unsafe { CMSG_SPACE(size_of::<c_int>() as _) as usize }],
}
let mut iov: iovec = std::mem::zeroed();
let mut fdpass: c_int = -1;
if (*msgbuf).rbuf.is_null() {
errno!() = EINVAL;
return -1;
}
iov.iov_base = (*msgbuf).rbuf.add((*msgbuf).roff).cast();
iov.iov_len = IBUF_READ_SIZE - (*msgbuf).roff;
msg.msg_iov = &raw mut iov;
msg.msg_iovlen = 1;
msg.msg_control = &raw mut cmsgbuf.buf as _;
msg.msg_controllen = size_of_val(&cmsgbuf.buf) as _;
let mut n: isize;
'again: loop {
n = recvmsg(fd, &raw mut msg, 0);
if n == -1 {
if errno!() == EINTR {
continue 'again;
}
if errno!() == EMSGSIZE {
continue 'again;
}
if errno!() == EAGAIN {
return 1; }
return -1;
}
break 'again;
}
if n == 0 {
return 0; }
(*msgbuf).roff += n as usize;
let mut cmsg: *mut cmsghdr = CMSG_FIRSTHDR(&raw const msg);
while !cmsg.is_null() {
if (*cmsg).cmsg_level == SOL_SOCKET && (*cmsg).cmsg_type == SCM_RIGHTS {
let j = ((cmsg as *mut u8).add((*cmsg).cmsg_len as usize).addr()
- CMSG_DATA(cmsg).addr())
/ size_of::<c_int>();
for k in 0..j {
let f = *(CMSG_DATA(cmsg) as *mut c_int).add(k);
if k == 0 {
fdpass = f;
} else {
close(f);
}
}
}
cmsg = CMSG_NXTHDR(&raw const msg, cmsg);
}
ibuf_read_process(msgbuf, fdpass)
}
}
unsafe fn msgbuf_drain(msgbuf: *mut msgbuf, mut n: usize) {
unsafe {
let mut buf;
while {
buf = tailq_first(&raw mut (*msgbuf).bufs.bufs);
!buf.is_null()
} {
if n >= ibuf_size(buf) {
n -= ibuf_size(buf);
tailq_remove(&raw mut (*msgbuf).bufs.bufs, buf);
(*msgbuf).bufs.queued -= 1;
ibuf_free(buf);
} else {
(*buf).rpos += n;
return;
}
}
}
}
unsafe fn ibufq_init(bufq: *mut ibufqueue) {
unsafe {
tailq_init(&raw mut (*bufq).bufs);
(*bufq).queued = 0;
}
}
pub unsafe fn ibufq_new() -> *mut ibufqueue {
unsafe {
let bufq = calloc(1, size_of::<ibufqueue>()) as *mut ibufqueue;
if bufq.is_null() {
return null_mut();
}
ibufq_init(bufq);
bufq
}
}
pub unsafe fn ibufq_free(bufq: *mut ibufqueue) {
unsafe {
if bufq.is_null() {
return;
}
ibufq_flush(bufq);
free(bufq as *mut c_void);
}
}
pub unsafe fn ibufq_pop(bufq: *mut ibufqueue) -> *mut ibuf {
unsafe {
let buf = tailq_first(&raw mut (*bufq).bufs);
if buf.is_null() {
return null_mut();
}
tailq_remove(&raw mut (*bufq).bufs, buf);
(*bufq).queued -= 1;
buf
}
}
pub unsafe fn ibufq_push(bufq: *mut ibufqueue, buf: *mut ibuf) {
unsafe {
if (*buf).fd == IBUF_FD_MARK_ON_STACK {
abort();
}
tailq_insert_tail::<_, _>(&raw mut (*bufq).bufs, buf);
(*bufq).queued += 1;
}
}
pub unsafe fn ibufq_queuelen(bufq: *mut ibufqueue) -> u32 {
unsafe { (*bufq).queued }
}
pub unsafe fn ibufq_concat(to: *mut ibufqueue, from: *mut ibufqueue) {
unsafe {
(*to).queued += (*from).queued;
let mut buf;
while {
buf = tailq_first(&raw mut (*from).bufs);
!buf.is_null()
} {
tailq_remove(&raw mut (*from).bufs, buf);
tailq_insert_tail::<_, _>(&raw mut (*to).bufs, buf);
}
(*from).queued = 0;
}
}
pub unsafe fn ibufq_flush(bufq: *mut ibufqueue) {
unsafe {
let mut buf;
while {
buf = tailq_first(&raw mut (*bufq).bufs);
!buf.is_null()
} {
tailq_remove(&raw mut (*bufq).bufs, buf);
ibuf_free(buf);
}
(*bufq).queued = 0;
}
}
#[cfg(test)]
mod tests {
use super::*;
unsafe fn readable(buf: *const ibuf) -> Vec<u8> {
unsafe {
let p = ibuf_data(buf) as *const u8;
let n = ibuf_size(buf);
std::slice::from_raw_parts(p, n).to_vec()
}
}
#[test]
fn test_ibufq_push_pop_fifo() {
unsafe {
let mut q: ibufqueue = std::mem::zeroed();
ibufq_init(&raw mut q);
assert_eq!(ibufq_queuelen(&raw mut q), 0);
let a = ibuf_dynamic(4, 64);
let b = ibuf_dynamic(4, 64);
ibufq_push(&raw mut q, a);
ibufq_push(&raw mut q, b);
assert_eq!(ibufq_queuelen(&raw mut q), 2);
assert_eq!(ibufq_pop(&raw mut q), a);
assert_eq!(ibufq_pop(&raw mut q), b);
assert_eq!(ibufq_queuelen(&raw mut q), 0);
assert!(ibufq_pop(&raw mut q).is_null());
ibuf_free(a);
ibuf_free(b);
}
}
#[test]
fn test_ibuf_open_basic_and_zero() {
unsafe {
let buf = ibuf_open(16);
assert!(!buf.is_null());
assert_eq!(ibuf_size(buf), 0);
assert_eq!((*buf).max, 16);
assert_eq!((*buf).size, 16);
assert_eq!((*buf).wpos, 0);
assert_eq!((*buf).rpos, 0);
assert_eq!((*buf).fd, -1);
assert_eq!(ibuf_left(buf), 16);
ibuf_free(buf);
errno!() = 0;
let z = ibuf_open(0);
assert!(z.is_null());
assert_eq!(errno!(), EINVAL);
}
}
#[test]
fn test_ibuf_dynamic_errors_and_alloc() {
unsafe {
errno!() = 0;
let a = ibuf_dynamic(0, 0);
assert!(a.is_null());
assert_eq!(errno!(), EINVAL);
errno!() = 0;
let b = ibuf_dynamic(8, 4);
assert!(b.is_null());
assert_eq!(errno!(), EINVAL);
let empty = ibuf_dynamic(0, 16);
assert!(!empty.is_null());
assert_eq!((*empty).size, 0);
assert_eq!((*empty).max, 16);
ibuf_free(empty);
let buf = ibuf_dynamic(4, 16);
assert!(!buf.is_null());
assert_eq!(ibuf_size(buf), 0);
assert_eq!((*buf).size, 4);
assert_eq!((*buf).max, 16);
assert_eq!((*buf).fd, -1);
let src = [0xAAu8, 0xBB, 0xCC, 0xDD];
assert_eq!(ibuf_add(buf, src.as_ptr() as *const c_void, 4), 0);
assert_eq!(ibuf_size(buf), 4);
assert_eq!(readable(buf), src);
ibuf_free(buf);
}
}
#[test]
fn test_ibuf_add_bytes_and_data() {
unsafe {
let buf = ibuf_open(16);
let first = [1u8, 2, 3, 4];
let second = [5u8, 6];
assert_eq!(ibuf_add(buf, first.as_ptr() as *const c_void, 4), 0);
assert_eq!(ibuf_size(buf), 4);
assert_eq!(ibuf_add(buf, second.as_ptr() as *const c_void, 2), 0);
assert_eq!(ibuf_size(buf), 6);
assert_eq!(readable(buf), [1, 2, 3, 4, 5, 6]);
assert_eq!((*buf).wpos, 6);
assert_eq!(ibuf_add(buf, first.as_ptr() as *const c_void, 0), 0);
assert_eq!(ibuf_size(buf), 6);
ibuf_free(buf);
}
}
#[test]
fn test_ibuf_reserve_and_backfill() {
unsafe {
let buf = ibuf_open(8);
let hdr = ibuf_reserve(buf, 4);
assert!(!hdr.is_null());
assert_eq!(ibuf_size(buf), 4);
assert_eq!((*buf).wpos, 4);
let body = [0x41u8, 0x42];
assert_eq!(ibuf_add(buf, body.as_ptr() as *const c_void, 2), 0);
assert_eq!(ibuf_size(buf), 6);
std::ptr::copy_nonoverlapping([0xDEu8, 0xAD, 0xBE, 0xEF].as_ptr(), hdr as *mut u8, 4);
assert_eq!(readable(buf), [0xDE, 0xAD, 0xBE, 0xEF, 0x41, 0x42]);
ibuf_free(buf);
}
}
#[test]
fn test_ibuf_reserve_over_max() {
unsafe {
let buf = ibuf_open(4);
errno!() = 0;
let p = ibuf_reserve(buf, 8);
assert!(p.is_null());
assert_eq!(errno!(), ERANGE);
assert_eq!((*buf).wpos, 0);
ibuf_free(buf);
}
}
#[test]
fn test_ibuf_add_get_n_and_h_round_trip() {
unsafe {
let buf = ibuf_open(64);
assert_eq!(ibuf_add_n8(buf, 0x12), 0);
assert_eq!(ibuf_add_n16(buf, 0x1234), 0);
assert_eq!(ibuf_add_n32(buf, 0x1234_5678), 0);
assert_eq!(ibuf_add_n64(buf, 0x1234_5678_9abc_def0), 0);
assert_eq!(ibuf_add_h16(buf, 0xBEEF), 0);
assert_eq!(ibuf_add_h32(buf, 0xDEAD_BEEF), 0);
assert_eq!(ibuf_add_h64(buf, 0x0011_2233_4455_6677), 0);
assert_eq!(ibuf_size(buf), 1 + 2 + 4 + 8 + 2 + 4 + 8);
let mut v8: u8 = 0;
let mut v16: u16 = 0;
let mut v32: u32 = 0;
let mut v64: u64 = 0;
assert_eq!(ibuf_get_n8(buf, &mut v8), 0);
assert_eq!(v8, 0x12);
assert_eq!(ibuf_get_n16(buf, &mut v16), 0);
assert_eq!(v16, 0x1234);
assert_eq!(ibuf_get_n32(buf, &mut v32), 0);
assert_eq!(v32, 0x1234_5678);
assert_eq!(ibuf_get_n64(buf, &mut v64), 0);
assert_eq!(v64, 0x1234_5678_9abc_def0);
assert_eq!(ibuf_get_h16(buf, &mut v16), 0);
assert_eq!(v16, 0xBEEF);
assert_eq!(ibuf_get_h32(buf, &mut v32), 0);
assert_eq!(v32, 0xDEAD_BEEF);
assert_eq!(ibuf_get_h64(buf, &mut v64), 0);
assert_eq!(v64, 0x0011_2233_4455_6677);
assert_eq!(ibuf_size(buf), 0);
ibuf_free(buf);
}
}
#[test]
fn test_ibuf_add_n16_is_big_endian() {
unsafe {
let buf = ibuf_open(2);
assert_eq!(ibuf_add_n16(buf, 0x0102), 0);
assert_eq!(readable(buf), [0x01, 0x02]);
ibuf_free(buf);
}
}
#[test]
fn test_ibuf_add_n_overflow() {
unsafe {
let buf = ibuf_open(16);
errno!() = 0;
assert_eq!(ibuf_add_n8(buf, 0x100), -1);
assert_eq!(errno!(), EINVAL);
errno!() = 0;
assert_eq!(ibuf_add_n16(buf, 0x1_0000), -1);
assert_eq!(errno!(), EINVAL);
errno!() = 0;
assert_eq!(ibuf_add_n32(buf, 0x1_0000_0000), -1);
assert_eq!(errno!(), EINVAL);
assert_eq!(ibuf_size(buf), 0);
ibuf_free(buf);
}
}
#[test]
fn test_ibuf_add_zero() {
unsafe {
let buf = ibuf_open(8);
let pre = [0xFFu8, 0xFF];
assert_eq!(ibuf_add(buf, pre.as_ptr() as *const c_void, 2), 0);
assert_eq!(ibuf_add_zero(buf, 3), 0);
assert_eq!(ibuf_size(buf), 5);
assert_eq!(readable(buf), [0xFF, 0xFF, 0x00, 0x00, 0x00]);
ibuf_free(buf);
}
}
#[test]
fn test_ibuf_seek_and_set() {
unsafe {
let buf = ibuf_open(16);
let src = [0u8, 1, 2, 3, 4, 5, 6, 7];
assert_eq!(ibuf_add(buf, src.as_ptr() as *const c_void, 8), 0);
let p = ibuf_seek(buf, 2, 4);
assert!(!p.is_null());
let seen = std::slice::from_raw_parts(p as *const u8, 4);
assert_eq!(seen, [2, 3, 4, 5]);
let patch = [0xAAu8, 0xBB];
assert_eq!(ibuf_set(buf, 4, patch.as_ptr() as *const c_void, 2), 0);
assert_eq!(ibuf_size(buf), 8);
assert_eq!(readable(buf), [0, 1, 2, 3, 0xAA, 0xBB, 6, 7]);
errno!() = 0;
assert!(ibuf_seek(buf, 6, 4).is_null());
assert_eq!(errno!(), ERANGE);
errno!() = 0;
assert!(ibuf_seek(buf, 9, 0).is_null());
assert_eq!(errno!(), ERANGE);
errno!() = 0;
assert_eq!(ibuf_set(buf, 7, patch.as_ptr() as *const c_void, 2), -1);
assert_eq!(errno!(), ERANGE);
ibuf_free(buf);
}
}
#[test]
fn test_ibuf_set_n32_backfill() {
unsafe {
let buf = ibuf_open(8);
assert_eq!(ibuf_add_zero(buf, 4), 0);
let body = [0xDEu8, 0xAD, 0xBE, 0xEF];
assert_eq!(ibuf_add(buf, body.as_ptr() as *const c_void, 4), 0);
assert_eq!(ibuf_set_n32(buf, 0, 4), 0);
assert_eq!(readable(buf), [0x00, 0x00, 0x00, 0x04, 0xDE, 0xAD, 0xBE, 0xEF]);
let mut len: u32 = 0;
assert_eq!(ibuf_get_n32(buf, &mut len), 0);
assert_eq!(len, 4);
ibuf_free(buf);
}
}
#[test]
fn test_ibuf_set_n8_overflow() {
unsafe {
let buf = ibuf_open(4);
assert_eq!(ibuf_add_zero(buf, 1), 0);
errno!() = 0;
assert_eq!(ibuf_set_n8(buf, 0, 0x100), -1);
assert_eq!(errno!(), EINVAL);
ibuf_free(buf);
}
}
#[test]
fn test_ibuf_data_moves_with_rpos() {
unsafe {
let buf = ibuf_open(8);
let src = [10u8, 20, 30, 40];
assert_eq!(ibuf_add(buf, src.as_ptr() as *const c_void, 4), 0);
let base = ibuf_data(buf) as usize;
let mut two = [0u8; 2];
assert_eq!(ibuf_get(buf, two.as_mut_ptr() as *mut c_void, 2), 0);
assert_eq!(two, [10, 20]);
assert_eq!(ibuf_size(buf), 2);
assert_eq!(ibuf_data(buf) as usize, base + 2);
assert_eq!(readable(buf), [30, 40]);
errno!() = 0;
let mut big = [0u8; 8];
assert_eq!(ibuf_get(buf, big.as_mut_ptr() as *mut c_void, 8), -1);
assert_eq!(errno!(), EBADMSG);
assert_eq!(ibuf_size(buf), 2);
ibuf_free(buf);
}
}
#[test]
fn test_ibuf_truncate_down_then_grow_zero() {
unsafe {
let buf = ibuf_open(16);
let src = [0u8, 1, 2, 3, 4, 5, 6, 7];
assert_eq!(ibuf_add(buf, src.as_ptr() as *const c_void, 8), 0);
assert_eq!(ibuf_truncate(buf, 4), 0);
assert_eq!(ibuf_size(buf), 4);
assert_eq!(readable(buf), [0, 1, 2, 3]);
assert_eq!(ibuf_truncate(buf, 6), 0);
assert_eq!(ibuf_size(buf), 6);
assert_eq!(readable(buf), [0, 1, 2, 3, 0, 0]);
ibuf_free(buf);
}
}
#[test]
fn test_ibuf_truncate_stack_cannot_grow() {
unsafe {
let mut backing = [9u8, 8, 7, 6, 5];
let mut sb: ibuf = std::mem::zeroed();
ibuf_from_buffer(&raw mut sb, backing.as_mut_ptr() as *mut c_void, 5);
assert_eq!(sb.max, 0); assert_eq!(ibuf_size(&raw const sb), 5);
assert_eq!(ibuf_truncate(&raw mut sb, 3), 0);
assert_eq!(ibuf_size(&raw const sb), 3);
errno!() = 0;
assert_eq!(ibuf_truncate(&raw mut sb, 10), -1);
assert_eq!(errno!(), ERANGE);
}
}
#[test]
fn test_ibuf_skip_and_underflow() {
unsafe {
let buf = ibuf_open(16);
let src = [10u8, 20, 30, 40, 50, 60];
assert_eq!(ibuf_add(buf, src.as_ptr() as *const c_void, 6), 0);
assert_eq!(ibuf_skip(buf, 2), 0);
assert_eq!(ibuf_size(buf), 4);
assert_eq!(readable(buf), [30, 40, 50, 60]);
errno!() = 0;
assert_eq!(ibuf_skip(buf, 5), -1);
assert_eq!(errno!(), EBADMSG);
assert_eq!(ibuf_size(buf), 4);
ibuf_free(buf);
}
}
#[test]
fn test_ibuf_get_ibuf_view_and_error() {
unsafe {
let buf = ibuf_open(16);
let src = [1u8, 2, 3, 4, 5, 6];
assert_eq!(ibuf_add(buf, src.as_ptr() as *const c_void, 6), 0);
let mut view: ibuf = std::mem::zeroed();
assert_eq!(ibuf_get_ibuf(buf, 4, &raw mut view), 0);
assert_eq!(ibuf_size(&raw const view), 4);
assert_eq!(readable(&raw const view), [1, 2, 3, 4]);
assert_eq!(ibuf_size(buf), 2);
assert_eq!(readable(buf), [5, 6]);
errno!() = 0;
let mut v2: ibuf = std::mem::zeroed();
assert_eq!(ibuf_get_ibuf(buf, 3, &raw mut v2), -1);
assert_eq!(errno!(), EBADMSG);
ibuf_free(buf);
}
}
#[test]
fn test_ibuf_add_ibuf_concatenates() {
unsafe {
let dst = ibuf_open(16);
let src = ibuf_open(8);
assert_eq!(ibuf_add(dst, [0xAAu8, 0xBB].as_ptr() as *const c_void, 2), 0);
assert_eq!(ibuf_add(src, [0x01u8, 0x02, 0x03].as_ptr() as *const c_void, 3), 0);
assert_eq!(ibuf_add_ibuf(dst, src), 0);
assert_eq!(readable(dst), [0xAA, 0xBB, 0x01, 0x02, 0x03]);
assert_eq!(ibuf_add_buf(dst, src), 0);
assert_eq!(ibuf_size(dst), 8);
assert_eq!(readable(dst), [0xAA, 0xBB, 0x01, 0x02, 0x03, 0x01, 0x02, 0x03]);
ibuf_free(src);
ibuf_free(dst);
}
}
#[test]
fn test_ibuf_left_and_rewind() {
unsafe {
let buf = ibuf_open(16);
let src = [1u8, 2, 3, 4, 5];
assert_eq!(ibuf_add(buf, src.as_ptr() as *const c_void, 5), 0);
assert_eq!(ibuf_left(buf), 11);
let mut two = [0u8; 2];
assert_eq!(ibuf_get(buf, two.as_mut_ptr() as *mut c_void, 2), 0);
assert_eq!(ibuf_size(buf), 3);
ibuf_rewind(buf);
assert_eq!(ibuf_size(buf), 5);
assert_eq!(readable(buf), [1, 2, 3, 4, 5]);
assert_eq!(ibuf_left(buf), 11);
ibuf_free(buf);
}
}
#[test]
fn test_ibuf_fd_set_get_avail() {
unsafe {
let buf = ibuf_dynamic(4, 4);
assert!(!buf.is_null());
assert_eq!(ibuf_fd_avail(buf), 0);
let fd = libc::dup(2);
assert!(fd >= 0);
ibuf_fd_set(buf, fd);
assert_eq!(ibuf_fd_avail(buf), 1);
let got = ibuf_fd_get(buf);
assert_eq!(got, fd);
assert_eq!(ibuf_fd_avail(buf), 0);
libc::close(fd);
ibuf_free(buf);
}
}
#[test]
fn test_msgbuf_enqueue_queuelen_clear() {
unsafe {
let mut mb: msgbuf = std::mem::zeroed();
ibufq_init(&raw mut mb.bufs);
ibufq_init(&raw mut mb.rbufs);
assert_eq!(msgbuf_queuelen(&raw mut mb), 0);
for k in 0..3u8 {
let b = ibuf_dynamic(4, 4);
assert!(!b.is_null());
assert_eq!(ibuf_add(b, [k, k, k, k].as_ptr() as *const c_void, 4), 0);
ibuf_close(&raw mut mb, b);
}
assert_eq!(mb.bufs.queued, 3);
assert_eq!(msgbuf_queuelen(&raw mut mb), 3);
let first = tailq_first(&raw mut mb.bufs.bufs);
assert!(!first.is_null());
assert_eq!(readable(first), [0, 0, 0, 0]);
msgbuf_clear(&raw mut mb);
assert_eq!(mb.bufs.queued, 0);
assert!(tailq_first(&raw mut mb.bufs.bufs).is_null());
}
}
#[test]
fn test_ibuf_set_n16_n64_backfill() {
unsafe {
let buf = ibuf_open(16);
assert_eq!(ibuf_add_zero(buf, 10), 0);
assert_eq!(ibuf_set_n16(buf, 0, 0x0102), 0);
assert_eq!(ibuf_set_n64(buf, 2, 0x1122_3344_5566_7788), 0);
let r = readable(buf);
assert_eq!(&r[0..2], &[0x01, 0x02]);
assert_eq!(
&r[2..10],
&[0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88]
);
let mut v16 = 0u16;
assert_eq!(ibuf_get_n16(buf, &mut v16), 0);
assert_eq!(v16, 0x0102);
let mut v64 = 0u64;
assert_eq!(ibuf_get_n64(buf, &mut v64), 0);
assert_eq!(v64, 0x1122_3344_5566_7788);
ibuf_free(buf);
}
}
#[test]
fn test_ibuf_set_h32_round_trip() {
unsafe {
let buf = ibuf_open(8);
assert_eq!(ibuf_add_zero(buf, 4), 0);
assert_eq!(ibuf_set_h32(buf, 0, 0xDEAD_BEEF), 0);
let r = readable(buf);
assert_eq!(r, 0xDEAD_BEEFu32.to_ne_bytes());
let mut v = 0u32;
assert_eq!(ibuf_get_h32(buf, &mut v), 0);
assert_eq!(v, 0xDEAD_BEEF);
ibuf_free(buf);
}
}
#[test]
fn test_ibuf_dynamic_grows_up_to_max() {
unsafe {
let buf = ibuf_dynamic(4, 8);
assert!(!buf.is_null());
assert_eq!((*buf).max, 8);
assert_eq!((*buf).size, 4);
assert_eq!(ibuf_add(buf, [1u8, 2, 3, 4].as_ptr() as *const c_void, 4), 0);
assert_eq!(ibuf_add(buf, [5u8, 6, 7, 8].as_ptr() as *const c_void, 4), 0);
assert_eq!(ibuf_size(buf), 8);
assert_eq!(readable(buf), [1, 2, 3, 4, 5, 6, 7, 8]);
errno!() = 0;
assert_eq!(ibuf_add(buf, [9u8].as_ptr() as *const c_void, 1), -1);
assert_eq!(errno!(), ERANGE);
assert_eq!(ibuf_size(buf), 8);
ibuf_free(buf);
}
}
#[test]
fn test_ibuf_from_ibuf_view() {
unsafe {
let src = ibuf_open(16);
assert_eq!(ibuf_add(src, [7u8, 8, 9, 10].as_ptr() as *const c_void, 4), 0);
let mut one = [0u8; 1];
assert_eq!(ibuf_get(src, one.as_mut_ptr() as *mut c_void, 1), 0); let mut view: ibuf = std::mem::zeroed();
ibuf_from_ibuf(&raw mut view, src);
assert_eq!(ibuf_size(&raw const view), 3);
assert_eq!(readable(&raw const view), [8, 9, 10]);
assert_eq!(view.max, 0); ibuf_free(src); }
}
#[test]
fn test_ibuf_from_buffer_stack_props() {
unsafe {
let mut backing = [0x11u8, 0x22, 0x33];
let mut b: ibuf = std::mem::zeroed();
ibuf_from_buffer(&raw mut b, backing.as_mut_ptr() as *mut c_void, 3);
assert_eq!(b.max, 0);
assert_eq!(b.fd, IBUF_FD_MARK_ON_STACK);
assert_eq!(b.wpos, 3);
assert_eq!(b.size, 3);
assert_eq!(b.rpos, 0);
assert_eq!(ibuf_size(&raw const b), 3);
assert_eq!(ibuf_left(&raw const b), 0);
assert_eq!(readable(&raw const b), [0x11, 0x22, 0x33]);
}
}
#[test]
fn test_ibuf_reserve_exact_to_size() {
unsafe {
let buf = ibuf_open(8);
let p = ibuf_reserve(buf, 8);
assert!(!p.is_null());
assert_eq!(ibuf_size(buf), 8);
assert_eq!((*buf).wpos, 8);
std::ptr::write_bytes(p as *mut u8, 0xCD, 8);
assert_eq!(readable(buf), [0xCD; 8]);
errno!() = 0;
assert!(ibuf_reserve(buf, 1).is_null());
assert_eq!(errno!(), ERANGE);
ibuf_free(buf);
}
}
}