fix: rewrite write_all for O_NONBLOCK pipe support
`wl_clip_persist` hands satellite a `WritePipe` with the `O_NONBLOCK` flag, which prevented more than 64 KiB (the Linux pipe capacity since 2.6.11) of selection data to be transferred. Since my research concluded having `O_NONBLOCK` on Wayland's pipe transfer ends was probably valid, I implemented basic handling for such a scenario. I previously considered just using `fcntl` to remove the `O_NONBLOCK` flag, but decided that was way too hacky, as it would not resolve the underlying problem (which could easily be triggered by the receiving program resetting `O_NONBLOCK` during the `write_all`.
This commit is contained in:
parent
59dc560182
commit
0162ac31ff
1 changed files with 28 additions and 2 deletions
|
|
@ -2,10 +2,12 @@ use super::{get_atom_name, XState};
|
|||
use crate::server::selection::{Clipboard, ForeignSelection, Primary, SelectionType};
|
||||
use crate::{RealServerState, X11Selection};
|
||||
use log::{debug, error, warn};
|
||||
use rustix::event::{fd_set_insert, select};
|
||||
use rustix::fd::AsRawFd;
|
||||
use smithay_client_toolkit::data_device_manager::WritePipe;
|
||||
use std::cell::RefCell;
|
||||
use std::collections::VecDeque;
|
||||
use std::io::Write;
|
||||
use std::io::{Error, ErrorKind, Result, Write};
|
||||
use std::rc::Rc;
|
||||
use xcb::x;
|
||||
|
||||
|
|
@ -164,8 +166,32 @@ impl Selection {
|
|||
}
|
||||
};
|
||||
|
||||
// Since the WritePipe given to us can have the O_NONBLOCK flag, we must respect that and
|
||||
// use `select` to monitor when the pipe is available to do more I/O.
|
||||
fn write_all(pipe: &mut WritePipe, mut buf: &[u8]) -> Result<()> {
|
||||
while !buf.is_empty() {
|
||||
match pipe.write(buf) {
|
||||
Ok(0) => return Err(Error::from(ErrorKind::WriteZero)),
|
||||
Ok(n) => buf = &buf[n..],
|
||||
Err(ref e) if e.kind() == ErrorKind::Interrupted => {}
|
||||
Err(ref e) if e.kind() == ErrorKind::WouldBlock => {
|
||||
let mut writefds = [Default::default()];
|
||||
fd_set_insert(&mut writefds, pipe.as_raw_fd());
|
||||
// SAFETY: nfds is correctly set to the numerically largest raw file
|
||||
// desciprtor being selected on plus 1, and all one fd is open for the
|
||||
// duration of the call.
|
||||
unsafe {
|
||||
select(pipe.as_raw_fd() + 1, None, Some(&mut writefds), None, None)?;
|
||||
}
|
||||
}
|
||||
Err(e) => return Err(e),
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
if !psd.incr || !data.is_empty() {
|
||||
if let Err(e) = psd.pipe.write_all(data) {
|
||||
if let Err(e) = write_all(&mut psd.pipe, data) {
|
||||
warn!("Failed to write selection data: {e:?}");
|
||||
} else if psd.incr {
|
||||
debug!(
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue