//
// Syd: rock-solid application kernel
// src/kernel/fcntl.rs: fcntl{,64}(2) handler
//
// Copyright (c) 2023, 2024, 2025 Ali Polatel <alip@chesswob.org>
//
// SPDX-License-Identifier: GPL-3.0

use std::os::fd::RawFd;

use libseccomp::ScmpNotifResp;
use nix::errno::Errno;

use crate::{
    lookup::{CanonicalPath, FileInfo},
    req::UNotifyEventRequest,
    sandbox::Capability,
};

pub(crate) fn sys_fcntl(request: UNotifyEventRequest) -> ScmpNotifResp {
    // Note, we only hook into F_SETFL requests
    // which do not have O_APPEND set!
    let req = request.scmpreq;
    let fd = if let Ok(fd) = RawFd::try_from(req.data.args[0]) {
        if fd < 0 {
            return request.fail_syscall(Errno::EBADF);
        }
        fd
    } else {
        return request.fail_syscall(Errno::EBADF);
    };

    let fd = if let Ok(fd) = request.get_fd(fd) {
        fd
    } else {
        return request.fail_syscall(Errno::EBADF);
    };

    let path = match CanonicalPath::new_fd(fd.into(), req.pid()) {
        Ok(path) => {
            if !request.is_valid() {
                return request.fail_syscall(Errno::ESRCH);
            }
            path
        }
        Err(_) => return request.fail_syscall(Errno::EBADF),
    };

    let sandbox = request.get_sandbox();
    let is_crypt = sandbox.enabled(Capability::CAP_CRYPT);
    let is_append = sandbox.is_append(path.abs());
    drop(sandbox); // release the read-lock.

    if is_append {
        // Deny silently.
        return request.return_syscall(0);
    }

    #[expect(clippy::disallowed_methods)]
    if is_crypt {
        let fd = path.dir.as_ref().unwrap();
        if let Ok(info) = FileInfo::from_fd(fd) {
            let files = request.crypt_map.as_ref().unwrap();
            let check = {
                let files = files.0.lock().unwrap_or_else(|err| err.into_inner());
                files.values().any(|map| map.info == info)
            }; // Lock is released here.

            if check {
                // Deny with EACCES, caller should know.
                return request.fail_syscall(Errno::EACCES);
            }
        }
    }

    // SAFETY: fcntl is fd-only.
    // No pointer dereference in access check.
    unsafe { request.continue_syscall() }
}
