const S_IFMT: u32 = 0o170000; const S_IFDIR: u32 = 0o040000; const S_IFREG: u32 = 0o100000; const S_IFLNK: u32 = 0o120000; const S_IFIFO: u32 = 0o010000; const S_IFSOCK: u32 = 0o140000; const S_IFCHR: u32 = 0o020000; const S_IFBLK: u32 = 0o060000;
const GO_MODE_DIR: u32 = 0x80000000; const GO_MODE_SYMLINK: u32 = 0x08000000; const GO_MODE_DEVICE: u32 = 0x04000000; const GO_MODE_NAMED_PIPE: u32 = 0x02000000; const GO_MODE_SOCKET: u32 = 0x01000000; const GO_MODE_SETUID: u32 = 0x00800000; const GO_MODE_SETGID: u32 = 0x00400000; const GO_MODE_CHAR_DEVICE: u32 = 0x00200000; const GO_MODE_STICKY: u32 = 0x00100000;
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub struct UnixMode(u32);
impl UnixMode {
#[inline]
pub const fn new(mode: u32) -> Self {
Self(mode)
}
#[inline]
pub const fn as_u32(self) -> u32 {
self.0
}
}
impl From<u32> for UnixMode {
#[inline]
fn from(mode: u32) -> Self {
Self(mode)
}
}
impl From<UnixMode> for u32 {
#[inline]
fn from(mode: UnixMode) -> Self {
mode.0
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub struct GoFileMode(u32);
impl GoFileMode {
#[inline]
pub const fn new(mode: u32) -> Self {
Self(mode)
}
#[inline]
pub const fn as_u32(self) -> u32 {
self.0
}
}
impl From<u32> for GoFileMode {
#[inline]
fn from(mode: u32) -> Self {
Self(mode)
}
}
impl From<GoFileMode> for u32 {
#[inline]
fn from(mode: GoFileMode) -> Self {
mode.0
}
}
impl From<UnixMode> for GoFileMode {
fn from(unix_mode: UnixMode) -> Self {
let mode = unix_mode.0;
let file_type = mode & S_IFMT;
let permissions = mode & 0o7777;
let mut go_mode = permissions & 0o777;
if permissions & 0o4000 != 0 {
go_mode |= GO_MODE_SETUID;
}
if permissions & 0o2000 != 0 {
go_mode |= GO_MODE_SETGID;
}
if permissions & 0o1000 != 0 {
go_mode |= GO_MODE_STICKY;
}
match file_type {
S_IFDIR => go_mode |= GO_MODE_DIR,
S_IFLNK => go_mode |= GO_MODE_SYMLINK,
S_IFIFO => go_mode |= GO_MODE_NAMED_PIPE,
S_IFSOCK => go_mode |= GO_MODE_SOCKET,
S_IFCHR => go_mode |= GO_MODE_CHAR_DEVICE | GO_MODE_DEVICE,
S_IFBLK => go_mode |= GO_MODE_DEVICE,
S_IFREG => {} _ => {}
}
GoFileMode(go_mode)
}
}
#[inline]
pub fn unix_mode_to_go_filemode(unix_mode: u32) -> u32 {
GoFileMode::from(UnixMode::from(unix_mode)).as_u32()
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_regular_file() {
let unix_mode = UnixMode::from(0o100644);
let go_mode = GoFileMode::from(unix_mode);
assert_eq!(go_mode.as_u32(), 0o644);
assert_eq!(unix_mode_to_go_filemode(0o100644), 0o644);
}
#[test]
fn test_directory() {
let unix_mode = UnixMode::from(0o040755);
let go_mode = GoFileMode::from(unix_mode);
assert_eq!(go_mode.as_u32(), GO_MODE_DIR | 0o755);
assert_eq!(go_mode.as_u32(), 0x800001ed);
}
#[test]
fn test_symlink() {
let unix_mode = UnixMode::from(0o120777);
let go_mode = GoFileMode::from(unix_mode);
assert_eq!(go_mode.as_u32(), GO_MODE_SYMLINK | 0o777);
}
#[test]
fn test_named_pipe() {
let unix_mode = UnixMode::from(0o010644);
let go_mode = GoFileMode::from(unix_mode);
assert_eq!(go_mode.as_u32(), GO_MODE_NAMED_PIPE | 0o644);
}
#[test]
fn test_socket() {
let unix_mode = UnixMode::from(0o140666);
let go_mode = GoFileMode::from(unix_mode);
assert_eq!(go_mode.as_u32(), GO_MODE_SOCKET | 0o666);
}
#[test]
fn test_char_device() {
let unix_mode = UnixMode::from(0o020666);
let go_mode = GoFileMode::from(unix_mode);
assert_eq!(
go_mode.as_u32(),
GO_MODE_CHAR_DEVICE | GO_MODE_DEVICE | 0o666
);
}
#[test]
fn test_block_device() {
let unix_mode = UnixMode::from(0o060666);
let go_mode = GoFileMode::from(unix_mode);
assert_eq!(go_mode.as_u32(), GO_MODE_DEVICE | 0o666);
}
#[test]
fn test_setuid_bit() {
let unix_mode = UnixMode::from(0o104755);
let go_mode = GoFileMode::from(unix_mode);
assert_eq!(go_mode.as_u32(), GO_MODE_SETUID | 0o755);
}
#[test]
fn test_setgid_bit() {
let unix_mode = UnixMode::from(0o102755);
let go_mode = GoFileMode::from(unix_mode);
assert_eq!(go_mode.as_u32(), GO_MODE_SETGID | 0o755);
}
#[test]
fn test_sticky_bit() {
let unix_mode = UnixMode::from(0o041755);
let go_mode = GoFileMode::from(unix_mode);
assert_eq!(go_mode.as_u32(), GO_MODE_DIR | GO_MODE_STICKY | 0o755);
}
#[test]
fn test_all_special_bits() {
let unix_mode = UnixMode::from(0o107777);
let go_mode = GoFileMode::from(unix_mode);
assert_eq!(
go_mode.as_u32(),
GO_MODE_SETUID | GO_MODE_SETGID | GO_MODE_STICKY | 0o777
);
}
#[test]
fn test_into_conversion() {
let unix_mode = UnixMode::from(0o040755);
let go_mode: GoFileMode = unix_mode.into();
assert_eq!(go_mode.as_u32(), 0x800001ed);
}
#[test]
fn test_type_conversions() {
let original = 0o100644u32;
let unix_mode = UnixMode::from(original);
let go_mode = GoFileMode::from(unix_mode);
let result = go_mode.as_u32();
assert_eq!(result, 0o644); }
}