xref: /DragonOS/kernel/src/filesystem/vfs/open.rs (revision a8753f8fffb992e4d3bbd21eda431081b395af6b)
1 use alloc::sync::Arc;
2 use log::warn;
3 use system_error::SystemError;
4 
5 use super::{
6     fcntl::AtFlags,
7     file::{File, FileMode},
8     syscall::{ModeType, OpenHow, OpenHowResolve},
9     utils::{rsplit_path, user_path_at},
10     FileType, IndexNode, MAX_PATHLEN, ROOT_INODE, VFS_MAX_FOLLOW_SYMLINK_TIMES,
11 };
12 use crate::filesystem::vfs::syscall::UtimensFlags;
13 use crate::time::{syscall::PosixTimeval, PosixTimeSpec};
14 use crate::{
15     driver::base::block::SeekFrom, process::ProcessManager,
16     syscall::user_access::check_and_clone_cstr,
17 };
18 use alloc::string::String;
19 
20 pub(super) fn do_faccessat(
21     dirfd: i32,
22     path: *const u8,
23     mode: ModeType,
24     flags: u32,
25 ) -> Result<usize, SystemError> {
26     if (mode.bits() & (!ModeType::S_IRWXO.bits())) != 0 {
27         return Err(SystemError::EINVAL);
28     }
29 
30     if (flags
31         & (!((AtFlags::AT_EACCESS | AtFlags::AT_SYMLINK_NOFOLLOW | AtFlags::AT_EMPTY_PATH).bits()
32             as u32)))
33         != 0
34     {
35         return Err(SystemError::EINVAL);
36     }
37 
38     // let follow_symlink = flags & AtFlags::AT_SYMLINK_NOFOLLOW.bits() as u32 == 0;
39 
40     let path = check_and_clone_cstr(path, Some(MAX_PATHLEN))?;
41     let path = path.to_str().map_err(|_| SystemError::EINVAL)?;
42 
43     let (inode, path) = user_path_at(&ProcessManager::current_pcb(), dirfd, path)?;
44 
45     // 如果找不到文件,则返回错误码ENOENT
46     let _inode = inode.lookup_follow_symlink(path.as_str(), VFS_MAX_FOLLOW_SYMLINK_TIMES)?;
47 
48     // todo: 接着完善(可以借鉴linux 6.1.9的do_faccessat)
49     return Ok(0);
50 }
51 
52 pub fn do_fchmodat(dirfd: i32, path: *const u8, _mode: ModeType) -> Result<usize, SystemError> {
53     let path = check_and_clone_cstr(path, Some(MAX_PATHLEN))?;
54     let path = path.to_str().map_err(|_| SystemError::EINVAL)?;
55 
56     let (inode, path) = user_path_at(&ProcessManager::current_pcb(), dirfd, path)?;
57 
58     // 如果找不到文件,则返回错误码ENOENT
59     let _inode = inode.lookup_follow_symlink(path.as_str(), VFS_MAX_FOLLOW_SYMLINK_TIMES)?;
60 
61     warn!("do_fchmodat: not implemented yet\n");
62     // todo: 真正去改变文件的权限
63 
64     return Ok(0);
65 }
66 
67 pub(super) fn do_sys_open(
68     dfd: i32,
69     path: &str,
70     o_flags: FileMode,
71     mode: ModeType,
72     follow_symlink: bool,
73 ) -> Result<usize, SystemError> {
74     let how = OpenHow::new(o_flags, mode, OpenHowResolve::empty());
75     return do_sys_openat2(dfd, path, how, follow_symlink);
76 }
77 
78 fn do_sys_openat2(
79     dirfd: i32,
80     path: &str,
81     how: OpenHow,
82     follow_symlink: bool,
83 ) -> Result<usize, SystemError> {
84     // debug!("open path: {}, how: {:?}", path, how);
85     let path = path.trim();
86 
87     let (inode_begin, path) = user_path_at(&ProcessManager::current_pcb(), dirfd, path)?;
88     let inode: Result<Arc<dyn IndexNode>, SystemError> = inode_begin.lookup_follow_symlink(
89         &path,
90         if follow_symlink {
91             VFS_MAX_FOLLOW_SYMLINK_TIMES
92         } else {
93             0
94         },
95     );
96 
97     let inode: Arc<dyn IndexNode> = match inode {
98         Ok(inode) => inode,
99         Err(errno) => {
100             // 文件不存在,且需要创建
101             if how.o_flags.contains(FileMode::O_CREAT)
102                 && !how.o_flags.contains(FileMode::O_DIRECTORY)
103                 && errno == SystemError::ENOENT
104             {
105                 let (filename, parent_path) = rsplit_path(&path);
106                 // 查找父目录
107                 let parent_inode: Arc<dyn IndexNode> =
108                     ROOT_INODE().lookup(parent_path.unwrap_or("/"))?;
109                 // 创建文件
110                 let inode: Arc<dyn IndexNode> = parent_inode.create(
111                     filename,
112                     FileType::File,
113                     ModeType::from_bits_truncate(0o755),
114                 )?;
115                 inode
116             } else {
117                 // 不需要创建文件,因此返回错误码
118                 return Err(errno);
119             }
120         }
121     };
122 
123     let file_type: FileType = inode.metadata()?.file_type;
124     // 如果要打开的是文件夹,而目标不是文件夹
125     if how.o_flags.contains(FileMode::O_DIRECTORY) && file_type != FileType::Dir {
126         return Err(SystemError::ENOTDIR);
127     }
128 
129     // 创建文件对象
130 
131     let file: File = File::new(inode, how.o_flags)?;
132 
133     // 打开模式为“追加”
134     if how.o_flags.contains(FileMode::O_APPEND) {
135         file.lseek(SeekFrom::SeekEnd(0))?;
136     }
137 
138     // 如果O_TRUNC,并且,打开模式包含O_RDWR或O_WRONLY,清空文件
139     if how.o_flags.contains(FileMode::O_TRUNC)
140         && (how.o_flags.contains(FileMode::O_RDWR) || how.o_flags.contains(FileMode::O_WRONLY))
141         && file_type == FileType::File
142     {
143         file.ftruncate(0)?;
144     }
145     // 把文件对象存入pcb
146     let r = ProcessManager::current_pcb()
147         .fd_table()
148         .write()
149         .alloc_fd(file, None)
150         .map(|fd| fd as usize);
151 
152     return r;
153 }
154 
155 /// On Linux, futimens() is a library function implemented on top of
156 /// the utimensat() system call.  To support this, the Linux
157 /// utimensat() system call implements a nonstandard feature: if
158 /// pathname is NULL, then the call modifies the timestamps of the
159 /// file referred to by the file descriptor dirfd (which may refer to
160 /// any type of file).
161 pub fn do_utimensat(
162     dirfd: i32,
163     pathname: Option<String>,
164     times: Option<[PosixTimeSpec; 2]>,
165     flags: UtimensFlags,
166 ) -> Result<usize, SystemError> {
167     const UTIME_NOW: i64 = (1i64 << 30) - 1i64;
168     const UTIME_OMIT: i64 = (1i64 << 30) - 2i64;
169     // log::debug!("do_utimensat: dirfd:{}, pathname:{:?}, times:{:?}, flags:{:?}", dirfd, pathname, times, flags);
170     let inode = match pathname {
171         Some(path) => {
172             let (inode_begin, path) =
173                 user_path_at(&ProcessManager::current_pcb(), dirfd, path.as_str())?;
174             let inode = if flags.contains(UtimensFlags::AT_SYMLINK_NOFOLLOW) {
175                 inode_begin.lookup(path.as_str())?
176             } else {
177                 inode_begin.lookup_follow_symlink(path.as_str(), VFS_MAX_FOLLOW_SYMLINK_TIMES)?
178             };
179             inode
180         }
181         None => {
182             let binding = ProcessManager::current_pcb().fd_table();
183             let fd_table_guard = binding.write();
184             let file = fd_table_guard
185                 .get_file_by_fd(dirfd)
186                 .ok_or(SystemError::EBADF)?;
187             file.inode()
188         }
189     };
190     let now = PosixTimeSpec::now();
191     let mut meta = inode.metadata()?;
192 
193     if let Some([atime, mtime]) = times {
194         if atime.tv_nsec == UTIME_NOW {
195             meta.atime = now;
196         } else if atime.tv_nsec != UTIME_OMIT {
197             meta.atime = atime;
198         }
199         if mtime.tv_nsec == UTIME_NOW {
200             meta.mtime = now;
201         } else if mtime.tv_nsec != UTIME_OMIT {
202             meta.mtime = mtime;
203         }
204         inode.set_metadata(&meta).unwrap();
205     } else {
206         meta.atime = now;
207         meta.mtime = now;
208         inode.set_metadata(&meta).unwrap();
209     }
210     return Ok(0);
211 }
212 
213 pub fn do_utimes(path: &str, times: Option<[PosixTimeval; 2]>) -> Result<usize, SystemError> {
214     // log::debug!("do_utimes: path:{:?}, times:{:?}", path, times);
215     let (inode_begin, path) = user_path_at(
216         &ProcessManager::current_pcb(),
217         AtFlags::AT_FDCWD.bits(),
218         path,
219     )?;
220     let inode = inode_begin.lookup_follow_symlink(path.as_str(), VFS_MAX_FOLLOW_SYMLINK_TIMES)?;
221     let mut meta = inode.metadata()?;
222 
223     if let Some([atime, mtime]) = times {
224         meta.atime = PosixTimeSpec::from(atime);
225         meta.mtime = PosixTimeSpec::from(mtime);
226         inode.set_metadata(&meta)?;
227     } else {
228         let now = PosixTimeSpec::now();
229         meta.atime = now;
230         meta.mtime = now;
231         inode.set_metadata(&meta)?;
232     }
233     return Ok(0);
234 }
235