#define _GNU_SOURCE

#include <fcntl.h>
#include <linux/openat2.h>
#include <sys/syscall.h>
#include <stdio.h>
#include <unistd.h>
#include <errno.h>
#include <string.h>
#include <sys/statfs.h>
#include <linux/magic.h>
#include <dirent.h>

struct linux_dirent {
	unsigned long  d_ino;     /* Inode number */
	unsigned long  d_off;     /* Offset to next linux_dirent */
	unsigned short d_reclen;  /* Length of this linux_dirent */
	char           d_name[];  /* Filename (null-terminated) */
	/*
	char           pad;       // Zero padding byte
	char           d_type;    // File type (only since Linux
	                              // 2.6.4); offset is (d_reclen - 1)
	*                                                                                       */
};


int main(int argc, char **argv)
{
	const char *target = NULL;
	int data_pathref_fd = -1;
	int d1_pathref_fd = -1;
	int proc_fd = -1;
	int rc = -1;
	int i;
	char proc_path[256];
	struct open_how linux_how = {
		.flags = O_RDONLY | O_DIRECTORY,
		.mode = 0,
		.resolve = RESOLVE_NO_XDEV,
	};
	char dentbuf[1024*1024];

	if (argc < 2) {
		printf("Usage: %s <target>\n", argv[0]);
		return 1;
	}
	target = argv[1];

	printf("Target is %s\n", target);

	rc = chdir(target);
	printf("chddir(%s) -> %d\n", target, rc);
	if (rc == -1) {
		perror("chdir");
		return 1;
	}

	data_pathref_fd = openat(AT_FDCWD,
				 "data",
				 O_RDONLY | O_NOFOLLOW | O_PATH);
	printf("openat(AT_FDCWD, data, O_RDONLY | O_NOFOLLOW | O_PATH) -> %d\n", data_pathref_fd);
	if (data_pathref_fd == -1) {
		perror("openat");
		return 1;
	}

	d1_pathref_fd = openat(data_pathref_fd,
			       "d1",
			       O_RDONLY | O_NOFOLLOW | O_PATH);
	printf("openat(%d, d1, O_RDONLY | O_NOFOLLOW | O_PATH) -> %d\n", data_pathref_fd, d1_pathref_fd);
	if (d1_pathref_fd == -1) {
		perror("openat");
		return 1;
	}

	snprintf(proc_path,
		 sizeof(proc_path),
		 "/proc/self/fd/%d",
		 d1_pathref_fd);
again:
	proc_fd = syscall(SYS_openat2, AT_FDCWD, proc_path, &linux_how, sizeof(linux_how));
	printf("openat2(AT_FDCWD, %s, ) -> %d %s\n", proc_path, proc_fd, proc_fd == -1 ? strerror(errno) : "");
	//printf("Press key to continue (pid=%d)...\n", getpid());
	//getchar();
	if (proc_fd == -1) {
		struct statfs sbuf = { 0 };
		rc = fstatfs(d1_pathref_fd, &sbuf);
		if (rc == -1) {
			perror("statfs");
			return 1;
		}

		printf("sbuf.f_type=0x%X\n", sbuf.f_type);
		if (sbuf.f_type == AUTOFS_SUPER_MAGIC) {
			printf("It is autofs, fallback to name-based\n");
			proc_fd = openat(data_pathref_fd,
			       "d1",
			       O_RDONLY | O_NOFOLLOW);
			printf("openat(%d, d1, O_RDONLY | O_NOFOLLOW) -> %d\n", data_pathref_fd, proc_fd);
			if (proc_fd == -1) {
				perror("fallback");
				return -1;
			}
			goto list;
		} else {
			linux_how.resolve &= ~RESOLVE_NO_XDEV;
			printf("It is not autofs, retry without RESOLVE_NO_XDEV\n");
			goto again;
		}
		return 1;
	}
list:
	rc = syscall(SYS_getdents, proc_fd, dentbuf, sizeof(dentbuf));
	printf("getdents(%d) -> %d\n", proc_fd);
	if (rc == -1) {
		perror("getdents");
		return 1;
	}
	for (i = 0; i < rc;) {
		struct linux_dirent *d = NULL;
		char d_type;
		d = (struct linux_dirent *) (dentbuf + i);
			printf("%s\n", (char *)d->d_name );
		i += d->d_reclen;
	}

	close(proc_fd);
	close(d1_pathref_fd);
	close(data_pathref_fd);
	return 0;
}
