1 use crate::fd::{AsFd, BorrowedFd, OwnedFd}; 2 use crate::ffi::{CStr, CString}; 3 use crate::fs::{ 4 fcntl_getfl, fstat, fstatfs, fstatvfs, openat, FileType, Mode, OFlags, Stat, StatFs, StatVfs, 5 }; 6 use crate::io; 7 use crate::process::fchdir; 8 use crate::utils::as_ptr; 9 use alloc::borrow::ToOwned; 10 use alloc::vec::Vec; 11 use core::fmt; 12 use core::mem::size_of; 13 use linux_raw_sys::general::{linux_dirent64, SEEK_SET}; 14 15 /// `DIR*` 16 pub struct Dir { 17 /// The `OwnedFd` that we read directory entries from. 18 fd: OwnedFd, 19 20 buf: Vec<u8>, 21 pos: usize, 22 next: Option<u64>, 23 } 24 25 impl Dir { 26 /// Construct a `Dir` that reads entries from the given directory 27 /// file descriptor. 28 #[inline] read_from<Fd: AsFd>(fd: Fd) -> io::Result<Self>29 pub fn read_from<Fd: AsFd>(fd: Fd) -> io::Result<Self> { 30 Self::_read_from(fd.as_fd()) 31 } 32 33 #[inline] _read_from(fd: BorrowedFd<'_>) -> io::Result<Self>34 fn _read_from(fd: BorrowedFd<'_>) -> io::Result<Self> { 35 let flags = fcntl_getfl(fd)?; 36 let fd_for_dir = openat(fd, cstr!("."), flags | OFlags::CLOEXEC, Mode::empty())?; 37 38 Ok(Self { 39 fd: fd_for_dir, 40 buf: Vec::new(), 41 pos: 0, 42 next: None, 43 }) 44 } 45 46 /// `rewinddir(self)` 47 #[inline] rewind(&mut self)48 pub fn rewind(&mut self) { 49 self.pos = self.buf.len(); 50 self.next = Some(0); 51 } 52 53 /// `readdir(self)`, where `None` means the end of the directory. read(&mut self) -> Option<io::Result<DirEntry>>54 pub fn read(&mut self) -> Option<io::Result<DirEntry>> { 55 if let Some(next) = self.next.take() { 56 match crate::backend::fs::syscalls::_seek(self.fd.as_fd(), next as i64, SEEK_SET) { 57 Ok(_) => (), 58 Err(err) => return Some(Err(err)), 59 } 60 } 61 62 // Compute linux_dirent64 field offsets. 63 let z = linux_dirent64 { 64 d_ino: 0_u64, 65 d_off: 0_i64, 66 d_type: 0_u8, 67 d_reclen: 0_u16, 68 d_name: Default::default(), 69 }; 70 let base = as_ptr(&z) as usize; 71 let offsetof_d_reclen = (as_ptr(&z.d_reclen) as usize) - base; 72 let offsetof_d_name = (as_ptr(&z.d_name) as usize) - base; 73 let offsetof_d_ino = (as_ptr(&z.d_ino) as usize) - base; 74 let offsetof_d_type = (as_ptr(&z.d_type) as usize) - base; 75 76 // Test if we need more entries, and if so, read more. 77 if self.buf.len() - self.pos < size_of::<linux_dirent64>() { 78 match self.read_more()? { 79 Ok(()) => (), 80 Err(e) => return Some(Err(e)), 81 } 82 } 83 84 // We successfully read an entry. Extract the fields. 85 let pos = self.pos; 86 87 // Do an unaligned u16 load. 88 let d_reclen = u16::from_ne_bytes([ 89 self.buf[pos + offsetof_d_reclen], 90 self.buf[pos + offsetof_d_reclen + 1], 91 ]); 92 assert!(self.buf.len() - pos >= d_reclen as usize); 93 self.pos += d_reclen as usize; 94 95 // Read the NUL-terminated name from the `d_name` field. Without 96 // `unsafe`, we need to scan for the NUL twice: once to obtain a size 97 // for the slice, and then once within `CStr::from_bytes_with_nul`. 98 let name_start = pos + offsetof_d_name; 99 let name_len = self.buf[name_start..] 100 .iter() 101 .position(|x| *x == b'\0') 102 .unwrap(); 103 let name = 104 CStr::from_bytes_with_nul(&self.buf[name_start..name_start + name_len + 1]).unwrap(); 105 let name = name.to_owned(); 106 assert!(name.as_bytes().len() <= self.buf.len() - name_start); 107 108 // Do an unaligned u64 load. 109 let d_ino = u64::from_ne_bytes([ 110 self.buf[pos + offsetof_d_ino], 111 self.buf[pos + offsetof_d_ino + 1], 112 self.buf[pos + offsetof_d_ino + 2], 113 self.buf[pos + offsetof_d_ino + 3], 114 self.buf[pos + offsetof_d_ino + 4], 115 self.buf[pos + offsetof_d_ino + 5], 116 self.buf[pos + offsetof_d_ino + 6], 117 self.buf[pos + offsetof_d_ino + 7], 118 ]); 119 120 let d_type = self.buf[pos + offsetof_d_type]; 121 122 // Check that our types correspond to the `linux_dirent64` types. 123 let _ = linux_dirent64 { 124 d_ino, 125 d_off: 0, 126 d_type, 127 d_reclen, 128 d_name: Default::default(), 129 }; 130 131 Some(Ok(DirEntry { 132 d_ino, 133 d_type, 134 name, 135 })) 136 } 137 read_more(&mut self) -> Option<io::Result<()>>138 fn read_more(&mut self) -> Option<io::Result<()>> { 139 let og_len = self.buf.len(); 140 // Capacity increment currently chosen by wild guess. 141 self.buf 142 .resize(self.buf.capacity() + 32 * size_of::<linux_dirent64>(), 0); 143 let nread = match crate::backend::fs::syscalls::getdents(self.fd.as_fd(), &mut self.buf) { 144 Ok(nread) => nread, 145 Err(err) => { 146 self.buf.resize(og_len, 0); 147 return Some(Err(err)); 148 } 149 }; 150 self.buf.resize(nread, 0); 151 self.pos = 0; 152 if nread == 0 { 153 None 154 } else { 155 Some(Ok(())) 156 } 157 } 158 159 /// `fstat(self)` 160 #[inline] stat(&self) -> io::Result<Stat>161 pub fn stat(&self) -> io::Result<Stat> { 162 fstat(&self.fd) 163 } 164 165 /// `fstatfs(self)` 166 #[inline] statfs(&self) -> io::Result<StatFs>167 pub fn statfs(&self) -> io::Result<StatFs> { 168 fstatfs(&self.fd) 169 } 170 171 /// `fstatvfs(self)` 172 #[inline] statvfs(&self) -> io::Result<StatVfs>173 pub fn statvfs(&self) -> io::Result<StatVfs> { 174 fstatvfs(&self.fd) 175 } 176 177 /// `fchdir(self)` 178 #[inline] chdir(&self) -> io::Result<()>179 pub fn chdir(&self) -> io::Result<()> { 180 fchdir(&self.fd) 181 } 182 } 183 184 impl Iterator for Dir { 185 type Item = io::Result<DirEntry>; 186 187 #[inline] next(&mut self) -> Option<Self::Item>188 fn next(&mut self) -> Option<Self::Item> { 189 Self::read(self) 190 } 191 } 192 193 impl fmt::Debug for Dir { fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result194 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { 195 f.debug_struct("Dir").field("fd", &self.fd).finish() 196 } 197 } 198 199 /// `struct dirent` 200 #[derive(Debug)] 201 pub struct DirEntry { 202 d_ino: u64, 203 d_type: u8, 204 name: CString, 205 } 206 207 impl DirEntry { 208 /// Returns the file name of this directory entry. 209 #[inline] file_name(&self) -> &CStr210 pub fn file_name(&self) -> &CStr { 211 &self.name 212 } 213 214 /// Returns the type of this directory entry. 215 #[inline] file_type(&self) -> FileType216 pub fn file_type(&self) -> FileType { 217 FileType::from_dirent_d_type(self.d_type) 218 } 219 220 /// Return the inode number of this directory entry. 221 #[inline] ino(&self) -> u64222 pub fn ino(&self) -> u64 { 223 self.d_ino 224 } 225 } 226