skip to content
==> src/durable.c <==
1/* blobs.rs's make_durable, write_durable, sync_dir and fsync, with POSIX calls. */
2#include "mesh/blobs/durable.h"
3#include "mesh/strbuf.h"
4
5#include <dirent.h>
6#include <errno.h>
7#include <fcntl.h>
8#include <stdio.h>
9#include <stdlib.h>
10#include <string.h>
11#include <sys/stat.h>
12#include <unistd.h>
13
14static bool fail(char **err, const char *what, const char *path) {
15    if (err) *err = mfmt("%s %s: %s", what, path, strerror(errno));
16    return false;
17}
18
19bool mb_mkdirs(const char *dir, char **err) {
20    char *p = mstrdup(dir);
21    for (char *s = p + 1; *s; s++) {
22        if (*s != '/') continue;
23        *s = 0;
24        if (mkdir(p, 0777) != 0 && errno != EEXIST) {
25            bool r = fail(err, "mkdir", p);
26            free(p);
27            return r;
28        }
29        *s = '/';
30    }
31    bool ok = mkdir(p, 0777) == 0 || errno == EEXIST || fail(err, "mkdir", p);
32    free(p);
33    return ok;
34}
35
36bool mb_write_durable(const char *dir, const char *name, const void *bytes, size_t n, char **err) {
37    if (!mb_mkdirs(dir, err)) return false;
38    char *tmp = mfmt("%s/%s.tmp", dir, name), *dst = mfmt("%s/%s", dir, name);
39    bool ok = false;
40    int fd = open(tmp, O_WRONLY | O_CREAT | O_TRUNC, 0644);
41    if (fd < 0) fail(err, "create", tmp);
42    else {
43        const char *p = bytes;
44        size_t left = n;
45        while (left) {
46            ssize_t w = write(fd, p, left);
47            if (w < 0 && errno == EINTR) continue;
48            if (w <= 0) break;
49            p += w;
50            left -= (size_t)w;
51        }
52        if (left) fail(err, "write", tmp);
53        else if (fsync(fd) != 0) fail(err, "fsync", tmp);
54        else ok = true;
55        close(fd);
56        if (ok && rename(tmp, dst) != 0) ok = fail(err, "rename", tmp);
57    }
58    free(tmp);
59    free(dst);
60    return ok;
61}
62
63bool mb_make_durable(const char *dir, const char *name, const char *link_from, const void *bytes, size_t n, char **err) {
64    if (link_from) {
65        if (!mb_mkdirs(dir, err)) return false;
66        char *dst = mfmt("%s/%s", dir, name);
67        int r = link(link_from, dst);
68        if (r == 0) {
69            free(dst);
70            return true;
71        }
72        if (errno == EEXIST) unlink(dst);
73        free(dst);
74    }
75    return mb_write_durable(dir, name, bytes, n, err);
76}
77
78bool mb_sync_dir(const char *dir, char **err) {
79    int fd = open(dir, O_RDONLY);
80    if (fd < 0) return fail(err, "open", dir);
81    bool ok = fsync(fd) == 0 || fail(err, "fsync", dir);
82    close(fd);
83    return ok;
84}
85
86void mb_remove_tree(const char *dir) {
87    DIR *d = opendir(dir);
88    if (!d) {
89        unlink(dir);
90        return;
91    }
92    struct dirent *e;
93    while ((e = readdir(d))) {
94        if (strcmp(e->d_name, ".") == 0 || strcmp(e->d_name, "..") == 0) continue;
95        char *p = mfmt("%s/%s", dir, e->d_name);
96        struct stat st;
97        if (lstat(p, &st) == 0 && S_ISDIR(st.st_mode)) mb_remove_tree(p);
98        else unlink(p);
99        free(p);
100    }
101    closedir(d);
102    rmdir(dir);
103}