diff --git a/docs/src/kernel/linux-compatibility.md b/docs/src/kernel/linux-compatibility.md index f21806863..cd8fe1615 100644 --- a/docs/src/kernel/linux-compatibility.md +++ b/docs/src/kernel/linux-compatibility.md @@ -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 | ❌ | diff --git a/kernel/aster-nix/src/device/mod.rs b/kernel/aster-nix/src/device/mod.rs index 36c132cfb..131e9b351 100644 --- a/kernel/aster-nix/src/device/mod.rs +++ b/kernel/aster-nix/src/device/mod.rs @@ -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> { 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)), diff --git a/kernel/aster-nix/src/fs/device.rs b/kernel/aster-nix/src/fs/device.rs index a8f21cfa8..229fde6c2 100644 --- a/kernel/aster-nix/src/fs/device.rs +++ b/kernel/aster-nix/src/fs/device.rs @@ -81,6 +81,12 @@ impl From for u64 { } } +impl From 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. diff --git a/kernel/aster-nix/src/fs/fs_resolver.rs b/kernel/aster-nix/src/fs/fs_resolver.rs index 88db34265..3dd8aa9c7 100644 --- a/kernel/aster-nix/src/fs/fs_resolver.rs +++ b/kernel/aster-nix/src/fs/fs_resolver.rs @@ -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, 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; diff --git a/kernel/aster-nix/src/syscall/link.rs b/kernel/aster-nix/src/syscall/link.rs index 1bb872477..53bcc2aee 100644 --- a/kernel/aster-nix/src/syscall/link.rs +++ b/kernel/aster-nix/src/syscall/link.rs @@ -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) }; diff --git a/kernel/aster-nix/src/syscall/mkdir.rs b/kernel/aster-nix/src/syscall/mkdir.rs index ef0a1b15d..0ce77ba6d 100644 --- a/kernel/aster-nix/src/syscall/mkdir.rs +++ b/kernel/aster-nix/src/syscall/mkdir.rs @@ -23,7 +23,10 @@ pub fn sys_mkdirat(dirfd: FileDesc, path_addr: Vaddr, mode: u16) -> Result { + 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)) diff --git a/kernel/aster-nix/src/syscall/mount.rs b/kernel/aster-nix/src/syscall/mount.rs index 2e9818a94..2b9dad552 100644 --- a/kernel/aster-nix/src/syscall/mount.rs +++ b/kernel/aster-nix/src/syscall/mount.rs @@ -165,14 +165,14 @@ fn get_fs(fs_type: CString, devname: CString) -> Result> { 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. diff --git a/kernel/aster-nix/src/syscall/stat.rs b/kernel/aster-nix/src/syscall/stat.rs index ff33ea2f9..a7b9c1b50 100644 --- a/kernel/aster-nix/src/syscall/stat.rs +++ b/kernel/aster-nix/src/syscall/stat.rs @@ -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. + } } } diff --git a/kernel/aster-nix/src/syscall/symlink.rs b/kernel/aster-nix/src/syscall/symlink.rs index c9b31e601..b483ef1d5 100644 --- a/kernel/aster-nix/src/syscall/symlink.rs +++ b/kernel/aster-nix/src/syscall/symlink.rs @@ -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(