Fix issue about pathname and redefine the Filetype.

Signed-off-by: Zhenchen Wang <m202372036@hust.edu.cn>
This commit is contained in:
Zhenchen Wang
2024-06-27 10:14:19 +08:00
committed by Tate, Hongliang Tian
parent d8389da797
commit 12b355b701
10 changed files with 103 additions and 44 deletions

View File

@ -154,8 +154,6 @@ provided by Linux on x86-64 architecture.
| 131 | sigaltstack | ✅ | | 131 | sigaltstack | ✅ |
| 132 | utime | ✅ | | 132 | utime | ✅ |
| 133 | mknod | ✅ | | 133 | mknod | ✅ |
| 132 | utime | ❌ |
| 133 | mknod | ✅ |
| 134 | uselib | ❌ | | 134 | uselib | ❌ |
| 135 | personality | ❌ | | 135 | personality | ❌ |
| 136 | ustat | ❌ | | 136 | ustat | ❌ |

View File

@ -48,12 +48,18 @@ pub fn init() -> Result<()> {
Ok(()) Ok(())
} }
// TODO: Implement a more scalable solution for ID-to-device mapping.
// Instead of hardcoding every device numbers in this function,
// a registration mechanism should be used to allow each driver to
// allocate device IDs either statically or dynamically.
pub fn get_device(dev: usize) -> Result<Arc<dyn Device>> { pub fn get_device(dev: usize) -> Result<Arc<dyn Device>> {
if dev == 0 { if dev == 0 {
return_errno_with_message!(Errno::EPERM, "whiteout device") return_errno_with_message!(Errno::EPERM, "whiteout device")
} }
let major = ((dev >> 32) & 0xffff_f000 | (dev >> 8) & 0x0000_0fff) as u32;
let minor = ((dev >> 12) & 0xffff_ff00 | dev & 0x0000_00ff) as u32; let devid = DeviceId::from(dev as u64);
let major = devid.major();
let minor = devid.minor();
match (major, minor) { match (major, minor) {
(1, 3) => Ok(Arc::new(null::Null)), (1, 3) => Ok(Arc::new(null::Null)),

View File

@ -81,6 +81,12 @@ impl From<DeviceId> for u64 {
} }
} }
impl From<u64> for DeviceId {
fn from(raw: u64) -> Self {
Self(raw)
}
}
/// Add a device node to FS for the device. /// Add a device node to FS for the device.
/// ///
/// If the parent path is not existing, `mkdir -p` the parent path. /// If the parent path is not existing, `mkdir -p` the parent path.

View File

@ -318,6 +318,31 @@ impl FsResolver {
Ok((dir_dentry, base_name)) Ok((dir_dentry, base_name))
} }
/// The method used to create a new file using pathname.
///
/// For example, mkdir, mknod, link, and symlink all need to create
/// new file and all need to perform unique processing on the last
/// component of the path name. It is used to provide a unified
/// method for pathname lookup and error handling.
///
/// is_dir is used to determine whether a directory needs to be created.
pub fn lookup_dir_and_new_basename(
&self,
path: &FsPath,
is_dir: bool,
) -> Result<(Arc<Dentry>, String)> {
let (dir_dentry, filename) = self.lookup_dir_and_base_name_inner(path, false)?;
if dir_dentry.lookup(filename.trim_end_matches('/')).is_ok() {
return_errno_with_message!(Errno::EEXIST, "file exists");
}
if !is_dir && filename.ends_with('/') {
return_errno_with_message!(Errno::ENOENT, "No such file or directory");
}
Ok((dir_dentry, filename))
}
} }
pub const AT_FDCWD: FileDesc = -100; pub const AT_FDCWD: FileDesc = -100;

View File

@ -37,8 +37,8 @@ pub fn sys_linkat(
return_errno_with_message!(Errno::ENOENT, "oldpath is empty"); return_errno_with_message!(Errno::ENOENT, "oldpath is empty");
} }
let new_path = new_path.to_string_lossy(); let new_path = new_path.to_string_lossy();
if new_path.ends_with('/') || new_path.is_empty() { if new_path.is_empty() {
return_errno_with_message!(Errno::ENOENT, "newpath is dir or is empty"); return_errno_with_message!(Errno::ENOENT, "newpath is empty");
} }
let old_fs_path = FsPath::new(old_dirfd, old_path.as_ref())?; let old_fs_path = FsPath::new(old_dirfd, old_path.as_ref())?;
@ -49,7 +49,7 @@ pub fn sys_linkat(
} else { } else {
fs.lookup_no_follow(&old_fs_path)? fs.lookup_no_follow(&old_fs_path)?
}; };
let (new_dir_dentry, new_name) = fs.lookup_dir_and_base_name(&new_fs_path)?; let (new_dir_dentry, new_name) = fs.lookup_dir_and_new_basename(&new_fs_path, false)?;
(old_dentry, new_dir_dentry, new_name) (old_dentry, new_dir_dentry, new_name)
}; };

View File

@ -23,7 +23,10 @@ pub fn sys_mkdirat(dirfd: FileDesc, path_addr: Vaddr, mode: u16) -> Result<Sysca
return_errno_with_message!(Errno::ENOENT, "path is empty"); return_errno_with_message!(Errno::ENOENT, "path is empty");
} }
let fs_path = FsPath::new(dirfd, path.as_ref())?; let fs_path = FsPath::new(dirfd, path.as_ref())?;
current.fs().read().lookup_dir_and_base_name(&fs_path)? current
.fs()
.read()
.lookup_dir_and_new_basename(&fs_path, true)?
}; };
let inode_mode = { let inode_mode = {

View File

@ -9,7 +9,7 @@ use crate::{
utils::{InodeMode, InodeType}, utils::{InodeMode, InodeType},
}, },
prelude::*, prelude::*,
syscall::{constants::MAX_FILENAME_LEN, stat::FileTypeFlags}, syscall::{constants::MAX_FILENAME_LEN, stat::FileType},
util::read_cstring_from_user, util::read_cstring_from_user,
}; };
@ -25,7 +25,7 @@ pub fn sys_mknodat(
let mask_mode = mode & !current.umask().read().get(); let mask_mode = mode & !current.umask().read().get();
InodeMode::from_bits_truncate(mask_mode) InodeMode::from_bits_truncate(mask_mode)
}; };
let file_type = FileTypeFlags::from_bits_truncate(mode); let file_type = FileType::from_mode(mode);
debug!( debug!(
"dirfd = {}, path = {:?}, inode_mode = {:?}, file_type = {:?}, dev = {}", "dirfd = {}, path = {:?}, inode_mode = {:?}, file_type = {:?}, dev = {}",
dirfd, path, inode_mode, file_type, dev dirfd, path, inode_mode, file_type, dev
@ -37,21 +37,24 @@ pub fn sys_mknodat(
return_errno_with_message!(Errno::ENOENT, "path is empty"); return_errno_with_message!(Errno::ENOENT, "path is empty");
} }
let fs_path = FsPath::new(dirfd, path.as_ref())?; let fs_path = FsPath::new(dirfd, path.as_ref())?;
current.fs().read().lookup_dir_and_base_name(&fs_path)? current
.fs()
.read()
.lookup_dir_and_new_basename(&fs_path, false)?
}; };
if file_type.contains(FileTypeFlags::S_IFREG) || file_type.is_empty() { match file_type {
let _ = dir_dentry.new_fs_child(name.trim_end_matches('/'), InodeType::File, inode_mode)?; FileType::RegularFile => {
} else if file_type.contains(FileTypeFlags::S_IFCHR) let _ = dir_dentry.new_fs_child(&name, InodeType::File, inode_mode)?;
|| file_type.contains(FileTypeFlags::S_IFBLK) }
{ FileType::CharacterDevice | FileType::BlockDevice => {
let _ = dir_dentry.mknod(name.trim_end_matches('/'), inode_mode, get_device(dev)?)?; let device_inode = get_device(dev)?;
} else if file_type.contains(FileTypeFlags::S_IFIFO) let _ = dir_dentry.mknod(&name, inode_mode, device_inode)?;
|| file_type.contains(FileTypeFlags::S_IFSOCK) }
{ FileType::Fifo | FileType::Socket => {
return_errno_with_message!(Errno::EINVAL, "unsupported file type flag"); return_errno_with_message!(Errno::EINVAL, "unsupported file types")
} else { }
return_errno_with_message!(Errno::EPERM, "unimplemented types"); _ => return_errno_with_message!(Errno::EPERM, "unimplemented file types"),
} }
Ok(SyscallReturn::Return(0)) Ok(SyscallReturn::Return(0))

View File

@ -165,14 +165,14 @@ fn get_fs(fs_type: CString, devname: CString) -> Result<Arc<dyn FileSystem>> {
bitflags! { bitflags! {
struct MountFlags: u32 { struct MountFlags: u32 {
const MS_RDONLY = 1 << 0; // Mount read-only */ const MS_RDONLY = 1 << 0; // Mount read-only.
const MS_NOSUID = 1 << 1; // Ignore suid and sgid bits */ const MS_NOSUID = 1 << 1; // Ignore suid and sgid bits.
const MS_NODEV = 1 << 2; // Disallow access to device special files */ const MS_NODEV = 1 << 2; // Disallow access to device special files.
const MS_NOEXEC = 1 << 3; // Disallow program execution */ const MS_NOEXEC = 1 << 3; // Disallow program execution.
const MS_SYNCHRONOUS = 1 << 4; // Writes are synced at once const MS_SYNCHRONOUS = 1 << 4; // Writes are synced at once.
const MS_REMOUNT = 1 << 5; // Alter flags of a mounted FS. const MS_REMOUNT = 1 << 5; // Alter flags of a mounted FS.
const MS_MANDLOCK = 1 << 6; // Allow mandatory locks on an FS. const MS_MANDLOCK = 1 << 6; // Allow mandatory locks on an FS.
const MS_DIRSYNC = 1 << 7; // Directory modifications are synchronous const MS_DIRSYNC = 1 << 7; // Directory modifications are synchronous.
const MS_NOSYMFOLLOW = 1 << 8; // Do not follow symlinks. const MS_NOSYMFOLLOW = 1 << 8; // Do not follow symlinks.
const MS_NOATIME = 1 << 10; // Do not update access times. const MS_NOATIME = 1 << 10; // Do not update access times.
const MS_NODIRATIME = 1 << 11; // Do not update directory access times. const MS_NODIRATIME = 1 << 11; // Do not update directory access times.

View File

@ -77,17 +77,35 @@ pub fn sys_fstatat(
Ok(SyscallReturn::Return(0)) Ok(SyscallReturn::Return(0))
} }
bitflags! { /// File type mask.
/// File type. const S_IFMT: u16 = 0o170000;
pub struct FileTypeFlags: u16 {
const S_IFMT = 0o170000; // File type mask. /// Enum representing different file types.
const S_IFSOCK = 0o140000; // Socket. #[derive(Debug, PartialEq)]
const S_IFCHR = 0o020000; // Character device. pub enum FileType {
const S_IFBLK = 0o060000; // Block device. Socket,
const S_IFDIR = 0o040000; // Directory. CharacterDevice,
const S_IFIFO = 0o010000; // FIFO (named pipe). BlockDevice,
const S_IFREG = 0o100000; // Regular file. Directory,
const S_IFLNK = 0o120000; // Symbolic link. Fifo,
RegularFile,
Symlink,
Unknown,
}
impl FileType {
/// Extract the file type from the mode.
pub fn from_mode(mode: u16) -> FileType {
match mode & S_IFMT {
0o140000 => FileType::Socket, // Socket.
0o020000 => FileType::CharacterDevice, // Character device.
0o060000 => FileType::BlockDevice, // Block device.
0o040000 => FileType::Directory, // Directory.
0o010000 => FileType::Fifo, // FIFO (named pipe).
0 | 0o100000 => FileType::RegularFile, // Regular file.
0o120000 => FileType::Symlink, // Symbolic link.
_ => FileType::Unknown, // Unkonwn file type.
}
} }
} }

View File

@ -34,11 +34,11 @@ pub fn sys_symlinkat(
if linkpath.is_empty() { if linkpath.is_empty() {
return_errno_with_message!(Errno::ENOENT, "linkpath is empty"); return_errno_with_message!(Errno::ENOENT, "linkpath is empty");
} }
if linkpath.ends_with('/') {
return_errno_with_message!(Errno::EISDIR, "linkpath is dir");
}
let fs_path = FsPath::new(dirfd, linkpath.as_ref())?; let fs_path = FsPath::new(dirfd, linkpath.as_ref())?;
current.fs().read().lookup_dir_and_base_name(&fs_path)? current
.fs()
.read()
.lookup_dir_and_new_basename(&fs_path, false)?
}; };
let new_dentry = dir_dentry.new_fs_child( let new_dentry = dir_dentry.new_fs_child(