1use alloc::string::{String, ToString};
2use alloc::sync::Arc;
3use core::ffi::{c_char, c_int};
4
5use axerrno::{LinuxError, LinuxResult};
6use axfs::fops::OpenOptions;
7use axio::{PollState, SeekFrom};
8use axsync::Mutex;
9
10use super::fd_ops::{FileLike, get_file_like};
11use crate::AT_FDCWD;
12use crate::{ctypes, utils::char_ptr_to_str};
13
14pub struct File {
16 inner: Mutex<axfs::fops::File>,
17 path: String,
18}
19
20impl File {
21 fn new(inner: axfs::fops::File, path: String) -> Self {
22 Self {
23 inner: Mutex::new(inner),
24 path,
25 }
26 }
27
28 fn add_to_fd_table(self) -> LinuxResult<c_int> {
29 super::fd_ops::add_file_like(Arc::new(self))
30 }
31
32 fn from_fd(fd: c_int) -> LinuxResult<Arc<Self>> {
33 let f = super::fd_ops::get_file_like(fd)?;
34 f.into_any()
35 .downcast::<Self>()
36 .map_err(|_| LinuxError::EINVAL)
37 }
38
39 pub fn path(&self) -> &str {
41 &self.path
42 }
43
44 pub fn inner(&self) -> &Mutex<axfs::fops::File> {
46 &self.inner
47 }
48}
49
50impl FileLike for File {
51 fn read(&self, buf: &mut [u8]) -> LinuxResult<usize> {
52 Ok(self.inner.lock().read(buf)?)
53 }
54
55 fn write(&self, buf: &[u8]) -> LinuxResult<usize> {
56 Ok(self.inner.lock().write(buf)?)
57 }
58
59 fn stat(&self) -> LinuxResult<ctypes::stat> {
60 let metadata = self.inner.lock().get_attr()?;
61 let ty = metadata.file_type() as u8;
62 let perm = metadata.perm().bits() as u32;
63 let st_mode = ((ty as u32) << 12) | perm;
64 Ok(ctypes::stat {
65 st_ino: 1,
66 st_nlink: 1,
67 st_mode,
68 st_uid: 1000,
69 st_gid: 1000,
70 st_size: metadata.size() as _,
71 st_blocks: metadata.blocks() as _,
72 st_blksize: 512,
73 ..Default::default()
74 })
75 }
76
77 fn into_any(self: Arc<Self>) -> Arc<dyn core::any::Any + Send + Sync> {
78 self
79 }
80
81 fn poll(&self) -> LinuxResult<PollState> {
82 Ok(PollState {
83 readable: true,
84 writable: true,
85 })
86 }
87
88 fn set_nonblocking(&self, _nonblocking: bool) -> LinuxResult {
89 Ok(())
90 }
91}
92
93fn flags_to_options(flags: c_int, _mode: ctypes::mode_t) -> OpenOptions {
95 let flags = flags as u32;
96 let mut options = OpenOptions::new();
97 match flags & 0b11 {
98 ctypes::O_RDONLY => options.read(true),
99 ctypes::O_WRONLY => options.write(true),
100 _ => {
101 options.read(true);
102 options.write(true);
103 }
104 };
105 if flags & ctypes::O_APPEND != 0 {
106 options.append(true);
107 }
108 if flags & ctypes::O_TRUNC != 0 {
109 options.truncate(true);
110 }
111 if flags & ctypes::O_CREAT != 0 {
112 options.create(true);
113 }
114 if flags & ctypes::O_EXEC != 0 {
115 options.execute(true);
117 }
118 if flags & ctypes::O_DIRECTORY != 0 {
119 options.directory(true);
120 }
121 options
122}
123
124pub fn sys_open(filename: *const c_char, flags: c_int, mode: ctypes::mode_t) -> c_int {
129 let filename = char_ptr_to_str(filename);
130 debug!("sys_open <= {:?} {:#o} {:#o}", filename, flags, mode);
131 syscall_body!(sys_open, {
132 add_file_or_directory_fd(
133 axfs::fops::File::open,
134 axfs::fops::Directory::open_dir,
135 filename?,
136 &flags_to_options(flags, mode),
137 )
138 })
139}
140
141pub fn sys_openat(
148 dirfd: c_int,
149 filename: *const c_char,
150 flags: c_int,
151 mode: ctypes::mode_t,
152) -> c_int {
153 let filename = match char_ptr_to_str(filename) {
154 Ok(s) => s,
155 Err(_) => return -1,
156 };
157
158 debug!(
159 "sys_openat <= {} {:?} {:#o} {:#o}",
160 dirfd, filename, flags, mode
161 );
162
163 if filename.starts_with('/') || dirfd == AT_FDCWD as _ {
164 return sys_open(filename.as_ptr() as _, flags, mode);
165 }
166
167 match Directory::from_fd(dirfd).and_then(|dir| {
168 add_file_or_directory_fd(
169 |filename, options| dir.inner.lock().open_file_at(filename, options),
170 |filename, options| dir.inner.lock().open_dir_at(filename, options),
171 filename,
172 &flags_to_options(flags, mode),
173 )
174 }) {
175 Ok(fd) => fd,
176 Err(e) => {
177 debug!("sys_openat => {}", e);
178 -1
179 }
180 }
181}
182
183fn add_file_or_directory_fd<F, D, E>(
186 open_file: F,
187 open_dir: D,
188 filename: &str,
189 options: &OpenOptions,
190) -> LinuxResult<c_int>
191where
192 E: Into<LinuxError>,
193 F: FnOnce(&str, &OpenOptions) -> Result<axfs::fops::File, E>,
194 D: FnOnce(&str, &OpenOptions) -> Result<axfs::fops::Directory, E>,
195{
196 if !options.has_directory() {
197 match open_file(filename, options)
198 .map_err(Into::into)
199 .and_then(|f| File::new(f, filename.into()).add_to_fd_table())
200 {
201 Err(LinuxError::EISDIR) => {}
202 r => return r,
203 }
204 }
205
206 Directory::new(
207 open_dir(filename, options).map_err(Into::into)?,
208 filename.to_string(),
209 )
210 .add_to_fd_table()
211}
212
213pub fn sys_lseek(fd: c_int, offset: ctypes::off_t, whence: c_int) -> ctypes::off_t {
217 debug!("sys_lseek <= {} {} {}", fd, offset, whence);
218 syscall_body!(sys_lseek, {
219 let pos = match whence {
220 0 => SeekFrom::Start(offset as _),
221 1 => SeekFrom::Current(offset as _),
222 2 => SeekFrom::End(offset as _),
223 _ => return Err(LinuxError::EINVAL),
224 };
225 let off = File::from_fd(fd)?.inner.lock().seek(pos)?;
226 Ok(off)
227 })
228}
229
230pub unsafe fn sys_stat(path: *const c_char, buf: *mut ctypes::stat) -> c_int {
234 let path = char_ptr_to_str(path);
235 debug!("sys_stat <= {:?} {:#x}", path, buf as usize);
236 syscall_body!(sys_stat, {
237 if buf.is_null() {
238 return Err(LinuxError::EFAULT);
239 }
240 let mut options = OpenOptions::new();
241 options.read(true);
242 let file = axfs::fops::File::open(path?, &options)?;
243 let st = File::new(file, path?.to_string()).stat()?;
244 unsafe { *buf = st };
245 Ok(0)
246 })
247}
248
249pub unsafe fn sys_fstat(fd: c_int, buf: *mut ctypes::stat) -> c_int {
253 debug!("sys_fstat <= {} {:#x}", fd, buf as usize);
254 syscall_body!(sys_fstat, {
255 if buf.is_null() {
256 return Err(LinuxError::EFAULT);
257 }
258
259 unsafe { *buf = get_file_like(fd)?.stat()? };
260 Ok(0)
261 })
262}
263
264pub unsafe fn sys_lstat(path: *const c_char, buf: *mut ctypes::stat) -> ctypes::ssize_t {
268 let path = char_ptr_to_str(path);
269 debug!("sys_lstat <= {:?} {:#x}", path, buf as usize);
270 syscall_body!(sys_lstat, {
271 if buf.is_null() {
272 return Err(LinuxError::EFAULT);
273 }
274 unsafe { *buf = Default::default() }; Ok(0)
276 })
277}
278
279pub fn sys_getcwd(buf: *mut c_char, size: usize) -> *mut c_char {
281 debug!("sys_getcwd <= {:#x} {}", buf as usize, size);
282 syscall_body!(sys_getcwd, {
283 if buf.is_null() {
284 return Ok(core::ptr::null::<c_char>() as _);
285 }
286 let dst = unsafe { core::slice::from_raw_parts_mut(buf as *mut u8, size as _) };
287 let cwd = axfs::api::current_dir()?;
288 let cwd = cwd.as_bytes();
289 if cwd.len() < size {
290 dst[..cwd.len()].copy_from_slice(cwd);
291 dst[cwd.len()] = 0;
292 Ok(buf)
293 } else {
294 Err(LinuxError::ERANGE)
295 }
296 })
297}
298
299pub fn sys_rename(old: *const c_char, new: *const c_char) -> c_int {
304 syscall_body!(sys_rename, {
305 let old_path = char_ptr_to_str(old)?;
306 let new_path = char_ptr_to_str(new)?;
307 debug!("sys_rename <= old: {:?}, new: {:?}", old_path, new_path);
308 axfs::api::rename(old_path, new_path)?;
309 Ok(0)
310 })
311}
312
313pub struct Directory {
315 inner: Mutex<axfs::fops::Directory>,
316 path: String,
317}
318
319impl Directory {
320 fn new(inner: axfs::fops::Directory, path: String) -> Self {
321 Self {
322 inner: Mutex::new(inner),
323 path,
324 }
325 }
326
327 fn add_to_fd_table(self) -> LinuxResult<c_int> {
328 super::fd_ops::add_file_like(Arc::new(self))
329 }
330
331 pub fn from_fd(fd: c_int) -> LinuxResult<Arc<Self>> {
333 let f = super::fd_ops::get_file_like(fd)?;
334 f.into_any()
335 .downcast::<Self>()
336 .map_err(|_| LinuxError::EINVAL)
337 }
338
339 pub fn path(&self) -> &str {
341 &self.path
342 }
343}
344
345impl FileLike for Directory {
346 fn read(&self, _buf: &mut [u8]) -> LinuxResult<usize> {
347 Err(LinuxError::EBADF)
348 }
349
350 fn write(&self, _buf: &[u8]) -> LinuxResult<usize> {
351 Err(LinuxError::EBADF)
352 }
353
354 fn stat(&self) -> LinuxResult<ctypes::stat> {
355 Err(LinuxError::EBADF)
356 }
357
358 fn into_any(self: Arc<Self>) -> Arc<dyn core::any::Any + Send + Sync> {
359 self
360 }
361
362 fn poll(&self) -> LinuxResult<PollState> {
363 Ok(PollState {
364 readable: true,
365 writable: false,
366 })
367 }
368
369 fn set_nonblocking(&self, _nonblocking: bool) -> LinuxResult {
370 Ok(())
371 }
372}