// SPDX-License-Identifier: GPL-2.0
/*
 * Copyright (c) 2000-2001 Silicon Graphics, Inc.
 * All Rights Reserved.
 */
 
 /*
  * This is mostly a "crash & burn" test. -v turns on verbosity
  * and -c actually fails on errors - but expected errors aren't
  * expected...
  */
 
#include "global.h"

int verbose;
int pid;

int checkflag=0;

#define MKNOD_DEV 0

static int dirstress(char *dirname, int	dirnum, int nfiles, int keep, int nprocs_per_dir);
static int create_entries(int nfiles);
static int scramble_entries(int	nfiles);
static int remove_entries(int nfiles);

int
main(
	int	argc,
	char	*argv[])
{
	char	*dirname;
	int	nprocs;
	int	nfiles;
	int	c;
	int	errflg;
	int	i;
	long	seed;
	int	childpid;
	int	nprocs_per_dir;
	int	keep;
        int     status, istatus;
        
        pid=getpid();

	errflg = 0;
	dirname = NULL;
	nprocs = 4;
	nfiles = 100;
	seed = time(NULL);
	nprocs_per_dir = 1;
	keep = 0;
        verbose = 0;
	while ((c = getopt(argc, argv, "d:p:f:s:n:kvc")) != EOF) {
		switch(c) {
			case 'p':
				nprocs = atoi(optarg);
				break;
			case 'f':
				nfiles = atoi(optarg);
				break;
			case 'n':
				nprocs_per_dir = atoi(optarg);
				break;
			case 'd':
				dirname = optarg;
				break;
			case 's':
				seed = strtol(optarg, NULL, 0);
				break;
			case 'k':
				keep = 1;
				break;
			case '?':
				errflg++;
				break;
			case 'v':
				verbose++;
				break;
			case 'c':
                                checkflag++;
                                break;
		}
	}
	if (errflg || (dirname == NULL)) {
		printf("Usage: dirstress [-d dir] [-p nprocs] [-f nfiles] [-n procs per dir]\n"
                       "                 [-v] [-s seed] [-k] [-c]\n");
		exit(0); 
	}

	printf("** [%d] Using seed %ld\n", pid, seed);
	srandom(seed);

	for (i = 0; i < nprocs; i++) {
                if (verbose) fprintf(stderr,"** [%d] fork\n", pid);
		childpid = fork();
		if (childpid < 0) {
			perror("Fork failed");
			exit(errno);
		}
		if (childpid == 0) {
                        int r;
			/* child */
                        pid=getpid();
                        
                        if (verbose) fprintf(stderr,"** [%d] forked\n", pid);
			r=dirstress(dirname, i / nprocs_per_dir, nfiles, keep, nprocs_per_dir);
                        if (verbose) fprintf(stderr,"** [%d] exit %d\n", pid, r);
			exit(r);
		}
	}
        if (verbose) fprintf(stderr,"** [%d] wait\n", pid);
        istatus=0;
        
        /* wait & reap children, accumulating error results */
	while (wait(&status) != -1)
            istatus+=status/256;
        
	printf("INFO: Dirstress complete\n");
        if (verbose) fprintf(stderr,"** [%d] parent exit %d\n", pid, istatus);
	return istatus;
}



int
dirstress(
	char	*dirname,
	int	dirnum,
	int	nfiles,
	int	keep,
	int	nprocs_per_dir)
{
	int		error;
	char		buf[1024];
        int             r;
        
	sprintf(buf, "%s/stressdir", dirname);
        if (verbose) fprintf(stderr,"** [%d] mkdir %s 0777\n", pid, buf);
	error = mkdir(buf, 0777);
	if (error && (errno != EEXIST)) {
		perror("Create stressdir directory failed");
		return 1;
	}

        if (verbose) fprintf(stderr,"** [%d] chdir %s\n", pid, buf);
	error = chdir(buf);
	if (error) {
		perror("Cannot chdir to main directory");
		return 1;
	}

	sprintf(buf, "stress.%d", dirnum);
        if (verbose) fprintf(stderr,"** [%d] mkdir %s 0777\n", pid, buf);
	error = mkdir(buf, 0777);
	if (error && (errno != EEXIST)) {
		perror("Create pid directory failed");
		return 1;
	}

        if (verbose) fprintf(stderr,"** [%d] chdir %s\n", pid, buf);
	error = chdir(buf);
	if (error) {
		perror("Cannot chdir to dirnum directory");
		return 1;
	}

        r=1; /* assume failure */
        if (verbose) fprintf(stderr,"** [%d] create entries\n", pid);
	if (create_entries(nfiles)) {
            printf("!! [%d] create failed\n", pid);
        } else {
            if (verbose) fprintf(stderr,"** [%d] scramble entries\n", pid);
	    if (scramble_entries(nfiles)) {
                printf("!! [%d] scramble failed\n", pid);
            } else {
	        if (keep) {
                    if (verbose) fprintf(stderr,"** [%d] keep entries\n", pid);
                    r=0; /* success */
                } else {
                    if (verbose) fprintf(stderr,"** [%d] remove entries\n", pid);
		    if (remove_entries(nfiles)) {
                        printf("!! [%d] remove failed\n", pid);
                    } else {
                        r=0; /* success */
                    }
                }
            }
        }

        if (verbose) fprintf(stderr,"** [%d] chdir ..\n", pid);
	error = chdir("..");
	if (error) {
		/* If this is multithreaded, then expecting a ENOENT here is fine,
		 * and ESTALE is normal in the NFS case. */
		if (nprocs_per_dir > 1 && (errno == ENOENT || errno == ESTALE)) {
			return 0;
		}

		perror("Cannot chdir out of pid directory");
		return 1;
	}

	if (!keep) {
		sprintf(buf, "stress.%d", dirnum);
                if (verbose) fprintf(stderr,"** [%d] rmdir %s\n", pid, buf);
		if (rmdir(buf)) {
                    perror("rmdir");
                    if (checkflag) return 1;
                }
	}
	
        if (verbose) fprintf(stderr,"** [%d] chdir ..\n", pid);
	error = chdir("..");
	if (error) {
		/* If this is multithreaded, then expecting a ENOENT here is fine,
		 * and ESTALE is normal in the NFS case. */
		if (nprocs_per_dir > 1 && (errno == ENOENT || errno == ESTALE)) {
			return 0;
		}

		perror("Cannot chdir out of working directory");
		return 1;
	}

	if (!keep) {
                if (verbose) fprintf(stderr,"** [%d] rmdir stressdir\n", pid);
		if (rmdir("stressdir")) {
                    perror("rmdir");
                    if (checkflag) return 1;
                }
        }

	return r;
}

int
create_entries(
       int	nfiles)
{
	int	i;
	int	fd;
	char	buf[1024];

	for (i = 0; i < nfiles; i++) {
		sprintf(buf, "XXXXXXXXXXXX.%d", i);
		switch (i % 4) {
		case 0:
			/*
			 * Create a file
			 */
                        if (verbose) fprintf(stderr,"** [%d] creat %s\n", pid, buf);
			fd = creat(buf, 0666);
			if (fd > 0) {
                                if (verbose) fprintf(stderr,"** [%d] close %s\n", pid, buf);
				close(fd);
			} else {
                                fprintf(stderr,"!! [%d] close %s failed\n", pid, buf);
                                perror("close");
                                if (checkflag) return 1;
                        }
                        
			break;
		case 1:
			/*
			 * Make a directory.
			 */
                        if (verbose) fprintf(stderr,"** [%d] mkdir %s 0777\n", pid, buf);
			if (mkdir(buf, 0777)) {
                            fprintf(stderr,"!! [%d] mkdir %s 0777 failed\n", pid, buf);
                            perror("mkdir");
                            if (checkflag) return 1;
                        }
                        
			break;
		case 2:
			/*
			 * Make a symlink
			 */
                        if (verbose) fprintf(stderr,"** [%d] symlink %s %s\n", pid, buf, buf);
			if (symlink(buf, buf)) {
                            fprintf(stderr,"!! [%d] symlink %s %s failed\n", pid, buf, buf);
                            perror("symlink");
                            if (checkflag) return 1;
                        }
                        
			break;
		case 3:
			/*
			 * Make a dev node
			 */
                        if (verbose) fprintf(stderr,"** [%d] mknod %s 0x%x\n", pid, buf, MKNOD_DEV);
			if (mknod(buf, S_IFCHR | 0666, MKNOD_DEV)) {
                            fprintf(stderr,"!! [%d] mknod %s 0x%x failed\n", pid, buf, MKNOD_DEV);
                            perror("mknod");
                            if (checkflag) return 1;
                        }
                        
			break;
		default:
			break;
		}
	}
        return 0;
}


int
scramble_entries(
	int	nfiles)
{
	int		i;
	char		buf[1024];
	char		buf1[1024];
	long		r;
	int		fd;

	for (i = 0; i < nfiles * 2; i++) {
		switch (i % 5) {
		case 0:
			/*
			 * rename two random entries
			 */
			r = random() % nfiles;
			sprintf(buf, "XXXXXXXXXXXX.%ld", r);
			r = random() % nfiles;
			sprintf(buf1, "XXXXXXXXXXXX.%ld", r);

                        if (verbose) fprintf(stderr,"** [%d] rename %s %s\n", pid, buf, buf1);
			if (rename(buf, buf1)) {
                            perror("rename");
                            if (checkflag) return 1;
                        }
			break;
		case 1:
			/*
			 * unlink a random entry
			 */
			r = random() % nfiles;
			sprintf(buf, "XXXXXXXXXXXX.%ld", r);
                        if (verbose) fprintf(stderr,"** [%d] unlink %s\n", pid, buf);
			if (unlink(buf)) {
                            fprintf(stderr,"!! [%d] unlink %s failed\n", pid, buf);
                            perror("unlink");
                            if (checkflag) return 1;
                        }
			break;
		case 2:
			/*
			 * rmdir a random entry
			 */
			r = random() % nfiles;
			sprintf(buf, "XXXXXXXXXXXX.%ld", r);
                        if (verbose) fprintf(stderr,"** [%d] rmdir %s\n", pid, buf);
			if (rmdir(buf)) {
                            fprintf(stderr,"!! [%d] rmdir %s failed\n", pid, buf);
                            perror("rmdir");
                            if (checkflag) return 1;
                        }
			break;
		case 3:
			/*
			 * create a random entry
			 */
			r = random() % nfiles;
			sprintf(buf, "XXXXXXXXXXXX.%ld", r);

                        if (verbose) fprintf(stderr,"** [%d] creat %s 0666\n", pid, buf);
			fd = creat(buf, 0666);
			if (fd > 0) {
                                if (verbose) fprintf(stderr,"** [%d] close %s\n", pid, buf);
				if (close(fd)) {
                                    fprintf(stderr,"!! [%d] close %s failed\n", pid, buf);
                                    perror("close");
                                    if (checkflag) return 1;
                                }
			} else {
                            fprintf(stderr,"!! [%d] creat %s 0666 failed\n", pid, buf);
                            perror("creat");
                            if (checkflag) return 1;
                        }
			break;
		case 4:
			/*
			 * mkdir a random entry
			 */
			r = random() % nfiles;
			sprintf(buf, "XXXXXXXXXXXX.%ld", r);
                        if (verbose) fprintf(stderr,"** [%d] mkdir %s\n", pid, buf);
			if (mkdir(buf, 0777)) {
                            fprintf(stderr,"!! [%d] mkdir %s failed\n", pid, buf);
                            perror("mkdir");
                            if (checkflag) return 1;
                        }
			break;
		default:
			break;
		}
	}
        return 0;
}
			
int
remove_entries(
	int	nfiles)
{
	int		i;
	char		buf[1024];
	struct stat	statb;
	int		error;

	for (i = 0; i < nfiles; i++) {
		sprintf(buf, "XXXXXXXXXXXX.%d", i);
		error = lstat(buf, &statb);
		if (error) {
                        /* ignore this one */
			continue;
		}
		if (S_ISDIR(statb.st_mode)) {
                        if (verbose) fprintf(stderr,"** [%d] rmdir %s\n", pid, buf);
			if (rmdir(buf)) {
                            fprintf(stderr,"!! [%d] rmdir %s failed\n", pid, buf);
                            perror("rmdir");
                            if (checkflag) return 1;
                        }
		} else {
                        if (verbose) fprintf(stderr,"** [%d] unlink %s\n", pid, buf);
			if (unlink(buf)) {
                            fprintf(stderr,"!! [%d] unlink %s failed\n", pid, buf);
                            perror("unlink");
                            if (checkflag) return 1;
                        }
		}
	}
        return 0;
}
