diff --git a/Cargo.lock b/Cargo.lock index a92562a7a2..46a3b52d71 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -21,6 +21,7 @@ dependencies = [ "hashbrown", "linked_list_allocator", "redox-initfs", + "redox-path", "redox-rt", "redox-scheme", "redox_syscall", @@ -29,7 +30,7 @@ dependencies = [ [[package]] name = "generic-rt" version = "0.1.0" -source = "git+https://gitlab.redox-os.org/redox-os/relibc.git?rev=e97f26c7#e97f26c7632b1033ca062199db5633f3236821ed" +source = "git+https://gitlab.redox-os.org/redox-os/relibc#1e97bae11b1a51a1589bb6ea44cf8e507d2d1275" [[package]] name = "goblin" @@ -50,9 +51,9 @@ checksum = "e5274423e17b7c9fc20b6e7e208532f9b19825d82dfd615708b70edd83df41f1" [[package]] name = "libc" -version = "0.2.158" +version = "0.2.155" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d8adc4bb1803a324070e64a98ae98f38934d91957a99cfb3a43dcbc01bc56439" +checksum = "97b3888a4aecf77e811145cadf6eef5901f4782c53886191b2f693f24761847c" [[package]] name = "libredox" @@ -107,25 +108,31 @@ dependencies = [ [[package]] name = "quote" -version = "1.0.37" +version = "1.0.36" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b5b9d34b8991d19d98081b46eacdd8eb58c6f2b201139f7c5f643cc155a633af" +checksum = "0fa76aaf39101c457836aec0ce2316dbdc3ab723cdda1c6bd4e6ad4208acaca7" dependencies = [ "proc-macro2", ] [[package]] name = "redox-initfs" -version = "0.1.0" -source = "git+https://gitlab.redox-os.org/redox-os/redox-initfs.git#65cf406b7d4c5befc38b79696cdeb317bf06ed62" +version = "0.2.0" +source = "git+https://gitlab.redox-os.org/redox-os/redox-initfs.git#d0802237d7894881df7ddd338dfc64220d0646ff" dependencies = [ "plain", ] +[[package]] +name = "redox-path" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "436d45c2b6a5b159d43da708e62b25be3a4a3d5550d654b72216ade4c4bfd717" + [[package]] name = "redox-rt" version = "0.1.0" -source = "git+https://gitlab.redox-os.org/redox-os/relibc.git?rev=e97f26c7#e97f26c7632b1033ca062199db5633f3236821ed" +source = "git+https://gitlab.redox-os.org/redox-os/relibc#1e97bae11b1a51a1589bb6ea44cf8e507d2d1275" dependencies = [ "bitflags", "generic-rt", @@ -136,8 +143,8 @@ dependencies = [ [[package]] name = "redox-scheme" -version = "0.2.1" -source = "git+https://gitlab.redox-os.org/redox-os/redox-scheme.git#67c75441660e11280a97396714c0df3e677119bc" +version = "0.2.2" +source = "git+https://gitlab.redox-os.org/redox-os/redox-scheme.git#9a2e8a31894e397594ea4e3f033c4a91709bbbdc" dependencies = [ "libredox", "redox_syscall", @@ -189,9 +196,9 @@ dependencies = [ [[package]] name = "syn" -version = "2.0.77" +version = "2.0.71" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9f35bcdf61fd8e7be6caf75f429fdca8beb3ed76584befb503b1569faee373ed" +checksum = "b146dcf730474b4bcd16c311627b31ede9ab149045db4d6088b3becaea046462" dependencies = [ "proc-macro2", "quote", @@ -200,6 +207,6 @@ dependencies = [ [[package]] name = "unicode-ident" -version = "1.0.13" +version = "1.0.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e91b56cd4cadaeb79bbf1a5645f6b4f8dc5bde8834ad5894a8db35fda9efa1fe" +checksum = "3354b9ac3fae1ff6755cb6db53683adb661634f67557942dea4facebec0fee4b" diff --git a/Cargo.toml b/Cargo.toml index 1a533d48b5..da9af52a15 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -10,12 +10,15 @@ name = "bootstrap" crate-type = ["staticlib"] [dependencies] -hashbrown = { version = "0.14", default-features = false, features = ["inline-more"] } +hashbrown = { version = "0.14", default-features = false, features = [ + "inline-more", +] } linked_list_allocator = "0.10" redox-initfs = { git = "https://gitlab.redox-os.org/redox-os/redox-initfs.git", default-features = false } redox_syscall = "0.5.4" redox-scheme = { git = "https://gitlab.redox-os.org/redox-os/redox-scheme.git" } -redox-rt = { git = "https://gitlab.redox-os.org/redox-os/relibc.git", rev = "e97f26c7" } +redox-rt = { git = "https://gitlab.redox-os.org/redox-os/relibc", default-features = false } +redox-path = "0.3.1" [profile.release] panic = "abort" diff --git a/src/exec.rs b/src/exec.rs index 52f69c333a..ae03aa2269 100644 --- a/src/exec.rs +++ b/src/exec.rs @@ -1,8 +1,8 @@ use alloc::borrow::ToOwned; use alloc::vec::Vec; -use syscall::{Error, EINTR}; use syscall::flag::{O_CLOEXEC, O_RDONLY}; +use syscall::{Error, EINTR}; use redox_rt::proc::*; @@ -28,27 +28,41 @@ pub fn main() -> ! { iter().map(|var| var.to_owned()).collect::>() }; - extern { + extern "C" { // The linker script will define this as the location of the initfs header. static __initfs_header: u8; } - let initfs_length = unsafe { (*(core::ptr::addr_of!(__initfs_header) as *const redox_initfs::types::Header)).initfs_size }; + let initfs_length = unsafe { + (*(core::ptr::addr_of!(__initfs_header) as *const redox_initfs::types::Header)).initfs_size + }; unsafe { // Creating a reference to NULL is UB. Mask the UB for now using black_box. // FIXME use a raw pointer and inline asm for reading instead for the initfs header. - spawn_initfs(core::ptr::addr_of!(__initfs_header), initfs_length.get() as usize); + spawn_initfs( + core::ptr::addr_of!(__initfs_header), + initfs_length.get() as usize, + ); } const CWD: &[u8] = b"/scheme/initfs"; + const DEFAULT_SCHEME: &[u8] = b"initfs"; let extrainfo = ExtraInfo { cwd: Some(CWD), + default_scheme: Some(DEFAULT_SCHEME), sigprocmask: 0, sigignmask: 0, }; let path = "/scheme/initfs/bin/init"; - let total_args_envs_auxvpointee_size = path.len() + 1 + envs.len() + envs.iter().map(|v| v.len()).sum::() + CWD.len() + 1; + let total_args_envs_auxvpointee_size = path.len() + + 1 + + envs.len() + + envs.iter().map(|v| v.len()).sum::() + + CWD.len() + + 1 + + DEFAULT_SCHEME.len() + + 1; let image_file = FdGuard::new(syscall::open(path, O_RDONLY).expect("failed to open init")); let open_via_dup = FdGuard::new( @@ -57,7 +71,18 @@ pub fn main() -> ! { ); let memory = FdGuard::new(syscall::open("/scheme/memory", 0).expect("failed to open memory")); - fexec_impl(image_file, open_via_dup, &memory, path.as_bytes(), [path], envs.iter(), total_args_envs_auxvpointee_size, &extrainfo, None).expect("failed to execute init"); + fexec_impl( + image_file, + open_via_dup, + &memory, + path.as_bytes(), + [path], + envs.iter(), + total_args_envs_auxvpointee_size, + &extrainfo, + None, + ) + .expect("failed to execute init"); unreachable!() } @@ -89,5 +114,8 @@ unsafe fn spawn_initfs(initfs_start: *const u8, initfs_length: usize) { return; } } - crate::initfs::run(core::slice::from_raw_parts(initfs_start, initfs_length), write); + crate::initfs::run( + core::slice::from_raw_parts(initfs_start, initfs_length), + write, + ); } diff --git a/src/initfs.rs b/src/initfs.rs index 48389949dd..6b96ea1f60 100644 --- a/src/initfs.rs +++ b/src/initfs.rs @@ -6,8 +6,9 @@ use core::str; use alloc::string::String; use hashbrown::HashMap; -use redox_initfs::{InitFs, InodeStruct, Inode, InodeDir, InodeKind, types::Timespec}; +use redox_initfs::{types::Timespec, InitFs, Inode, InodeDir, InodeKind, InodeStruct}; +use redox_path::canonicalize_to_standard; use redox_scheme::CallerCtx; use redox_scheme::OpenResult; use redox_scheme::RequestKind; @@ -55,7 +56,6 @@ impl InitFsScheme { } } - struct Iter { dir: InodeDir<'static>, idx: u32, @@ -71,7 +71,8 @@ impl Iterator for Iter { fn size_hint(&self) -> (usize, Option) { match self.dir.entry_count().ok() { Some(size) => { - let size = usize::try_from(size).expect("expected u32 to be convertible into usize"); + let size = + usize::try_from(size).expect("expected u32 to be convertible into usize"); (size, Some(size)) } None => (0, None), @@ -82,14 +83,18 @@ impl Iterator for Iter { fn inode_len(inode: InodeStruct<'static>) -> Result { Ok(match inode.kind() { InodeKind::File(file) => file.data().map_err(|_| Error::new(EIO))?.len(), - InodeKind::Dir(dir) => (Iter { dir, idx: 0 }) - .fold(0, |len, entry| len + entry.and_then(|entry| entry.name().map_err(|_| Error::new(EIO))).map_or(0, |name| name.len() + 1)), + InodeKind::Dir(dir) => (Iter { dir, idx: 0 }).fold(0, |len, entry| { + len + entry + .and_then(|entry| entry.name().map_err(|_| Error::new(EIO))) + .map_or(0, |name| name.len() + 1) + }), + InodeKind::Link(link) => link.data().map_err(|_| Error::new(EIO))?.len(), InodeKind::Unknown => return Err(Error::new(EIO)), }) } impl SchemeMut for InitFsScheme { - fn xopen(&mut self, path: &str, _flags: usize, ctx: &CallerCtx) -> Result { + fn xopen(&mut self, path: &str, flags: usize, ctx: &CallerCtx) -> Result { let mut components = path // trim leading and trailing slash .trim_matches('/') @@ -105,7 +110,7 @@ impl SchemeMut for InitFsScheme { "." => continue, ".." => { let _ = components.next_back(); - continue + continue; } _ => (), @@ -116,16 +121,18 @@ impl SchemeMut for InitFsScheme { let dir = match current_inode_struct.kind() { InodeKind::Dir(dir) => dir, + // TODO: Support symlinks in other position than xopen target + InodeKind::Link(link) => { + return Err(Error::new(EOPNOTSUPP)); + } + // If we still have more components in the path, and the file tree for that // particular branch is not all directories except the last, then that file cannot // exist. InodeKind::File(_) | InodeKind::Unknown => return Err(Error::new(ENOENT)), }; - let mut entries = Iter { - dir, - idx: 0, - }; + let mut entries = Iter { dir, idx: 0 }; current_inode = loop { let entry_res = match entries.next() { @@ -140,17 +147,40 @@ impl SchemeMut for InitFsScheme { }; } + // xopen target is link -- return EXDEV so that the file is opened as a link. + // TODO: Maybe follow initfs-local symlinks here? Would be faster + let is_link = matches!( + Self::get_inode(&self.fs, current_inode)?.kind(), + InodeKind::Link(_) + ); + let o_symlink = flags & O_SYMLINK != 0; + if is_link && !o_symlink { + return Err(Error::new(EXDEV)); + } + let id = self.next_id(); - let old = self.handles.insert(id, Handle { - inode: current_inode, - filename: path.into(), - }); + let old = self.handles.insert( + id, + Handle { + inode: current_inode, + filename: path.into(), + }, + ); assert!(old.is_none()); - Ok(OpenResult::ThisScheme { number: id, flags: NewFdFlags::POSITIONED }) + Ok(OpenResult::ThisScheme { + number: id, + flags: NewFdFlags::POSITIONED, + }) } - fn read(&mut self, id: usize, buffer: &mut [u8], offset: u64, _fcntl_flags: u32) -> Result { + fn read( + &mut self, + id: usize, + buffer: &mut [u8], + offset: u64, + _fcntl_flags: u32, + ) -> Result { let Ok(offset) = usize::try_from(offset) else { return Ok(0); }; @@ -168,10 +198,29 @@ impl SchemeMut for InitFsScheme { Ok(to_copy) } InodeKind::Dir(_) => Err(Error::new(EISDIR)), + InodeKind::Link(link) => { + let link_data = link.data().map_err(|_| Error::new(EIO))?; + let path = core::str::from_utf8(link_data).map_err(|_| Error::new(ENOENT))?; + let cannonical = + canonicalize_to_standard(Some("/"), path).ok_or_else(|| Error::new(ENOENT))?; + let data = cannonical.as_bytes(); + + let src_buf = &data[core::cmp::min(offset, data.len())..]; + + let to_copy = core::cmp::min(src_buf.len(), buffer.len()); + buffer[..to_copy].copy_from_slice(&src_buf[..to_copy]); + + Ok(to_copy) + } InodeKind::Unknown => Err(Error::new(EIO)), } } - fn getdents<'buf>(&mut self, id: usize, mut buf: DirentBuf<&'buf mut [u8]>, opaque_offset: u64) -> Result> { + fn getdents<'buf>( + &mut self, + id: usize, + mut buf: DirentBuf<&'buf mut [u8]>, + opaque_offset: u64, + ) -> Result> { let Ok(offset) = u32::try_from(opaque_offset) else { return Ok(buf); }; @@ -187,7 +236,11 @@ impl SchemeMut for InitFsScheme { //inode: entry.inode(), inode: 0, - name: entry.name().ok().and_then(|utf8| core::str::from_utf8(utf8).ok()).ok_or(Error::new(EIO))?, + name: entry + .name() + .ok() + .and_then(|utf8| core::str::from_utf8(utf8).ok()) + .ok_or(Error::new(EIO))?, next_opaque_id: index as u64 + 1, kind: DirentKind::Unspecified, })?; @@ -229,7 +282,12 @@ impl SchemeMut for InitFsScheme { let inode = Self::get_inode(&self.fs, handle.inode)?; - stat.st_mode = inode.mode() | match inode.kind() { InodeKind::Dir(_) => MODE_DIR, InodeKind::File(_) => MODE_FILE, _ => 0 }; + stat.st_mode = inode.mode() + | match inode.kind() { + InodeKind::Dir(_) => MODE_DIR, + InodeKind::File(_) => MODE_FILE, + _ => 0, + }; stat.st_uid = inode.uid(); stat.st_gid = inode.gid(); stat.st_size = u64::try_from(inode_len(inode)?).unwrap_or(u64::MAX); @@ -259,14 +317,16 @@ impl SchemeMut for InitFsScheme { pub fn run(bytes: &'static [u8], sync_pipe: usize) -> ! { let mut scheme = InitFsScheme::new(bytes); - let socket = Socket::::create("initfs") - .expect("failed to open initfs scheme socket"); + let socket = Socket::::create("initfs").expect("failed to open initfs scheme socket"); let _ = syscall::write(sync_pipe, &[0]); let _ = syscall::close(sync_pipe); loop { - let RequestKind::Call(req) = (match socket.next_request(SignalBehavior::Restart).expect("bootstrap: failed to read scheme request from kernel") { + let RequestKind::Call(req) = (match socket + .next_request(SignalBehavior::Restart) + .expect("bootstrap: failed to read scheme request from kernel") + { Some(req) => req.kind(), None => break, }) else { @@ -274,7 +334,10 @@ pub fn run(bytes: &'static [u8], sync_pipe: usize) -> ! { }; let resp = req.handle_scheme_mut(&mut scheme); - if !socket.write_response(resp, SignalBehavior::Restart).expect("bootstrap: failed to write scheme response to kernel") { + if !socket + .write_response(resp, SignalBehavior::Restart) + .expect("bootstrap: failed to write scheme response to kernel") + { break; } } diff --git a/src/lib.rs b/src/lib.rs index 3c45afb000..66d3c7d8f3 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,10 +1,6 @@ #![no_std] #![allow(internal_features)] -#![feature( - asm_const, - core_intrinsics, - str_from_raw_parts, -)] +#![feature(asm_const, core_intrinsics, str_from_raw_parts, iter_intersperse)] #[cfg(target_arch = "aarch64")] #[path = "aarch64.rs"] @@ -35,7 +31,9 @@ fn panic_handler(info: &core::panic::PanicInfo) -> ! { impl Write for Writer { fn write_str(&mut self, s: &str) -> core::fmt::Result { - syscall::write(1, s.as_bytes()).map_err(|_| core::fmt::Error).map(|_| ()) + syscall::write(1, s.as_bytes()) + .map_err(|_| core::fmt::Error) + .map(|_| ()) } } @@ -62,8 +60,19 @@ unsafe impl alloc::alloc::GlobalAlloc for Allocator { unsafe fn alloc(&self, layout: core::alloc::Layout) -> *mut u8 { let heap = HEAP.get_or_insert_with(|| { HEAP_TOP = HEAP_OFF + SIZE; - let _ = syscall::fmap(!0, &Map { offset: 0, size: SIZE, address: HEAP_OFF, flags: MapFlags::PROT_WRITE | MapFlags::PROT_READ | MapFlags::MAP_PRIVATE | MapFlags::MAP_FIXED_NOREPLACE }) - .expect("failed to map initial heap"); + let _ = syscall::fmap( + !0, + &Map { + offset: 0, + size: SIZE, + address: HEAP_OFF, + flags: MapFlags::PROT_WRITE + | MapFlags::PROT_READ + | MapFlags::MAP_PRIVATE + | MapFlags::MAP_FIXED_NOREPLACE, + }, + ) + .expect("failed to map initial heap"); linked_list_allocator::Heap::new(HEAP_OFF as *mut u8, SIZE) }); @@ -74,8 +83,19 @@ unsafe impl alloc::alloc::GlobalAlloc for Allocator { return core::ptr::null_mut(); } - let _ = syscall::fmap(!0, &Map { offset: 0, size: HEAP_INCREASE_BY, address: HEAP_TOP, flags: MapFlags::PROT_WRITE | MapFlags::PROT_READ | MapFlags::MAP_PRIVATE | MapFlags::MAP_FIXED_NOREPLACE }) - .expect("failed to extend heap"); + let _ = syscall::fmap( + !0, + &Map { + offset: 0, + size: HEAP_INCREASE_BY, + address: HEAP_TOP, + flags: MapFlags::PROT_WRITE + | MapFlags::PROT_READ + | MapFlags::MAP_PRIVATE + | MapFlags::MAP_FIXED_NOREPLACE, + }, + ) + .expect("failed to extend heap"); heap.extend(HEAP_INCREASE_BY); HEAP_TOP += HEAP_INCREASE_BY; @@ -84,6 +104,8 @@ unsafe impl alloc::alloc::GlobalAlloc for Allocator { } } unsafe fn dealloc(&self, ptr: *mut u8, layout: core::alloc::Layout) { - HEAP.as_mut().unwrap().deallocate(core::ptr::NonNull::new(ptr).unwrap(), layout) + HEAP.as_mut() + .unwrap() + .deallocate(core::ptr::NonNull::new(ptr).unwrap(), layout) } } diff --git a/src/start.rs b/src/start.rs index f8b2ec4c87..ebaeb17505 100644 --- a/src/start.rs +++ b/src/start.rs @@ -13,13 +13,28 @@ mod offsets { static __bss_end: u8; } pub fn text() -> (usize, usize) { - unsafe { (&__text_start as *const u8 as usize, &__text_end as *const u8 as usize) } + unsafe { + ( + &__text_start as *const u8 as usize, + &__text_end as *const u8 as usize, + ) + } } pub fn rodata() -> (usize, usize) { - unsafe { (&__rodata_start as *const u8 as usize, &__rodata_end as *const u8 as usize) } + unsafe { + ( + &__rodata_start as *const u8 as usize, + &__rodata_end as *const u8 as usize, + ) + } } pub fn data_and_bss() -> (usize, usize) { - unsafe { (&__data_start as *const u8 as usize, &__bss_end as *const u8 as usize) } + unsafe { + ( + &__data_start as *const u8 as usize, + &__bss_end as *const u8 as usize, + ) + } } } @@ -35,12 +50,33 @@ pub unsafe extern "C" fn start() -> ! { let _ = syscall::open("/scheme/debug", syscall::O_WRONLY); // stdout let _ = syscall::open("/scheme/debug", syscall::O_WRONLY); // stderr - let _ = syscall::mprotect(4096, 4096, MapFlags::PROT_READ | MapFlags::MAP_PRIVATE).expect("mprotect failed for initfs header page"); + let _ = syscall::mprotect(4096, 4096, MapFlags::PROT_READ | MapFlags::MAP_PRIVATE) + .expect("mprotect failed for initfs header page"); - let _ = syscall::mprotect(text_start, text_end - text_start, MapFlags::PROT_READ | MapFlags::PROT_EXEC | MapFlags::MAP_PRIVATE).expect("mprotect failed for .text"); - let _ = syscall::mprotect(rodata_start, rodata_end - rodata_start, MapFlags::PROT_READ | MapFlags::MAP_PRIVATE).expect("mprotect failed for .rodata"); - let _ = syscall::mprotect(data_start, data_end - data_start, MapFlags::PROT_READ | MapFlags::PROT_WRITE | MapFlags::MAP_PRIVATE).expect("mprotect failed for .data/.bss"); - let _ = syscall::mprotect(data_end, crate::arch::STACK_START - data_end, MapFlags::PROT_READ | MapFlags::MAP_PRIVATE).expect("mprotect failed for rest of memory"); + let _ = syscall::mprotect( + text_start, + text_end - text_start, + MapFlags::PROT_READ | MapFlags::PROT_EXEC | MapFlags::MAP_PRIVATE, + ) + .expect("mprotect failed for .text"); + let _ = syscall::mprotect( + rodata_start, + rodata_end - rodata_start, + MapFlags::PROT_READ | MapFlags::MAP_PRIVATE, + ) + .expect("mprotect failed for .rodata"); + let _ = syscall::mprotect( + data_start, + data_end - data_start, + MapFlags::PROT_READ | MapFlags::PROT_WRITE | MapFlags::MAP_PRIVATE, + ) + .expect("mprotect failed for .data/.bss"); + let _ = syscall::mprotect( + data_end, + crate::arch::STACK_START - data_end, + MapFlags::PROT_READ | MapFlags::MAP_PRIVATE, + ) + .expect("mprotect failed for rest of memory"); redox_rt::initialize_freestanding();