skalibs: the unix-transactional library interface
Keywords: skalibs c unix-transactional library libunixonacid

libunixonacid
libskarnet
skalibs
Software
www.skarnet.org

# The `unix-transactional` library interface

The following functions are declared in the `skalibs/unix-transactional.h` header, and implemented in the `libskarnet.a` or `libskarnet.so` library.

## General information

`unix-transactional` provides an API for transactional/reliable filesystem operations.

Most functions are safe wrappers around the Unix `*at` system calls. They differ from the traditional system calls (safe-wrapped in `djbunix` and `allreadwrite`) in that they take an additional argument fd. When file is relative, it is opened relative to the directory associated with the file descriptor fd instead of the current working directory.

If file is absolute, they are equivalent to their `djbunix` and `allreadwrite` counterparts.

On systems that do not implement the `*at` system calls, these functions fall back to changing the current working directory, opening file with the corresponding `libstddjb` function, and restoring the original working directory using `fd_chdir`.

## Functions

### Basic fd operations relative to a directory

`int open2_at (int fd, char const *file, int flags)`
Safe wrapper around openat() when it takes 2 arguments.

`int open3_at (int fd, char const *file, int flags)`
Safe wrapper around openat() when it takes 3 arguments.

`int access_at (int fd, char const *file, int amode, unsigned int flag)`
Calls faccessat() with flags set either to zero, or `AT_EACCESS`, if flag is nonzero. This function ensures a fall back on platforms without `*at` system calls.

`int stat_at (int fd, char const *file, struct stat *st)`
`int lstat_at (int fd, char const *file, struct stat *st)`
Calls fstatat() without and with the `AT_SYMLINK_NOFOLLOW` flag, respectively. This function ensures a fall back on platforms without `*at` system calls.

`DIR *opendir_at (int dfd, char const *name)`
Relative version of opendir().

`int opengetlnclose (char const *file, stralloc *sa, int sep)`
`int opengetlnclose_at (int dirfd, char const *file, stralloc *sa, int sep)`
Slurps file (relative to dirfd in the `_at` version) into *sa up to (and including) the first sep character. Returns 1 on success, 0 if some bytes were read but no sep was found, -1 EPIPE on immediate EOF and -1 (and sets errno) on error.

`size_t openwritenclose_at (int dirfd, char const *file, char const *s, size_t n)`
`size_t openwritevnclose_at (int dirfd, char const *file, struct iovec const *v, unsigned int n)`
Relative versions of `openwritenclose_unsafe_sync` and `openwritevnclose_unsafe_sync` in `djbunix`, except for the fact that they return the number of bytes written instead of just 1 and 0.

The remaining functions are named after their corresponding `libstddjb` functions.

### Atomic filesystem deletion

`int atomic_rm_rf (char const *filename)`
`int atomic_rm_rf_tmp (char const *filename, stralloc *tmp)` Renames filename to a unique name and deletes its filesystem subtree. The difference between these two functions is the same as that between `rm_rf` and `rm_rf_tmp` in `djbunix`.

### Atomic symbolic link creation

###

`int atomic_symlink4 (char const *target, char const *name, char *buf, size_t len)` Atomically makes a symlink named name pointing to target. If name designated an existing file, it is unlinked. Does not work if name is an existing directory (but will work if it is a symlink to a directory!) Returns 1 on success, and 0 (and sets errno) on error.
In addition, if buf is not NULL, and points to a buffer of nonzero length len, then if name exists prior to the call, it must be a symbolic link, and its target be shorter than len bytes. If these conditions are fulfilled, the operation succeeds, and buf then contains the previous target of name, with a terminating NULL. If they are not, the function fails with 0 (errno is ENAMETOOLONG if the buffer is too small).