Use splice to pump tty to avoid userspace copying

This commit is contained in:
LoveSy 2025-04-17 00:34:58 +08:00
parent 14f9ed91a1
commit a721b2ad84
No known key found for this signature in database
2 changed files with 49 additions and 27 deletions

View File

@ -4,13 +4,14 @@
#![feature(fn_traits)]
#![feature(unix_socket_ancillary_data)]
#![feature(unix_socket_peek)]
#![feature(maybe_uninit_uninit_array)]
#![allow(clippy::missing_safety_doc)]
use crate::ffi::SuRequest;
use crate::socket::Encodable;
use base::{Utf8CStr, libc};
use cxx::{ExternType, type_id};
use daemon::{MagiskD, daemon_entry};
use base::{libc, Utf8CStr};
use cxx::{type_id, ExternType};
use daemon::{daemon_entry, MagiskD};
use derive::Decodable;
use logging::{android_logging, setup_logfile, zygisk_close_logd, zygisk_get_logd, zygisk_logging};
use mount::{find_preinit_device, revert_unmount};

View File

@ -1,25 +1,21 @@
use base::{
ResultExt, error,
error,
libc::{
POLLIN, SFD_CLOEXEC, SIG_BLOCK, SIGWINCH, TCSADRAIN, TCSAFLUSH, TIOCGWINSZ, TIOCSWINSZ,
cfmakeraw, close, poll, pollfd, raise, sigaddset, sigemptyset, signalfd, sigprocmask,
sigset_t, tcsetattr, winsize,
cfmakeraw, close, pipe, poll, pollfd, raise, read, sigaddset,
sigemptyset, signalfd, signalfd_siginfo, sigprocmask, sigset_t, splice, tcsetattr, winsize, POLLIN, SFD_CLOEXEC,
SIGWINCH, SIG_BLOCK, TCSADRAIN, TCSAFLUSH, TIOCGWINSZ, TIOCSWINSZ,
},
libc::{STDIN_FILENO, STDOUT_FILENO, tcgetattr, termios},
libc::{tcgetattr, termios, STDIN_FILENO, STDOUT_FILENO},
warn,
};
use std::fs::File;
use std::io::{Read, Write};
use std::mem::ManuallyDrop;
use std::os::fd::{FromRawFd, RawFd};
use std::ptr::null_mut;
use std::{ffi::c_int, mem::MaybeUninit, ptr::null_mut};
static mut OLD_STDIN: Option<termios> = None;
const TIOCGPTN: u32 = 0x80045430;
unsafe extern "C" {
// Don't use the declaration from the libc crate as request should be u32 not i32
fn ioctl(fd: RawFd, request: u32, ...) -> i32;
fn ioctl(fd: c_int, request: u32, ...) -> i32;
}
pub fn get_pty_num(fd: i32) -> i32 {
@ -79,6 +75,23 @@ fn resize_pty(outfd: i32) {
}
}
fn pump_via_pipe(infd: i32, outfd: i32, pipe: &[c_int; 2]) -> bool {
let s = unsafe { splice(infd, null_mut(), pipe[1], null_mut(), usize::MAX, 0) };
if s < 0 {
error!("splice error");
return false;
}
if s == 0 {
return true;
}
let s = unsafe { splice(pipe[0], null_mut(), outfd, null_mut(), s as usize, 0) };
if s < 0 {
error!("splice error");
return false;
}
true
}
pub fn pump_tty(infd: i32, outfd: i32) {
set_stdin_raw();
@ -112,7 +125,11 @@ pub fn pump_tty(infd: i32, outfd: i32) {
},
];
let mut buf = [0u8; 4096];
let mut p: [c_int; 2] = [0; 2];
if unsafe { pipe(&mut p as *mut c_int) } < 0 {
error!("pipe error");
return;
}
'poll: loop {
let ready = unsafe { poll(pfds.as_mut_ptr(), pfds.len() as _, -1) };
@ -123,21 +140,25 @@ pub fn pump_tty(infd: i32, outfd: i32) {
for pfd in &pfds {
if pfd.revents & POLLIN != 0 {
let mut in_file = ManuallyDrop::new(unsafe { File::from_raw_fd(pfd.fd) });
let Ok(n) = in_file.read(&mut buf) else {
error!("read error");
break 'poll;
};
if pfd.fd == STDIN_FILENO {
let mut out = ManuallyDrop::new(unsafe { File::from_raw_fd(outfd) });
out.write_all(&buf[..n]).log_ok();
let res = if pfd.fd == STDIN_FILENO {
pump_via_pipe(pfd.fd, outfd, &p)
} else if pfd.fd == infd {
let mut out = ManuallyDrop::new(unsafe { File::from_raw_fd(STDOUT_FILENO) });
out.write_all(&buf[..n]).log_ok();
pump_via_pipe(pfd.fd, STDOUT_FILENO, &p)
} else if pfd.fd == sfd {
resize_pty(outfd);
let mut buf: [MaybeUninit<u8>; size_of::<signalfd_siginfo>()] =
MaybeUninit::uninit_array();
if unsafe { read(pfd.fd, buf.as_mut_ptr() as *mut _, buf.len()) } < 0 {
error!("read error");
false
} else {
true
}
} else {
unreachable!()
};
if !res {
break 'poll;
}
} else if pfd.revents != 0 && pfd.fd == infd {
unsafe { close(pfd.fd) };