==> 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}