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 | ✅ |
| 132 | utime | ✅ |
| 133 | mknod | ✅ |
| 132 | utime | ❌ |
| 133 | mknod | ✅ |
| 134 | uselib | ❌ |
| 135 | personality | ❌ |
| 136 | ustat | ❌ |

View File

@ -48,12 +48,18 @@ pub fn init() -> Result<()> {
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>> {
if dev == 0 {
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) {
(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.
///
/// 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))
}
/// 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;

View File

@ -37,8 +37,8 @@ pub fn sys_linkat(
return_errno_with_message!(Errno::ENOENT, "oldpath is empty");
}
let new_path = new_path.to_string_lossy();
if new_path.ends_with('/') || new_path.is_empty() {
return_errno_with_message!(Errno::ENOENT, "newpath is dir or is empty");
if new_path.is_empty() {
return_errno_with_message!(Errno::ENOENT, "newpath is empty");
}
let old_fs_path = FsPath::new(old_dirfd, old_path.as_ref())?;
@ -49,7 +49,7 @@ pub fn sys_linkat(
} else {
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)
};

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");
}
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 = {

View File

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

View File

@ -165,14 +165,14 @@ fn get_fs(fs_type: CString, devname: CString) -> Result<Arc<dyn FileSystem>> {
bitflags! {
struct MountFlags: u32 {
const MS_RDONLY = 1 << 0; // Mount read-only */
const MS_NOSUID = 1 << 1; // Ignore suid and sgid bits */
const MS_NODEV = 1 << 2; // Disallow access to device special files */
const MS_NOEXEC = 1 << 3; // Disallow program execution */
const MS_SYNCHRONOUS = 1 << 4; // Writes are synced at once
const MS_RDONLY = 1 << 0; // Mount read-only.
const MS_NOSUID = 1 << 1; // Ignore suid and sgid bits.
const MS_NODEV = 1 << 2; // Disallow access to device special files.
const MS_NOEXEC = 1 << 3; // Disallow program execution.
const MS_SYNCHRONOUS = 1 << 4; // Writes are synced at once.
const MS_REMOUNT = 1 << 5; // Alter flags of a mounted 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_NOATIME = 1 << 10; // Do not update 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))
}
bitflags! {
/// File type.
pub struct FileTypeFlags: u16 {
const S_IFMT = 0o170000; // File type mask.
const S_IFSOCK = 0o140000; // Socket.
const S_IFCHR = 0o020000; // Character device.
const S_IFBLK = 0o060000; // Block device.
const S_IFDIR = 0o040000; // Directory.
const S_IFIFO = 0o010000; // FIFO (named pipe).
const S_IFREG = 0o100000; // Regular file.
const S_IFLNK = 0o120000; // Symbolic link.
/// File type mask.
const S_IFMT: u16 = 0o170000;
/// Enum representing different file types.
#[derive(Debug, PartialEq)]
pub enum FileType {
Socket,
CharacterDevice,
BlockDevice,
Directory,
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() {
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())?;
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(