Finding UNIX File Permissions in Zig

This Zig program is a command line utility designed to read and display the file permissions of a single file specified as an argument:

const std = @import("std");

pub fn main() !void {
    const allocator = std.heap.page_allocator;
    const args = try std.process.argsAlloc(allocator);

    if (args.len < 2) {
        std.debug.print("Usage: {s} <path>\n", .{args[0]});
        return;
    }
    const path = args[1];

    const cwd = std.fs.cwd();
    var file = try cwd.openFile(path, .{ .mode = .read_only });
    defer file.close();

    const fileStat = try file.stat();
    const permissions = fileStat.mode & 0o7777;
    std.debug.print("File permissions (octal): {o}\n", .{permissions});

    if (permissions == 0o755) {
        std.debug.print("File permissions (octal): {o}\n", .{permissions});
    }
}

First, the program imports the Zig standard library (std), which is necessary for accessing functions related to memory allocation, process arguments, file system operations and debugging output.

The execution entry point is the pub fn main() !void function. The !void return type signifies that the function either returns nothing (void) on success or can return an error.

The program then checks if the user provided enough arguments. args[0] is always the name of the program itself, so args.len < 2 checks if the user failed to provide any other arguments. If no file path is given, it prints a usage message to standard error using std.debug.print() and then returns, ending the program.

Next, it performs file operations. It gets the current working directory with std.fs.cwd() and uses that directory to openFile() using the path provided. The file is opened in .read_only mode. A defer file.close() statement is placed immediately after opening the file. This is a crucial Zig feature that guarantees file.close() will be called when the function exits, ensuring the file is always closed, even if an error happens later.

With the file open, the program calls try file.stat() to get the file metadata (or “statistics”). This returns a struct (fileStat) that includes a mode field. This mode field contains information about the file type (e.g., regular file, directory) and its permission bits. To isolate only the permission bits, the code performs a bitwise AND (&) with the octal mask 0o7777. This mask effectively zeroes out all bits except the standard Unix permission bits.

Finally, the program prints the resulting permissions to standard error. The format string {o} tells std.debug.print() to format the permissions integer as an octal number, which is the conventional way to display file permissions. The program then has a redundant if block that checks if the permissions are exactly 0o755 and, if so, prints the exact same line again.

Save the code as unixFilePerm.zig and execute it on your machine:

$ zig version
0.15.2
$ zig run unixFilePerm.zig -- unixFilePerm.zig
File permissions (octal): 644 
$ zig run unixFilePerm.zig -- /bin
File permissions (octal): 755
File permissions (octal): 755

Happy coding in Zig!