1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
//! An implementation of BSD strmode(3).

/// Returns a `String` describing the file permissions contained in a `u32`,
/// as in the`st_mode` bit field of `struct stat`. It is formatted in the usual
/// UNIX convention, eg `-rw-r--r--`.
///
/// # Examples
///
/// ```
/// use std::fs;
/// use std::os::unix::fs::PermissionsExt;
/// use strmode::strmode;
///
/// fn main() -> std::io::Result<()> {
///     let metadata = fs::metadata("/dev/null")?;
///     let permissions = metadata.permissions();
///     let mode = permissions.mode();
///
///     assert_eq!(strmode(mode), "crw-rw-rw-");
///
///     Ok(())
/// }
/// ```
pub fn strmode(mode: u32) -> String {
    let mut flags = ['-'; 10];

    let perms = [
        (0o000400, 'r'), (0o000200, 'w'), (0o000100, 'x'), // user
        (0o000040, 'r'), (0o000020, 'w'), (0o000010, 'x'), // group
        (0o000004, 'r'), (0o000002, 'w'), (0o000001, 'x'), // other
    ];

    // Permissions
    let s = &mut flags[1..];
    for i in 0..9 {
        if mode & perms[i].0 == perms[i].0 {
            s[i] = perms[i].1;
        }
    }

    // File type
    match mode & 0o170000 {
        0o010000    => { flags[0] = 'p' },  // fifo
        0o020000    => { flags[0] = 'c' },  // character special
        0o040000    => { flags[0] = 'd' },  // directory
        0o060000    => { flags[0] = 'b' },  // block special
        0o100000    => { },                 // regular file
        0o120000    => { flags[0] = 'l' },  // symbolic link
        0o140000    => { flags[0] = 's' },  // socket
        _           => { flags[0] = '?' },  // unknown
    }

    // setuid
    let xusr_setuid = mode & (0o000100 | 0o004000);
    if xusr_setuid == 0o004000 {
        flags[3] = 'S';
    } else if xusr_setuid == (0o000100 | 0o004000) {
         flags[3] = 's';
    }

    // setgid
    let xgrp_setgid = mode & (0o000010 | 0o002000);
    if xgrp_setgid == 0o002000 {
        flags[6] = 'S';
    } else if xgrp_setgid == (0o000010 | 0o002000) {
        flags[6] = 's';
    }

    // sticky
    let xoth_sticky = mode & (0o000001 | 0o001000);
    if xoth_sticky == 0o001000 {
        flags[9] = 'T';
    } else if xoth_sticky == (0o000001 | 0o001000) {
        flags[9] = 't';
    }

    return flags.into_iter().collect();
}

#[test]
fn test_strmode() {
    let tests = [
        (0o100644, "-rw-r--r--", "file, 644"),
        (0o100600, "-rw-------", "file, 600"),
        (0o100777, "-rwxrwxrwx", "file, 777"),
        (0o040755, "drwxr-xr-x", "directory, 755"),
        (0o040711, "drwx--x--x", "directory, 711"),
        (0o020660, "crw-rw----", "character special, 660"),
        (0o060660, "brw-rw----", "block special, 660"),
        (0o120777, "lrwxrwxrwx", "symbolic link, 777"),
        (0o010600, "prw-------", "fifo, 600"),
        (0o140755, "srwxr-xr-x", "socket ,755"),
        (0o104555, "-r-sr-xr-x", "file, 755 with setuid"),
        (0o104644, "-rwSr--r--", "file, 644 with setuid"),
        (0o044755, "drwsr-xr-x", "directory, 755 with setuid"),
        (0o044666, "drwSrw-rw-", "directory, 666 with setuid"),
        (0o102755, "-rwxr-sr-x", "file, 755 with setgid"),
        (0o102644, "-rw-r-Sr--", "file, 644 with setgid"),
        (0o042755, "drwxr-sr-x", "directory, 755 with setgid"),
        (0o042644, "drw-r-Sr--", "directory, 644 with setgid"),
        (0o041755, "drwxr-xr-t", "directory, 755 with sticky"),
        (0o041644, "drw-r--r-T", "directory, 644 with sticky"),
        (0o104471, "-r-Srwx--x", "file, 471 with setuid"),
        (0o106471, "-r-Srws--x", "file, 471 with setuid and setgid"),
        (0o044471, "dr-Srwx--x", "directory, 471 with setuid"),
        (0o046471, "dr-Srws--x", "directory, 471 with setuid and setgid"),
        (0o045471, "dr-Srwx--t", "directory, 471 with setuid and sticky"),
        (0o047471, "dr-Srws--t", "directory, 471 with setuid, setgid, and sticky"),
        (0o047470, "dr-Srws--T", "directory, 470 with setuid, setgid, and sticky"),
    ];

    for t in &tests {
        assert_eq!(t.1, strmode(t.0), "{}: {:o}", t.2, t.0);
    }
}