Scott M. Mcdermott

UNIX Systems & Network Administrator
available for contract or salaried positions

acl.c

/*
 * ACL routines
 */

#define _GNU_SOURCE

#include <sys/stat.h>
#include <sys/acl.h>

#include <acl/libacl.h>

#include <stdbool.h>
#include <stddef.h>
#include <stdio.h>
#include <errno.h>
#include <string.h>
#include <stdlib.h>
#include <fcntl.h>
#include <unistd.h>

#include "util.h"
#include "acl.h"

^L

static bool
user_has_execute_bit (int fd)
{
        struct stat st;

        fstat(fd, &st);

        return (st.st_mode & S_IXUSR);
}

static void
add_acl_bit_ifhave (acl_t *acl,
                    acl_perm_t which,
                    acl_perm_t only_ifhave)
{
        int entid;
        acl_entry_t aclent;
        acl_permset_t aclperms;

        for (entid = ACL_FIRST_ENTRY;
             acl_get_entry(*acl, entid, &aclent) == 1;
             entid = ACL_NEXT_ENTRY)
        {
                acl_get_permset(aclent, &aclperms);
                if (acl_get_perm(aclperms, only_ifhave) == 1)
                        acl_add_perm(aclperms, which);
        }
}

static void
clear_acl_bits (acl_t *acl,
                acl_perm_t which)
{
        int entid;
        acl_entry_t aclent;
        acl_permset_t aclperms;

        for (entid = ACL_FIRST_ENTRY;
             acl_get_entry(*acl, entid, &aclent) == 1;
             entid = ACL_NEXT_ENTRY)
        {
                acl_get_permset(aclent, &aclperms);
                acl_delete_perm(aclperms, which);
        }
}

acl_t
xacl_get_fd (int fd)
{
        acl_t acl;

        if ((acl = acl_get_fd(fd)) == NULL) {
                struct stat st;
                fstat(fd, &st);
                fprintf(stderr, "%s: acl_get_fd: fd %d, inode %lu: %s:\n",
                        progname, fd, st.st_ino, strerror(errno));
                exit(EXIT_FAILURE);
        }

        return acl;
}

void
xacl_set_fd (int fd,
             acl_t acl)
{
        if (acl_set_fd(fd, acl) == -1) {
                struct stat st;
                fstat(fd, &st);
//              fprintf(stderr,
//                      "%s: acl_set_fd: %d, inode %lu: %s (continuing!)\n",
//                      progname, fd, st.st_ino, strerror(errno));
                //exit(EXIT_FAILURE);
        }
}

void
set_repoacl_from_parent (char *filename,
                         int parentfd)
{
        char *repofile, *atticfile;
        int repofilefd, atticfd;
        acl_t acl;

        bool inattic = false;

//      fprintf(stderr, "%s: set_repoacl_from_parent: called with %s\n",
//              progname, filename);

        /* use RCS filename */
        asprintf(&repofile, "%s,v", filename);
        if ((repofilefd = open(repofile, O_RDONLY)) == -1) {
                if (errno == ENOENT) {

                        /* it might have been moved to the attic */
                        asprintf(&atticfile, "Attic/%s", repofile);
                        repofilefd = xopen(atticfile, O_RDONLY);
                        free(atticfile);

                        /* this might be a new attic, in
                         * which case we have to set its
                         * permissions as well */
                        atticfd = xopen("Attic", O_RDONLY);
                        inattic = true;
//                      fprintf(stderr,
//                              "%s: set_repoacl_from_parent: inattic\n",
//                              progname);
                } else {
                        fprintf(stderr,
                                "%s: set_repoacl_from_parent: open: %s: %s\n",
                                progname, repofile, strerror(errno));
                        exit(EXIT_FAILURE);
                }
        }

        acl = xacl_get_fd(parentfd);

        /* annoying file we have no other means to set
         * except trying to every time */
#if 0
        if (access("CVS/fileattr", R_OK|W_OK) == 0) {
                int fd;
                fd = open("CVS/fileattr", O_RDWR);
                xacl_set_fd(fd, acl);
                close(fd);
                fd = open("CVS", O_RDONLY);
                xacl_set_fd(fd, acl);
        }
#endif

        if (inattic) {
                xacl_set_fd(atticfd, acl);
                close(atticfd);
        }

        clear_acl_bits(&acl, ACL_WRITE);
        if (user_has_execute_bit(repofilefd))
                add_acl_bit_ifhave(&acl, ACL_EXECUTE, ACL_READ);
        else
                clear_acl_bits(&acl, ACL_EXECUTE);

        xacl_set_fd(repofilefd, acl);

        close(repofilefd);
        free(repofile);
}