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::FdFlag;
21use super::FileBody;
22use super::Inode;
23use enumset::EnumSet;
24use std::cell::RefCell;
25use std::fmt::Debug;
26use std::io::SeekFrom;
27use std::rc::Rc;
28
29/// Maximum number of bytes guaranteed to be atomic when writing to a pipe.
30///
31/// This value is for the virtual system implementation.
32/// The real system may have a different configuration.
33pub const PIPE_BUF: usize = 512;
34
35/// Maximum number of bytes a pipe can hold at a time.
36///
37/// This value is for the virtual system implementation.
38/// The real system may have a different configuration.
39pub const PIPE_SIZE: usize = PIPE_BUF * 2;
40
41/// State of a file opened for reading and/or writing
42#[derive(Clone, Debug)]
43#[non_exhaustive]
44pub struct OpenFileDescription {
45    /// File content and metadata
46    pub(crate) file: Rc<RefCell<Inode>>,
47    /// Position in bytes to perform next I/O operation at
48    pub(crate) offset: usize,
49    /// Whether this file is opened for reading
50    pub(crate) is_readable: bool,
51    /// Whether this file is opened for writing
52    pub(crate) is_writable: bool,
53    /// Whether this file is opened for appending
54    pub(crate) is_appending: bool,
55    // TODO is_nonblocking
56    // TODO consider making these fields public
57}
58
59impl Drop for OpenFileDescription {
60    fn drop(&mut self) {
61        if let FileBody::Fifo {
62            readers, writers, ..
63        } = &mut self.file.borrow_mut().body
64        {
65            if self.is_readable {
66                *readers -= 1;
67            }
68            if self.is_writable {
69                *writers -= 1;
70            }
71        }
72    }
73}
74
75impl OpenFileDescription {
76    /// Returns true if you can read from this open file description.
77    #[must_use]
78    pub fn is_readable(&self) -> bool {
79        self.is_readable
80    }
81
82    /// Returns true if you can write to this open file description.
83    #[must_use]
84    pub fn is_writable(&self) -> bool {
85        self.is_writable
86    }
87
88    /// Returns true if you can read from this open file description without
89    /// blocking.
90    #[must_use]
91    pub fn is_ready_for_reading(&self) -> bool {
92        match &self.file.borrow().body {
93            FileBody::Regular { .. } | FileBody::Directory { .. } | FileBody::Terminal { .. } => {
94                true
95            }
96            FileBody::Fifo {
97                content, writers, ..
98            } => !self.is_readable || !content.is_empty() || *writers == 0,
99            FileBody::Symlink { target: _ } => false,
100        }
101    }
102
103    /// Returns true if you can write to this open file description without
104    /// blocking.
105    #[must_use]
106    pub fn is_ready_for_writing(&self) -> bool {
107        match &self.file.borrow().body {
108            FileBody::Regular { .. } | FileBody::Directory { .. } | FileBody::Terminal { .. } => {
109                true
110            }
111            FileBody::Fifo {
112                content, readers, ..
113            } => *readers == 0 || PIPE_SIZE - content.len() >= PIPE_BUF,
114            FileBody::Symlink { target: _ } => false,
115        }
116    }
117
118    /// Reads from this open file description.
119    ///
120    /// Returns the number of bytes successfully read.
121    pub fn read(&mut self, mut buffer: &mut [u8]) -> Result<usize, Errno> {
122        if !self.is_readable {
123            return Err(Errno::EBADF);
124        }
125        match &mut self.file.borrow_mut().body {
126            FileBody::Regular { content, .. } | FileBody::Terminal { content } => {
127                let len = content.len();
128                if self.offset >= len {
129                    return Ok(0);
130                }
131                let limit = len - self.offset;
132                if buffer.len() > limit {
133                    buffer = &mut buffer[..limit];
134                }
135                let count = buffer.len();
136                let src = &content[self.offset..][..count];
137                buffer.copy_from_slice(src);
138                self.offset += count;
139                Ok(count)
140            }
141            FileBody::Fifo {
142                content, writers, ..
143            } => {
144                let limit = content.len();
145                if limit == 0 && *writers > 0 {
146                    return Err(Errno::EAGAIN);
147                }
148                let mut count = 0;
149                for to in buffer {
150                    if let Some(from) = content.pop_front() {
151                        *to = from;
152                        count += 1;
153                    } else {
154                        break;
155                    }
156                }
157                Ok(count)
158            }
159            FileBody::Directory { .. } => Err(Errno::EISDIR),
160            FileBody::Symlink { target: _ } => Err(Errno::ENOTSUP),
161        }
162    }
163
164    /// Writes to this open file description.
165    ///
166    /// Returns the number of bytes successfully written.
167    pub fn write(&mut self, mut buffer: &[u8]) -> Result<usize, Errno> {
168        if !self.is_writable {
169            return Err(Errno::EBADF);
170        }
171        match &mut self.file.borrow_mut().body {
172            FileBody::Regular { content, .. } | FileBody::Terminal { content } => {
173                let len = content.len();
174                let count = buffer.len();
175                if self.is_appending {
176                    self.offset = len;
177                }
178                if self.offset > len {
179                    let zeroes = self.offset - len;
180                    content.reserve(zeroes + count);
181                    content.resize_with(self.offset, u8::default);
182                }
183                let limit = count.min(content.len() - self.offset);
184                let dst = &mut content[self.offset..][..limit];
185                dst.copy_from_slice(&buffer[..limit]);
186                content.reserve(count - limit);
187                content.extend(&buffer[limit..]);
188                self.offset += count;
189                Ok(count)
190            }
191            FileBody::Fifo {
192                content, readers, ..
193            } => {
194                if *readers == 0 {
195                    // TODO SIGPIPE
196                    return Err(Errno::EPIPE);
197                }
198                let room = PIPE_SIZE - content.len();
199                if room < buffer.len() {
200                    if room == 0 || buffer.len() <= PIPE_BUF {
201                        return Err(Errno::EAGAIN);
202                    }
203                    buffer = &buffer[..room];
204                }
205                content.extend(buffer);
206                debug_assert!(content.len() <= PIPE_SIZE);
207                Ok(buffer.len())
208            }
209            FileBody::Directory { .. } => Err(Errno::EISDIR),
210            FileBody::Symlink { target: _ } => Err(Errno::ENOTSUP),
211        }
212    }
213
214    /// Moves the file offset and returns the new offset.
215    pub fn seek(&mut self, position: SeekFrom) -> Result<usize, Errno> {
216        let len = match &self.file.borrow().body {
217            FileBody::Regular { content, .. } => content.len(),
218            FileBody::Directory { files, .. } => files.len(),
219            FileBody::Fifo { .. } => return Err(Errno::ESPIPE),
220            FileBody::Symlink { .. } | FileBody::Terminal { .. } => return Err(Errno::ENOTSUP),
221        };
222
223        let new_offset = match position {
224            SeekFrom::Start(offset) => offset.try_into().ok(),
225            SeekFrom::Current(offset) => offset
226                .try_into()
227                .ok()
228                .and_then(|offset| self.offset.checked_add_signed(offset)),
229            SeekFrom::End(offset) => offset
230                .try_into()
231                .ok()
232                .and_then(|offset| len.checked_add_signed(offset)),
233        };
234
235        let new_offset = new_offset.ok_or(Errno::EINVAL)?;
236        self.offset = new_offset;
237        Ok(new_offset)
238    }
239
240    /// Returns the i-node this open file description is operating on.
241    #[must_use]
242    pub fn inode(&self) -> &Rc<RefCell<Inode>> {
243        &self.file
244    }
245}
246
247/// State of a file descriptor.
248#[derive(Clone, Debug)]
249pub struct FdBody {
250    /// Underlying open file description.
251    pub open_file_description: Rc<RefCell<OpenFileDescription>>,
252    /// Flags for this file descriptor
253    pub flags: EnumSet<FdFlag>,
254}
255
256impl PartialEq for FdBody {
257    fn eq(&self, rhs: &Self) -> bool {
258        Rc::ptr_eq(&self.open_file_description, &rhs.open_file_description)
259            && self.flags == rhs.flags
260    }
261}
262
263impl Eq for FdBody {}
264
265#[cfg(test)]
266mod tests {
267    use super::super::Mode;
268    use super::*;
269    use assert_matches::assert_matches;
270    use std::collections::VecDeque;
271
272    #[test]
273    fn regular_file_read_unreadable() {
274        let mut open_file = OpenFileDescription {
275            file: Rc::new(RefCell::new(Inode::new([]))),
276            offset: 0,
277            is_readable: false,
278            is_writable: false,
279            is_appending: false,
280        };
281
282        let mut buffer = [0];
283        let result = open_file.read(&mut buffer);
284        assert_eq!(result, Err(Errno::EBADF));
285    }
286
287    #[test]
288    fn regular_file_read_beyond_file_length() {
289        let mut open_file = OpenFileDescription {
290            file: Rc::new(RefCell::new(Inode::new([1]))),
291            offset: 1,
292            is_readable: true,
293            is_writable: false,
294            is_appending: false,
295        };
296
297        let mut buffer = [0];
298        let result = open_file.read(&mut buffer);
299        assert_eq!(result, Ok(0));
300        assert_eq!(open_file.offset, 1);
301
302        open_file.offset = 2;
303        let result = open_file.read(&mut buffer);
304        assert_eq!(result, Ok(0));
305        assert_eq!(open_file.offset, 2);
306    }
307
308    #[test]
309    fn regular_file_read_more_than_content() {
310        let mut open_file = OpenFileDescription {
311            file: Rc::new(RefCell::new(Inode::new([1, 2, 3]))),
312            offset: 1,
313            is_readable: true,
314            is_writable: false,
315            is_appending: false,
316        };
317
318        let mut buffer = [0; 3];
319        let result = open_file.read(&mut buffer);
320        assert_eq!(result, Ok(2));
321        assert_eq!(open_file.offset, 3);
322        assert_eq!(buffer[..2], [2, 3]);
323    }
324
325    #[test]
326    fn regular_file_read_less_than_content() {
327        let mut open_file = OpenFileDescription {
328            file: Rc::new(RefCell::new(Inode::new([1, 2, 3, 4, 5]))),
329            offset: 1,
330            is_readable: true,
331            is_writable: false,
332            is_appending: false,
333        };
334
335        let mut buffer = [0; 3];
336        let result = open_file.read(&mut buffer);
337        assert_eq!(result, Ok(3));
338        assert_eq!(open_file.offset, 4);
339        assert_eq!(buffer, [2, 3, 4]);
340    }
341
342    #[test]
343    fn regular_file_write_unwritable() {
344        let mut open_file = OpenFileDescription {
345            file: Rc::new(RefCell::new(Inode::new([]))),
346            offset: 0,
347            is_readable: false,
348            is_writable: false,
349            is_appending: false,
350        };
351
352        let result = open_file.write(&[0]);
353        assert_eq!(result, Err(Errno::EBADF));
354    }
355
356    #[test]
357    fn regular_file_write_less_than_content() {
358        let mut open_file = OpenFileDescription {
359            file: Rc::new(RefCell::new(Inode::new([1, 2, 3, 4, 5]))),
360            offset: 1,
361            is_readable: false,
362            is_writable: true,
363            is_appending: false,
364        };
365
366        let result = open_file.write(&[9, 8, 7]);
367        assert_eq!(result, Ok(3));
368        assert_eq!(open_file.offset, 4);
369        assert_matches!(
370            &open_file.file.borrow().body,
371            FileBody::Regular { content, .. } => {
372                assert_eq!(content[..], [1, 9, 8, 7, 5]);
373            }
374        );
375    }
376
377    #[test]
378    fn regular_file_write_more_than_content() {
379        let mut open_file = OpenFileDescription {
380            file: Rc::new(RefCell::new(Inode::new([1, 2, 3]))),
381            offset: 1,
382            is_readable: false,
383            is_writable: true,
384            is_appending: false,
385        };
386
387        let result = open_file.write(&[9, 8, 7, 6]);
388        assert_eq!(result, Ok(4));
389        assert_eq!(open_file.offset, 5);
390        assert_matches!(
391            &open_file.file.borrow().body,
392            FileBody::Regular { content, .. } => {
393                assert_eq!(content[..], [1, 9, 8, 7, 6]);
394            }
395        );
396    }
397
398    #[test]
399    fn regular_file_write_beyond_file_length() {
400        let mut open_file = OpenFileDescription {
401            file: Rc::new(RefCell::new(Inode::new([1]))),
402            offset: 3,
403            is_readable: false,
404            is_writable: true,
405            is_appending: false,
406        };
407
408        let result = open_file.write(&[2, 3]);
409        assert_eq!(result, Ok(2));
410        assert_eq!(open_file.offset, 5);
411        assert_matches!(
412            &open_file.file.borrow().body,
413            FileBody::Regular { content, .. } => {
414                assert_eq!(content[..], [1, 0, 0, 2, 3]);
415            }
416        );
417    }
418
419    #[test]
420    fn regular_file_write_appending() {
421        let mut open_file = OpenFileDescription {
422            file: Rc::new(RefCell::new(Inode::new([1, 2, 3]))),
423            offset: 1,
424            is_readable: false,
425            is_writable: true,
426            is_appending: true,
427        };
428
429        let result = open_file.write(&[4, 5]);
430        assert_eq!(result, Ok(2));
431        assert_eq!(open_file.offset, 5);
432        assert_matches!(
433            &open_file.file.borrow().body,
434            FileBody::Regular { content, .. } => {
435                assert_eq!(content[..], [1, 2, 3, 4, 5]);
436            }
437        );
438    }
439
440    #[test]
441    fn regular_file_seek_from_start() {
442        let mut open_file = OpenFileDescription {
443            file: Rc::new(RefCell::new(Inode::new([]))),
444            offset: 3,
445            is_readable: true,
446            is_writable: true,
447            is_appending: false,
448        };
449
450        let result = open_file.seek(SeekFrom::Start(10));
451        assert_eq!(result, Ok(10));
452        assert_eq!(open_file.offset, 10);
453
454        let result = open_file.seek(SeekFrom::Start(0));
455        assert_eq!(result, Ok(0));
456        assert_eq!(open_file.offset, 0);
457
458        let result = open_file.seek(SeekFrom::Start(3));
459        assert_eq!(result, Ok(3));
460        assert_eq!(open_file.offset, 3);
461    }
462
463    #[test]
464    fn regular_file_seek_from_current() {
465        let mut open_file = OpenFileDescription {
466            file: Rc::new(RefCell::new(Inode::new([]))),
467            offset: 5,
468            is_readable: true,
469            is_writable: true,
470            is_appending: false,
471        };
472
473        let result = open_file.seek(SeekFrom::Current(10));
474        assert_eq!(result, Ok(15));
475        assert_eq!(open_file.offset, 15);
476
477        let result = open_file.seek(SeekFrom::Current(0));
478        assert_eq!(result, Ok(15));
479        assert_eq!(open_file.offset, 15);
480
481        let result = open_file.seek(SeekFrom::Current(-5));
482        assert_eq!(result, Ok(10));
483        assert_eq!(open_file.offset, 10);
484
485        let result = open_file.seek(SeekFrom::Current(-11));
486        assert_eq!(result, Err(Errno::EINVAL));
487        assert_eq!(open_file.offset, 10);
488    }
489
490    #[test]
491    fn regular_file_seek_from_end() {
492        let mut open_file = OpenFileDescription {
493            file: Rc::new(RefCell::new(Inode::new([1, 2, 3]))),
494            offset: 2,
495            is_readable: true,
496            is_writable: true,
497            is_appending: false,
498        };
499
500        let result = open_file.seek(SeekFrom::End(7));
501        assert_eq!(result, Ok(10));
502        assert_eq!(open_file.offset, 10);
503
504        let result = open_file.seek(SeekFrom::End(0));
505        assert_eq!(result, Ok(3));
506        assert_eq!(open_file.offset, 3);
507
508        let result = open_file.seek(SeekFrom::End(-3));
509        assert_eq!(result, Ok(0));
510        assert_eq!(open_file.offset, 0);
511
512        let result = open_file.seek(SeekFrom::End(-4));
513        assert_eq!(result, Err(Errno::EINVAL));
514        assert_eq!(open_file.offset, 0);
515    }
516
517    #[test]
518    fn fifo_reader_drop() {
519        let file = Rc::new(RefCell::new(Inode {
520            body: FileBody::Fifo {
521                content: VecDeque::new(),
522                readers: 1,
523                writers: 1,
524            },
525            permissions: Mode::default(),
526        }));
527        let open_file = OpenFileDescription {
528            file: Rc::clone(&file),
529            offset: 0,
530            is_readable: true,
531            is_writable: false,
532            is_appending: false,
533        };
534        drop(open_file);
535
536        assert_matches!(&file.borrow().body, FileBody::Fifo { readers, writers, .. } => {
537            assert_eq!(*readers, 0);
538            assert_eq!(*writers, 1);
539        });
540    }
541
542    #[test]
543    fn fifo_writer_drop() {
544        let file = Rc::new(RefCell::new(Inode {
545            body: FileBody::Fifo {
546                content: VecDeque::new(),
547                readers: 1,
548                writers: 1,
549            },
550            permissions: Mode::default(),
551        }));
552        let open_file = OpenFileDescription {
553            file: Rc::clone(&file),
554            offset: 0,
555            is_readable: false,
556            is_writable: true,
557            is_appending: false,
558        };
559        drop(open_file);
560
561        assert_matches!(&file.borrow().body, FileBody::Fifo { readers, writers, .. } => {
562            assert_eq!(*readers, 1);
563            assert_eq!(*writers, 0);
564        });
565    }
566
567    #[test]
568    fn fifo_is_ready_for_writing() {
569        let file = Rc::new(RefCell::new(Inode {
570            body: FileBody::Fifo {
571                content: VecDeque::new(),
572                readers: 1,
573                writers: 1,
574            },
575            permissions: Mode::default(),
576        }));
577        let mut open_file = OpenFileDescription {
578            file: Rc::clone(&file),
579            offset: 0,
580            is_readable: false,
581            is_writable: true,
582            is_appending: false,
583        };
584
585        assert!(open_file.is_ready_for_writing());
586
587        let buffer = [42; PIPE_SIZE - PIPE_BUF];
588        let result = open_file.write(&buffer);
589        assert_eq!(result, Ok(PIPE_SIZE - PIPE_BUF));
590        assert!(open_file.is_ready_for_writing());
591
592        let result = open_file.write(&[123]);
593        assert_eq!(result, Ok(1));
594        assert!(!open_file.is_ready_for_writing());
595
596        assert_matches!(&mut file.borrow_mut().body, FileBody::Fifo { readers, .. } => {
597            *readers = 0;
598        });
599        assert!(open_file.is_ready_for_writing());
600    }
601
602    #[test]
603    fn fifo_read_empty() {
604        let mut open_file = OpenFileDescription {
605            file: Rc::new(RefCell::new(Inode {
606                body: FileBody::Fifo {
607                    content: VecDeque::new(),
608                    readers: 1,
609                    writers: 0,
610                },
611                permissions: Mode::default(),
612            })),
613            offset: 0,
614            is_readable: true,
615            is_writable: false,
616            is_appending: false,
617        };
618
619        let mut buffer = [100; 5];
620        let result = open_file.read(&mut buffer);
621        assert_eq!(result, Ok(0));
622    }
623
624    #[test]
625    fn fifo_read_non_empty() {
626        let mut open_file = OpenFileDescription {
627            file: Rc::new(RefCell::new(Inode {
628                body: FileBody::Fifo {
629                    content: VecDeque::from([1, 5, 7, 3, 42, 7, 6]),
630                    readers: 1,
631                    writers: 0,
632                },
633                permissions: Mode::default(),
634            })),
635            offset: 0,
636            is_readable: true,
637            is_writable: false,
638            is_appending: false,
639        };
640
641        let mut buffer = [100; 4];
642        let result = open_file.read(&mut buffer);
643        assert_eq!(result, Ok(4));
644        assert_eq!(buffer, [1, 5, 7, 3]);
645
646        let result = open_file.read(&mut buffer);
647        assert_eq!(result, Ok(3));
648        assert_eq!(buffer[..3], [42, 7, 6]);
649
650        let result = open_file.read(&mut buffer);
651        assert_eq!(result, Ok(0));
652    }
653
654    #[test]
655    fn fifo_read_not_ready() {
656        let mut open_file = OpenFileDescription {
657            file: Rc::new(RefCell::new(Inode {
658                body: FileBody::Fifo {
659                    content: VecDeque::new(),
660                    readers: 1,
661                    writers: 1,
662                },
663                permissions: Mode::default(),
664            })),
665            offset: 0,
666            is_readable: true,
667            is_writable: false,
668            is_appending: false,
669        };
670
671        let mut buffer = [100; 5];
672        let result = open_file.read(&mut buffer);
673        assert_eq!(result, Err(Errno::EAGAIN));
674    }
675
676    #[test]
677    fn fifo_write_vacant() {
678        let file = Rc::new(RefCell::new(Inode {
679            body: FileBody::Fifo {
680                content: VecDeque::new(),
681                readers: 1,
682                writers: 1,
683            },
684            permissions: Mode::default(),
685        }));
686        let mut open_file = OpenFileDescription {
687            file: Rc::clone(&file),
688            offset: 0,
689            is_readable: false,
690            is_writable: true,
691            is_appending: false,
692        };
693
694        let result = open_file.write(&[1, 1, 2, 3]);
695        assert_eq!(result, Ok(4));
696
697        let result = open_file.write(&[5, 8, 13]);
698        assert_eq!(result, Ok(3));
699
700        assert_matches!(&mut file.borrow_mut().body, FileBody::Fifo { content, .. } => {
701            assert_eq!(content.make_contiguous(), [1, 1, 2, 3, 5, 8, 13]);
702        });
703    }
704
705    #[test]
706    fn fifo_write_full() {
707        let mut open_file = OpenFileDescription {
708            file: Rc::new(RefCell::new(Inode {
709                body: FileBody::Fifo {
710                    content: VecDeque::new(),
711                    readers: 1,
712                    writers: 1,
713                },
714                permissions: Mode::default(),
715            })),
716            offset: 0,
717            is_readable: false,
718            is_writable: true,
719            is_appending: false,
720        };
721
722        open_file.write(&[0; PIPE_SIZE]).unwrap();
723
724        // The pipe is full. No more can be written.
725        let result = open_file.write(&[1; 1]);
726        assert_eq!(result, Err(Errno::EAGAIN));
727        let result = open_file.write(&[1; PIPE_BUF + 1]);
728        assert_eq!(result, Err(Errno::EAGAIN));
729
730        // However, empty write should succeed.
731        let result = open_file.write(&[1; 0]);
732        assert_eq!(result, Ok(0));
733    }
734
735    #[test]
736    fn fifo_write_atomic_full() {
737        let file = Rc::new(RefCell::new(Inode {
738            body: FileBody::Fifo {
739                content: VecDeque::new(),
740                readers: 1,
741                writers: 1,
742            },
743            permissions: Mode::default(),
744        }));
745        let mut open_file = OpenFileDescription {
746            file: Rc::clone(&file),
747            offset: 0,
748            is_readable: false,
749            is_writable: true,
750            is_appending: false,
751        };
752
753        const LEN: usize = PIPE_SIZE - PIPE_BUF + 1;
754        open_file.write(&[0; LEN]).unwrap();
755
756        // The remaining room in the pipe is less than the length we're writing,
757        // which is PIPE_BUF. Nothing is written in this case.
758        let result = open_file.write(&[1; PIPE_BUF]);
759        assert_eq!(result, Err(Errno::EAGAIN));
760
761        assert_matches!(&file.borrow().body, FileBody::Fifo { content, .. } => {
762            assert_eq!(content.len(), LEN);
763        });
764    }
765
766    #[test]
767    fn fifo_write_non_atomic_full() {
768        let mut open_file = OpenFileDescription {
769            file: Rc::new(RefCell::new(Inode {
770                body: FileBody::Fifo {
771                    content: VecDeque::new(),
772                    readers: 1,
773                    writers: 1,
774                },
775                permissions: Mode::default(),
776            })),
777            offset: 0,
778            is_readable: false,
779            is_writable: true,
780            is_appending: false,
781        };
782
783        const LEN: usize = PIPE_SIZE - PIPE_BUF;
784        open_file.write(&[0; LEN]).unwrap();
785
786        // The remaining room in the pipe is less than the length we're writing,
787        // which exceeds PIPE_BUF. Only as much as possible is written in this
788        // case.
789        let result = open_file.write(&[1; PIPE_BUF + 1]);
790        assert_eq!(result, Ok(PIPE_BUF));
791    }
792
793    #[test]
794    fn fifo_write_orphan() {
795        let mut open_file = OpenFileDescription {
796            file: Rc::new(RefCell::new(Inode {
797                body: FileBody::Fifo {
798                    content: VecDeque::new(),
799                    readers: 0,
800                    writers: 1,
801                },
802                permissions: Mode::default(),
803            })),
804            offset: 0,
805            is_readable: false,
806            is_writable: true,
807            is_appending: false,
808        };
809
810        let result = open_file.write(&[1; 1]);
811        assert_eq!(result, Err(Errno::EPIPE));
812    }
813}