Skip to main content

yash_env/system/virtual/
io.rs

1// This file is part of yash, an extended POSIX shell.
2// Copyright (C) 2021 WATANABE Yuki
3//
4// This program is free software: you can redistribute it and/or modify
5// it under the terms of the GNU General Public License as published by
6// the Free Software Foundation, either version 3 of the License, or
7// (at your option) any later version.
8//
9// This program is distributed in the hope that it will be useful,
10// but WITHOUT ANY WARRANTY; without even the implied warranty of
11// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12// GNU General Public License for more details.
13//
14// You should have received a copy of the GNU General Public License
15// along with this program.  If not, see <https://www.gnu.org/licenses/>.
16
17//! I/O within a virtual system.
18
19use super::super::Errno;
20use super::super::FileType;
21use super::FdFlag;
22use super::FileBody;
23use super::Inode;
24use enumset::EnumSet;
25use std::cell::Cell;
26use std::cell::RefCell;
27use std::fmt::Debug;
28use std::io::SeekFrom;
29use std::rc::Rc;
30use std::rc::Weak;
31use std::task::Poll;
32use std::task::Waker;
33
34/// State of a file opened for reading and/or writing
35#[derive(Clone, Debug)]
36pub struct OpenFileDescription {
37    /// File content and metadata
38    file: Rc<RefCell<Inode>>,
39    /// Position in bytes to perform next I/O operation at
40    offset: usize,
41    /// Whether this file is opened for reading
42    is_readable: bool,
43    /// Whether this file is opened for writing
44    is_writable: bool,
45    /// Whether this file is opened for appending
46    is_appending: bool,
47    /// Whether this file is opened in non-blocking mode
48    is_nonblocking: bool,
49}
50
51impl Drop for OpenFileDescription {
52    fn drop(&mut self) {
53        let mut file = self.file.borrow_mut();
54        file.body.close(self.is_readable, self.is_writable);
55    }
56}
57
58impl OpenFileDescription {
59    /// Creates a new open file description.
60    pub(crate) fn new(
61        file: Rc<RefCell<Inode>>,
62        offset: usize,
63        is_readable: bool,
64        is_writable: bool,
65        is_appending: bool,
66        is_nonblocking: bool,
67    ) -> Self {
68        file.borrow_mut().body.open(is_readable, is_writable);
69
70        Self {
71            file,
72            offset,
73            is_readable,
74            is_writable,
75            is_appending,
76            is_nonblocking,
77        }
78    }
79
80    /// Returns the i-node this open file description is operating on.
81    #[must_use]
82    pub(crate) fn file(&self) -> &Rc<RefCell<Inode>> {
83        &self.file
84    }
85
86    /// Returns true if you can read from this open file description.
87    #[must_use]
88    pub fn is_readable(&self) -> bool {
89        self.is_readable
90    }
91
92    /// Returns true if you can write to this open file description.
93    #[must_use]
94    pub fn is_writable(&self) -> bool {
95        self.is_writable
96    }
97
98    /// Returns true if this open file description is in non-blocking mode.
99    #[inline]
100    #[must_use]
101    pub fn is_nonblocking(&self) -> bool {
102        self.is_nonblocking
103    }
104
105    /// Sets whether this open file description is in non-blocking mode.
106    #[inline]
107    pub fn set_nonblocking(&mut self, is_nonblocking: bool) {
108        self.is_nonblocking = is_nonblocking
109    }
110
111    /// Returns true if a read operation on this open file description would not
112    /// block.
113    #[must_use]
114    pub fn is_ready_for_reading(&self) -> bool {
115        // If this file is not readable, it is considered ready for reading
116        // because any read operation on it would immediately fail.
117        !self.is_readable || self.file.borrow().body.is_ready_for_reading()
118    }
119
120    /// Returns true if a write operation on this open file description would
121    /// not block.
122    #[must_use]
123    pub fn is_ready_for_writing(&self) -> bool {
124        // If this file is not writable, it is considered ready for writing
125        // because any write operation on it would immediately fail.
126        !self.is_writable || self.file.borrow().body.is_ready_for_writing()
127    }
128
129    /// Registers a waker to be woken up when this open file description becomes
130    /// ready for reading.
131    ///
132    /// The caller should ensure that the OFD is not
133    /// [ready for reading](Self::is_ready_for_reading) when calling this
134    /// method, otherwise the waker may never be woken up.
135    pub(super) fn register_reader_waker(&mut self, waker: Weak<Cell<Option<Waker>>>) {
136        self.file.borrow_mut().body.register_reader_waker(waker);
137    }
138
139    /// Registers a waker to be woken up when this open file description becomes
140    /// ready for writing.
141    ///
142    /// The caller should ensure that the OFD is not
143    /// [ready for writing](Self::is_ready_for_writing) when calling this
144    /// method, otherwise the waker may never be woken up.
145    pub(super) fn register_writer_waker(&mut self, waker: Weak<Cell<Option<Waker>>>) {
146        self.file.borrow_mut().body.register_writer_waker(waker);
147    }
148
149    /// Reads from this open file description.
150    ///
151    /// Returns the number of bytes successfully read.
152    ///
153    /// This function does not support blocking read. If the file is not ready
154    /// for reading, it returns `Err(Errno::EAGAIN)`. Use
155    /// [`poll_read`](Self::poll_read) for polling support.
156    pub fn read(&mut self, buffer: &mut [u8]) -> Result<usize, Errno> {
157        match self.poll_read(buffer, Weak::new) {
158            Poll::Ready(result) => result,
159            Poll::Pending => Err(Errno::EAGAIN),
160        }
161    }
162
163    /// Polls for the result of reading from this open file description.
164    ///
165    /// The `get_waker` parameter is a function that returns a weak reference to
166    /// the waker of the current task. It is used to register the waker for
167    /// pending read operations on files like FIFOs. The function is called only
168    /// when the read operation would block, so it can be used to avoid
169    /// unnecessary allocations of wakers when the operation can complete
170    /// immediately. Since the waker is passed as a weak reference, the caller
171    /// must ensure that there is a strong reference to the waker that lives at
172    /// least until the file body wakes it up, otherwise the weak reference may
173    /// become invalid and the task may not be woken up correctly. The waker is
174    /// wrapped in `Cell<Option<Waker>>` to allow it to be shared among multiple
175    /// wake conditions and to allow it to be taken by the first condition that
176    /// wakes the task.
177    ///
178    /// The returned `Poll` indicates whether the read operation has completed
179    /// or is still pending. If it is `Poll::Ready`, the contained `Result`
180    /// indicates whether the read was successful and how many bytes were read,
181    /// or if it failed with an error. If it is `Poll::Pending`, it means a
182    /// waker has been registered and the caller should wait until it is woken
183    /// up, when this method should be called again.
184    pub fn poll_read<F>(
185        &mut self,
186        buffer: &mut [u8],
187        mut get_waker: F,
188    ) -> Poll<Result<usize, Errno>>
189    where
190        F: FnMut() -> Weak<Cell<Option<Waker>>>,
191    {
192        if !self.is_readable {
193            return Poll::Ready(Err(Errno::EBADF));
194        }
195
196        let file = self.file.borrow_mut();
197        let is_nonblocking = self.is_nonblocking;
198        let maybe_get_waker = move || {
199            if is_nonblocking {
200                Weak::new()
201            } else {
202                get_waker()
203            }
204        };
205
206        let poll = { file }
207            .body
208            .poll_read(buffer, self.offset, maybe_get_waker);
209
210        if is_nonblocking && poll.is_pending() {
211            return Poll::Ready(Err(Errno::EAGAIN));
212        }
213        if let Poll::Ready(Ok(count)) = poll {
214            self.offset += count;
215        }
216
217        poll
218    }
219
220    /// Writes to this open file description.
221    ///
222    /// Returns the number of bytes successfully written.
223    pub fn write(&mut self, buffer: &[u8]) -> Result<usize, Errno> {
224        match self.poll_write(buffer, Weak::new) {
225            Poll::Ready(result) => result,
226            Poll::Pending => Err(Errno::EAGAIN),
227        }
228    }
229
230    /// Polls for the result of writing to this open file description.
231    ///
232    /// The `get_waker` parameter is a function that returns a weak reference to
233    /// the waker of the current task. It is used to register the waker for
234    /// pending write operations on files like FIFOs. The function is called
235    /// only when the write operation would block, so it can be used to avoid
236    /// unnecessary allocations of wakers when the operation can complete
237    /// immediately. Since the waker is passed as a weak reference, the caller
238    /// must ensure that there is a strong reference to the waker that lives at
239    /// least until the file body wakes it up, otherwise the weak reference may
240    /// become invalid and the task may not be woken up correctly. The waker is
241    /// wrapped in `Cell<Option<Waker>>` to allow it to be shared among multiple
242    /// wake conditions and to allow it to be taken by the first condition that
243    /// wakes the task.
244    ///
245    /// The returned `Poll` indicates whether the write operation has completed
246    /// or is still pending. If it is `Poll::Ready`, the contained `Result`
247    /// indicates whether the write was successful and how many bytes were
248    /// written, or if it failed with an error. If it is `Poll::Pending`, it
249    /// means a waker has been registered and the caller should wait until it is
250    /// woken up, when this method should be called again.
251    pub fn poll_write<F>(&mut self, buffer: &[u8], mut get_waker: F) -> Poll<Result<usize, Errno>>
252    where
253        F: FnMut() -> Weak<Cell<Option<Waker>>>,
254    {
255        if !self.is_writable {
256            return Poll::Ready(Err(Errno::EBADF));
257        }
258
259        let file = self.file.borrow_mut();
260        let offset = if self.is_appending {
261            file.body.size()
262        } else {
263            self.offset
264        };
265        let is_nonblocking = self.is_nonblocking;
266        let maybe_get_waker = move || {
267            if is_nonblocking {
268                Weak::new()
269            } else {
270                get_waker()
271            }
272        };
273
274        let poll = { file }.body.poll_write(buffer, offset, maybe_get_waker);
275
276        if is_nonblocking && poll.is_pending() {
277            return Poll::Ready(Err(Errno::EAGAIN));
278        }
279        if let Poll::Ready(Ok(count)) = poll {
280            self.offset = offset + count;
281        }
282
283        poll
284    }
285
286    /// Drives a `write(2)`-equivalent loop on this open file description.
287    ///
288    /// This helper encapsulates the POSIX rule that a single blocking `write(2)`
289    /// call to a pipe must, in the absence of a signal, transfer the entire
290    /// buffer even when the buffer is larger than [`PIPE_BUF`]. For a blocking
291    /// FIFO it repeatedly calls [`poll_write`](Self::poll_write), advancing
292    /// `bytes_written` until either the buffer is exhausted (returns
293    /// `Ready(Ok(*bytes_written))`), no more bytes fit and the next call would
294    /// block (returns `Pending` after registering the waker), or an error is
295    /// reported (returns `Ready(Err(_))` if no bytes have been transferred yet,
296    /// or `Ready(Ok(*bytes_written))` otherwise).
297    ///
298    /// For non-FIFO files or non-blocking file descriptors it returns after a
299    /// single `poll_write` call without looping; this matches POSIX `write(2)`
300    /// semantics for those cases.
301    ///
302    /// `bytes_written` is updated in place so that the caller can resume the
303    /// operation across multiple polls without losing the running total when a
304    /// signal interrupts the operation. This function will panic if
305    /// `bytes_written` exceeds the length of `buffer`. `get_waker` has the same
306    /// contract as for [`poll_write`](Self::poll_write).
307    ///
308    /// [`PIPE_BUF`]: super::PIPE_BUF
309    pub fn poll_write_full<F>(
310        &mut self,
311        buffer: &[u8],
312        bytes_written: &mut usize,
313        mut get_waker: F,
314    ) -> Poll<Result<usize, Errno>>
315    where
316        F: FnMut() -> Weak<Cell<Option<Waker>>>,
317    {
318        // Whether to keep looping after a successful partial write. Only a
319        // blocking FIFO requires looping; everything else returns after the
320        // first successful poll_write call.
321        let loop_on_success =
322            !self.is_nonblocking && self.file.borrow().body.r#type() == FileType::Fifo;
323        loop {
324            let remaining = &buffer[*bytes_written..];
325            if remaining.is_empty() {
326                return Poll::Ready(Ok(*bytes_written));
327            }
328            match self.poll_write(remaining, &mut get_waker) {
329                Poll::Ready(Ok(n)) => {
330                    *bytes_written += n;
331                    if !loop_on_success || n == 0 {
332                        return Poll::Ready(Ok(*bytes_written));
333                    }
334                    // Blocking FIFO with bytes still remaining: try again.
335                }
336                Poll::Ready(Err(e)) => {
337                    return Poll::Ready(if *bytes_written > 0 {
338                        Ok(*bytes_written)
339                    } else {
340                        Err(e)
341                    });
342                }
343                Poll::Pending => return Poll::Pending,
344            }
345        }
346    }
347
348    /// Moves the file offset and returns the new offset.
349    pub fn seek(&mut self, position: SeekFrom) -> Result<usize, Errno> {
350        let len = match &self.file.borrow().body {
351            FileBody::Regular { content, .. } => content.len(),
352            FileBody::Directory { files, .. } => files.len(),
353            FileBody::Fifo { .. } => return Err(Errno::ESPIPE),
354            FileBody::Symlink { .. } | FileBody::Terminal { .. } => return Err(Errno::ENOTSUP),
355        };
356
357        let new_offset = match position {
358            SeekFrom::Start(offset) => offset.try_into().ok(),
359            SeekFrom::Current(offset) => offset
360                .try_into()
361                .ok()
362                .and_then(|offset| self.offset.checked_add_signed(offset)),
363            SeekFrom::End(offset) => offset
364                .try_into()
365                .ok()
366                .and_then(|offset| len.checked_add_signed(offset)),
367        };
368
369        let new_offset = new_offset.ok_or(Errno::EINVAL)?;
370        self.offset = new_offset;
371        Ok(new_offset)
372    }
373
374    /// Returns the i-node this open file description is operating on.
375    #[must_use]
376    pub fn inode(&self) -> &Rc<RefCell<Inode>> {
377        &self.file
378    }
379}
380
381/// State of a file descriptor.
382#[derive(Clone, Debug)]
383pub struct FdBody {
384    /// Underlying open file description.
385    pub open_file_description: Rc<RefCell<OpenFileDescription>>,
386    /// Flags for this file descriptor
387    pub flags: EnumSet<FdFlag>,
388}
389
390impl PartialEq for FdBody {
391    fn eq(&self, rhs: &Self) -> bool {
392        Rc::ptr_eq(&self.open_file_description, &rhs.open_file_description)
393            && self.flags == rhs.flags
394    }
395}
396
397impl Eq for FdBody {}
398
399#[cfg(test)]
400mod tests {
401    use super::super::{Mode, PIPE_SIZE, WakerSet};
402    use super::*;
403    use assert_matches::assert_matches;
404    use std::collections::VecDeque;
405
406    #[test]
407    fn regular_file_read_unreadable() {
408        let mut open_file = OpenFileDescription {
409            file: Rc::new(RefCell::new(Inode::new([]))),
410            offset: 0,
411            is_readable: false,
412            is_writable: false,
413            is_appending: false,
414            is_nonblocking: false,
415        };
416
417        let mut buffer = [0];
418        let result = open_file.read(&mut buffer);
419        assert_eq!(result, Err(Errno::EBADF));
420    }
421
422    #[test]
423    fn regular_file_read_more_than_content() {
424        let mut open_file = OpenFileDescription {
425            file: Rc::new(RefCell::new(Inode::new([1, 2, 3]))),
426            offset: 1,
427            is_readable: true,
428            is_writable: false,
429            is_appending: false,
430            is_nonblocking: false,
431        };
432
433        let mut buffer = [0; 3];
434        let result = open_file.read(&mut buffer);
435        assert_eq!(result, Ok(2));
436        assert_eq!(open_file.offset, 3);
437        assert_eq!(buffer[..2], [2, 3]);
438    }
439
440    #[test]
441    fn fifo_nonblocking_read_not_ready() {
442        let fifo = Rc::new(RefCell::new(Inode {
443            body: FileBody::Fifo {
444                content: VecDeque::new(),
445                readers: 1,
446                writers: 1,
447                pending_open_wakers: WakerSet::new(),
448                pending_read_wakers: WakerSet::new(),
449                pending_write_wakers: WakerSet::new(),
450            },
451            permissions: Mode::default(),
452        }));
453        let mut open_file = OpenFileDescription::new(
454            fifo, /* offset */ 0, /* is_readable */ true, /* is_writable */ false,
455            /* is_appending */ false, /* is_nonblocking */ true,
456        );
457
458        let mut buffer = [0; 4];
459        let result = open_file.poll_read(&mut buffer, || unreachable!());
460        assert_eq!(result, Poll::Ready(Err(Errno::EAGAIN)));
461    }
462
463    #[test]
464    fn fifo_nonblocking_read_ready() {
465        let fifo = Rc::new(RefCell::new(Inode {
466            body: FileBody::Fifo {
467                content: VecDeque::from([0, 1, 2, 3]),
468                readers: 1,
469                writers: 1,
470                pending_open_wakers: WakerSet::new(),
471                pending_read_wakers: WakerSet::new(),
472                pending_write_wakers: WakerSet::new(),
473            },
474            permissions: Mode::default(),
475        }));
476        let mut open_file = OpenFileDescription::new(
477            fifo, /* offset */ 0, /* is_readable */ true, /* is_writable */ false,
478            /* is_appending */ false, /* is_nonblocking */ true,
479        );
480
481        let mut buffer = [0; 4];
482        let result = open_file.poll_read(&mut buffer, || unreachable!());
483        assert_eq!(result, Poll::Ready(Ok(4)));
484        assert_eq!(buffer, [0, 1, 2, 3]);
485    }
486
487    #[test]
488    fn regular_file_write_unwritable() {
489        let mut open_file = OpenFileDescription {
490            file: Rc::new(RefCell::new(Inode::new([]))),
491            offset: 0,
492            is_readable: false,
493            is_writable: false,
494            is_appending: false,
495            is_nonblocking: false,
496        };
497
498        let result = open_file.write(&[0]);
499        assert_eq!(result, Err(Errno::EBADF));
500    }
501
502    #[test]
503    fn regular_file_write_more_than_content() {
504        let mut open_file = OpenFileDescription {
505            file: Rc::new(RefCell::new(Inode::new([1, 2, 3]))),
506            offset: 1,
507            is_readable: false,
508            is_writable: true,
509            is_appending: false,
510            is_nonblocking: false,
511        };
512
513        let result = open_file.write(&[9, 8, 7, 6]);
514        assert_eq!(result, Ok(4));
515        assert_eq!(open_file.offset, 5);
516        assert_matches!(
517            &open_file.file.borrow().body,
518            FileBody::Regular { content, .. } => {
519                assert_eq!(content[..], [1, 9, 8, 7, 6]);
520            }
521        );
522    }
523
524    #[test]
525    fn regular_file_write_appending() {
526        let mut open_file = OpenFileDescription {
527            file: Rc::new(RefCell::new(Inode::new([1, 2, 3]))),
528            offset: 1,
529            is_readable: false,
530            is_writable: true,
531            is_appending: true,
532            is_nonblocking: false,
533        };
534
535        let result = open_file.write(&[4, 5]);
536        assert_eq!(result, Ok(2));
537        assert_eq!(open_file.offset, 5);
538        assert_matches!(
539            &open_file.file.borrow().body,
540            FileBody::Regular { content, .. } => {
541                assert_eq!(content[..], [1, 2, 3, 4, 5]);
542            }
543        );
544    }
545
546    #[test]
547    fn fifo_nonblocking_write_not_ready() {
548        let fifo = Rc::new(RefCell::new(Inode {
549            body: FileBody::Fifo {
550                content: VecDeque::from([0; PIPE_SIZE]),
551                readers: 1,
552                writers: 1,
553                pending_open_wakers: WakerSet::new(),
554                pending_read_wakers: WakerSet::new(),
555                pending_write_wakers: WakerSet::new(),
556            },
557            permissions: Mode::default(),
558        }));
559        let mut open_file = OpenFileDescription::new(
560            fifo, /* offset */ 0, /* is_readable */ false, /* is_writable */ true,
561            /* is_appending */ false, /* is_nonblocking */ true,
562        );
563
564        let result = open_file.poll_write(&[0; 4], || unreachable!());
565        assert_eq!(result, Poll::Ready(Err(Errno::EAGAIN)));
566    }
567
568    #[test]
569    fn fifo_nonblocking_write_ready() {
570        let fifo = Rc::new(RefCell::new(Inode {
571            body: FileBody::Fifo {
572                content: VecDeque::new(),
573                readers: 1,
574                writers: 1,
575                pending_open_wakers: WakerSet::new(),
576                pending_read_wakers: WakerSet::new(),
577                pending_write_wakers: WakerSet::new(),
578            },
579            permissions: Mode::default(),
580        }));
581        let mut open_file = OpenFileDescription::new(
582            fifo, /* offset */ 0, /* is_readable */ false, /* is_writable */ true,
583            /* is_appending */ false, /* is_nonblocking */ true,
584        );
585
586        let result = open_file.poll_write(&[9; 4], || unreachable!());
587        assert_eq!(result, Poll::Ready(Ok(4)));
588    }
589
590    #[test]
591    fn poll_write_full_regular_file_returns_after_one_call() {
592        // For a regular file, poll_write_full must not loop. Even though
593        // FileBody::Regular always writes the whole buffer in one call, the
594        // helper must not branch on FIFO-specific logic.
595        let mut open_file = OpenFileDescription {
596            file: Rc::new(RefCell::new(Inode::new([]))),
597            offset: 0,
598            is_readable: false,
599            is_writable: true,
600            is_appending: false,
601            is_nonblocking: false,
602        };
603        let mut bytes_written = 0usize;
604        let buffer = [1, 2, 3, 4, 5];
605        let result = open_file.poll_write_full(&buffer, &mut bytes_written, || unreachable!());
606        assert_eq!(result, Poll::Ready(Ok(5)));
607        assert_eq!(bytes_written, 5);
608        assert_matches!(
609            &open_file.file.borrow().body,
610            FileBody::Regular { content, .. } => {
611                assert_eq!(content[..], [1, 2, 3, 4, 5]);
612            }
613        );
614    }
615
616    #[test]
617    fn poll_write_full_blocking_fifo_loops_until_full() {
618        // For a blocking FIFO, poll_write_full must keep calling poll_write
619        // until either the buffer is exhausted or no more bytes fit (returning
620        // Pending).
621        let fifo = Rc::new(RefCell::new(Inode {
622            body: FileBody::Fifo {
623                content: VecDeque::new(),
624                readers: 1,
625                writers: 1,
626                pending_open_wakers: WakerSet::new(),
627                pending_read_wakers: WakerSet::new(),
628                pending_write_wakers: WakerSet::new(),
629            },
630            permissions: Mode::default(),
631        }));
632        let mut open_file = OpenFileDescription::new(
633            fifo, /* offset */ 0, /* is_readable */ false, /* is_writable */ true,
634            /* is_appending */ false, /* is_nonblocking */ false,
635        );
636
637        // Write a buffer larger than the pipe capacity. The helper writes as
638        // much as fits (PIPE_SIZE bytes) and then returns Pending.
639        let buffer = [7u8; PIPE_SIZE + 1];
640        let waker_holder: Rc<Cell<Option<Waker>>> = Rc::new(Cell::new(Some(Waker::noop().clone())));
641        let mut bytes_written = 0usize;
642        let result =
643            open_file.poll_write_full(&buffer, &mut bytes_written, || Rc::downgrade(&waker_holder));
644        assert_eq!(result, Poll::Pending);
645        assert_eq!(bytes_written, PIPE_SIZE);
646    }
647
648    #[test]
649    fn poll_write_full_nonblocking_fifo_returns_after_one_chunk() {
650        // For a non-blocking FIFO, poll_write_full must not loop even if more
651        // bytes could be written across multiple poll_write calls.
652        let fifo = Rc::new(RefCell::new(Inode {
653            body: FileBody::Fifo {
654                content: VecDeque::new(),
655                readers: 1,
656                writers: 1,
657                pending_open_wakers: WakerSet::new(),
658                pending_read_wakers: WakerSet::new(),
659                pending_write_wakers: WakerSet::new(),
660            },
661            permissions: Mode::default(),
662        }));
663        let mut open_file = OpenFileDescription::new(
664            fifo, /* offset */ 0, /* is_readable */ false, /* is_writable */ true,
665            /* is_appending */ false, /* is_nonblocking */ true,
666        );
667
668        let buffer = [3u8; PIPE_SIZE * 2];
669        let mut bytes_written = 0usize;
670        let result = open_file.poll_write_full(&buffer, &mut bytes_written, || unreachable!());
671        // The first chunk fits entirely; the helper returns immediately rather
672        // than calling poll_write again (which would return EAGAIN).
673        assert_eq!(result, Poll::Ready(Ok(PIPE_SIZE)));
674        assert_eq!(bytes_written, PIPE_SIZE);
675    }
676
677    #[test]
678    fn regular_file_seek_from_start() {
679        let mut open_file = OpenFileDescription {
680            file: Rc::new(RefCell::new(Inode::new([]))),
681            offset: 3,
682            is_readable: true,
683            is_writable: true,
684            is_appending: false,
685            is_nonblocking: false,
686        };
687
688        let result = open_file.seek(SeekFrom::Start(10));
689        assert_eq!(result, Ok(10));
690        assert_eq!(open_file.offset, 10);
691
692        let result = open_file.seek(SeekFrom::Start(0));
693        assert_eq!(result, Ok(0));
694        assert_eq!(open_file.offset, 0);
695
696        let result = open_file.seek(SeekFrom::Start(3));
697        assert_eq!(result, Ok(3));
698        assert_eq!(open_file.offset, 3);
699    }
700
701    #[test]
702    fn regular_file_seek_from_current() {
703        let mut open_file = OpenFileDescription {
704            file: Rc::new(RefCell::new(Inode::new([]))),
705            offset: 5,
706            is_readable: true,
707            is_writable: true,
708            is_appending: false,
709            is_nonblocking: false,
710        };
711
712        let result = open_file.seek(SeekFrom::Current(10));
713        assert_eq!(result, Ok(15));
714        assert_eq!(open_file.offset, 15);
715
716        let result = open_file.seek(SeekFrom::Current(0));
717        assert_eq!(result, Ok(15));
718        assert_eq!(open_file.offset, 15);
719
720        let result = open_file.seek(SeekFrom::Current(-5));
721        assert_eq!(result, Ok(10));
722        assert_eq!(open_file.offset, 10);
723
724        let result = open_file.seek(SeekFrom::Current(-11));
725        assert_eq!(result, Err(Errno::EINVAL));
726        assert_eq!(open_file.offset, 10);
727    }
728
729    #[test]
730    fn regular_file_seek_from_end() {
731        let mut open_file = OpenFileDescription {
732            file: Rc::new(RefCell::new(Inode::new([1, 2, 3]))),
733            offset: 2,
734            is_readable: true,
735            is_writable: true,
736            is_appending: false,
737            is_nonblocking: false,
738        };
739
740        let result = open_file.seek(SeekFrom::End(7));
741        assert_eq!(result, Ok(10));
742        assert_eq!(open_file.offset, 10);
743
744        let result = open_file.seek(SeekFrom::End(0));
745        assert_eq!(result, Ok(3));
746        assert_eq!(open_file.offset, 3);
747
748        let result = open_file.seek(SeekFrom::End(-3));
749        assert_eq!(result, Ok(0));
750        assert_eq!(open_file.offset, 0);
751
752        let result = open_file.seek(SeekFrom::End(-4));
753        assert_eq!(result, Err(Errno::EINVAL));
754        assert_eq!(open_file.offset, 0);
755    }
756
757    #[test]
758    fn fifo_reader_drop() {
759        let file = Rc::new(RefCell::new(Inode {
760            body: FileBody::Fifo {
761                content: VecDeque::new(),
762                readers: 1,
763                writers: 1,
764                pending_open_wakers: WakerSet::new(),
765                pending_read_wakers: WakerSet::new(),
766                pending_write_wakers: WakerSet::new(),
767            },
768            permissions: Mode::default(),
769        }));
770        let open_file = OpenFileDescription {
771            file: Rc::clone(&file),
772            offset: 0,
773            is_readable: true,
774            is_writable: false,
775            is_appending: false,
776            is_nonblocking: false,
777        };
778        drop(open_file);
779
780        assert_matches!(&file.borrow().body, FileBody::Fifo { readers, writers, .. } => {
781            assert_eq!(*readers, 0);
782            assert_eq!(*writers, 1);
783        });
784    }
785
786    #[test]
787    fn fifo_writer_drop() {
788        let file = Rc::new(RefCell::new(Inode {
789            body: FileBody::Fifo {
790                content: VecDeque::new(),
791                readers: 1,
792                writers: 1,
793                pending_open_wakers: WakerSet::new(),
794                pending_read_wakers: WakerSet::new(),
795                pending_write_wakers: WakerSet::new(),
796            },
797            permissions: Mode::default(),
798        }));
799        let open_file = OpenFileDescription {
800            file: Rc::clone(&file),
801            offset: 0,
802            is_readable: false,
803            is_writable: true,
804            is_appending: false,
805            is_nonblocking: false,
806        };
807        drop(open_file);
808
809        assert_matches!(&file.borrow().body, FileBody::Fifo { readers, writers, .. } => {
810            assert_eq!(*readers, 1);
811            assert_eq!(*writers, 0);
812        });
813    }
814}