Skip to main content

starnix_core/vfs/
pipe.rs

1// Copyright 2021 The Fuchsia Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5use crate::mm::{MemoryAccessorExt, PAGE_SIZE, TaskMemoryAccessor};
6use crate::security;
7use crate::signals::{SignalInfo, send_standard_signal};
8use crate::task::{CurrentTask, EventHandler, WaitCallback, WaitCanceler, WaitQueue, Waiter};
9use crate::vfs::buffers::{
10    Buffer, InputBuffer, InputBufferCallback, InputBufferExt, MessageData, MessageQueue,
11    OutputBuffer, OutputBufferCallback, PeekBufferSegmentsCallback, PipeMessageData,
12    UserBuffersOutputBuffer, VecInputBuffer, VecOutputBuffer,
13};
14use crate::vfs::fs_registry::FsRegistry;
15use crate::vfs::{
16    CacheMode, FileHandle, FileObject, FileObjectState, FileOps, FileSystem, FileSystemHandle,
17    FileSystemOps, FileSystemOptions, FsNodeInfo, FsStr, SpecialNode, default_fcntl,
18    fileops_impl_nonseekable, fileops_impl_noop_sync,
19};
20use starnix_sync::{LockDepGuard, LockDepMutex, PipeLock, ordered_lock};
21use starnix_syscalls::{SUCCESS, SyscallArg, SyscallResult};
22use starnix_types::user_buffer::{UserBuffer, UserBuffers};
23use starnix_types::vfs::default_statfs;
24use starnix_uapi::auth::CAP_SYS_RESOURCE;
25use starnix_uapi::errors::{EAGAIN, EPIPE, Errno};
26use starnix_uapi::file_mode::mode;
27use starnix_uapi::fs_type::FileSystemTypeFlags;
28use starnix_uapi::open_flags::OpenFlags;
29use starnix_uapi::resource_limits::Resource;
30use starnix_uapi::signals::{SIGPIPE, SIGXFSZ};
31use starnix_uapi::user_address::{UserAddress, UserRef};
32use starnix_uapi::vfs::FdEvents;
33use starnix_uapi::{
34    F_GETPIPE_SZ, F_SETPIPE_SZ, FIONREAD, PIPEFS_MAGIC, errno, error, statfs, uapi,
35};
36use std::sync::Arc;
37
38const ATOMIC_IO_BYTES: u16 = 4096;
39
40/// The maximum size of a pipe, independent of task capabilities and sysctl limits.
41const PIPE_MAX_SIZE: usize = 1 << 31;
42
43/// Wait-queue key notified when an in-progress splice read (`SpliceReadGuard`) completes.
44const SPLICE_READ_WAIT_KEY: u64 = 0;
45
46/// Wait-queue key notified when an in-progress splice write (`SpliceWriteGuard`) completes.
47const SPLICE_WRITE_WAIT_KEY: u64 = 1;
48
49/// Wait-queue key notified whenever buffer space increases, even if available space remains below
50/// `PIPE_BUF`.
51const PIPE_WRITE_SPACE_WAIT_KEY: u64 = 2;
52
53#[derive(Debug)]
54pub struct Pipe {
55    messages: MessageQueue<PipeMessageData>,
56
57    waiters: WaitQueue,
58
59    /// The number of open readers.
60    reader_count: usize,
61
62    /// Whether the pipe has ever had a reader.
63    had_reader: bool,
64
65    /// The number of open writers.
66    writer_count: usize,
67
68    /// Whether the pipe has ever had a writer.
69    had_writer: bool,
70
71    /// Whether an in-flight splice read is currently consuming messages from this pipe.
72    splice_read_in_progress: bool,
73
74    /// Number of bytes at the front of the queue reserved by an in-progress splice read.
75    splice_read_reserved_bytes: usize,
76
77    /// Whether an in-flight splice write is currently producing messages into this pipe.
78    splice_write_in_progress: bool,
79}
80
81pub type PipeHandle = Arc<LockDepMutex<Pipe, PipeLock>>;
82
83impl Pipe {
84    pub fn new(default_pipe_capacity: usize) -> PipeHandle {
85        Arc::new(
86            Pipe {
87                messages: MessageQueue::new(default_pipe_capacity),
88                waiters: WaitQueue::default(),
89                reader_count: 0,
90                had_reader: false,
91                writer_count: 0,
92                had_writer: false,
93                splice_read_in_progress: false,
94                splice_read_reserved_bytes: 0,
95                splice_write_in_progress: false,
96            }
97            .into(),
98        )
99    }
100
101    pub fn open(
102        current_task: &CurrentTask,
103        pipe: &PipeHandle,
104        flags: OpenFlags,
105    ) -> Result<Box<dyn FileOps>, Errno> {
106        let mut events = FdEvents::empty();
107        let mut pipe_locked = pipe.lock();
108        let mut must_wait_events = FdEvents::empty();
109        if flags.can_read() {
110            if !pipe_locked.had_reader {
111                events |= FdEvents::POLLOUT;
112            }
113            pipe_locked.add_reader();
114            if !flags.contains(OpenFlags::NONBLOCK) && !flags.can_write() && !pipe_locked.had_writer
115            {
116                must_wait_events |= FdEvents::POLLIN;
117            }
118        }
119        if flags.can_write() {
120            // https://man7.org/linux/man-pages/man2/open.2.html says:
121            //
122            //  ENXIO  O_NONBLOCK | O_WRONLY is set, the named file is a FIFO,
123            //         and no process has the FIFO open for reading.
124            if flags.contains(OpenFlags::NONBLOCK) && pipe_locked.reader_count == 0 {
125                assert!(!flags.can_read()); // Otherwise add_reader() would have run above.
126                return error!(ENXIO);
127            }
128            if !pipe_locked.had_writer {
129                events |= FdEvents::POLLIN;
130            }
131            pipe_locked.add_writer();
132            if !flags.contains(OpenFlags::NONBLOCK) && !pipe_locked.had_reader {
133                must_wait_events |= FdEvents::POLLOUT;
134            }
135        }
136        if events != FdEvents::empty() {
137            pipe_locked.waiters.notify_fd_events(events);
138        }
139        let ops = PipeFileObject { pipe: Arc::clone(pipe) };
140        if must_wait_events == FdEvents::empty() {
141            return Ok(Box::new(ops));
142        }
143
144        // Ensures that the new PipeFileObject is closed if is it dropped before being returned.
145        let ops = scopeguard::guard(ops, |ops| {
146            ops.on_close(flags);
147        });
148
149        // Wait for the pipe to be connected.
150        let waiter = Waiter::new();
151        loop {
152            pipe_locked.waiters.wait_async_fd_events(
153                &waiter,
154                must_wait_events,
155                WaitCallback::none(),
156            );
157            std::mem::drop(pipe_locked);
158            match waiter.wait(current_task) {
159                Err(e) => {
160                    return Err(e);
161                }
162                _ => {}
163            }
164            pipe_locked = pipe.lock();
165            if pipe_locked.had_writer && pipe_locked.had_reader {
166                return Ok(Box::new(scopeguard::ScopeGuard::into_inner(ops)));
167            }
168        }
169    }
170
171    /// Increments the reader count for this pipe by 1.
172    pub fn add_reader(&mut self) {
173        self.reader_count += 1;
174        self.had_reader = true;
175    }
176
177    /// Increments the writer count for this pipe by 1.
178    pub fn add_writer(&mut self) {
179        self.writer_count += 1;
180        self.had_writer = true;
181    }
182
183    /// Called whenever a fd to the pipe is closed. Reset the pipe state if there is not more
184    /// reader or writer.
185    pub fn on_close(&mut self) {
186        if self.reader_count == 0 && self.writer_count == 0 {
187            debug_assert!(!self.splice_read_in_progress);
188            debug_assert!(!self.splice_write_in_progress);
189            debug_assert_eq!(self.splice_read_reserved_bytes, 0);
190            self.had_reader = false;
191            self.had_writer = false;
192            self.messages = MessageQueue::new(self.messages.capacity());
193            self.waiters = WaitQueue::default();
194        }
195    }
196
197    fn is_empty(&self) -> bool {
198        self.readable_len() == 0
199    }
200
201    fn readable_len(&self) -> usize {
202        self.messages.len().saturating_sub(self.splice_read_reserved_bytes)
203    }
204
205    fn capacity(&self) -> usize {
206        self.messages.capacity()
207    }
208
209    fn set_capacity(
210        &mut self,
211        current_task: &CurrentTask,
212        mut requested_capacity: usize,
213    ) -> Result<(), Errno> {
214        if requested_capacity > PIPE_MAX_SIZE {
215            return error!(EINVAL);
216        }
217        let page_size = *PAGE_SIZE as usize;
218        if requested_capacity < page_size {
219            requested_capacity = page_size;
220        }
221        requested_capacity = requested_capacity.next_power_of_two();
222        let old_capacity = self.messages.capacity();
223        if requested_capacity > old_capacity
224            && requested_capacity
225                > current_task
226                    .kernel()
227                    .system_limits
228                    .pipe_max_size
229                    .load(std::sync::atomic::Ordering::Relaxed)
230        {
231            security::check_task_capable(current_task, CAP_SYS_RESOURCE)?;
232        }
233        let was_writable = self.is_writable(uapi::PIPE_BUF as usize);
234        self.messages.set_capacity(requested_capacity)?;
235        if requested_capacity > old_capacity {
236            self.notify_write_space(was_writable);
237        }
238        Ok(())
239    }
240
241    fn is_readable(&self) -> bool {
242        !self.is_empty() || (self.writer_count == 0 && self.had_writer)
243    }
244
245    /// Returns whether the pipe can accommodate at least part of a message of length `data_size`.
246    fn is_writable(&self, data_size: usize) -> bool {
247        let available_capacity = self.messages.available_capacity();
248        // POSIX requires that a write smaller than PIPE_BUF be atomic, but requires no
249        // atomicity for writes larger than this.
250        self.had_reader
251            && (self.reader_count == 0
252                || available_capacity >= data_size
253                || (available_capacity > 0 && data_size > uapi::PIPE_BUF as usize))
254    }
255
256    /// Minimum free capacity required for non-merging operations (`splice`, `vmsplice`, `tee`).
257    ///
258    /// A single-page pipe (`capacity <= PAGE_SIZE`) has only one page slot; when non-empty, its
259    /// sole slot is already occupied and cannot accept non-merging splice writes until drained.
260    fn min_splice_write_bytes(&self) -> usize {
261        let page_size = *PAGE_SIZE as usize;
262        if self.messages.capacity() <= page_size { page_size } else { 1 }
263    }
264
265    pub fn read(&mut self, data: &mut dyn OutputBuffer) -> Result<usize, Errno> {
266        debug_assert!(!self.splice_read_in_progress);
267        if data.available() == 0 {
268            return Ok(0);
269        }
270
271        // Return EAGAIN if the pipe is empty and still has at least one open writer so the caller
272        // blocks; otherwise fall through and return 0 to indicate EOF.
273        if !self.is_readable() {
274            return error!(EAGAIN);
275        }
276
277        self.messages.read_stream(data).map(|(info, _)| info.bytes_read)
278    }
279
280    pub fn write(&mut self, data: &mut dyn InputBuffer) -> Result<usize, Errno> {
281        debug_assert!(!self.splice_write_in_progress);
282        if data.available() == 0 {
283            return Ok(0);
284        }
285
286        if !self.had_reader {
287            return error!(EAGAIN);
288        }
289
290        if self.reader_count == 0 {
291            return error!(EPIPE);
292        }
293
294        let total_requested = data.available().saturating_add(data.bytes_read());
295        if !self.is_writable(total_requested) {
296            return error!(EAGAIN);
297        }
298
299        let page_size = *PAGE_SIZE as usize;
300        let mut total_written = 0;
301        while self.messages.available_capacity() > 0 && data.available() > 0 {
302            let chunk_len = std::cmp::min(
303                self.messages.available_capacity(),
304                std::cmp::min(data.available(), page_size),
305            );
306            let bytes = data.read_to_vec_limited(chunk_len)?;
307            let actual = bytes.len();
308            if actual == 0 {
309                break;
310            }
311            self.messages.write_message(PipeMessageData::from(bytes).into());
312            total_written += actual;
313        }
314        Ok(total_written)
315    }
316
317    fn query_events(&self, flags: OpenFlags) -> FdEvents {
318        let mut events = FdEvents::empty();
319
320        if flags.can_read() && self.is_readable() {
321            let writer_closed = self.writer_count == 0 && self.had_writer;
322            let has_data = !self.is_empty();
323            if writer_closed {
324                events |= FdEvents::POLLHUP;
325            }
326            if has_data {
327                events |= FdEvents::POLLIN;
328            }
329        }
330
331        if flags.can_write() && self.is_writable(uapi::PIPE_BUF as usize) {
332            if self.reader_count == 0 && self.had_reader {
333                events |= FdEvents::POLLERR;
334            }
335
336            events |= FdEvents::POLLOUT;
337        }
338
339        events
340    }
341
342    fn fcntl(
343        &mut self,
344        _file: &FileObject,
345        current_task: &CurrentTask,
346        cmd: u32,
347        arg: u64,
348    ) -> Result<SyscallResult, Errno> {
349        match cmd {
350            F_GETPIPE_SZ => Ok(self.capacity().into()),
351            F_SETPIPE_SZ => {
352                let raw_arg = arg as i32;
353                if raw_arg < 0 {
354                    return error!(EINVAL);
355                }
356                self.set_capacity(current_task, raw_arg as usize)?;
357                Ok(self.capacity().into())
358            }
359            _ => default_fcntl(cmd),
360        }
361    }
362
363    fn notify_fd_events(&self, events: FdEvents) {
364        self.waiters.notify_fd_events(events);
365    }
366
367    fn notify_write_space(&self, was_writable: bool) {
368        if !was_writable {
369            if self.is_writable(uapi::PIPE_BUF as usize) {
370                self.notify_fd_events(FdEvents::POLLOUT);
371            } else if self.is_writable(1) {
372                self.waiters.notify_value(PIPE_WRITE_SPACE_WAIT_KEY);
373            }
374        }
375    }
376
377    /// Consumes `bytes_read` bytes from the front of the pipe queue and wakes blocked writers if
378    /// buffer space becomes available.
379    fn consume(&mut self, bytes_read: usize) {
380        let was_writable = self.is_writable(uapi::PIPE_BUF as usize);
381        let mut consumed = bytes_read;
382        while consumed > 0 {
383            let mut message =
384                self.messages.read_message().expect("pipe messages shrunk during splice read");
385            if let Some(remaining) = MessageData::split_off(&mut message.data, consumed) {
386                self.messages.write_front(remaining.into());
387                break;
388            }
389            consumed -= message.len();
390        }
391        if bytes_read > 0 {
392            self.notify_write_space(was_writable);
393        }
394    }
395}
396
397/// Creates a new pipe between the two returned FileObjects.
398///
399/// The first FileObject is the read endpoint of the pipe. The second is the
400/// write endpoint of the pipe. This order matches the order expected by
401/// sys_pipe2().
402pub fn new_pipe(current_task: &CurrentTask) -> Result<(FileHandle, FileHandle), Errno> {
403    let fs = pipe_fs(current_task, FileSystemOptions::default())?;
404    let mut info = FsNodeInfo::new(mode!(IFIFO, 0o600), current_task.current_fscred());
405    info.blksize = ATOMIC_IO_BYTES.into();
406    let node = fs.create_node_and_allocate_node_id(SpecialNode, info);
407    let pipe = node.fifo(current_task);
408    {
409        let mut state = pipe.lock();
410        state.add_reader();
411        state.add_writer();
412    }
413
414    // Creating the readable `FileObject` takes care of initializing the `node` security label.
415    let read_ops = PipeFileObject { pipe: Arc::clone(pipe) };
416    let read_file = FileObject::new_anonymous(
417        current_task,
418        Box::new(read_ops),
419        Arc::clone(&node),
420        OpenFlags::RDONLY,
421    );
422
423    // Create the writable `FileObject` using the readable object's `name` to reduce overhead.
424    let write_ops = PipeFileObject { pipe: Arc::clone(pipe) };
425    let write_file = FileObject::new(
426        current_task,
427        Box::new(write_ops),
428        read_file.name.to_passive(),
429        OpenFlags::WRONLY,
430    )?;
431
432    Ok((read_file, write_file))
433}
434
435/// Creates a new anonymous [`FileObject`] backed by a `pipefs` [`crate::vfs::FsNode`]
436/// (`S_IFIFO`) with the specified `ops`.
437pub(crate) fn new_pipe_file(
438    current_task: &CurrentTask,
439    ops: Box<dyn FileOps>,
440    flags: OpenFlags,
441) -> Result<FileHandle, Errno> {
442    let fs = pipe_fs(current_task, FileSystemOptions::default())?;
443    let mut info = FsNodeInfo::new(mode!(IFIFO, 0o600), current_task.current_fscred());
444    info.blksize = ATOMIC_IO_BYTES.into();
445    let node = fs.create_node_and_allocate_node_id(SpecialNode, info);
446    Ok(FileObject::new_anonymous(current_task, ops, node, flags))
447}
448
449struct PipeFs;
450impl FileSystemOps for PipeFs {
451    fn statfs(&self, _fs: &FileSystem, _current_task: &CurrentTask) -> Result<statfs, Errno> {
452        Ok(default_statfs(PIPEFS_MAGIC))
453    }
454    fn name(&self) -> &'static FsStr {
455        "pipefs".into()
456    }
457}
458
459fn pipe_fs(
460    current_task: &CurrentTask,
461    _options: FileSystemOptions,
462) -> Result<FileSystemHandle, Errno> {
463    struct PipeFsHandle(FileSystemHandle);
464
465    let kernel = current_task.kernel();
466    let fs = kernel
467        .expando
468        .get_or_init(|| {
469            PipeFsHandle(
470                FileSystem::new(kernel, CacheMode::Uncached, PipeFs, FileSystemOptions::default())
471                    .expect("pipefs constructed with valid options"),
472            )
473        })
474        .0
475        .clone();
476    security::file_system_resolve_security(current_task, &fs)?;
477    Ok(fs)
478}
479
480pub fn register_pipe_fs(fs_registry: &FsRegistry) {
481    fs_registry.register("pipefs".into(), FileSystemTypeFlags::empty(), pipe_fs);
482}
483
484pub struct PipeFileObject {
485    pipe: PipeHandle,
486}
487
488impl FileOps for PipeFileObject {
489    fileops_impl_nonseekable!();
490    fileops_impl_noop_sync!();
491
492    fn close(self: Box<Self>, file: &FileObjectState, _current_task: &CurrentTask) {
493        self.on_close(file.flags());
494    }
495
496    fn read(
497        &self,
498        file: &FileObject,
499        current_task: &CurrentTask,
500        offset: usize,
501        data: &mut dyn OutputBuffer,
502    ) -> Result<usize, Errno> {
503        debug_assert!(offset == 0);
504        file.blocking_op(current_task, FdEvents::POLLIN | FdEvents::POLLHUP, None, || {
505            let mut pipe = self.lock_pipe_without_splice(current_task)?;
506            let was_writable = pipe.is_writable(uapi::PIPE_BUF as usize);
507            let actual = pipe.read(data)?;
508            if actual > 0 {
509                pipe.notify_write_space(was_writable);
510            }
511            Ok(actual)
512        })
513    }
514
515    fn write(
516        &self,
517        file: &FileObject,
518        current_task: &CurrentTask,
519        offset: usize,
520        data: &mut dyn InputBuffer,
521    ) -> Result<usize, Errno> {
522        debug_assert!(offset == 0);
523        debug_assert!(data.bytes_read() == 0);
524
525        let result =
526            file.blocking_op(current_task, FdEvents::POLLOUT | FdEvents::POLLERR, None, || {
527                let mut pipe = self.lock_pipe_without_splice(current_task)?;
528                let offset_before = data.bytes_read();
529                let bytes_written = match pipe.write(data) {
530                    Err(e) => {
531                        if data.bytes_read() > offset_before {
532                            pipe.notify_fd_events(FdEvents::POLLIN);
533                        }
534                        if e == EPIPE {
535                            std::mem::drop(pipe);
536                            send_standard_signal(current_task, SignalInfo::kernel(SIGPIPE));
537                        }
538                        return Err(e);
539                    }
540                    Ok(bytes_written) => bytes_written,
541                };
542                debug_assert!(data.bytes_read() - offset_before == bytes_written);
543                if bytes_written > 0 {
544                    pipe.notify_fd_events(FdEvents::POLLIN);
545                }
546                if data.available() > 0 {
547                    return error!(EAGAIN);
548                }
549                Ok(())
550            });
551
552        let bytes_written = data.bytes_read();
553        if bytes_written == 0 {
554            // Return an error only if no data was sent; return partial progress otherwise.
555            result?;
556        }
557        Ok(bytes_written)
558    }
559
560    fn wait_async(
561        &self,
562        file: &FileObject,
563        _current_task: &CurrentTask,
564        waiter: &Waiter,
565        mut events: FdEvents,
566        handler: EventHandler,
567    ) -> Option<WaitCanceler> {
568        let flags = file.flags();
569        if !flags.can_read() {
570            events.remove(FdEvents::POLLIN);
571        }
572        if !flags.can_write() {
573            events.remove(FdEvents::POLLOUT);
574        }
575        let subscribe_write_space =
576            matches!(handler, EventHandler::None) && events.contains(FdEvents::POLLOUT);
577        let pipe = self.pipe.lock();
578        let canceler = pipe.waiters.wait_async_fd_events(waiter, events, handler);
579        if subscribe_write_space {
580            let write_space_canceler =
581                pipe.waiters.wait_async_value(waiter, PIPE_WRITE_SPACE_WAIT_KEY);
582            Some(WaitCanceler::merge(canceler, write_space_canceler))
583        } else {
584            Some(canceler)
585        }
586    }
587
588    fn query_events(
589        &self,
590        file: &FileObject,
591        _current_task: &CurrentTask,
592    ) -> Result<FdEvents, Errno> {
593        Ok(self.pipe.lock().query_events(file.flags()))
594    }
595
596    fn can_query_events_without_wait(&self) -> bool {
597        // `wait_async` only registers on the pipe's wait queue.
598        true
599    }
600
601    fn fcntl(
602        &self,
603        file: &FileObject,
604        current_task: &CurrentTask,
605        cmd: u32,
606        arg: u64,
607    ) -> Result<SyscallResult, Errno> {
608        self.lock_pipe_without_splice(current_task)?.fcntl(file, current_task, cmd, arg)
609    }
610
611    fn ioctl(
612        &self,
613        _file: &FileObject,
614        current_task: &CurrentTask,
615        request: u32,
616        arg: SyscallArg,
617    ) -> Result<SyscallResult, Errno> {
618        let user_addr = UserAddress::from(arg);
619        match request {
620            FIONREAD => {
621                let pipe = self.lock_pipe_without_splice(current_task)?;
622                let value: i32 = pipe.readable_len().try_into().map_err(|_| errno!(EINVAL))?;
623                std::mem::drop(pipe);
624                let addr = UserRef::<i32>::new(user_addr);
625                current_task.write_object(addr, &value)?;
626                Ok(SUCCESS)
627            }
628            _ => error!(ENOTTY),
629        }
630    }
631}
632
633/// Guard that marks a pipe as having an in-progress splice read while the pipe lock is not held.
634struct SpliceReadGuard<'a> {
635    pipe: Option<&'a PipeHandle>,
636}
637
638impl<'a> SpliceReadGuard<'a> {
639    /// Marks `pipe` as having a splice read in progress and drops the pipe lock.
640    fn new(
641        pipe_handle: &'a PipeHandle,
642        mut pipe: LockDepGuard<'a, Pipe>,
643        reserved_bytes: usize,
644    ) -> Self {
645        assert!(!pipe.splice_read_in_progress);
646        pipe.splice_read_in_progress = true;
647        pipe.splice_read_reserved_bytes = reserved_bytes;
648        Self { pipe: Some(pipe_handle) }
649    }
650
651    /// Consumes `bytes_read` bytes from the front of the pipe, clears the in-progress splice read
652    /// state, and wakes any waiting readers.
653    fn consume(mut self, bytes_read: usize) {
654        if let Some(pipe_handle) = self.pipe.take() {
655            let mut pipe = pipe_handle.lock();
656            pipe.consume(bytes_read);
657            Self::clear(&mut pipe);
658        }
659    }
660
661    /// Clears the in-progress splice read state on `pipe` and wakes any waiting readers.
662    fn clear(pipe: &mut Pipe) {
663        assert!(pipe.splice_read_in_progress);
664        pipe.splice_read_in_progress = false;
665        pipe.splice_read_reserved_bytes = 0;
666        pipe.waiters.notify_value(SPLICE_READ_WAIT_KEY);
667        if !pipe.is_empty() {
668            pipe.notify_fd_events(FdEvents::POLLIN);
669        }
670    }
671}
672
673impl Drop for SpliceReadGuard<'_> {
674    fn drop(&mut self) {
675        if let Some(pipe_handle) = self.pipe.take() {
676            Self::clear(&mut pipe_handle.lock());
677        }
678    }
679}
680
681/// Guard that marks a pipe as having an in-progress splice write while the pipe lock is not held.
682struct SpliceWriteGuard<'a> {
683    pipe: Option<&'a PipeHandle>,
684}
685
686impl<'a> SpliceWriteGuard<'a> {
687    /// Marks `pipe` as having a splice write in progress and drops the pipe lock.
688    fn new(pipe_handle: &'a PipeHandle, mut pipe: LockDepGuard<'a, Pipe>) -> Self {
689        assert!(!pipe.splice_write_in_progress);
690        pipe.splice_write_in_progress = true;
691        Self { pipe: Some(pipe_handle) }
692    }
693
694    /// Enqueues `bytes` into the back of the pipe, clears the in-progress splice write state, and
695    /// wakes any waiting readers or writers.
696    fn enqueue(mut self, bytes: Vec<u8>) -> Result<(), Errno> {
697        if let Some(pipe_handle) = self.pipe.take() {
698            let mut pipe = pipe_handle.lock();
699            if pipe.reader_count == 0 {
700                Self::clear(&mut pipe);
701                return error!(EPIPE);
702            }
703            if !bytes.is_empty() {
704                for chunk in bytes.chunks(*PAGE_SIZE as usize) {
705                    pipe.messages.write_message(PipeMessageData::from(chunk.to_vec()).into());
706                }
707                pipe.notify_fd_events(FdEvents::POLLIN);
708            }
709            Self::clear(&mut pipe);
710        }
711        Ok(())
712    }
713
714    /// Clears the in-progress splice write state on `pipe` and wakes any waiting writers.
715    fn clear(pipe: &mut Pipe) {
716        assert!(pipe.splice_write_in_progress);
717        pipe.splice_write_in_progress = false;
718        pipe.waiters.notify_value(SPLICE_WRITE_WAIT_KEY);
719        if pipe.reader_count == 0 {
720            pipe.notify_fd_events(FdEvents::POLLOUT | FdEvents::POLLERR);
721        } else {
722            pipe.notify_write_space(false);
723        }
724    }
725}
726
727impl Drop for SpliceWriteGuard<'_> {
728    fn drop(&mut self) {
729        if let Some(pipe_handle) = self.pipe.take() {
730            Self::clear(&mut pipe_handle.lock());
731        }
732    }
733}
734
735impl std::fmt::Debug for SpliceReadGuard<'_> {
736    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
737        f.debug_struct("SpliceReadGuard").finish_non_exhaustive()
738    }
739}
740
741impl std::fmt::Debug for SpliceWriteGuard<'_> {
742    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
743        f.debug_struct("SpliceWriteGuard").finish_non_exhaustive()
744    }
745}
746
747/// An OutputBuffer that will write the data to `pipe`.
748#[derive(Debug)]
749pub struct SpliceOutputBuffer<'a> {
750    guard: SpliceWriteGuard<'a>,
751    buffer: VecOutputBuffer,
752}
753
754impl<'a> Buffer for SpliceOutputBuffer<'a> {
755    fn segments_count(&self) -> Result<usize, Errno> {
756        self.buffer.segments_count()
757    }
758
759    fn peek_each_segment(
760        &mut self,
761        callback: &mut PeekBufferSegmentsCallback<'_>,
762    ) -> Result<(), Errno> {
763        self.buffer.peek_each_segment(callback)
764    }
765}
766
767impl<'a> OutputBuffer for SpliceOutputBuffer<'a> {
768    fn write_each(&mut self, callback: &mut OutputBufferCallback<'_>) -> Result<usize, Errno> {
769        self.buffer.write_each(callback)
770    }
771
772    fn available(&self) -> usize {
773        self.buffer.available()
774    }
775
776    fn bytes_written(&self) -> usize {
777        self.buffer.bytes_written()
778    }
779
780    fn zero(&mut self) -> Result<usize, Errno> {
781        self.buffer.zero()
782    }
783
784    unsafe fn advance(&mut self, length: usize) -> Result<(), Errno> {
785        // SAFETY: Delegated directly to the inner `VecOutputBuffer` under the same safety
786        // contract.
787        unsafe { self.buffer.advance(length) }
788    }
789}
790
791/// An InputBuffer that will read the data from `pipe`.
792#[derive(Debug)]
793pub struct SpliceInputBuffer<'a> {
794    guard: SpliceReadGuard<'a>,
795    buffer: VecInputBuffer,
796}
797
798impl<'a> Buffer for SpliceInputBuffer<'a> {
799    fn segments_count(&self) -> Result<usize, Errno> {
800        self.buffer.segments_count()
801    }
802
803    fn peek_each_segment(
804        &mut self,
805        callback: &mut PeekBufferSegmentsCallback<'_>,
806    ) -> Result<(), Errno> {
807        self.buffer.peek_each_segment(callback)
808    }
809}
810
811impl<'a> InputBuffer for SpliceInputBuffer<'a> {
812    fn peek_each(&mut self, callback: &mut InputBufferCallback<'_>) -> Result<usize, Errno> {
813        self.buffer.peek_each(callback)
814    }
815
816    fn available(&self) -> usize {
817        self.buffer.available()
818    }
819
820    fn bytes_read(&self) -> usize {
821        self.buffer.bytes_read()
822    }
823
824    fn drain(&mut self) -> usize {
825        self.buffer.drain()
826    }
827
828    fn advance(&mut self, length: usize) -> Result<(), Errno> {
829        self.buffer.advance(length)
830    }
831}
832
833impl PipeFileObject {
834    /// Called whenever a fd to a pipe is closed.
835    fn on_close(&self, flags: OpenFlags) {
836        let mut events = FdEvents::empty();
837        let mut pipe = self.pipe.lock();
838        if flags.can_read() {
839            assert!(pipe.reader_count > 0);
840            pipe.reader_count -= 1;
841            if pipe.reader_count == 0 {
842                events |= FdEvents::POLLOUT | FdEvents::POLLERR;
843            }
844        }
845        if flags.can_write() {
846            assert!(pipe.writer_count > 0);
847            pipe.writer_count -= 1;
848            if pipe.writer_count == 0 {
849                if pipe.reader_count > 0 {
850                    events |= FdEvents::POLLHUP;
851                }
852                if !pipe.is_empty() {
853                    events |= FdEvents::POLLIN;
854                }
855            }
856        }
857        if events != FdEvents::empty() {
858            pipe.waiters.notify_fd_events(events);
859        }
860        pipe.on_close();
861    }
862
863    /// Acquires the pipe lock once neither a splice read nor a splice write is in progress.
864    fn lock_pipe_without_splice<'a>(
865        &'a self,
866        current_task: &CurrentTask,
867    ) -> Result<LockDepGuard<'a, Pipe>, Errno> {
868        loop {
869            let pipe = self.pipe.lock();
870            if !pipe.splice_read_in_progress && !pipe.splice_write_in_progress {
871                return Ok(pipe);
872            }
873            let waiter = Waiter::new();
874            if pipe.splice_read_in_progress {
875                pipe.waiters.wait_async_value(&waiter, SPLICE_READ_WAIT_KEY);
876            }
877            if pipe.splice_write_in_progress {
878                pipe.waiters.wait_async_value(&waiter, SPLICE_WRITE_WAIT_KEY);
879            }
880            std::mem::drop(pipe);
881            waiter.wait(current_task)?;
882        }
883    }
884
885    /// Locks the pipe for reading if it is readable, waiting for any active splice to finish.
886    fn try_lock_pipe_for_reading<'a>(
887        &'a self,
888        current_task: &CurrentTask,
889        _non_blocking: bool,
890    ) -> Result<LockDepGuard<'a, Pipe>, Errno> {
891        let pipe = self.lock_pipe_without_splice(current_task)?;
892        if pipe.is_readable() { Ok(pipe) } else { error!(EAGAIN) }
893    }
894
895    /// Locks the pipe for writing if it can accept part of `len` bytes without blocking, waiting
896    /// for any active splice to finish.
897    fn try_lock_pipe_for_writing<'a>(
898        &'a self,
899        current_task: &CurrentTask,
900        _non_blocking: bool,
901        len: usize,
902    ) -> Result<LockDepGuard<'a, Pipe>, Errno> {
903        let pipe = self.lock_pipe_without_splice(current_task)?;
904        if pipe.had_reader && pipe.reader_count == 0 {
905            std::mem::drop(pipe);
906            send_standard_signal(current_task, SignalInfo::kernel(SIGPIPE));
907            return error!(EPIPE);
908        }
909        if !pipe.is_writable(std::cmp::max(len, pipe.min_splice_write_bytes())) {
910            return error!(EAGAIN);
911        }
912        Ok(pipe)
913    }
914
915    fn lock_pipe_for_reading<'a>(
916        &'a self,
917        current_task: &CurrentTask,
918        file: &FileHandle,
919        non_blocking: bool,
920    ) -> Result<LockDepGuard<'a, Pipe>, Errno> {
921        if non_blocking {
922            return self.try_lock_pipe_for_reading(current_task, true);
923        }
924        match self.try_lock_pipe_for_reading(current_task, false) {
925            Err(errno) if errno == EAGAIN => {}
926            result => return result,
927        }
928        let waiter = Waiter::new();
929        loop {
930            file.wait_async(
931                current_task,
932                &waiter,
933                FdEvents::POLLIN | FdEvents::POLLHUP,
934                WaitCallback::none(),
935            );
936            match self.try_lock_pipe_for_reading(current_task, false) {
937                Err(e) if e == EAGAIN => {}
938                result => return result,
939            }
940            waiter.wait(current_task)?;
941        }
942    }
943
944    fn lock_pipe_for_writing<'a>(
945        &'a self,
946        current_task: &CurrentTask,
947        file: &FileHandle,
948        non_blocking: bool,
949        len: usize,
950    ) -> Result<LockDepGuard<'a, Pipe>, Errno> {
951        if non_blocking {
952            return self.try_lock_pipe_for_writing(current_task, true, len);
953        }
954        match self.try_lock_pipe_for_writing(current_task, false, len) {
955            Err(errno) if errno == EAGAIN => {}
956            result => return result,
957        }
958        let waiter = Waiter::new();
959        loop {
960            file.wait_async(
961                current_task,
962                &waiter,
963                FdEvents::POLLOUT | FdEvents::POLLERR,
964                WaitCallback::none(),
965            );
966            match self.try_lock_pipe_for_writing(current_task, false, len) {
967                Err(e) if e == EAGAIN => {}
968                result => return result,
969            }
970            waiter.wait(current_task)?;
971        }
972    }
973
974    /// Splice from the given file handle to this pipe.
975    ///
976    /// The given file handle must not be a pipe; for two pipes use `PipeFileObject::splice_pipes`.
977    pub fn splice_from(
978        &self,
979        current_task: &CurrentTask,
980        self_file: &FileHandle,
981        from: &FileHandle,
982        maybe_offset: Option<usize>,
983        len: usize,
984        non_blocking: bool,
985    ) -> Result<usize, Errno> {
986        // For pipe-to-pipe transfers, callers must use `PipeFileObject::splice_pipes`.
987        assert!(from.downcast_file::<PipeFileObject>().is_none());
988
989        let non_blocking = non_blocking || self_file.is_non_blocking();
990        let mut buffer = {
991            let pipe = self.lock_pipe_for_writing(current_task, self_file, non_blocking, 1)?;
992            let len = std::cmp::min(len, pipe.messages.available_capacity());
993            SpliceOutputBuffer {
994                guard: SpliceWriteGuard::new(&self.pipe, pipe),
995                buffer: VecOutputBuffer::new(len),
996            }
997        };
998
999        let bytes_read = from
1000            .splice_read(current_task, maybe_offset, &mut buffer, non_blocking)
1001            .inspect_err(|err| {
1002                if *err == EPIPE {
1003                    send_standard_signal(current_task, SignalInfo::kernel(SIGPIPE));
1004                }
1005            })?;
1006        if bytes_read > 0 {
1007            buffer.guard.enqueue(Vec::from(buffer.buffer)).inspect_err(|err| {
1008                if *err == EPIPE {
1009                    send_standard_signal(current_task, SignalInfo::kernel(SIGPIPE));
1010                }
1011            })?;
1012        }
1013        Ok(bytes_read)
1014    }
1015
1016    /// Splice from this pipe to the given file handle.
1017    ///
1018    /// The given file handle must not be a pipe; for two pipes use `PipeFileObject::splice_pipes`.
1019    pub fn splice_to(
1020        &self,
1021        current_task: &CurrentTask,
1022        self_file: &FileHandle,
1023        to: &FileHandle,
1024        maybe_offset: Option<usize>,
1025        len: usize,
1026        non_blocking: bool,
1027    ) -> Result<usize, Errno> {
1028        // For pipe-to-pipe transfers, callers must use `PipeFileObject::splice_pipes`.
1029        assert!(to.downcast_file::<PipeFileObject>().is_none());
1030
1031        let non_blocking = non_blocking || self_file.is_non_blocking();
1032        let (read_guard, mut data) = {
1033            let pipe = self.lock_pipe_for_reading(current_task, self_file, non_blocking)?;
1034            let limit = std::cmp::min(len, pipe.messages.len());
1035            let mut data = Vec::with_capacity(limit);
1036            for message in pipe.messages.peek_queue() {
1037                let remaining = limit - data.len();
1038                if remaining == 0 {
1039                    break;
1040                }
1041                message.data.with_bytes(|bytes| {
1042                    let count = std::cmp::min(bytes.len(), remaining);
1043                    data.extend_from_slice(&bytes[..count]);
1044                    Ok(())
1045                })?;
1046            }
1047            if data.is_empty() {
1048                return Ok(0);
1049            }
1050            let reserved_bytes = data.len();
1051            let guard = SpliceReadGuard::new(&self.pipe, pipe, reserved_bytes);
1052            (guard, data)
1053        };
1054        let mut clamped_by_fsize = false;
1055        if to.node().is_reg() {
1056            let fsize_limit = current_task.thread_group().get_rlimit(Resource::FSIZE);
1057            if fsize_limit != uapi::RLIM_INFINITY as u64 {
1058                let out_offset = match maybe_offset {
1059                    Some(off) => off as u64,
1060                    None => to.offset.read().max(0) as u64,
1061                };
1062                if out_offset >= fsize_limit {
1063                    send_standard_signal(current_task, SignalInfo::kernel(SIGXFSZ));
1064                    return error!(EFBIG);
1065                }
1066                let allowed = usize::try_from(fsize_limit - out_offset).unwrap_or(usize::MAX);
1067                if data.len() > allowed {
1068                    data.truncate(allowed);
1069                    clamped_by_fsize = true;
1070                }
1071            }
1072        }
1073        let mut buffer =
1074            SpliceInputBuffer { guard: read_guard, buffer: VecInputBuffer::from(data) };
1075        let bytes_written =
1076            to.splice_write(current_task, maybe_offset, &mut buffer, non_blocking)?;
1077        if bytes_written > 0 {
1078            if clamped_by_fsize {
1079                send_standard_signal(current_task, SignalInfo::kernel(SIGXFSZ));
1080            }
1081            buffer.guard.consume(bytes_written);
1082        }
1083        Ok(bytes_written)
1084    }
1085
1086    /// Share the mappings backing the given input buffer into the pipe.
1087    ///
1088    /// Returns the number of bytes enqueued.
1089    pub fn vmsplice_from(
1090        &self,
1091        current_task: &CurrentTask,
1092        self_file: &FileHandle,
1093        mut iovec: UserBuffers,
1094        non_blocking: bool,
1095    ) -> Result<usize, Errno> {
1096        let available = UserBuffer::cap_buffers_to_max_rw_count(
1097            current_task.maximum_valid_address().ok_or_else(|| errno!(EINVAL))?,
1098            &mut iovec,
1099        )?;
1100        if available == 0 {
1101            return Ok(0);
1102        }
1103
1104        let mut pipe = self.lock_pipe_for_writing(current_task, self_file, non_blocking, 1)?;
1105        let max_bytes = std::cmp::min(available, pipe.messages.available_capacity());
1106        let mappings = current_task.mm()?.get_mappings_for_vmsplice(&iovec, max_bytes)?;
1107
1108        let mut bytes_transferred = 0;
1109        for mapping in mappings.into_iter() {
1110            let actual = mapping.len();
1111            pipe.messages.write_message(PipeMessageData::Vmspliced(mapping).into());
1112            bytes_transferred += actual;
1113        }
1114        if bytes_transferred > 0 {
1115            pipe.notify_fd_events(FdEvents::POLLIN);
1116        }
1117        Ok(bytes_transferred)
1118    }
1119
1120    /// Copy data from the pipe to the given output buffer.
1121    ///
1122    /// Returns the number of bytes transferred.
1123    pub fn vmsplice_to(
1124        &self,
1125        current_task: &CurrentTask,
1126        self_file: &FileHandle,
1127        iovec: UserBuffers,
1128        non_blocking: bool,
1129    ) -> Result<usize, Errno> {
1130        let mut data = UserBuffersOutputBuffer::unified_new(current_task, iovec)?;
1131        if data.available() == 0 {
1132            return Ok(0);
1133        }
1134        let mut pipe = self.lock_pipe_for_reading(current_task, self_file, non_blocking)?;
1135        let was_writable = pipe.is_writable(uapi::PIPE_BUF as usize);
1136        let actual = pipe.read(&mut data)?;
1137        if actual > 0 {
1138            pipe.notify_write_space(was_writable);
1139        }
1140        Ok(actual)
1141    }
1142
1143    /// Copies or moves messages from `pipe_in` to `pipe_out`, locking both pipes in memory-address
1144    /// order via `starnix_sync::ordered_lock`.
1145    fn copy_pipes(
1146        current_task: &CurrentTask,
1147        file_in: &FileHandle,
1148        pipe_in: &PipeFileObject,
1149        file_out: &FileHandle,
1150        pipe_out: &PipeFileObject,
1151        len: usize,
1152        non_blocking: bool,
1153        consume_source: bool,
1154    ) -> Result<usize, Errno> {
1155        // Reject splicing/teeing a pipe to itself before calling ordered_lock,
1156        // because ordered_lock on identical LockDepMutex instances deadlocks.
1157        if Arc::ptr_eq(&pipe_in.pipe, &pipe_out.pipe) {
1158            return error!(EINVAL);
1159        }
1160        if len == 0 {
1161            return Ok(0);
1162        }
1163
1164        let non_blocking = non_blocking || file_in.is_non_blocking() || file_out.is_non_blocking();
1165        let mut waiting_on_out = false;
1166
1167        let (mut from, mut to) = loop {
1168            let (pipe_in_guard, pipe_out_guard) = ordered_lock(&*pipe_in.pipe, &*pipe_out.pipe);
1169
1170            if pipe_in_guard.splice_read_in_progress || pipe_in_guard.splice_write_in_progress {
1171                let waiter = Waiter::new();
1172                let key = if pipe_in_guard.splice_read_in_progress {
1173                    SPLICE_READ_WAIT_KEY
1174                } else {
1175                    SPLICE_WRITE_WAIT_KEY
1176                };
1177                pipe_in_guard.waiters.wait_async_value(&waiter, key);
1178                std::mem::drop(pipe_in_guard);
1179                std::mem::drop(pipe_out_guard);
1180                waiter.wait(current_task)?;
1181                continue;
1182            }
1183
1184            if !waiting_on_out && !pipe_in_guard.is_readable() {
1185                if non_blocking {
1186                    return error!(EAGAIN);
1187                }
1188                let waiter = Waiter::new();
1189                pipe_in_guard.waiters.wait_async_fd_events(
1190                    &waiter,
1191                    FdEvents::POLLIN | FdEvents::POLLHUP,
1192                    WaitCallback::none(),
1193                );
1194                std::mem::drop(pipe_in_guard);
1195                std::mem::drop(pipe_out_guard);
1196                waiter.wait(current_task)?;
1197                continue;
1198            }
1199
1200            if pipe_out_guard.splice_read_in_progress || pipe_out_guard.splice_write_in_progress {
1201                let waiter = Waiter::new();
1202                let key = if pipe_out_guard.splice_read_in_progress {
1203                    SPLICE_READ_WAIT_KEY
1204                } else {
1205                    SPLICE_WRITE_WAIT_KEY
1206                };
1207                pipe_out_guard.waiters.wait_async_value(&waiter, key);
1208                std::mem::drop(pipe_in_guard);
1209                std::mem::drop(pipe_out_guard);
1210                waiter.wait(current_task)?;
1211                continue;
1212            }
1213
1214            if pipe_out_guard.reader_count == 0 {
1215                std::mem::drop(pipe_in_guard);
1216                std::mem::drop(pipe_out_guard);
1217                send_standard_signal(current_task, SignalInfo::kernel(SIGPIPE));
1218                return error!(EPIPE);
1219            }
1220
1221            if !pipe_out_guard.is_writable(pipe_out_guard.min_splice_write_bytes()) {
1222                if non_blocking {
1223                    return error!(EAGAIN);
1224                }
1225                waiting_on_out = true;
1226                let waiter = Waiter::new();
1227                pipe_out_guard.waiters.wait_async_fd_events(
1228                    &waiter,
1229                    FdEvents::POLLOUT | FdEvents::POLLERR,
1230                    WaitCallback::none(),
1231                );
1232                pipe_out_guard.waiters.wait_async_value(&waiter, PIPE_WRITE_SPACE_WAIT_KEY);
1233                std::mem::drop(pipe_in_guard);
1234                std::mem::drop(pipe_out_guard);
1235                waiter.wait(current_task)?;
1236                continue;
1237            }
1238
1239            if waiting_on_out && pipe_in_guard.is_empty() {
1240                if pipe_in_guard.is_readable() || !consume_source {
1241                    return Ok(0);
1242                }
1243                waiting_on_out = false;
1244                continue;
1245            }
1246
1247            if pipe_in_guard.is_empty() {
1248                return Ok(0);
1249            }
1250
1251            break (pipe_in_guard, pipe_out_guard);
1252        };
1253
1254        let mut bytes_transferred = 0;
1255        if consume_source {
1256            let was_writable = from.is_writable(uapi::PIPE_BUF as usize);
1257            while bytes_transferred < len && to.messages.available_capacity() > 0 {
1258                let Some(mut message) = from.messages.read_message() else {
1259                    break;
1260                };
1261                let limit =
1262                    std::cmp::min(len - bytes_transferred, to.messages.available_capacity());
1263                if let Some(remaining) = message.split_off(limit) {
1264                    from.messages.write_front(remaining);
1265                }
1266                bytes_transferred += message.len();
1267                to.messages.write_message(message);
1268            }
1269            if bytes_transferred > 0 {
1270                from.notify_write_space(was_writable);
1271                to.notify_fd_events(FdEvents::POLLIN);
1272            }
1273        } else {
1274            let mut cloned_messages = Vec::new();
1275            for message in from.messages.peek_queue() {
1276                let limit = std::cmp::min(
1277                    len - bytes_transferred,
1278                    to.messages.available_capacity() - bytes_transferred,
1279                );
1280                if limit == 0 {
1281                    break;
1282                }
1283                let cloned = message.clone_at_most(limit);
1284                bytes_transferred += cloned.len();
1285                cloned_messages.push(cloned);
1286            }
1287            for message in cloned_messages {
1288                to.messages.write_message(message);
1289            }
1290            if bytes_transferred > 0 {
1291                to.notify_fd_events(FdEvents::POLLIN);
1292            }
1293        }
1294
1295        Ok(bytes_transferred)
1296    }
1297
1298    /// Splice messages from `pipe_in` to `pipe_out`.
1299    pub fn splice_pipes(
1300        current_task: &CurrentTask,
1301        file_in: &FileHandle,
1302        pipe_in: &PipeFileObject,
1303        file_out: &FileHandle,
1304        pipe_out: &PipeFileObject,
1305        len: usize,
1306        non_blocking: bool,
1307    ) -> Result<usize, Errno> {
1308        Self::copy_pipes(
1309            current_task,
1310            file_in,
1311            pipe_in,
1312            file_out,
1313            pipe_out,
1314            len,
1315            non_blocking,
1316            true,
1317        )
1318    }
1319
1320    /// Tee messages from `file_in` to `file_out`.
1321    pub fn tee(
1322        current_task: &CurrentTask,
1323        file_in: &FileHandle,
1324        file_out: &FileHandle,
1325        len: usize,
1326        non_blocking: bool,
1327    ) -> Result<usize, Errno> {
1328        let pipe_in = file_in.downcast_file::<PipeFileObject>().ok_or_else(|| errno!(EINVAL))?;
1329        let pipe_out = file_out.downcast_file::<PipeFileObject>().ok_or_else(|| errno!(EINVAL))?;
1330        Self::copy_pipes(
1331            current_task,
1332            file_in,
1333            &pipe_in,
1334            file_out,
1335            &pipe_out,
1336            len,
1337            non_blocking,
1338            false,
1339        )
1340    }
1341}
1342
1343#[cfg(test)]
1344mod tests {
1345    use super::*;
1346    use crate::mm::memory::MemoryObject;
1347    use crate::mm::{ProtectionFlags, VmsplicePayload, VmsplicePayloadSegment};
1348    use crate::task::dynamic_thread_spawner::SpawnRequestBuilder;
1349    use crate::testing::{anon_test_file, fill_page, map_memory, spawn_kernel_and_run};
1350    use crate::vfs::buffers::UserBuffersInputBuffer;
1351    use crate::vfs::new_memfd;
1352    use smallvec::smallvec;
1353    use starnix_sync::Mutex;
1354    use starnix_uapi::seal_flags::SealFlags;
1355    use std::sync::mpsc;
1356
1357    struct BoundedSinkFile {
1358        max_write: usize,
1359        written: Arc<Mutex<Vec<u8>>>,
1360    }
1361
1362    impl FileOps for BoundedSinkFile {
1363        fileops_impl_nonseekable!();
1364        fileops_impl_noop_sync!();
1365
1366        fn read(
1367            &self,
1368            _file: &FileObject,
1369            _current_task: &CurrentTask,
1370            _offset: usize,
1371            _data: &mut dyn OutputBuffer,
1372        ) -> Result<usize, Errno> {
1373            error!(ENOTSUP)
1374        }
1375
1376        fn write(
1377            &self,
1378            _file: &FileObject,
1379            _current_task: &CurrentTask,
1380            _offset: usize,
1381            data: &mut dyn InputBuffer,
1382        ) -> Result<usize, Errno> {
1383            let mut remaining = self.max_write;
1384            data.read_each(&mut |slice| {
1385                let count = std::cmp::min(slice.len(), remaining);
1386                self.written.lock().extend_from_slice(&slice[..count]);
1387                remaining -= count;
1388                Ok(count)
1389            })
1390        }
1391    }
1392
1393    struct BarrierSinkFile {
1394        entered_tx: Mutex<Option<mpsc::Sender<()>>>,
1395        resume_rx: Mutex<Option<mpsc::Receiver<()>>>,
1396        max_write: usize,
1397        written: Mutex<Vec<u8>>,
1398    }
1399
1400    impl FileOps for BarrierSinkFile {
1401        fileops_impl_nonseekable!();
1402        fileops_impl_noop_sync!();
1403
1404        fn read(
1405            &self,
1406            _file: &FileObject,
1407            _current_task: &CurrentTask,
1408            _offset: usize,
1409            _data: &mut dyn OutputBuffer,
1410        ) -> Result<usize, Errno> {
1411            error!(ENOTSUP)
1412        }
1413
1414        fn write(
1415            &self,
1416            _file: &FileObject,
1417            _current_task: &CurrentTask,
1418            _offset: usize,
1419            data: &mut dyn InputBuffer,
1420        ) -> Result<usize, Errno> {
1421            if let Some(tx) = self.entered_tx.lock().take() {
1422                tx.send(()).expect("notify entered write");
1423            }
1424            if let Some(rx) = self.resume_rx.lock().take() {
1425                rx.recv().expect("wait to resume write");
1426            }
1427            let mut remaining = self.max_write;
1428            data.read_each(&mut |slice| {
1429                let count = std::cmp::min(slice.len(), remaining);
1430                self.written.lock().extend_from_slice(&slice[..count]);
1431                remaining -= count;
1432                Ok(count)
1433            })
1434        }
1435    }
1436
1437    struct StaticSourceFile {
1438        payload: &'static [u8],
1439    }
1440
1441    impl FileOps for StaticSourceFile {
1442        fileops_impl_nonseekable!();
1443        fileops_impl_noop_sync!();
1444
1445        fn read(
1446            &self,
1447            _file: &FileObject,
1448            _current_task: &CurrentTask,
1449            _offset: usize,
1450            data: &mut dyn OutputBuffer,
1451        ) -> Result<usize, Errno> {
1452            data.write(self.payload)
1453        }
1454
1455        fn write(
1456            &self,
1457            _file: &FileObject,
1458            _current_task: &CurrentTask,
1459            _offset: usize,
1460            _data: &mut dyn InputBuffer,
1461        ) -> Result<usize, Errno> {
1462            error!(ENOTSUP)
1463        }
1464    }
1465
1466    struct BarrierSourceFile {
1467        entered_tx: Mutex<Option<mpsc::Sender<()>>>,
1468        resume_rx: Mutex<Option<mpsc::Receiver<()>>>,
1469        payload: &'static [u8],
1470    }
1471
1472    impl FileOps for BarrierSourceFile {
1473        fileops_impl_nonseekable!();
1474        fileops_impl_noop_sync!();
1475
1476        fn read(
1477            &self,
1478            _file: &FileObject,
1479            _current_task: &CurrentTask,
1480            _offset: usize,
1481            data: &mut dyn OutputBuffer,
1482        ) -> Result<usize, Errno> {
1483            if let Some(tx) = self.entered_tx.lock().take() {
1484                tx.send(()).expect("notify entered read");
1485            }
1486            if let Some(rx) = self.resume_rx.lock().take() {
1487                rx.recv().expect("wait to resume read");
1488            }
1489            data.write(self.payload)
1490        }
1491
1492        fn write(
1493            &self,
1494            _file: &FileObject,
1495            _current_task: &CurrentTask,
1496            _offset: usize,
1497            _data: &mut dyn InputBuffer,
1498        ) -> Result<usize, Errno> {
1499            error!(ENOTSUP)
1500        }
1501    }
1502
1503    #[::fuchsia::test]
1504    async fn test_splice_to_partial_write_preserves_fifo_order() {
1505        spawn_kernel_and_run(async |current_task| {
1506            let kernel = current_task.kernel();
1507            register_pipe_fs(kernel.expando.get::<FsRegistry>().as_ref());
1508
1509            // Part 1: Single-thread partial write across `[MsgA("AAAA"), MsgB("BBBB")]`.
1510            let (pipe_out, pipe_in) = new_pipe(&current_task).unwrap();
1511            assert_eq!(pipe_in.write(&current_task, &mut VecInputBuffer::new(b"AAAA")).unwrap(), 4);
1512            assert_eq!(pipe_in.write(&current_task, &mut VecInputBuffer::new(b"BBBB")).unwrap(), 4);
1513
1514            let sink_written = Arc::new(Mutex::new(Vec::new()));
1515            let sink_file = anon_test_file(
1516                &current_task,
1517                Box::new(BoundedSinkFile { max_write: 2, written: Arc::clone(&sink_written) }),
1518                OpenFlags::WRONLY,
1519            );
1520            let spliced = pipe_out
1521                .downcast_file::<PipeFileObject>()
1522                .unwrap()
1523                .splice_to(&current_task, &pipe_out, &sink_file, None, 6, true)
1524                .unwrap();
1525            assert_eq!(spliced, 2);
1526            assert_eq!(*sink_written.lock(), b"AA");
1527
1528            let mut remaining = Vec::new();
1529            loop {
1530                let mut out = VecOutputBuffer::new(16);
1531                match pipe_out.read(&current_task, &mut out) {
1532                    Ok(n) if n > 0 => {
1533                        remaining.extend_from_slice(&out.data()[..n]);
1534                        if remaining.len() == 6 {
1535                            break;
1536                        }
1537                    }
1538                    _ => break,
1539                }
1540            }
1541            assert_eq!(remaining, b"AABBBB");
1542
1543            // Part 2: Concurrent reader + blocked partial `splice_to` on `[MsgA("AAAA"), MsgB("BBBB")]`.
1544            let (pipe_out, pipe_in) = new_pipe(&current_task).unwrap();
1545            assert_eq!(pipe_in.write(&current_task, &mut VecInputBuffer::new(b"AAAA")).unwrap(), 4);
1546            assert_eq!(pipe_in.write(&current_task, &mut VecInputBuffer::new(b"BBBB")).unwrap(), 4);
1547
1548            let (entered_tx, entered_rx) = mpsc::channel();
1549            let (resume_tx, resume_rx) = mpsc::channel();
1550            let barrier_sink = anon_test_file(
1551                &current_task,
1552                Box::new(BarrierSinkFile {
1553                    entered_tx: Mutex::new(Some(entered_tx)),
1554                    resume_rx: Mutex::new(Some(resume_rx)),
1555                    max_write: 2,
1556                    written: Mutex::new(Vec::new()),
1557                }),
1558                OpenFlags::WRONLY,
1559            );
1560
1561            let (reader_ready_tx, reader_ready_rx) = mpsc::channel();
1562            let pipe_out_unblock = pipe_out.clone();
1563            let (unblock_join, unblock_req) = SpawnRequestBuilder::new()
1564                .with_sync_closure(move |_worker_task: &CurrentTask| {
1565                    reader_ready_rx.recv().expect("wait until non-blocking reader starts");
1566                    while pipe_out_unblock
1567                        .downcast_file::<PipeFileObject>()
1568                        .unwrap()
1569                        .pipe
1570                        .lock()
1571                        .waiters
1572                        .is_empty()
1573                    {
1574                        std::thread::yield_now();
1575                    }
1576                    resume_tx.send(()).expect("allow splice_to write to complete");
1577                })
1578                .build_with_sync_result();
1579            kernel.kthreads.spawner().spawn_from_request(unblock_req);
1580
1581            let pipe_out_clone = pipe_out.clone();
1582            let (reader_join, req) = SpawnRequestBuilder::new()
1583                .with_sync_closure(move |worker_task: &CurrentTask| {
1584                    entered_rx.recv().expect("wait until splice_to enters write");
1585                    {
1586                        let pipe =
1587                            pipe_out_clone.downcast_file::<PipeFileObject>().unwrap().pipe.lock();
1588                        assert!(pipe.splice_read_in_progress);
1589                        assert_eq!(pipe.messages.len(), 8);
1590                    }
1591                    reader_ready_tx.send(()).expect("signal unblocker");
1592                    // A non-blocking read waits for the active splice read to finish and then reads
1593                    // the remaining bytes in FIFO order.
1594                    let mut pipe = pipe_out_clone
1595                        .downcast_file::<PipeFileObject>()
1596                        .unwrap()
1597                        .lock_pipe_for_reading(worker_task, &pipe_out_clone, true)
1598                        .expect("non-blocking read lock succeeds once splice_to finishes");
1599                    let mut buf = VecOutputBuffer::new(4);
1600                    let n = pipe.read(&mut buf).unwrap();
1601                    buf.data()[..n].to_vec()
1602                })
1603                .build_with_sync_result();
1604            kernel.kthreads.spawner().spawn_from_request(req);
1605
1606            let spliced = pipe_out
1607                .downcast_file::<PipeFileObject>()
1608                .unwrap()
1609                .splice_to(&current_task, &pipe_out, &barrier_sink, None, 8, true)
1610                .unwrap();
1611            assert_eq!(spliced, 2);
1612            unblock_join().unwrap();
1613
1614            // Concurrent 4-byte stream read receives `"AA"` (from `MsgA`) followed by `"BB"` (from `MsgB`).
1615            let concurrent_bytes = reader_join().unwrap();
1616            assert_eq!(concurrent_bytes, b"AABB");
1617
1618            // The remaining `"BB"` tail of `MsgB` follows in FIFO order.
1619            let mut tail_buf = VecOutputBuffer::new(4);
1620            let tail_len = pipe_out.read(&current_task, &mut tail_buf).unwrap();
1621            assert_eq!(&tail_buf.data()[..tail_len], b"BB");
1622        })
1623        .await;
1624    }
1625
1626    #[::fuchsia::test]
1627    async fn test_splice_from_serializes_concurrent_writers() {
1628        spawn_kernel_and_run(async |current_task| {
1629            let kernel = current_task.kernel();
1630            register_pipe_fs(kernel.expando.get::<FsRegistry>().as_ref());
1631
1632            let (pipe_out, pipe_in) = new_pipe(&current_task).unwrap();
1633            let capacity =
1634                pipe_in.downcast_file::<PipeFileObject>().unwrap().pipe.lock().capacity();
1635            let prefix = vec![b'P'; capacity - 8];
1636            assert_eq!(
1637                pipe_in.write(&current_task, &mut VecInputBuffer::new(&prefix)).unwrap(),
1638                capacity - 8
1639            );
1640
1641            let (entered_tx, entered_rx) = mpsc::channel();
1642            let (resume_tx, resume_rx) = mpsc::channel();
1643            let barrier_source = anon_test_file(
1644                &current_task,
1645                Box::new(BarrierSourceFile {
1646                    entered_tx: Mutex::new(Some(entered_tx)),
1647                    resume_rx: Mutex::new(Some(resume_rx)),
1648                    payload: b"SSSS",
1649                }),
1650                OpenFlags::RDONLY,
1651            );
1652
1653            let (writer_ready_tx, writer_ready_rx) = mpsc::channel();
1654            let pipe_in_unblock = pipe_in.clone();
1655            let (unblock_join, unblock_req) = SpawnRequestBuilder::new()
1656                .with_sync_closure(move |_worker_task: &CurrentTask| {
1657                    writer_ready_rx.recv().expect("wait until non-blocking writer starts");
1658                    while pipe_in_unblock
1659                        .downcast_file::<PipeFileObject>()
1660                        .unwrap()
1661                        .pipe
1662                        .lock()
1663                        .waiters
1664                        .is_empty()
1665                    {
1666                        std::thread::yield_now();
1667                    }
1668                    resume_tx.send(()).expect("allow splice_from read to complete");
1669                })
1670                .build_with_sync_result();
1671            kernel.kthreads.spawner().spawn_from_request(unblock_req);
1672
1673            let pipe_in_clone = pipe_in.clone();
1674            let (writer_join, req) = SpawnRequestBuilder::new()
1675                .with_sync_closure(move |worker_task: &CurrentTask| {
1676                    entered_rx.recv().expect("wait until splice_from enters read");
1677                    {
1678                        let pipe =
1679                            pipe_in_clone.downcast_file::<PipeFileObject>().unwrap().pipe.lock();
1680                        assert!(pipe.splice_write_in_progress);
1681                        assert_eq!(pipe.messages.available_capacity(), 8);
1682                    }
1683                    writer_ready_tx.send(()).expect("signal unblocker");
1684                    // A non-blocking write waits for the active splice write to finish and then
1685                    // writes into the remaining 4 bytes of capacity in FIFO order after `SSSS`.
1686                    let mut pipe = pipe_in_clone
1687                        .downcast_file::<PipeFileObject>()
1688                        .unwrap()
1689                        .lock_pipe_for_writing(worker_task, &pipe_in_clone, true, 4)
1690                        .expect("non-blocking write lock succeeds once splice_from finishes");
1691                    assert_eq!(pipe.write(&mut VecInputBuffer::new(b"WWWW")).unwrap(), 4);
1692                    std::mem::drop(pipe);
1693
1694                    std::mem::drop(
1695                        pipe_in_clone
1696                            .downcast_file::<PipeFileObject>()
1697                            .unwrap()
1698                            .lock_pipe_without_splice(worker_task)
1699                            .unwrap(),
1700                    );
1701                    // Once both 4-byte writes finish, the pipe is full (`EAGAIN`).
1702                    pipe_in_clone
1703                        .downcast_file::<PipeFileObject>()
1704                        .unwrap()
1705                        .lock_pipe_for_writing(worker_task, &pipe_in_clone, true, 4)
1706                        .err()
1707                        .expect("pipe should be full once splice_from and concurrent write finish")
1708                })
1709                .build_with_sync_result();
1710            kernel.kthreads.spawner().spawn_from_request(req);
1711
1712            let spliced = pipe_in
1713                .downcast_file::<PipeFileObject>()
1714                .unwrap()
1715                .splice_from(&current_task, &pipe_in, &barrier_source, None, 4, true)
1716                .unwrap();
1717            assert_eq!(spliced, 4);
1718            unblock_join().unwrap();
1719            assert_eq!(writer_join().unwrap(), errno!(EAGAIN));
1720
1721            let mut pre_buf = VecOutputBuffer::new(capacity - 8);
1722            assert_eq!(pipe_out.read(&current_task, &mut pre_buf).unwrap(), capacity - 8);
1723            let mut spliced_buf = VecOutputBuffer::new(8);
1724            assert_eq!(pipe_out.read(&current_task, &mut spliced_buf).unwrap(), 8);
1725            assert_eq!(spliced_buf.data(), b"SSSSWWWW");
1726        })
1727        .await;
1728    }
1729
1730    #[::fuchsia::test]
1731    async fn test_splice_from_closed_reader_returns_epipe_and_queues_sigpipe() {
1732        spawn_kernel_and_run(async |current_task| {
1733            let kernel = current_task.kernel();
1734            register_pipe_fs(kernel.expando.get::<FsRegistry>().as_ref());
1735
1736            let (pipe_out, pipe_in) = new_pipe(&current_task).unwrap();
1737            std::mem::drop(pipe_out);
1738            current_task.trigger_delayed_releaser();
1739
1740            let source_file = anon_test_file(
1741                &current_task,
1742                Box::new(StaticSourceFile { payload: b"DATA" }),
1743                OpenFlags::RDONLY,
1744            );
1745            let err = pipe_in
1746                .downcast_file::<PipeFileObject>()
1747                .unwrap()
1748                .splice_from(&current_task, &pipe_in, &source_file, None, 4, true)
1749                .unwrap_err();
1750            assert_eq!(err, errno!(EPIPE));
1751            assert!(current_task.write().signals_mut().has_queued(SIGPIPE));
1752        })
1753        .await;
1754    }
1755
1756    #[::fuchsia::test]
1757    async fn test_copy_pipes_with_vmspliced_payload() {
1758        spawn_kernel_and_run(async |current_task| {
1759            let kernel = current_task.kernel();
1760            register_pipe_fs(kernel.expando.get::<FsRegistry>().as_ref());
1761
1762            let (pipe_a_out, pipe_a_in) = new_pipe(&current_task).unwrap();
1763            let (pipe_b_out, pipe_b_in) = new_pipe(&current_task).unwrap();
1764
1765            let mm = current_task.mm().unwrap();
1766            let page_size = *PAGE_SIZE as usize;
1767            let memory = Arc::new(MemoryObject::from(zx::Vmo::create(page_size as u64).unwrap()));
1768            memory.write(b"VMSPLICED_DATA", 0).unwrap();
1769            let payload = VmsplicePayload::new(
1770                Arc::downgrade(&mm),
1771                VmsplicePayloadSegment {
1772                    addr_offset: UserAddress::NULL,
1773                    length: 14,
1774                    memory,
1775                    memory_offset: 0,
1776                    should_snapshot_on_unmap: true,
1777                },
1778            );
1779            pipe_a_in
1780                .downcast_file::<PipeFileObject>()
1781                .unwrap()
1782                .pipe
1783                .lock()
1784                .messages
1785                .write_message(PipeMessageData::Vmspliced(payload).into());
1786
1787            let copied = PipeFileObject::copy_pipes(
1788                &current_task,
1789                &pipe_a_out,
1790                &pipe_a_out.downcast_file::<PipeFileObject>().unwrap(),
1791                &pipe_b_in,
1792                &pipe_b_in.downcast_file::<PipeFileObject>().unwrap(),
1793                10,
1794                false,
1795                false,
1796            )
1797            .unwrap();
1798            assert_eq!(copied, 10);
1799
1800            let mut buf_b = VecOutputBuffer::new(10);
1801            assert_eq!(pipe_b_out.read(&current_task, &mut buf_b).unwrap(), 10);
1802            assert_eq!(buf_b.data(), b"VMSPLICED_");
1803
1804            let mut buf_a = VecOutputBuffer::new(14);
1805            assert_eq!(pipe_a_out.read(&current_task, &mut buf_a).unwrap(), 14);
1806            assert_eq!(buf_a.data(), b"VMSPLICED_DATA");
1807        })
1808        .await;
1809    }
1810    #[::fuchsia::test]
1811    async fn test_splice_from_reader_closes_during_read_returns_epipe() {
1812        spawn_kernel_and_run(async |current_task| {
1813            let kernel = current_task.kernel();
1814            register_pipe_fs(kernel.expando.get::<FsRegistry>().as_ref());
1815
1816            let (pipe_out, pipe_in) = new_pipe(&current_task).unwrap();
1817            let (entered_tx, entered_rx) = mpsc::channel();
1818            let (resume_tx, resume_rx) = mpsc::channel();
1819            let barrier_source = anon_test_file(
1820                &current_task,
1821                Box::new(BarrierSourceFile {
1822                    entered_tx: Mutex::new(Some(entered_tx)),
1823                    resume_rx: Mutex::new(Some(resume_rx)),
1824                    payload: b"DATA",
1825                }),
1826                OpenFlags::RDONLY,
1827            );
1828
1829            let (closer_join, req) = SpawnRequestBuilder::new()
1830                .with_sync_closure(move |worker_task: &CurrentTask| {
1831                    entered_rx.recv().expect("wait until splice_from enters read");
1832                    std::mem::drop(pipe_out);
1833                    worker_task.trigger_delayed_releaser();
1834                    resume_tx.send(()).expect("allow splice_from read to complete");
1835                })
1836                .build_with_sync_result();
1837            kernel.kthreads.spawner().spawn_from_request(req);
1838
1839            let err = pipe_in
1840                .downcast_file::<PipeFileObject>()
1841                .unwrap()
1842                .splice_from(&current_task, &pipe_in, &barrier_source, None, 4, true)
1843                .unwrap_err();
1844            closer_join().unwrap();
1845            assert_eq!(err, errno!(EPIPE));
1846            assert!(current_task.write().signals_mut().has_queued(SIGPIPE));
1847            assert_eq!(
1848                pipe_in.downcast_file::<PipeFileObject>().unwrap().pipe.lock().messages.len(),
1849                0
1850            );
1851        })
1852        .await;
1853    }
1854
1855    #[::fuchsia::test]
1856    async fn test_partial_read_wakes_blocked_writer() {
1857        spawn_kernel_and_run(async |current_task| {
1858            let kernel = current_task.kernel();
1859            register_pipe_fs(kernel.expando.get::<FsRegistry>().as_ref());
1860
1861            let (pipe_out, pipe_in) = new_pipe(&current_task).unwrap();
1862            let capacity =
1863                pipe_in.downcast_file::<PipeFileObject>().unwrap().pipe.lock().capacity();
1864            let fill = vec![b'A'; capacity];
1865            assert_eq!(
1866                pipe_in.write(&current_task, &mut VecInputBuffer::new(&fill)).unwrap(),
1867                capacity
1868            );
1869
1870            let pipe_in_clone = pipe_in.clone();
1871            let (writer_join, req) = SpawnRequestBuilder::new()
1872                .with_sync_closure(move |worker_task: &CurrentTask| {
1873                    pipe_in_clone
1874                        .write(worker_task, &mut VecInputBuffer::new(b"BBBB"))
1875                        .expect("blocked write should succeed after partial read")
1876                })
1877                .build_with_sync_result();
1878            kernel.kthreads.spawner().spawn_from_request(req);
1879
1880            while pipe_in.downcast_file::<PipeFileObject>().unwrap().pipe.lock().waiters.is_empty()
1881            {
1882                std::thread::yield_now();
1883            }
1884
1885            // Read one page from the full pipe; the pipe is still non-empty, but has space.
1886            let page_size = *PAGE_SIZE as usize;
1887            let mut partial = VecOutputBuffer::new(page_size);
1888            assert_eq!(pipe_out.read(&current_task, &mut partial).unwrap(), page_size);
1889            assert_eq!(partial.data(), &fill[..page_size]);
1890
1891            assert_eq!(writer_join().unwrap(), 4);
1892        })
1893        .await;
1894    }
1895
1896    struct WouldBlockEndpointFile;
1897
1898    impl FileOps for WouldBlockEndpointFile {
1899        fileops_impl_nonseekable!();
1900        fileops_impl_noop_sync!();
1901
1902        fn read(
1903            &self,
1904            file: &FileObject,
1905            current_task: &CurrentTask,
1906            _offset: usize,
1907            _data: &mut dyn OutputBuffer,
1908        ) -> Result<usize, Errno> {
1909            file.blocking_op(current_task, FdEvents::POLLIN, None, || error!(EAGAIN))
1910        }
1911
1912        fn write(
1913            &self,
1914            file: &FileObject,
1915            current_task: &CurrentTask,
1916            _offset: usize,
1917            _data: &mut dyn InputBuffer,
1918        ) -> Result<usize, Errno> {
1919            file.blocking_op(current_task, FdEvents::POLLOUT, None, || error!(EAGAIN))
1920        }
1921
1922        fn wait_async(
1923            &self,
1924            _file: &FileObject,
1925            _current_task: &CurrentTask,
1926            _waiter: &Waiter,
1927            _events: FdEvents,
1928            _handler: EventHandler,
1929        ) -> Option<WaitCanceler> {
1930            Some(WaitCanceler::new_noop())
1931        }
1932    }
1933
1934    #[::fuchsia::test]
1935    async fn test_splice_non_blocking_unix_stream_and_pipe_flags() {
1936        spawn_kernel_and_run(async |current_task| {
1937            let kernel = current_task.kernel();
1938            register_pipe_fs(kernel.expando.get::<FsRegistry>().as_ref());
1939
1940            let (pipe_out, pipe_in) = new_pipe(&current_task).unwrap();
1941            let (unix_sock, unix_peer) = crate::vfs::socket::UnixSocket::new_pair(
1942                &current_task,
1943                crate::vfs::socket::SocketDomain::Unix,
1944                crate::vfs::socket::SocketType::Stream,
1945                OpenFlags::RDWR,
1946            )
1947            .unwrap();
1948            let blocking_sink =
1949                anon_test_file(&current_task, Box::new(WouldBlockEndpointFile), OpenFlags::WRONLY);
1950
1951            // SPLICE_F_NONBLOCK (`non_blocking = true`) makes `splice_from` on an empty blocking
1952            // AF_UNIX stream socket return EAGAIN without marking the socket itself non-blocking.
1953            assert_eq!(
1954                pipe_in
1955                    .downcast_file::<PipeFileObject>()
1956                    .unwrap()
1957                    .splice_from(&current_task, &pipe_in, &unix_sock, None, 4, true)
1958                    .unwrap_err(),
1959                errno!(EAGAIN)
1960            );
1961            assert!(!unix_sock.is_non_blocking());
1962
1963            // Setting O_NONBLOCK on the pipe write end also makes `splice_from` on an empty
1964            // blocking AF_UNIX stream socket return EAGAIN even when `non_blocking = false`.
1965            pipe_in.update_file_flags(OpenFlags::NONBLOCK, OpenFlags::NONBLOCK);
1966            assert_eq!(
1967                pipe_in
1968                    .downcast_file::<PipeFileObject>()
1969                    .unwrap()
1970                    .splice_from(&current_task, &pipe_in, &unix_sock, None, 4, false)
1971                    .unwrap_err(),
1972                errno!(EAGAIN)
1973            );
1974            assert!(!unix_sock.is_non_blocking());
1975
1976            // Data written to the peer splices into the pipe and subsequent empty splice_from
1977            // still returns EAGAIN without mutating the socket's O_NONBLOCK flag.
1978            assert_eq!(
1979                unix_peer.write(&current_task, &mut VecInputBuffer::new(b"ABCD")).unwrap(),
1980                4
1981            );
1982            assert_eq!(
1983                pipe_in
1984                    .downcast_file::<PipeFileObject>()
1985                    .unwrap()
1986                    .splice_from(&current_task, &pipe_in, &unix_sock, None, 4, false)
1987                    .unwrap(),
1988                4
1989            );
1990            let mut read_buf = VecOutputBuffer::new(4);
1991            assert_eq!(pipe_out.read(&current_task, &mut read_buf).unwrap(), 4);
1992            assert_eq!(read_buf.data(), b"ABCD");
1993            assert!(!unix_sock.is_non_blocking());
1994
1995            // Setting O_NONBLOCK on the pipe read end makes `splice_to` on an empty pipe return
1996            // EAGAIN even when `non_blocking = false`.
1997            pipe_out.update_file_flags(OpenFlags::NONBLOCK, OpenFlags::NONBLOCK);
1998            assert_eq!(
1999                pipe_out
2000                    .downcast_file::<PipeFileObject>()
2001                    .unwrap()
2002                    .splice_to(&current_task, &pipe_out, &blocking_sink, None, 4, false)
2003                    .unwrap_err(),
2004                errno!(EAGAIN)
2005            );
2006        })
2007        .await;
2008    }
2009
2010    #[::fuchsia::test]
2011    async fn test_fcntl_getpipe_sz_and_setpipe_sz_during_active_splice() {
2012        spawn_kernel_and_run(async |current_task| {
2013            let kernel = current_task.kernel();
2014            register_pipe_fs(kernel.expando.get::<FsRegistry>().as_ref());
2015
2016            let (pipe_out, pipe_in) = new_pipe(&current_task).unwrap();
2017            assert_eq!(pipe_in.write(&current_task, &mut VecInputBuffer::new(b"DATA")).unwrap(), 4);
2018            let initial_capacity =
2019                pipe_out.downcast_file::<PipeFileObject>().unwrap().pipe.lock().capacity();
2020
2021            // 1. Both F_GETPIPE_SZ and F_SETPIPE_SZ serialize behind an active `splice_to`.
2022            let (entered_tx, entered_rx) = mpsc::channel();
2023            let (resume_tx, resume_rx) = mpsc::channel();
2024            let barrier_sink = anon_test_file(
2025                &current_task,
2026                Box::new(BarrierSinkFile {
2027                    entered_tx: Mutex::new(Some(entered_tx)),
2028                    resume_rx: Mutex::new(Some(resume_rx)),
2029                    max_write: 4,
2030                    written: Mutex::new(Vec::new()),
2031                }),
2032                OpenFlags::WRONLY,
2033            );
2034
2035            let (ready_tx, ready_rx) = mpsc::channel();
2036            let pipe_out_unblock = pipe_out.clone();
2037            let (unblock_join, unblock_req) = SpawnRequestBuilder::new()
2038                .with_sync_closure(move |_worker_task: &CurrentTask| {
2039                    ready_rx.recv().expect("wait until F_GETPIPE_SZ starts");
2040                    while pipe_out_unblock
2041                        .downcast_file::<PipeFileObject>()
2042                        .unwrap()
2043                        .pipe
2044                        .lock()
2045                        .waiters
2046                        .is_empty()
2047                    {
2048                        std::thread::yield_now();
2049                    }
2050                    resume_tx.send(()).expect("allow splice_to write to complete");
2051                })
2052                .build_with_sync_result();
2053            kernel.kthreads.spawner().spawn_from_request(unblock_req);
2054
2055            let pipe_out_clone = pipe_out.clone();
2056            let (fcntl_join, req) = SpawnRequestBuilder::new()
2057                .with_sync_closure(move |worker_task: &CurrentTask| {
2058                    entered_rx.recv().expect("wait until splice_to enters write");
2059                    ready_tx.send(()).expect("signal unblocker");
2060                    // F_GETPIPE_SZ waits for the active splice read to finish.
2061                    let got_sz =
2062                        pipe_out_clone.fcntl(worker_task, F_GETPIPE_SZ, 0).expect("F_GETPIPE_SZ");
2063                    assert_eq!(got_sz, initial_capacity.into());
2064
2065                    let new_sz = (*PAGE_SIZE as u64) * 2;
2066                    pipe_out_clone
2067                        .fcntl(worker_task, F_SETPIPE_SZ, new_sz)
2068                        .expect("F_SETPIPE_SZ after splice_to")
2069                })
2070                .build_with_sync_result();
2071            kernel.kthreads.spawner().spawn_from_request(req);
2072
2073            let spliced = pipe_out
2074                .downcast_file::<PipeFileObject>()
2075                .unwrap()
2076                .splice_to(&current_task, &pipe_out, &barrier_sink, None, 4, true)
2077                .unwrap();
2078            assert_eq!(spliced, 4);
2079            unblock_join().unwrap();
2080            assert_eq!(fcntl_join().unwrap(), ((*PAGE_SIZE as usize) * 2).into());
2081
2082            // 2. Both F_SETPIPE_SZ and F_GETPIPE_SZ serialize behind an active `splice_from`.
2083            let (entered_tx2, entered_rx2) = mpsc::channel();
2084            let (resume_tx2, resume_rx2) = mpsc::channel();
2085            let barrier_source = anon_test_file(
2086                &current_task,
2087                Box::new(BarrierSourceFile {
2088                    entered_tx: Mutex::new(Some(entered_tx2)),
2089                    resume_rx: Mutex::new(Some(resume_rx2)),
2090                    payload: b"DATA",
2091                }),
2092                OpenFlags::RDONLY,
2093            );
2094
2095            let (ready_tx2, ready_rx2) = mpsc::channel();
2096            let pipe_in_unblock = pipe_in.clone();
2097            let (unblock_join2, unblock_req2) = SpawnRequestBuilder::new()
2098                .with_sync_closure(move |_worker_task: &CurrentTask| {
2099                    ready_rx2.recv().expect("wait until F_SETPIPE_SZ starts");
2100                    while pipe_in_unblock
2101                        .downcast_file::<PipeFileObject>()
2102                        .unwrap()
2103                        .pipe
2104                        .lock()
2105                        .waiters
2106                        .is_empty()
2107                    {
2108                        std::thread::yield_now();
2109                    }
2110                    resume_tx2.send(()).expect("allow splice_from read to complete");
2111                })
2112                .build_with_sync_result();
2113            kernel.kthreads.spawner().spawn_from_request(unblock_req2);
2114
2115            let pipe_in_clone = pipe_in.clone();
2116            let (fcntl_join2, req2) = SpawnRequestBuilder::new()
2117                .with_sync_closure(move |worker_task: &CurrentTask| {
2118                    entered_rx2.recv().expect("wait until splice_from enters read");
2119                    ready_tx2.send(()).expect("signal unblocker");
2120                    let new_sz = (*PAGE_SIZE as u64) * 4;
2121                    let set_sz = pipe_in_clone
2122                        .fcntl(worker_task, F_SETPIPE_SZ, new_sz)
2123                        .expect("F_SETPIPE_SZ after splice_from");
2124                    assert_eq!(set_sz, ((*PAGE_SIZE as usize) * 4).into());
2125                    pipe_in_clone.fcntl(worker_task, F_GETPIPE_SZ, 0).expect("F_GETPIPE_SZ")
2126                })
2127                .build_with_sync_result();
2128            kernel.kthreads.spawner().spawn_from_request(req2);
2129
2130            let spliced2 = pipe_in
2131                .downcast_file::<PipeFileObject>()
2132                .unwrap()
2133                .splice_from(&current_task, &pipe_in, &barrier_source, None, 4, true)
2134                .unwrap();
2135            assert_eq!(spliced2, 4);
2136            unblock_join2().unwrap();
2137            assert_eq!(fcntl_join2().unwrap(), ((*PAGE_SIZE as usize) * 4).into());
2138        })
2139        .await;
2140    }
2141
2142    #[::fuchsia::test]
2143    async fn test_splice_short_transfer_when_available_capacity_below_pipe_buf() {
2144        spawn_kernel_and_run(async |current_task| {
2145            let kernel = current_task.kernel();
2146            register_pipe_fs(kernel.expando.get::<FsRegistry>().as_ref());
2147
2148            let (pipe_out, pipe_in) = new_pipe(&current_task).unwrap();
2149            let capacity =
2150                pipe_in.downcast_file::<PipeFileObject>().unwrap().pipe.lock().capacity();
2151            let fill = vec![b'P'; capacity - 4];
2152            assert_eq!(
2153                pipe_in.write(&current_task, &mut VecInputBuffer::new(&fill)).unwrap(),
2154                capacity - 4
2155            );
2156
2157            // Requesting 8 bytes (`< PIPE_BUF`) via `splice_from` when 4 bytes of space remain
2158            // succeeds with a 4-byte transfer instead of failing with `EAGAIN`.
2159            let source_file = anon_test_file(
2160                &current_task,
2161                Box::new(StaticSourceFile { payload: b"12345678" }),
2162                OpenFlags::RDONLY,
2163            );
2164            let spliced = pipe_in
2165                .downcast_file::<PipeFileObject>()
2166                .unwrap()
2167                .splice_from(&current_task, &pipe_in, &source_file, None, 8, true)
2168                .unwrap();
2169            assert_eq!(spliced, 4);
2170
2171            // Likewise for `copy_pipes` into a pipe with 4 bytes of remaining space.
2172            let (pipe_b_out, pipe_b_in) = new_pipe(&current_task).unwrap();
2173            assert_eq!(
2174                pipe_b_in.write(&current_task, &mut VecInputBuffer::new(&fill)).unwrap(),
2175                capacity - 4
2176            );
2177            let copied = PipeFileObject::copy_pipes(
2178                &current_task,
2179                &pipe_out,
2180                &pipe_out.downcast_file::<PipeFileObject>().unwrap(),
2181                &pipe_b_in,
2182                &pipe_b_in.downcast_file::<PipeFileObject>().unwrap(),
2183                8,
2184                true,
2185                true,
2186            )
2187            .unwrap();
2188            assert_eq!(copied, 4);
2189            let _ = pipe_b_out;
2190        })
2191        .await;
2192    }
2193
2194    #[::fuchsia::test]
2195    async fn test_copy_pipes_honors_nonblock_and_eof_and_epipe() {
2196        spawn_kernel_and_run(async |current_task| {
2197            let kernel = current_task.kernel();
2198            register_pipe_fs(kernel.expando.get::<FsRegistry>().as_ref());
2199
2200            // 1. O_NONBLOCK on `file_out` causes `copy_pipes` on an empty `pipe_in` to return EAGAIN.
2201            let (pipe_a_out, pipe_a_in) = new_pipe(&current_task).unwrap();
2202            let (pipe_b_out, pipe_b_in) = new_pipe(&current_task).unwrap();
2203            pipe_b_in.update_file_flags(OpenFlags::NONBLOCK, OpenFlags::NONBLOCK);
2204            assert_eq!(
2205                PipeFileObject::copy_pipes(
2206                    &current_task,
2207                    &pipe_a_out,
2208                    &pipe_a_out.downcast_file::<PipeFileObject>().unwrap(),
2209                    &pipe_b_in,
2210                    &pipe_b_in.downcast_file::<PipeFileObject>().unwrap(),
2211                    4,
2212                    false,
2213                    false,
2214                )
2215                .unwrap_err(),
2216                errno!(EAGAIN)
2217            );
2218
2219            // 2. When `pipe_in` is at EOF and `pipe_out` is writable, `copy_pipes` returns `Ok(0)`;
2220            // when `pipe_out` is full (with an open reader) and non-blocking, `copy_pipes` returns
2221            // `EAGAIN`.
2222            std::mem::drop(pipe_a_in);
2223            current_task.trigger_delayed_releaser();
2224            assert_eq!(
2225                PipeFileObject::copy_pipes(
2226                    &current_task,
2227                    &pipe_a_out,
2228                    &pipe_a_out.downcast_file::<PipeFileObject>().unwrap(),
2229                    &pipe_b_in,
2230                    &pipe_b_in.downcast_file::<PipeFileObject>().unwrap(),
2231                    4,
2232                    true,
2233                    false,
2234                )
2235                .unwrap(),
2236                0
2237            );
2238            let capacity =
2239                pipe_b_in.downcast_file::<PipeFileObject>().unwrap().pipe.lock().capacity();
2240            let fill = vec![b'F'; capacity];
2241            assert_eq!(
2242                pipe_b_in.write(&current_task, &mut VecInputBuffer::new(&fill)).unwrap(),
2243                capacity
2244            );
2245            assert_eq!(
2246                PipeFileObject::copy_pipes(
2247                    &current_task,
2248                    &pipe_a_out,
2249                    &pipe_a_out.downcast_file::<PipeFileObject>().unwrap(),
2250                    &pipe_b_in,
2251                    &pipe_b_in.downcast_file::<PipeFileObject>().unwrap(),
2252                    4,
2253                    true,
2254                    false,
2255                )
2256                .unwrap_err(),
2257                errno!(EAGAIN)
2258            );
2259
2260            // 3. When `pipe_out` has no readers, `copy_pipes` returns `EPIPE` and queues `SIGPIPE`.
2261            std::mem::drop(pipe_b_out);
2262            current_task.trigger_delayed_releaser();
2263            assert_eq!(
2264                PipeFileObject::copy_pipes(
2265                    &current_task,
2266                    &pipe_a_out,
2267                    &pipe_a_out.downcast_file::<PipeFileObject>().unwrap(),
2268                    &pipe_b_in,
2269                    &pipe_b_in.downcast_file::<PipeFileObject>().unwrap(),
2270                    4,
2271                    true,
2272                    false,
2273                )
2274                .unwrap_err(),
2275                errno!(EPIPE)
2276            );
2277            assert!(current_task.write().signals_mut().has_queued(SIGPIPE));
2278        })
2279        .await;
2280    }
2281
2282    #[::fuchsia::test]
2283    async fn test_copy_pipes_wakes_with_epipe_when_destination_reader_closes() {
2284        spawn_kernel_and_run(async |current_task| {
2285            let kernel = current_task.kernel();
2286            register_pipe_fs(kernel.expando.get::<FsRegistry>().as_ref());
2287
2288            let (pipe_a_out, pipe_a_in) = new_pipe(&current_task).unwrap();
2289            let (pipe_b_out, pipe_b_in) = new_pipe(&current_task).unwrap();
2290            assert_eq!(
2291                pipe_a_in.write(&current_task, &mut VecInputBuffer::new(b"DATA")).unwrap(),
2292                4
2293            );
2294            let capacity =
2295                pipe_b_in.downcast_file::<PipeFileObject>().unwrap().pipe.lock().capacity();
2296            let fill = vec![b'F'; capacity];
2297            assert_eq!(
2298                pipe_b_in.write(&current_task, &mut VecInputBuffer::new(&fill)).unwrap(),
2299                capacity
2300            );
2301
2302            let pipe_b_in_clone = pipe_b_in.clone();
2303            let (closer_join, req) = SpawnRequestBuilder::new()
2304                .with_sync_closure(move |worker_task: &CurrentTask| {
2305                    while pipe_b_in_clone
2306                        .downcast_file::<PipeFileObject>()
2307                        .unwrap()
2308                        .pipe
2309                        .lock()
2310                        .waiters
2311                        .is_empty()
2312                    {
2313                        std::thread::yield_now();
2314                    }
2315                    std::mem::drop(pipe_b_out);
2316                    worker_task.trigger_delayed_releaser();
2317                })
2318                .build_with_sync_result();
2319            kernel.kthreads.spawner().spawn_from_request(req);
2320
2321            let err = PipeFileObject::copy_pipes(
2322                &current_task,
2323                &pipe_a_out,
2324                &pipe_a_out.downcast_file::<PipeFileObject>().unwrap(),
2325                &pipe_b_in,
2326                &pipe_b_in.downcast_file::<PipeFileObject>().unwrap(),
2327                4,
2328                false,
2329                true,
2330            )
2331            .unwrap_err();
2332            closer_join().unwrap();
2333            assert_eq!(err, errno!(EPIPE));
2334            assert!(current_task.write().signals_mut().has_queued(SIGPIPE));
2335        })
2336        .await;
2337    }
2338
2339    #[::fuchsia::test]
2340    async fn test_splice_to_eof_pipe_does_not_invoke_destination_write() {
2341        spawn_kernel_and_run(async |current_task| {
2342            let kernel = current_task.kernel();
2343            register_pipe_fs(kernel.expando.get::<FsRegistry>().as_ref());
2344
2345            let (pipe_out, pipe_in) = new_pipe(&current_task).unwrap();
2346            std::mem::drop(pipe_in);
2347            current_task.trigger_delayed_releaser();
2348
2349            let endpoint =
2350                anon_test_file(&current_task, Box::new(WouldBlockEndpointFile), OpenFlags::WRONLY);
2351            let pipe_ops = pipe_out.downcast_file::<PipeFileObject>().unwrap();
2352
2353            // `WouldBlockEndpointFile::write` would block (or return `EAGAIN` if non-blocking),
2354            // so returning `Ok(0)` proves `to.write` was skipped on source pipe EOF.
2355            assert_eq!(
2356                pipe_ops.splice_to(&current_task, &pipe_out, &endpoint, None, 16, true).unwrap(),
2357                0
2358            );
2359        })
2360        .await;
2361    }
2362
2363    #[::fuchsia::test]
2364    async fn test_pipe_readiness_and_fionread_during_eof_and_active_splice() {
2365        spawn_kernel_and_run(async |current_task| {
2366            let kernel = current_task.kernel();
2367            register_pipe_fs(kernel.expando.get::<FsRegistry>().as_ref());
2368
2369            let (pipe_out, pipe_in) = new_pipe(&current_task).unwrap();
2370            assert_eq!(pipe_in.write(&current_task, &mut VecInputBuffer::new(b"AB")).unwrap(), 2);
2371
2372            // Register an async waiter for POLLIN while the pipe is already non-empty, then write
2373            // 2 more bytes; the write must still notify POLLIN.
2374            let waiter = Waiter::new();
2375            pipe_out.wait_async(&current_task, &waiter, FdEvents::POLLIN, WaitCallback::none());
2376            assert_eq!(pipe_in.write(&current_task, &mut VecInputBuffer::new(b"CD")).unwrap(), 2);
2377            waiter.wait(&current_task).expect("write to non-empty pipe must notify POLLIN");
2378
2379            // Reserve all 4 queued bytes in an active splice read.
2380            let pipe_ops = pipe_out.downcast_file::<PipeFileObject>().unwrap();
2381            let guard = SpliceReadGuard::new(&pipe_ops.pipe, pipe_ops.pipe.lock(), 4);
2382            assert_eq!(pipe_out.query_events(&current_task).unwrap(), FdEvents::empty());
2383            assert_eq!(pipe_ops.pipe.lock().readable_len(), 0);
2384            assert!(!pipe_ops.pipe.lock().is_readable());
2385
2386            // A concurrent write serializes behind the active splice read (`lock_pipe_without_splice`)
2387            // and notifies POLLIN once the splice read completes.
2388            let pipe_in_clone = pipe_in.clone();
2389            let (writer_join, req) = SpawnRequestBuilder::new()
2390                .with_sync_closure(move |worker_task: &CurrentTask| {
2391                    pipe_in_clone
2392                        .write(worker_task, &mut VecInputBuffer::new(b"EF"))
2393                        .expect("write after active splice read")
2394                })
2395                .build_with_sync_result();
2396            kernel.kthreads.spawner().spawn_from_request(req);
2397            while pipe_ops.pipe.lock().waiters.is_empty() {
2398                std::thread::yield_now();
2399            }
2400
2401            let waiter2 = Waiter::new();
2402            pipe_out.wait_async(&current_task, &waiter2, FdEvents::POLLIN, WaitCallback::none());
2403            guard.consume(4);
2404            assert_eq!(writer_join().unwrap(), 2);
2405            assert_eq!(
2406                pipe_out.query_events(&current_task).unwrap(),
2407                FdEvents::POLLIN | FdEvents::POLLRDNORM
2408            );
2409            assert_eq!(pipe_ops.pipe.lock().readable_len(), 2);
2410            waiter2.wait(&current_task).expect("write after splice read must notify POLLIN");
2411
2412            let mut remaining = VecOutputBuffer::new(2);
2413            assert_eq!(pipe_out.read(&current_task, &mut remaining).unwrap(), 2);
2414            assert_eq!(remaining.data(), b"EF");
2415
2416            // Close the writer when empty: query_events must report POLLHUP without POLLIN.
2417            std::mem::drop(pipe_in);
2418            current_task.trigger_delayed_releaser();
2419            assert_eq!(pipe_out.query_events(&current_task).unwrap(), FdEvents::POLLHUP);
2420        })
2421        .await;
2422    }
2423
2424    #[::fuchsia::test]
2425    async fn test_zero_length_pipe_io_and_no_empty_message_enqueued() {
2426        spawn_kernel_and_run(async |current_task| {
2427            let kernel = current_task.kernel();
2428            register_pipe_fs(kernel.expando.get::<FsRegistry>().as_ref());
2429
2430            let (pipe_out, pipe_in) = new_pipe(&current_task).unwrap();
2431            let pipe_ops = pipe_out.downcast_file::<PipeFileObject>().unwrap();
2432
2433            // Zero-length read and vmsplice_to on an empty pipe return 0 without blocking or EAGAIN.
2434            assert_eq!(pipe_ops.pipe.lock().read(&mut VecOutputBuffer::new(0)).unwrap(), 0);
2435            assert_eq!(pipe_out.read(&current_task, &mut VecOutputBuffer::new(0)).unwrap(), 0);
2436            assert_eq!(
2437                pipe_ops
2438                    .vmsplice_to(&current_task, &pipe_out, UserBuffers::default(), true)
2439                    .unwrap(),
2440                0
2441            );
2442
2443            // Zero-length write via `Pipe::write` does not enqueue an empty message in `MessageQueue`.
2444            assert_eq!(pipe_ops.pipe.lock().write(&mut VecInputBuffer::new(b"")).unwrap(), 0);
2445            assert_eq!(pipe_ops.pipe.lock().messages.peek_queue().len(), 0);
2446
2447            // Close the reader: zero-length write and vmsplice_from on a broken pipe return 0
2448            // without EPIPE or SIGPIPE.
2449            std::mem::drop(pipe_out);
2450            current_task.trigger_delayed_releaser();
2451            let pipe_in_ops = pipe_in.downcast_file::<PipeFileObject>().unwrap();
2452            assert_eq!(pipe_in_ops.pipe.lock().write(&mut VecInputBuffer::new(b"")).unwrap(), 0);
2453            assert_eq!(pipe_in.write(&current_task, &mut VecInputBuffer::new(b"")).unwrap(), 0);
2454            assert_eq!(
2455                pipe_in_ops
2456                    .vmsplice_from(&current_task, &pipe_in, UserBuffers::default(), true)
2457                    .unwrap(),
2458                0
2459            );
2460            assert!(!current_task.write().signals_mut().has_queued(SIGPIPE));
2461        })
2462        .await;
2463    }
2464
2465    #[::fuchsia::test]
2466    async fn test_readv_writev_vmsplice_partial_efault_short_counts_and_rollback() {
2467        spawn_kernel_and_run(async |current_task| {
2468            let kernel = current_task.kernel();
2469            register_pipe_fs(kernel.expando.get::<FsRegistry>().as_ref());
2470
2471            let page_size = *PAGE_SIZE as usize;
2472            let two_pages = map_memory(&current_task, UserAddress::default(), (*PAGE_SIZE) * 2);
2473            let bad_page = (two_pages + *PAGE_SIZE).unwrap();
2474            fill_page(&current_task, two_pages, 'M');
2475            current_task
2476                .mm()
2477                .unwrap()
2478                .protect(&current_task, bad_page, page_size, ProtectionFlags::empty())
2479                .unwrap();
2480
2481            // 1. Multi-page `writev` (`[page_size valid, page_size PROT_NONE]`) enqueues the first
2482            // page and returns a short count (`page_size`).
2483            let (pipe_out, pipe_in) = new_pipe(&current_task).unwrap();
2484            let mut writev_buf = UserBuffersInputBuffer::unified_new(
2485                &current_task,
2486                smallvec![
2487                    UserBuffer { address: two_pages, length: page_size },
2488                    UserBuffer { address: bad_page, length: page_size },
2489                ],
2490            )
2491            .unwrap();
2492            assert_eq!(pipe_in.write(&current_task, &mut writev_buf).unwrap(), page_size);
2493            assert_eq!(
2494                pipe_out.downcast_file::<PipeFileObject>().unwrap().pipe.lock().readable_len(),
2495                page_size
2496            );
2497            std::mem::drop((pipe_out, pipe_in));
2498
2499            // 2. Sub-PIPE_BUF `writev` (`[100B valid, 100B PROT_NONE]`) preserves POSIX PIPE_BUF
2500            // atomicity on EFAULT, returning EFAULT with 0 bytes queued.
2501            let (pipe_out, pipe_in) = new_pipe(&current_task).unwrap();
2502            let mut sub_pipe_buf = UserBuffersInputBuffer::unified_new(
2503                &current_task,
2504                smallvec![
2505                    UserBuffer { address: two_pages, length: 100 },
2506                    UserBuffer { address: bad_page, length: 100 },
2507                ],
2508            )
2509            .unwrap();
2510            assert_eq!(pipe_in.write(&current_task, &mut sub_pipe_buf), error!(EFAULT));
2511            assert_eq!(
2512                pipe_out.downcast_file::<PipeFileObject>().unwrap().pipe.lock().readable_len(),
2513                0
2514            );
2515            std::mem::drop((pipe_out, pipe_in));
2516
2517            // 3. `vmsplice_from` with `[page_size valid, page_size PROT_NONE]` enqueues the valid
2518            // prefix and returns `page_size`, while `[PROT_NONE]` alone returns `EFAULT`.
2519            let (pipe_out, pipe_in) = new_pipe(&current_task).unwrap();
2520            let pipe_in_ops = pipe_in.downcast_file::<PipeFileObject>().unwrap();
2521            assert_eq!(
2522                pipe_in_ops.vmsplice_from(
2523                    &current_task,
2524                    &pipe_in,
2525                    smallvec![UserBuffer { address: bad_page, length: page_size }],
2526                    true,
2527                ),
2528                error!(EFAULT)
2529            );
2530            assert_eq!(
2531                pipe_in_ops
2532                    .vmsplice_from(
2533                        &current_task,
2534                        &pipe_in,
2535                        smallvec![
2536                            UserBuffer { address: two_pages, length: page_size },
2537                            UserBuffer { address: bad_page, length: page_size },
2538                        ],
2539                        true,
2540                    )
2541                    .unwrap(),
2542                page_size
2543            );
2544            assert_eq!(
2545                pipe_out.downcast_file::<PipeFileObject>().unwrap().pipe.lock().readable_len(),
2546                page_size
2547            );
2548            std::mem::drop((pipe_out, pipe_in));
2549
2550            // 4. `read` into a `PROT_NONE` buffer returns `EFAULT` and preserves the 4-byte
2551            // message at the front of the pipe.
2552            let (pipe_out, pipe_in) = new_pipe(&current_task).unwrap();
2553            assert_eq!(pipe_in.write(&current_task, &mut VecInputBuffer::new(b"AAAA")).unwrap(), 4);
2554            let mut bad_out =
2555                UserBuffersOutputBuffer::unified_new_at(&current_task, bad_page, 4).unwrap();
2556            assert_eq!(pipe_out.read(&current_task, &mut bad_out), error!(EFAULT));
2557            assert_eq!(
2558                pipe_out.downcast_file::<PipeFileObject>().unwrap().pipe.lock().readable_len(),
2559                4
2560            );
2561            std::mem::drop((pipe_out, pipe_in));
2562
2563            // 5. `readv` and `vmsplice_to` with `[4B valid, 4B PROT_NONE]` against a single 8-byte
2564            // message roll back the partial copy, return `EFAULT`, and preserve all 8 bytes.
2565            let (pipe_out, pipe_in) = new_pipe(&current_task).unwrap();
2566            let pipe_out_ops = pipe_out.downcast_file::<PipeFileObject>().unwrap();
2567            assert_eq!(
2568                pipe_in.write(&current_task, &mut VecInputBuffer::new(b"AAAABBBB")).unwrap(),
2569                8
2570            );
2571            let fault_iov = smallvec![
2572                UserBuffer { address: two_pages, length: 4 },
2573                UserBuffer { address: bad_page, length: 4 },
2574            ];
2575            assert_eq!(
2576                pipe_out_ops.vmsplice_to(&current_task, &pipe_out, fault_iov.clone(), true),
2577                error!(EFAULT)
2578            );
2579            assert_eq!(pipe_out_ops.pipe.lock().readable_len(), 8);
2580
2581            let mut readv_out =
2582                UserBuffersOutputBuffer::unified_new(&current_task, fault_iov.clone()).unwrap();
2583            assert_eq!(pipe_out.read(&current_task, &mut readv_out), error!(EFAULT));
2584            assert_eq!(readv_out.bytes_written(), 0);
2585            assert_eq!(pipe_out_ops.pipe.lock().readable_len(), 8);
2586            std::mem::drop((pipe_out, pipe_in));
2587
2588            // 6. `vmsplice_to` and `readv` with `[4B valid, 4B PROT_NONE]` across two separate
2589            // 4-byte messages consume the first message, re-queue the second message on EFAULT,
2590            // and return the short count (`4`).
2591            let (pipe_out, pipe_in) = new_pipe(&current_task).unwrap();
2592            let pipe_out_ops = pipe_out.downcast_file::<PipeFileObject>().unwrap();
2593            assert_eq!(pipe_in.write(&current_task, &mut VecInputBuffer::new(b"AAAA")).unwrap(), 4);
2594            assert_eq!(pipe_in.write(&current_task, &mut VecInputBuffer::new(b"BBBB")).unwrap(), 4);
2595            assert_eq!(
2596                pipe_out_ops
2597                    .vmsplice_to(&current_task, &pipe_out, fault_iov.clone(), true)
2598                    .unwrap(),
2599                4
2600            );
2601            assert_eq!(pipe_out_ops.pipe.lock().readable_len(), 4);
2602            let mut rem_buf = VecOutputBuffer::new(4);
2603            assert_eq!(pipe_out.read(&current_task, &mut rem_buf).unwrap(), 4);
2604            assert_eq!(rem_buf.data(), b"BBBB");
2605
2606            assert_eq!(pipe_in.write(&current_task, &mut VecInputBuffer::new(b"CCCC")).unwrap(), 4);
2607            assert_eq!(pipe_in.write(&current_task, &mut VecInputBuffer::new(b"DDDD")).unwrap(), 4);
2608            let mut readv_out2 =
2609                UserBuffersOutputBuffer::unified_new(&current_task, fault_iov).unwrap();
2610            assert_eq!(pipe_out.read(&current_task, &mut readv_out2).unwrap(), 4);
2611            assert_eq!(pipe_out_ops.pipe.lock().readable_len(), 4);
2612            let mut rem_buf2 = VecOutputBuffer::new(4);
2613            assert_eq!(pipe_out.read(&current_task, &mut rem_buf2).unwrap(), 4);
2614            assert_eq!(rem_buf2.data(), b"DDDD");
2615        })
2616        .await;
2617    }
2618
2619    #[::fuchsia::test]
2620    async fn test_blocking_write_wakes_when_reader_frees_less_than_pipe_buf() {
2621        spawn_kernel_and_run(async |current_task| {
2622            let kernel = current_task.kernel();
2623            register_pipe_fs(kernel.expando.get::<FsRegistry>().as_ref());
2624
2625            let (pipe_out, pipe_in) = new_pipe(&current_task).unwrap();
2626            let capacity =
2627                pipe_in.downcast_file::<PipeFileObject>().unwrap().pipe.lock().capacity();
2628            let fill = vec![b'A'; capacity];
2629            assert_eq!(
2630                pipe_in.write(&current_task, &mut VecInputBuffer::new(&fill)).unwrap(),
2631                capacity
2632            );
2633
2634            let pipe_in_clone = pipe_in.clone();
2635            let (writer_join, req) = SpawnRequestBuilder::new()
2636                .with_sync_closure(move |worker_task: &CurrentTask| {
2637                    pipe_in_clone
2638                        .write(worker_task, &mut VecInputBuffer::new(b"BBBB"))
2639                        .expect("blocked 4-byte write wakes when < PIPE_BUF bytes are freed")
2640                })
2641                .build_with_sync_result();
2642            kernel.kthreads.spawner().spawn_from_request(req);
2643
2644            while pipe_in.downcast_file::<PipeFileObject>().unwrap().pipe.lock().waiters.is_empty()
2645            {
2646                std::thread::yield_now();
2647            }
2648
2649            // Register a poll (`EventHandler::Enqueue`) waiter and an edge-triggered epoll waiter
2650            // watching `POLLOUT` on the full pipe alongside the blocked writer.
2651            let poll_waiter = Waiter::new();
2652            let poll_ready_items = Arc::default();
2653            pipe_in.wait_async(
2654                &current_task,
2655                &poll_waiter,
2656                FdEvents::POLLOUT,
2657                EventHandler::Enqueue {
2658                    key: crate::task::ReadyItemKey::Usize(1),
2659                    queue: Arc::clone(&poll_ready_items),
2660                    sought_events: FdEvents::POLLOUT | FdEvents::POLLERR | FdEvents::POLLHUP,
2661                },
2662            );
2663            let epoll_file = crate::vfs::epoll::EpollFileObject::new_file(&current_task);
2664            let epoll_ops =
2665                epoll_file.downcast_file::<crate::vfs::epoll::EpollFileObject>().unwrap();
2666            epoll_ops
2667                .add(
2668                    &current_task,
2669                    &pipe_in,
2670                    &epoll_file,
2671                    starnix_uapi::vfs::EpollEvent::new(FdEvents::POLLOUT | FdEvents::EPOLLET, 1),
2672                )
2673                .unwrap();
2674
2675            let mut small_buf = VecOutputBuffer::new(100);
2676            assert_eq!(pipe_out.read(&current_task, &mut small_buf).unwrap(), 100);
2677
2678            assert_eq!(writer_join().unwrap(), 4);
2679            // Free capacity is 96 bytes (`< PIPE_BUF = 4096`), so neither poll nor epoll may report
2680            // spurious POLLOUT readiness.
2681            assert!(poll_ready_items.lock().is_empty());
2682            assert!(
2683                epoll_ops.wait(&current_task, 1, zx::MonotonicInstant::ZERO).unwrap().is_empty()
2684            );
2685
2686            // Reading `PIPE_BUF` bytes brings available capacity above `PIPE_BUF`, notifying both
2687            // poll and epoll with `POLLOUT`.
2688            let mut page_buf = VecOutputBuffer::new(uapi::PIPE_BUF as usize);
2689            assert_eq!(
2690                pipe_out.read(&current_task, &mut page_buf).unwrap(),
2691                uapi::PIPE_BUF as usize
2692            );
2693            assert!(!poll_ready_items.lock().is_empty());
2694            let epoll_events =
2695                epoll_ops.wait(&current_task, 1, zx::MonotonicInstant::ZERO).unwrap();
2696            assert_eq!(epoll_events.len(), 1);
2697            assert!(epoll_events[0].events().contains(FdEvents::POLLOUT));
2698        })
2699        .await;
2700    }
2701    #[::fuchsia::test]
2702    async fn test_splice_to_rlimit_fsize_truncation_and_sigxfsz() {
2703        spawn_kernel_and_run(async |current_task| {
2704            let kernel = current_task.kernel();
2705            register_pipe_fs(kernel.expando.get::<FsRegistry>().as_ref());
2706
2707            let reg_file = new_memfd(
2708                &current_task,
2709                b"rlimit_fsize".into(),
2710                SealFlags::empty(),
2711                OpenFlags::RDWR,
2712            )
2713            .unwrap();
2714            assert!(reg_file.node().is_reg());
2715            current_task
2716                .thread_group()
2717                .limits
2718                .lock()
2719                .set(Resource::FSIZE, uapi::rlimit { rlim_cur: 4, rlim_max: 4 });
2720
2721            let take_sigxfsz = |task: &CurrentTask| -> bool {
2722                task.write().signals_mut().take_next_where(|sig| sig.signal == SIGXFSZ).is_some()
2723            };
2724
2725            // Subtest 1: `off = 0 < limit = 4, len = 8` with 8 bytes in pipe truncates to 4 bytes,
2726            // raises SIGXFSZ, and leaves the remaining 4 bytes in the pipe.
2727            let (pipe_out, pipe_in) = new_pipe(&current_task).unwrap();
2728            let pipe_ops = pipe_out.downcast_file::<PipeFileObject>().unwrap();
2729            assert_eq!(
2730                pipe_in.write(&current_task, &mut VecInputBuffer::new(b"AAAABBBB")).unwrap(),
2731                8
2732            );
2733            assert_eq!(
2734                pipe_ops.splice_to(&current_task, &pipe_out, &reg_file, Some(0), 8, true).unwrap(),
2735                4
2736            );
2737            assert!(take_sigxfsz(&current_task));
2738            let mut rem = VecOutputBuffer::new(8);
2739            assert_eq!(pipe_out.read(&current_task, &mut rem).unwrap(), 4);
2740            assert_eq!(rem.data(), b"BBBB");
2741
2742            // Subtest 2: `off = 0 < limit = 4, len = 8` with 4 bytes in pipe writes 4 bytes
2743            // without raising SIGXFSZ.
2744            assert_eq!(pipe_in.write(&current_task, &mut VecInputBuffer::new(b"AAAA")).unwrap(), 4);
2745            assert_eq!(
2746                pipe_ops.splice_to(&current_task, &pipe_out, &reg_file, Some(0), 8, true).unwrap(),
2747                4
2748            );
2749            assert!(!take_sigxfsz(&current_task));
2750
2751            // Subtest 3: `off = 0 < limit = 4, len = 8` on an EOF pipe returns 0 without SIGXFSZ.
2752            let (eof_out, eof_in) = new_pipe(&current_task).unwrap();
2753            std::mem::drop(eof_in);
2754            current_task.trigger_delayed_releaser();
2755            let eof_ops = eof_out.downcast_file::<PipeFileObject>().unwrap();
2756            assert_eq!(
2757                eof_ops.splice_to(&current_task, &eof_out, &reg_file, Some(0), 8, false).unwrap(),
2758                0
2759            );
2760            assert!(!take_sigxfsz(&current_task));
2761
2762            // Subtest 4: `off = 0 < limit = 4, len = 8` on an empty non-blocking pipe returns
2763            // EAGAIN without SIGXFSZ.
2764            assert_eq!(
2765                pipe_ops.splice_to(&current_task, &pipe_out, &reg_file, Some(0), 8, true),
2766                error!(EAGAIN)
2767            );
2768            assert!(!take_sigxfsz(&current_task));
2769
2770            // Subtest 5: `off = 4 >= limit = 4, len = 4` on an EOF pipe returns 0 without SIGXFSZ.
2771            assert_eq!(
2772                eof_ops.splice_to(&current_task, &eof_out, &reg_file, Some(4), 4, false).unwrap(),
2773                0
2774            );
2775            assert!(!take_sigxfsz(&current_task));
2776
2777            // Subtest 6: `off = 4 >= limit = 4, len = 4` on an empty non-blocking pipe returns
2778            // EAGAIN without SIGXFSZ.
2779            assert_eq!(
2780                pipe_ops.splice_to(&current_task, &pipe_out, &reg_file, Some(4), 4, true),
2781                error!(EAGAIN)
2782            );
2783            assert!(!take_sigxfsz(&current_task));
2784
2785            // Subtest 7: `off = 4 >= limit = 4, len = 4` with 4 bytes in pipe returns EFBIG,
2786            // raises SIGXFSZ, and leaves all 4 bytes unconsumed in the pipe.
2787            assert_eq!(pipe_in.write(&current_task, &mut VecInputBuffer::new(b"AAAA")).unwrap(), 4);
2788            assert_eq!(
2789                pipe_ops.splice_to(&current_task, &pipe_out, &reg_file, Some(4), 4, true),
2790                error!(EFBIG)
2791            );
2792            assert!(take_sigxfsz(&current_task));
2793            let mut rem2 = VecOutputBuffer::new(8);
2794            assert_eq!(pipe_out.read(&current_task, &mut rem2).unwrap(), 4);
2795            assert_eq!(rem2.data(), b"AAAA");
2796        })
2797        .await;
2798    }
2799    #[::fuchsia::test]
2800    async fn test_poll_and_nonblocking_read_during_inflight_splice_to() {
2801        spawn_kernel_and_run(async |current_task| {
2802            let kernel = current_task.kernel();
2803            register_pipe_fs(kernel.expando.get::<FsRegistry>().as_ref());
2804
2805            let (pipe_out, pipe_in) = new_pipe(&current_task).unwrap();
2806            assert_eq!(
2807                pipe_in.write(&current_task, &mut VecInputBuffer::new(b"AAAABBBB")).unwrap(),
2808                8
2809            );
2810            pipe_out.update_file_flags(OpenFlags::NONBLOCK, OpenFlags::NONBLOCK);
2811
2812            let (entered_tx, entered_rx) = mpsc::channel();
2813            let (resume_tx, resume_rx) = mpsc::channel();
2814            let barrier_sink = anon_test_file(
2815                &current_task,
2816                Box::new(BarrierSinkFile {
2817                    entered_tx: Mutex::new(Some(entered_tx)),
2818                    resume_rx: Mutex::new(Some(resume_rx)),
2819                    max_write: 4,
2820                    written: Mutex::new(Vec::new()),
2821                }),
2822                OpenFlags::WRONLY,
2823            );
2824
2825            let (ready_tx, ready_rx) = mpsc::channel();
2826            let pipe_out_unblock = pipe_out.clone();
2827            let (unblock_join, unblock_req) = SpawnRequestBuilder::new()
2828                .with_sync_closure(move |_worker_task: &CurrentTask| {
2829                    ready_rx.recv().expect("wait until non-blocking read starts");
2830                    while pipe_out_unblock
2831                        .downcast_file::<PipeFileObject>()
2832                        .unwrap()
2833                        .pipe
2834                        .lock()
2835                        .waiters
2836                        .is_empty()
2837                    {
2838                        std::thread::yield_now();
2839                    }
2840                    resume_tx.send(()).expect("allow splice_to write to complete");
2841                })
2842                .build_with_sync_result();
2843            kernel.kthreads.spawner().spawn_from_request(unblock_req);
2844
2845            let pipe_out_clone = pipe_out.clone();
2846            let (reader_join, req) = SpawnRequestBuilder::new()
2847                .with_sync_closure(move |worker_task: &CurrentTask| {
2848                    entered_rx.recv().expect("wait until splice_to enters write");
2849                    // 4 unreserved bytes remain, so query_events reports POLLIN.
2850                    assert_eq!(
2851                        pipe_out_clone.query_events(worker_task).unwrap(),
2852                        FdEvents::POLLIN | FdEvents::POLLRDNORM
2853                    );
2854                    ready_tx.send(()).expect("signal unblocker");
2855                    let mut out = VecOutputBuffer::new(4);
2856                    let n = pipe_out_clone
2857                        .read(worker_task, &mut out)
2858                        .expect("O_NONBLOCK read waits for in-flight splice_to and succeeds");
2859                    out.data()[..n].to_vec()
2860                })
2861                .build_with_sync_result();
2862            kernel.kthreads.spawner().spawn_from_request(req);
2863
2864            let spliced = pipe_out
2865                .downcast_file::<PipeFileObject>()
2866                .unwrap()
2867                .splice_to(&current_task, &pipe_out, &barrier_sink, None, 4, true)
2868                .unwrap();
2869            assert_eq!(spliced, 4);
2870            unblock_join().unwrap();
2871            assert_eq!(reader_join().unwrap(), b"BBBB");
2872        })
2873        .await;
2874    }
2875
2876    #[::fuchsia::test]
2877    async fn test_nonblocking_write_to_readerless_pipe_during_inflight_splice_returns_epipe() {
2878        spawn_kernel_and_run(async |current_task| {
2879            let kernel = current_task.kernel();
2880            register_pipe_fs(kernel.expando.get::<FsRegistry>().as_ref());
2881
2882            let (pipe_out, pipe_in) = new_pipe(&current_task).unwrap();
2883            pipe_in.update_file_flags(OpenFlags::NONBLOCK, OpenFlags::NONBLOCK);
2884
2885            let (entered_tx, entered_rx) = mpsc::channel();
2886            let (resume_tx, resume_rx) = mpsc::channel();
2887            let barrier_source = anon_test_file(
2888                &current_task,
2889                Box::new(BarrierSourceFile {
2890                    entered_tx: Mutex::new(Some(entered_tx)),
2891                    resume_rx: Mutex::new(Some(resume_rx)),
2892                    payload: b"AAAA",
2893                }),
2894                OpenFlags::RDONLY,
2895            );
2896
2897            let (ready_tx, ready_rx) = mpsc::channel();
2898            let pipe_in_unblock = pipe_in.clone();
2899            let (unblock_join, unblock_req) = SpawnRequestBuilder::new()
2900                .with_sync_closure(move |_worker_task: &CurrentTask| {
2901                    ready_rx.recv().expect("wait until non-blocking write starts");
2902                    while pipe_in_unblock
2903                        .downcast_file::<PipeFileObject>()
2904                        .unwrap()
2905                        .pipe
2906                        .lock()
2907                        .waiters
2908                        .is_empty()
2909                    {
2910                        std::thread::yield_now();
2911                    }
2912                    resume_tx.send(()).expect("allow splice_from read to complete");
2913                })
2914                .build_with_sync_result();
2915            kernel.kthreads.spawner().spawn_from_request(unblock_req);
2916
2917            let pipe_in_clone = pipe_in.clone();
2918            let (writer_join, req) = SpawnRequestBuilder::new()
2919                .with_sync_closure(move |worker_task: &CurrentTask| {
2920                    entered_rx.recv().expect("wait until splice_from enters read");
2921                    std::mem::drop(pipe_out);
2922                    worker_task.trigger_delayed_releaser();
2923                    ready_tx.send(()).expect("signal unblocker");
2924                    let err = pipe_in_clone
2925                        .write(worker_task, &mut VecInputBuffer::new(b"BBBB"))
2926                        .unwrap_err();
2927                    let has_sigpipe = worker_task.write().signals_mut().has_queued(SIGPIPE);
2928                    (err, has_sigpipe)
2929                })
2930                .build_with_sync_result();
2931            kernel.kthreads.spawner().spawn_from_request(req);
2932
2933            let splice_err = pipe_in
2934                .downcast_file::<PipeFileObject>()
2935                .unwrap()
2936                .splice_from(&current_task, &pipe_in, &barrier_source, None, 4, true)
2937                .unwrap_err();
2938            assert_eq!(splice_err, errno!(EPIPE));
2939            unblock_join().unwrap();
2940            let (write_err, has_sigpipe) = writer_join().unwrap();
2941            assert_eq!(write_err, errno!(EPIPE));
2942            assert!(has_sigpipe);
2943        })
2944        .await;
2945    }
2946
2947    #[::fuchsia::test]
2948    async fn test_tee_concurrent_with_reader_preserves_fifo_without_duplicates() {
2949        spawn_kernel_and_run(async |current_task| {
2950            let kernel = current_task.kernel();
2951            register_pipe_fs(kernel.expando.get::<FsRegistry>().as_ref());
2952
2953            // 1. Concurrent `splice_pipes` (`consume_source = true`) and `read` on `pipe_a` must
2954            // partition messages across `pipe_a` and `pipe_b` in FIFO order without duplicates.
2955            let (pipe_a_out, pipe_a_in) = new_pipe(&current_task).unwrap();
2956            let (pipe_b_out, pipe_b_in) = new_pipe(&current_task).unwrap();
2957            const NUM_MSGS: usize = 64;
2958            for i in 0..NUM_MSGS {
2959                let msg = format!("M{:03}", i);
2960                assert_eq!(
2961                    pipe_a_in
2962                        .write(&current_task, &mut VecInputBuffer::new(msg.as_bytes()))
2963                        .unwrap(),
2964                    4
2965                );
2966            }
2967            std::mem::drop(pipe_a_in);
2968            current_task.trigger_delayed_releaser();
2969
2970            let pipe_a_out_clone = pipe_a_out.clone();
2971            let pipe_b_in_clone = pipe_b_in.clone();
2972            let (splicer_join, req) = SpawnRequestBuilder::new()
2973                .with_sync_closure(move |worker_task: &CurrentTask| {
2974                    let pipe_a_ops = pipe_a_out_clone.downcast_file::<PipeFileObject>().unwrap();
2975                    let pipe_b_ops = pipe_b_in_clone.downcast_file::<PipeFileObject>().unwrap();
2976                    loop {
2977                        let n = PipeFileObject::splice_pipes(
2978                            worker_task,
2979                            &pipe_a_out_clone,
2980                            &pipe_a_ops,
2981                            &pipe_b_in_clone,
2982                            &pipe_b_ops,
2983                            4,
2984                            false,
2985                        )
2986                        .unwrap();
2987                        if n == 0 {
2988                            break;
2989                        }
2990                    }
2991                })
2992                .build_with_sync_result();
2993            kernel.kthreads.spawner().spawn_from_request(req);
2994
2995            let mut read_msgs = Vec::new();
2996            loop {
2997                let mut buf = VecOutputBuffer::new(4);
2998                let n = pipe_a_out.read(&current_task, &mut buf).unwrap();
2999                if n == 0 {
3000                    break;
3001                }
3002                assert_eq!(n, 4);
3003                read_msgs.push(String::from_utf8(buf.data().to_vec()).unwrap());
3004            }
3005            splicer_join().unwrap();
3006            std::mem::drop(pipe_b_in);
3007            current_task.trigger_delayed_releaser();
3008
3009            let mut spliced_msgs = Vec::new();
3010            loop {
3011                let mut buf = VecOutputBuffer::new(4);
3012                let n = pipe_b_out.read(&current_task, &mut buf).unwrap();
3013                if n == 0 {
3014                    break;
3015                }
3016                assert_eq!(n, 4);
3017                spliced_msgs.push(String::from_utf8(buf.data().to_vec()).unwrap());
3018            }
3019
3020            // Both streams must be strictly increasing, and their disjoint union must be M000..M063.
3021            assert!(read_msgs.windows(2).all(|w| w[0] < w[1]));
3022            assert!(spliced_msgs.windows(2).all(|w| w[0] < w[1]));
3023            let mut combined = [read_msgs, spliced_msgs].concat();
3024            combined.sort();
3025            let expected: Vec<String> = (0..NUM_MSGS).map(|i| format!("M{:03}", i)).collect();
3026            assert_eq!(combined, expected);
3027
3028            // 2. Concurrent `tee` (`consume_source = false`) and `read` on `pipe_c` must never
3029            // reorder messages into `pipe_d`.
3030            let (pipe_c_out, pipe_c_in) = new_pipe(&current_task).unwrap();
3031            let (pipe_d_out, pipe_d_in) = new_pipe(&current_task).unwrap();
3032            for i in 0..NUM_MSGS {
3033                let msg = format!("T{:03}", i);
3034                assert_eq!(
3035                    pipe_c_in
3036                        .write(&current_task, &mut VecInputBuffer::new(msg.as_bytes()))
3037                        .unwrap(),
3038                    4
3039                );
3040            }
3041            std::mem::drop(pipe_c_in);
3042            current_task.trigger_delayed_releaser();
3043
3044            let pipe_c_out_clone = pipe_c_out.clone();
3045            let pipe_d_in_clone = pipe_d_in.clone();
3046            let (tee_join, tee_req) = SpawnRequestBuilder::new()
3047                .with_sync_closure(move |worker_task: &CurrentTask| {
3048                    loop {
3049                        let n = PipeFileObject::tee(
3050                            worker_task,
3051                            &pipe_c_out_clone,
3052                            &pipe_d_in_clone,
3053                            4,
3054                            false,
3055                        )
3056                        .unwrap();
3057                        if n == 0 {
3058                            break;
3059                        }
3060                    }
3061                })
3062                .build_with_sync_result();
3063            kernel.kthreads.spawner().spawn_from_request(tee_req);
3064
3065            loop {
3066                let mut buf = VecOutputBuffer::new(4);
3067                if pipe_c_out.read(&current_task, &mut buf).unwrap() == 0 {
3068                    break;
3069                }
3070            }
3071            tee_join().unwrap();
3072            std::mem::drop(pipe_d_in);
3073            current_task.trigger_delayed_releaser();
3074
3075            let mut teed_msgs = Vec::new();
3076            loop {
3077                let mut buf = VecOutputBuffer::new(4);
3078                let n = pipe_d_out.read(&current_task, &mut buf).unwrap();
3079                if n == 0 {
3080                    break;
3081                }
3082                assert_eq!(n, 4);
3083                teed_msgs.push(String::from_utf8(buf.data().to_vec()).unwrap());
3084            }
3085            assert!(teed_msgs.windows(2).all(|w| w[0] <= w[1]));
3086        })
3087        .await;
3088    }
3089
3090    #[::fuchsia::test]
3091    async fn test_copy_pipes_empty_input_and_readerless_output_matches_linux_ipipe_prep_order() {
3092        spawn_kernel_and_run(async |current_task| {
3093            let kernel = current_task.kernel();
3094            register_pipe_fs(kernel.expando.get::<FsRegistry>().as_ref());
3095
3096            // 1. When `pipe_in` is empty (with open writers) and `pipe_out` has no readers,
3097            // non-blocking `splice_pipes` and `tee` return `EAGAIN` without `SIGPIPE`.
3098            let (pipe_a_out, pipe_a_in) = new_pipe(&current_task).unwrap();
3099            let (pipe_b_out, pipe_b_in) = new_pipe(&current_task).unwrap();
3100            std::mem::drop(pipe_b_out);
3101            current_task.trigger_delayed_releaser();
3102
3103            let pipe_a_ops = pipe_a_out.downcast_file::<PipeFileObject>().unwrap();
3104            let pipe_b_ops = pipe_b_in.downcast_file::<PipeFileObject>().unwrap();
3105            assert_eq!(
3106                PipeFileObject::splice_pipes(
3107                    &current_task,
3108                    &pipe_a_out,
3109                    &pipe_a_ops,
3110                    &pipe_b_in,
3111                    &pipe_b_ops,
3112                    4,
3113                    true,
3114                )
3115                .unwrap_err(),
3116                errno!(EAGAIN)
3117            );
3118            assert!(!current_task.write().signals_mut().has_queued(SIGPIPE));
3119            assert_eq!(
3120                PipeFileObject::tee(&current_task, &pipe_a_out, &pipe_b_in, 4, true).unwrap_err(),
3121                errno!(EAGAIN)
3122            );
3123            assert!(!current_task.write().signals_mut().has_queued(SIGPIPE));
3124
3125            // 2. Blocking `splice_pipes` on empty `pipe_in` + readerless `pipe_out` blocks on
3126            // `pipe_in` first, then returns `EPIPE` + `SIGPIPE` once `pipe_in` becomes readable.
3127            let pipe_a_out_clone = pipe_a_out.clone();
3128            let pipe_b_in_clone = pipe_b_in.clone();
3129            let (splice_join, req) = SpawnRequestBuilder::new()
3130                .with_sync_closure(move |worker_task: &CurrentTask| {
3131                    let a_ops = pipe_a_out_clone.downcast_file::<PipeFileObject>().unwrap();
3132                    let b_ops = pipe_b_in_clone.downcast_file::<PipeFileObject>().unwrap();
3133                    let err = PipeFileObject::splice_pipes(
3134                        worker_task,
3135                        &pipe_a_out_clone,
3136                        &a_ops,
3137                        &pipe_b_in_clone,
3138                        &b_ops,
3139                        4,
3140                        false,
3141                    )
3142                    .unwrap_err();
3143                    let has_sigpipe = worker_task.write().signals_mut().has_queued(SIGPIPE);
3144                    (err, has_sigpipe)
3145                })
3146                .build_with_sync_result();
3147            kernel.kthreads.spawner().spawn_from_request(req);
3148
3149            while pipe_a_ops.pipe.lock().waiters.is_empty() {
3150                std::thread::yield_now();
3151            }
3152            assert_eq!(
3153                pipe_a_in.write(&current_task, &mut VecInputBuffer::new(b"DATA")).unwrap(),
3154                4
3155            );
3156            let (err, has_sigpipe) = splice_join().unwrap();
3157            assert_eq!(err, errno!(EPIPE));
3158            assert!(has_sigpipe);
3159            std::mem::drop(pipe_a_in);
3160
3161            // 3. Same-pipe `copy_pipes` returns `EINVAL` without deadlocking `ordered_lock`.
3162            let (same_out, same_in) = new_pipe(&current_task).unwrap();
3163            let same_out_ops = same_out.downcast_file::<PipeFileObject>().unwrap();
3164            let same_in_ops = same_in.downcast_file::<PipeFileObject>().unwrap();
3165            assert_eq!(
3166                PipeFileObject::splice_pipes(
3167                    &current_task,
3168                    &same_out,
3169                    &same_out_ops,
3170                    &same_in,
3171                    &same_in_ops,
3172                    4,
3173                    true,
3174                )
3175                .unwrap_err(),
3176                errno!(EINVAL)
3177            );
3178        })
3179        .await;
3180    }
3181
3182    #[test]
3183    #[cfg(debug_assertions)]
3184    #[should_panic(expected = "LockDep: Self-deadlock detected")]
3185    fn test_unordered_nested_pipe_lock_panics_in_lockdep() {
3186        let pipe1 = Pipe::new(*PAGE_SIZE as usize);
3187        let pipe2 = Pipe::new(*PAGE_SIZE as usize);
3188        let _guard1 = pipe1.lock();
3189        // Attempting to acquire a second pipe lock while holding one must panic in LockDep.
3190        let _guard2 = pipe2.lock();
3191    }
3192
3193    #[::fuchsia::test]
3194    async fn test_f_setpipe_sz_power_of_two_rounding_and_grow_wakes_blocked_writer() {
3195        spawn_kernel_and_run(async |current_task| {
3196            let kernel = current_task.kernel();
3197            register_pipe_fs(kernel.expando.get::<FsRegistry>().as_ref());
3198
3199            let (_pipe_out, pipe_in) = new_pipe(&current_task).unwrap();
3200            let page_size = *PAGE_SIZE as usize;
3201
3202            // Negative sizes must be rejected with `EINVAL`.
3203            assert_eq!(
3204                pipe_in.fcntl(&current_task, F_SETPIPE_SZ, (-1i32 as u32) as u64).unwrap_err(),
3205                errno!(EINVAL)
3206            );
3207
3208            // Non-power-of-two sizes above PAGE_SIZE round up to the next power of two.
3209            let rounded = pipe_in
3210                .fcntl(&current_task, F_SETPIPE_SZ, (page_size * 3) as u64)
3211                .expect("F_SETPIPE_SZ rounds to power of two");
3212            assert_eq!(rounded, (page_size * 4).into());
3213            assert_eq!(
3214                pipe_in.fcntl(&current_task, F_GETPIPE_SZ, 0).unwrap(),
3215                (page_size * 4).into()
3216            );
3217
3218            // Fill the pipe to capacity (`page_size * 4`).
3219            let fill = vec![b'F'; page_size * 4];
3220            assert_eq!(
3221                pipe_in.write(&current_task, &mut VecInputBuffer::new(&fill)).unwrap(),
3222                page_size * 4
3223            );
3224
3225            let pipe_in_clone = pipe_in.clone();
3226            let (writer_join, req) = SpawnRequestBuilder::new()
3227                .with_sync_closure(move |worker_task: &CurrentTask| {
3228                    pipe_in_clone
3229                        .write(worker_task, &mut VecInputBuffer::new(b"GROW"))
3230                        .expect("blocked writer wakes when F_SETPIPE_SZ grows pipe")
3231                })
3232                .build_with_sync_result();
3233            kernel.kthreads.spawner().spawn_from_request(req);
3234
3235            while pipe_in.downcast_file::<PipeFileObject>().unwrap().pipe.lock().waiters.is_empty()
3236            {
3237                std::thread::yield_now();
3238            }
3239
3240            let grown = pipe_in
3241                .fcntl(&current_task, F_SETPIPE_SZ, (page_size * 8) as u64)
3242                .expect("F_SETPIPE_SZ grows capacity");
3243            assert_eq!(grown, (page_size * 8).into());
3244            assert_eq!(writer_join().unwrap(), 4);
3245        })
3246        .await;
3247    }
3248}