Reading and Writing ACL Entries in Linux: Complete ACL Guide

 

Reading and Writing ACL Entries in Linux: Complete ACL Guide

Part 3 of 8 —Reading and Writing ACL Entries in Linux: Complete ACL Guide

📝
Long Text Form
📋
Short Text Form
🔄
acl_from_text / acl_to_text

When you use getfacl, setfacl, or ACL library functions, ACL entries are represented as text strings. There are two text formats: long text form (one entry per line, used by getfacl) and short text form (all entries on one line, comma-separated, used with setfacl -m). Understanding both formats is essential for working with ACLs from the command line and in C programs.

Key Terms

Long text formShort text form tag-type:qualifier:permsacl_from_text() acl_to_text()getfacl setfacl -msetfacl -M

The Basic Format of an ACL Entry in Text

Every ACL entry in text form follows this structure:

tag-type : [tag-qualifier] : permissions
Field Description Required? Example
tag-type Type keyword or abbreviation Always user or u
tag-qualifier Username, group name, UID, or GID Only for ACL_USER & ACL_GROUP bob or 1007
permissions Any combo of r, w, x (or – for absent) Always r-x or rw

Tag Type Keywords — Full Reference

Both long-form keywords and short abbreviations are accepted everywhere:

Short Form Long Form Qualifier Present? Tag Type Entry For
u user No ACL_USER_OBJ File owner
u user Yes ACL_USER Specific named user
g group No ACL_GROUP_OBJ File group
g group Yes ACL_GROUP Specific named group
m mask No ACL_MASK Mask for group class
o other No ACL_OTHER All other users
How to distinguish ACL_USER_OBJ from ACL_USER: If the qualifier field is empty (e.g., u::rw-), it’s ACL_USER_OBJ. If the qualifier has a name or number (e.g., u:bob:rw-), it’s ACL_USER. Same rule applies for group vs group_obj.

Long Text Form

Format: One entry per line, comments allowed

Used by getfacl output and the setfacl -M acl-file option (read from a file).

# Long text form — this is what getfacl produces
# Lines starting with # are comments

# file: project.c
# owner: ravi
# group: users
user::rw-          # ACL_USER_OBJ — owner gets rw
user:bob:r--       # ACL_USER — bob gets read only
user:alice:rwx     # ACL_USER — alice gets full access
group::r--         # ACL_GROUP_OBJ — file group gets read
group:devteam:rw-  # ACL_GROUP — devteam gets rw
mask::rw-          # ACL_MASK — cap for group class
other::---         # ACL_OTHER — everyone else: no access

Save this to a file and apply it with:

# Apply ACL from a file (long text form)
setfacl -M acl_spec.txt project.c

# You can use this to copy ACLs between files:
getfacl source.c | setfacl -M - dest.c

Short Text Form

Format: All entries on one line, comma-separated

Used with setfacl -m on the command line and with acl_from_text() / acl_to_text() in C programs.

# Short text form examples

# Traditional 0640 permissions as ACL:
u::rw-,g::r--,o::---

# Same using abbreviated keywords (r instead of r--):
u::rw,g::r,o::-

# Same using full keywords:
user::rw,group::r,other::-

# Full extended ACL in one line:
u::rw,u:bob:r,u:alice:rwx,g::r,g:devteam:rw,m::rwx,o::---

# Apply using setfacl -m:
setfacl -m "u:bob:r--,g:devteam:rw-" project.c

# Permissions field: can use full rwx or just letters present
# These are equivalent:
# r--  =  r
# rw-  =  rw
# rwx  =  rwx
# ---  =  -  (or just empty)

Permission Field — All Valid Formats

Format Meaning Example
rwx Read + Write + Execute u:alice:rwx
rw- Read + Write (no execute) g:devs:rw-
r-x Read + Execute u:bob:r-x
r– Read only (same as r) u:bob:r--
No permissions o::---
rw Short form: read+write (no x) g:devs:rw
No permissions (short form) o::-

Coding Examples

Example 1: Shell — build an ACL from a text file and apply it
#!/bin/bash
# Script: apply_acl_from_file.sh

TARGET="$1"
if [ -z "$TARGET" ]; then
    echo "Usage: $0 filename"
    exit 1
fi

# Create an ACL spec file in long text form
cat > /tmp/my_acl.txt <<'EOF'
# Team project file ACL
user::rw-
user:alice:rwx
user:bob:r--
group::r--
group:devteam:rw-
mask::rwx
other::---
EOF

# Apply the ACL from the file
setfacl -M /tmp/my_acl.txt "$TARGET"
echo "ACL applied to $TARGET"

# Verify
getfacl "$TARGET"

rm /tmp/my_acl.txt
Example 2: Shell — copy ACL from one file to another
# Method 1: pipe getfacl output into setfacl
getfacl source.c | setfacl --set-file=- destination.c

# Method 2: save and restore
getfacl source.c > /tmp/source_acl.txt
setfacl -M /tmp/source_acl.txt destination.c

# Method 3: recursive ACL backup and restore
# Save all ACLs in a directory tree:
getfacl -R /project/ > /backup/project_acls.txt

# Restore later (must be run from /):
setfacl --restore=/backup/project_acls.txt
Example 3: C — convert in-memory ACL to text and back
#include <stdio.h>
#include <stdlib.h>
#include <sys/acl.h>

int main(int argc, char *argv[]) {
    if (argc != 2) {
        fprintf(stderr, "Usage: %s filename\n", argv[0]);
        return 1;
    }

    /* --- acl_get_file: disk → in-memory acl_t --- */
    acl_t acl = acl_get_file(argv[1], ACL_TYPE_ACCESS);
    if (!acl) { perror("acl_get_file"); return 1; }

    /* --- acl_to_text: in-memory acl_t → text string --- */
    ssize_t len;
    char *text = acl_to_text(acl, &len);
    if (!text) { perror("acl_to_text"); acl_free(acl); return 1; }

    printf("=== ACL as text (long form) ===\n%s\n", text);
    printf("Text length: %zd bytes\n\n", len);

    /* --- acl_from_text: text string → in-memory acl_t --- */
    /* Build a new ACL from a short text string */
    const char *short_form = "u::rw-,u:bob:r--,g::r--,m::r--,o::---";
    acl_t new_acl = acl_from_text(short_form);
    if (!new_acl) {
        perror("acl_from_text");
    } else {
        /* Validate the ACL */
        if (acl_valid(new_acl) == 0) {
            printf("Parsed ACL is VALID\n");
        } else {
            printf("Parsed ACL is INVALID\n");
        }

        /* Convert back to text for display */
        char *new_text = acl_to_text(new_acl, NULL);
        printf("Parsed ACL:\n%s\n", new_text);
        acl_free(new_text);
        acl_free(new_acl);
    }

    /* Always free memory from ACL functions */
    acl_free(text);
    acl_free(acl);
    return 0;
}
/* Compile: gcc -o acl_text acl_text.c -lacl */
Example 4: C — create an ACL from scratch using text form and apply it
#include <stdio.h>
#include <sys/acl.h>

int main(int argc, char *argv[]) {
    if (argc != 2) {
        fprintf(stderr, "Usage: %s filename\n", argv[0]);
        return 1;
    }

    /* Define desired ACL in short text form */
    /* Owner: rw, Named user bob: read, File group: read,
       Group devteam: rw, Mask: rw, Others: none */
    const char *acl_spec = "u::rw-,u:bob:r--,g::r--,g:devteam:rw-,m::rw-,o::---";

    /* Parse text into in-memory ACL */
    acl_t acl = acl_from_text(acl_spec);
    if (!acl) { perror("acl_from_text"); return 1; }

    /* Validate before applying */
    if (acl_valid(acl) != 0) {
        fprintf(stderr, "ACL spec is invalid!\n");
        acl_free(acl);
        return 1;
    }

    /* Write ACL to the file (in-memory → disk) */
    if (acl_set_file(argv[1], ACL_TYPE_ACCESS, acl) != 0) {
        perror("acl_set_file");
        acl_free(acl);
        return 1;
    }

    printf("ACL applied successfully to %s\n", argv[1]);

    /* Verify by reading back */
    acl_t verify = acl_get_file(argv[1], ACL_TYPE_ACCESS);
    char *text = acl_to_text(verify, NULL);
    printf("Applied ACL:\n%s\n", text);

    acl_free(text);
    acl_free(verify);
    acl_free(acl);
    return 0;
}
/* Compile: gcc -o set_acl_text set_acl_text.c -lacl */
Example 5: Parse and display ACL text form entries in detail
#include <stdio.h>
#include <sys/acl.h>

void print_permset(acl_permset_t ps) {
    printf("%c%c%c",
        acl_get_perm(ps, ACL_READ)    ? 'r' : '-',
        acl_get_perm(ps, ACL_WRITE)   ? 'w' : '-',
        acl_get_perm(ps, ACL_EXECUTE) ? 'x' : '-'
    );
}

int main(void) {
    /* Parse a short form ACL string */
    const char *spec = "u::rwx,u:1007:r--,g::r-x,g:60:rw-,m::rw-,o::r--";
    acl_t acl = acl_from_text(spec);
    if (!acl) { perror("acl_from_text"); return 1; }

    printf("Parsing: %s\n\n", spec);
    printf("%-6s %-14s %-10s %s\n", "Type", "Tag", "Qualifier", "Perms");
    printf("%-6s %-14s %-10s %s\n", "----", "---", "---------", "-----");

    acl_entry_t entry;
    acl_tag_t tag;
    acl_permset_t perms;
    int id = ACL_FIRST_ENTRY;

    while (acl_get_entry(acl, id, &entry) == 1) {
        id = ACL_NEXT_ENTRY;
        acl_get_tag_type(entry, &tag);
        acl_get_permset(entry, &perms);

        /* Tag type abbreviation */
        const char *abbrev = (tag == ACL_USER_OBJ || tag == ACL_USER) ? "u" :
                             (tag == ACL_GROUP_OBJ || tag == ACL_GROUP) ? "g" :
                             (tag == ACL_MASK) ? "m" : "o";

        /* Full name */
        const char *full = (tag == ACL_USER_OBJ)  ? "user (owner)" :
                           (tag == ACL_USER)       ? "user (named)" :
                           (tag == ACL_GROUP_OBJ)  ? "group (owner)" :
                           (tag == ACL_GROUP)      ? "group (named)" :
                           (tag == ACL_MASK)       ? "mask" : "other";

        printf("%-6s %-14s ", abbrev, full);

        if (tag == ACL_USER) {
            uid_t *uid = acl_get_qualifier(entry);
            printf("uid=%-6d ", (int)*uid);
            acl_free(uid);
        } else if (tag == ACL_GROUP) {
            gid_t *gid = acl_get_qualifier(entry);
            printf("gid=%-6d ", (int)*gid);
            acl_free(gid);
        } else {
            printf("%-10s ", "-");
        }

        print_permset(perms);
        printf("\n");
    }

    acl_free(acl);
    return 0;
}
/* Compile: gcc -o parse_acl parse_acl.c -lacl */

Interview Questions

Q1. What are the two text formats for ACL entries and when is each used?

Long text form: One entry per line, supports comments (with #). Used by getfacl output and setfacl -M acl-file (read from file).

Short text form: All entries on one comma-separated line. Used with setfacl -m on command line and in C programs with acl_from_text()/acl_to_text().

Q2. How do you distinguish ACL_USER_OBJ from ACL_USER in text form?

By the qualifier field. user::rw- (empty qualifier between second and third colons) = ACL_USER_OBJ (file owner). user:bob:rw- (qualifier present) = ACL_USER for named user bob. Same logic applies to group:: (ACL_GROUP_OBJ) vs group:name: (ACL_GROUP).

Q3. What C functions convert between in-memory ACL and text form?

acl_to_text(acl, &len) converts in-memory acl_t to a long-text-form string. acl_from_text(string) converts a long or short text form string to an in-memory acl_t. Both allocate memory that must be freed with acl_free().

Q4. Write a setfacl command to give user ‘ravi’ read+execute, group ‘qa’ read-only, and deny everyone else.

setfacl -m "u:ravi:r-x,g:qa:r--,m::r-x,o::---" filename

Note: ACL_MASK must also be set (either explicitly or let setfacl auto-calculate). The owner’s existing entry is not changed by this command — only the named entries.

Q5. How do you back up and restore ACLs for an entire directory tree?

# Backup
getfacl -R /myproject/ > acl_backup.txt

# Restore (run from / to use correct relative paths)
cd /
setfacl --restore=acl_backup.txt
Next: ACL Mask Entry →

Understand how ACL_MASK interacts with chmod and protects ACL data

Continue → ← Back

Leave a Reply

Your email address will not be published. Required fields are marked *