// setattr.c by kanda.motohiro@gmail.com
// xfs extended attribute corruption bug reproducer
#include <stdio.h>
#include <string.h>
#include <fcntl.h>
#include <errno.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/xattr.h>
#include <sys/stat.h>
#include <sys/param.h>
#include <linux/limits.h>

#define die() do { perror(""); \
fprintf(stderr, "error at line %d\n", __LINE__); \
exit(1); } while (0)

#define fail(...) do { \
fprintf(stderr, __VA_ARGS__); exit (1); \
} while (0)

int main(int argc, char *argv[])
{
	int ret;
	int fd;
	char *path;
	char *name = "user.world";
	char *value;
	struct stat sbuf;
	size_t size = sizeof(value);

	if (argc != 2)
		fail("Usage: %s <file>\n", argv[0]);
	path = argv[1];

	fd = open(path, O_RDWR | O_CREAT, S_IRUSR | S_IWUSR);
	if (fd < 0) die();

	/*
	 * The value should be 3/4 the size of a fs block to ensure that we
	 * get to extents format.
	 */
	ret = fstat(fd, &sbuf);
	if (ret < 0) die();
	size = sbuf.st_blksize * 3 / 4;
	if (!size)
		fail("Invalid st_blksize(%ld)\n", sbuf.st_blksize);
	size = MIN(size, XATTR_SIZE_MAX);
	value = malloc(size);
	if (!value)
		fail("Failed to allocate memory\n");

	/* First, create a small xattr. */
	memset(value, '0', 1);
	ret = fsetxattr(fd, name, value, 1, XATTR_CREATE);
	if (ret < 0) die();
	close(fd);

	fd = open(path, O_RDWR);
	if (fd < 0) die();

	/* Then, replace it with bigger one, forcing short form to leaf conversion. */
	memset(value, '1', size);
	ret = fsetxattr(fd, name, value, size, XATTR_REPLACE);
	if (ret < 0) die();
	close(fd);

	return 0;
}
