xref: /DragonOS/kernel/src/filesystem/vfs/open.rs (revision 7b0ef10895108a0de5ff5ef3d2f93f40cf2e33a5)
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::{
13     driver::base::block::SeekFrom, process::ProcessManager,
14     syscall::user_access::check_and_clone_cstr,
15 };
16 use crate::{filesystem::vfs::syscall::UtimensFlags, process::cred::Kgid};
17 use crate::{
18     process::cred::GroupInfo,
19     time::{syscall::PosixTimeval, PosixTimeSpec},
20 };
21 use alloc::string::String;
22 
23 pub(super) fn do_faccessat(
24     dirfd: i32,
25     path: *const u8,
26     mode: ModeType,
27     flags: u32,
28 ) -> Result<usize, SystemError> {
29     if (mode.bits() & (!ModeType::S_IRWXO.bits())) != 0 {
30         return Err(SystemError::EINVAL);
31     }
32 
33     if (flags
34         & (!((AtFlags::AT_EACCESS | AtFlags::AT_SYMLINK_NOFOLLOW | AtFlags::AT_EMPTY_PATH).bits()
35             as u32)))
36         != 0
37     {
38         return Err(SystemError::EINVAL);
39     }
40 
41     // let follow_symlink = flags & AtFlags::AT_SYMLINK_NOFOLLOW.bits() as u32 == 0;
42 
43     let path = check_and_clone_cstr(path, Some(MAX_PATHLEN))?;
44     let path = path.to_str().map_err(|_| SystemError::EINVAL)?;
45 
46     let (inode, path) = user_path_at(&ProcessManager::current_pcb(), dirfd, path)?;
47 
48     // 如果找不到文件,则返回错误码ENOENT
49     let _inode = inode.lookup_follow_symlink(path.as_str(), VFS_MAX_FOLLOW_SYMLINK_TIMES)?;
50 
51     // todo: 接着完善(可以借鉴linux 6.1.9的do_faccessat)
52     return Ok(0);
53 }
54 
55 pub fn do_fchmodat(dirfd: i32, path: *const u8, _mode: ModeType) -> Result<usize, SystemError> {
56     let path = check_and_clone_cstr(path, Some(MAX_PATHLEN))?;
57     let path = path.to_str().map_err(|_| SystemError::EINVAL)?;
58 
59     let (inode, path) = user_path_at(&ProcessManager::current_pcb(), dirfd, path)?;
60 
61     // 如果找不到文件,则返回错误码ENOENT
62     let _inode = inode.lookup_follow_symlink(path.as_str(), VFS_MAX_FOLLOW_SYMLINK_TIMES)?;
63 
64     warn!("do_fchmodat: not implemented yet\n");
65     // todo: 真正去改变文件的权限
66 
67     return Ok(0);
68 }
69 
70 pub fn do_fchownat(
71     dirfd: i32,
72     path: &str,
73     uid: usize,
74     gid: usize,
75     flag: AtFlags,
76 ) -> Result<usize, SystemError> {
77     // 检查flag是否合法
78     if flag.contains(!(AtFlags::AT_SYMLINK_NOFOLLOW | AtFlags::AT_EMPTY_PATH)) {
79         return Err(SystemError::EINVAL);
80     }
81 
82     let follow_symlink = flag.contains(!AtFlags::AT_SYMLINK_NOFOLLOW);
83     let (inode, path) = user_path_at(&ProcessManager::current_pcb(), dirfd, path)?;
84 
85     // 如果找不到文件,则返回错误码ENOENT
86     let inode = if follow_symlink {
87         inode.lookup_follow_symlink2(path.as_str(), VFS_MAX_FOLLOW_SYMLINK_TIMES, false)
88     } else {
89         inode.lookup(path.as_str())
90     };
91 
92     if inode.is_err() {
93         let errno = inode.clone().unwrap_err();
94         // 文件不存在
95         if errno == SystemError::ENOENT {
96             return Err(SystemError::ENOENT);
97         }
98     }
99 
100     let inode = inode.unwrap();
101 
102     return chown_common(inode, uid, gid);
103 }
104 
105 fn chown_common(inode: Arc<dyn IndexNode>, uid: usize, gid: usize) -> Result<usize, SystemError> {
106     let mut meta = inode.metadata()?;
107     let cred = ProcessManager::current_pcb().cred();
108     let current_uid = cred.uid.data();
109     let current_gid = cred.gid.data();
110     let mut group_info = GroupInfo::default();
111     if let Some(info) = cred.group_info.as_ref() {
112         group_info = info.clone();
113     }
114 
115     // 检查权限
116     match current_uid {
117         0 => {
118             meta.uid = uid;
119             meta.gid = gid;
120         }
121         _ => {
122             // 非文件所有者不能更改信息,且不能更改uid
123             if current_uid != meta.uid || uid != meta.uid {
124                 return Err(SystemError::EPERM);
125             }
126             if gid != current_gid && !group_info.gids.contains(&Kgid::from(gid)) {
127                 return Err(SystemError::EPERM);
128             }
129             meta.gid = gid;
130         }
131     }
132 
133     meta.mode.remove(ModeType::S_ISUID | ModeType::S_ISGID);
134     inode.set_metadata(&meta)?;
135 
136     return Ok(0);
137 }
138 
139 pub fn ksys_fchown(fd: i32, uid: usize, gid: usize) -> Result<usize, SystemError> {
140     let fd_table = &ProcessManager::current_pcb().fd_table();
141     let fd_table = fd_table.read();
142 
143     let inode = fd_table.get_file_by_fd(fd).unwrap().inode();
144 
145     let result = chown_common(inode, uid, gid);
146 
147     drop(fd_table);
148 
149     return result;
150 }
151 
152 pub(super) fn do_sys_open(
153     dfd: i32,
154     path: &str,
155     o_flags: FileMode,
156     mode: ModeType,
157     follow_symlink: bool,
158 ) -> Result<usize, SystemError> {
159     let how = OpenHow::new(o_flags, mode, OpenHowResolve::empty());
160     return do_sys_openat2(dfd, path, how, follow_symlink);
161 }
162 
163 fn do_sys_openat2(
164     dirfd: i32,
165     path: &str,
166     how: OpenHow,
167     follow_symlink: bool,
168 ) -> Result<usize, SystemError> {
169     // debug!("open path: {}, how: {:?}", path, how);
170     let path = path.trim();
171 
172     let (inode_begin, path) = user_path_at(&ProcessManager::current_pcb(), dirfd, path)?;
173     let inode: Result<Arc<dyn IndexNode>, SystemError> = inode_begin.lookup_follow_symlink(
174         &path,
175         if follow_symlink {
176             VFS_MAX_FOLLOW_SYMLINK_TIMES
177         } else {
178             0
179         },
180     );
181 
182     let inode: Arc<dyn IndexNode> = match inode {
183         Ok(inode) => inode,
184         Err(errno) => {
185             // 文件不存在,且需要创建
186             if how.o_flags.contains(FileMode::O_CREAT)
187                 && !how.o_flags.contains(FileMode::O_DIRECTORY)
188                 && errno == SystemError::ENOENT
189             {
190                 let (filename, parent_path) = rsplit_path(&path);
191                 // 查找父目录
192                 let parent_inode: Arc<dyn IndexNode> =
193                     ROOT_INODE().lookup(parent_path.unwrap_or("/"))?;
194                 // 创建文件
195                 let inode: Arc<dyn IndexNode> = parent_inode.create(
196                     filename,
197                     FileType::File,
198                     ModeType::from_bits_truncate(0o755),
199                 )?;
200                 inode
201             } else {
202                 // 不需要创建文件,因此返回错误码
203                 return Err(errno);
204             }
205         }
206     };
207 
208     let file_type: FileType = inode.metadata()?.file_type;
209     // 如果要打开的是文件夹,而目标不是文件夹
210     if how.o_flags.contains(FileMode::O_DIRECTORY) && file_type != FileType::Dir {
211         return Err(SystemError::ENOTDIR);
212     }
213 
214     // 创建文件对象
215 
216     let file: File = File::new(inode, how.o_flags)?;
217 
218     // 打开模式为“追加”
219     if how.o_flags.contains(FileMode::O_APPEND) {
220         file.lseek(SeekFrom::SeekEnd(0))?;
221     }
222 
223     // 如果O_TRUNC,并且,打开模式包含O_RDWR或O_WRONLY,清空文件
224     if how.o_flags.contains(FileMode::O_TRUNC)
225         && (how.o_flags.contains(FileMode::O_RDWR) || how.o_flags.contains(FileMode::O_WRONLY))
226         && file_type == FileType::File
227     {
228         file.ftruncate(0)?;
229     }
230     // 把文件对象存入pcb
231     let r = ProcessManager::current_pcb()
232         .fd_table()
233         .write()
234         .alloc_fd(file, None)
235         .map(|fd| fd as usize);
236 
237     return r;
238 }
239 
240 /// On Linux, futimens() is a library function implemented on top of
241 /// the utimensat() system call.  To support this, the Linux
242 /// utimensat() system call implements a nonstandard feature: if
243 /// pathname is NULL, then the call modifies the timestamps of the
244 /// file referred to by the file descriptor dirfd (which may refer to
245 /// any type of file).
246 pub fn do_utimensat(
247     dirfd: i32,
248     pathname: Option<String>,
249     times: Option<[PosixTimeSpec; 2]>,
250     flags: UtimensFlags,
251 ) -> Result<usize, SystemError> {
252     const UTIME_NOW: i64 = (1i64 << 30) - 1i64;
253     const UTIME_OMIT: i64 = (1i64 << 30) - 2i64;
254     // log::debug!("do_utimensat: dirfd:{}, pathname:{:?}, times:{:?}, flags:{:?}", dirfd, pathname, times, flags);
255     let inode = match pathname {
256         Some(path) => {
257             let (inode_begin, path) =
258                 user_path_at(&ProcessManager::current_pcb(), dirfd, path.as_str())?;
259             let inode = if flags.contains(UtimensFlags::AT_SYMLINK_NOFOLLOW) {
260                 inode_begin.lookup(path.as_str())?
261             } else {
262                 inode_begin.lookup_follow_symlink(path.as_str(), VFS_MAX_FOLLOW_SYMLINK_TIMES)?
263             };
264             inode
265         }
266         None => {
267             let binding = ProcessManager::current_pcb().fd_table();
268             let fd_table_guard = binding.write();
269             let file = fd_table_guard
270                 .get_file_by_fd(dirfd)
271                 .ok_or(SystemError::EBADF)?;
272             file.inode()
273         }
274     };
275     let now = PosixTimeSpec::now();
276     let mut meta = inode.metadata()?;
277 
278     if let Some([atime, mtime]) = times {
279         if atime.tv_nsec == UTIME_NOW {
280             meta.atime = now;
281         } else if atime.tv_nsec != UTIME_OMIT {
282             meta.atime = atime;
283         }
284         if mtime.tv_nsec == UTIME_NOW {
285             meta.mtime = now;
286         } else if mtime.tv_nsec != UTIME_OMIT {
287             meta.mtime = mtime;
288         }
289         inode.set_metadata(&meta).unwrap();
290     } else {
291         meta.atime = now;
292         meta.mtime = now;
293         inode.set_metadata(&meta).unwrap();
294     }
295     return Ok(0);
296 }
297 
298 pub fn do_utimes(path: &str, times: Option<[PosixTimeval; 2]>) -> Result<usize, SystemError> {
299     // log::debug!("do_utimes: path:{:?}, times:{:?}", path, times);
300     let (inode_begin, path) = user_path_at(
301         &ProcessManager::current_pcb(),
302         AtFlags::AT_FDCWD.bits(),
303         path,
304     )?;
305     let inode = inode_begin.lookup_follow_symlink(path.as_str(), VFS_MAX_FOLLOW_SYMLINK_TIMES)?;
306     let mut meta = inode.metadata()?;
307 
308     if let Some([atime, mtime]) = times {
309         meta.atime = PosixTimeSpec::from(atime);
310         meta.mtime = PosixTimeSpec::from(mtime);
311         inode.set_metadata(&meta)?;
312     } else {
313         let now = PosixTimeSpec::now();
314         meta.atime = now;
315         meta.mtime = now;
316         inode.set_metadata(&meta)?;
317     }
318     return Ok(0);
319 }
320