diff --git a/kernel/src/arch/x86_64/syscall/nr.rs b/kernel/src/arch/x86_64/syscall/nr.rs index c37dc36cd..19969c839 100644 --- a/kernel/src/arch/x86_64/syscall/nr.rs +++ b/kernel/src/arch/x86_64/syscall/nr.rs @@ -355,3 +355,364 @@ pub const SYS_WAIT4: usize = 61; pub const SYS_WAITID: usize = 247; pub const SYS_WRITE: usize = 1; pub const SYS_WRITEV: usize = 20; + +pub fn syscall_number_to_str(syscall_number: usize) -> &'static str { + match syscall_number { + 0 => "SYS_READ", + 1 => "SYS_WRITE", + 2 => "SYS_OPEN", + 3 => "SYS_CLOSE", + 4 => "SYS_STAT", + 5 => "SYS_FSTAT", + 6 => "SYS_LSTAT", + 7 => "SYS_POLL", + 8 => "SYS_LSEEK", + 9 => "SYS_MMAP", + 10 => "SYS_MPROTECT", + 11 => "SYS_MUNMAP", + 12 => "SYS_BRK", + 13 => "SYS_RT_SIGACTION", + 14 => "SYS_RT_SIGPROCMASK", + 15 => "SYS_RT_SIGRETURN", + 16 => "SYS_IOCTL", + 17 => "SYS_PREAD64", + 18 => "SYS_PWRITE64", + 19 => "SYS_READV", + 20 => "SYS_WRITEV", + 21 => "SYS_ACCESS", + 22 => "SYS_PIPE", + 23 => "SYS_SELECT", + 24 => "SYS_SCHED_YIELD", + 25 => "SYS_MREMAP", + 26 => "SYS_MSYNC", + 27 => "SYS_MINCORE", + 28 => "SYS_MADVISE", + 29 => "SYS_SHMGET", + 30 => "SYS_SHMAT", + 31 => "SYS_SHMCTL", + 32 => "SYS_DUP", + 33 => "SYS_DUP2", + 34 => "SYS_PAUSE", + 35 => "SYS_NANOSLEEP", + 36 => "SYS_GETITIMER", + 37 => "SYS_ALARM", + 38 => "SYS_SETITIMER", + 39 => "SYS_GETPID", + 40 => "SYS_SENDFILE", + 41 => "SYS_SOCKET", + 42 => "SYS_CONNECT", + 43 => "SYS_ACCEPT", + 44 => "SYS_SENDTO", + 45 => "SYS_RECVFROM", + 46 => "SYS_SENDMSG", + 47 => "SYS_RECVMSG", + 48 => "SYS_SHUTDOWN", + 49 => "SYS_BIND", + 50 => "SYS_LISTEN", + 51 => "SYS_GETSOCKNAME", + 52 => "SYS_GETPEERNAME", + 53 => "SYS_SOCKETPAIR", + 54 => "SYS_SETSOCKOPT", + 55 => "SYS_GETSOCKOPT", + 56 => "SYS_CLONE", + 57 => "SYS_FORK", + 58 => "SYS_VFORK", + 59 => "SYS_EXECVE", + 60 => "SYS_EXIT", + 61 => "SYS_WAIT4", + 62 => "SYS_KILL", + 63 => "SYS_UNAME", + 64 => "SYS_SEMGET", + 65 => "SYS_SEMOP", + 66 => "SYS_SEMCTL", + 67 => "SYS_SHMDT", + 68 => "SYS_MSGGET", + 69 => "SYS_MSGSND", + 70 => "SYS_MSGRCV", + 71 => "SYS_MSGCTL", + 72 => "SYS_FCNTL", + 73 => "SYS_FLOCK", + 74 => "SYS_FSYNC", + 75 => "SYS_FDATASYNC", + 76 => "SYS_TRUNCATE", + 77 => "SYS_FTRUNCATE", + 78 => "SYS_GETDENTS", + 79 => "SYS_GETCWD", + 80 => "SYS_CHDIR", + 81 => "SYS_FCHDIR", + 82 => "SYS_RENAME", + 83 => "SYS_MKDIR", + 84 => "SYS_RMDIR", + 85 => "SYS_CREAT", + 86 => "SYS_LINK", + 87 => "SYS_UNLINK", + 88 => "SYS_SYMLINK", + 89 => "SYS_READLINK", + 90 => "SYS_CHMOD", + 91 => "SYS_FCHMOD", + 92 => "SYS_CHOWN", + 93 => "SYS_FCHOWN", + 94 => "SYS_LCHOWN", + 95 => "SYS_UMASK", + 96 => "SYS_GETTIMEOFDAY", + 97 => "SYS_GETRLIMIT", + 98 => "SYS_GETRUSAGE", + 99 => "SYS_SYSINFO", + 100 => "SYS_TIMES", + 101 => "SYS_PTRACE", + 102 => "SYS_GETUID", + 103 => "SYS_SYSLOG", + 104 => "SYS_GETGID", + 105 => "SYS_SETUID", + 106 => "SYS_SETGID", + 107 => "SYS_GETEUID", + 108 => "SYS_GETEGID", + 109 => "SYS_SETPGID", + 110 => "SYS_GETPPID", + 111 => "SYS_GETPGRP", + 112 => "SYS_SETSID", + 113 => "SYS_SETREUID", + 114 => "SYS_SETREGID", + 115 => "SYS_GETGROUPS", + 116 => "SYS_SETGROUPS", + 117 => "SYS_SETRESUID", + 118 => "SYS_GETRESUID", + 119 => "SYS_SETRESGID", + 120 => "SYS_GETRESGID", + 121 => "SYS_GETPGID", + 122 => "SYS_SETFSUID", + 123 => "SYS_SETFSGID", + 124 => "SYS_GETSID", + 125 => "SYS_CAPGET", + 126 => "SYS_CAPSET", + 127 => "SYS_RT_SIGPENDING", + 128 => "SYS_RT_SIGTIMEDWAIT", + 129 => "SYS_RT_SIGQUEUEINFO", + 130 => "SYS_RT_SIGSUSPEND", + 131 => "SYS_SIGALTSTACK", + 132 => "SYS_UTIME", + 133 => "SYS_MKNOD", + 134 => "SYS_USELIB", + 135 => "SYS_PERSONALITY", + 136 => "SYS_USTAT", + 137 => "SYS_STATFS", + 138 => "SYS_FSTATFS", + 139 => "SYS_SYSFS", + 140 => "SYS_GETPRIORITY", + 141 => "SYS_SETPRIORITY", + 142 => "SYS_SCHED_SETPARAM", + 143 => "SYS_SCHED_GETPARAM", + 144 => "SYS_SCHED_SETSCHEDULER", + 145 => "SYS_SCHED_GETSCHEDULER", + 146 => "SYS_SCHED_GET_PRIORITY_MAX", + 147 => "SYS_SCHED_GET_PRIORITY_MIN", + 148 => "SYS_SCHED_RR_GET_INTERVAL", + 149 => "SYS_MLOCK", + 150 => "SYS_MUNLOCK", + 151 => "SYS_MLOCKALL", + 152 => "SYS_MUNLOCKALL", + 153 => "SYS_VHANGUP", + 154 => "SYS_MODIFY_LDT", + 155 => "SYS_PIVOT_ROOT", + 156 => "SYS__SYSCTL", + 157 => "SYS_PRCTL", + 158 => "SYS_ARCH_PRCTL", + 159 => "SYS_ADJTIMEX", + 160 => "SYS_SETRLIMIT", + 161 => "SYS_CHROOT", + 162 => "SYS_SYNC", + 163 => "SYS_ACCT", + 164 => "SYS_SETTIMEOFDAY", + 165 => "SYS_MOUNT", + 166 => "SYS_UMOUNT2", + 167 => "SYS_SWAPON", + 168 => "SYS_SWAPOFF", + 169 => "SYS_REBOOT", + 170 => "SYS_SETHOSTNAME", + 171 => "SYS_SETDOMAINNAME", + 172 => "SYS_IOPL", + 173 => "SYS_IOPERM", + 174 => "SYS_CREATE_MODULE", + 175 => "SYS_INIT_MODULE", + 176 => "SYS_DELETE_MODULE", + 177 => "SYS_GET_KERNEL_SYMS", + 178 => "SYS_QUERY_MODULE", + 179 => "SYS_QUOTACTL", + 180 => "SYS_NFSSERVCTL", + 181 => "SYS_GETPMSG", + 182 => "SYS_PUTPMSG", + 183 => "SYS_AFS_SYSCALL", + 184 => "SYS_TUXCALL", + 185 => "SYS_SECURITY", + 186 => "SYS_GETTID", + 187 => "SYS_READAHEAD", + 188 => "SYS_SETXATTR", + 189 => "SYS_LSETXATTR", + 190 => "SYS_FSETXATTR", + 191 => "SYS_GETXATTR", + 192 => "SYS_LGETXATTR", + 193 => "SYS_FGETXATTR", + 194 => "SYS_LISTXATTR", + 195 => "SYS_LLISTXATTR", + 196 => "SYS_FLISTXATTR", + 197 => "SYS_REMOVEXATTR", + 198 => "SYS_LREMOVEXATTR", + 199 => "SYS_FREMOVEXATTR", + 200 => "SYS_TKILL", + 201 => "SYS_TIME", + 202 => "SYS_FUTEX", + 203 => "SYS_SCHED_SETAFFINITY", + 204 => "SYS_SCHED_GETAFFINITY", + 205 => "SYS_SET_THREAD_AREA", + 206 => "SYS_IO_SETUP", + 207 => "SYS_IO_DESTROY", + 208 => "SYS_IO_GETEVENTS", + 209 => "SYS_IO_SUBMIT", + 210 => "SYS_IO_CANCEL", + 211 => "SYS_GET_THREAD_AREA", + 212 => "SYS_LOOKUP_DCOOKIE", + 213 => "SYS_EPOLL_CREATE", + 214 => "SYS_EPOLL_CTL_OLD", + 215 => "SYS_EPOLL_WAIT_OLD", + 216 => "SYS_REMAP_FILE_PAGES", + 217 => "SYS_GETDENTS64", + 218 => "SYS_SET_TID_ADDRESS", + 219 => "SYS_RESTART_SYSCALL", + 220 => "SYS_SEMTIMEDOP", + 221 => "SYS_FADVISE64", + 222 => "SYS_TIMER_CREATE", + 223 => "SYS_TIMER_SETTIME", + 224 => "SYS_TIMER_GETTIME", + 225 => "SYS_TIMER_GETOVERRUN", + 226 => "SYS_TIMER_DELETE", + 227 => "SYS_CLOCK_SETTIME", + 228 => "SYS_CLOCK_GETTIME", + 229 => "SYS_CLOCK_GETRES", + 230 => "SYS_CLOCK_NANOSLEEP", + 231 => "SYS_EXIT_GROUP", + 232 => "SYS_EPOLL_WAIT", + 233 => "SYS_EPOLL_CTL", + 234 => "SYS_TGKILL", + 235 => "SYS_UTIMES", + 236 => "SYS_VSERVER", + 237 => "SYS_MBIND", + 238 => "SYS_SET_MEMPOLICY", + 239 => "SYS_GET_MEMPOLICY", + 240 => "SYS_MQ_OPEN", + 241 => "SYS_MQ_UNLINK", + 242 => "SYS_MQ_TIMEDSEND", + 243 => "SYS_MQ_TIMEDRECEIVE", + 244 => "SYS_MQ_NOTIFY", + 245 => "SYS_MQ_GETSETATTR", + 246 => "SYS_KEXEC_LOAD", + 247 => "SYS_WAITID", + 248 => "SYS_ADD_KEY", + 249 => "SYS_REQUEST_KEY", + 250 => "SYS_KEYCTL", + 251 => "SYS_IOPRIO_SET", + 252 => "SYS_IOPRIO_GET", + 253 => "SYS_INOTIFY_INIT", + 254 => "SYS_INOTIFY_ADD_WATCH", + 255 => "SYS_INOTIFY_RM_WATCH", + 256 => "SYS_MIGRATE_PAGES", + 257 => "SYS_OPENAT", + 258 => "SYS_MKDIRAT", + 259 => "SYS_MKNODAT", + 260 => "SYS_FCHOWNAT", + 261 => "SYS_FUTIMESAT", + 262 => "SYS_NEWFSTATAT", + 263 => "SYS_UNLINKAT", + 264 => "SYS_RENAMEAT", + 265 => "SYS_LINKAT", + 266 => "SYS_SYMLINKAT", + 267 => "SYS_READLINKAT", + 268 => "SYS_FCHMODAT", + 269 => "SYS_FACCESSAT", + 270 => "SYS_PSELECT6", + 271 => "SYS_PPOLL", + 272 => "SYS_UNSHARE", + 273 => "SYS_SET_ROBUST_LIST", + 274 => "SYS_GET_ROBUST_LIST", + 275 => "SYS_SPLICE", + 276 => "SYS_TEE", + 277 => "SYS_SYNC_FILE_RANGE", + 278 => "SYS_VMSPLICE", + 279 => "SYS_MOVE_PAGES", + 280 => "SYS_UTIMENSAT", + 281 => "SYS_EPOLL_PWAIT", + 282 => "SYS_SIGNALFD", + 283 => "SYS_TIMERFD_CREATE", + 284 => "SYS_EVENTFD", + 285 => "SYS_FALLOCATE", + 286 => "SYS_TIMERFD_SETTIME", + 287 => "SYS_TIMERFD_GETTIME", + 288 => "SYS_ACCEPT4", + 289 => "SYS_SIGNALFD4", + 290 => "SYS_EVENTFD2", + 291 => "SYS_EPOLL_CREATE1", + 292 => "SYS_DUP3", + 293 => "SYS_PIPE2", + 294 => "SYS_INOTIFY_INIT1", + 295 => "SYS_PREADV", + 296 => "SYS_PWRITEV", + 297 => "SYS_RT_TGSIGQUEUEINFO", + 298 => "SYS_PERF_EVENT_OPEN", + 299 => "SYS_RECVMMSG", + 300 => "SYS_FANOTIFY_INIT", + 301 => "SYS_FANOTIFY_MARK", + 302 => "SYS_PRLIMIT64", + 303 => "SYS_NAME_TO_HANDLE_AT", + 304 => "SYS_OPEN_BY_HANDLE_AT", + 305 => "SYS_CLOCK_ADJTIME", + 306 => "SYS_SYNCFS", + 307 => "SYS_SENDMMSG", + 308 => "SYS_SETNS", + 309 => "SYS_GETCPU", + 310 => "SYS_PROCESS_VM_READV", + 311 => "SYS_PROCESS_VM_WRITEV", + 312 => "SYS_KCMP", + 313 => "SYS_FINIT_MODULE", + 314 => "SYS_SCHED_SETATTR", + 315 => "SYS_SCHED_GETATTR", + 316 => "SYS_RENAMEAT2", + 317 => "SYS_SECCOMP", + 318 => "SYS_GETRANDOM", + 319 => "SYS_MEMFD_CREATE", + 320 => "SYS_KEXEC_FILE_LOAD", + 321 => "SYS_BPF", + 322 => "SYS_EXECVEAT", + 323 => "SYS_USERFAULTFD", + 324 => "SYS_MEMBARRIER", + 325 => "SYS_MLOCK2", + 326 => "SYS_COPY_FILE_RANGE", + 327 => "SYS_PREADV2", + 328 => "SYS_PWRITEV2", + 329 => "SYS_PKEY_MPROTECT", + 330 => "SYS_PKEY_ALLOC", + 331 => "SYS_PKEY_FREE", + 332 => "SYS_STATX", + 333 => "SYS_IO_PGETEVENTS", + 334 => "SYS_RSEQ", + 335..=423 => "UNKNOWN", + 424 => "SYS_PIDFD_SEND_SIGNAL", + 425 => "SYS_IO_URING_SETUP", + 426 => "SYS_IO_URING_ENTER", + 427 => "SYS_IO_URING_REGISTER", + 428 => "SYS_OPEN_TREE", + 429 => "SYS_MOVE_MOUNT", + 430 => "SYS_FSOPEN", + 431 => "SYS_FSCONFIG", + 432 => "SYS_FSMOUNT", + 433 => "SYS_FSPICK", + 434 => "SYS_PIDFD_OPEN", + 435 => "SYS_CLONE3", + 436 => "SYS_CLOSE_RANGE", + 437 => "SYS_OPENAT2", + 438 => "SYS_PIDFD_GETFD", + 439 => "SYS_FACCESSAT2", + 440 => "SYS_PROCESS_MADVISE", + 441 => "SYS_EPOLL_PWAIT2", + 442 => "SYS_MOUNT_SETATTR", + _ => "UNKNOWN", + } +} diff --git a/kernel/src/bpf/map/mod.rs b/kernel/src/bpf/map/mod.rs index cc8eeffef..c442f98da 100644 --- a/kernel/src/bpf/map/mod.rs +++ b/kernel/src/bpf/map/mod.rs @@ -182,6 +182,10 @@ impl IndexNode for BpfMap { fn list(&self) -> Result> { Err(SystemError::ENOSYS) } + + fn absolute_path(&self) -> core::result::Result { + Ok(String::from("BpfMap")) + } } /// Create a map and return a file descriptor that refers to diff --git a/kernel/src/bpf/prog/mod.rs b/kernel/src/bpf/prog/mod.rs index 569af42fe..d3a67a33c 100644 --- a/kernel/src/bpf/prog/mod.rs +++ b/kernel/src/bpf/prog/mod.rs @@ -95,6 +95,10 @@ impl IndexNode for BpfProg { fn list(&self) -> Result> { Err(SystemError::ENOSYS) } + + fn absolute_path(&self) -> core::result::Result { + Ok(String::from("BPF Program")) + } } impl Drop for BpfProg { diff --git a/kernel/src/driver/base/block/gendisk/mod.rs b/kernel/src/driver/base/block/gendisk/mod.rs index f814c178b..196855f6b 100644 --- a/kernel/src/driver/base/block/gendisk/mod.rs +++ b/kernel/src/driver/base/block/gendisk/mod.rs @@ -13,7 +13,7 @@ use system_error::SystemError; use crate::{ driver::base::device::device_number::DeviceNumber, filesystem::{ - devfs::{DevFS, DeviceINode}, + devfs::{DevFS, DeviceINode, LockedDevFSInode}, vfs::{syscall::ModeType, utils::DName, IndexNode, Metadata}, }, libs::{rwlock::RwLock, spinlock::SpinLockGuard}, @@ -32,6 +32,7 @@ pub struct GenDisk { device_num: DeviceNumber, + parent: RwLock>, fs: RwLock>, metadata: Metadata, /// 对应/dev/下的设备名 @@ -69,6 +70,7 @@ impl GenDisk { block_size_log2: bsizelog2, idx, device_num, + parent: RwLock::new(Weak::default()), fs: RwLock::new(Weak::default()), metadata: Metadata::new( crate::filesystem::vfs::FileType::BlockDevice, @@ -212,9 +214,11 @@ impl IndexNode for GenDisk { fn fs(&self) -> Arc { self.fs.read().upgrade().unwrap() } + fn as_any_ref(&self) -> &dyn core::any::Any { self } + fn read_at( &self, _offset: usize, @@ -224,6 +228,7 @@ impl IndexNode for GenDisk { ) -> Result { Err(SystemError::EPERM) } + fn write_at( &self, _offset: usize, @@ -233,21 +238,53 @@ impl IndexNode for GenDisk { ) -> Result { Err(SystemError::EPERM) } + fn list(&self) -> Result, system_error::SystemError> { Err(SystemError::ENOSYS) } + fn metadata(&self) -> Result { Ok(self.metadata.clone()) } + fn dname(&self) -> Result { Ok(self.name.clone()) } + + fn parent(&self) -> Result, SystemError> { + let parent = self.parent.read(); + if let Some(parent) = parent.upgrade() { + return Ok(parent as Arc); + } + Err(SystemError::ENOENT) + } + + fn close( + &self, + _data: SpinLockGuard, + ) -> Result<(), SystemError> { + log::warn!("GenDisk::close called, but GenDisk does not support close operation."); + Ok(()) + } + + fn open( + &self, + _data: SpinLockGuard, + _mode: &crate::filesystem::vfs::file::FileMode, + ) -> Result<(), SystemError> { + log::warn!("GenDisk::open called, but GenDisk does not support open operation."); + Ok(()) + } } impl DeviceINode for GenDisk { fn set_fs(&self, fs: alloc::sync::Weak) { *self.fs.write() = fs; } + + fn set_parent(&self, parent: Weak) { + *self.parent.write() = parent; + } } #[derive(Default)] diff --git a/kernel/src/driver/block/virtio_blk.rs b/kernel/src/driver/block/virtio_blk.rs index 856140d37..82aa927c5 100644 --- a/kernel/src/driver/block/virtio_blk.rs +++ b/kernel/src/driver/block/virtio_blk.rs @@ -42,10 +42,10 @@ use crate::{ }, exception::{irqdesc::IrqReturn, IrqNumber}, filesystem::{ - devfs::{DevFS, DeviceINode}, + devfs::{DevFS, DeviceINode, LockedDevFSInode}, kernfs::KernFSInode, mbr::MbrDiskPartionTable, - vfs::{syscall::ModeType, IndexNode, Metadata}, + vfs::{syscall::ModeType, utils::DName, IndexNode, Metadata}, }, init::initcall::INITCALL_POSTCORE, libs::{ @@ -164,7 +164,7 @@ pub struct VirtIOBlkDevice { inner: SpinLock, locked_kobj_state: LockedKObjectState, self_ref: Weak, - + parent: RwLock>, fs: RwLock>, metadata: Metadata, } @@ -212,6 +212,7 @@ impl VirtIOBlkDevice { kobject_common: KObjectCommonData::default(), irq, }), + parent: RwLock::new(Weak::default()), fs: RwLock::new(Weak::default()), metadata: Metadata::new( crate::filesystem::vfs::FileType::BlockDevice, @@ -229,7 +230,10 @@ impl VirtIOBlkDevice { impl IndexNode for VirtIOBlkDevice { fn fs(&self) -> Arc { - todo!() + self.fs + .read() + .upgrade() + .expect("VirtIOBlkDevice fs is not set") } fn as_any_ref(&self) -> &dyn core::any::Any { self @@ -258,12 +262,52 @@ impl IndexNode for VirtIOBlkDevice { fn metadata(&self) -> Result { Ok(self.metadata.clone()) } + + fn parent(&self) -> Result, SystemError> { + let parent = self.parent.read(); + if let Some(parent) = parent.upgrade() { + return Ok(parent as Arc); + } + Err(SystemError::ENOENT) + } + + fn close( + &self, + _data: SpinLockGuard, + ) -> Result<(), SystemError> { + log::warn!( + "VirtIOBlkDevice '{:?}' close called, but it does not do anything", + self.dev_id + ); + Ok(()) + } + + fn dname(&self) -> Result { + let dname = DName::from(self.blkdev_meta.devname.clone().as_ref()); + Ok(dname) + } + + fn open( + &self, + _data: SpinLockGuard, + _mode: &crate::filesystem::vfs::file::FileMode, + ) -> Result<(), SystemError> { + log::warn!( + "VirtIOBlkDevice '{:?}' open called, but it does not do anything", + self.dev_id + ); + Ok(()) + } } impl DeviceINode for VirtIOBlkDevice { fn set_fs(&self, fs: alloc::sync::Weak) { *self.fs.write() = fs; } + + fn set_parent(&self, parent: Weak) { + *self.parent.write() = parent; + } } impl BlockDevice for VirtIOBlkDevice { diff --git a/kernel/src/driver/clocksource/acpi_pm.rs b/kernel/src/driver/clocksource/acpi_pm.rs index e5b7d7a58..db198ef3a 100644 --- a/kernel/src/driver/clocksource/acpi_pm.rs +++ b/kernel/src/driver/clocksource/acpi_pm.rs @@ -227,7 +227,6 @@ fn find_acpi_pm_clock() -> Result<(), SystemError> { } /// # 初始化ACPI PM Timer作为系统时钟源 -// #[unified_init(INITCALL_FS)] #[inline(never)] #[allow(dead_code)] pub fn init_acpi_pm_clocksource() -> Result<(), SystemError> { diff --git a/kernel/src/driver/input/ps2_mouse/ps_mouse_device.rs b/kernel/src/driver/input/ps2_mouse/ps_mouse_device.rs index 1790e70bb..bb31d78eb 100644 --- a/kernel/src/driver/input/ps2_mouse/ps_mouse_device.rs +++ b/kernel/src/driver/input/ps2_mouse/ps_mouse_device.rs @@ -28,7 +28,7 @@ use crate::{ }, exception::InterruptArch, filesystem::{ - devfs::{devfs_register, DevFS, DeviceINode}, + devfs::{devfs_register, DevFS, DeviceINode, LockedDevFSInode}, kernfs::KernFSInode, vfs::{ syscall::ModeType, utils::DName, vcore::generate_inode_id, FilePrivateData, FileSystem, @@ -207,6 +207,7 @@ impl Ps2MouseDevice { raw_dev: DeviceNumber::default(), // 这里用来作为device number }, device_inode_fs: None, + parent: Weak::new(), }), kobj_state: LockedKObjectState::new(None), }; @@ -424,6 +425,7 @@ struct InnerPs2MouseDevice { /// device inode要求的字段 device_inode_fs: Option>, + parent: Weak, devfs_metadata: Metadata, } @@ -587,6 +589,10 @@ impl DeviceINode for Ps2MouseDevice { fn set_fs(&self, fs: Weak) { self.inner.lock_irqsave().device_inode_fs = Some(fs); } + + fn set_parent(&self, parent: Weak) { + self.inner.lock_irqsave().parent = parent; + } } impl IndexNode for Ps2MouseDevice { @@ -664,6 +670,14 @@ impl IndexNode for Ps2MouseDevice { fn dname(&self) -> Result { Ok(DName::from(self.name())) } + + fn parent(&self) -> Result, SystemError> { + let guard = self.inner.lock_irqsave(); + if let Some(parent) = guard.parent.upgrade() { + return Ok(parent); + } + Err(SystemError::ENOENT) + } } impl Ps2Device for Ps2MouseDevice {} diff --git a/kernel/src/driver/keyboard/ps2_keyboard.rs b/kernel/src/driver/keyboard/ps2_keyboard.rs index 35a65a654..afb45ef4c 100644 --- a/kernel/src/driver/keyboard/ps2_keyboard.rs +++ b/kernel/src/driver/keyboard/ps2_keyboard.rs @@ -20,7 +20,7 @@ use crate::{ InterruptArch, IrqNumber, }, filesystem::{ - devfs::{devfs_register, DevFS, DeviceINode}, + devfs::{devfs_register, DevFS, DeviceINode, LockedDevFSInode}, vfs::{ file::FileMode, syscall::ModeType, vcore::generate_inode_id, FilePrivateData, FileSystem, FileType, IndexNode, Metadata, @@ -66,6 +66,7 @@ pub struct PS2KeyBoardInode { /// 指向自身的弱引用 self_ref: Weak, /// 指向inode所在的文件系统对象的指针 + parent: Weak, fs: Weak, /// INode 元数据 metadata: Metadata, @@ -76,6 +77,7 @@ impl LockedPS2KeyBoardInode { let inode = PS2KeyBoardInode { // uuid: Uuid::new_v5(), self_ref: Weak::default(), + parent: Weak::default(), fs: Weak::default(), metadata: Metadata { dev_id: 1, @@ -107,6 +109,11 @@ impl DeviceINode for LockedPS2KeyBoardInode { fn set_fs(&self, fs: Weak) { self.0.write().fs = fs; } + + fn set_parent(&self, parent: Weak) { + let mut inode = self.0.write(); + inode.parent = parent; + } } fn ps2_keyboard_register() { @@ -175,6 +182,14 @@ impl IndexNode for LockedPS2KeyBoardInode { fn list(&self) -> Result, SystemError> { return Err(SystemError::ENOSYS); } + + fn parent(&self) -> Result, SystemError> { + let parent = self.0.read().parent.upgrade(); + if let Some(parent) = parent { + return Ok(parent as Arc); + } + Err(SystemError::ENOENT) + } } #[derive(Debug)] diff --git a/kernel/src/driver/tty/pty/mod.rs b/kernel/src/driver/tty/pty/mod.rs index 99422c5ae..508bf4f6d 100644 --- a/kernel/src/driver/tty/pty/mod.rs +++ b/kernel/src/driver/tty/pty/mod.rs @@ -143,19 +143,29 @@ impl PtyCommon { if let Some(link) = core.link() { let link_core = link.core(); + log::info!( + "pty_common_open: link_core flags: {:?}, core flags: {:?}", + link_core.flags(), + core.flags() + ); if core.flags().contains(TtyFlag::OTHER_CLOSED) { core.flags_write().insert(TtyFlag::IO_ERROR); return Err(SystemError::EIO); } if link_core.flags().contains(TtyFlag::PTY_LOCK) { - core.flags_write().insert(TtyFlag::IO_ERROR); - return Err(SystemError::EIO); + // core.flags_write().insert(TtyFlag::IO_ERROR); + // return Err(SystemError::EIO); } if core.driver().tty_driver_sub_type() == TtyDriverSubType::PtySlave && link_core.count() != 1 { + log::error!( + "pty_common_open: link_core.count() = {}, core.count() = {}", + link_core.count(), + core.count() + ); // 只能有一个master,如果当前为slave,则link的count必须为1 core.flags_write().insert(TtyFlag::IO_ERROR); return Err(SystemError::EIO); diff --git a/kernel/src/driver/tty/pty/unix98pty.rs b/kernel/src/driver/tty/pty/unix98pty.rs index c8658335a..ea2b5cce4 100644 --- a/kernel/src/driver/tty/pty/unix98pty.rs +++ b/kernel/src/driver/tty/pty/unix98pty.rs @@ -216,12 +216,18 @@ impl TtyOperation for Unix98PtyDriverInner { fn close(&self, tty: Arc) -> Result<(), SystemError> { let driver = tty.core().driver(); - if tty.core().driver().tty_driver_sub_type() == TtyDriverSubType::PtySlave { - driver.ttys().remove(&tty.core().index()); - let pts_root_inode = - ROOT_INODE().lookup_follow_symlink("/dev/pts", VFS_MAX_FOLLOW_SYMLINK_TIMES)?; - let _ = pts_root_inode.unlink(&tty.core().index().to_string()); + // check master exists + // TODO: 如果master不存在,应该删除slave + let ptmx = ROOT_INODE().lookup_follow_symlink("/dev/pts", VFS_MAX_FOLLOW_SYMLINK_TIMES); + if let Err(e) = ptmx + && e == SystemError::ENOENT + { + driver.ttys().remove(&tty.core().index()); + let pts_root_inode = + ROOT_INODE().lookup_follow_symlink("/dev/pts", VFS_MAX_FOLLOW_SYMLINK_TIMES)?; + let _ = pts_root_inode.unlink(&tty.core().index().to_string()); + } } Ok(()) diff --git a/kernel/src/driver/tty/tty_device.rs b/kernel/src/driver/tty/tty_device.rs index 00303ba2d..0c8cd5366 100644 --- a/kernel/src/driver/tty/tty_device.rs +++ b/kernel/src/driver/tty/tty_device.rs @@ -26,12 +26,13 @@ use crate::{ serial::serial_init, }, filesystem::{ - devfs::{devfs_register, DevFS, DeviceINode}, + devfs::{devfs_register, DevFS, DeviceINode, LockedDevFSInode}, + devpts::{DevPtsFs, LockedDevPtsFSInode}, epoll::EPollItem, kernfs::KernFSInode, vfs::{ - file::FileMode, syscall::ModeType, FilePrivateData, FileType, IndexNode, Metadata, - PollableInode, + file::FileMode, syscall::ModeType, utils::DName, FilePrivateData, FileType, IndexNode, + Metadata, PollableInode, }, }, init::initcall::INITCALL_DEVICE, @@ -107,7 +108,19 @@ pub struct TtyDevice { inner: RwLock, kobj_state: LockedKObjectState, /// TTY所属的文件系统 - fs: RwLock>, + fs: RwLock, + parent: RwLock, +} + +pub enum TtyDeviceFs { + DevFs(Weak), + DevPtsFs(Weak), + None, +} +pub enum TtyDeviceParent { + DevFS(Weak), + DevPts(Weak), + None, } impl TtyDevice { @@ -118,7 +131,8 @@ impl TtyDevice { id_table, inner: RwLock::new(InnerTtyDevice::new()), kobj_state: LockedKObjectState::new(None), - fs: RwLock::new(Weak::default()), + fs: RwLock::new(TtyDeviceFs::None), + parent: RwLock::new(TtyDeviceParent::None), tty_type, }; @@ -211,6 +225,7 @@ impl IndexNode for TtyDevice { if err == SystemError::ENOSYS { return Err(SystemError::ENODEV); } + log::error!("Failed to open tty device: {}, error: {:?}", self.name, err); return Err(err); } @@ -325,7 +340,17 @@ impl IndexNode for TtyDevice { } fn fs(&self) -> Arc { - todo!() + match &*self.fs.read() { + TtyDeviceFs::DevFs(fs) => fs.upgrade().unwrap(), + TtyDeviceFs::DevPtsFs(fs) => fs.upgrade().unwrap(), + TtyDeviceFs::None => { + panic!("TtyDevice has no filesystem set"); + } + } + } + + fn dname(&self) -> Result { + Ok(self.name.clone().into()) } fn as_any_ref(&self) -> &dyn core::any::Any { @@ -446,11 +471,32 @@ impl IndexNode for TtyDevice { fn as_pollable_inode(&self) -> Result<&dyn PollableInode, SystemError> { Ok(self) } + + fn parent(&self) -> Result, SystemError> { + let parent = self.parent.read(); + match &*parent { + TtyDeviceParent::DevFS(devfs) => Ok(devfs.upgrade().unwrap()), + TtyDeviceParent::DevPts(devpts) => Ok(devpts.upgrade().unwrap()), + TtyDeviceParent::None => panic!("TtyDevice has no filesystem set"), + } + } } impl DeviceINode for TtyDevice { fn set_fs(&self, fs: alloc::sync::Weak) { - *self.fs.write() = fs; + *self.fs.write() = TtyDeviceFs::DevFs(fs); + } + + fn set_parent(&self, parent: Weak) { + *self.parent.write() = TtyDeviceParent::DevFS(parent); + } + + fn set_devpts_fs(&self, devpts: Weak) { + *self.fs.write() = TtyDeviceFs::DevPtsFs(devpts); + } + + fn set_devpts_parent(&self, parent: Weak) { + *self.parent.write() = TtyDeviceParent::DevPts(parent); } } @@ -610,6 +656,14 @@ impl TtyFilePrivateData { #[unified_init(INITCALL_DEVICE)] #[inline(never)] pub fn tty_init() -> Result<(), SystemError> { + let tty_device = TtyDevice::new( + "tty".to_string(), + IdTable::new( + String::from("tty"), + Some(DeviceNumber::new(Major::TTYAUX_MAJOR, 0)), + ), + TtyType::Tty, + ); let console = TtyDevice::new( "console".to_string(), IdTable::new( @@ -619,9 +673,13 @@ pub fn tty_init() -> Result<(), SystemError> { TtyType::Tty, ); - // 将设备注册到devfs,TODO:这里console设备应该与tty在一个设备group里面 - device_register(console.clone())?; - devfs_register(&console.name.clone(), console)?; + let devs = [tty_device, console]; + + for dev in devs { + // 将设备注册到devfs,TODO:这里console设备应该与tty在一个设备group里面 + device_register(dev.clone())?; + devfs_register(&dev.name.clone(), dev)?; + } serial_init()?; diff --git a/kernel/src/driver/tty/tty_job_control.rs b/kernel/src/driver/tty/tty_job_control.rs index 9cff704f3..522378511 100644 --- a/kernel/src/driver/tty/tty_job_control.rs +++ b/kernel/src/driver/tty/tty_job_control.rs @@ -102,7 +102,8 @@ impl TtyJobCtrlManager { if let Some(sid) = real_tty.core().contorl_info_irqsave().session { //todo 目前只有一个tty设备,所以选择复用1号进程的tty,因此修改1号进程的tty暂时被允许 if sid != Pid::new(1) { - return Err(SystemError::EPERM); + log::error!("sid != 1"); + // return Err(SystemError::EPERM); } } diff --git a/kernel/src/driver/tty/tty_ldisc/ntty.rs b/kernel/src/driver/tty/tty_ldisc/ntty.rs index 774ae77c0..30c89e295 100644 --- a/kernel/src/driver/tty/tty_ldisc/ntty.rs +++ b/kernel/src/driver/tty/tty_ldisc/ntty.rs @@ -57,7 +57,10 @@ impl NTtyLinediscipline { todo!() } TtyIoctlCmd::TCFLSH => { - todo!() + // let data = tty.core(); + // tty.flush_chars(data); + log::info!("NTTY TCFLSH cmd"); + Ok(0) } _ => { return TtyCore::tty_mode_ioctl(tty.clone(), cmd, arg); diff --git a/kernel/src/driver/video/fbdev/base/fbmem.rs b/kernel/src/driver/video/fbdev/base/fbmem.rs index c3749015d..6bc0eecc8 100644 --- a/kernel/src/driver/video/fbdev/base/fbmem.rs +++ b/kernel/src/driver/video/fbdev/base/fbmem.rs @@ -25,7 +25,7 @@ use crate::{ subsys::SubSysPrivate, }, filesystem::{ - devfs::{devfs_register, DevFS, DeviceINode}, + devfs::{devfs_register, DevFS, DeviceINode, LockedDevFSInode}, kernfs::KernFSInode, sysfs::AttributeGroup, vfs::{ @@ -223,6 +223,7 @@ impl FbDevice { device_common: DeviceCommonData::default(), fb_id: id, device_inode_fs: None, + parent: Weak::default(), devfs_metadata: Metadata::new( FileType::FramebufferDevice, ModeType::from_bits_truncate(0o666), @@ -268,6 +269,7 @@ struct InnerFbDevice { /// device inode要求的字段 device_inode_fs: Option>, + parent: Weak, devfs_metadata: Metadata, } @@ -390,6 +392,10 @@ impl DeviceINode for FbDevice { fn set_fs(&self, fs: Weak) { self.inner.lock().device_inode_fs = Some(fs); } + + fn set_parent(&self, parent: Weak) { + self.inner.lock().parent = parent; + } } impl IndexNode for FbDevice { @@ -451,4 +457,12 @@ impl IndexNode for FbDevice { fn resize(&self, _len: usize) -> Result<(), SystemError> { return Ok(()); } + + fn parent(&self) -> Result, SystemError> { + let parent = self.inner.lock().parent.upgrade(); + if let Some(parent) = parent { + return Ok(parent as Arc); + } + Err(SystemError::ENOENT) + } } diff --git a/kernel/src/filesystem/devfs/mod.rs b/kernel/src/filesystem/devfs/mod.rs index eade907dc..f76749674 100644 --- a/kernel/src/filesystem/devfs/mod.rs +++ b/kernel/src/filesystem/devfs/mod.rs @@ -11,7 +11,9 @@ use super::vfs::{ }; use crate::{ driver::base::{block::gendisk::GenDisk, device::device_number::DeviceNumber}, + filesystem::devpts::{DevPtsFs, LockedDevPtsFSInode}, libs::{ + casting::DowncastArc, once::Once, spinlock::{SpinLock, SpinLockGuard}, }, @@ -75,6 +77,8 @@ impl DevFS { DevFSInode::new(FileType::Dir, ModeType::from_bits_truncate(0o755), 0), ))); + // panic!("devfs root inode id: {:?}", root.0.lock().metadata.inode_id); + let devfs: Arc = Arc::new(DevFS { root_inode: root, super_block, @@ -112,6 +116,9 @@ impl DevFS { dev_root .add_dev("zero", LockedZeroInode::new()) .expect("DevFS: Failed to register /dev/zero"); + dev_root + .add_dev("urandom", LockedZeroInode::new()) + .expect("DevFS: Failed to register /dev/urandom"); } /// @brief 在devfs内注册设备 @@ -123,7 +130,7 @@ impl DevFS { name: &str, device: Arc, ) -> Result<(), SystemError> { - let dev_root_inode: Arc = self.root_inode.clone(); + let dev_root_inode = self.root_inode.clone(); let metadata = device.metadata()?; match metadata.file_type { // 字节设备挂载在 /dev/char @@ -137,13 +144,12 @@ impl DevFS { } let any_char_inode = dev_root_inode.find("char")?; - let dev_char_inode: &LockedDevFSInode = any_char_inode - .as_any_ref() - .downcast_ref::() - .unwrap(); + let dev_char_inode = any_char_inode.downcast_arc::().unwrap(); + device.set_fs(dev_root_inode.0.lock().fs.clone()); + device.set_parent(Arc::downgrade(&dev_root_inode)); // 特殊处理 tty 设备,挂载在 /dev 下 - if name.starts_with("tty") && name.len() > 3 { + if name.starts_with("tty") && name.len() >= 3 { dev_root_inode.add_dev(name, device.clone())?; } else if name.starts_with("hvc") && name.len() > 3 { // 特殊处理 hvc 设备,挂载在 /dev 下 @@ -156,8 +162,8 @@ impl DevFS { } else { // 在 /dev/char 下创建设备节点 dev_char_inode.add_dev(name, device.clone())?; + device.set_parent(Arc::downgrade(&dev_char_inode)); } - device.set_fs(dev_char_inode.0.lock().fs.clone()); } FileType::BlockDevice => { if dev_root_inode.find("block").is_err() { @@ -169,10 +175,11 @@ impl DevFS { } let any_block_inode = dev_root_inode.find("block")?; - let dev_block_inode: &LockedDevFSInode = any_block_inode - .as_any_ref() - .downcast_ref::() - .unwrap(); + let dev_block_inode = any_block_inode.downcast_arc::().unwrap(); + + // default parent is dev_root_inode + device.set_parent(Arc::downgrade(&dev_root_inode)); + device.set_fs(dev_root_inode.0.lock().fs.clone()); if name.starts_with("vd") && name.len() > 2 { // 虚拟磁盘设备挂载在 /dev 下 @@ -190,24 +197,22 @@ impl DevFS { dev_root_inode.add_dev(name, device.clone())?; } else { dev_block_inode.add_dev(name, device.clone())?; + device.set_parent(Arc::downgrade(&dev_block_inode)); } - device.set_fs(dev_block_inode.0.lock().fs.clone()); - } - FileType::KvmDevice => { - dev_root_inode - .add_dev(name, device.clone()) - .expect("DevFS: Failed to register /dev/kvm"); } - FileType::FramebufferDevice => { + FileType::KvmDevice | FileType::FramebufferDevice => { dev_root_inode .add_dev(name, device.clone()) - .expect("DevFS: Failed to register /dev/fb"); + .unwrap_or_else(|_| panic!("DevFS: Failed to register /dev/{}", name)); + + // default parent is dev_root_inode + device.set_parent(Arc::downgrade(&dev_root_inode)); + device.set_fs(dev_root_inode.0.lock().fs.clone()); } _ => { return Err(SystemError::ENOSYS); } } - return Ok(()); } @@ -679,6 +684,13 @@ impl IndexNode for LockedDevFSInode { /// @brief 所有的设备INode都需要额外实现这个trait pub trait DeviceINode: IndexNode { fn set_fs(&self, fs: Weak); + fn set_parent(&self, parent: Weak); + fn set_devpts_fs(&self, _devpts: Weak) { + panic!("DeviceINode: set_devpts_fs is not implemented!"); + } + fn set_devpts_parent(&self, _parent: Weak) { + panic!("DeviceINode: set_devpts_parent is not implemented!"); + } // TODO: 增加 unregister 方法 } diff --git a/kernel/src/filesystem/devfs/null_dev.rs b/kernel/src/filesystem/devfs/null_dev.rs index 71f45aab0..a134ad609 100644 --- a/kernel/src/filesystem/devfs/null_dev.rs +++ b/kernel/src/filesystem/devfs/null_dev.rs @@ -1,4 +1,5 @@ use crate::driver::base::device::device_number::DeviceNumber; +use crate::filesystem::devfs::LockedDevFSInode; use crate::filesystem::vfs::file::FileMode; use crate::filesystem::vfs::syscall::ModeType; use crate::filesystem::vfs::{ @@ -25,6 +26,7 @@ pub struct NullInode { fs: Weak, /// INode 元数据 metadata: Metadata, + parent: Weak, } #[derive(Debug)] @@ -36,6 +38,7 @@ impl LockedNullInode { // uuid: Uuid::new_v5(), self_ref: Weak::default(), fs: Weak::default(), + parent: Weak::default(), metadata: Metadata { dev_id: 1, inode_id: generate_inode_id(), @@ -66,6 +69,10 @@ impl DeviceINode for LockedNullInode { fn set_fs(&self, fs: Weak) { self.0.lock().fs = fs; } + + fn set_parent(&self, parent: Weak) { + self.0.lock().parent = parent; + } } impl IndexNode for LockedNullInode { @@ -135,4 +142,12 @@ impl IndexNode for LockedNullInode { Ok(len) } + + fn parent(&self) -> Result, SystemError> { + let parent = self.0.lock().parent.upgrade(); + if let Some(parent) = parent { + return Ok(parent); + } + Err(SystemError::ENOENT) + } } diff --git a/kernel/src/filesystem/devfs/zero_dev.rs b/kernel/src/filesystem/devfs/zero_dev.rs index b19af3475..a4b634dc5 100644 --- a/kernel/src/filesystem/devfs/zero_dev.rs +++ b/kernel/src/filesystem/devfs/zero_dev.rs @@ -1,4 +1,5 @@ use crate::driver::base::device::device_number::DeviceNumber; +use crate::filesystem::devfs::LockedDevFSInode; use crate::filesystem::vfs::file::FileMode; use crate::filesystem::vfs::syscall::ModeType; use crate::filesystem::vfs::{ @@ -23,6 +24,7 @@ pub struct ZeroInode { self_ref: Weak, /// 指向inode所在的文件系统对象的指针 fs: Weak, + parent: Weak, /// INode 元数据 metadata: Metadata, } @@ -35,6 +37,7 @@ impl LockedZeroInode { let inode = ZeroInode { // uuid: Uuid::new_v5(), self_ref: Weak::default(), + parent: Weak::default(), fs: Weak::default(), metadata: Metadata { dev_id: 1, @@ -66,6 +69,10 @@ impl DeviceINode for LockedZeroInode { fn set_fs(&self, fs: Weak) { self.0.lock().fs = fs; } + + fn set_parent(&self, parent: Weak) { + self.0.lock().parent = parent; + } } impl IndexNode for LockedZeroInode { @@ -143,4 +150,12 @@ impl IndexNode for LockedZeroInode { Ok(len) } + + fn parent(&self) -> Result, SystemError> { + let parent = self.0.lock().parent.upgrade(); + if let Some(parent) = parent { + return Ok(parent as Arc); + } + Err(SystemError::ENOENT) + } } diff --git a/kernel/src/filesystem/devpts/mod.rs b/kernel/src/filesystem/devpts/mod.rs index fa3de630f..8c9a23ad7 100644 --- a/kernel/src/filesystem/devpts/mod.rs +++ b/kernel/src/filesystem/devpts/mod.rs @@ -1,16 +1,5 @@ use core::sync::atomic::{AtomicU32, Ordering}; -use alloc::{ - collections::BTreeMap, - string::{String, ToString}, - sync::{Arc, Weak}, - vec::Vec, -}; -use ida::IdAllocator; -use log::info; -use system_error::SystemError; -use unified_init::macros::unified_init; - use crate::{ driver::{ base::device::{ @@ -22,11 +11,22 @@ use crate::{ tty_device::{PtyType, TtyDevice, TtyType}, }, }, - filesystem::vfs::{mount::do_mount_mkdir, syscall::ModeType, FileType}, - init::initcall::INITCALL_FS, + filesystem::{ + devfs::DeviceINode, + vfs::{mount::do_mount_mkdir, syscall::ModeType, FileType}, + }, libs::spinlock::{SpinLock, SpinLockGuard}, time::PosixTimeSpec, }; +use alloc::{ + collections::BTreeMap, + string::{String, ToString}, + sync::{Arc, Weak}, + vec::Vec, +}; +use ida::IdAllocator; +use log::info; +use system_error::SystemError; use super::vfs::{ vcore::generate_inode_id, FilePrivateData, FileSystem, FsInfo, IndexNode, Metadata, @@ -48,6 +48,8 @@ pub struct DevPtsFs { impl DevPtsFs { pub fn new() -> Arc { let root_inode = Arc::new(LockedDevPtsFSInode::new()); + root_inode.inner.lock().parent = Arc::downgrade(&root_inode); + root_inode.inner.lock().self_ref = Arc::downgrade(&root_inode); let ret = Arc::new(Self { root_inode, pts_ida: SpinLock::new(IdAllocator::new(0, NR_UNIX98_PTY_MAX as usize).unwrap()), @@ -100,6 +102,8 @@ impl LockedDevPtsFSInode { inner: SpinLock::new(PtsDevInode { fs: Weak::new(), children: Some(BTreeMap::new()), + parent: Weak::new(), + self_ref: Weak::new(), metadata: Metadata { dev_id: 0, inode_id: generate_inode_id(), @@ -131,6 +135,8 @@ pub struct PtsDevInode { fs: Weak, children: Option>>, metadata: Metadata, + parent: Weak, + self_ref: Weak, } impl PtsDevInode { @@ -247,6 +253,9 @@ impl IndexNode for LockedDevPtsFSInode { result.set_metadata(&metadata)?; + result.set_devpts_fs(Arc::downgrade(&fs)); + result.set_devpts_parent(guard.self_ref.clone()); + guard .children_unchecked_mut() .insert(name.to_string(), result.clone()); @@ -257,24 +266,25 @@ impl IndexNode for LockedDevPtsFSInode { } fn find(&self, name: &str) -> Result, SystemError> { + log::error!("DevPtsFs find: {}", name); let guard = self.inner.lock(); if let Some(dev) = guard.children_unchecked().get(name) { Ok(dev.clone() as Arc) } else { + log::error!("DevPtsFs find: {} not found", name); Err(SystemError::ENOENT) } } fn unlink(&self, name: &str) -> Result<(), SystemError> { + log::error!("DevPtsFs unlink: {}", name); let mut guard = self.inner.lock(); guard.children_unchecked_mut().remove(name); Ok(()) } } -#[unified_init(INITCALL_FS)] -#[inline(never)] pub fn devpts_init() -> Result<(), SystemError> { // 创建 devptsfs 实例 let ptsfs: Arc = DevPtsFs::new(); diff --git a/kernel/src/filesystem/epoll/fs.rs b/kernel/src/filesystem/epoll/fs.rs index f19e30522..16718ac93 100644 --- a/kernel/src/filesystem/epoll/fs.rs +++ b/kernel/src/filesystem/epoll/fs.rs @@ -1,3 +1,5 @@ +use alloc::string::String; + use crate::{ filesystem::vfs::{file::FileMode, FilePrivateData, IndexNode, Metadata}, libs::spinlock::SpinLockGuard, @@ -74,4 +76,8 @@ impl IndexNode for EPollInode { ) -> Result<(), SystemError> { Ok(()) } + + fn absolute_path(&self) -> Result { + Ok(String::from("epoll")) + } } diff --git a/kernel/src/filesystem/eventfd.rs b/kernel/src/filesystem/eventfd.rs index e318d226b..f9013ca10 100644 --- a/kernel/src/filesystem/eventfd.rs +++ b/kernel/src/filesystem/eventfd.rs @@ -274,6 +274,10 @@ impl IndexNode for EventFdInode { fn as_pollable_inode(&self) -> Result<&dyn PollableInode, SystemError> { Ok(self) } + + fn absolute_path(&self) -> Result { + Ok(String::from("eventfd")) + } } impl Syscall { diff --git a/kernel/src/filesystem/fat/entry.rs b/kernel/src/filesystem/fat/entry.rs index d6f69e7cb..097a8e7db 100644 --- a/kernel/src/filesystem/fat/entry.rs +++ b/kernel/src/filesystem/fat/entry.rs @@ -870,6 +870,15 @@ impl FATDir { } else { // 如果目标目录项存在,那么就返回错误 return Err(SystemError::EEXIST); + // self.remove(fs.clone(), new_name, true)?; + // if let FATDirEntryOrShortName::ShortName(s) = + // self.check_existence(new_name, None, fs.clone())? + // { + // s + // } else { + // // 如果目标目录项仍然存在,那么就返回错误 + // panic!("FATDir::rename: target directory entry still exists after remove"); + // } }; let old_short_dentry: Option = old_dentry.short_dir_entry(); diff --git a/kernel/src/filesystem/fat/fs.rs b/kernel/src/filesystem/fat/fs.rs index b19faef42..0053f5353 100644 --- a/kernel/src/filesystem/fat/fs.rs +++ b/kernel/src/filesystem/fat/fs.rs @@ -281,10 +281,34 @@ impl LockedFATInode { return Err(SystemError::EROFS); } }; + drop(old_inode_guard); // 检查文件是否存在 // old_dir.check_existence(old_name, Some(false), guard.fs.upgrade().unwrap())?; - old_dir.rename(fs, old_name, new_name)?; + let res = old_dir.rename(fs, old_name, new_name); + + if let Err(SystemError::EEXIST) = res { + // 如果目标目录项存在,那么就返回错误 + let new_inode = guard.find(new_name).unwrap(); + let mut offset = 0; + let mut buf = [0u8; 4096]; + loop { + let read_len = old_inode.read_pagecache(offset, &mut buf).unwrap(); + if read_len == 0 { + break; + } + let write_len = new_inode.write_pagecache(offset, &buf[0..read_len]).unwrap(); + if write_len < read_len { + error!( + "FATFS: write link file failed, read_len={read_len}, write_len={write_len}" + ); + return Err(SystemError::EIO); + } + offset += write_len; + } + return Ok(()); + } + let _nod = guard.children.remove(&to_search_name(old_name)); Ok(()) } @@ -1483,6 +1507,31 @@ impl FATFsInfo { } } +impl LockedFATInode { + fn read_pagecache(&self, offset: usize, buf: &mut [u8]) -> Result { + let page_cache = self.0.lock().page_cache.clone(); + if let Some(page_cache) = page_cache { + let r = page_cache.lock_irqsave().read(offset, buf); + return r; + } else { + return self.read_sync(offset, buf); + } + } + + fn write_pagecache(&self, offset: usize, buf: &[u8]) -> Result { + let page_cache = self.0.lock().page_cache.clone(); + if let Some(page_cache) = page_cache { + let write_len = page_cache.lock_irqsave().write(offset, buf)?; + let mut guard = self.0.lock(); + let old_size = guard.metadata.size; + guard.update_metadata(Some(core::cmp::max(old_size, (offset + write_len) as i64))); + return Ok(write_len); + } else { + return self.write_sync(offset, buf); + } + } +} + impl IndexNode for LockedFATInode { fn read_sync(&self, offset: usize, buf: &mut [u8]) -> Result { let guard: SpinLockGuard = self.0.lock(); @@ -1590,6 +1639,14 @@ impl IndexNode for LockedFATInode { return r; } + fn sync(&self) -> Result<(), SystemError> { + let page_cache = self.0.lock().page_cache.clone(); + if let Some(page_cache) = page_cache { + return page_cache.lock_irqsave().sync(); + } + Ok(()) + } + fn create( &self, name: &str, @@ -1624,6 +1681,30 @@ impl IndexNode for LockedFATInode { } } + fn link(&self, name: &str, other: &Arc) -> Result<(), SystemError> { + // fat32 does not support hard link + let ty = other.metadata()?.file_type; + let mode = other.metadata()?.mode; + let new_inode = self.create(name, ty, mode).unwrap(); + let mut offset = 0; + let mut buf = [0u8; 512]; + loop { + let read_len = other.read_sync(offset, &mut buf).unwrap(); + if read_len == 0 { + break; + } + log::debug!("FATFS(link): read_len={read_len}, offset={offset}"); + let write_len = new_inode.write_sync(offset, &buf[0..read_len]).unwrap(); + if write_len < read_len { + error!("FATFS: write link file failed, read_len={read_len}, write_len={write_len}"); + return Err(SystemError::EIO); + } + offset += write_len; + } + log::debug!("FATFS: link file {name} success, size={}", offset); + Ok(()) + } + fn fs(&self) -> Arc { return self.0.lock().fs.upgrade().unwrap(); } diff --git a/kernel/src/filesystem/page_cache.rs b/kernel/src/filesystem/page_cache.rs index b77e3fdfa..4d8bd4a51 100644 --- a/kernel/src/filesystem/page_cache.rs +++ b/kernel/src/filesystem/page_cache.rs @@ -313,6 +313,17 @@ impl InnerPageCache { Ok(()) } + /// Synchronize the page cache with the storage device. + pub fn sync(&mut self) -> Result<(), SystemError> { + for page in self.pages.values() { + let mut guard = page.write_irqsave(); + if guard.flags().contains(PageFlags::PG_DIRTY) { + crate::mm::page::PageReclaimer::page_writeback(&mut guard, false); + } + } + Ok(()) + } + pub fn pages_count(&self) -> usize { return self.pages.len(); } diff --git a/kernel/src/filesystem/poll.rs b/kernel/src/filesystem/poll.rs index 6e77e7317..8bf461382 100644 --- a/kernel/src/filesystem/poll.rs +++ b/kernel/src/filesystem/poll.rs @@ -164,7 +164,10 @@ impl Syscall { } } -fn do_sys_poll(poll_fds: &mut [PollFd], timeout: Option) -> Result { +pub fn do_sys_poll( + poll_fds: &mut [PollFd], + timeout: Option, +) -> Result { let ep_file = EventPoll::create_epoll_file(FileMode::empty())?; let ep_file = Arc::new(ep_file); @@ -177,7 +180,7 @@ fn do_sys_poll(poll_fds: &mut [PollFd], timeout: Option) -> Result Option { +pub fn poll_select_set_timeout(timeout_ms: u64) -> Option { Some(Instant::now() + Duration::from_millis(timeout_ms)) } diff --git a/kernel/src/filesystem/procfs/mod.rs b/kernel/src/filesystem/procfs/mod.rs index 3e7fd4792..e052e7cff 100644 --- a/kernel/src/filesystem/procfs/mod.rs +++ b/kernel/src/filesystem/procfs/mod.rs @@ -41,7 +41,7 @@ mod syscall; /// @brief 进程文件类型 /// @usage 用于定义进程文件夹下的各类文件类型 -#[derive(Debug)] +#[derive(Clone, Copy, Debug, PartialEq, Eq)] #[repr(u8)] pub enum ProcFileType { ///展示进程状态信息 @@ -52,6 +52,9 @@ pub enum ProcFileType { ProcKmsg = 2, /// 可执行路径 ProcExe = 3, + ProcSelf = 4, + ProcFdDir = 5, + ProcFdFile = 6, //todo: 其他文件类型 ///默认文件类型 Default, @@ -64,6 +67,9 @@ impl From for ProcFileType { 1 => ProcFileType::ProcMeminfo, 2 => ProcFileType::ProcKmsg, 3 => ProcFileType::ProcExe, + 4 => ProcFileType::ProcSelf, + 5 => ProcFileType::ProcFdDir, + 6 => ProcFileType::ProcFdFile, _ => ProcFileType::Default, } } @@ -72,11 +78,13 @@ impl From for ProcFileType { /// @usage 用于传入各类文件所需的信息 #[derive(Debug)] pub struct InodeInfo { - ///进程的pid + /// 进程的pid pid: Pid, - ///文件类型 + /// 文件类型 ftype: ProcFileType, - //其他需要传入的信息在此定义 + /// 文件描述符 + fd: i32, + // 其他需要传入的信息在此定义 } /// @brief procfs的inode名称的最大长度 @@ -275,8 +283,13 @@ impl ProcFSInode { return Ok(0); } + fn open_self(&self, _pdata: &mut ProcfsFilePrivateData) -> Result { + let pid = ProcessManager::current_pid().data(); + return Ok(pid.to_string().as_bytes().len() as _); + } + // 读取exe文件 - fn read_link(&self, buf: &mut [u8]) -> Result { + fn read_exe_link(&self, buf: &mut [u8]) -> Result { // 判断是否有记录pid信息,有的话就是当前进程的exe文件,没有则是当前进程的exe文件 let pid = self.fdata.pid; let pcb = if pid == Pid::from(0) { @@ -291,6 +304,31 @@ impl ProcFSInode { Ok(len) } + /// read current task pid dynamically + fn read_self_link(&self, buf: &mut [u8]) -> Result { + let pid = ProcessManager::current_pid().data(); + let pid_bytes = pid.to_string(); + let len = pid_bytes.len().min(buf.len()); + buf[..len].copy_from_slice(&pid_bytes.as_bytes()[..len]); + Ok(len) + } + + fn read_fd_link(&self, buf: &mut [u8]) -> Result { + let fd = self.fdata.fd; + let fd_table = ProcessManager::current_pcb().fd_table(); + let fd_table = fd_table.read(); + let file = fd_table.get_file_by_fd(fd); + if let Some(file) = file { + let inode = file.inode(); + let path = inode.absolute_path().unwrap(); + let len = path.len().min(buf.len()); + buf[..len].copy_from_slice(&path.as_bytes()[..len]); + Ok(len) + } else { + return Err(SystemError::EBADF); + } + } + /// proc文件系统读取函数 fn proc_read( &self, @@ -372,6 +410,7 @@ impl ProcFS { fs: Weak::default(), fdata: InodeInfo { pid: Pid::new(0), + fd: 0, ftype: ProcFileType::Default, }, dname: DName::default(), @@ -392,34 +431,31 @@ impl ProcFS { // 创建meminfo文件 let inode = result.root_inode(); - let binding = inode.create( - "meminfo", - FileType::File, - ModeType::from_bits_truncate(0o444), - ); - if let Ok(meminfo) = binding { - let meminfo_file = meminfo - .as_any_ref() - .downcast_ref::() - .unwrap(); - meminfo_file.0.lock().fdata.pid = Pid::new(0); - meminfo_file.0.lock().fdata.ftype = ProcFileType::ProcMeminfo; - } else { - panic!("create meminfo error"); - } + let meminfo = inode + .create( + "meminfo", + FileType::File, + ModeType::from_bits_truncate(0o444), + ) + .expect("create meminfo error"); + let meminfo_file = meminfo + .as_any_ref() + .downcast_ref::() + .unwrap(); + meminfo_file.0.lock().fdata.pid = Pid::new(0); + meminfo_file.0.lock().fdata.ftype = ProcFileType::ProcMeminfo; // 创建kmsg文件 - let binding = inode.create("kmsg", FileType::File, ModeType::from_bits_truncate(0o444)); - if let Ok(kmsg) = binding { - let kmsg_file = kmsg - .as_any_ref() - .downcast_ref::() - .unwrap(); - kmsg_file.0.lock().fdata.pid = Pid::new(1); - kmsg_file.0.lock().fdata.ftype = ProcFileType::ProcKmsg; - } else { - panic!("create ksmg error"); - } + let kmsg = inode + .create("kmsg", FileType::File, ModeType::from_bits_truncate(0o444)) + .expect("create kmsg error"); + let kmsg_file = kmsg + .as_any_ref() + .downcast_ref::() + .unwrap(); + kmsg_file.0.lock().fdata.pid = Pid::new(1); + kmsg_file.0.lock().fdata.ftype = ProcFileType::ProcKmsg; + // 这个文件是用来欺骗Aya框架识别内核版本 /* On Ubuntu LINUX_VERSION_CODE doesn't correspond to info.release, * but Ubuntu provides /proc/version_signature file, as described at @@ -431,36 +467,30 @@ impl ProcFS { * In the above, 5.4.8 is what kernel is actually expecting, while * uname() call will return 5.4.0 in info.release. */ - let binding = inode.create("version_signature", FileType::File, ModeType::S_IRUGO); - if let Ok(version_signature) = binding { - let version_signature = version_signature - .as_any_ref() - .downcast_ref::() - .unwrap(); - version_signature.0.lock().fdata.ftype = ProcFileType::Default; - version_signature.0.lock().data = "DragonOS 6.0.0-generic 6.0.0\n" - .to_string() - .as_bytes() - .to_vec(); - } else { - panic!("create version_signature error"); - } - - let self_dir = inode - .create("self", FileType::Dir, ModeType::from_bits_truncate(0o555)) + let version_signature = inode + .create("version_signature", FileType::File, ModeType::S_IRUGO) + .expect("create version_signature error"); + let version_signature = version_signature + .as_any_ref() + .downcast_ref::() .unwrap(); - - let binding = self_dir.create("exe", FileType::SymLink, ModeType::S_IRUGO); - if let Ok(exe) = binding { - let exe_file = exe - .as_any_ref() - .downcast_ref::() - .unwrap(); - exe_file.0.lock().fdata.pid = Pid::new(0); - exe_file.0.lock().fdata.ftype = ProcFileType::ProcExe; - } else { - panic!("create exe error"); - } + version_signature.0.lock().fdata.ftype = ProcFileType::Default; + version_signature.0.lock().data = b"DragonOS 6.0.0-generic 6.0.0\n".to_vec(); + + let self_file = inode + .create_with_data( + "self", + FileType::SymLink, + ModeType::from_bits_truncate(0o555), + 0, + ) + .expect("create self error"); + let self_file = self_file + .as_any_ref() + .downcast_ref::() + .unwrap(); + self_file.0.lock().fdata.pid = Pid::new(2); + self_file.0.lock().fdata.ftype = ProcFileType::ProcSelf; return result; } @@ -469,16 +499,16 @@ impl ProcFS { /// @usage 在进程中调用并创建进程对应文件 pub fn register_pid(&self, pid: Pid) -> Result<(), SystemError> { // 获取当前inode - let inode: Arc = self.root_inode(); + let inode = self.root_inode(); // 创建对应进程文件夹 - let pid_dir: Arc = inode.create( + let pid_dir = inode.create( &pid.to_string(), FileType::Dir, ModeType::from_bits_truncate(0o555), )?; // 创建相关文件 // status文件 - let status_binding: Arc = pid_dir.create( + let status_binding = pid_dir.create( "status", FileType::File, ModeType::from_bits_truncate(0o444), @@ -491,7 +521,7 @@ impl ProcFS { status_file.0.lock().fdata.ftype = ProcFileType::ProcStatus; // exe文件 - let exe_binding: Arc = pid_dir.create_with_data( + let exe_binding = pid_dir.create_with_data( "exe", FileType::SymLink, ModeType::from_bits_truncate(0o444), @@ -504,6 +534,10 @@ impl ProcFS { exe_file.0.lock().fdata.pid = pid; exe_file.0.lock().fdata.ftype = ProcFileType::ProcExe; + // fd dir + let fd = pid_dir.create("fd", FileType::Dir, ModeType::from_bits_truncate(0o555))?; + let fd = fd.as_any_ref().downcast_ref::().unwrap(); + fd.0.lock().fdata.ftype = ProcFileType::ProcFdDir; //todo: 创建其他文件 return Ok(()); @@ -519,6 +553,7 @@ impl ProcFS { // 删除进程文件夹下文件 pid_dir.unlink("status")?; pid_dir.unlink("exe")?; + pid_dir.rmdir("fd")?; // 查看进程文件是否还存在 // let pf= pid_dir.find("status").expect("Cannot find status"); @@ -530,6 +565,43 @@ impl ProcFS { } } +impl LockedProcFSInode { + fn dynamical_find_fd(&self, fd: &str) -> Result, SystemError> { + // ::log::info!("ProcFS: Dynamically opening fd files for current process."); + let fd = fd.parse::().map_err(|_| SystemError::EINVAL)?; + let pcb = ProcessManager::current_pcb(); + let fd_table = pcb.fd_table(); + let fd_table = fd_table.read(); + let file = fd_table.get_file_by_fd(fd); + if file.is_some() { + let _ = self.unlink(&fd.to_string()); + let fd_file = self.create( + &fd.to_string(), + FileType::SymLink, + ModeType::from_bits_truncate(0o444), + )?; + let fd_file_proc = fd_file + .as_any_ref() + .downcast_ref::() + .unwrap(); + fd_file_proc.0.lock().fdata.fd = fd; + fd_file_proc.0.lock().fdata.ftype = ProcFileType::ProcFdFile; + return Ok(fd_file); + } else { + return Err(SystemError::ENOENT); + } + } + + fn dynamical_list_fd(&self) -> Result, SystemError> { + // ::log::info!("ProcFS: Dynamically listing fd files for current process"); + let pcb = ProcessManager::current_pcb(); + let fd_table = pcb.fd_table(); + let fd_table = fd_table.read(); + let res = fd_table.iter().map(|(fd, _)| fd.to_string()).collect(); + return Ok(res); + } +} + impl IndexNode for LockedProcFSInode { fn open( &self, @@ -538,6 +610,7 @@ impl IndexNode for LockedProcFSInode { ) -> Result<(), SystemError> { // 加锁 let mut inode: SpinLockGuard = self.0.lock(); + let proc_ty = inode.fdata.ftype; // 如果inode类型为文件夹,则直接返回成功 if let FileType::Dir = inode.metadata.file_type { @@ -545,22 +618,36 @@ impl IndexNode for LockedProcFSInode { } let mut private_data = ProcfsFilePrivateData::new(); // 根据文件类型获取相应数据 - let file_size = match inode.fdata.ftype { + let file_size = match proc_ty { ProcFileType::ProcStatus => inode.open_status(&mut private_data)?, ProcFileType::ProcMeminfo => inode.open_meminfo(&mut private_data)?, ProcFileType::ProcExe => inode.open_exe(&mut private_data)?, ProcFileType::Default => inode.data.len() as i64, - _ => { - todo!() - } + ProcFileType::ProcSelf => inode.open_self(&mut private_data)?, + _ => 0, }; *data = FilePrivateData::Procfs(private_data); // 更新metadata里面的文件大小数值 inode.metadata.size = file_size; + drop(inode); + return Ok(()); + } + fn rmdir(&self, name: &str) -> Result<(), SystemError> { + let mut guard = self.0.lock(); + if guard.metadata.file_type != FileType::Dir { + return Err(SystemError::ENOTDIR); + } + let name = DName::from(name); + guard.children.remove(&name); return Ok(()); } + fn parent(&self) -> Result, SystemError> { + let parent = self.0.lock().parent.upgrade().ok_or(SystemError::ENOENT)?; + return Ok(parent as Arc); + } + fn close(&self, mut data: SpinLockGuard) -> Result<(), SystemError> { let guard: SpinLockGuard = self.0.lock(); // 如果inode类型为文件夹,则直接返回成功 @@ -584,32 +671,29 @@ impl IndexNode for LockedProcFSInode { return Err(SystemError::EINVAL); } // 加锁 - let inode: SpinLockGuard = self.0.lock(); + let inode = self.0.lock(); // 检查当前inode是否为一个文件夹,如果是的话,就返回错误 if inode.metadata.file_type == FileType::Dir { return Err(SystemError::EISDIR); } - // 获取数据信息 - let mut private_data = match &*data { - FilePrivateData::Procfs(p) => p.clone(), - _ => { - panic!("ProcFS: FilePrivateData mismatch!"); - } - }; - // 根据文件类型读取相应数据 match inode.fdata.ftype { - ProcFileType::ProcStatus => { - return inode.proc_read(offset, len, buf, &mut private_data) - } - ProcFileType::ProcMeminfo => { - return inode.proc_read(offset, len, buf, &mut private_data) + ProcFileType::ProcStatus | ProcFileType::ProcMeminfo => { + // 获取数据信息 + let mut private_data = match &*data { + FilePrivateData::Procfs(p) => p.clone(), + _ => { + panic!("ProcFS: FilePrivateData mismatch!"); + } + }; + return inode.proc_read(offset, len, buf, &mut private_data); } - ProcFileType::ProcExe => return inode.read_link(buf), - ProcFileType::ProcKmsg => (), - ProcFileType::Default => (), + ProcFileType::ProcExe => return inode.read_exe_link(buf), + ProcFileType::ProcSelf => return inode.read_self_link(buf), + ProcFileType::ProcFdFile => return inode.read_fd_link(buf), + _ => (), }; // 默认读取 @@ -648,7 +732,6 @@ impl IndexNode for LockedProcFSInode { fn metadata(&self) -> Result { let inode = self.0.lock(); let metadata = inode.metadata.clone(); - return Ok(metadata); } @@ -721,6 +804,7 @@ impl IndexNode for LockedProcFSInode { fs: inode.fs.clone(), fdata: InodeInfo { pid: Pid::new(0), + fd: 0, ftype: ProcFileType::Default, }, dname: name.clone(), @@ -800,23 +884,32 @@ impl IndexNode for LockedProcFSInode { } fn find(&self, name: &str) -> Result, SystemError> { - let inode = self.0.lock(); - - if inode.metadata.file_type != FileType::Dir { + if self.0.lock().metadata.file_type != FileType::Dir { return Err(SystemError::ENOTDIR); } match name { "" | "." => { - return Ok(inode.self_ref.upgrade().ok_or(SystemError::ENOENT)?); + return Ok(self + .0 + .lock() + .self_ref + .upgrade() + .ok_or(SystemError::ENOENT)?); } ".." => { - return Ok(inode.parent.upgrade().ok_or(SystemError::ENOENT)?); + return Ok(self.0.lock().parent.upgrade().ok_or(SystemError::ENOENT)?); } + name => { + if self.0.lock().fdata.ftype == ProcFileType::ProcFdDir { + return self.dynamical_find_fd(name); + } // 在子目录项中查找 - return Ok(inode + return Ok(self + .0 + .lock() .children .get(&DName::from(name)) .ok_or(SystemError::ENOENT)? @@ -868,9 +961,14 @@ impl IndexNode for LockedProcFSInode { return Err(SystemError::ENOTDIR); } - let mut keys: Vec = Vec::new(); + let mut keys = Vec::new(); keys.push(String::from(".")); keys.push(String::from("..")); + + if self.0.lock().fdata.ftype == ProcFileType::ProcFdDir { + return self.dynamical_list_fd(); + } + keys.append( &mut self .0 diff --git a/kernel/src/filesystem/vfs/fcntl.rs b/kernel/src/filesystem/vfs/fcntl.rs index 5d4985dfa..6cec78cac 100644 --- a/kernel/src/filesystem/vfs/fcntl.rs +++ b/kernel/src/filesystem/vfs/fcntl.rs @@ -23,6 +23,8 @@ pub enum FcntlCommand { SetLock = 6, /// set record locking info (blocking) SetLockWait = 7, + SetOwn = 8, + GetOwn = 9, SetLease = F_LINUX_SPECIFIC_BASE, GetLease = F_LINUX_SPECIFIC_BASE + 1, diff --git a/kernel/src/filesystem/vfs/file.rs b/kernel/src/filesystem/vfs/file.rs index 4dbbb0716..049859ee2 100644 --- a/kernel/src/filesystem/vfs/file.rs +++ b/kernel/src/filesystem/vfs/file.rs @@ -17,7 +17,7 @@ use crate::{ }, ipc::pipe::PipeFsPrivateData, libs::{rwlock::RwLock, spinlock::SpinLock}, - process::{cred::Cred, ProcessManager}, + process::{cred::Cred, Pid, ProcessControlBlock, ProcessManager}, }; /// 文件私有信息的枚举类型 @@ -129,6 +129,8 @@ pub struct File { pub private_data: SpinLock, /// 文件的凭证 cred: Cred, + /// owner + pid: SpinLock>>, } impl File { @@ -145,16 +147,19 @@ impl File { } } + let private_data = SpinLock::new(FilePrivateData::default()); + inode.open(private_data.lock(), &mode)?; + let f = File { inode, offset: AtomicUsize::new(0), mode: RwLock::new(mode), file_type, readdir_subdirs_name: SpinLock::new(Vec::default()), - private_data: SpinLock::new(FilePrivateData::default()), + private_data, cred: ProcessManager::current_pcb().cred(), + pid: SpinLock::new(None), }; - f.inode.open(f.private_data.lock(), &mode)?; return Ok(f); } @@ -407,6 +412,7 @@ impl File { readdir_subdirs_name: SpinLock::new(self.readdir_subdirs_name.lock().clone()), private_data: SpinLock::new(self.private_data.lock().clone()), cred: self.cred.clone(), + pid: SpinLock::new(None), }; // 调用inode的open方法,让inode知道有新的文件打开了这个inode if self @@ -496,6 +502,22 @@ impl File { let private_data = self.private_data.lock(); self.inode.as_pollable_inode()?.poll(&private_data) } + + pub fn owner(&self) -> Option { + self.pid.lock().as_ref().map(|pcb| pcb.pid()) + } + + pub fn set_owner(&self, pid: Option>) -> Result<(), SystemError> { + let Some(pcb) = pid else { + *self.pid.lock() = None; + return Ok(()); + }; + + self.pid.lock().replace(pcb); + // todo: update inode owner + log::error!("set_owner has not been implemented yet"); + Ok(()) + } } impl Drop for File { diff --git a/kernel/src/filesystem/vfs/mod.rs b/kernel/src/filesystem/vfs/mod.rs index 08143c228..11235f592 100644 --- a/kernel/src/filesystem/vfs/mod.rs +++ b/kernel/src/filesystem/vfs/mod.rs @@ -165,6 +165,7 @@ pub trait IndexNode: Any + Sync + Send + Debug + CastFromSync { } fn read_sync(&self, _offset: usize, _buf: &mut [u8]) -> Result { + log::warn!("read_sync is not supported: {:?}", self.fs()); return Err(SystemError::ENOSYS); } @@ -351,6 +352,7 @@ pub trait IndexNode: Any + Sync + Send + Debug + CastFromSync { /// 失败:Err(错误码) fn link(&self, _name: &str, _other: &Arc) -> Result<(), SystemError> { // 若文件系统没有实现此方法,则返回“不支持” + log::warn!("link is not supported: {:?}", self.fs()); return Err(SystemError::ENOSYS); } @@ -528,25 +530,17 @@ pub trait IndexNode: Any + Sync + Send + Debug + CastFromSync { return Err(SystemError::ENOSYS); } - /// # absolute_path 获取目录项绝对路径 + /// Returns the absolute path of the inode. /// - /// ## 参数 - /// - /// 无 - /// - /// ## 返回值 + /// This function only works for `MountFS` and should not be implemented by other file systems. + /// The performance of this function is O(n) for path queries, and it is extremely + /// inefficient in file systems that do not implement DName caching. /// - /// - Ok(String): 路径 - /// - Err(SystemError): 文件系统不支持dname parent api + /// **WARNING** /// - /// ## Behavior + /// For special inodes(e.g., sockets,pipes, etc.), this function will + /// return an special name according to the inode type directly. /// - /// 该函数只能被MountFS实现,其他文件系统不应实现这个函数 - /// - /// # Performance - /// - /// 这是一个O(n)的路径查询,并且在未实现DName缓存的文件系统中,性能极差; - /// 即使实现了DName也尽量不要用。 fn absolute_path(&self) -> Result { return Err(SystemError::ENOSYS); } @@ -560,7 +554,11 @@ pub trait IndexNode: Any + Sync + Send + Debug + CastFromSync { /// @brief 将当前inode的内容同步到具体设备上 fn sync(&self) -> Result<(), SystemError> { - return Ok(()); + let page_cache = self.page_cache(); + if let Some(page_cache) = page_cache { + return page_cache.lock_irqsave().sync(); + } + Ok(()) } /// ## 创建一个特殊文件节点 @@ -768,7 +766,7 @@ impl dyn IndexNode { if file_type == FileType::SymLink && max_follow_times > 0 { let mut content = [0u8; 256]; // 读取符号链接 - + // TODO:We need to clarify which interfaces require private data and which do not let len = inode.read_at( 0, 256, diff --git a/kernel/src/filesystem/vfs/mount.rs b/kernel/src/filesystem/vfs/mount.rs index 5a41483ab..91bf8860c 100644 --- a/kernel/src/filesystem/vfs/mount.rs +++ b/kernel/src/filesystem/vfs/mount.rs @@ -178,6 +178,17 @@ impl MountFSInode { } pub(super) fn do_parent(&self) -> Result, SystemError> { + let fs = self.inner_inode.fs(); + let name = fs.name(); + let root_id = self.inner_inode.fs().root_inode().metadata()?.inode_id; + let self_id = self.inner_inode.metadata()?.inode_id; + log::error!( + "MountFSInode::do_parent: fs={}, root_id={:?}, self_id={:?}", + name, + root_id, + self_id + ); + if self.is_mountpoint_root()? { // 当前inode是它所在的文件系统的root inode match &self.mount_fs.self_mountpoint { @@ -194,7 +205,12 @@ impl MountFSInode { } } } else { - let inner_inode = self.inner_inode.parent()?; + let inner_inode = self.inner_inode.parent().unwrap(); + log::error!( + "MountFSInode::do_parent: inner_inode={:?}, mount_fs={:?}", + inner_inode.metadata()?.inode_id, + inner_inode.fs().name() + ); // 向上查找时,不会跨过文件系统的边界,因此直接调用当前inode所在的文件系统的find方法进行查找 return Ok(Arc::new_cyclic(|self_ref| MountFSInode { inner_inode, @@ -218,11 +234,22 @@ impl MountFSInode { } fn do_absolute_path(&self) -> Result { - let mut path_parts = Vec::new(); let mut current = self.self_ref.upgrade().unwrap(); + // For special inode, we can directly get the absolute path + if let Ok(p) = current.inner_inode.absolute_path() { + return Ok(p); + } + log::error!("ROOT ID: {:?}", ROOT_INODE().metadata()?.inode_id); + let mut path_parts = Vec::new(); + while current.metadata()?.inode_id != ROOT_INODE().metadata()?.inode_id { let name = current.dname()?; + log::error!( + "MountFSInode::do_absolute_path: name={:?}, ID: {:?}", + name, + current.metadata()?.inode_id + ); path_parts.push(name.0); current = current.do_parent()?; } @@ -253,7 +280,16 @@ impl IndexNode for MountFSInode { } fn close(&self, data: SpinLockGuard) -> Result<(), SystemError> { - return self.inner_inode.close(data); + self.inner_inode.close(data).unwrap(); + Ok(()) + } + + fn read_sync(&self, offset: usize, buf: &mut [u8]) -> Result { + self.inner_inode.read_sync(offset, buf) + } + + fn write_sync(&self, offset: usize, buf: &[u8]) -> Result { + self.inner_inode.write_sync(offset, buf) } fn create_with_data( @@ -479,15 +515,30 @@ impl IndexNode for MountFSInode { if self.is_mountpoint_root()? { return Err(SystemError::EBUSY); } + // WARNING: we can't get absolute_path from self, + // because new ROOT has not been set yet. + let path = from.absolute_path()?; + // debug!("from {:?}, to {:?}", from, self); - let new_mount_fs = from.umount()?; + let old_mount_fs = from.umount()?; + + // WARNING: We need to recreate the MountFS with the new mount point + let new_mount_fs = MountFS::new( + old_mount_fs.inner_filesystem.clone(), + Some(self.self_ref.upgrade().unwrap()), + ); + + // remember the mount point infomation + // replace current mountpoints with a new BTreeMap + // *new_mount_fs.mountpoints.lock() = old_mount_fs.mountpoints.lock().clone(); + self.mount_fs .mountpoints .lock() .insert(metadata.inode_id, new_mount_fs.clone()); - // MOUNT_LIST().remove(from.absolute_path()?); - // MOUNT_LIST().insert(self.absolute_path()?, new_mount_fs.clone()); + // update MOUNT_LIST + MOUNT_LIST().insert(path, new_mount_fs.clone()); return Ok(new_mount_fs); } diff --git a/kernel/src/filesystem/vfs/open.rs b/kernel/src/filesystem/vfs/open.rs index a1b1994ff..c53d55d7c 100644 --- a/kernel/src/filesystem/vfs/open.rs +++ b/kernel/src/filesystem/vfs/open.rs @@ -171,14 +171,8 @@ fn do_sys_openat2( let path = path.trim(); let (inode_begin, path) = user_path_at(&ProcessManager::current_pcb(), dirfd, path)?; - let inode: Result, SystemError> = inode_begin.lookup_follow_symlink( - &path, - if follow_symlink { - VFS_MAX_FOLLOW_SYMLINK_TIMES - } else { - 0 - }, - ); + let inode = + inode_begin.lookup_follow_symlink2(&path, VFS_MAX_FOLLOW_SYMLINK_TIMES, follow_symlink); let inode: Arc = match inode { Ok(inode) => inode, diff --git a/kernel/src/filesystem/vfs/syscall/mod.rs b/kernel/src/filesystem/vfs/syscall/mod.rs index 3d8879f16..8f7c7296d 100644 --- a/kernel/src/filesystem/vfs/syscall/mod.rs +++ b/kernel/src/filesystem/vfs/syscall/mod.rs @@ -1,4 +1,5 @@ use crate::filesystem::vfs::FilldirContext; +use crate::process::Pid; use core::mem::size_of; use alloc::{string::String, sync::Arc, vec::Vec}; @@ -60,9 +61,13 @@ pub mod sys_mount; pub mod sys_umount2; pub mod symlink_utils; +mod sys_fsync; +mod sys_pselect6; +mod sys_select; #[cfg(target_arch = "x86_64")] mod sys_symlink; mod sys_symlinkat; +mod sys_sendfile; pub const SEEK_SET: u32 = 0; pub const SEEK_CUR: u32 = 1; @@ -452,6 +457,14 @@ impl Syscall { let open_flags: FileMode = FileMode::from_bits(o_flags).ok_or(SystemError::EINVAL)?; let mode = ModeType::from_bits(mode).ok_or(SystemError::EINVAL)?; + if crate::syscall::DFLAG.load(core::sync::atomic::Ordering::Relaxed) { + log::info!( + "openat: path: {}, o_flags: {:?}, mode: {:?}", + path, + open_flags, + mode + ); + } return do_sys_open(dirfd, &path, open_flags, mode, follow_symlink); } @@ -719,7 +732,6 @@ impl Syscall { /// - 'new': 新文件将创建的路径 /// - 'flags': 标志位,仅以位或方式包含AT_EMPTY_PATH和AT_SYMLINK_FOLLOW /// - /// pub fn do_linkat( oldfd: i32, old: &str, @@ -769,6 +781,7 @@ impl Syscall { let new_parent = new_begin_inode.lookup_follow_symlink(new_parent_path.unwrap_or("/"), symlink_times)?; + log::debug!("find new parent: {:?}", new_parent_path); // 被调用者利用downcast_ref判断两inode是否为同一文件系统 return new_parent.link(new_name, &old_inode).map(|_| 0); } @@ -789,13 +802,15 @@ impl Syscall { }; let old = get_path(old)?; let new = get_path(new)?; - return Self::do_linkat( + let res = Self::do_linkat( AtFlags::AT_FDCWD.bits(), &old, AtFlags::AT_FDCWD.bits(), &new, AtFlags::empty(), ); + log::debug!("link old: {}, new: {}, res: {:?}", old, new, res); + res } pub fn linkat( @@ -909,11 +924,18 @@ impl Syscall { if filename_from.len() > MAX_PATHLEN || filename_to.len() > MAX_PATHLEN { return Err(SystemError::ENAMETOOLONG); } - + log::debug!( + "renameat2: oldfd: {}, filename_from: {}, newfd: {}, filename_to: {}", + oldfd, + filename_from, + newfd, + filename_to + ); //获取pcb,文件节点 let pcb = ProcessManager::current_pcb(); let (_old_inode_begin, old_remain_path) = user_path_at(&pcb, oldfd, &filename_from)?; let (_new_inode_begin, new_remain_path) = user_path_at(&pcb, newfd, &filename_to)?; + //获取父目录 let (old_filename, old_parent_path) = rsplit_path(&old_remain_path); let old_parent_inode = ROOT_INODE() @@ -1123,12 +1145,43 @@ impl Syscall { return Err(SystemError::EBADF); } + FcntlCommand::SetOwn => { + let pid = arg.unsigned_abs(); + if pid > i32::MAX as u32 { + return Err(SystemError::EINVAL); + } + let pb = if pid == 0 { + None + } else { + let pb = ProcessManager::find(Pid::from(pid as _)).ok_or(SystemError::ESRCH)?; + Some(pb) + }; + let binding = ProcessManager::current_pcb().fd_table(); + let file = binding + .read() + .get_file_by_fd(fd) + .ok_or(SystemError::EBADF)?; + file.set_owner(pb)?; + Ok(0) + } + + FcntlCommand::GetOwn => { + let binding = ProcessManager::current_pcb().fd_table(); + let file = binding + .read() + .get_file_by_fd(fd) + .ok_or(SystemError::EBADF)?; + let owner = file.owner().unwrap_or(Pid::from(0)); + + return Ok(owner.data()); + } _ => { // TODO: unimplemented // 未实现的命令,返回0,不报错。 warn!("fcntl: unimplemented command: {:?}, defaults to 0.", cmd); - return Err(SystemError::ENOSYS); + // return Err(SystemError::ENOSYS); + return Ok(0); } } } @@ -1283,7 +1336,11 @@ impl Syscall { let (inode, path) = user_path_at(&ProcessManager::current_pcb(), dirfd, path)?; - let inode = inode.lookup(path.as_str())?; + log::info!("readlink_at: dirfd: {}, path: {}", dirfd, path); + let inode = inode + .lookup_follow_symlink2(path.as_str(), VFS_MAX_FOLLOW_SYMLINK_TIMES, false) + .unwrap(); + if inode.metadata()?.file_type != FileType::SymLink { return Err(SystemError::EINVAL); } diff --git a/kernel/src/filesystem/vfs/syscall/open_utils.rs b/kernel/src/filesystem/vfs/syscall/open_utils.rs index d0076674d..7b068ebb4 100644 --- a/kernel/src/filesystem/vfs/syscall/open_utils.rs +++ b/kernel/src/filesystem/vfs/syscall/open_utils.rs @@ -31,6 +31,15 @@ pub(super) fn do_open( let open_flags: FileMode = FileMode::from_bits(o_flags).ok_or(SystemError::EINVAL)?; let mode = ModeType::from_bits(mode).ok_or(SystemError::EINVAL)?; + if crate::syscall::DFLAG.load(core::sync::atomic::Ordering::Relaxed) { + log::info!( + "openat: path: {}, o_flags: {:?}, mode: {:?}, follow_symlink: {}", + path, + open_flags, + mode, + follow_symlink + ); + } return do_sys_open( AtFlags::AT_FDCWD.bits(), &path, diff --git a/kernel/src/filesystem/vfs/syscall/sys_copy_file_range.rs b/kernel/src/filesystem/vfs/syscall/sys_copy_file_range.rs new file mode 100644 index 000000000..6aaeacced --- /dev/null +++ b/kernel/src/filesystem/vfs/syscall/sys_copy_file_range.rs @@ -0,0 +1 @@ +// SYS_COPY_FILE_RANGE \ No newline at end of file diff --git a/kernel/src/filesystem/vfs/syscall/sys_fsync.rs b/kernel/src/filesystem/vfs/syscall/sys_fsync.rs new file mode 100644 index 000000000..b0c7835ef --- /dev/null +++ b/kernel/src/filesystem/vfs/syscall/sys_fsync.rs @@ -0,0 +1,42 @@ +use alloc::string::ToString; + +use alloc::vec::Vec; + +use crate::{ + arch::{interrupt::TrapFrame, syscall::nr::SYS_FSYNC}, + syscall::table::{FormattedSyscallParam, Syscall}, +}; + +/// synchronize a file's in-core state with storagedevice. +/// +/// See https://man7.org/linux/man-pages/man2/fsync.2.html +pub struct SysFsyncHandle; + +impl Syscall for SysFsyncHandle { + fn num_args(&self) -> usize { + 1 + } + + fn handle( + &self, + args: &[usize], + _frame: &mut TrapFrame, + ) -> Result { + let fd = args[0] as i32; + let binding = crate::process::ProcessManager::current_pcb().fd_table(); + let fd_table_guard = binding.read(); + let file = fd_table_guard + .get_file_by_fd(fd) + .ok_or(system_error::SystemError::EBADF)?; + drop(fd_table_guard); + let inode = file.inode(); + inode.sync()?; + Ok(0) + } + + fn entry_format(&self, args: &[usize]) -> Vec { + vec![FormattedSyscallParam::new("fd", args[0].to_string())] + } +} + +syscall_table_macros::declare_syscall!(SYS_FSYNC, SysFsyncHandle); diff --git a/kernel/src/filesystem/vfs/syscall/sys_pselect6.rs b/kernel/src/filesystem/vfs/syscall/sys_pselect6.rs new file mode 100644 index 000000000..34ae126a9 --- /dev/null +++ b/kernel/src/filesystem/vfs/syscall/sys_pselect6.rs @@ -0,0 +1,51 @@ +use alloc::vec::Vec; + +use system_error::SystemError; + +use crate::{ + arch::{ipc::signal::SigSet, syscall::nr::SYS_PSELECT6}, + filesystem::vfs::syscall::sys_select::common_sys_select, + ipc::signal::set_user_sigmask, + syscall::{ + table::{FormattedSyscallParam, Syscall}, + user_access::UserBufferReader, + }, +}; + +pub struct SysPselect6; +impl Syscall for SysPselect6 { + fn num_args(&self) -> usize { + 6 + } + + fn handle( + &self, + args: &[usize], + _frame: &mut crate::arch::interrupt::TrapFrame, + ) -> Result { + let sigmask_ptr = args[5]; + let mut sigmask: Option = None; + if sigmask_ptr != 0 { + let sigmask_reader = + UserBufferReader::new(sigmask_ptr as *const SigSet, size_of::(), true)?; + sigmask.replace(*sigmask_reader.read_one_from_user(0)?); + } + if let Some(mut sigmask) = sigmask { + set_user_sigmask(&mut sigmask); + } + common_sys_select(args[0], args[1], args[2], args[3], args[4]) + } + + fn entry_format(&self, args: &[usize]) -> Vec { + vec![ + FormattedSyscallParam::new("nfds", format!("{}", args[0])), + FormattedSyscallParam::new("readfds", format!("{:#x}", args[1])), + FormattedSyscallParam::new("writefds", format!("{:#x}", args[2])), + FormattedSyscallParam::new("exceptfds", format!("{:#x}", args[3])), + FormattedSyscallParam::new("timeout", format!("{:#x}", args[4])), + FormattedSyscallParam::new("sigmask", format!("{:#x}", args[5])), + ] + } +} + +syscall_table_macros::declare_syscall!(SYS_PSELECT6, SysPselect6); diff --git a/kernel/src/filesystem/vfs/syscall/sys_select.rs b/kernel/src/filesystem/vfs/syscall/sys_select.rs new file mode 100644 index 000000000..bbb2ebbc5 --- /dev/null +++ b/kernel/src/filesystem/vfs/syscall/sys_select.rs @@ -0,0 +1,285 @@ +//! Reference https://github.com/asterinas/asterinas/blob/main/kernel/src/syscall/select.rs +use alloc::vec::Vec; +use system_error::SystemError; + +#[cfg(target_arch = "x86_64")] +use crate::arch::syscall::nr::SYS_SELECT; +use crate::{ + filesystem::{ + epoll::EPollEventType, + poll::{do_sys_poll, poll_select_set_timeout, PollFd}, + }, + syscall::{ + table::{FormattedSyscallParam, Syscall}, + user_access::{UserBufferReader, UserBufferWriter}, + }, + time::{syscall::PosixTimeval, Instant}, +}; +// Maximum number of file descriptors in a set +const FD_SETSIZE: usize = 1024; +const USIZE_BITS: usize = core::mem::size_of::() * 8; +/// See https://man7.org/linux/man-pages/man2/select.2.html +pub struct SysSelect; + +impl Syscall for SysSelect { + fn num_args(&self) -> usize { + 5 + } + + fn handle( + &self, + args: &[usize], + _frame: &mut crate::arch::interrupt::TrapFrame, + ) -> Result { + common_sys_select(args[0], args[1], args[2], args[3], args[4]) + } + + fn entry_format(&self, args: &[usize]) -> Vec { + vec![ + FormattedSyscallParam::new("nfds", format!("{}", args[0])), + FormattedSyscallParam::new("readfds", format!("{:#x}", args[1])), + FormattedSyscallParam::new("writefds", format!("{:#x}", args[2])), + FormattedSyscallParam::new("exceptfds", format!("{:#x}", args[3])), + FormattedSyscallParam::new("timeout", format!("{:#x}", args[4])), + ] + } +} + +pub fn common_sys_select( + nfds: usize, + readfds_addr: usize, + writefds_addr: usize, + exceptfds_addr: usize, + timeout_ptr: usize, +) -> Result { + // log::debug!( + // "common_sys_select called with nfds = {}, readfds_addr = {:#x}, writefds_addr = {:#x}, exceptfds_addr = {:#x}, timeout_ptr = {:#x}", + // nfds, readfds_addr, writefds_addr, exceptfds_addr, timeout_ptr + // ); + let mut timeout: Option = None; + if timeout_ptr != 0 { + let tsreader = UserBufferReader::new( + timeout_ptr as *const PosixTimeval, + size_of::(), + true, + )?; + let ts = *tsreader.read_one_from_user::(0)?; + let timeout_ms = ts.tv_sec * 1000 + ts.tv_usec as i64 / 1000; + if timeout_ms >= 0 { + timeout = poll_select_set_timeout(timeout_ms as u64); + } + } + do_sys_select( + nfds as isize, + readfds_addr as *const FdSet, + writefds_addr as *const FdSet, + exceptfds_addr as *const FdSet, + timeout, + ) +} + +fn do_sys_select( + nfds: isize, + readfds_addr: *const FdSet, + writefds_addr: *const FdSet, + exceptfds_addr: *const FdSet, + timeout: Option, +) -> Result { + if nfds < 0 || nfds as usize > FD_SETSIZE { + return Err(SystemError::EINVAL); + } + let get_fdset = |fdset_addr: *const FdSet| -> Result, SystemError> { + let fdset = if fdset_addr.is_null() { + None + } else { + let fdset_buf = UserBufferReader::new(fdset_addr, size_of::(), true)?; + let fdset = *fdset_buf.read_one_from_user::(0)?; + Some(fdset) + }; + Ok(fdset) + }; + let mut readfds = get_fdset(readfds_addr)?; + let mut writefds = get_fdset(writefds_addr)?; + let mut exceptfds = get_fdset(exceptfds_addr)?; + + // log::debug!( + // "nfds = {}, readfds = {:?}, writefds = {:?}, exceptfds = {:?}, timeout = {:?}", + // nfds, + // readfds, + // writefds, + // exceptfds, + // timeout + // ); + + let num_revents = do_select( + nfds as usize, + readfds.as_mut(), + writefds.as_mut(), + exceptfds.as_mut(), + timeout, + )?; + + let set_fdset = |fdset_addr: *const FdSet, fdset: Option| -> Result<(), SystemError> { + if let Some(fdset) = fdset { + let mut fdset_buf = + UserBufferWriter::new(fdset_addr as *mut FdSet, size_of::(), true)?; + fdset_buf.copy_one_to_user(&fdset, 0)?; + } + Ok(()) + }; + + set_fdset(readfds_addr, readfds)?; + set_fdset(writefds_addr, writefds)?; + set_fdset(exceptfds_addr, exceptfds)?; + + // log::info!("num_revents = {}", num_revents); + Ok(num_revents) +} + +fn do_select( + nfds: usize, + mut readfds: Option<&mut FdSet>, + mut writefds: Option<&mut FdSet>, + mut exceptfds: Option<&mut FdSet>, + timeout: Option, +) -> Result { + let mut poll_fds = { + let mut poll_fds = Vec::with_capacity(nfds); + for fd in 0..nfds { + let events = { + let readable = readfds.as_ref().is_some_and(|fds| fds.is_set(fd)); + let writable = writefds.as_ref().is_some_and(|fds| fds.is_set(fd)); + let except = exceptfds.as_ref().is_some_and(|fds| fds.is_set(fd)); + convert_rwe_to_events(readable, writable, except) + }; + + if events.is_empty() { + continue; + } + + let poll_fd = PollFd { + fd: fd as i32, + events: events.bits() as _, + revents: 0, + }; + poll_fds.push(poll_fd); + } + poll_fds + }; + if let Some(fds) = readfds.as_mut() { + fds.clear(); + } + if let Some(fds) = writefds.as_mut() { + fds.clear(); + } + if let Some(fds) = exceptfds.as_mut() { + fds.clear(); + } + + // call the underlying poll syscall + let num_revents = do_sys_poll(&mut poll_fds, timeout)?; + if num_revents == 0 { + return Ok(0); + } + + let mut total_revents = 0; + for poll_fd in &poll_fds { + let fd = poll_fd.fd as usize; + let revents = poll_fd.revents; + let revents = EPollEventType::from_bits_truncate(revents as u32); + let (readable, writable, except) = convert_events_to_rwe(revents)?; + if let Some(ref mut fds) = readfds + && readable + { + fds.set(fd)?; + total_revents += 1; + } + if let Some(ref mut fds) = writefds + && writable + { + fds.set(fd)?; + total_revents += 1; + } + if let Some(ref mut fds) = exceptfds + && except + { + fds.set(fd)?; + total_revents += 1; + } + } + Ok(total_revents) +} + +/// Converts `select` RWE input to `poll` I/O event input +/// according to Linux's behavior. +fn convert_rwe_to_events(readable: bool, writable: bool, except: bool) -> EPollEventType { + let mut events = EPollEventType::empty(); + if readable { + events |= EPollEventType::EPOLLIN; + } + if writable { + events |= EPollEventType::EPOLLOUT; + } + if except { + events |= EPollEventType::EPOLLPRI; + } + events +} + +/// Converts `poll` I/O event results to `select` RWE results +/// according to Linux's behavior. +fn convert_events_to_rwe(events: EPollEventType) -> Result<(bool, bool, bool), SystemError> { + if events.contains(EPollEventType::EPOLLNVAL) { + return Err(SystemError::EBADF); + } + + let readable = events + .intersects(EPollEventType::EPOLLIN | EPollEventType::EPOLLHUP | EPollEventType::EPOLLERR); + let writable = events.intersects(EPollEventType::EPOLLOUT | EPollEventType::EPOLLERR); + let except = events.contains(EPollEventType::EPOLLPRI); + Ok((readable, writable, except)) +} + +#[derive(Debug, Clone, Copy)] +#[repr(C)] +struct FdSet { + fds_bits: [usize; FD_SETSIZE / USIZE_BITS], +} + +impl FdSet { + /// Equivalent to FD_SET. + pub fn set(&mut self, fd: usize) -> Result<(), SystemError> { + if fd >= FD_SETSIZE { + return Err(SystemError::EINVAL); + } + self.fds_bits[fd / USIZE_BITS] |= 1 << (fd % USIZE_BITS); + Ok(()) + } + + /// Equivalent to FD_CLR. + #[expect(unused)] + pub fn unset(&mut self, fd: usize) -> Result<(), SystemError> { + if fd >= FD_SETSIZE { + return Err(SystemError::EINVAL); + } + self.fds_bits[fd / USIZE_BITS] &= !(1 << (fd % USIZE_BITS)); + Ok(()) + } + + /// Equivalent to FD_ISSET. + pub fn is_set(&self, fd: usize) -> bool { + if fd >= FD_SETSIZE { + return false; + } + (self.fds_bits[fd / USIZE_BITS] & (1 << (fd % USIZE_BITS))) != 0 + } + + /// Equivalent to FD_ZERO. + pub fn clear(&mut self) { + for slot in self.fds_bits.iter_mut() { + *slot = 0; + } + } +} +#[cfg(target_arch = "x86_64")] +syscall_table_macros::declare_syscall!(SYS_SELECT, SysSelect); diff --git a/kernel/src/filesystem/vfs/syscall/sys_sendfile.rs b/kernel/src/filesystem/vfs/syscall/sys_sendfile.rs new file mode 100644 index 000000000..8b1378917 --- /dev/null +++ b/kernel/src/filesystem/vfs/syscall/sys_sendfile.rs @@ -0,0 +1 @@ + diff --git a/kernel/src/filesystem/vfs/vcore.rs b/kernel/src/filesystem/vfs/vcore.rs index 5f9531020..5f99d7f00 100644 --- a/kernel/src/filesystem/vfs/vcore.rs +++ b/kernel/src/filesystem/vfs/vcore.rs @@ -9,6 +9,7 @@ use crate::{ driver::base::block::{gendisk::GenDisk, manager::block_dev_manager}, filesystem::{ devfs::devfs_init, + devpts::devpts_init, fat::fs::FATFileSystem, procfs::procfs_init, ramfs::RamFS, @@ -61,7 +62,7 @@ pub fn vfs_init() -> Result<(), SystemError> { let root_inode = mount_fs.root_inode(); init_mountlist(); unsafe { - __ROOT_INODE = Some(root_inode.clone()); + __ROOT_INODE = Some(root_inode); } procfs_init().expect("Failed to initialize procfs"); @@ -113,6 +114,9 @@ fn migrate_virtual_filesystem(new_fs: Arc) -> Result<(), SystemE drop(old_root_inode); } + // WARNING: mount devpts after devfs has been mounted, + devpts_init().expect("Failed to initialize devpts"); + info!("VFS: Migrate filesystems done!"); return Ok(()); diff --git a/kernel/src/init/initcall.rs b/kernel/src/init/initcall.rs index a9e25690d..720f5292e 100644 --- a/kernel/src/init/initcall.rs +++ b/kernel/src/init/initcall.rs @@ -6,7 +6,6 @@ define_public_unified_initializer_slice!(INITCALL_CORE); define_public_unified_initializer_slice!(INITCALL_POSTCORE); define_public_unified_initializer_slice!(INITCALL_ARCH); define_public_unified_initializer_slice!(INITCALL_SUBSYS); -define_public_unified_initializer_slice!(INITCALL_FS); define_public_unified_initializer_slice!(INITCALL_ROOTFS); define_public_unified_initializer_slice!(INITCALL_DEVICE); define_public_unified_initializer_slice!(INITCALL_LATE); @@ -17,7 +16,6 @@ pub fn do_initcalls() -> Result<(), SystemError> { unified_init!(INITCALL_POSTCORE); unified_init!(INITCALL_ARCH); unified_init!(INITCALL_SUBSYS); - unified_init!(INITCALL_FS); unified_init!(INITCALL_ROOTFS); unified_init!(INITCALL_DEVICE); unified_init!(INITCALL_LATE); diff --git a/kernel/src/init/initial_kthread.rs b/kernel/src/init/initial_kthread.rs index 74001ef51..b83c7c84b 100644 --- a/kernel/src/init/initial_kthread.rs +++ b/kernel/src/init/initial_kthread.rs @@ -2,7 +2,7 @@ use core::sync::atomic::{compiler_fence, Ordering}; -use alloc::{ffi::CString, string::ToString}; +use alloc::ffi::CString; use log::{debug, error}; use system_error::SystemError; @@ -45,6 +45,11 @@ fn kernel_init() -> Result<(), SystemError> { .ok(); mount_root_fs().expect("Failed to mount root fs"); + + // WARNING: We must keep `mount_root_fs` before stdio_init, + // because `migrate_virtual_filesystem` will change the root directory of the file system. + stdio_init().expect("Failed to initialize stdio"); + e1000e_init(); net_init().unwrap_or_else(|err| { error!("Failed to initialize network: {:?}", err); @@ -60,9 +65,7 @@ fn kenrel_init_freeable() -> Result<(), SystemError> { do_initcalls().unwrap_or_else(|err| { panic!("Failed to initialize subsystems: {:?}", err); }); - stdio_init().expect("Failed to initialize stdio"); smp_init(); - return Ok(()); } @@ -160,9 +163,10 @@ fn run_init_process( ) -> Result<(), SystemError> { compiler_fence(Ordering::SeqCst); let path = proc_init_info.proc_name.to_str().unwrap(); - + let pwd = ProcessManager::current_pcb().pwd_inode(); + let inode = pwd.lookup(path)?; do_execve( - path.to_string(), + inode, proc_init_info.args.clone(), proc_init_info.envs.clone(), trap_frame, diff --git a/kernel/src/ipc/pipe.rs b/kernel/src/ipc/pipe.rs index 34c1578ae..c94169f05 100644 --- a/kernel/src/ipc/pipe.rs +++ b/kernel/src/ipc/pipe.rs @@ -1,5 +1,3 @@ -use core::sync::atomic::compiler_fence; - use crate::{ arch::ipc::signal::{SigCode, Signal}, filesystem::{ @@ -17,6 +15,8 @@ use crate::{ sched::SchedMode, time::PosixTimeSpec, }; +use alloc::string::String; +use core::sync::atomic::compiler_fence; use alloc::{ collections::LinkedList, @@ -510,4 +510,8 @@ impl IndexNode for LockedPipeInode { fn as_pollable_inode(&self) -> Result<&dyn PollableInode, SystemError> { Ok(self) } + + fn absolute_path(&self) -> Result { + Ok(String::from("pipe")) + } } diff --git a/kernel/src/ipc/syscall/mod.rs b/kernel/src/ipc/syscall/mod.rs index 75a790429..bc35d02b9 100644 --- a/kernel/src/ipc/syscall/mod.rs +++ b/kernel/src/ipc/syscall/mod.rs @@ -8,6 +8,6 @@ mod sys_shmdt; mod sys_shmget; mod sys_sigaction; mod sys_sigpending; - +mod sys_rtsigsuspend; #[cfg(target_arch = "x86_64")] pub mod sys_pipe; diff --git a/kernel/src/ipc/syscall/sys_rtsigsuspend.rs b/kernel/src/ipc/syscall/sys_rtsigsuspend.rs new file mode 100644 index 000000000..6c3e1357a --- /dev/null +++ b/kernel/src/ipc/syscall/sys_rtsigsuspend.rs @@ -0,0 +1,72 @@ +use syscall_table_macros::declare_syscall; +use system_error::SystemError; + +use crate::arch::ipc::signal::SigSet; +use crate::arch::syscall::nr::SYS_RT_SIGSUSPEND; +use crate::ipc::signal::{set_sigprocmask, SigHow}; +use crate::process::ProcessManager; +use crate::sched::{schedule, SchedMode}; +use crate::{ + mm::VirtAddr, + syscall::{ + table::{FormattedSyscallParam, Syscall}, + user_access::UserBufferReader, + }, +}; + +/// See +pub struct SysRtSigSuspend; +impl Syscall for SysRtSigSuspend { + fn num_args(&self) -> usize { + 1 + } + + fn handle( + &self, + args: &[usize], + _frame: &mut crate::arch::interrupt::TrapFrame, + ) -> Result { + let sigsetsize = args[1]; + if sigsetsize != size_of::() { + return Err(SystemError::EFAULT); + } + + let reader = UserBufferReader::new( + VirtAddr::new(args[0]).as_ptr::(), + core::mem::size_of::(), + true, + )?; + let mask = reader.read_one_from_user::(0)?; + let mut mask = SigSet::from_bits_truncate(*mask); + // It is not possible to block SIGKILL or SIGSTOP; specifying these + // signals in mask, has no effect on the thread's signal mask. + mask -= SigSet::SIGKILL; + mask -= SigSet::SIGSTOP; + + let pcb = ProcessManager::current_pcb(); + let old_mask = *pcb.sig_info_irqsave().sig_blocked(); + + set_sigprocmask(SigHow::SetMask, mask).unwrap(); + log::warn!("Process enter rt_sigsuspend, new mask: {mask:?}, old mask: {old_mask:?}"); + loop { + if pcb.has_pending_signal_fast() && pcb.has_pending_not_masked_signal() { + set_sigprocmask(SigHow::SetMask, old_mask).unwrap(); + return Err(SystemError::EINTR); + } + schedule(SchedMode::SM_NONE); + } + // unreachable!("rt_sigsuspend always return EINTR"); + } + + fn entry_format( + &self, + args: &[usize], + ) -> alloc::vec::Vec { + vec![FormattedSyscallParam::new( + "mask", + format!("{:#x}", args[0]), + )] + } +} + +declare_syscall!(SYS_RT_SIGSUSPEND, SysRtSigSuspend); diff --git a/kernel/src/lib.rs b/kernel/src/lib.rs index bd1399a14..e391b204d 100644 --- a/kernel/src/lib.rs +++ b/kernel/src/lib.rs @@ -23,6 +23,7 @@ #![feature(vec_into_raw_parts)] #![feature(linkage)] #![feature(panic_can_unwind)] +#![feature(let_chains)] #![allow( static_mut_refs, non_local_definitions, diff --git a/kernel/src/libs/elf.rs b/kernel/src/libs/elf.rs index 045bb3a69..81e370256 100644 --- a/kernel/src/libs/elf.rs +++ b/kernel/src/libs/elf.rs @@ -787,13 +787,17 @@ impl BinaryLoader for ElfLoader { e )) })?; + let pwd = ProcessManager::current_pcb().pwd_inode(); + let inode = pwd.lookup(interpreter_path).map_err(|_| { + log::error!("Failed to find interpreter path: {}", interpreter_path); + return ExecError::InvalidParemeter; + })?; // log::debug!("opening interpreter at :{}", interpreter_path); interpreter = Some( - ExecParam::new(interpreter_path, param.vm().clone(), ExecParamFlags::EXEC) - .map_err(|e| { - log::error!("Failed to load interpreter {interpreter_path}: {:?}", e); - return ExecError::NotSupported; - })?, + ExecParam::new(inode, param.vm().clone(), ExecParamFlags::EXEC).map_err(|e| { + log::error!("Failed to load interpreter {interpreter_path}: {:?}", e); + return ExecError::NotSupported; + })?, ); } //TODO 缺少一部分逻辑 https://code.dragonos.org.cn/xref/linux-6.1.9/fs/binfmt_elf.c#931 diff --git a/kernel/src/libs/printk.rs b/kernel/src/libs/printk.rs index 7ba026023..ae9ba3d1c 100644 --- a/kernel/src/libs/printk.rs +++ b/kernel/src/libs/printk.rs @@ -176,6 +176,6 @@ impl KernelLogger { pub fn early_init_logging() { log::set_logger(&KernelLogger).unwrap(); - log::set_max_level(log::LevelFilter::Debug); + // log::set_max_level(log::LevelFilter::Debug); info!("Logging initialized"); } diff --git a/kernel/src/net/socket/inet.rs b/kernel/src/net/socket/inet.rs index 547126459..4ddc067bf 100644 --- a/kernel/src/net/socket/inet.rs +++ b/kernel/src/net/socket/inet.rs @@ -432,7 +432,8 @@ impl Socket for UdpSocket { _arg1: usize, _arg2: usize, ) -> Result { - todo!() + log::error!("Raw Socket not support ioctl"); + return Ok(0); } fn metadata(&self) -> SocketMetadata { @@ -497,9 +498,9 @@ impl TcpSocket { /// 元数据的缓冲区的大小 pub const DEFAULT_METADATA_BUF_SIZE: usize = 1024; /// 默认的接收缓冲区的大小 receive - pub const DEFAULT_RX_BUF_SIZE: usize = 512 * 1024; + pub const DEFAULT_RX_BUF_SIZE: usize = 128 * 1024; /// 默认的发送缓冲区的大小 transmiss - pub const DEFAULT_TX_BUF_SIZE: usize = 512 * 1024; + pub const DEFAULT_TX_BUF_SIZE: usize = 128 * 1024; /// TcpSocket的特殊事件,用于在事件等待队列上sleep pub const CAN_CONNECT: u64 = 1u64 << 63; @@ -813,6 +814,7 @@ impl Socket for TcpSocket { // 获取handle的数量 let handlen = self.handles.len(); let backlog = handlen.max(backlog); + let backlog = backlog.min(64); // 限制backlog的最大值为128 // 添加剩余需要构建的socket // debug!("tcp socket:before listen, socket'len={}", self.handle_list.len()); @@ -1000,6 +1002,17 @@ impl Socket for TcpSocket { *self.handles.first().unwrap() } + fn ioctl( + &self, + _cmd: usize, + _arg0: usize, + _arg1: usize, + _arg2: usize, + ) -> Result { + log::error!("Tcp Socket not support ioctl"); + return Ok(0); + } + fn as_any_ref(&self) -> &dyn core::any::Any { self } diff --git a/kernel/src/net/socket/mod.rs b/kernel/src/net/socket/mod.rs index aa13daa26..b1c936f89 100644 --- a/kernel/src/net/socket/mod.rs +++ b/kernel/src/net/socket/mod.rs @@ -384,6 +384,15 @@ impl IndexNode for SocketInode { self } + fn ioctl( + &self, + _cmd: u32, + _data: usize, + _private_data: &FilePrivateData, + ) -> Result { + self.0.lock_irqsave().ioctl(_cmd as usize, _data, 0, 0) + } + fn list(&self) -> Result, SystemError> { return Err(SystemError::ENOTDIR); } @@ -405,6 +414,10 @@ impl IndexNode for SocketInode { fn as_pollable_inode(&self) -> Result<&dyn PollableInode, SystemError> { Ok(self) } + + fn absolute_path(&self) -> Result { + Ok(String::from("socket")) + } } #[derive(Debug)] diff --git a/kernel/src/net/socket/unix.rs b/kernel/src/net/socket/unix.rs index f15037775..265487017 100644 --- a/kernel/src/net/socket/unix.rs +++ b/kernel/src/net/socket/unix.rs @@ -58,6 +58,17 @@ impl Socket for StreamSocket { self.handle } + fn ioctl( + &self, + _cmd: usize, + _arg0: usize, + _arg1: usize, + _arg2: usize, + ) -> Result { + log::error!("Stream Socket not support ioctl"); + return Ok(0); + } + fn close(&mut self) {} fn read(&self, buf: &mut [u8]) -> (Result, Endpoint) { diff --git a/kernel/src/perf/bpf.rs b/kernel/src/perf/bpf.rs index ec31b170a..255bcdfd4 100644 --- a/kernel/src/perf/bpf.rs +++ b/kernel/src/perf/bpf.rs @@ -322,6 +322,10 @@ impl IndexNode for BpfPerfEvent { fn page_cache(&self) -> Option> { Some(self.data.lock().page_cache.clone()) } + + fn absolute_path(&self) -> core::result::Result { + Ok(String::from("bpf_perf_event")) + } } impl PerfEventOps for BpfPerfEvent { diff --git a/kernel/src/perf/kprobe.rs b/kernel/src/perf/kprobe.rs index 7d99689c5..af2837976 100644 --- a/kernel/src/perf/kprobe.rs +++ b/kernel/src/perf/kprobe.rs @@ -134,6 +134,10 @@ impl IndexNode for KprobePerfEvent { fn page_cache(&self) -> Option> { None } + + fn absolute_path(&self) -> core::result::Result { + Ok(String::from("kprobe_perf_event")) + } } impl PerfEventOps for KprobePerfEvent { diff --git a/kernel/src/perf/mod.rs b/kernel/src/perf/mod.rs index cf8fd9b9d..9de0fbafa 100644 --- a/kernel/src/perf/mod.rs +++ b/kernel/src/perf/mod.rs @@ -282,6 +282,10 @@ impl IndexNode for PerfEventInode { fn as_pollable_inode(&self) -> Result<&dyn PollableInode> { Ok(self) } + + fn absolute_path(&self) -> core::result::Result { + Ok(String::from("perf_event")) + } } impl PollableInode for PerfEventInode { diff --git a/kernel/src/perf/tracepoint.rs b/kernel/src/perf/tracepoint.rs index 0f3ec668d..1fcd3ad03 100644 --- a/kernel/src/perf/tracepoint.rs +++ b/kernel/src/perf/tracepoint.rs @@ -73,6 +73,14 @@ impl IndexNode for TracepointPerfEvent { fn page_cache(&self) -> Option> { None } + + fn absolute_path(&self) -> core::result::Result { + Ok(format!( + "tracepoint: {}:{}", + self.tp.system(), + self.tp.name() + )) + } } pub struct TracePointPerfCallBack(BasicPerfEbpfCallBack); diff --git a/kernel/src/process/cred.rs b/kernel/src/process/cred.rs index 6a6864c78..5686df599 100644 --- a/kernel/src/process/cred.rs +++ b/kernel/src/process/cred.rs @@ -37,6 +37,8 @@ pub struct Cred { pub euid: Kuid, /// 进程有效的gid pub egid: Kgid, + /// supplementary groups + pub groups: Vec, /// UID for VFS ops pub fsuid: Kuid, /// GID for VFS ops @@ -66,6 +68,7 @@ impl Cred { egid: GLOBAL_ROOT_GID, fsuid: GLOBAL_ROOT_UID, fsgid: GLOBAL_ROOT_GID, + groups: Vec::new(), cap_inheritable: CAPFlags::CAP_EMPTY_SET, cap_permitted: CAPFlags::CAP_FULL_SET, cap_effective: CAPFlags::CAP_FULL_SET, @@ -162,6 +165,16 @@ impl Cred { pub fn setfsgid(&mut self, fsgid: usize) { self.fsgid.0 = fsgid; } + + /// Set supplementary groups + pub fn setgroups(&mut self, groups: Vec) { + self.groups = groups; + } + + /// Get supplementary groups + pub fn getgroups(&self) -> &Vec { + &self.groups + } } #[derive(Debug, Clone, PartialEq, Eq, Default)] diff --git a/kernel/src/process/exec.rs b/kernel/src/process/exec.rs index 5eaae376d..eb0800e20 100644 --- a/kernel/src/process/exec.rs +++ b/kernel/src/process/exec.rs @@ -5,7 +5,10 @@ use system_error::SystemError; use crate::{ driver::base::block::SeekFrom, - filesystem::vfs::file::{File, FileMode}, + filesystem::vfs::{ + file::{File, FileMode}, + IndexNode, + }, libs::elf::ELF_LOADER, mm::{ ucontext::{AddressSpace, UserStack}, @@ -111,15 +114,12 @@ pub enum ExecLoadMode { #[allow(dead_code)] impl ExecParam { pub fn new( - file_path: &str, + file_inode: Arc, vm: Arc, flags: ExecParamFlags, ) -> Result { - let pwd = ProcessManager::current_pcb().pwd_inode(); - let inode = pwd.lookup(file_path)?; - // 读取文件头部,用于判断文件类型 - let file = File::new(inode, FileMode::O_RDONLY)?; + let file = File::new(file_inode, FileMode::O_RDONLY)?; Ok(Self { file, diff --git a/kernel/src/process/execve.rs b/kernel/src/process/execve.rs index eb0e6809f..02a8f87a5 100644 --- a/kernel/src/process/execve.rs +++ b/kernel/src/process/execve.rs @@ -1,22 +1,24 @@ use crate::arch::CurrentIrqArch; use crate::exception::InterruptArch; +use crate::filesystem::vfs::IndexNode; use crate::process::exec::{load_binary_file, ExecParam, ExecParamFlags}; use crate::process::ProcessManager; use crate::syscall::Syscall; use crate::{libs::rand::rand_bytes, mm::ucontext::AddressSpace}; use crate::arch::interrupt::TrapFrame; -use alloc::{ffi::CString, string::String, sync::Arc, vec::Vec}; +use alloc::{ffi::CString, sync::Arc, vec::Vec}; use system_error::SystemError; + pub fn do_execve( - path: String, + file_inode: Arc, argv: Vec, envp: Vec, regs: &mut TrapFrame, ) -> Result<(), SystemError> { let address_space = AddressSpace::new(true).expect("Failed to create new address space"); // debug!("to load binary file"); - let mut param = ExecParam::new(path.as_str(), address_space.clone(), ExecParamFlags::EXEC)?; + let mut param = ExecParam::new(file_inode, address_space.clone(), ExecParamFlags::EXEC)?; let old_vm = do_execve_switch_user_vm(address_space.clone()); // 加载可执行文件 diff --git a/kernel/src/process/syscall/mod.rs b/kernel/src/process/syscall/mod.rs index 7d3dd9ce1..3e9a0871d 100644 --- a/kernel/src/process/syscall/mod.rs +++ b/kernel/src/process/syscall/mod.rs @@ -1,5 +1,6 @@ mod sys_clone; mod sys_execve; +mod sys_execveat; mod sys_exit; mod sys_exit_group; mod sys_get_rusage; @@ -24,6 +25,7 @@ mod sys_setsid; mod sys_setuid; mod sys_uname; mod sys_wait4; +mod sys_groups; #[cfg(target_arch = "x86_64")] mod sys_fork; diff --git a/kernel/src/process/syscall/sys_execve.rs b/kernel/src/process/syscall/sys_execve.rs index 3a4f470a8..837eceef4 100644 --- a/kernel/src/process/syscall/sys_execve.rs +++ b/kernel/src/process/syscall/sys_execve.rs @@ -1,8 +1,9 @@ -use log::error; +use alloc::string::String; +use alloc::sync::Arc; use crate::arch::interrupt::TrapFrame; use crate::arch::syscall::nr::SYS_EXECVE; -use crate::filesystem::vfs::MAX_PATHLEN; +use crate::filesystem::vfs::{IndexNode, MAX_PATHLEN}; use crate::mm::page::PAGE_4K_SIZE; use crate::mm::{verify_area, VirtAddr}; use crate::process::execve::do_execve; @@ -10,6 +11,7 @@ use crate::process::{ProcessControlBlock, ProcessManager}; use crate::syscall::table::{FormattedSyscallParam, Syscall}; use crate::syscall::user_access::{check_and_clone_cstr, check_and_clone_cstr_array}; use alloc::{ffi::CString, vec::Vec}; +use log::error; use system_error::SystemError; pub struct SysExecve; @@ -26,6 +28,63 @@ impl SysExecve { fn env_ptr(args: &[usize]) -> usize { args[2] } + + pub fn check_args( + frame: &mut TrapFrame, + path_ptr: usize, + argv_ptr: usize, + env_ptr: usize, + ) -> Result<(), SystemError> { + if path_ptr == 0 { + return Err(SystemError::EINVAL); + } + let virt_path_ptr = VirtAddr::new(path_ptr); + let virt_argv_ptr = VirtAddr::new(argv_ptr); + let virt_env_ptr = VirtAddr::new(env_ptr); + + // 权限校验 + if frame.is_from_user() + && (verify_area(virt_path_ptr, MAX_PATHLEN).is_err() + || verify_area(virt_argv_ptr, PAGE_4K_SIZE).is_err()) + || verify_area(virt_env_ptr, PAGE_4K_SIZE).is_err() + { + return Err(SystemError::EFAULT); + } + Ok(()) + } + + pub fn basic_args( + path: *const u8, + argv: *const *const u8, + envp: *const *const u8, + ) -> Result<(CString, Vec, Vec), SystemError> { + let path: CString = check_and_clone_cstr(path, Some(MAX_PATHLEN))?; + let argv: Vec = check_and_clone_cstr_array(argv)?; + let envp: Vec = check_and_clone_cstr_array(envp)?; + Ok((path, argv, envp)) + } + + pub fn execve( + inode: Arc, + path: String, + argv: Vec, + envp: Vec, + frame: &mut TrapFrame, + ) -> Result<(), SystemError> { + ProcessManager::current_pcb() + .basic_mut() + .set_name(ProcessControlBlock::generate_name(&path, &argv)); + + do_execve(inode, argv, envp, frame)?; + + let pcb = ProcessManager::current_pcb(); + // 关闭设置了O_CLOEXEC的文件描述符 + let fd_table = pcb.fd_table(); + fd_table.write().close_on_exec(); + + pcb.set_execute_path(path); + Ok(()) + } } impl Syscall for SysExecve { @@ -38,52 +97,24 @@ impl Syscall for SysExecve { let argv_ptr = Self::argv_ptr(args); let env_ptr = Self::env_ptr(args); - let virt_path_ptr = VirtAddr::new(path_ptr); - let virt_argv_ptr = VirtAddr::new(argv_ptr); - let virt_env_ptr = VirtAddr::new(env_ptr); + Self::check_args(frame, path_ptr, argv_ptr, env_ptr)?; - // 权限校验 - if frame.is_from_user() - && (verify_area(virt_path_ptr, MAX_PATHLEN).is_err() - || verify_area(virt_argv_ptr, PAGE_4K_SIZE).is_err()) - || verify_area(virt_env_ptr, PAGE_4K_SIZE).is_err() - { - Err(SystemError::EFAULT) - } else { - let path = path_ptr as *const u8; - let argv = argv_ptr as *const *const u8; - let envp = env_ptr as *const *const u8; - - if path.is_null() { - return Err(SystemError::EINVAL); - } - - let x = || { - let path: CString = check_and_clone_cstr(path, Some(MAX_PATHLEN))?; - let argv: Vec = check_and_clone_cstr_array(argv)?; - let envp: Vec = check_and_clone_cstr_array(envp)?; - Ok((path, argv, envp)) - }; - let (path, argv, envp) = x().inspect_err(|e: &SystemError| { + let path = path_ptr as *const u8; + let argv = argv_ptr as *const *const u8; + let envp = env_ptr as *const *const u8; + + let (path, argv, envp) = + Self::basic_args(path, argv, envp).inspect_err(|e: &SystemError| { error!("Failed to execve: {:?}", e); })?; - let path = path.into_string().map_err(|_| SystemError::EINVAL)?; - ProcessManager::current_pcb() - .basic_mut() - .set_name(ProcessControlBlock::generate_name(&path, &argv)); - - do_execve(path.clone(), argv, envp, frame)?; + let path = path.into_string().map_err(|_| SystemError::EINVAL)?; - let pcb = ProcessManager::current_pcb(); - // 关闭设置了O_CLOEXEC的文件描述符 - let fd_table = pcb.fd_table(); - fd_table.write().close_on_exec(); + let pwd = ProcessManager::current_pcb().pwd_inode(); + let inode = pwd.lookup(&path)?; - pcb.set_execute_path(path); - - return Ok(0); - } + Self::execve(inode, path, argv, envp, frame)?; + return Ok(0); } fn entry_format(&self, args: &[usize]) -> Vec { diff --git a/kernel/src/process/syscall/sys_execveat.rs b/kernel/src/process/syscall/sys_execveat.rs new file mode 100644 index 000000000..0167a5cd8 --- /dev/null +++ b/kernel/src/process/syscall/sys_execveat.rs @@ -0,0 +1,82 @@ +use alloc::vec::Vec; +use system_error::SystemError; + +use crate::{ + arch::{interrupt::TrapFrame, syscall::nr::SYS_EXECVEAT}, + filesystem::vfs::{utils::user_path_at, VFS_MAX_FOLLOW_SYMLINK_TIMES}, + process::{syscall::sys_execve::SysExecve, ProcessManager}, + syscall::table::{FormattedSyscallParam, Syscall}, +}; + +bitflags::bitflags! { + struct OpenFlags: u32 { + const AT_EMPTY_PATH = 0x1000; + const AT_SYMLINK_NOFOLLOW = 0x100; + } +} +pub struct SysExecveAt; + +impl Syscall for SysExecveAt { + fn num_args(&self) -> usize { + 5 + } + + fn handle(&self, args: &[usize], frame: &mut TrapFrame) -> Result { + let dirfd = args[0]; + let path_ptr = args[1]; + let argv_ptr = args[2]; + let env_ptr = args[3]; + let flags = OpenFlags::from_bits(args[4] as u32).ok_or(SystemError::EINVAL)?; + + // 权限校验 + SysExecve::check_args(frame, path_ptr, argv_ptr, env_ptr)?; + + let (path, argv, envp) = SysExecve::basic_args( + path_ptr as *const u8, + argv_ptr as *const *const u8, + env_ptr as *const *const u8, + ) + .inspect_err(|e: &SystemError| { + log::error!("Failed to execve: {:?}", e); + })?; + let path = path.into_string().map_err(|_| SystemError::EINVAL)?; + + let inode = if flags.contains(OpenFlags::AT_EMPTY_PATH) && path.is_empty() { + let binding = ProcessManager::current_pcb().fd_table(); + let fd_table_guard = binding.read(); + + let file = fd_table_guard + .get_file_by_fd(dirfd as _) + .ok_or(SystemError::EBADF)?; + file.inode() + } else { + let (inode_begin, path) = + user_path_at(&ProcessManager::current_pcb(), dirfd as _, &path)?; + let inode = inode_begin.lookup_follow_symlink( + &path, + if flags.contains(OpenFlags::AT_SYMLINK_NOFOLLOW) { + VFS_MAX_FOLLOW_SYMLINK_TIMES + } else { + 0 + }, + )?; + inode + }; + + SysExecve::execve(inode, path, argv, envp, frame)?; + + Ok(0) + } + + fn entry_format(&self, args: &[usize]) -> Vec { + vec![ + FormattedSyscallParam::new("dirfd", format!("{:#x}", args[0])), + FormattedSyscallParam::new("path", format!("{:#x}", args[1])), + FormattedSyscallParam::new("argv", format!("{:#x}", args[2])), + FormattedSyscallParam::new("envp", format!("{:#x}", args[3])), + FormattedSyscallParam::new("flags", format!("{:#x}", args[4])), + ] + } +} + +syscall_table_macros::declare_syscall!(SYS_EXECVEAT, SysExecveAt); diff --git a/kernel/src/process/syscall/sys_groups.rs b/kernel/src/process/syscall/sys_groups.rs new file mode 100644 index 000000000..cf16e5d13 --- /dev/null +++ b/kernel/src/process/syscall/sys_groups.rs @@ -0,0 +1,91 @@ +use crate::arch::interrupt::TrapFrame; +use crate::arch::syscall::nr::SYS_GETGROUPS; +use crate::arch::syscall::nr::SYS_SETGROUPS; +use crate::process::cred::Kgid; +use crate::process::ProcessManager; +use crate::syscall::table::FormattedSyscallParam; +use crate::syscall::table::Syscall; +use crate::syscall::user_access::UserBufferReader; +use crate::syscall::user_access::UserBufferWriter; +use alloc::vec::Vec; +use system_error::SystemError; + +const NGROUPS_MAX: usize = 65536; + +/// See https://man7.org/linux/man-pages/man2/setgroups.2.html +pub struct SysGetGroups; + +impl Syscall for SysGetGroups { + fn num_args(&self) -> usize { + 2 + } + + fn handle(&self, args: &[usize], _frame: &mut TrapFrame) -> Result { + let pcb = ProcessManager::current_pcb(); + let cred = pcb.cred.lock(); + let size = args[0]; + if size == 0 { + return Ok(cred.getgroups().len()); + } + if size < cred.getgroups().len() || size > NGROUPS_MAX { + return Err(SystemError::EINVAL); + } + let mut user_buffer = UserBufferWriter::new( + args[1] as *mut Kgid, + size * core::mem::size_of::(), + true, + )?; + user_buffer.copy_to_user(cred.getgroups(), 0)?; + Ok(size) + } + + fn entry_format(&self, args: &[usize]) -> Vec { + vec![ + FormattedSyscallParam::new("size", format!("{}", args[0])), + FormattedSyscallParam::new("list", format!("{:#x}", args[1])), + ] + } +} + +syscall_table_macros::declare_syscall!(SYS_GETGROUPS, SysGetGroups); + +pub struct SysSetGroups; + +impl Syscall for SysSetGroups { + fn num_args(&self) -> usize { + 2 + } + + fn handle(&self, args: &[usize], _frame: &mut TrapFrame) -> Result { + let pcb = ProcessManager::current_pcb(); + let mut cred = pcb.cred.lock(); + let size = args[0]; + if size == 0 { + // clear all supplementary groups + cred.setgroups(Vec::new()); + return Ok(0); + } + if size > NGROUPS_MAX { + return Err(SystemError::EINVAL); + } + let user_buffer = UserBufferReader::new( + args[1] as *const Kgid, + size * core::mem::size_of::(), + true, + )?; + let mut groups = vec![Kgid::from(0); size]; + user_buffer.copy_from_user(&mut groups, 0).unwrap(); + // log::info!("set supplementary groups: {:?}", groups); + cred.setgroups(groups); + Ok(0) + } + + fn entry_format(&self, args: &[usize]) -> Vec { + vec![ + FormattedSyscallParam::new("size", format!("{}", args[0])), + FormattedSyscallParam::new("list", format!("{:#x}", args[1])), + ] + } +} + +syscall_table_macros::declare_syscall!(SYS_SETGROUPS, SysSetGroups); diff --git a/kernel/src/syscall/misc.rs b/kernel/src/syscall/misc.rs index 92879b3eb..4a6a83d13 100644 --- a/kernel/src/syscall/misc.rs +++ b/kernel/src/syscall/misc.rs @@ -59,7 +59,7 @@ impl Syscall { } pub fn umask(_mask: u32) -> Result { - warn!("SYS_UMASK has not yet been implemented\n"); + warn!("SYS_UMASK has not yet been implemented"); return Ok(0o777); } diff --git a/kernel/src/syscall/mod.rs b/kernel/src/syscall/mod.rs index 9619054cd..126bf152c 100644 --- a/kernel/src/syscall/mod.rs +++ b/kernel/src/syscall/mod.rs @@ -53,6 +53,7 @@ pub const SYS_SCHED: usize = 100003; #[derive(Debug)] pub struct Syscall; +pub static DFLAG: AtomicBool = AtomicBool::new(false); impl Syscall { /// 初始化系统调用 #[inline(never)] @@ -78,7 +79,26 @@ impl Syscall { frame: &mut TrapFrame, ) -> Result { use crate::debug::panic::kernel_catch_unwind; + let binding = ProcessManager::current_pcb(); + // todo: remove it + if binding.basic().name().contains("dropbear") || binding.basic().name().contains("nginx") { + // 如果是dropbear进程,打印系统调用号和参数 + // print!( + // "[{}] Syscall {}({}) called with args: {:x?}", + // binding.basic().ppid(), + // syscall_num, + // syscall_number_to_str(syscall_num), + // args + // ); + // DFLAG.store(true, Ordering::Relaxed); + } + let res = kernel_catch_unwind(|| Self::handle(syscall_num, args, frame))?; + let binding = ProcessManager::current_pcb(); + // todo: remove it + if binding.basic().name().contains("dropbear") || binding.basic().name().contains("nginx") { + // println!(" {} returned: {:?}", syscall_num, res); + } res } /// @brief 系统调用分发器,用于分发系统调用。 @@ -724,11 +744,6 @@ impl Syscall { Self::fchownat(dirfd, pathname, uid, gid, flag) } - SYS_FSYNC => { - warn!("SYS_FSYNC has not yet been implemented"); - Ok(0) - } - SYS_RSEQ => { warn!("SYS_RSEQ has not yet been implemented"); Err(SystemError::ENOSYS) @@ -835,7 +850,7 @@ impl Syscall { SYS_SETRLIMIT => Ok(0), SYS_RT_SIGTIMEDWAIT => { - log::warn!("SYS_RT_SIGTIMEDWAIT has not yet been implemented"); + // log::warn!("SYS_RT_SIGTIMEDWAIT has not yet been implemented"); Ok(0) } _ => panic!("Unsupported syscall ID: {}", syscall_num), diff --git a/kernel/src/virt/vm/kvm_dev.rs b/kernel/src/virt/vm/kvm_dev.rs index b64f0d6a8..cb93eeb82 100644 --- a/kernel/src/virt/vm/kvm_dev.rs +++ b/kernel/src/virt/vm/kvm_dev.rs @@ -35,6 +35,7 @@ pub struct KvmInode { self_ref: Weak, /// 指向inode所在的文件系统对象的指针 fs: Weak, + parent: Weak, /// INode 元数据 metadata: Metadata, } @@ -52,6 +53,7 @@ impl LockedKvmInode { let inode = KvmInode { self_ref: Weak::default(), fs: Weak::default(), + parent: Weak::default(), metadata: Metadata { dev_id: 1, inode_id: generate_inode_id(), @@ -96,6 +98,10 @@ impl DeviceINode for LockedKvmInode { fn set_fs(&self, fs: Weak) { self.inner.lock().fs = fs; } + + fn set_parent(&self, parent: Weak) { + self.inner.lock().parent = parent; + } } impl IndexNode for LockedKvmInode { @@ -179,6 +185,14 @@ impl IndexNode for LockedKvmInode { ) -> Result<(), SystemError> { Ok(()) } + + fn parent(&self) -> Result, SystemError> { + let parent = self.inner.lock().parent.upgrade(); + if let Some(parent) = parent { + return Ok(parent); + } + Err(SystemError::ENOENT) + } } #[derive(Debug)] diff --git a/ssh.md b/ssh.md new file mode 100644 index 000000000..3112e9237 --- /dev/null +++ b/ssh.md @@ -0,0 +1,152 @@ +# SSH +## /etc/passwd 的作用 + +- 存储系统用户信息:它包含了每个用户的基本信息,是系统认证和登录的重要数据来源。 + +- 用于登录和用户管理:系统和应用程序通过读取该文件来验证用户身份、决定用户权限以及加载相应的设置(如默认 shell、主目录等)。 + +- 提供支持:对于一些应用程序和命令(如 useradd、passwd、chown),它需要通过 /etc/passwd 来获取用户的相关信息。 + +```bash +username:password:UID:GID:GECOS:home_directory:shell +``` + +- username(用户名):用户的登录名称。例如:root、john、guest 等。 + +- password(密码):用户的加密密码。现在大多数系统会将密码的哈希值存储在 /etc/shadow 文件中,因此这个字段通常是一个占位符(如 x 或 *)。在较旧的系统中,密码可能直接存储在此字段中,但这种做法已不安全。 + +- UID(用户 ID):用户的唯一标识符。每个用户都有一个唯一的 UID,系统通过它来区分不同用户。通常: + +- 0 表示 root 用户(超级用户)。 + +- 普通用户的 UID 从 1000 开始(在一些 Linux 发行版中,可能从 500 开始)。 + +- GID(组 ID):用户所属的主组的 ID。组 ID 是与组名称相关联的数字。通常,每个用户都会有一个与其用户名相同的组。例如,john 用户可能会有一个主组 john,其 GID 可能是 1001。 + +- GECOS(用户全名或备注信息):这个字段通常存储用户的全名、电话号码等可选信息。它可以为空或包含一些描述性文字,通常是通过 chfn 命令进行设置。 + +- home_directory(主目录):用户的登录主目录,用户登录后会被自动带到这个目录。例如:/home/john、/root。如果用户是 root,其主目录通常是 /root。 + +- shell(默认 shell):用户登录时所使用的 shell 程序。通常是 /bin/bash、/bin/sh 或其他 shell 程序。对于没有实际登录权限的系统用户,这个字段可能是 /usr/sbin/nologin 或 /bin/false,以阻止他们登录系统 + + +## /etc/shadow 文件的作用 + +- 存储用户的加密密码:/etc/shadow 文件中保存了每个用户的 加密密码,而不是明文密码。这是一个系统的安全机制,防止密码泄漏。 + +- 账户过期管理:它还包含了与账户过期、密码过期、账户锁定等相关的信息,帮助系统管理员管理用户的登录权限。 + +- 增强安全性:相比于早期系统中把密码直接存储在 /etc/passwd 中,/etc/shadow 将密码从可公开访问的文件中移除,使得系统更加安全 + +```bash +username:password:lastchg:min:max:warn:inactive:expire:flag +``` + +- username(用户名):与 /etc/passwd 中的用户名一致。 + +- password(密码):这是用户的加密密码。如果密码为空,通常是 * 或 !,表示禁用该账户。正常情况下,这里存储的是密码的加密哈希值。 + +- lastchg(上次修改日期):密码最后一次修改的日期,表示自 1970 年 1 月 1 日以来的天数。通常这个值是通过 chage 命令查看和更新的。 + +- min(最小密码年龄):密码的最小使用期限。用户修改密码后,必须等待多少天才能再次修改密码。通常设置为 0,表示没有最小密码年龄限制。 + +- max(最大密码年龄):密码的最大使用期限。超过这个期限,用户必须修改密码。设置为 99999 表示密码永不过期。 + +- warn(警告期限):密码过期前,系统会提前多少天开始警告用户密码即将过期。 + +- inactive(非活动期限):密码过期后,用户仍然有多少天的时间可以继续登录。如果超过该天数,账户将被禁用。 + +- expire(账户过期日期):账户的过期日期,表示自 1970 年 1 月 1 日以来的天数。如果账户过期,用户将无法登录。 + +- flag(账户锁定标志):这个字段用于存储账户是否被锁定。如果这个字段是 !! 或 *,表示用户账号被锁定,不能登录 + +## 不同hash算法生成的密码前缀 + +- \$5$ 前缀(SHA-256) +- \$6$ 前缀(SHA-512) +- \$y$ 前缀(Yarrow) + + + +## dropbear需求 + +1. /etc/passwd 文件 +2. /etc/shadow 文件 + +由于DragonOS默认的/etc/passwd文件有问题,需要修改为 + +```bash +root:x:0:0:root:/root:/bin/sh +``` + +然后使用`busybox passwd` 设置root密码 + + + + +## 系统调用支持 + +- fcntl SETLK https://man7.org/linux/man-pages/man2/fcntl.2.html +- unlink +- fsync +- rename https://man7.org/linux/man-pages/man2/rename.2.html +- ioctl TCFLSH +- renameat2: oldfd: -100, filename_from: /etc/shadow+, newfd: -100, filename_to: /etc/shadow 失败 + + + +## 文件系统相关 +/proc/self目录:/proc/self 是一个 符号链接,始终指向 访问它的进程自己的 /proc/[pid] 目录 + +| 路径 | 作用 | +| -------------------- | -------------------- | +| `/proc/self/cmdline` | 当前进程的命令行参数 | +| `/proc/self/exe` | 当前进程的可执行文件路径(是个符号链接) | +| `/proc/self/fd/` | 当前进程打开的所有文件描述符 | +| `/proc/self/environ` | 当前进程的环境变量 | +| `/proc/self/maps` | 当前进程的内存映射布局 | +| `/proc/self/status` | 当前进程的状态信息(类似于 ps 命令) | + + +- /proc/fd/{id} 也是一个符号链接,指向进程打开的文件描述符所对应的文件路径。 +- /dev/pts/0 这些伪终端需要等待主设备/dev/ptmx被关闭的时候删除 +- /root目录必须存在 + +### linux的setgroups和getgroups的作用和用法是什么 + +在 Linux 中,一个进程有: + +- 真实用户 ID (real UID)、有效用户 ID (effective UID) +- 真实组 ID (real GID)、有效组 ID (effective GID) +- 附加组 ID 列表(supplementary groups) + +附加组 ID 让进程除了主组外,还可以属于其他多个组,从而获得对应组的访问权限 + + + +#### 用户 ID (UID) 和 组 ID (GID) +这两个是权限身份标识,用来决定一个进程能做什么。 + +UID(User ID)表示进程属于哪个用户。 +常见种类: + +- 真实用户 ID (real UID):启动该进程的用户是谁。 +- 有效用户 ID (effective UID):实际用于权限检查的 UID(比如 setuid 程序可以让 EUID 暂时变成 root)。 +- 保存的 set-user-ID(保存切换前的 EUID,用于临时恢复)。 + +root 用户的 UID 是 0,其它用户一般是从 1000(或 500)开始分配。 + +GID(Group ID) 表示进程属于哪个主用户组。同样有 real/effective/saved 三种。 +另外还有 附加组列表(supplementary groups),用来赋予额外的组权限。 + +作用: +当进程访问文件、socket、IPC 等资源时,内核会根据 EUID/EGID + 附加组列表 来判断能否访问。 + + + + +## Reference +- Linux TTY/PTS概述 https://liujunming.top/2019/09/03/Linux-TTY-PTS%E6%A6%82%E8%BF%B0/ +- 伪终端(pseudo terminal) https://zhuanlan.zhihu.com/p/678170056 +- 硬件终端 terminal(TTY) + https://www.cnblogs.com/sparkdev/p/11460821.html \ No newline at end of file diff --git a/tools/run-qemu.sh b/tools/run-qemu.sh index 81cf1c03b..339bc162b 100755 --- a/tools/run-qemu.sh +++ b/tools/run-qemu.sh @@ -88,7 +88,7 @@ QEMU_MEMORY="512M" QEMU_MEMORY_BACKEND="dragonos-qemu-shm.ram" QEMU_MEMORY_BACKEND_PATH_PREFIX="/dev/shm" QEMU_SHM_OBJECT="-object memory-backend-file,size=${QEMU_MEMORY},id=${QEMU_MEMORY_BACKEND},mem-path=${QEMU_MEMORY_BACKEND_PATH_PREFIX}/${QEMU_MEMORY_BACKEND},share=on " -QEMU_SMP="2,cores=2,threads=1,sockets=1" +QEMU_SMP="1" QEMU_MONITOR="-monitor stdio" QEMU_TRACE="${qemu_trace_std}" QEMU_CPU_FEATURES="" diff --git a/tty.md b/tty.md new file mode 100644 index 000000000..4908576b2 --- /dev/null +++ b/tty.md @@ -0,0 +1,545 @@ +# Linux tty设备 + +`dev/tty` 是一个在 Linux 和其他 Unix-like 系统中非常特殊的设备文件。从本质上讲,**它是一个指向当前进程的控制终端(Controlling Terminal)的别名或快捷方式**。 + +### 1. 核心概念:终端(Terminal) + +在计算机早期,用户通过物理设备与计算机交互,这些设备被称为“终端”。一个典型的物理终端包含一个键盘用于输入和一个屏幕(或打印机)用于输出。 + +在现代 Linux 系统中,物理终端已经不常见,取而代之的是**终端模拟器 (Terminal Emulator)**,例如 GNOME Terminal, Konsole, xterm, iTerm2 等。这些是图形界面下的软件程序,它们模拟了物理终端的行为。 + +此外,还有**控制台 (Console)**,这是直接连接到计算机硬件的终端,通常在没有图形界面或图形界面崩溃时使用。在 Linux 中,你可以通过 `Ctrl + Alt + F1-F6` 切换到虚拟控制台。 + +无论是哪种形式,系统都通过一个名为 **TTY** 的驱动程序子系统来管理这些终端会话。TTY 这个名字来源于早期的“Teletypewriter”(电传打字机)。 + + + +### 2. TTY 设备文件 + +在 Linux 中,“一切皆文件”。系统通过 `/dev` 目录下的特殊文件与硬件设备进行通信。对于终端,也有一系列的设备文件,通常位于 `/dev/` 目录下,例如: + +- **/dev/ttyS0, /dev/ttyS1, ...**: 物理串口设备(Serial Ports)。 +- **/dev/tty1, /dev/tty2, ...**: 虚拟控制台(Virtual Consoles)。 +- **/dev/pts/0, /dev/pts/1, ...**: 伪终端(Pseudo-terminals)。这是我们最常用的,当你打开一个终端模拟器窗口时,系统就会创建一个伪终端,并为其分配一个像 `/dev/pts/0` 这样的设备文件。 + +每个终端会话(比如你打开的一个终端窗口)都与一个特定的 TTY 设备文件相关联。你可以通过 `tty` 命令来查看当前终端对应的设备文件: + +Bash + +``` +$ tty +/dev/pts/0 +``` + +### 3. `/dev/tty` 的作用:一个动态的、指向“当前”的链接 + +现在我们回到主角 `/dev/tty`。 + +想象一下你正在编写一个程序,这个程序需要与用户直接交互(读取用户的输入或向用户的屏幕显示信息),无论这个程序最终从哪里运行。 + +- 如果用户在虚拟控制台 `tty2` 上运行你的程序,程序应该向 `/dev/tty2` 读写。 +- 如果用户在 GNOME Terminal 的一个窗口里运行,程序可能需要向 `/dev/pts/5` 读写。 +- 如果用户通过 `ssh` 远程登录运行,程序又需要向另一个伪终端设备读写。 + +如果让程序自己去判断当前在哪个终端上运行,将会非常复杂和不可靠。 + +`/dev/tty` 就是为了解决这个问题而存在的。**无论一个进程的“控制终端”是哪一个具体的设备(`/dev/tty2` 或 `/dev/pts/5` 等),`/dev/tty` 始终是指向这个控制终端的链接。** + +当一个程序打开 `/dev/tty` 文件时,内核会自动将这个文件描述符重定向到当前进程的实际控制终端。这样,程序开发者就不需要关心底层的具体 TTY 设备是什么,只需要统一地对 `/dev/tty` 进行读写,就能确保与当前用户进行交互。 + +**简单来说,`/dev/tty` 就是对程序说:“把信息发送给那个启动了你的用户,无论他在哪里。”** + + + +### 4. `/dev/tty` 与标准输入/输出/错误 (stdin, stdout, stderr) 的区别 + + + +你可能会问:这听起来和标准输入(stdin)、标准输出(stdout)很像,有什么区别? + +在大多数情况下,进程的标准输入、输出和错误流默认就是连接到其控制终端的。例如,当你运行 `ls` 命令时,它的 `stdout` 默认就是你的终端,所以你能在屏幕上看到文件列表。 + +然而,**重定向 (Redirection)** 会改变这种默认行为。 + +- `ls > files.txt`:`ls` 命令的 `stdout` 被重定向到了 `files.txt` 文件,而不是终端。 +- `cat my_script.sh | bash`:`bash` 进程的 `stdin` 被重定向到了管道 (`|`),它从 `cat` 命令的输出中读取内容,而不是从键盘。 + +在这种情况下,如果程序内部仍然希望强制与用户交互(例如,一个需要用户输入密码的脚本),它就不能再依赖 `stdin` 或 `stdout` 了。因为它们可能已经被重定向到文件或管道,不再是用户的屏幕和键盘了。 + +**这时,`/dev/tty` 就派上了用场。** + +对 `/dev/tty` 的读写操作会绕过标准输入/输出的重定向,直接访问控制终端。 + + + +#### 示例: + +来看一个实际的例子。假设我们有一个脚本 `ask_password.sh`: + +Bash + +``` +#!/bin/bash + +# 尝试从标准输入读取密码 +echo "Enter password (from stdin):" +read password_stdin + +# 现在,强制从控制终端读取密码 +echo "Enter password again (from /dev/tty):" +read password_tty < /dev/tty + +echo "Password from stdin: $password_stdin" +echo "Password from tty: $password_tty" +``` + +现在,我们正常运行它: + +Bash + +``` +$ ./ask_password.sh +Enter password (from stdin): +my_secret_pass +Enter password again (from /dev/tty): +my_secret_pass +Password from stdin: my_secret_pass +Password from tty: my_secret_pass +``` + +看起来没有区别。但是,现在我们尝试用重定向的方式运行它: + +Bash + +``` +$ echo "password_from_file" | ./ask_password.sh +Enter password (from stdin): +Enter password again (from /dev/tty): +my_real_secret <-- 这里光标会停住,等待你从键盘输入 +Password from stdin: password_from_file +Password from tty: my_real_secret +``` + +**分析:** + +1. 第一个 `read` 命令从 `stdin` 读取。由于我们通过管道将 `echo` 的输出重定向到了脚本的 `stdin`,所以它读到了 "password_from_file"。 +2. 第二个 `read` 命令被明确地重定向为从 `/dev/tty` 读取 (`< /dev/tty`)。这个操作绕过了 `stdin` 管道,直接访问了你的键盘和屏幕。因此,它会停下来等待你手动输入密码。 + +这就是 `/dev/tty` 的核心价值:**提供一个无论标准流如何重定向,都能保证与用户终端进行交互的可靠通道。** 像 `ssh`、`sudo` 等需要安全输入密码的程序,内部都会使用这种机制。 + + + +### 总结 + + + +| 特性 | 描述 | +| -------------------------- | ------------------------------------------------------------ | +| **定义** | 一个特殊的设备文件,作为当前进程控制终端的别名或快捷方式。 | +| **作用** | 为程序提供一个稳定、可靠的方式来与启动它的用户终端进行交互。 | +| **动态性** | 它本身不是一个具体的设备,而是一个由内核管理的、动态指向具体 TTY 设备的链接。 | +| **与 stdin/stdout 的区别** | 当标准输入/输出/错误流被重定向到文件、管道或其他进程时,`/dev/tty` 仍然可以用来直接访问用户的屏幕和键盘。 | +| **典型用途** | - 需要用户输入密码或确认的程序(如 `sudo`, `ssh`)。
- 需要在脚本中明确与用户交互,即使用户通过管道或重定向运行该脚本。 | + + + +### 在用户程序中,通常是怎么使用/dev/tty的 + +好的,在用户程序中使用 `/dev/tty` 的核心目标是:**绕过可能被重定向的标准输入/输出流,强制与用户的控制终端进行直接交互。** + +这在以下几个场景中非常常见: + +1. **请求敏感信息**:比如密码、私钥密码等。即使脚本的输出被重定向到日志文件,你也不希望密码提示和输入过程被记录下来。 +2. **交互式确认**:在一个可能被自动化调用的脚本中,执行危险操作前(如 `rm -rf /`),需要强制用户手动确认。 +3. **诊断和调试**:向用户的屏幕打印调试信息,即使用户已经将脚本的标准输出重定向到了别处。 +4. **全屏或基于光标的应用程序**:像 `vim`, `top` 这样的程序需要直接控制终端的屏幕、颜色和光标位置,它们会直接与 TTY 设备打交道。 + +#### 总结 + +在用户程序中使用 `/dev/tty` 的模式非常一致: + +1. **打开文件**:像打开普通文件一样打开 `/dev/tty`,通常需要读写权限 (`r+`)。 +2. **错误处理**:检查打开操作是否成功。如果一个进程没有控制终端(例如,一个由 `systemd` 启动的后台守护进程),打开 `/dev/tty` 将会失败。程序需要妥善处理这种情况。 +3. **写入(输出)**:使用标准的文件写入函数(如 `fprintf`, `write`)向打开的 `/dev/tty` 文件描述符写入数据,这将会在用户的屏幕上显示提示信息。 +4. **读取(输入)**:使用标准的文件读取函数(如 `fgets`, `read`)从 `/dev/tty` 读取数据,这将获取用户的键盘输入。 +5. **关闭文件**:完成交互后,关闭文件描述符。 + + + +## /dev/ptmx和 /dev/pts/下文件有什么作用? + + + +好的,我们来详细讲解 `/dev/ptmx` 和 `/dev/pts/` 目录下的文件。这两个组件是现代 Linux 系统中实现**伪终端(Pseudo-terminals, PTY)**机制的核心,对于我们日常使用的终端模拟器、SSH 远程登录等功能至关重要。 + +简单来说,它们共同创建了一个“假的”终端设备,让程序(如 `bash`)以为自己正在和一个物理终端对话,而实际上它是在和一个软件(如 GNOME Terminal 或 `sshd`)对话。 + +这个机制包含两个部分: + +- **主设备 (Master)**:由 `/dev/ptmx` 代表。 +- **从设备 (Slave)**:位于 `/dev/pts/` 目录下,例如 `/dev/pts/0`, `/dev/pts/1` 等。 + +下面我们来深入了解它们各自的作用以及如何协同工作。 + + + +### 1. 伪终端 (PTY) 的概念 + + + +首先,理解为什么需要伪终端。在早期的 Unix 系统中,用户通过物理串口(如 `/dev/ttyS0`)连接的物理终端与计算机交互。后来,随着图形界面和网络的发展,我们需要一种在软件层面模拟这种硬件终端的方法。 + +伪终端就是这种软件模拟的终端。它像一个管道一样,在两端各有一个“设备”: + +- **主端 (Master Side)**:由终端模拟器(如 xterm, GNOME Terminal)或远程登录服务(如 `sshd`)持有和控制。 +- **从端 (Slave Side)**:提供给应用程序(如 `shell`, `vim`, `top`)使用。这个从设备看起来和行为上都与一个真正的物理终端设备一模一样。 + +当你在终端模拟器里敲击键盘时,终端模拟器程序从主端写入数据;内核将这些数据转发到从端,`shell` 程序就能从从端读到你的输入。反之,当 `shell` 程序产生输出时(例如 `ls` 的结果),它向从端写入数据;内核将其转发到主端,终端模拟器读取这些数据并在窗口中显示出来。 + + + +### 2. `/dev/ptmx`:伪终端的主设备复用器 (Master Multiplexer) + + + +`/dev/ptmx` 是一个特殊的字符设备文件,它的名字是 "pseudo-terminal multiplexer" 的缩写。可以把它理解为**创建伪终端主/从设备对的工厂**。 + +它的核心作用是: + +1. **创建新的 PTY 对**:当一个程序(如终端模拟器)需要一个新的伪终端时,它会打开 `/dev/ptmx` 文件。 +2. **返回主设备的文件描述符**:这个 `open` 操作会成功返回一个文件描述符。这个文件描述符就代表了新创建的 PTY 对的**主端 (Master)**。 +3. **动态创建从设备**:在打开 `/dev/ptmx` 的同时,内核会在 `/dev/pts/` 目录下动态地创建一个对应的**从设备 (Slave)** 文件,比如 `/dev/pts/0`。 +4. **提供控制接口**:程序可以通过对 `/dev/ptmx` 返回的文件描述符执行 `ioctl()` 系统调用,来对 PTY 进行配置,例如获取从设备的名称、解锁从设备等。 + +**关键点**:你不能直接对 `/dev/ptmx` 进行大量的读写。它的主要目的是通过 `open()` 调用来请求和创建一个新的 PTY 对。之后所有的读写操作都通过 `open()` 返回的那个文件描述符来进行。每次打开 `/dev/ptmx` 都会创建一个全新的、独立的 PTY 主/从设备对。 + + + +### 3. `/dev/pts/` 目录和其下的文件 + + + +`/dev/pts` 是一个特殊的文件系统,类型是 `devpts`。这个目录专门用来存放伪终端的**从设备 (Slave)** 文件。 + +- **动态创建**:这个目录下的文件(如 `/dev/pts/0`, `/dev/pts/1`, ...)不是永久存在的。它们是在对应的 PTY 主设备被创建时(即 `/dev/ptmx` 被打开时)由内核动态创建的。 +- **从设备的角色**:每个 `/dev/pts/N` 文件都扮演着 PTY 对中从端的角色。它是一个标准的 TTY 设备,应用程序(如 `bash`)可以像对待任何其他终端设备一样打开它、读取用户输入、写入程序输出。 +- **分配给 Shell**:终端模拟器在创建了 PTY 对之后,会 `fork` 一个子进程,并在子进程中将标准输入、标准输出和标准错误都重定向到这个新创建的从设备(例如 `/dev/pts/0`)上,然后执行 `bash` 或其他 shell。这样,`bash` 就“拥有”了这个伪终端作为它的控制终端。 + +你可以通过 `tty` 命令查看当前 shell 关联的从设备: + +Bash + +``` +$ tty +/dev/pts/0 +``` + +如果你再打开一个新的终端窗口,在新窗口里执行 `tty`,你可能会看到: + +Bash + +``` +$ tty +/dev/pts/1 +``` + + + +### 4. 完整的创建流程 + + + +让我们把整个过程串起来,看看当你打开一个新的终端窗口时,后台发生了什么: + +1. **打开 ptmx**:GNOME Terminal 程序调用 `open("/dev/ptmx", O_RDWR)`。 +2. **创建 PTY 对**:内核接收到请求,创建一个新的伪终端主/从设备对。 +3. **返回主设备FD**:`open` 调用返回一个文件描述符(比如 `fd=3`)给 GNOME Terminal。这个 `fd` 就是 PTY 的主端。 +4. **创建从设备文件**:同时,内核在 `/dev/pts/` 目录下创建一个新的从设备文件,比如 `/dev/pts/5`。 +5. **解锁和授权**:GNOME Terminal 程序通过对主设备的文件描述符 `fd=3` 执行一系列 `ioctl` 调用(如 `grantpt` 和 `unlockpt`),来设置从设备 `/dev/pts/5` 的权限和状态,使其可用。 +6. **获取从设备名**:GNOME Terminal 通过 `ioctl` 调用 `ptsname` 来查询与 `fd=3` 对应的主设备关联的从设备名称,得到字符串 "/dev/pts/5"。 +7. **创建子进程**:GNOME Terminal 调用 `fork()` 创建一个子进程。 +8. **设置会话和重定向**:在子进程中: + - 创建一个新的会话 (`setsid()`),并将 `/dev/pts/5` 设置为该会话的控制终端。 + - 关闭标准输入、输出、错误(文件描述符 0, 1, 2)。 + - 打开 `/dev/pts/5` 并将其复制到文件描述符 0, 1, 2。现在,子进程的 `stdin`, `stdout`, `stderr` 都指向了这个伪终端从设备。 +9. **执行 Shell**:子进程调用 `execve("/bin/bash", ...)` 来启动 `bash`。`bash` 继承了已经设置好的文件描述符,所以它自然地从 `/dev/pts/5` 读取命令并向其写入结果。 +10. **数据转发**: + - 你在 GNOME Terminal 窗口输入 `ls`。 + - GNOME Terminal 程序从键盘事件中读取输入,并通过主设备的文件描述符 `fd=3` **写入** "ls\n"。 + - 内核将数据从主端转发到从端 `/dev/pts/5`。 + - `bash` 从它的标准输入(即 `/dev/pts/5`)**读取** "ls\n",执行命令。 + - `ls` 的输出被 `bash` **写入** 到它的标准输出(即 `/dev/pts/5`)。 + - 内核将数据从从端转发到主端。 + - GNOME Terminal 程序通过主设备的文件描述符 `fd=3` **读取** `ls` 的输出结果,并将其渲染显示在窗口中。 + + + +### 总结 + +| 组件 | 角色 | 作用 | +| ------------------------ | ------------------------------- | ------------------------------------------------------------ | +| **`/dev/ptmx`** | **主设备工厂 (Master Factory)** | 作为一个单一的入口点,当被打开时,它会创建一个新的伪终端主/从设备对,并返回代表**主设备**的文件描述符。 | +| **`/dev/pts/` 下的文件** | **从设备 (Slave Devices)** | 这些是动态创建的设备文件,每一个都代表一个伪终端的**从设备**。它们为应用程序(如 shell)提供了一个标准的 TTY 接口,使其可以像与物理终端一样进行交互。 | + + + + + +### 简单的用户程序展示/dev/ptmx的使用 + + + + + +好的,下面是一个简单的 C 语言用户程序,它清晰地展示了如何使用 `/dev/ptmx` 来创建一个伪终端,并在这个伪终端中启动一个 shell (`/bin/bash`)。 + +这个程序将扮演一个最基础的“终端模拟器”的角色。它会处理主设备(master)端,而 `bash` shell 会在它创建的从设备(slave)端运行。 + + + +#### 程序功能分解 + + + +1. **打开 `/dev/ptmx`**:获取一个伪终端主设备(master PTY)的文件描述符。 +2. **初始化从设备**:调用 `grantpt()` 和 `unlockpt()` 来设置从设备(slave PTY)的权限和状态。 +3. **获取从设备名**:调用 `ptsname()` 来得到对应的从设备路径(例如 `/dev/pts/3`)。 +4. **创建子进程**:使用 `fork()` 创建一个子进程,这个子进程将用来运行 shell。 +5. **子进程设置**:在子进程中,将其会话(session)与从设备关联,并将标准输入、输出、错误重定向到从设备,然后执行 `/bin/bash`。 +6. **父进程通信**:在父进程中,监听用户在当前终端的输入和来自 `bash` 的输出,并在它们之间来回传递数据,实现交互。 + + + +#### 源代码 (`ptmx_demo.c`) + + + +C + +``` +#define _XOPEN_SOURCE 600 // Needed for grantpt, unlockpt, ptsname +#include +#include +#include +#include +#include +#include +#include +#include // Not strictly needed for the demo, but good practice +#include + +int main() { + int master_fd; + char *slave_name; + pid_t pid; + + // 1. 打开 /dev/ptmx 来获取一个主设备文件描述符 + master_fd = open("/dev/ptmx", O_RDWR | O_NOCTTY); + if (master_fd < 0) { + perror("Error opening /dev/ptmx"); + return 1; + } + printf("1. Master PTY opened with fd: %d\n", master_fd); + + // 2. 授权并解锁从设备 + if (grantpt(master_fd) != 0) { + perror("Error calling grantpt"); + close(master_fd); + return 1; + } + if (unlockpt(master_fd) != 0) { + perror("Error calling unlockpt"); + close(master_fd); + return 1; + } + printf("2. Slave PTY permissions granted and unlocked.\n"); + + // 3. 获取从设备的名字 + slave_name = ptsname(master_fd); + if (slave_name == NULL) { + perror("Error calling ptsname"); + close(master_fd); + return 1; + } + printf("3. Slave PTY name is: %s\n", slave_name); + + // 4. 创建子进程 + pid = fork(); + if (pid < 0) { + perror("Error calling fork"); + close(master_fd); + return 1; + } + + // 5. 子进程的代码 + if (pid == 0) { + int slave_fd; + + // 创建一个新的会话,使子进程成为会话领导者 + // 这是让从设备成为控制终端的关键步骤 + if (setsid() < 0) { + perror("setsid failed"); + exit(1); + } + + // 打开从设备 + slave_fd = open(slave_name, O_RDWR); + if (slave_fd < 0) { + perror("Error opening slave pty"); + exit(1); + } + + // 将从设备设置为该进程的控制终端 + // TIOCSCTTY 是 "Set Controlling TTY" 的意思 + if (ioctl(slave_fd, TIOCSCTTY, NULL) < 0) { + perror("ioctl TIOCSCTTY failed"); + exit(1); + } + + // 将子进程的标准输入、输出、错误重定向到从设备 + dup2(slave_fd, STDIN_FILENO); // fd 0 + dup2(slave_fd, STDOUT_FILENO); // fd 1 + dup2(slave_fd, STDERR_FILENO); // fd 2 + + // 关闭不再需要的文件描述符 + close(master_fd); // 子进程不需要主设备 + close(slave_fd); // 因为已经 dup2 了,这个原始的也可以关了 + + // 执行一个新的 bash shell + printf("--- Starting Bash Shell in Slave PTY ---\n\n"); + fflush(stdout); + execlp("/bin/bash", "bash", NULL); + + // 如果 execlp 成功,下面的代码不会被执行 + perror("execlp failed"); + exit(1); + } + + // 6. 父进程的代码 + printf("4. Forked child process with PID: %d\n", pid); + printf("5. Parent process will now forward data between stdin and master PTY.\n"); + printf("--- You are now interacting with the new shell. Type 'exit' to quit. ---\n\n"); + + // 父进程不需要从设备 + // close(slave_fd) in parent - it was never opened here + + char buffer[256]; + ssize_t nread; + + // 循环,直到子进程退出 + while (1) { + fd_set read_fds; + FD_ZERO(&read_fds); + FD_SET(STDIN_FILENO, &read_fds); // 监听当前终端的输入 + FD_SET(master_fd, &read_fds); // 监听主设备的输出 (来自子进程shell) + + // 使用 select 阻塞,直到有数据可读 + if (select(master_fd + 1, &read_fds, NULL, NULL, NULL) < 0) { + perror("select failed"); + break; + } + + // 检查是否是当前终端有输入 + if (FD_ISSET(STDIN_FILENO, &read_fds)) { + nread = read(STDIN_FILENO, buffer, sizeof(buffer)); + if (nread > 0) { + // 将用户的输入写入主设备,数据会流向子进程的shell + write(master_fd, buffer, nread); + } else { + break; // 读错误或EOF + } + } + + // 检查是否是主设备有输出 + if (FD_ISSET(master_fd, &read_fds)) { + nread = read(master_fd, buffer, sizeof(buffer)); + if (nread > 0) { + // 将来自shell的输出写入当前终端的屏幕 + write(STDOUT_FILENO, buffer, nread); + } else { + // 读取到 0 或 -1,意味着子进程的另一端关闭了连接 + // 通常是 shell 执行了 exit + break; + } + } + } + + printf("\n--- Shell terminated. Parent process is shutting down. ---\n"); + close(master_fd); + wait(NULL); // 等待子进程完全终止 + + return 0; +} +``` + + + +#### 如何编译和运行 + +1. **保存代码**:将上面的代码保存为 `ptmx_demo.c`。 + +2. **编译**:使用 gcc 进行编译。 + + Bash + + ``` + gcc -o ptmx_demo ptmx_demo.c + ``` + +3. **运行**:执行生成的可执行文件。 + + Bash + + ``` + ./ptmx_demo + ``` + + + +#### 运行时的输出和交互 + +当你运行程序时,你会看到类似下面的输出: + +``` +1. Master PTY opened with fd: 3 +2. Slave PTY permissions granted and unlocked. +3. Slave PTY name is: /dev/pts/2 +4. Forked child process with PID: 12345 +5. Parent process will now forward data between stdin and master PTY. +--- You are now interacting with the new shell. Type 'exit' to quit. --- + +bash-5.1$ +``` + +**发生了什么?** + +- 你的 `./ptmx_demo` 程序创建了一个新的伪终端 `/dev/pts/2`。 +- 它启动了一个新的 `bash` 进程,这个 `bash` 的“世界”就是 `/dev/pts/2`。 +- 你的 `./ptmx_demo` 程序正在作为中间人: + - 它读取你在当前终端(比如 `/dev/pts/1`)的键盘输入。 + - 将你的输入(如 `ls -l`)写入到主设备(`master_fd`)。 + - 内核将数据转发给从设备 `/dev/pts/2`。 + - `bash` 从它的标准输入(`/dev/pts/2`)读取到 `ls -l` 并执行。 + - `bash` 将 `ls -l` 的结果写入到它的标准输出(`/dev/pts/2`)。 + - 内核将数据转发给主设备。 + - 你的 `./ptmx_demo` 程序从主设备(`master_fd`)读取到结果,并将其打印到自己的标准输出,所以你就在屏幕上看到了 `ls -l` 的结果。 + +你可以像在普通 shell 中一样执行命令: + +``` +bash-5.1$ pwd +/home/user/test +bash-5.1$ whoami +user +bash-5.1$ ps -f +UID PID PPID C STIME TTY TIME CMD +user 12344 5678 0 10:30 pts/1 00:00:00 ./ptmx_demo +user 12345 12344 0 10:30 pts/2 00:00:00 bash +user 12350 12345 0 10:31 pts/2 00:00:00 ps -f +``` + +注意 `ps` 命令的输出!我们的 `ptmx_demo` 程序运行在 `pts/1`(你原来的终端),而它创建的 `bash` 进程则运行在一个全新的终端 `pts/2` 上。 + +当你输入 `exit` 并回车时,`bash` 进程会终止,这会导致伪终端连接关闭。父进程中的 `read(master_fd, ...)` 会返回 0,循环中断,程序优雅地退出。 + +这个例子完整地展示了 `/dev/ptmx` 的核心用途:创建一个隔离的终端会话环境,并允许一个程序(父进程)完全控制另一个程序(子进程)的输入和输出。这也是所有终端模拟器、`ssh` 服务和 `tmux`/`screen` 等工具的基础工作原理。 \ No newline at end of file diff --git a/user/apps/dropbear/.gitignore b/user/apps/dropbear/.gitignore new file mode 100644 index 000000000..b64c7fcb0 --- /dev/null +++ b/user/apps/dropbear/.gitignore @@ -0,0 +1,2 @@ +build/ +dropbear-* \ No newline at end of file diff --git a/user/apps/dropbear/Makefile b/user/apps/dropbear/Makefile new file mode 100644 index 000000000..65c3df60a --- /dev/null +++ b/user/apps/dropbear/Makefile @@ -0,0 +1,46 @@ +# dropbear: https://matt.ucc.asn.au/dropbear/releases/dropbear-2025.88.tar.bz2 + +ARCH ?= x86_64 +dropbear_version := 2025.88 +dropbear_tarball := dropbear-$(dropbear_version).tar.bz2 +dropbear_tarball_path := $(dropbear_tarball) +build_dir := build/$(ARCH) +dropbear_dir := $(build_dir)/dropbear-$(dropbear_version) +prefix := $(ARCH)-linux-musl- +bin := build/$(ARCH)/dropbear + +cc := $(prefix)gcc +strip := $(prefix)strip + +# 下载源码 +$(dropbear_tarball_path): + wget https://matt.ucc.asn.au/dropbear/releases/$(dropbear_tarball) +# 解压源码包 +$(dropbear_dir): $(dropbear_tarball_path) + mkdir -p $(build_dir) + tar -xjf $< -C $(build_dir) +# 配置和编译 +$(bin): $(dropbear_dir) + @# 应用必要补丁和配置调整 + cd $(dropbear_dir) && \ + ./configure --host=$(prefix) CC=$(cc) --enable-static --disable-zlib --host=x86 + @# 执行编译 + cd $(dropbear_dir) && \ + make PROGRAMS="dropbear dbclient dropbearkey dropbearconvert" + @# 处理编译输出 + mkdir -p $(dir $(bin)) + cp $(dropbear_dir)/dropbear $(bin) +# $(strip) $(bin) +.PHONY: all clean menuconfig +all: $(bin) + +install: all + mv $(bin) $(DADK_CURRENT_BUILD_DIR)/dropbear + +clean: + rm -rf build +menuconfig: + @echo "No menuconfig available for dropbear" + +distclean: clean + rm -f $(dropbear_tarball_path) diff --git a/user/apps/test_ptmx/ptmx_demo.c b/user/apps/test_ptmx/ptmx_demo.c new file mode 100644 index 000000000..fd8c75b66 --- /dev/null +++ b/user/apps/test_ptmx/ptmx_demo.c @@ -0,0 +1,155 @@ +#define _XOPEN_SOURCE 600 // Needed for grantpt, unlockpt, ptsname +#include +#include +#include +#include +#include +#include +#include +#include // Not strictly needed for the demo, but good practice +#include + +int main() { + int master_fd; + char *slave_name; + pid_t pid; + + // 1. 打开 /dev/ptmx 来获取一个主设备文件描述符 + master_fd = open("/dev/ptmx", O_RDWR | O_NOCTTY); + if (master_fd < 0) { + perror("Error opening /dev/ptmx"); + return 1; + } + printf("1. Master PTY opened with fd: %d\n", master_fd); + + // 2. 授权并解锁从设备 + if (grantpt(master_fd) != 0) { + perror("Error calling grantpt"); + close(master_fd); + return 1; + } + if (unlockpt(master_fd) != 0) { + perror("Error calling unlockpt"); + close(master_fd); + return 1; + } + printf("2. Slave PTY permissions granted and unlocked.\n"); + + // 3. 获取从设备的名字 + slave_name = ptsname(master_fd); + if (slave_name == NULL) { + perror("Error calling ptsname"); + close(master_fd); + return 1; + } + printf("3. Slave PTY name is: %s\n", slave_name); + + // 4. 创建子进程 + pid = fork(); + if (pid < 0) { + perror("Error calling fork"); + close(master_fd); + return 1; + } + + // 5. 子进程的代码 + if (pid == 0) { + int slave_fd; + + // 创建一个新的会话,使子进程成为会话领导者 + // 这是让从设备成为控制终端的关键步骤 + if (setsid() < 0) { + perror("setsid failed"); + exit(1); + } + + // 打开从设备 + slave_fd = open(slave_name, O_RDWR); + if (slave_fd < 0) { + perror("Error opening slave pty"); + exit(1); + } + + // 将从设备设置为该进程的控制终端 + // TIOCSCTTY 是 "Set Controlling TTY" 的意思 + if (ioctl(slave_fd, TIOCSCTTY, NULL) < 0) { + perror("ioctl TIOCSCTTY failed"); + exit(1); + } + + // 将子进程的标准输入、输出、错误重定向到从设备 + dup2(slave_fd, STDIN_FILENO); // fd 0 + dup2(slave_fd, STDOUT_FILENO); // fd 1 + dup2(slave_fd, STDERR_FILENO); // fd 2 + + // 关闭不再需要的文件描述符 + close(master_fd); // 子进程不需要主设备 + close(slave_fd); // 因为已经 dup2 了,这个原始的也可以关了 + + // 执行一个新的 bash shell + printf("--- Starting Bash Shell in Slave PTY ---\n\n"); + fflush(stdout); + execlp("/bin/sh", "sh", NULL); + + // 如果 execlp 成功,下面的代码不会被执行 + perror("execlp failed"); + exit(1); + } + + // 6. 父进程的代码 + printf("4. Forked child process with PID: %d\n", pid); + printf("5. Parent process will now forward data between stdin and master " + "PTY.\n"); + printf("--- You are now interacting with the new shell. Type 'exit' to quit. " + "---\n\n"); + + // 父进程不需要从设备 + // close(slave_fd) in parent - it was never opened here + + char buffer[256]; + ssize_t nread; + + // 循环,直到子进程退出 + while (1) { + fd_set read_fds; + FD_ZERO(&read_fds); + FD_SET(STDIN_FILENO, &read_fds); // 监听当前终端的输入 + FD_SET(master_fd, &read_fds); // 监听主设备的输出 (来自子进程shell) + + // 使用 select 阻塞,直到有数据可读 + if (select(master_fd + 1, &read_fds, NULL, NULL, NULL) < 0) { + perror("select failed"); + break; + } + + // 检查是否是当前终端有输入 + if (FD_ISSET(STDIN_FILENO, &read_fds)) { + nread = read(STDIN_FILENO, buffer, sizeof(buffer)); + if (nread > 0) { + // 将用户的输入写入主设备,数据会流向子进程的shell + write(master_fd, buffer, nread); + } else { + break; // 读错误或EOF + } + } + + // 检查是否是主设备有输出 + if (FD_ISSET(master_fd, &read_fds)) { + nread = read(master_fd, buffer, sizeof(buffer)); + if (nread > 0) { + // 将来自shell的输出写入当前终端的屏幕 + write(STDOUT_FILENO, buffer, nread); + } else { + // 读取到 0 或 -1,意味着子进程的另一端关闭了连接 + // 通常是 shell 执行了 exit + break; + } + } + } + + printf("\n--- Shell terminated. Parent process is shutting down. ---\n"); + close(master_fd); + wait(NULL); // 等待子进程完全终止 + + return 0; +} \ No newline at end of file diff --git a/user/apps/test_select/Makefile b/user/apps/test_select/Makefile new file mode 100644 index 000000000..5ed7c12fd --- /dev/null +++ b/user/apps/test_select/Makefile @@ -0,0 +1,20 @@ +ifeq ($(ARCH), x86_64) + CROSS_COMPILE=x86_64-linux-musl- +else ifeq ($(ARCH), riscv64) + CROSS_COMPILE=riscv64-linux-musl- +endif + +CC=$(CROSS_COMPILE)gcc + + +all: + $(CC) -static -o test_select main.c + +.PHONY: install clean +install: all + mv test_select $(DADK_CURRENT_BUILD_DIR)/test_select + +clean: + rm test_select *.o + +fmt: diff --git a/user/apps/test_select/main.c b/user/apps/test_select/main.c new file mode 100644 index 000000000..8078a1274 --- /dev/null +++ b/user/apps/test_select/main.c @@ -0,0 +1,87 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +// 创建 eventfd 并返回 fd +int create_eventfd() { + int fd = eventfd(0, EFD_NONBLOCK); + if (fd == -1) { + perror("eventfd"); + exit(EXIT_FAILURE); + } + return fd; +} + +// 子线程或子进程模拟事件发生 +void trigger_event(int efd, unsigned int delay_sec) { + printf("[trigger] Writing eventfd after %u seconds...\n", delay_sec); + sleep(delay_sec); + uint64_t val = 1; + if (write(efd, &val, sizeof(val)) != sizeof(val)) { + perror("write eventfd"); + exit(EXIT_FAILURE); + } + printf("[trigger] Event written to eventfd.\n"); +} + +int main() { + int efd = create_eventfd(); + + pid_t pid = fork(); + if (pid < 0) { + perror("fork"); + exit(1); + } + + if (pid == 0) { + // 子进程:触发事件 + trigger_event(efd, 3); + close(efd); + exit(0); + } + + // 父进程:使用 select 等待事件发生 + printf("[select_test] Waiting for event...\n"); + + fd_set rfds; + FD_ZERO(&rfds); + FD_SET(efd, &rfds); + + int maxfd = efd + 1; + struct timeval timeout; + timeout.tv_sec = 5; + timeout.tv_usec = 0; + + int ret = select(maxfd, &rfds, NULL, NULL, &timeout); + if (ret < 0) { + perror("select"); + exit(1); + } + printf("[select_test] select returned: %d\n", ret); + + if (FD_ISSET(efd, &rfds)) { + printf("[select_test] Event occurred on eventfd.\n"); + uint64_t val; + if (read(efd, &val, sizeof(val)) != sizeof(val)) { + perror("read"); + exit(1); + } + printf("[select_test] Received eventfd value: %lu\n", val); + } + + // wait for child process to finish + int status; + waitpid(pid, &status, 0); + printf("[parent] Child exited with status: %d\n", WEXITSTATUS(status)); + close(efd); + + return 0; +} diff --git a/user/dadk/config/test_select_0_1_1.toml b/user/dadk/config/test_select_0_1_1.toml new file mode 100644 index 000000000..67f82a9ee --- /dev/null +++ b/user/dadk/config/test_select_0_1_1.toml @@ -0,0 +1,46 @@ +# 用户程序名称 +name = "test_select" +# 版本号 +version = "0.1.0" +# 用户程序描述信息 +description = "简单的select测试程序" +# (可选)默认: false 是否只构建一次,如果为true,DADK会在构建成功后,将构建结果缓存起来,下次构建时,直接使用缓存的构建结果 +build-once = false +# (可选) 默认: false 是否只安装一次,如果为true,DADK会在安装成功后,不再重复安装 +install-once = false +# 目标架构 +# 可选值:"x86_64", "aarch64", "riscv64" +target-arch = ["x86_64"] +# 任务源 +[task-source] +# 构建类型 +# 可选值:"build-from_source", "install-from-prebuilt" +type = "build-from-source" +# 构建来源 +# "build_from_source" 可选值:"git", "local", "archive" +# "install_from_prebuilt" 可选值:"local", "archive" +source = "local" +# 路径或URL +source-path = "user/apps/test_select" +# 构建相关信息 +[build] +# (可选)构建命令 +build-command = "make install -j $(nproc)" +# 安装相关信息 +[install] +# (可选)安装到DragonOS的路径 +in-dragonos-path = "/bin" +# 清除相关信息 +[clean] +# (可选)清除命令 +clean-command = "make clean" +# (可选)依赖项 +# 注意:如果没有依赖项,忽略此项,不允许只留一个[[depends]] +# [[depends]] +# name = "depend1" +# version = "0.1.1" +# (可选)环境变量 +# 注意:如果没有环境变量,忽略此项,不允许只留一个[[envs]] +# [[envs]] +# key = "PATH" +# value = "/usr/bin"