//
// Syd: rock-solid application kernel
// src/kernel/open.rs: creat(2), open(2), openat(2), and openat2(2) handlers
//
// Copyright (c) 2023, 2024, 2025 Ali Polatel <alip@chesswob.org>
//
// SPDX-License-Identifier: GPL-3.0

use std::{
    borrow::Cow,
    env,
    ffi::CStr,
    fs::File,
    io::{BufWriter, Seek, Write},
    os::{
        fd::{AsFd, AsRawFd, IntoRawFd, OwnedFd, RawFd},
        unix::ffi::OsStrExt,
    },
};

use libseccomp::ScmpNotifResp;
use nix::{
    errno::Errno,
    fcntl::OFlag,
    sys::stat::Mode,
    unistd::{Pid, Uid},
    NixPath,
};

use crate::{
    compat::{fstatx, FsType, OpenHow, ResolveFlag, STATX_MODE, STATX_UID},
    config::{
        ENV_ID, ESYD_SH, MAGIC_PREFIX, NULL_FD, PINK_FLOYD, PROC_FILE, SAFE_MFD_FLAGS,
        SEE_EMILY_PLAY, SYD_EL, THE_PIPER,
    },
    confine::EIDRM,
    cookie::{safe_memfd_create, safe_openat2, safe_umask, safe_uname},
    err::err2no,
    error,
    fd::{is_dev_kfd, seal_memfd_all},
    fs::{format_oflags, oflag_accmode, oflag_nonblock},
    hash::aes_ctr_tmp,
    info, log_enabled,
    lookup::{
        safe_open_how_msym, safe_open_msym, CanonicalPath, FileInfo, FileType, FsFlags, MaybeFd,
    },
    mask::mask_proc_pid_status,
    ofd::{lock_fd, unlock_fd},
    path::{XPath, XPathBuf},
    proc::{proc_tty, proc_umask},
    req::{SysArg, SysFlags, UNotifyEventRequest},
    retry::retry_on_eintr,
    sandbox::{Action, Capability, Sandbox},
    syslog::LogLevel,
    warn,
    workers::aes::{AesMod, AesVal},
};

// `OpenSyscall` represents possible open family system calls.
//
//  The list of open family system calls are: creat(2), open(2),
//  openat(2), and openat2(2).
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
enum OpenSyscall {
    Creat,
    Open,
    Openat,
    Openat2,
}

pub(crate) fn sys_creat(request: UNotifyEventRequest) -> ScmpNotifResp {
    let req = request.scmpreq;

    // SAFETY: Reject invalid mode.
    let mode = match req.data.args[1].try_into() {
        Ok(mode) => mode,
        Err(_) => return request.fail_syscall(Errno::EINVAL),
    };
    let mode = match Mode::from_bits(mode) {
        Some(mode) => mode,
        None => return request.fail_syscall(Errno::EINVAL),
    };

    // Here we omit MUST_PATH in fsflags as path may not exist yet.
    let arg = SysArg {
        path: Some(0),
        fsflags: FsFlags::empty(),
        ..Default::default()
    };
    let flags = OFlag::O_CREAT | OFlag::O_WRONLY | OFlag::O_TRUNC;

    // Path pointer will be validated by open handler
    // after further flag validation.
    syscall_open_handler(request, OpenSyscall::Creat, arg, flags, mode)
}

pub(crate) fn sys_open(request: UNotifyEventRequest) -> ScmpNotifResp {
    let req = request.scmpreq;

    // SAFETY: Truncate undefined/invalid flags.
    let flags = match to_oflag(req.data.args[1], true) {
        Ok(flags) => flags,
        Err(errno) => return request.fail_syscall(errno),
    };

    // SAFETY:
    // 1. Reject undefined/invalid mode.
    // 2. Ignore mode if no creation flags are passed.
    let mode = match to_mode(req.data.args[2], flags, true) {
        Ok(mode) => mode,
        Err(errno) => return request.fail_syscall(errno),
    };

    // Path pointer will be validated by open handler
    // after further flag validation.
    let arg = SysArg::open(flags, false, ResolveFlag::empty());
    syscall_open_handler(request, OpenSyscall::Open, arg, flags, mode)
}

pub(crate) fn sys_openat(request: UNotifyEventRequest) -> ScmpNotifResp {
    let req = request.scmpreq;

    // SAFETY: Truncate undefined/invalid flags.
    let flags = match to_oflag(req.data.args[2], true) {
        Ok(flags) => flags,
        Err(errno) => return request.fail_syscall(errno),
    };

    // SAFETY:
    // 1. Reject undefined/invalid mode.
    // 2. Ignore mode if no creation flags are passed.
    let mode = match to_mode(req.data.args[3], flags, true) {
        Ok(mode) => mode,
        Err(errno) => return request.fail_syscall(errno),
    };

    // Path pointer will be validated by open handler
    // after further flag validation.
    let arg = SysArg::open(flags, true, ResolveFlag::empty());
    syscall_open_handler(request, OpenSyscall::Openat, arg, flags, mode)
}

pub(crate) fn sys_openat2(request: UNotifyEventRequest) -> ScmpNotifResp {
    let req = request.scmpreq;

    // SAFETY: remote_ohow validates address and size.
    let open_how = match request.remote_ohow(req.data.args[2], req.data.args[3]) {
        Ok(open_how) => open_how,
        Err(errno) => {
            return request.fail_syscall(errno);
        }
    };

    // SAFETY: Reject undefined and invalid flags.
    let flags = match to_oflag(open_how.flags, false) {
        Ok(flags) => flags,
        Err(errno) => return request.fail_syscall(errno),
    };

    // SAFETY: Return EINVAL for invalid resolve flags or combinations.
    let rflags = match ResolveFlag::from_bits(open_how.resolve) {
        Some(rflags)
            if rflags.contains(ResolveFlag::RESOLVE_BENEATH | ResolveFlag::RESOLVE_IN_ROOT) =>
        {
            return request.fail_syscall(Errno::EINVAL);
        }
        Some(rflags) => rflags,
        None => return request.fail_syscall(Errno::EINVAL),
    };

    // SAFETY:
    // 1. Reject undefined/invalid mode.
    // 2. Reject non-zero mode if no creation flags are passed.
    let mode = match to_mode(open_how.mode, flags, false) {
        Ok(mode) => mode,
        Err(errno) => return request.fail_syscall(errno),
    };

    // Path pointer will be validated by open handler
    // after further flag validation.
    let arg = SysArg::open(flags, true, rflags);
    syscall_open_handler(request, OpenSyscall::Openat2, arg, flags, mode)
}

#[expect(clippy::cognitive_complexity)]
fn syscall_open_handler(
    request: UNotifyEventRequest,
    syscall: OpenSyscall,
    arg: SysArg,
    flags: OFlag,
    mode: Mode,
) -> ScmpNotifResp {
    syscall_handler!(request, |request: UNotifyEventRequest| {
        // SAFETY: When emulating the open call we always open the file
        // descriptor with O_CLOEXEC flag for safety. Here, we mark the
        // state of the O_CLOEXEC flag to use it in seccomp_addfd call.
        // Mutability:
        //   1. o_cloexec is mutable because trace/open_force_cloexec:1
        //      may change its value for O_CLOEXEC.
        //   2. flags is mutable because trace/allow_unsafe_open:0
        //      may change its value for O_PATH. Append-only may
        //      also edit flags.
        //   3. flags is mutable because trace/allow_unsafe_create:0
        //      may insert O_NOFOLLOW when O_CREAT is set but O_EXCL
        //      is not set.
        let mut o_cloexec = flags.contains(OFlag::O_CLOEXEC);
        let mut flags = flags | OFlag::O_CLOEXEC;

        // Determine capabilities based on access mode:
        //
        // TODO: Confine O_PATH file descriptors with Stat category,
        // rather than Read category when they can be properly emulated.
        // See: https://bugzilla.kernel.org/show_bug.cgi?id=218501
        let (mut caps, o_path, o_rdwr) = match oflag_accmode(flags) {
            OFlag::O_RDONLY => (Capability::CAP_READ, false, false),
            OFlag::O_WRONLY => (Capability::CAP_WRITE, false, false),
            OFlag::O_RDWR => (Capability::CAP_READ | Capability::CAP_WRITE, false, true),
            OFlag::O_PATH => (Capability::CAP_READ, true, false),
            _ => return Err(Errno::EINVAL), // Invalid access mode.
        };

        let o_creat = flags.contains(OFlag::O_CREAT);
        let o_tmpfl = flags.contains(OFlag::O_TMPFILE);
        let o_trunc = flags.contains(OFlag::O_TRUNC);
        let mut o_mask = o_creat || o_tmpfl;

        // O_PATH|O_CREAT, O_PATH|O_TMPFILE, and O_CREAT|O_TMPFILE
        // combinations are invalid.
        if (o_path && o_mask) || (o_creat && o_tmpfl) {
            return Err(Errno::EINVAL);
        }

        // O_TMPFILE must be combined with O_WRONLY or O_RDWR.
        if o_tmpfl && !caps.contains(Capability::CAP_WRITE) {
            return Err(Errno::EINVAL);
        }

        let req = request.scmpreq;
        let sandbox = request.get_sandbox();

        // Check for chroot.
        if sandbox.is_chroot() {
            return Err(Errno::ENOENT);
        }

        // We use exactly one of Mktemp, Create, Truncate sandboxing
        // categories to improve usability, ie the priority is
        // O_TMPFILE > O_CREAT > O_TRUNC.
        if o_tmpfl {
            caps.insert(Capability::CAP_MKTEMP);
        } else if o_creat {
            caps.insert(Capability::CAP_CREATE);
        } else if o_trunc {
            caps.insert(Capability::CAP_TRUNCATE);
        }

        let can_write = caps.can_write();
        if can_write && sandbox.flags.force_ro_open() {
            // SAFETY: Deny write attempt with trace/force_ro_open:1.
            return Err(Errno::EACCES);
        }

        if sandbox.log_scmp() {
            crate::debug!("ctx": "open", "op": "init",
                "cap": caps,
                "sbc": *sandbox.state,
                "arg": format!("{arg:?}"),
                "flg": format!("{flags:?}"),
                "mod": format!("{mode:?}"),
                "req": &request);
        } else {
            crate::debug!("ctx": "open", "op": "init",
                "cap": caps,
                "sbc": *sandbox.state,
                "arg": format!("{arg:?}"),
                "flg": format!("{flags:?}"),
                "mod": format!("{mode:?}"),
                "pid": request.scmpreq.pid);
        }

        // SAFETY: Apply deny_dotdot as necessary for open family.
        let mut arg = arg;
        if sandbox.flags.deny_dotdot() {
            arg.fsflags.insert(FsFlags::NO_RESOLVE_DOTDOT);
        }

        // SAFETY: Prevent file creation through dangling symlinks.
        let restrict_create = !sandbox.flags.allow_unsafe_create();
        if o_creat && restrict_create && !flags.contains(OFlag::O_EXCL) {
            flags.insert(OFlag::O_NOFOLLOW);
            arg.fsflags.insert(FsFlags::NO_FOLLOW_LAST);
        }

        // Read the remote path and check for magic path.
        arg.flags.insert(SysFlags::CHECK_MAGIC);
        let (mut path, magic, _) = request.read_path(&sandbox, arg)?;

        if sandbox.log_scmp() {
            crate::debug!("ctx": "open", "op": "read_path",
                "path": &path, "magic": magic,
                "req": &request);
        } else {
            crate::debug!("ctx": "open", "op": "read_path",
                "path": &path, "magic": magic,
                "pid": request.scmpreq.pid().as_raw());
        }

        // Handle the special /dev/syd paths.
        if magic {
            // Check sandbox lock for Read access.
            //
            // a. LockState::Set allows no access.
            // b. LockState::Read allows open(2) based read-only access.
            //
            // `locked_read_for` returns true for LockState::Set, and
            // false for LockState::Read.
            if sandbox.locked_read_for(req.pid()) {
                return Err(Errno::ENOENT);
            }
            if o_path {
                return Err(Errno::EINVAL);
            }

            let fd = if caps.contains(Capability::CAP_READ) {
                let mut file = safe_memfd_create(c"syd/box", *SAFE_MFD_FLAGS).map(File::from)?;

                const FLEN: usize = MAGIC_PREFIX.len() + 3; /* .{el,sh} */
                let fack = path.abs().len() == FLEN;
                let fext = path.abs().extension();
                let data = if path.abs().is_equal(MAGIC_PREFIX) {
                    Cow::Owned(serde_json::to_string_pretty(&*sandbox).or(Err(Errno::EINVAL))?)
                } else if fack && fext.map(|ext| ext.is_equal(b"el")).unwrap_or(false) {
                    Cow::Borrowed(SYD_EL)
                } else if fack && fext.map(|ext| ext.is_equal(b"sh")).unwrap_or(false) {
                    Cow::Borrowed(ESYD_SH)
                } else if path.abs().is_equal(b"/dev/syd/pink") {
                    Cow::Borrowed(PINK_FLOYD)
                } else if path.abs().is_equal(b"/dev/syd/pipe") {
                    Cow::Borrowed(THE_PIPER)
                } else if path.abs().is_equal(b"/dev/syd/play") {
                    Cow::Borrowed(SEE_EMILY_PLAY)
                } else {
                    return Err(Errno::EINVAL);
                };
                drop(sandbox); // release the read-lock.

                file.write_all(data.as_bytes()).or(Err(Errno::EIO))?;
                file.rewind().or(Err(Errno::EIO))?;

                // SAFETY: Deny further writes to the file descriptor.
                seal_memfd_all(&file)?;

                MaybeFd::Owned(file.into())
            } else {
                MaybeFd::RawFd(NULL_FD())
            };

            // Send the file descriptor to the process and return the fd no.
            // SAFETY: Randomize FD to prevent reuse.
            return request.send_fd(fd, true, true);
        }

        // Validate file flags based on file type information.
        //
        // is_dir: Used for mask to determine directory override.
        // is_blocking: Used by interrupt handler to unblock FIFOs.
        // may_crypt: Used by Crypt sandboxing.
        let (is_dir, is_blocking, may_crypt) = if let Some(ref file_type) = path.typ {
            if o_creat {
                // Creating open with existing file:
                // Remove Create from capabilities, and add back
                // Truncate capability as necessary to improve
                // usability.
                caps.remove(Capability::CAP_CREATE);
                if o_trunc {
                    caps.insert(Capability::CAP_TRUNCATE);
                }
            }

            match *file_type {
                FileType::Reg => {
                    // SAFETY:
                    // 1. We only ever attempt to encrypt regular files.
                    // 2. We do not support interruptions on regular file blocks.
                    (false, false, true)
                }
                FileType::Dir => {
                    // We know it's a directory, so let's assert it.
                    // Unless path ends with a slash, in that case
                    // we don't want to break expectations.
                    if path.abs().last() != Some(b'/') {
                        flags.insert(OFlag::O_DIRECTORY);
                    }

                    // Change capability from Read to Readdir.
                    caps.remove(Capability::CAP_READ);
                    caps.insert(Capability::CAP_READDIR);

                    (true, false, false)
                }
                FileType::Mfd => {
                    // SAFETY:
                    // 1. We do not encrypt memory fds.
                    // 2. We do not support interruptions on memory fd blocks.
                    (false, false, false)
                }
                FileType::MagicLnk(_) => {
                    // SAFETY/TODO: Implement further restrictions on magic-links here.
                    if file_type.is_magic_dir() {
                        // Change capability from Read to Readdir.
                        caps.remove(Capability::CAP_READ);
                        caps.insert(Capability::CAP_READDIR);
                    }

                    // This may or may not block, better safe than sorry.
                    (false, !oflag_nonblock(flags), false)
                }
                FileType::Lnk => {
                    // This may or may not block, better safe than sorry.
                    (false, !oflag_nonblock(flags), false)
                }
                FileType::Chr | FileType::Fifo | FileType::Sock => {
                    // Character devices, fifos and sockets may block.
                    (false, !oflag_nonblock(flags), false)
                }
                FileType::Blk => {
                    // SAFETY:
                    // 1. Do not allow access to block devices.
                    // 2. Restriction may be relaxed with trace/allow_unsafe_mkbdev:1.
                    // 3. Deny with ENOENT for stealth.
                    if !sandbox.flags.allow_unsafe_mkbdev() {
                        return Err(Errno::ENOENT);
                    }

                    // Block devices may block.
                    (false, !oflag_nonblock(flags), false)
                }
                FileType::Unk => {
                    // SAFETY:
                    // 1. Do not allow access to files with unknown types.
                    // 2. Deny with ENOENT for stealth.
                    return Err(Errno::ENOENT);
                }
            }
        } else if !o_creat {
            // Non-creating open on non-existing file.
            return Err(Errno::ENOENT);
        } else {
            // Creating open on non-existing file.
            //
            // Note, adding O_EXCL to flags here to assert file creation
            // may result in a race condition where a fellow Syd thread
            // can race with this one in creating the same file, making
            // the thread losing the race return a confusing EEXIST error.
            // Therefore we avoid doing that, see:
            // https://gitlab.exherbo.org/sydbox/sydbox/-/issues/211
            //
            // flags.insert(OFlag::O_EXCL);

            // Non-existing files are not directories.
            // Non-existing files do not block.
            // Non-existing files may be encrypted from scratch.
            (false, false, true)
        };

        // SAFETY:
        // 1. We must provide safe access to sandbox process' controlling terminal.
        // 2. Both the virtual "/dev/console" or "/dev/tty" and the replacing tty
        //    path are checked for access.
        let pid = req.pid();
        let mut path_tty = None;
        if path.is_char_device() {
            const TTY: &[&[u8]] = &[b"/dev/tty", b"/dev/console"];
            for p in TTY {
                if path.abs().is_equal(p) {
                    let real_tty = proc_tty(pid)?;
                    if !request.is_valid() {
                        return Err(Errno::ESRCH);
                    }
                    path = CanonicalPath::new_tty(real_tty)?;
                    path_tty = Some(Cow::Borrowed(XPath::from_bytes(p)));
                    break;
                }
            }
        }
        let has_tty = path_tty.is_some();

        // SAFETY: Ensure the sandbox process cannot
        // acquire a new controlling terminal other
        // than what they already have.
        // Note, O_NOCTTY is invalid with O_PATH,
        // but we don't care for now since we'll change it
        // to O_RDONLY later as a mitigation due to our
        // inability to emulate them.
        // See: https://bugzilla.kernel.org/show_bug.cgi?id=218501
        flags.insert(OFlag::O_NOCTTY);

        // Sandboxing.
        let orig_caps = caps;
        let caps = sandbox.getcaps(orig_caps); // Get enabled caps.

        let mut action = Action::Allow;
        let mut filter = false;

        // Convert /proc/$pid to /proc/self as necessary.
        let mut path_check = path.abs().replace_proc_self(pid);

        if !caps.is_empty() {
            // If open is a TTY both the virtual and real path are checked for access.
            // The more critical action wins.
            let mut flip_tty = false;
            for (idx, path) in [Some(&path_check), path_tty.as_ref()]
                .iter()
                .flatten()
                .enumerate()
            {
                for cap in caps {
                    let (new_action, new_filter) = sandbox.check_path(cap, path);
                    if new_action >= action {
                        action = new_action;
                        filter = new_filter;
                        if idx > 0 && new_action > Action::Allow {
                            flip_tty = true;
                        }
                    }
                }
            }
            #[expect(clippy::disallowed_methods)]
            if flip_tty {
                path_check = path_tty.unwrap()
            }
        }

        let (hidden, mut crypted) = if action.is_denying() {
            // No need to check for mask when denying.
            // No need to check for encryption when denying.
            (sandbox.is_hidden(&path_check), false)
        } else {
            // No need for hidden check if we're allowing.
            // SAFETY:
            // 1. Check for encrypted path and mark for later.
            // 2. Check for masked path and change path to /dev/null.
            // 3. Check for append-only path and edit flags argument.
            // We perform these check only if we're allowing.
            let crypted = if let Some(mask) = sandbox.is_masked(&path_check) {
                let mask = if let Some(mask_dir) = &mask.mask_dir {
                    // Override mask for directories as necessary.
                    if is_dir {
                        Some(mask_dir)
                    } else {
                        mask.mask_all.as_ref()
                    }
                } else {
                    mask.mask_all.as_ref()
                };
                match mask {
                    None => path = CanonicalPath::new_null(),
                    Some(mask) => path = CanonicalPath::new_mask(mask, &path_check)?,
                };
                // SAFETY: Mask path is not subject to sandbox check!
                // `path_check' is only used for logging and points
                // to the original path.
                path_check = Cow::Borrowed(path.abs());
                false // masked path is not encrypted.
            } else if sandbox.enabled(Capability::CAP_CRYPT) {
                may_crypt && sandbox.is_crypt(&path_check)
            } else {
                false // encryption not enabled for path.
            };

            // Check for append-only path and edit flags argument.
            // Temporary files can not be made append-only.
            if can_write && sandbox.is_append(&path_check) {
                flags.insert(OFlag::O_APPEND);
                flags.remove(OFlag::O_TRUNC);

                if sandbox.log_scmp() {
                    info!("ctx": "open", "op": "set_append_only",
                            "msg": "added O_APPEND and removed O_TRUNC from open flags",
                            "sys": request.syscall, "path": &path_check,
                            "mode": mode.bits(),
                            "oflags": format_oflags(flags), "rflags": arg.fsflags,
                            "type": path.typ, "cap": caps, "req": &request);
                } else {
                    info!("ctx": "open", "op": "set_append_only",
                            "msg": "added O_APPEND and removed O_TRUNC from open flags",
                            "sys": request.syscall, "path": &path_check,
                            "mode": mode.bits(),
                            "oflags": format_oflags(flags), "rflags": arg.fsflags,
                            "type": path.typ, "cap": caps, "pid": request.scmpreq.pid);
                }
            }

            (false, crypted)
        };

        // SAFETY:
        // Check for Filesystem sandboxing as necessary.
        let log_scmp = sandbox.log_scmp();
        let mut maybe_fs_type = None;
        if action.is_allowing() && sandbox.has_fs() {
            let fd = path.dir.as_ref().ok_or(Errno::EBADF)?;
            let fs_type = FsType::get(fd)?;
            let (action, filter) = sandbox.check_fs(fs_type);
            if !filter && action >= Action::Warn && log_enabled!(LogLevel::Warn) {
                if log_scmp {
                    warn!("ctx": "access", "cap": Capability::CAP_FS, "act": action,
                        "sys": request.syscall, "fs": fs_type, "path": &path_check,
                        "mode": mode.bits(),
                        "oflags": format_oflags(flags), "rflags": arg.fsflags,
                        "type": path.typ,
                        "tip": format!("configure `allow/fs+{fs_type}'"),
                        "req": &request);
                } else {
                    warn!("ctx": "access", "cap": Capability::CAP_FS, "act": action,
                        "sys": request.syscall, "fs": fs_type, "path": &path_check,
                        "mode": mode.bits(),
                        "oflags": format_oflags(flags), "rflags": arg.fsflags,
                        "type": path.typ,
                        "tip": format!("configure `allow/fs+{fs_type}'"),
                        "pid": request.scmpreq.pid);
                }
            }

            match action {
                Action::Allow | Action::Warn => {}
                Action::Deny | Action::Filter if hidden => return Err(Errno::ENOENT),
                Action::Deny | Action::Filter => return Err(Errno::EACCES),
                Action::Panic => panic!(),
                Action::Exit => std::process::exit(libc::EACCES),
                action => {
                    // Stop|Kill
                    let _ = request.kill(action);
                    return Err(Errno::EACCES);
                }
            }
            maybe_fs_type = Some(fs_type);
        }

        let force_umask = sandbox.umask;
        let unsafe_open_kfd = sandbox.flags.allow_unsafe_open_kfd();
        let unsafe_open_path = sandbox.flags.allow_unsafe_open_path();
        let filter_machine_id = !sandbox.flags.allow_unsafe_machine_id();
        let filter_proc_status = !sandbox.flags.allow_unsafe_proc_pid_status();
        let filter_uname = !sandbox.flags.allow_unsafe_uname();
        if sandbox.flags.force_cloexec() {
            o_cloexec = true;
        }
        let o_rand_fd = sandbox.flags.force_rand_fd();
        let setup_fds = sandbox.crypt_setup();
        let crypt_tmp = if crypted { sandbox.crypt_tmp } else { None };
        drop(sandbox); // release the read-lock.

        // Perform action: allow->emulate, deny->log.
        if !filter && action >= Action::Warn && log_enabled!(LogLevel::Warn) {
            let fs_type = if let Some(fs_type) = maybe_fs_type {
                fs_type
            } else {
                path.dir
                    .as_ref()
                    .ok_or(Errno::EBADF)
                    .and_then(FsType::get)?
            };
            let grp = caps.to_string().to_ascii_lowercase();
            if log_scmp {
                warn!("ctx": "access", "cap": caps, "act": action,
                    "sys": request.syscall, "fs": fs_type, "path": &path_check,
                    "mode": mode.bits(),
                    "oflags": format_oflags(flags), "rflags": arg.fsflags,
                    "type": path.typ,
                    "tip": format!("configure `allow/{grp}+{path_check}'"),
                    "req": &request);
            } else {
                warn!("ctx": "access", "cap": caps, "act": action,
                    "sys": request.syscall, "fs": fs_type, "path": &path_check,
                    "mode": mode.bits(),
                    "oflags": format_oflags(flags), "rflags": arg.fsflags,
                    "type": path.typ,
                    "tip": format!("configure `allow/{grp}+{path_check}'"),
                    "pid": request.scmpreq.pid);
            }
        }

        // SAFETY: Access check is done, now it is safe to:
        //
        // 1. Return ENOENT if path is hidden.
        // 2. Return EEXIST if options include MISS_LAST.
        // 3. Return ENOTDIR for non-directories with trailing slash.
        // 4. Return EISDIR for write opens on directories.
        // 5. Return ELOOP for symlinks unless O_NOFOLLOW was passed.

        // We check for the actions Deny|Filter here as other actions
        // such as Panic, Stop, Kill are handled afterwards as necessary.
        if hidden && matches!(action, Action::Deny | Action::Filter) {
            return Err(Errno::ENOENT);
        }

        if arg.fsflags.missing() && path.typ.is_some() {
            // Exclusive open for existing file.
            return Err(Errno::EEXIST);
        }

        if let Some(file_type) = &path.typ {
            if !matches!(file_type, FileType::Dir | FileType::MagicLnk(_))
                && path.abs().last() == Some(b'/')
            {
                return Err(Errno::ENOTDIR);
            }
        }

        if let Some(FileType::Dir) = &path.typ {
            if !o_tmpfl && orig_caps.can_write() {
                // Open for write on directory.
                return Err(Errno::EISDIR);
            } else if o_creat && path.abs().last() == Some(b'/') {
                // Creating open on directory.
                return Err(Errno::EISDIR);
            }
        } else if o_tmpfl {
            // O_TMPFILE open on non-directory.
            return Err(Errno::ENOTDIR);
        }

        // SAFETY: Return EPERM for {/dev,/proc}/kmsg, so dmesg(1) falls back to
        // syslog(2) which we provide. EPERM is fine as we do this after the
        // access check so path hiding was already done as necessary.
        if !has_tty && path.is_char_device() && path.abs().is_equal(b"/dev/kmsg") {
            return Err(Errno::EPERM);
        }
        if !has_tty && path.is_file() && path.abs().is_equal(b"/proc/kmsg") {
            return Err(Errno::EPERM);
        }

        // SAFETY: Do not follow (magic) symlinks after canonicalization.
        // Exception: Last component is allowed with O_NOFOLLOW.
        if path.is_symlink() && !flags.contains(OFlag::O_NOFOLLOW) {
            return Err(Errno::ELOOP);
        }

        // SAFETY: Restrictions a la protected_{fifos,regular}
        // and CONFIG_GRKERNSEC_FIFO. Here, though, we take
        // a stricter approach and block creates on non-owned
        // files or fifos regardless of the owned directory.
        //
        // This is done after O_CREAT|O_EXCL -> EEXIST check.
        if restrict_create && o_creat && matches!(path.typ, Some(FileType::Reg | FileType::Fifo)) {
            let fd = path.dir.as_ref().map(|fd| fd.as_fd()).ok_or(Errno::EBADF)?;
            // assert fd refers to file.
            assert!(path.base().is_empty());

            let statx = fstatx(fd, STATX_MODE | STATX_UID)?;
            let mode = libc::mode_t::from(statx.stx_mode);

            // Return EACCES if:
            // 1. File is group or world-writable.
            // 2. File is not owned by caller.
            let mut err = None;
            if mode & libc::S_IWOTH != 0 {
                // File is world-writable.
                err = Some("file is world writable");
            } else if mode & libc::S_IWGRP != 0 {
                // File is group-writable.
                err = Some("file is group writable");
            } else if statx.stx_uid != Uid::effective().as_raw() {
                // File is not owned by caller.
                err = Some("file is not owned by caller");
            }

            if let Some(msg) = err {
                if log_scmp {
                    error!("ctx": "trusted_file_creation", "cap": caps,
                        "sys": request.syscall, "path": &path_check,
                        "mode": mode, "oflags": format_oflags(flags), "rflags": arg.fsflags,
                        "msg": format!("create for untrusted path blocked: {msg}"),
                        "tip": "fix path permissions or use `trace/allow_unsafe_create:1'",
                        "req": &request);
                } else {
                    error!("ctx": "trusted_file_creation", "cap": caps,
                        "sys": request.syscall, "path": &path_check,
                        "mode": mode, "oflags": format_oflags(flags), "rflags": arg.fsflags,
                        "msg": format!("create for untrusted path blocked: {msg}"),
                        "tip": "fix path permissions or use `trace/allow_unsafe_create:1'",
                        "pid": request.scmpreq.pid);
                }
                return Err(Errno::EACCES);
            }
        }

        match action {
            Action::Allow | Action::Warn => {
                // The system call is allowed.
                // To prevent TOCTOU, we open the file ourselves,
                // and put the file descriptor to the process'
                // address space with SECCOMP_IOCTL_NOTIF_ADDFD.
                if o_path {
                    if unsafe_open_path {
                        // SAFETY:
                        // seccomp addfd operation returns EBADF for O_PATH file
                        // descriptors so there's no TOCTOU-free way to emulate
                        // this as of yet. However we did our best by
                        // delaying continue up to this point, thereby
                        // including the open request to the sandbox access
                        // check.
                        return unsafe { Ok(request.continue_syscall()) };
                    }
                    // SAFETY: Turn O_PATH flag to O_RDONLY for successful emulation.
                    flags.remove(OFlag::O_PATH);
                    flags.insert(OFlag::O_RDONLY);
                } else if unsafe_open_kfd
                    && o_rdwr
                    && !o_creat
                    && !o_trunc
                    && !o_tmpfl
                    && is_dev_kfd(path.dir.as_ref().map(|fd| fd.as_fd()).ok_or(Errno::EBADF)?)
                        .unwrap_or(false)
                {
                    // SAFETY:
                    //
                    // trace/allow_unsafe_kfd:true
                    //
                    // 1. AMD KFD character devices require per-application access
                    //    to the GPU device, therefore opening the device in the
                    //    Syd emulator thread and then continuing the subsequent ioctl(2)
                    //    system calls in the sandbox process is going to return EBADF.
                    //    Until, Syd has a way to fully emulate the ioctl(2) request space
                    //    and is able to call ioctl(2) directly from Syd emulator threads,
                    //    this option may be used to access such character devices.
                    //    Setting this option opens a TOCTOU attack vector, whereby the
                    //    sandbox process can open an arbitrary file instead of the character
                    //    device in question!
                    // 2. Syd does not CONTINUE the system call if at least one of the flags
                    //    O_CREAT|O_TRUNC|O_TMPFILE is set in flags argument to limit the scope
                    //    of the TOCTOU attack vector.
                    //  3. Syd CONTINUEs the system call if and only if O_RDWR is set in the flags
                    //     argument to limit the scope of the TOCTOU attack vector.
                    //  4. Syd returns ENOSYS for openat2(2) rather than CONTINUE'ing the system call
                    //     to prevent the "struct open_how" pointer indirection to bypass the restrictions
                    //     applied to the flags argument.
                    //  5. This option may be changed at runtime, and it is highly recommended to unset
                    //     this option right after the respective character device is open using the syd(2)
                    //     API to close the TOCTOU attack vector.
                    return if syscall != OpenSyscall::Openat2 {
                        // SAFETY: See above, stupid clippy.
                        unsafe { Ok(request.continue_syscall()) }
                    } else {
                        Err(Errno::ENOSYS)
                    };
                }

                // SAFETY: We have already resolved the symbolic
                // links in the path as necessary, to prevent a
                // time-of-check to time-of-use vector:
                // 1. Add O_NOFOLLOW to flags.
                // 2. Add RESOLVE_BENEATH to flags.
                // 3. Add RESOLVE_NO_MAGICLINKS | RESOLVE_NO_SYMLINKS to flags.
                // 4. Mode must be 0 if O_CREAT or O_TMPFILE is not in flags.
                // Note, magic symbolic links are an exception here.
                if matches!(path.typ, Some(FileType::MagicLnk(_))) {
                    if !path.base().is_empty() || flags.contains(OFlag::O_NOFOLLOW) {
                        // Magic symlink O_PATH fds were continued
                        // as necessary if relevant unsafe options
                        // were set. After this point, we have to
                        // ELOOP.
                        return Err(Errno::ELOOP);
                    }

                    // SAFETY: Ensure we can never acquire a
                    // controlling terminal by misguided magic symlink.
                    flags.insert(OFlag::O_NOCTTY);

                    // SAFETY: Ensure no encryption or file creation
                    // attempts can ever happen for magic symlinks.
                    o_mask = false;
                    crypted = false;
                    flags.remove(OFlag::O_CREAT);
                    flags.remove(OFlag::O_TMPFILE);
                }

                // We use RESOLVE_NO_SYMLINKS to prevent symlinks as needed,
                // and do not want to leak O_NOFOLLOW into F_GETFL(2const).
                // See Linux kernel's openat2 tests for more information.
                // flags.insert(OFlag::O_NOFOLLOW);

                let fd: OwnedFd = if crypted {
                    // Handle Encryption.
                    let opts = CryptOpts {
                        o_cloexec,
                        o_rand_fd,
                        force_umask,
                        setup_fds,
                        tmpdir: crypt_tmp,
                    };
                    if let Some(fd) = handle_crypt(&request, &path, flags, mode, opts)? {
                        // read-only encryption.
                        fd
                    } else {
                        // read-write encryption.
                        //
                        // We do not need to send a response,
                        // send a dummy response to the caller
                        // can skip it gracefully.
                        return Ok(ScmpNotifResp::new(0, 0, EIDRM, 0));
                    }
                } else if path.base().is_empty() && !o_tmpfl {
                    if let Some(fd) = do_open_pre(&request, &path, filter_machine_id, filter_uname)?
                    {
                        // pre-open hook intercepted, no need to open.
                        fd
                    } else {
                        // Existing path: Construct path to proc(5) magic symlink,
                        // or pre-open file descriptor.
                        let fd = do_open(&request, &path, flags, is_blocking)?;
                        do_open_post(fd, request.scmpreq.pid(), path, filter_proc_status)?
                    }
                } else if o_mask {
                    // Cannot be O_PATH or encrypted fd!
                    //
                    // SAFETY:
                    // If we're creating the file, we need to change
                    // per-thread umask and still honour POSIX ACLs.
                    do_creat(&request, &path, flags, mode, force_umask)?
                } else {
                    unreachable!("BUG: MUST_PATH returned a directory for open, report a bug!");
                };

                // File opened successfully, return to caller at one go.
                request.send_fd(fd, o_cloexec, o_rand_fd)
            }
            Action::Deny | Action::Filter if hidden => Err(Errno::ENOENT),
            Action::Deny | Action::Filter => Err(Errno::EACCES),
            Action::Panic => panic!(),
            Action::Exit => std::process::exit(libc::EACCES),
            action => {
                // Stop|Kill
                let _ = request.kill(action);
                Err(Errno::EACCES)
            }
        }
    })
}

/// Options for `handle_crypt` function.
#[derive(Copy, Clone, Debug)]
struct CryptOpts {
    o_cloexec: bool,
    o_rand_fd: bool,
    force_umask: Option<Mode>,
    tmpdir: Option<RawFd>,
    setup_fds: Result<(RawFd, RawFd), Errno>,
}

/// Handle open() calls for encrypted files.
fn handle_crypt(
    request: &UNotifyEventRequest,
    path: &CanonicalPath,
    safe_flags: OFlag,
    mode: Mode,
    opts: CryptOpts,
) -> Result<Option<OwnedFd>, Errno> {
    #[expect(clippy::disallowed_methods)]
    let files = request.cache.crypt_map.as_ref().unwrap();
    let my_mode = AesMod::from(safe_flags);

    // Check for concurrent read access to the same crypt fd.
    if my_mode == AesMod::Read {
        // Lock the mutex and attempt to take the crypt fd.
        // Concurrent access is only allowed if both modes are Read.
        let crypt_fd_opt = {
            let files = files.0.lock().unwrap_or_else(|err| err.into_inner());
            files
                .get(path.abs())
                .filter(|&entry| entry.mode == AesMod::Read)
                .map(|aes_val| aes_val.crypt_fd)
        }; // Lock is released here.

        if let Some(crypt_fd) = crypt_fd_opt {
            // Open a new file description.
            // Lock it for read and pass to sandbox process.
            let mut flags = safe_flags;
            flags.remove(OFlag::O_NOFOLLOW);

            let pfd = XPathBuf::from_self_fd(crypt_fd)?;
            match safe_open_msym(PROC_FILE(), &pfd, flags, ResolveFlag::empty()) {
                Ok(fd) if retry_on_eintr(|| lock_fd(&fd, false, false)).is_ok() => {
                    // Return shared fd.
                    return Ok(Some(fd));
                }
                Ok(fd) => {
                    // SAFETY: If our attempt to lock fails,
                    // syd_aes thread has already taken over.
                    // We can not use the crypt fd any longer!
                    drop(fd);
                    /* fall through */
                }
                Err(Errno::ENOENT) => {
                    // SAFETY: If our attempt to open via procfs fails,
                    // syd_aes thread had already closed the crypt fd.
                    // We can not use the crypt fd any longer!
                    /* fall through */
                }
                Err(errno) => return Err(errno),
            };
        }
    }

    // Promote O_WRONLY to O_RDWR and drop O_APPEND.
    // SAFETY: This fd is not exposed to sandbox process.
    let mut flags = safe_flags;
    flags.remove(OFlag::O_WRONLY);
    flags.insert(OFlag::O_RDWR);
    flags.remove(OFlag::O_APPEND);

    // Strip O_ASYNC|O_NDELAY|O_NONBLOCK.
    // We want blocking writes to the underlying fd.
    flags.remove(OFlag::O_ASYNC | OFlag::O_NDELAY | OFlag::O_NONBLOCK);

    // Open or create the file as read-write.
    //
    // Note, if `path.base().is_empty()` we have an existing file
    // which do_open_crypt is going to reopen using proc(5) magic
    // symlink.
    let create = if path.base().is_empty() {
        // Strip O_CREAT when NOT creating.
        flags.remove(OFlag::O_CREAT);
        // O_TMPFILE is valid if base is empty, when
        // we create a temporary file under parent-fd.
        flags.contains(OFlag::O_TMPFILE)
    } else {
        assert!(
            !flags.contains(OFlag::O_TMPFILE),
            "BUG: O_CREAT|O_TMPFILE flags are invalid, report a bug!"
        );
        flags.contains(OFlag::O_CREAT)
    };
    let enc_fd = if create {
        do_creat(request, path, flags, mode, opts.force_umask)
    } else {
        do_open_crypt(path, flags)
    }?;

    // Hold a write-lock to ensure no concurrent Syd aes writes.
    //
    // SAFETY: Block until we can acquire the lock to ensure safe
    // concurrent access _unless_ the open was a non-blocking open
    // in which case we happily return EAGAIN so the caller can
    // retry.
    //
    // Similarly, `aes_ctr_tmp` respects non-blocking opens too,
    // and returns a non-blocking fd as necessary.
    let wait = !oflag_nonblock(safe_flags);
    retry_on_eintr(|| lock_fd(&enc_fd, true, create || wait))?;

    // Acquire encryption sockets.
    let setup_fds = opts.setup_fds?;

    // Map decrypted version to memory/temporary FD.
    // SAFETY: This does not read plaintext into memory!
    // We use zero-copy with splice and pipes.
    // Note, enc_fd is an OwnedFd so in the event of
    // an aes_ctr_tmp error it'll be closed and the lock
    // will be released.
    let (fd, iv) = if let Some((fd, iv)) = aes_ctr_tmp(setup_fds, &enc_fd, safe_flags, opts.tmpdir)?
    {
        (fd, iv)
    } else {
        // SAFETY:
        // 1. This is not a new file.
        // 2. This is not a Syd encrypted file.
        // Unlock and open as-is.
        unlock_fd(&enc_fd)?;
        return Ok(Some(enc_fd));
    };

    // Reopen a new instance to pass to the sandbox process.
    // This instance points to a different open file description!
    let pfd = XPathBuf::from_self_fd(fd.as_raw_fd())?;

    let mut flags = safe_flags;
    flags.remove(
        OFlag::O_WRONLY | OFlag::O_ASYNC | OFlag::O_CREAT | OFlag::O_EXCL | OFlag::O_NOFOLLOW,
    );
    flags.insert(OFlag::O_RDWR);

    let aes_fd = safe_open_msym(PROC_FILE(), &pfd, flags, ResolveFlag::empty())?;

    // Record the file information,
    // so we can answer reliably on fstat.
    let info = FileInfo::from_fd(&aes_fd)?;

    // SAFETY:
    // No need to wait on this lock, as we've just opened the file.
    // Close the send fd to get rid off our copy of the lock.
    retry_on_eintr(|| lock_fd(&aes_fd, false, false))?;
    request.send_fd(aes_fd, opts.o_cloexec, opts.o_rand_fd)?;

    // Record encryption process information,
    // and notify the syd_aes thread to wake up.
    {
        let (aes_map, cvar) = &**files;
        let mut aes_map = aes_map.lock().unwrap_or_else(|e| e.into_inner());
        aes_map.insert(
            path.abs().to_owned(),
            AesVal {
                info,
                iv: Some(iv),
                enc_fd: enc_fd.into_raw_fd(),
                crypt_fd: fd.into_raw_fd(),
                mode: my_mode,
            },
        );
        cvar.notify_one();
    } // Lock is released here.

    Ok(None)
}

/// Handle open(2) calls that can potentially create files.
fn do_creat(
    request: &UNotifyEventRequest,
    path: &CanonicalPath,
    safe_flags: OFlag,
    mut mode: Mode,
    force_umask: Option<Mode>,
) -> Result<OwnedFd, Errno> {
    // SAFETY: force_umask overrides POSIX ACLs.
    if let Some(mask) = force_umask {
        mode &= !mask;
    }

    let how = OpenHow::new().flags(safe_flags).mode(mode).resolve(
        ResolveFlag::RESOLVE_BENEATH
            | ResolveFlag::RESOLVE_NO_MAGICLINKS
            | ResolveFlag::RESOLVE_NO_SYMLINKS,
    );

    // Determine process umask to apply in the thread.
    let req = request.scmpreq;
    let mask = proc_umask(req.pid())?;

    // `path.base()` may be empty for O_TMPFILE.
    let base = if !path.base().is_empty() {
        path.base()
    } else {
        XPath::from_bytes(b".")
    };

    // Set umask which is per-thread here.
    safe_umask(mask);

    // All set, make the openat2(2) call.
    let fd = path.dir.as_ref().map(|fd| fd.as_fd()).ok_or(Errno::EBADF)?;
    safe_openat2(fd, base, how)
}

fn to_oflag(arg: u64, truncate: bool) -> Result<OFlag, Errno> {
    // SAFETY: Reject undefined flags.
    let flags = arg.try_into().or(Err(Errno::EINVAL))?;

    // SAFETY: Reject invalid flags.
    const KEEP_OFLAG: OFlag =
        // Keep O_LARGEFILE for old ABI.
        OFlag::from_bits_retain(0x8000);

    let mut flags = OFlag::from_bits_retain(flags);
    let preserved = flags & KEEP_OFLAG;
    flags.remove(KEEP_OFLAG);

    if truncate {
        // open, openat truncates invalid flags.
        return Ok((flags & OFlag::all()) | preserved);
    }

    // openat2 validates invalid flags.
    let unknown_flags = flags.difference(OFlag::all());
    if unknown_flags.bits() != 0 {
        crate::error!("ctx": "open", "op": "invalid_flags",
            "msg": "passed invalid flags to open flags argument",
            "flags": format!("{unknown_flags:?}"));
        return Err(Errno::EINVAL);
    }

    Ok(flags | preserved)
}

// Handle open(2) calls for Crypt sandboxing for existing paths.
//
// This function has no internal encryption logic,
// and it's only used by Crypt sandboxing when opening existing paths
// using proc(5) magic symlink, otherwise do_creat() is used.
fn do_open_crypt(path: &CanonicalPath, mut safe_flags: OFlag) -> Result<OwnedFd, Errno> {
    assert!(
        path.base().is_empty(),
        "BUG: non-canonical path passed to do_open_crypt, report a bug!"
    );
    assert!(
        !safe_flags.contains(OFlag::O_CREAT),
        "BUG: O_CREAT passed to do_open_crypt, report a bug!"
    );
    assert!(
        !safe_flags.contains(OFlag::O_TMPFILE),
        "BUG: O_TMPFILE passed to do_open_crypt, report a bug!"
    );
    safe_flags.remove(OFlag::O_NOFOLLOW);

    // Construct path to proc(5) magic symlink.
    //
    // SAFETY: ^^ empty base asserts dir is Some.
    #[expect(clippy::disallowed_methods)]
    let fd = path.dir.as_ref().map(|fd| fd.as_raw_fd()).unwrap();
    let pfd = XPathBuf::from_self_fd(fd)?;

    // Reopen the `O_PATH` path fd with the requested flags.
    safe_open_msym(PROC_FILE(), &pfd, safe_flags, ResolveFlag::empty())
}

// Existing path: Construct path to proc(5) magic symlink, or pre-open file descriptor.
fn do_open(
    request: &UNotifyEventRequest,
    path: &CanonicalPath,
    mut flags: OFlag,
    is_blocking: bool,
) -> Result<OwnedFd, Errno> {
    assert!(path.base().is_empty());

    // SAFETY: Note, the path may be borrowed here, ie it can be a fd to the preopen `/`,
    // `/proc`, or `/dev/null` fds. In these cases, we still want to reopen, because the
    // fd we're going to send will share the same open file description and we don't want
    // to mess up file offsets for everyone.
    // SAFETY: ^^ empty base asserts dir is Some.
    #[expect(clippy::disallowed_methods)]
    let fd = path.dir.as_ref().map(|fd| fd.as_raw_fd()).unwrap();
    let pfd = XPathBuf::from_self_fd(fd)?;

    // Reopen the `O_PATH` path fd with the requested flags.
    flags.remove(OFlag::O_CREAT);
    flags.remove(OFlag::O_NOFOLLOW);
    let how = safe_open_how_msym(flags, ResolveFlag::empty());

    // SAFETY: Record blocking call so it can get invalidated.
    let req = request.scmpreq;
    if is_blocking {
        request.cache.add_sys_block(req, false)?;
    }

    let result = safe_openat2(PROC_FILE(), &pfd, how);

    // Remove invalidation record unless interrupted.
    if is_blocking {
        request
            .cache
            .del_sys_block(req.id, matches!(result, Err(Errno::EINTR)))?;
    }

    result
}

fn do_open_pre(
    request: &UNotifyEventRequest,
    path: &CanonicalPath,
    filter_machine_id: bool,
    filter_uname: bool,
) -> Result<Option<OwnedFd>, Errno> {
    // SAFETY: Substitute machine-id(5) with per-Syd random boot-id.
    if filter_machine_id && path.abs().is_machine_id() {
        return Ok(Some(do_open_machine_id()?));
    }

    // SAFETY: Provide identical data to uname(2) for /proc/version.
    if filter_uname && path.abs().is_proc_version() {
        return Ok(Some(do_open_proc_version(request)?));
    }

    // SAFETY: Provide identical data to uname(2) for /proc/sys/kernel/osrelease.
    if filter_uname && path.abs().is_proc_osrelease() {
        return Ok(Some(do_open_proc_osrelease()?));
    }

    Ok(None)
}

fn do_open_post(
    mut fd: OwnedFd,
    pid: Pid,
    path: CanonicalPath,
    filter_proc_status: bool,
) -> Result<OwnedFd, Errno> {
    // SAFETY: Filter security-sensitive proc_pid_status(5) contents.
    if filter_proc_status && path.abs().is_proc_status() {
        fd = do_mask_proc_pid_status(fd, pid, path)?;
    }

    Ok(fd)
}

fn do_open_machine_id() -> Result<OwnedFd, Errno> {
    // Open a memory fd to be sealed for write.
    let mut mfd = do_open_memfd(c"syd/etc/machine-id").map(BufWriter::new)?;

    // Generate machine-id(5) contents.
    #[expect(clippy::disallowed_methods)]
    let mut machine_id = env::var(ENV_ID).expect("BUG: SYD_ID environment variable isn't set!");
    machine_id.truncate(32);
    machine_id.push('\n');

    // Write file contents, retrying on interrupts.
    mfd.write_all(machine_id.as_bytes())
        .map_err(|err| err2no(&err))?;
    // into_inner fails on flush errors.
    let mut mfd = mfd.into_inner().or(Err(Errno::EIO))?;

    // Rewind file offset and seal to deny further writes.
    mfd.rewind().or(Err(Errno::EIO))?;
    seal_memfd_all(&mfd)?;

    Ok(mfd.into())
}

fn do_open_proc_version(request: &UNotifyEventRequest) -> Result<OwnedFd, Errno> {
    // Open a memory fd to be sealed for write.
    let mut mfd = do_open_memfd(c"syd/proc/version").map(BufWriter::new)?;

    // Read UtsName using uname(2) syscall.
    //
    // SAFETY: Syscall is confined with syscall argument cookies.
    let mut utsname = safe_uname()?;

    // Mask utsname using the sandbox.
    let sandbox = request.get_sandbox();
    sandbox.mask_uname(&mut utsname);
    drop(sandbox); // release the read-lock.

    // Write the utsname's Display into file, retry on interrupts.
    let utsname = utsname.to_string();
    mfd.write_all(utsname.as_bytes())
        .map_err(|err| err2no(&err))?;
    // into_inner fails on flush errors.
    let mut mfd = mfd.into_inner().or(Err(Errno::EIO))?;

    // Rewind file offset and seal to deny further writes.
    mfd.rewind().or(Err(Errno::EIO))?;
    seal_memfd_all(&mfd)?;

    Ok(mfd.into())
}

fn do_open_proc_osrelease() -> Result<OwnedFd, Errno> {
    // Open a memory fd to be sealed for write.
    let mut mfd = do_open_memfd(c"syd/proc/sys/kernel/osrelease").map(BufWriter::new)?;

    // Read static release string.
    let release = Sandbox::os_release();

    // Write release string avoiding interrupts.
    // End it with a newline.
    mfd.write_all(release.as_bytes())
        .map_err(|err| err2no(&err))?;
    mfd.write_all(b"\n").map_err(|err| err2no(&err))?;
    // into_inner fails on flush errors.
    let mut mfd = mfd.into_inner().or(Err(Errno::EIO))?;

    // Rewind file offset and seal to deny further writes.
    mfd.rewind().or(Err(Errno::EIO))?;
    seal_memfd_all(&mfd)?;

    Ok(mfd.into())
}

fn do_mask_proc_pid_status(fd: OwnedFd, pid: Pid, path: CanonicalPath) -> Result<OwnedFd, Errno> {
    // Prepare the name of the memfd for identification.
    // Undo /proc/self -> /proc/$pid.
    let mut path = path.take();
    if path.starts_with(b"/proc/self/") {
        let mut pfd = XPathBuf::from("/proc");
        pfd.push_pid(pid);
        path.replace_prefix(b"/proc/self", pfd.as_bytes())?;
    }
    path.replace_prefix(b"/", b"syd/")?;

    // Open a memory fd to be sealed for write.
    let mut mfd = path.with_nix_path(do_open_memfd)??;

    // Filter proc_pid_status(5) into the memory fd.
    mask_proc_pid_status(fd, &mfd)?;

    // Rewind file offset and seal to deny further writes.
    mfd.rewind().or(Err(Errno::EIO))?;
    seal_memfd_all(&mfd)?;

    Ok(mfd.into())
}

// Open a memory fd to be sealed for write.
// Memory fd is exec-sealed on Linux>=6.3.
fn do_open_memfd(name: &CStr) -> Result<File, Errno> {
    safe_memfd_create(name, *SAFE_MFD_FLAGS).map(File::from)
}

fn to_mode(arg: u64, flags: OFlag, ignore: bool) -> Result<Mode, Errno> {
    if flags.contains(OFlag::O_CREAT) || flags.contains(OFlag::O_TMPFILE) {
        if ignore {
            // SAFETY: Strip undefined/invalid mode.
            // Used by open, openat, and creat.
            Ok(crate::kernel::to_mode(arg))
        } else {
            // SAFETY: Reject undefined/invalid mode.
            // Used by openat2.
            crate::kernel::to_mode2(arg)
        }
    } else if !ignore && arg != 0 {
        // SAFETY: mode argument must be zero without creation flags for openat2.
        Err(Errno::EINVAL)
    } else {
        // SAFETY: mode argument is ignored without creation flags.
        // glibc sets it to 0, musl does not. Both are valid behaviour.
        Ok(Mode::empty())
    }
}
