У меня появилась необходимость в составе одной задачи проверять программно свободное место на блочном устройстве под Linux. Самое простое блочное устройство — диск.

Попытка поиска способа решить задачу с помощью стандартной библиотеки Zig 0.16.0 не увенчалась успехом, хотя еще в 0.15.2 он был. Ну и ладно, когда нас это останавливало?

Самый простой способ — использовать системный вызов.

Если кто не в курсе, системные вызовы (syscalls) — это интерфейс между пользовательским пространством и ядром ОС. Этот интерфейс есть в любой операционной системе. Правда, нужно сказать, что идентификаторы различаются даже внутри одной ОС на разных архитектурах. Благо, что нам эти идентификаторы предоставляет стандартная библиотека, нужно просто знать, где смотреть.

Так вот, все языки программирования используют системные вызовы для проведения привычных нам операций, например печати на экран, открытия файла или чтения информации о файле, но делают это неявно, предоставляя нам удобный интерфейс.

Вот и мы займемся примерно тем же самым для получения свободного места на диске, раз в стандартной библиотеке такой функции в пространстве имён std.Io нет. Для этого мы используем системный вызов statfs, информацию о котором можно получить с помощью команды man statfs.2 в консоли Linux или на сайте.

Некоторое время было мной потрачено на изучение мануалов и возможностей стандартной библиотеки Zig на предмет выявления механизмов по осуществлению системных вызовов, и появился следующий код, который делает ровно то, что нам нужно:

//! Простейшая утилита для получения свободного места на диске в Linux
const std = @import("std");
const linux = std.os.linux;

// путь к директории для проверки свободного места диска, на котором директория находится
const check_directory_path = "/";

// struct statfs для x86_64 / aarch64 Linux
// подробнее о структуре statfs: https://man7.org/linux/man-pages/man2/statfs.2.html
const Statfs = extern struct {
    f_type: isize,
    f_bsize: isize,
    f_blocks: u64,
    f_bfree: u64,
    f_bavail: u64,
    f_files: u64,
    f_ffree: u64,
    f_fsid: [2]i32,
    f_namelen: isize,
    f_frsize: isize,
    f_flags: isize,
    f_spare: [4]isize,
};

pub fn main() !void {
    // получаем свободное место на указанной файловой системе
    const free = try getFreeDiskSpace(check_directory_path);
    // выводим результат
    std.debug.print("Свободно на \"{s}\": {d} байт ({1Bi:.1})\n", .{ check_directory_path, free });
}

// получаем свободное место на диске
pub fn getFreeDiskSpace(path: [*:0]const u8) !u64 {
    var stat: Statfs = undefined;
    // вызываем системный вызов statfs для получения информации о файловой системе
    const rc = linux.syscall2(.statfs, @intFromPtr(path), @intFromPtr(&stat));
    // проверяем результат вызова
    if (rc != 0) return error.SystemCallFailed;
    // вычислим свободное место для непривилегированного пользователя в байтах
    return stat.f_bavail * @as(u64, @intCast(stat.f_bsize));
}

Как бы вот. Буду рад, если кому-нибудь поможет. А также, если кто-то предложит кросс-платформенный вариант.