Add support to handle symbolic link file
This commit is contained in:
		
							parent
							
								
									3cd46fd224
								
							
						
					
					
						commit
						1ad8f22170
					
				
							
								
								
									
										2
									
								
								deps/sefs
									
									
									
									
										vendored
									
									
								
							
							
								
								
								
								
								
									
									
								
							
						
						
									
										2
									
								
								deps/sefs
									
									
									
									
										vendored
									
									
								
							| @ -1 +1 @@ | ||||
| Subproject commit cc6f694c0ac27755a1b0e1546022d598eb53ca76 | ||||
| Subproject commit 2420622f050efda627ccebcff6e86a71dcf405f5 | ||||
| @ -95,6 +95,7 @@ impl ToErrno for rcore_fs::vfs::FsError { | ||||
|             FsError::WrProtected => EROFS, | ||||
|             FsError::NoIntegrity => EIO, | ||||
|             FsError::PermError => EPERM, | ||||
|             FsError::NameTooLong => ENAMETOOLONG, | ||||
|         } | ||||
|     } | ||||
| } | ||||
|  | ||||
| @ -49,7 +49,7 @@ pub fn do_faccessat( | ||||
|     ); | ||||
|     if Path::new(path).is_absolute() { | ||||
|         // Path is absolute, so dirfd is ignored
 | ||||
|         return Ok(do_access(path, mode)?); | ||||
|         return Ok(do_access(path, mode, flags)?); | ||||
|     } | ||||
|     let path = match dirfd { | ||||
|         DirFd::Fd(dirfd) => { | ||||
| @ -58,14 +58,22 @@ pub fn do_faccessat( | ||||
|         } | ||||
|         DirFd::Cwd => path.to_owned(), | ||||
|     }; | ||||
|     do_access(&path, mode) | ||||
|     do_access(&path, mode, flags) | ||||
| } | ||||
| 
 | ||||
| fn do_access(path: &str, mode: AccessibilityCheckMode) -> Result<()> { | ||||
| fn do_access( | ||||
|     path: &str, | ||||
|     mode: AccessibilityCheckMode, | ||||
|     flags: AccessibilityCheckFlags, | ||||
| ) -> Result<()> { | ||||
|     let inode = { | ||||
|         let current = current!(); | ||||
|         let fs = current.fs().lock().unwrap(); | ||||
|         fs.lookup_inode(path)? | ||||
|         if flags.contains(AccessibilityCheckFlags::AT_SYMLINK_NOFOLLOW) { | ||||
|             fs.lookup_inode_no_follow(path)? | ||||
|         } else { | ||||
|             fs.lookup_inode(path)? | ||||
|         } | ||||
|     }; | ||||
|     if mode.test_for_exist() { | ||||
|         return Ok(()); | ||||
|  | ||||
| @ -1,8 +1,17 @@ | ||||
| use super::*; | ||||
| 
 | ||||
| pub fn do_chown(path: &str, uid: u32, gid: u32) -> Result<()> { | ||||
|     warn!("chown is partial implemented as lchown"); | ||||
|     do_lchown(path, uid, gid) | ||||
|     debug!("chown: path: {:?}, uid: {}, gid: {}", path, uid, gid); | ||||
|     let inode = { | ||||
|         let current = current!(); | ||||
|         let fs = current.fs().lock().unwrap(); | ||||
|         fs.lookup_inode(path)? | ||||
|     }; | ||||
|     let mut info = inode.metadata()?; | ||||
|     info.uid = uid as usize; | ||||
|     info.gid = gid as usize; | ||||
|     inode.set_metadata(&info)?; | ||||
|     Ok(()) | ||||
| } | ||||
| 
 | ||||
| pub fn do_fchown(fd: FileDesc, uid: u32, gid: u32) -> Result<()> { | ||||
| @ -20,7 +29,7 @@ pub fn do_lchown(path: &str, uid: u32, gid: u32) -> Result<()> { | ||||
|     let inode = { | ||||
|         let current = current!(); | ||||
|         let fs = current.fs().lock().unwrap(); | ||||
|         fs.lookup_inode(path)? | ||||
|         fs.lookup_inode_no_follow(path)? | ||||
|     }; | ||||
|     let mut info = inode.metadata()?; | ||||
|     info.uid = uid as usize; | ||||
|  | ||||
| @ -71,6 +71,10 @@ impl CreationFlags { | ||||
|     pub fn is_exclusive(&self) -> bool { | ||||
|         self.contains(CreationFlags::O_EXCL) | ||||
|     } | ||||
| 
 | ||||
|     pub fn no_follow_symlink(&self) -> bool { | ||||
|         self.contains(CreationFlags::O_NOFOLLOW) | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| bitflags! { | ||||
| @ -99,4 +103,8 @@ impl StatusFlags { | ||||
|     pub fn always_append(&self) -> bool { | ||||
|         self.contains(StatusFlags::O_APPEND) | ||||
|     } | ||||
| 
 | ||||
|     pub fn is_fast_open(&self) -> bool { | ||||
|         self.contains(StatusFlags::O_PATH) | ||||
|     } | ||||
| } | ||||
|  | ||||
| @ -7,7 +7,7 @@ pub fn do_link(oldpath: &str, newpath: &str) -> Result<()> { | ||||
|     let (inode, new_dir_inode) = { | ||||
|         let current = current!(); | ||||
|         let fs = current.fs().lock().unwrap(); | ||||
|         let inode = fs.lookup_inode(&oldpath)?; | ||||
|         let inode = fs.lookup_inode_no_follow(&oldpath)?; | ||||
|         let new_dir_inode = fs.lookup_inode(new_dir_path)?; | ||||
|         (inode, new_dir_inode) | ||||
|     }; | ||||
|  | ||||
| @ -25,7 +25,7 @@ pub use self::rename::do_rename; | ||||
| pub use self::rmdir::do_rmdir; | ||||
| pub use self::sendfile::do_sendfile; | ||||
| pub use self::stat::{do_fstat, do_fstatat, do_lstat, Stat, StatFlags}; | ||||
| pub use self::symlink::do_readlink; | ||||
| pub use self::symlink::{do_readlink, do_symlinkat}; | ||||
| pub use self::truncate::{do_ftruncate, do_truncate}; | ||||
| pub use self::unlink::do_unlink; | ||||
| pub use self::write::{do_pwrite, do_write, do_writev}; | ||||
|  | ||||
| @ -139,7 +139,7 @@ fn do_stat(path: &str) -> Result<Stat> { | ||||
|     let inode = { | ||||
|         let current = current!(); | ||||
|         let fs = current.fs().lock().unwrap(); | ||||
|         fs.lookup_inode_follow(&path)? | ||||
|         fs.lookup_inode(&path)? | ||||
|     }; | ||||
|     let stat = Stat::from(inode.metadata()?); | ||||
|     Ok(stat) | ||||
| @ -149,7 +149,6 @@ pub fn do_fstat(fd: u32) -> Result<Stat> { | ||||
|     debug!("fstat: fd: {}", fd); | ||||
|     let file_ref = current!().file(fd as FileDesc)?; | ||||
|     let stat = Stat::from(file_ref.metadata()?); | ||||
|     // TODO: handle symlink
 | ||||
|     Ok(stat) | ||||
| } | ||||
| 
 | ||||
| @ -158,7 +157,7 @@ pub fn do_lstat(path: &str) -> Result<Stat> { | ||||
|     let inode = { | ||||
|         let current = current!(); | ||||
|         let fs = current.fs().lock().unwrap(); | ||||
|         fs.lookup_inode(&path)? | ||||
|         fs.lookup_inode_no_follow(&path)? | ||||
|     }; | ||||
|     let stat = Stat::from(inode.metadata()?); | ||||
|     Ok(stat) | ||||
| @ -185,9 +184,14 @@ pub fn do_fstatat(dirfd: DirFd, path: &str, flags: StatFlags) -> Result<Stat> { | ||||
|             } else { | ||||
|                 let dir_path = get_dir_path(dirfd)?; | ||||
|                 let path = dir_path + "/" + path; | ||||
|                 do_stat(&path) | ||||
|                 if !flags.contains(StatFlags::AT_SYMLINK_NOFOLLOW) { | ||||
|                     do_stat(&path) | ||||
|                 } else { | ||||
|                     do_lstat(&path) | ||||
|                 } | ||||
|             } | ||||
|         } | ||||
|         DirFd::Cwd => do_stat(path), | ||||
|         DirFd::Cwd if !flags.contains(StatFlags::AT_SYMLINK_NOFOLLOW) => do_stat(path), | ||||
|         DirFd::Cwd => do_lstat(path), | ||||
|     } | ||||
| } | ||||
|  | ||||
| @ -18,11 +18,60 @@ pub fn do_readlink(path: &str, buf: &mut [u8]) -> Result<usize> { | ||||
|                 return_errno!(EINVAL, "not a normal file link") | ||||
|             } | ||||
|         } else { | ||||
|             // TODO: support symbolic links
 | ||||
|             return_errno!(EINVAL, "not a symbolic link") | ||||
|             let inode = { | ||||
|                 let current = current!(); | ||||
|                 let fs = current.fs().lock().unwrap(); | ||||
|                 fs.lookup_inode_no_follow(path)? | ||||
|             }; | ||||
|             if inode.metadata()?.type_ != FileType::SymLink { | ||||
|                 return_errno!(EINVAL, "not a symbolic link"); | ||||
|             } | ||||
|             let mut content = vec![0u8; PATH_MAX]; | ||||
|             let len = inode.read_at(0, &mut content)?; | ||||
|             let path = std::str::from_utf8(&content[..len]) | ||||
|                 .map_err(|_| errno!(EINVAL, "invalid symlink"))?; | ||||
|             String::from(path) | ||||
|         } | ||||
|     }; | ||||
|     let len = file_path.len().min(buf.len()); | ||||
|     buf[0..len].copy_from_slice(&file_path.as_bytes()[0..len]); | ||||
|     Ok(len) | ||||
| } | ||||
| 
 | ||||
| fn do_symlink(target: &str, link_path: &str) -> Result<usize> { | ||||
|     let (dir_path, link_name) = split_path(&link_path); | ||||
|     let dir_inode = { | ||||
|         let current = current!(); | ||||
|         let fs = current.fs().lock().unwrap(); | ||||
|         fs.lookup_inode(dir_path)? | ||||
|     }; | ||||
|     if !dir_inode.allow_write()? { | ||||
|         return_errno!(EPERM, "symlink cannot be created"); | ||||
|     } | ||||
|     let link_inode = dir_inode.create(link_name, FileType::SymLink, 0o0777)?; | ||||
|     let data = target.as_bytes(); | ||||
|     link_inode.resize(data.len())?; | ||||
|     link_inode.write_at(0, data)?; | ||||
|     Ok(0) | ||||
| } | ||||
| 
 | ||||
| pub fn do_symlinkat(target: &str, new_dirfd: DirFd, link_path: &str) -> Result<usize> { | ||||
|     debug!( | ||||
|         "symlinkat: target: {:?}, new_dirfd: {:?}, link_path: {:?}", | ||||
|         target, new_dirfd, link_path | ||||
|     ); | ||||
|     if target.is_empty() || link_path.is_empty() { | ||||
|         return_errno!(ENOENT, "target or linkpath is an empty string"); | ||||
|     } | ||||
|     if target.len() > PATH_MAX || link_path.len() > PATH_MAX { | ||||
|         return_errno!(ENAMETOOLONG, "target or linkpath is too long"); | ||||
|     } | ||||
|     let link_path = match new_dirfd { | ||||
|         DirFd::Fd(dirfd) => { | ||||
|             let dir_path = get_dir_path(dirfd)?; | ||||
|             dir_path + "/" + link_path | ||||
|         } | ||||
|         DirFd::Cwd => link_path.to_owned(), | ||||
|     }; | ||||
|     do_symlink(target, &link_path) | ||||
| } | ||||
|  | ||||
| @ -53,64 +53,120 @@ impl FsView { | ||||
|             return Ok(Box::new(DevSgx)); | ||||
|         } | ||||
|         let creation_flags = CreationFlags::from_bits_truncate(flags); | ||||
|         let inode = if creation_flags.can_create() { | ||||
|             let (dir_path, file_name) = split_path(&path); | ||||
|             let dir_inode = self.lookup_inode(dir_path)?; | ||||
|             match dir_inode.find(file_name) { | ||||
|                 Ok(file_inode) => { | ||||
|                     if creation_flags.is_exclusive() { | ||||
|         let inode = if creation_flags.no_follow_symlink() { | ||||
|             match self.lookup_inode_no_follow(path) { | ||||
|                 Ok(inode) => { | ||||
|                     let status_flags = StatusFlags::from_bits_truncate(flags); | ||||
|                     if inode.metadata()?.type_ == FileType::SymLink && !status_flags.is_fast_open() | ||||
|                     { | ||||
|                         return_errno!(ELOOP, "file is a symlink"); | ||||
|                     } | ||||
|                     if creation_flags.can_create() && creation_flags.is_exclusive() { | ||||
|                         return_errno!(EEXIST, "file exists"); | ||||
|                     } | ||||
|                     file_inode | ||||
|                     inode | ||||
|                 } | ||||
|                 Err(FsError::EntryNotFound) => { | ||||
|                 Err(e) if e.errno() == ENOENT && creation_flags.can_create() => { | ||||
|                     let (dir_path, file_name) = split_path(&path); | ||||
|                     let dir_inode = self.lookup_inode(dir_path)?; | ||||
|                     if !dir_inode.allow_write()? { | ||||
|                         return_errno!(EPERM, "file cannot be created"); | ||||
|                     } | ||||
|                     dir_inode.create(file_name, FileType::File, mode)? | ||||
|                 } | ||||
|                 Err(e) => return Err(Error::from(e)), | ||||
|                 Err(e) => return Err(e), | ||||
|             } | ||||
|         } else { | ||||
|             self.lookup_inode(&path)? | ||||
|             match self.lookup_inode(path) { | ||||
|                 Ok(inode) => { | ||||
|                     if creation_flags.can_create() && creation_flags.is_exclusive() { | ||||
|                         return_errno!(EEXIST, "file exists"); | ||||
|                     } | ||||
|                     inode | ||||
|                 } | ||||
|                 Err(e) if e.errno() == ENOENT && creation_flags.can_create() => { | ||||
|                     let real_path = self.lookup_real_path(&path)?; | ||||
|                     let (dir_path, file_name) = split_path(&real_path); | ||||
|                     let dir_inode = self.lookup_inode(dir_path)?; | ||||
|                     if !dir_inode.allow_write()? { | ||||
|                         return_errno!(EPERM, "file cannot be created"); | ||||
|                     } | ||||
|                     dir_inode.create(file_name, FileType::File, mode)? | ||||
|                 } | ||||
|                 Err(e) => return Err(e), | ||||
|             } | ||||
|         }; | ||||
|         let abs_path = self.convert_to_abs_path(&path); | ||||
|         Ok(Box::new(INodeFile::open(inode, &abs_path, flags)?)) | ||||
|     } | ||||
| 
 | ||||
|     /// Lookup INode from the cwd of the process
 | ||||
|     pub fn lookup_inode(&self, path: &str) -> Result<Arc<dyn INode>> { | ||||
|         self.lookup_inode_follow_with_max_times(path, 0) | ||||
|     /// Recursively lookup the real path of giving path, dereference symlinks
 | ||||
|     pub fn lookup_real_path(&self, path: &str) -> Result<String> { | ||||
|         let (dir_path, file_name) = split_path(&path); | ||||
|         let dir_inode = self.lookup_inode(dir_path)?; | ||||
|         match dir_inode.find(file_name) { | ||||
|             // Handle symlink
 | ||||
|             Ok(inode) if inode.metadata()?.type_ == FileType::SymLink => { | ||||
|                 let new_path = { | ||||
|                     let mut content = vec![0u8; PATH_MAX]; | ||||
|                     let len = inode.read_at(0, &mut content)?; | ||||
|                     let path = std::str::from_utf8(&content[..len]) | ||||
|                         .map_err(|_| errno!(ENOENT, "invalid symlink content"))?; | ||||
|                     let path = String::from(path); | ||||
|                     match path.chars().next() { | ||||
|                         None => unreachable!(), | ||||
|                         // absolute path
 | ||||
|                         Some('/') => path, | ||||
|                         // relative path
 | ||||
|                         _ => { | ||||
|                             let dir_path = if dir_path.ends_with("/") { | ||||
|                                 String::from(dir_path) | ||||
|                             } else { | ||||
|                                 String::from(dir_path) + "/" | ||||
|                             }; | ||||
|                             dir_path + &path | ||||
|                         } | ||||
|                     } | ||||
|                 }; | ||||
|                 self.lookup_real_path(&new_path) | ||||
|             } | ||||
|             Err(FsError::EntryNotFound) | Ok(_) => { | ||||
|                 debug!("real_path: cwd: {:?}, path: {:?}", self.cwd(), path); | ||||
|                 Ok(String::from(path)) | ||||
|             } | ||||
|             Err(e) => return Err(Error::from(e)), | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     /// Lookup INode from the cwd of the process, follow symlinks
 | ||||
|     pub fn lookup_inode_follow(&self, path: &str) -> Result<Arc<dyn INode>> { | ||||
|     /// Lookup INode from the cwd of the process. If path is a symlink, do not dereference it
 | ||||
|     pub fn lookup_inode_no_follow(&self, path: &str) -> Result<Arc<dyn INode>> { | ||||
|         debug!("lookup_inode: cwd: {:?}, path: {:?}", self.cwd(), path); | ||||
|         let (dir_path, file_name) = split_path(&path); | ||||
|         let dir_inode = self.lookup_inode(dir_path)?; | ||||
|         Ok(dir_inode.lookup(file_name)?) | ||||
|     } | ||||
| 
 | ||||
|     /// Lookup INode from the cwd of the process, dereference symlink
 | ||||
|     pub fn lookup_inode(&self, path: &str) -> Result<Arc<dyn INode>> { | ||||
|         // Linux uses 40 as the upper limit for resolving symbolic links,
 | ||||
|         // so Occlum use it as a reasonable value
 | ||||
|         const MAX_SYMLINKS: usize = 40; | ||||
|         self.lookup_inode_follow_with_max_times(path, MAX_SYMLINKS) | ||||
|     } | ||||
| 
 | ||||
|     fn lookup_inode_follow_with_max_times( | ||||
|         &self, | ||||
|         path: &str, | ||||
|         max_times: usize, | ||||
|     ) -> Result<Arc<dyn INode>> { | ||||
|         debug!( | ||||
|             "lookup_inode_follow_with_max_times: cwd: {:?}, path: {:?}, max_times: {}", | ||||
|             "lookup_inode_follow: cwd: {:?}, path: {:?}", | ||||
|             self.cwd(), | ||||
|             path, | ||||
|             max_times | ||||
|             path | ||||
|         ); | ||||
|         if path.len() > 0 && path.as_bytes()[0] == b'/' { | ||||
|             // absolute path
 | ||||
|             let abs_path = path.trim_start_matches('/'); | ||||
|             let inode = ROOT_INODE.lookup_follow(abs_path, max_times)?; | ||||
|             let inode = ROOT_INODE.lookup_follow(abs_path, MAX_SYMLINKS)?; | ||||
|             Ok(inode) | ||||
|         } else { | ||||
|             // relative path
 | ||||
|             let cwd = self.cwd().trim_start_matches('/'); | ||||
|             let inode = ROOT_INODE.lookup(cwd)?.lookup_follow(path, max_times)?; | ||||
|             let inode = ROOT_INODE | ||||
|                 .lookup_follow(cwd, MAX_SYMLINKS)? | ||||
|                 .lookup_follow(path, MAX_SYMLINKS)?; | ||||
|             Ok(inode) | ||||
|         } | ||||
|     } | ||||
|  | ||||
| @ -127,7 +127,10 @@ impl INode for HNode { | ||||
|             FileType::File => { | ||||
|                 try_std!(fs::File::create(&new_path)); | ||||
|             } | ||||
|             _ => unimplemented!("only support creating files in HostFS"), | ||||
|             _ => { | ||||
|                 warn!("only support creating regular files in HostFS"); | ||||
|                 return Err(FsError::PermError); | ||||
|             } | ||||
|         } | ||||
|         Ok(Arc::new(HNode { | ||||
|             path: new_path, | ||||
|  | ||||
| @ -1,7 +1,7 @@ | ||||
| use super::*; | ||||
| 
 | ||||
| use process; | ||||
| use rcore_fs::vfs::{FileSystem, FileType, FsError, INode, Metadata, Timespec}; | ||||
| use rcore_fs::vfs::{FileSystem, FileType, FsError, INode, Metadata, Timespec, PATH_MAX}; | ||||
| use std; | ||||
| use std::any::Any; | ||||
| use std::fmt; | ||||
|  | ||||
| @ -388,6 +388,34 @@ pub fn do_readlink(path: *const i8, buf: *mut u8, size: usize) -> Result<isize> | ||||
|     Ok(len as isize) | ||||
| } | ||||
| 
 | ||||
| pub fn do_symlink(target: *const i8, link_path: *const i8) -> Result<isize> { | ||||
|     let target = from_user::clone_cstring_safely(target)? | ||||
|         .to_string_lossy() | ||||
|         .into_owned(); | ||||
|     let link_path = from_user::clone_cstring_safely(link_path)? | ||||
|         .to_string_lossy() | ||||
|         .into_owned(); | ||||
|     file_ops::do_symlinkat(&target, DirFd::Cwd, &link_path)?; | ||||
|     Ok(0) | ||||
| } | ||||
| 
 | ||||
| pub fn do_symlinkat(target: *const i8, new_dirfd: i32, link_path: *const i8) -> Result<isize> { | ||||
|     let target = from_user::clone_cstring_safely(target)? | ||||
|         .to_string_lossy() | ||||
|         .into_owned(); | ||||
|     let link_path = from_user::clone_cstring_safely(link_path)? | ||||
|         .to_string_lossy() | ||||
|         .into_owned(); | ||||
|     let new_dirfd = if Path::new(&link_path).is_absolute() { | ||||
|         // Link path is absolute, new_dirfd is treated as Cwd
 | ||||
|         DirFd::Cwd | ||||
|     } else { | ||||
|         DirFd::from_i32(new_dirfd)? | ||||
|     }; | ||||
|     file_ops::do_symlinkat(&target, new_dirfd, &link_path)?; | ||||
|     Ok(0) | ||||
| } | ||||
| 
 | ||||
| pub fn do_chmod(path: *const i8, mode: u16) -> Result<isize> { | ||||
|     let path = from_user::clone_cstring_safely(path)? | ||||
|         .to_string_lossy() | ||||
|  | ||||
| @ -64,7 +64,7 @@ pub fn load_file_to_vec(file_path: &str, current_ref: &ThreadRef) -> Result<Vec< | ||||
|         .fs() | ||||
|         .lock() | ||||
|         .unwrap() | ||||
|         .lookup_inode_follow(file_path) | ||||
|         .lookup_inode(file_path) | ||||
|         .map_err(|e| errno!(e.errno(), "cannot find the file"))?; | ||||
|     let file_mode = { | ||||
|         let info = inode.metadata()?; | ||||
|  | ||||
| @ -22,8 +22,9 @@ use crate::fs::{ | ||||
|     do_eventfd2, do_faccessat, do_fchmod, do_fchown, do_fcntl, do_fdatasync, do_fstat, do_fstatat, | ||||
|     do_fsync, do_ftruncate, do_getcwd, do_getdents64, do_ioctl, do_lchown, do_link, do_lseek, | ||||
|     do_lstat, do_mkdir, do_open, do_openat, do_pipe, do_pipe2, do_pread, do_pwrite, do_read, | ||||
|     do_readlink, do_readv, do_rename, do_rmdir, do_sendfile, do_stat, do_sync, do_truncate, | ||||
|     do_unlink, do_write, do_writev, iovec_t, File, FileDesc, FileRef, HostStdioFds, Stat, | ||||
|     do_readlink, do_readv, do_rename, do_rmdir, do_sendfile, do_stat, do_symlink, do_symlinkat, | ||||
|     do_sync, do_truncate, do_unlink, do_write, do_writev, iovec_t, File, FileDesc, FileRef, | ||||
|     HostStdioFds, Stat, | ||||
| }; | ||||
| use crate::misc::{resource_t, rlimit_t, sysinfo_t, utsname_t}; | ||||
| use crate::net::{ | ||||
| @ -165,7 +166,7 @@ macro_rules! process_syscall_table_with_callback { | ||||
|             (Creat = 85) => handle_unsupported(), | ||||
|             (Link = 86) => do_link(oldpath: *const i8, newpath: *const i8), | ||||
|             (Unlink = 87) => do_unlink(path: *const i8), | ||||
|             (Symlink = 88) => handle_unsupported(), | ||||
|             (Symlink = 88) => do_symlink(target: *const i8, link_path: *const i8), | ||||
|             (Readlink = 89) => do_readlink(path: *const i8, buf: *mut u8, size: usize), | ||||
|             (Chmod = 90) => do_chmod(path: *const i8, mode: u16), | ||||
|             (Fchmod = 91) => do_fchmod(fd: FileDesc, mode: u16), | ||||
| @ -343,7 +344,7 @@ macro_rules! process_syscall_table_with_callback { | ||||
|             (Unlinkat = 263) => handle_unsupported(), | ||||
|             (Renameat = 264) => handle_unsupported(), | ||||
|             (Linkat = 265) => handle_unsupported(), | ||||
|             (Symlinkat = 266) => handle_unsupported(), | ||||
|             (Symlinkat = 266) => do_symlinkat(target: *const i8, new_dirfd: i32, link_path: *const i8), | ||||
|             (Readlinkat = 267) => handle_unsupported(), | ||||
|             (Fchmodat = 268) => handle_unsupported(), | ||||
|             (Faccessat = 269) => do_faccessat(dirfd: i32, path: *const i8, mode: u32, flags: u32), | ||||
|  | ||||
| @ -7,6 +7,7 @@ | ||||
| #include <string.h> | ||||
| #include <stdio.h> | ||||
| #include <stdlib.h> | ||||
| #include <errno.h> | ||||
| #include "test.h" | ||||
| 
 | ||||
| // ============================================================================
 | ||||
| @ -84,7 +85,6 @@ static int __test_realpath(const char *file_path) { | ||||
|     char dirc[128] = { 0 }; | ||||
|     char basec[128] = { 0 }; | ||||
|     char *dir_name, *file_name, *res; | ||||
|     int ret; | ||||
| 
 | ||||
|     if (snprintf(dirc, sizeof(dirc), "%s", file_path) < 0 || | ||||
|             snprintf(basec, sizeof(dirc), "%s", file_path) < 0) { | ||||
| @ -92,8 +92,7 @@ static int __test_realpath(const char *file_path) { | ||||
|     } | ||||
|     dir_name = dirname(dirc); | ||||
|     file_name = basename(basec); | ||||
|     ret = chdir(dir_name); | ||||
|     if (ret < 0) { | ||||
|     if (chdir(dir_name) < 0) { | ||||
|         THROW_ERROR("failed to chdir to %s", dir_name); | ||||
|     } | ||||
|     res = realpath(file_name, buf); | ||||
| @ -106,6 +105,9 @@ static int __test_realpath(const char *file_path) { | ||||
|     if (strncmp(buf, file_path, strlen(buf)) != 0) { | ||||
|         THROW_ERROR("check the realpath for '%s' failed", file_name); | ||||
|     } | ||||
|     if (chdir("/") < 0) { | ||||
|         THROW_ERROR("failed to chdir to '/'"); | ||||
|     } | ||||
| 
 | ||||
|     return 0; | ||||
| } | ||||
| @ -136,7 +138,6 @@ static int test_realpath() { | ||||
|     return test_readlink_framework(__test_realpath); | ||||
| } | ||||
| 
 | ||||
| 
 | ||||
| static int test_readlink_from_proc_self_exe() { | ||||
|     char exe_buf[128] = { 0 }; | ||||
|     char absolute_path[128] = { 0 }; | ||||
| @ -160,6 +161,193 @@ static int test_readlink_from_proc_self_exe() { | ||||
|     return 0; | ||||
| } | ||||
| 
 | ||||
| // ============================================================================
 | ||||
| // Test cases for symlink
 | ||||
| // ============================================================================
 | ||||
| 
 | ||||
| static int __test_symlink(const char *target, const char *link_path) { | ||||
|     char dir_buf[128] = { 0 }; | ||||
|     char *dir_name; | ||||
|     char target_path[256] = { 0 }; | ||||
| 
 | ||||
|     if (target[0] == '/') { | ||||
|         snprintf(target_path, sizeof(target_path), "%s", target); | ||||
|     } else { | ||||
|         // If `target` is not an absolute path,
 | ||||
|         // it must be a path relative to the directory of the `link_path`
 | ||||
|         snprintf(dir_buf, sizeof(dir_buf), "%s", link_path); | ||||
|         dir_name = dirname(dir_buf); | ||||
|         snprintf(target_path, sizeof(target_path), "%s/%s", dir_name, target); | ||||
|     } | ||||
|     if (create_file(target_path) < 0) { | ||||
|         THROW_ERROR("failed to create target file"); | ||||
|     } | ||||
| 
 | ||||
|     int fd = open(target_path, O_WRONLY); | ||||
|     if (fd < 0) { | ||||
|         THROW_ERROR("failed to open target to write"); | ||||
|     } | ||||
|     char *write_str = "Hello World\n"; | ||||
|     if (write(fd, write_str, strlen(write_str)) <= 0) { | ||||
|         THROW_ERROR("failed to write"); | ||||
|     } | ||||
|     close(fd); | ||||
| 
 | ||||
|     if (symlink(target, link_path) < 0) { | ||||
|         THROW_ERROR("failed to create symlink"); | ||||
|     } | ||||
| 
 | ||||
|     fd = open(link_path, O_RDONLY | O_NOFOLLOW); | ||||
|     if (fd >= 0 || errno != ELOOP) { | ||||
|         THROW_ERROR("failed to check open file with O_NOFOLLOW flags"); | ||||
|     } | ||||
| 
 | ||||
|     fd = open(link_path, O_RDONLY); | ||||
|     if (fd < 0) { | ||||
|         THROW_ERROR("failed to open link file to read"); | ||||
|     } | ||||
|     char read_buf[128] = { 0 }; | ||||
|     if (read(fd, read_buf, sizeof(read_buf)) != strlen(write_str)) { | ||||
|         THROW_ERROR("failed to read"); | ||||
|     } | ||||
|     if (strcmp(write_str, read_buf) != 0) { | ||||
|         THROW_ERROR("the message read from the file is not as it was written"); | ||||
|     } | ||||
|     close(fd); | ||||
| 
 | ||||
|     char readlink_buf[256] = { 0 }; | ||||
|     if (readlink(link_path, readlink_buf, sizeof(readlink_buf)) < 0) { | ||||
|         THROW_ERROR("readlink failed"); | ||||
|     } | ||||
|     if (strcmp(target, readlink_buf) != 0) { | ||||
|         THROW_ERROR("check readlink result failed"); | ||||
|     } | ||||
| 
 | ||||
|     if (remove_file(target_path) < 0) { | ||||
|         THROW_ERROR("failed to delete target file"); | ||||
|     } | ||||
| 
 | ||||
|     return 0; | ||||
| } | ||||
| 
 | ||||
| static int __test_create_file_from_symlink(const char *target, const char *link_path) { | ||||
|     char dir_buf[128] = { 0 }; | ||||
|     char *dir_name; | ||||
|     char target_path[256] = { 0 }; | ||||
| 
 | ||||
|     if (target[0] == '/') { | ||||
|         snprintf(target_path, sizeof(target_path), "%s", target); | ||||
|     } else { | ||||
|         // If `target` is not an absolute path,
 | ||||
|         // it must be a path relative to the directory of the `link_path`
 | ||||
|         snprintf(dir_buf, sizeof(dir_buf), "%s", link_path); | ||||
|         dir_name = dirname(dir_buf); | ||||
|         snprintf(target_path, sizeof(target_path), "%s/%s", dir_name, target); | ||||
|     } | ||||
| 
 | ||||
|     if (symlink(target, link_path) < 0) { | ||||
|         THROW_ERROR("failed to create symlink"); | ||||
|     } | ||||
| 
 | ||||
|     int fd = open(link_path, O_RDONLY, 00666); | ||||
|     if (fd >= 0 || errno != ENOENT) { | ||||
|         THROW_ERROR("failed to check open a dangling symbolic link"); | ||||
|     } | ||||
| 
 | ||||
|     if (create_file(link_path) < 0) { | ||||
|         THROW_ERROR("failed to create link file"); | ||||
|     } | ||||
|     struct stat stat_buf; | ||||
|     if (stat(target_path, &stat_buf) < 0) { | ||||
|         THROW_ERROR("failed to stat the target file"); | ||||
|     } | ||||
| 
 | ||||
|     if (remove_file(target_path) < 0) { | ||||
|         THROW_ERROR("failed to delete target file"); | ||||
|     } | ||||
| 
 | ||||
|     return 0; | ||||
| } | ||||
| 
 | ||||
| typedef int(*test_symlink_func_t)(const char *, const char *); | ||||
| 
 | ||||
| static int test_symlink_framework(test_symlink_func_t fn, const char *target, | ||||
|                                   const char *link) { | ||||
|     if (fn(target, link) < 0) { | ||||
|         return -1; | ||||
|     } | ||||
|     if (remove_file(link) < 0) { | ||||
|         return -1; | ||||
|     } | ||||
| 
 | ||||
|     return 0; | ||||
| } | ||||
| 
 | ||||
| static int test_symlink_to_absolute_target() { | ||||
|     char *target = "/root/test_symlink.file"; | ||||
|     char *link = "/root/test_symlink.link"; | ||||
|     return test_symlink_framework(__test_symlink, target, link); | ||||
| } | ||||
| 
 | ||||
| static int test_symlink_to_relative_target() { | ||||
|     char *target = "./test_symlink.file"; | ||||
|     char *link = "/root/test_symlink.link"; | ||||
|     if (test_symlink_framework(__test_symlink, target, link) < 0) { | ||||
|         return -1; | ||||
|     } | ||||
|     target = "../root/test_symlink.file"; | ||||
|     if (test_symlink_framework(__test_symlink, target, link) < 0) { | ||||
|         return -1; | ||||
|     } | ||||
|     return 0; | ||||
| } | ||||
| 
 | ||||
| static int test_symlink_from_ramfs() { | ||||
|     char *target = "/root/test_symlink.file"; | ||||
|     char *link = "/tmp/test_symlink.link"; | ||||
|     return test_symlink_framework(__test_symlink, target, link); | ||||
| } | ||||
| 
 | ||||
| static int test_symlink_to_ramfs() { | ||||
|     char *target = "/tmp/test_symlink.file"; | ||||
|     char *link = "/root/test_symlink.link"; | ||||
|     return test_symlink_framework(__test_symlink, target, link); | ||||
| } | ||||
| 
 | ||||
| static int test_symlink_with_empty_target_or_link_path() { | ||||
|     char *target = "/root/test_symlink.file"; | ||||
|     char *link_path = "/root/test_symlink.link"; | ||||
| 
 | ||||
|     int ret = symlink("", link_path); | ||||
|     if (ret >= 0 || errno != ENOENT) { | ||||
|         THROW_ERROR("failed to check symlink with empty target"); | ||||
|     } | ||||
|     ret = symlink(target, ""); | ||||
|     if (ret >= 0 || errno != ENOENT) { | ||||
|         THROW_ERROR("failed to check symlink with empty linkpath"); | ||||
|     } | ||||
|     return 0; | ||||
| } | ||||
| 
 | ||||
| static int test_create_file_from_symlink_to_absolute_target() { | ||||
|     char *target = "/root/test_symlink.file"; | ||||
|     char *link = "/root/test_symlink.link"; | ||||
|     return test_symlink_framework(__test_create_file_from_symlink, target, link); | ||||
| } | ||||
| 
 | ||||
| static int test_create_file_from_symlink_to_relative_target() { | ||||
|     char *target = "test_symlink.file"; | ||||
|     char *link = "/root/test_symlink.link"; | ||||
|     if (test_symlink_framework(__test_create_file_from_symlink, target, link) < 0) { | ||||
|         return -1; | ||||
|     } | ||||
|     target = "../root/test_symlink.file"; | ||||
|     if (test_symlink_framework(__test_create_file_from_symlink, target, link) < 0) { | ||||
|         return -1; | ||||
|     } | ||||
|     return 0; | ||||
| } | ||||
| 
 | ||||
| // ============================================================================
 | ||||
| // Test suite main
 | ||||
| // ============================================================================
 | ||||
| @ -168,6 +356,13 @@ static test_case_t test_cases[] = { | ||||
|     TEST_CASE(test_readlink_from_proc_self_fd), | ||||
|     TEST_CASE(test_realpath), | ||||
|     TEST_CASE(test_readlink_from_proc_self_exe), | ||||
|     TEST_CASE(test_symlink_to_absolute_target), | ||||
|     TEST_CASE(test_symlink_to_relative_target), | ||||
|     TEST_CASE(test_symlink_from_ramfs), | ||||
|     TEST_CASE(test_symlink_to_ramfs), | ||||
|     TEST_CASE(test_symlink_with_empty_target_or_link_path), | ||||
|     TEST_CASE(test_create_file_from_symlink_to_absolute_target), | ||||
|     TEST_CASE(test_create_file_from_symlink_to_relative_target), | ||||
| }; | ||||
| 
 | ||||
| int main(int argc, const char *argv[]) { | ||||
|  | ||||
		Loading…
	
		Reference in New Issue
	
	Block a user