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    inode: 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 inode = self.inode.borrow_mut();
54        inode.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        inode: 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        inode.borrow_mut().body.open(is_readable, is_writable);
69
70        Self {
71            inode,
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 fn inode(&self) -> &Rc<RefCell<Inode>> {
83        &self.inode
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.inode.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.inode.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.inode.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.inode.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.inode.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.inode.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.inode.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.inode.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
375/// State of a file descriptor.
376#[derive(Clone, Debug)]
377pub struct FdBody {
378    /// Underlying open file description.
379    pub open_file_description: Rc<RefCell<OpenFileDescription>>,
380    /// Flags for this file descriptor
381    pub flags: EnumSet<FdFlag>,
382}
383
384impl PartialEq for FdBody {
385    fn eq(&self, rhs: &Self) -> bool {
386        Rc::ptr_eq(&self.open_file_description, &rhs.open_file_description)
387            && self.flags == rhs.flags
388    }
389}
390
391impl Eq for FdBody {}
392
393#[cfg(test)]
394mod tests {
395    use super::super::{Mode, PIPE_SIZE, WakerSet};
396    use super::*;
397    use assert_matches::assert_matches;
398    use std::collections::VecDeque;
399
400    #[test]
401    fn regular_file_read_unreadable() {
402        let mut open_file = OpenFileDescription {
403            inode: Rc::new(RefCell::new(Inode::new([]))),
404            offset: 0,
405            is_readable: false,
406            is_writable: false,
407            is_appending: false,
408            is_nonblocking: false,
409        };
410
411        let mut buffer = [0];
412        let result = open_file.read(&mut buffer);
413        assert_eq!(result, Err(Errno::EBADF));
414    }
415
416    #[test]
417    fn regular_file_read_more_than_content() {
418        let mut open_file = OpenFileDescription {
419            inode: Rc::new(RefCell::new(Inode::new([1, 2, 3]))),
420            offset: 1,
421            is_readable: true,
422            is_writable: false,
423            is_appending: false,
424            is_nonblocking: false,
425        };
426
427        let mut buffer = [0; 3];
428        let result = open_file.read(&mut buffer);
429        assert_eq!(result, Ok(2));
430        assert_eq!(open_file.offset, 3);
431        assert_eq!(buffer[..2], [2, 3]);
432    }
433
434    #[test]
435    fn fifo_nonblocking_read_not_ready() {
436        let fifo = Rc::new(RefCell::new(Inode {
437            body: FileBody::Fifo {
438                content: VecDeque::new(),
439                readers: 1,
440                writers: 1,
441                pending_open_wakers: WakerSet::new(),
442                pending_read_wakers: WakerSet::new(),
443                pending_write_wakers: WakerSet::new(),
444            },
445            permissions: Mode::default(),
446        }));
447        let mut open_file = OpenFileDescription::new(
448            fifo, /* offset */ 0, /* is_readable */ true, /* is_writable */ false,
449            /* is_appending */ false, /* is_nonblocking */ true,
450        );
451
452        let mut buffer = [0; 4];
453        let result = open_file.poll_read(&mut buffer, || unreachable!());
454        assert_eq!(result, Poll::Ready(Err(Errno::EAGAIN)));
455    }
456
457    #[test]
458    fn fifo_nonblocking_read_ready() {
459        let fifo = Rc::new(RefCell::new(Inode {
460            body: FileBody::Fifo {
461                content: VecDeque::from([0, 1, 2, 3]),
462                readers: 1,
463                writers: 1,
464                pending_open_wakers: WakerSet::new(),
465                pending_read_wakers: WakerSet::new(),
466                pending_write_wakers: WakerSet::new(),
467            },
468            permissions: Mode::default(),
469        }));
470        let mut open_file = OpenFileDescription::new(
471            fifo, /* offset */ 0, /* is_readable */ true, /* is_writable */ false,
472            /* is_appending */ false, /* is_nonblocking */ true,
473        );
474
475        let mut buffer = [0; 4];
476        let result = open_file.poll_read(&mut buffer, || unreachable!());
477        assert_eq!(result, Poll::Ready(Ok(4)));
478        assert_eq!(buffer, [0, 1, 2, 3]);
479    }
480
481    #[test]
482    fn regular_file_write_unwritable() {
483        let mut open_file = OpenFileDescription {
484            inode: Rc::new(RefCell::new(Inode::new([]))),
485            offset: 0,
486            is_readable: false,
487            is_writable: false,
488            is_appending: false,
489            is_nonblocking: false,
490        };
491
492        let result = open_file.write(&[0]);
493        assert_eq!(result, Err(Errno::EBADF));
494    }
495
496    #[test]
497    fn regular_file_write_more_than_content() {
498        let mut open_file = OpenFileDescription {
499            inode: Rc::new(RefCell::new(Inode::new([1, 2, 3]))),
500            offset: 1,
501            is_readable: false,
502            is_writable: true,
503            is_appending: false,
504            is_nonblocking: false,
505        };
506
507        let result = open_file.write(&[9, 8, 7, 6]);
508        assert_eq!(result, Ok(4));
509        assert_eq!(open_file.offset, 5);
510        assert_matches!(
511            &open_file.inode.borrow().body,
512            FileBody::Regular { content, .. } => {
513                assert_eq!(content[..], [1, 9, 8, 7, 6]);
514            }
515        );
516    }
517
518    #[test]
519    fn regular_file_write_appending() {
520        let mut open_file = OpenFileDescription {
521            inode: Rc::new(RefCell::new(Inode::new([1, 2, 3]))),
522            offset: 1,
523            is_readable: false,
524            is_writable: true,
525            is_appending: true,
526            is_nonblocking: false,
527        };
528
529        let result = open_file.write(&[4, 5]);
530        assert_eq!(result, Ok(2));
531        assert_eq!(open_file.offset, 5);
532        assert_matches!(
533            &open_file.inode.borrow().body,
534            FileBody::Regular { content, .. } => {
535                assert_eq!(content[..], [1, 2, 3, 4, 5]);
536            }
537        );
538    }
539
540    #[test]
541    fn fifo_nonblocking_write_not_ready() {
542        let fifo = Rc::new(RefCell::new(Inode {
543            body: FileBody::Fifo {
544                content: VecDeque::from([0; PIPE_SIZE]),
545                readers: 1,
546                writers: 1,
547                pending_open_wakers: WakerSet::new(),
548                pending_read_wakers: WakerSet::new(),
549                pending_write_wakers: WakerSet::new(),
550            },
551            permissions: Mode::default(),
552        }));
553        let mut open_file = OpenFileDescription::new(
554            fifo, /* offset */ 0, /* is_readable */ false, /* is_writable */ true,
555            /* is_appending */ false, /* is_nonblocking */ true,
556        );
557
558        let result = open_file.poll_write(&[0; 4], || unreachable!());
559        assert_eq!(result, Poll::Ready(Err(Errno::EAGAIN)));
560    }
561
562    #[test]
563    fn fifo_nonblocking_write_ready() {
564        let fifo = Rc::new(RefCell::new(Inode {
565            body: FileBody::Fifo {
566                content: VecDeque::new(),
567                readers: 1,
568                writers: 1,
569                pending_open_wakers: WakerSet::new(),
570                pending_read_wakers: WakerSet::new(),
571                pending_write_wakers: WakerSet::new(),
572            },
573            permissions: Mode::default(),
574        }));
575        let mut open_file = OpenFileDescription::new(
576            fifo, /* offset */ 0, /* is_readable */ false, /* is_writable */ true,
577            /* is_appending */ false, /* is_nonblocking */ true,
578        );
579
580        let result = open_file.poll_write(&[9; 4], || unreachable!());
581        assert_eq!(result, Poll::Ready(Ok(4)));
582    }
583
584    #[test]
585    fn poll_write_full_regular_file_returns_after_one_call() {
586        // For a regular file, poll_write_full must not loop. Even though
587        // FileBody::Regular always writes the whole buffer in one call, the
588        // helper must not branch on FIFO-specific logic.
589        let mut open_file = OpenFileDescription {
590            inode: Rc::new(RefCell::new(Inode::new([]))),
591            offset: 0,
592            is_readable: false,
593            is_writable: true,
594            is_appending: false,
595            is_nonblocking: false,
596        };
597        let mut bytes_written = 0usize;
598        let buffer = [1, 2, 3, 4, 5];
599        let result = open_file.poll_write_full(&buffer, &mut bytes_written, || unreachable!());
600        assert_eq!(result, Poll::Ready(Ok(5)));
601        assert_eq!(bytes_written, 5);
602        assert_matches!(
603            &open_file.inode.borrow().body,
604            FileBody::Regular { content, .. } => {
605                assert_eq!(content[..], [1, 2, 3, 4, 5]);
606            }
607        );
608    }
609
610    #[test]
611    fn poll_write_full_blocking_fifo_loops_until_full() {
612        // For a blocking FIFO, poll_write_full must keep calling poll_write
613        // until either the buffer is exhausted or no more bytes fit (returning
614        // Pending).
615        let fifo = Rc::new(RefCell::new(Inode {
616            body: FileBody::Fifo {
617                content: VecDeque::new(),
618                readers: 1,
619                writers: 1,
620                pending_open_wakers: WakerSet::new(),
621                pending_read_wakers: WakerSet::new(),
622                pending_write_wakers: WakerSet::new(),
623            },
624            permissions: Mode::default(),
625        }));
626        let mut open_file = OpenFileDescription::new(
627            fifo, /* offset */ 0, /* is_readable */ false, /* is_writable */ true,
628            /* is_appending */ false, /* is_nonblocking */ false,
629        );
630
631        // Write a buffer larger than the pipe capacity. The helper writes as
632        // much as fits (PIPE_SIZE bytes) and then returns Pending.
633        let buffer = [7u8; PIPE_SIZE + 1];
634        let waker_holder: Rc<Cell<Option<Waker>>> = Rc::new(Cell::new(Some(Waker::noop().clone())));
635        let mut bytes_written = 0usize;
636        let result =
637            open_file.poll_write_full(&buffer, &mut bytes_written, || Rc::downgrade(&waker_holder));
638        assert_eq!(result, Poll::Pending);
639        assert_eq!(bytes_written, PIPE_SIZE);
640    }
641
642    #[test]
643    fn poll_write_full_nonblocking_fifo_returns_after_one_chunk() {
644        // For a non-blocking FIFO, poll_write_full must not loop even if more
645        // bytes could be written across multiple poll_write calls.
646        let fifo = Rc::new(RefCell::new(Inode {
647            body: FileBody::Fifo {
648                content: VecDeque::new(),
649                readers: 1,
650                writers: 1,
651                pending_open_wakers: WakerSet::new(),
652                pending_read_wakers: WakerSet::new(),
653                pending_write_wakers: WakerSet::new(),
654            },
655            permissions: Mode::default(),
656        }));
657        let mut open_file = OpenFileDescription::new(
658            fifo, /* offset */ 0, /* is_readable */ false, /* is_writable */ true,
659            /* is_appending */ false, /* is_nonblocking */ true,
660        );
661
662        let buffer = [3u8; PIPE_SIZE * 2];
663        let mut bytes_written = 0usize;
664        let result = open_file.poll_write_full(&buffer, &mut bytes_written, || unreachable!());
665        // The first chunk fits entirely; the helper returns immediately rather
666        // than calling poll_write again (which would return EAGAIN).
667        assert_eq!(result, Poll::Ready(Ok(PIPE_SIZE)));
668        assert_eq!(bytes_written, PIPE_SIZE);
669    }
670
671    #[test]
672    fn regular_file_seek_from_start() {
673        let mut open_file = OpenFileDescription {
674            inode: Rc::new(RefCell::new(Inode::new([]))),
675            offset: 3,
676            is_readable: true,
677            is_writable: true,
678            is_appending: false,
679            is_nonblocking: false,
680        };
681
682        let result = open_file.seek(SeekFrom::Start(10));
683        assert_eq!(result, Ok(10));
684        assert_eq!(open_file.offset, 10);
685
686        let result = open_file.seek(SeekFrom::Start(0));
687        assert_eq!(result, Ok(0));
688        assert_eq!(open_file.offset, 0);
689
690        let result = open_file.seek(SeekFrom::Start(3));
691        assert_eq!(result, Ok(3));
692        assert_eq!(open_file.offset, 3);
693    }
694
695    #[test]
696    fn regular_file_seek_from_current() {
697        let mut open_file = OpenFileDescription {
698            inode: Rc::new(RefCell::new(Inode::new([]))),
699            offset: 5,
700            is_readable: true,
701            is_writable: true,
702            is_appending: false,
703            is_nonblocking: false,
704        };
705
706        let result = open_file.seek(SeekFrom::Current(10));
707        assert_eq!(result, Ok(15));
708        assert_eq!(open_file.offset, 15);
709
710        let result = open_file.seek(SeekFrom::Current(0));
711        assert_eq!(result, Ok(15));
712        assert_eq!(open_file.offset, 15);
713
714        let result = open_file.seek(SeekFrom::Current(-5));
715        assert_eq!(result, Ok(10));
716        assert_eq!(open_file.offset, 10);
717
718        let result = open_file.seek(SeekFrom::Current(-11));
719        assert_eq!(result, Err(Errno::EINVAL));
720        assert_eq!(open_file.offset, 10);
721    }
722
723    #[test]
724    fn regular_file_seek_from_end() {
725        let mut open_file = OpenFileDescription {
726            inode: Rc::new(RefCell::new(Inode::new([1, 2, 3]))),
727            offset: 2,
728            is_readable: true,
729            is_writable: true,
730            is_appending: false,
731            is_nonblocking: false,
732        };
733
734        let result = open_file.seek(SeekFrom::End(7));
735        assert_eq!(result, Ok(10));
736        assert_eq!(open_file.offset, 10);
737
738        let result = open_file.seek(SeekFrom::End(0));
739        assert_eq!(result, Ok(3));
740        assert_eq!(open_file.offset, 3);
741
742        let result = open_file.seek(SeekFrom::End(-3));
743        assert_eq!(result, Ok(0));
744        assert_eq!(open_file.offset, 0);
745
746        let result = open_file.seek(SeekFrom::End(-4));
747        assert_eq!(result, Err(Errno::EINVAL));
748        assert_eq!(open_file.offset, 0);
749    }
750
751    #[test]
752    fn fifo_reader_drop() {
753        let file = Rc::new(RefCell::new(Inode {
754            body: FileBody::Fifo {
755                content: VecDeque::new(),
756                readers: 1,
757                writers: 1,
758                pending_open_wakers: WakerSet::new(),
759                pending_read_wakers: WakerSet::new(),
760                pending_write_wakers: WakerSet::new(),
761            },
762            permissions: Mode::default(),
763        }));
764        let open_file = OpenFileDescription {
765            inode: Rc::clone(&file),
766            offset: 0,
767            is_readable: true,
768            is_writable: false,
769            is_appending: false,
770            is_nonblocking: false,
771        };
772        drop(open_file);
773
774        assert_matches!(&file.borrow().body, FileBody::Fifo { readers, writers, .. } => {
775            assert_eq!(*readers, 0);
776            assert_eq!(*writers, 1);
777        });
778    }
779
780    #[test]
781    fn fifo_writer_drop() {
782        let file = Rc::new(RefCell::new(Inode {
783            body: FileBody::Fifo {
784                content: VecDeque::new(),
785                readers: 1,
786                writers: 1,
787                pending_open_wakers: WakerSet::new(),
788                pending_read_wakers: WakerSet::new(),
789                pending_write_wakers: WakerSet::new(),
790            },
791            permissions: Mode::default(),
792        }));
793        let open_file = OpenFileDescription {
794            inode: Rc::clone(&file),
795            offset: 0,
796            is_readable: false,
797            is_writable: true,
798            is_appending: false,
799            is_nonblocking: false,
800        };
801        drop(open_file);
802
803        assert_matches!(&file.borrow().body, FileBody::Fifo { readers, writers, .. } => {
804            assert_eq!(*readers, 1);
805            assert_eq!(*writers, 0);
806        });
807    }
808}