// perms.c: muestra los bits de permiso de un archivo y repite, paso a paso,
// la verificación que hace el kernel (owner, grupo, otros). Luego compara
// con lo que responde access(2).
//   uso: ./perms archivo...
#define _GNU_SOURCE
#include <stdio.h>
#include <sys/stat.h>
#include <unistd.h>

// Arma "rwxr-x---" a partir de st_mode, incluidos setuid/setgid/sticky.
static void mode_string(mode_t m, char *s) {
    const char *rwx = "rwxrwxrwx";
    for (int i = 0; i < 9; i++)
        s[i] = (m & (0400 >> i)) ? rwx[i] : '-';
    if (m & S_ISUID) s[2] = (m & S_IXUSR) ? 's' : 'S';
    if (m & S_ISGID) s[5] = (m & S_IXGRP) ? 's' : 'S';
    if (m & S_ISVTX) s[8] = (m & S_IXOTH) ? 't' : 'T';
    s[9] = '\0';
}

// La verificación de Unix: se elige UNA clase y solo se miran sus 3 bits.
// want es una máscara de 3 bits: 4 = leer, 2 = escribir, 1 = ejecutar.
static int unix_check(const struct stat *st, int want, const char **who) {
    uid_t uid = geteuid();
    if (uid == 0) {           // root se salta r y w; para x pide algún bit x
        *who = "root";
        return want != 1 || (st->st_mode & 0111) != 0;
    }
    int bits;
    if (uid == st->st_uid) {
        *who = "owner";
        bits = (st->st_mode >> 6) & 7;
    } else if (getegid() == st->st_gid || group_member(st->st_gid)) {
        *who = "grupo";
        bits = (st->st_mode >> 3) & 7;
    } else {
        *who = "otros";
        bits = st->st_mode & 7;
    }
    return (bits & want) == want;
}

int main(int argc, char *argv[]) {
    printf("uid=%d euid=%d gid=%d\n", (int)getuid(), (int)geteuid(), (int)getgid());
    for (int i = 1; i < argc; i++) {
        struct stat st;
        if (stat(argv[i], &st) < 0) {
            perror(argv[i]);
            continue;
        }
        char ms[10];
        mode_string(st.st_mode, ms);
        printf("%s  %04o %s  owner=%d grupo=%d\n", argv[i],
               (unsigned)(st.st_mode & 07777), ms, (int)st.st_uid, (int)st.st_gid);

        const char *names[] = {"leer", "escribir", "ejecutar"};
        int wants[] = {4, 2, 1};
        int modes[] = {R_OK, W_OK, X_OK};
        for (int k = 0; k < 3; k++) {
            const char *who;
            int ours = unix_check(&st, wants[k], &who);
            // eaccess usa los IDs efectivos, como open(2)
            int kern = eaccess(argv[i], modes[k]) == 0;
            printf("  %-8s clase=%-5s nuestro=%s kernel=%s\n", names[k], who,
                   ours ? "sí" : "no", kern ? "sí" : "no");
        }
    }
    return 0;
}
