fernfs 0.1.5

A Rust NFS Server implementation
Documentation
use crate::protocol::xdr::{self, nfs3};

use super::Capabilities;

#[derive(Clone, Copy, Debug)]
pub struct UnixPerms {
    pub read: bool,
    pub write: bool,
    pub exec: bool,
}

fn auth_matches_gid(auth: &xdr::rpc::auth_unix, gid: nfs3::gid3) -> bool {
    auth.gid == gid || auth.gids.contains(&gid)
}

pub fn unix_mode_perms(attr: &nfs3::fattr3, auth: &xdr::rpc::auth_unix) -> UnixPerms {
    if auth.uid == 0 {
        return UnixPerms { read: true, write: true, exec: true };
    }

    let mode = attr.mode & 0o777;
    if auth.uid == attr.uid {
        UnixPerms { read: mode & 0o400 != 0, write: mode & 0o200 != 0, exec: mode & 0o100 != 0 }
    } else if auth_matches_gid(auth, attr.gid) {
        UnixPerms { read: mode & 0o040 != 0, write: mode & 0o020 != 0, exec: mode & 0o010 != 0 }
    } else {
        UnixPerms { read: mode & 0o004 != 0, write: mode & 0o002 != 0, exec: mode & 0o001 != 0 }
    }
}

pub fn access_mask(
    attr: &nfs3::fattr3,
    auth: &xdr::rpc::auth_unix,
    capabilities: Capabilities,
    requested: u32,
) -> u32 {
    let perms = unix_mode_perms(attr, auth);
    let supports_write = matches!(capabilities, Capabilities::ReadWrite);
    let allow_write = supports_write && perms.write;
    let write_mask = nfs3::ACCESS3_MODIFY | nfs3::ACCESS3_EXTEND;
    let delete_mask = nfs3::ACCESS3_DELETE;
    let mut granted_access = 0;

    match attr.ftype {
        nfs3::ftype3::NF3REG => {
            if requested & nfs3::ACCESS3_READ != 0 && perms.read {
                granted_access |= nfs3::ACCESS3_READ;
            }
            if requested & nfs3::ACCESS3_EXECUTE != 0 && perms.exec {
                granted_access |= nfs3::ACCESS3_EXECUTE;
            }
            if requested & write_mask != 0 && allow_write {
                granted_access |= requested & write_mask;
            }
        }
        nfs3::ftype3::NF3DIR => {
            if requested & nfs3::ACCESS3_READ != 0 && perms.read {
                granted_access |= nfs3::ACCESS3_READ;
            }
            if requested & nfs3::ACCESS3_LOOKUP != 0 && perms.exec {
                granted_access |= nfs3::ACCESS3_LOOKUP;
            }
            if requested & nfs3::ACCESS3_EXECUTE != 0 && perms.exec {
                granted_access |= nfs3::ACCESS3_EXECUTE;
            }
            if requested & (write_mask | delete_mask) != 0 && allow_write && perms.exec {
                granted_access |= requested & (write_mask | delete_mask);
            }
        }
        nfs3::ftype3::NF3LNK => {
            if requested & nfs3::ACCESS3_READ != 0 && perms.read {
                granted_access |= nfs3::ACCESS3_READ;
            }
            if requested & nfs3::ACCESS3_EXECUTE != 0 && perms.exec {
                granted_access |= nfs3::ACCESS3_EXECUTE;
            }
        }
        _ => {
            if requested & nfs3::ACCESS3_READ != 0 && perms.read {
                granted_access |= nfs3::ACCESS3_READ;
            }
            if requested & nfs3::ACCESS3_EXECUTE != 0 && perms.exec {
                granted_access |= nfs3::ACCESS3_EXECUTE;
            }
        }
    }

    granted_access
}