1use core::{
2 ffi::{c_char, c_int, c_void},
3 mem::offset_of,
4};
5
6use alloc::ffi::CString;
7use axerrno::{LinuxError, LinuxResult};
8use axfs::fops::DirEntry;
9use linux_raw_sys::general::{
10 AT_FDCWD, AT_REMOVEDIR, DT_BLK, DT_CHR, DT_DIR, DT_FIFO, DT_LNK, DT_REG, DT_SOCK, DT_UNKNOWN,
11 linux_dirent64,
12};
13
14use crate::{
15 file::{Directory, FileLike},
16 path::{HARDLINK_MANAGER, handle_file_path},
17 ptr::{UserConstPtr, UserPtr, nullable},
18};
19
20pub fn sys_ioctl(_fd: i32, _op: usize, _argp: UserPtr<c_void>) -> LinuxResult<isize> {
29 warn!("Unimplemented syscall: SYS_IOCTL");
30 Ok(0)
31}
32
33pub fn sys_chdir(path: UserConstPtr<c_char>) -> LinuxResult<isize> {
34 let path = path.get_as_str()?;
35 debug!("sys_chdir <= {:?}", path);
36
37 axfs::api::set_current_dir(path)?;
38 Ok(0)
39}
40
41pub fn sys_mkdirat(dirfd: i32, path: UserConstPtr<c_char>, mode: u32) -> LinuxResult<isize> {
42 let path = path.get_as_str()?;
43 debug!(
44 "sys_mkdirat <= dirfd: {}, path: {}, mode: {}",
45 dirfd, path, mode
46 );
47
48 if mode != 0 {
49 warn!("directory mode not supported.");
50 }
51
52 let path = handle_file_path(dirfd, path)?;
53 axfs::api::create_dir(path.as_str())?;
54
55 Ok(0)
56}
57
58#[allow(dead_code)]
59#[repr(u8)]
60#[derive(Debug, Clone, Copy)]
61pub enum FileType {
62 Unknown = DT_UNKNOWN as u8,
63 Fifo = DT_FIFO as u8,
64 Chr = DT_CHR as u8,
65 Dir = DT_DIR as u8,
66 Blk = DT_BLK as u8,
67 Reg = DT_REG as u8,
68 Lnk = DT_LNK as u8,
69 Socket = DT_SOCK as u8,
70}
71
72impl From<axfs::api::FileType> for FileType {
73 fn from(ft: axfs::api::FileType) -> Self {
74 match ft {
75 ft if ft.is_dir() => FileType::Dir,
76 ft if ft.is_file() => FileType::Reg,
77 _ => FileType::Unknown,
78 }
79 }
80}
81
82struct DirBuffer<'a> {
84 buf: &'a mut [u8],
85 offset: usize,
86}
87
88impl<'a> DirBuffer<'a> {
89 fn new(buf: &'a mut [u8]) -> Self {
90 Self { buf, offset: 0 }
91 }
92
93 fn remaining_space(&self) -> usize {
94 self.buf.len().saturating_sub(self.offset)
95 }
96
97 fn write_entry(&mut self, d_type: FileType, name: &[u8]) -> bool {
98 const NAME_OFFSET: usize = offset_of!(linux_dirent64, d_name);
99
100 let len = NAME_OFFSET + name.len() + 1;
101 let len = len.next_multiple_of(align_of::<linux_dirent64>());
103 if self.remaining_space() < len {
104 return false;
105 }
106
107 unsafe {
108 let entry_ptr = self.buf.as_mut_ptr().add(self.offset);
109 entry_ptr.cast::<linux_dirent64>().write(linux_dirent64 {
110 d_ino: 1,
112 d_off: 0,
113 d_reclen: len as _,
114 d_type: d_type as _,
115 d_name: Default::default(),
116 });
117
118 let name_ptr = entry_ptr.add(NAME_OFFSET);
119 name_ptr.copy_from_nonoverlapping(name.as_ptr(), name.len());
120 name_ptr.add(name.len()).write(0);
121 }
122
123 self.offset += len;
124 true
125 }
126}
127
128pub fn sys_getdents64(fd: i32, buf: UserPtr<u8>, len: usize) -> LinuxResult<isize> {
129 let buf = buf.get_as_mut_slice(len)?;
130 debug!(
131 "sys_getdents64 <= fd: {}, buf: {:p}, len: {}",
132 fd,
133 buf.as_ptr(),
134 buf.len()
135 );
136
137 let mut buffer = DirBuffer::new(buf);
138
139 let dir = Directory::from_fd(fd)?;
140
141 let mut last_dirent = dir.last_dirent();
142 if let Some(ent) = last_dirent.take()
143 && !buffer.write_entry(ent.entry_type().into(), ent.name_as_bytes())
144 {
145 *last_dirent = Some(ent);
146 return Err(LinuxError::EINVAL);
147 }
148
149 let mut inner = dir.inner();
150 loop {
151 let mut dirents = [DirEntry::default()];
152 let cnt = inner.read_dir(&mut dirents)?;
153 if cnt == 0 {
154 break;
155 }
156
157 let [ent] = dirents;
158 if !buffer.write_entry(ent.entry_type().into(), ent.name_as_bytes()) {
159 *last_dirent = Some(ent);
160 break;
161 }
162 }
163
164 if last_dirent.is_some() && buffer.offset == 0 {
165 return Err(LinuxError::EINVAL);
166 }
167 Ok(buffer.offset as _)
168}
169
170pub fn sys_linkat(
176 old_dirfd: c_int,
177 old_path: UserConstPtr<c_char>,
178 new_dirfd: c_int,
179 new_path: UserConstPtr<c_char>,
180 flags: i32,
181) -> LinuxResult<isize> {
182 let old_path = old_path.get_as_str()?;
183 let new_path = new_path.get_as_str()?;
184 debug!(
185 "sys_linkat <= old_dirfd: {}, old_path: {}, new_dirfd: {}, new_path: {}, flags: {}",
186 old_dirfd, old_path, new_dirfd, new_path, flags
187 );
188
189 if flags != 0 {
190 warn!("Unsupported flags: {flags}");
191 }
192
193 let old_path = handle_file_path(old_dirfd, old_path)?;
195 let new_path = handle_file_path(new_dirfd, new_path)?;
197
198 HARDLINK_MANAGER.create_link(&new_path, &old_path)?;
199
200 Ok(0)
201}
202
203pub fn sys_link(
204 old_path: UserConstPtr<c_char>,
205 new_path: UserConstPtr<c_char>,
206) -> LinuxResult<isize> {
207 sys_linkat(AT_FDCWD, old_path, AT_FDCWD, new_path, 0)
208}
209
210pub fn sys_unlinkat(dirfd: c_int, path: UserConstPtr<c_char>, flags: u32) -> LinuxResult<isize> {
216 let path = path.get_as_str()?;
217 debug!(
218 "sys_unlinkat <= dirfd: {}, path: {}, flags: {}",
219 dirfd, path, flags
220 );
221
222 let path = handle_file_path(dirfd, path)?;
223
224 if flags == AT_REMOVEDIR {
225 axfs::api::remove_dir(path.as_str())?;
226 } else {
227 let metadata = axfs::api::metadata(path.as_str())?;
228 if metadata.is_dir() {
229 return Err(LinuxError::EISDIR);
230 } else {
231 debug!("unlink file: {:?}", path);
232 HARDLINK_MANAGER
233 .remove_link(&path)
234 .ok_or(LinuxError::ENOENT)?;
235 }
236 }
237 Ok(0)
238}
239
240pub fn sys_unlink(path: UserConstPtr<c_char>) -> LinuxResult<isize> {
241 sys_unlinkat(AT_FDCWD, path, 0)
242}
243
244pub fn sys_getcwd(buf: UserPtr<u8>, size: usize) -> LinuxResult<isize> {
245 let buf = nullable!(buf.get_as_mut_slice(size))?;
246
247 let Some(buf) = buf else {
248 return Ok(0);
249 };
250
251 let cwd = CString::new(axfs::api::current_dir()?).map_err(|_| LinuxError::EINVAL)?;
252 let cwd = cwd.as_bytes_with_nul();
253
254 if cwd.len() <= buf.len() {
255 buf[..cwd.len()].copy_from_slice(cwd);
256 Ok(buf.as_ptr() as _)
257 } else {
258 Err(LinuxError::ERANGE)
259 }
260}