/*
 * $Id: bulkstat_unlink_test_modified.c,v 1.1 2007/10/03 16:23:57 mohamedb.longdrop.melbourne.sgi.com Exp $
 * Test bulkstat doesn't returned unlinked inodes.
 * Mark Goodwin <markgw@sgi.com> Fri Jul 20 09:13:57 EST 2007
 *
 * This is a modified version of bulkstat_unlink_test.c to reproduce a specific
 * problem see pv 969192
 */
#include <stdlib.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <xfs/xfs.h>
#include <unistd.h>
#include <string.h>

int
main(int argc, char *argv[])
{
    int e;
    int fd = 0;
    int i;
    int j;
    int k;
    int nfiles;
    int stride;
    struct stat sbuf;
    ino_t *inodelist;
    __u32 *genlist;
    struct xfs_fsop_bulkreq a;
    struct xfs_bstat *ret;
    int iterations;
    char fname[MAXPATHLEN];
    char *dirname;

    if (argc != 5) {
    	fprintf(stderr, "Usage: %s iterations nfiles stride dir\n", argv[0]);
    	fprintf(stderr, "Create dir with nfiles, unlink each stride'th file, sync, bulkstat\n");
	exit(1);
    }

    iterations = atoi(argv[1]);
    nfiles = atoi(argv[2]);
    stride = atoi(argv[3]);
    dirname = argv[4];
    if (!nfiles || !iterations) {
	fprintf(stderr, "Iterations and nfiles showld be non zero.\n");
    	exit(1);
    }

    inodelist = (ino_t *)malloc(nfiles * sizeof(ino_t));
    genlist = (__u32 *)malloc(nfiles * sizeof(__u32));
    ret = (struct xfs_bstat *)malloc(nfiles * sizeof(struct xfs_bstat));

    for (k=0; k < iterations; k++) {
	xfs_ino_t last_inode = 0;
	int count = 0;
	int testFiles = 0;

	printf("Iteration %d ... \n", k);

	memset(inodelist, 0, nfiles * sizeof(ino_t));
	memset(genlist, 0, nfiles * sizeof(__u32));
	memset(ret, 0, nfiles * sizeof(struct xfs_bstat));
	memset(&a, 0, sizeof(struct xfs_fsop_bulkreq));
	a.lastip = (__u64 *)&last_inode;
	a.icount = nfiles;
	a.ubuffer = ret;
	a.ocount = &count;

	if (mkdir(dirname, 0755) < 0) {
	    perror(dirname);
	    exit(1);
	}

	/* create nfiles and store their inode numbers in inodelist */
	for (i=0; i < nfiles; i++) {
	    sprintf(fname, "%s/file%06d", dirname, i);
	    if ((fd = open(fname, O_RDWR | O_CREAT | O_TRUNC, 0644)) < 0) {
		perror(fname);
		exit(1);
	    }
	    write(fd, fname, sizeof(fname));
	    if (fstat(fd, &sbuf) < 0) {
		perror(fname);
		exit(1);
	    }
	    inodelist[i] = sbuf.st_ino;
	    close(fd);
	}
	
	sync();
	
	/* collect bs_gen for the nfiles files */
	if ((fd = open(dirname, O_RDONLY)) < 0) {
	    perror(dirname);
	    exit(1);
	}

	testFiles = 0;
	for (;;) {
	    if ((e = xfsctl(dirname, fd, XFS_IOC_FSBULKSTAT, &a)) < 0) {
		perror("XFS_IOC_FSBULKSTAT1:");
		exit(1);
	    }

	    if (count == 0)
		break;

	    for (i=0; i < count; i++) {
		for (j=0; j < nfiles; j += stride) {
		    if (ret[i].bs_ino == inodelist[j]) {
			genlist[j] = ret[i].bs_gen;
			testFiles++;
		    }
		}
	    }
	}
	close(fd);
	
	printf("testFiles %d ... \n", testFiles);

	/* remove some of the first set of files */
	for (i=0; i < nfiles; i += stride) {
	    sprintf(fname, "%s/file%06d", dirname, i);
	    if (unlink(fname) < 0) {
	    	perror(fname);
		exit(1);
	    }
	}

	/* create a new set of files (replacing the unlinked ones) */
	for (i=0; i < nfiles; i += stride) {
	    sprintf(fname, "%s/file%06d", dirname, i);
	    if ((fd = open(fname, O_RDWR | O_CREAT | O_TRUNC, 0644)) < 0) {
		perror(fname);
		exit(1);
	    }
	    write(fd, fname, sizeof(fname));
	    close(fd);
	}

	sync();
	last_inode = 0; count = 0;

	if ((fd = open(dirname, O_RDONLY)) < 0) {
	    perror(dirname);
	    exit(1);
	}

	for (;;) {
	    if ((e = xfsctl(dirname, fd, XFS_IOC_FSBULKSTAT, &a)) < 0) {
		perror("XFS_IOC_FSBULKSTAT:");
		exit(1);
	    }

	    if (count == 0)
		    break;

	    for (i=0; i < count; i++) {
		for (j=0; j < nfiles; j += stride) {
		    if ((ret[i].bs_ino == inodelist[j]) &&
			(ret[i].bs_gen == genlist[j])) {
			/* oops, the same inode with old gen number */
			printf("Unlinked inode %llu with generation %d "
			       "returned by bulkstat\n",
				(unsigned long long)inodelist[j],
				 genlist[j]);
			exit(1);
		    }
		    if (ret[i].bs_ino == inodelist[j] &&
			ret[i].bs_gen != genlist[j] + 1) {
			/* oops, the new gen number is not 1 bigger than the old */
			printf("Inode with old generation %d, new generation %d\n",
			genlist[j], ret[i].bs_gen);
			exit(1);
		    }
		}
	    }
	}

	close(fd);

	sprintf(fname, "rm -rf %s\n", dirname);
	system(fname);

	sync();
	sleep(2);
	printf("passed\n");
    }

    exit(0);
}
