Skip to content

Instantly share code, notes, and snippets.

@vassilit
Last active February 23, 2025 20:23
Show Gist options
  • Save vassilit/e00d59135e3204aabcc4cca8c333c140 to your computer and use it in GitHub Desktop.
Save vassilit/e00d59135e3204aabcc4cca8c333c140 to your computer and use it in GitHub Desktop.
simple test program to bench when the FS is ready after returning from fuse_main()
#define _DEFAULT_SOURCE
#define FUSE_USE_VERSION 31
#include <fuse.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/stat.h>
#include <sys/wait.h>
#include <sys/mman.h>
#include <errno.h>
#if __linux__
#include <sys/mount.h>
#define UNMOUNT(mnt_point) umount(mnt_point)
#elif __FreeBSD__
#include <sys/param.h>
#include <sys/mount.h>
#define UNMOUNT(mnt_point) unmount(mnt_point, 0)
#endif
#define likely(x) __builtin_expect(!!(x), 1)
#define unlikely(x) __builtin_expect(!!(x), 0)
#define NELS(array) (sizeof(array)/sizeof(array[0]))
#define AR_MNT_TPL "/tmp/tmpfXXXXXX"
#define AR_MNT_LEN NELS(AR_MNT_TPL)
#define TESTED_MODE (S_IFDIR | 0444) /* dr--r--r-- */
#define TESTED_NLINK 2
static char mnt_point[AR_MNT_LEN];
static struct fuse_args args;
static int argc = 2;
static char *argv[] = { "f-test", mnt_point, NULL };
static volatile char *sem;
static int dummy_getattr(const char *path, struct stat *stbuf, struct fuse_file_info *fi __attribute__ ((unused))) {
int res = 0;
memset(stbuf, 0, sizeof (struct stat));
if (strcmp(path, "/") == 0) {
stbuf->st_mode = TESTED_MODE;
stbuf->st_nlink = TESTED_NLINK;
}
else
res = -ENOENT;
return res;
}
static void *dummy_init(struct fuse_conn_info *conn __attribute__ ((unused)), struct fuse_config *cfg) {
*sem = 0;
__sync_synchronize();
return NULL;
}
static struct fuse_operations my_ops = {
.init = dummy_init,
.getattr = dummy_getattr,
};
static void mount_archive(void) {
static const char template[] = AR_MNT_TPL;
memcpy(mnt_point, template, AR_MNT_LEN - 1);
if (unlikely(mkdtemp(mnt_point) == NULL)) {
perror("test.c: mkdtemp failed");
exit(EXIT_FAILURE);
}
args = (struct fuse_args) FUSE_ARGS_INIT(argc, argv);
if (unlikely(fuse_opt_parse(&args, NULL, NULL, NULL) < 0)) {
fputs("test.c: fuse_opt_parse failed\n", stderr);
exit(EXIT_FAILURE);
}
pid_t pid = fork();
if (pid == 0)
_exit(fuse_main(args.argc, args.argv, &my_ops, NULL));
else if (unlikely(pid < 0)) {
perror("test.c: fork failed");
exit(EXIT_FAILURE);
}
else {
fuse_opt_free_args(&args);
int status;
for (;;) {
if (unlikely(waitpid(pid, &status, 0) < 0)) {
if (unlikely(errno == EINTR))
continue;
fprintf(stderr, "test.c: waitpid %d failed\n", pid);
exit(EXIT_FAILURE);
}
if (likely(!status))
return;
if (WIFEXITED(status)) {
if (unlikely(status = WEXITSTATUS(status))) {
fprintf(stderr, "test.c: fuse_main failed with status: %d\n", status);
status = -1;
}
break;
}
else if (WIFSIGNALED(status)) {
fprintf(stderr, "test.c: fuse_main died with signal: %d\n", WTERMSIG(status));
status = -1;
break;
}
}
if (unlikely(status)) {
if (unlikely(rmdir(mnt_point) < 0))
perror("test.c: removing mount point failed");
exit(EXIT_FAILURE);
}
else
return;
}
}
static int umount_archive(void) {
int status = 0;
if (unlikely(UNMOUNT(mnt_point) < 0)) {
perror("test.c: u(n)mount: failed");
status = -1;
} else if (unlikely(rmdir(mnt_point) < 0)) {
perror("test.c: removing mount point failed");
status = -1;
}
return status;
}
int main(void) {
struct stat st;
if (unlikely((sem = mmap(0, sizeof (char), PROT_READ | PROT_WRITE,
MAP_SHARED | MAP_ANON | MAP_32BIT, -1, 0)) == MAP_FAILED)) {
perror("mmap");
return -1;
}
for (int i = 200; *sem = 1, i > 0; i--) {
mount_archive();
while (likely(*sem))
__sync_synchronize();
usleep(i);
if (unlikely(lstat(mnt_point, &st) < 0))
perror("lstat");
else if (st.st_mode != TESTED_MODE || st.st_nlink != TESTED_NLINK) {
printf("us_wait: %d, mode: %d, nlink: %ld\n", i, st.st_mode, st.st_nlink);
usleep(10000); /* for the umount to succeed, */
}
if (unlikely(umount_archive() < 0))
return 1;
}
return 0;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment