363 lines
		
	
	
		
			7.2 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
			
		
		
	
	
			363 lines
		
	
	
		
			7.2 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
/*
 | 
						|
 * Copyright (c) 2000, 2001, 2002, 2003, 2004, 2005, 2008, 2009
 | 
						|
 *	The President and Fellows of Harvard College.
 | 
						|
 *
 | 
						|
 * Redistribution and use in source and binary forms, with or without
 | 
						|
 * modification, are permitted provided that the following conditions
 | 
						|
 * are met:
 | 
						|
 * 1. Redistributions of source code must retain the above copyright
 | 
						|
 *    notice, this list of conditions and the following disclaimer.
 | 
						|
 * 2. Redistributions in binary form must reproduce the above copyright
 | 
						|
 *    notice, this list of conditions and the following disclaimer in the
 | 
						|
 *    documentation and/or other materials provided with the distribution.
 | 
						|
 * 3. Neither the name of the University nor the names of its contributors
 | 
						|
 *    may be used to endorse or promote products derived from this software
 | 
						|
 *    without specific prior written permission.
 | 
						|
 *
 | 
						|
 * THIS SOFTWARE IS PROVIDED BY THE UNIVERSITY AND CONTRIBUTORS ``AS IS'' AND
 | 
						|
 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 | 
						|
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 | 
						|
 * ARE DISCLAIMED.  IN NO EVENT SHALL THE UNIVERSITY OR CONTRIBUTORS BE LIABLE
 | 
						|
 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
 | 
						|
 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
 | 
						|
 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
 | 
						|
 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
 | 
						|
 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
 | 
						|
 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
 | 
						|
 * SUCH DAMAGE.
 | 
						|
 */
 | 
						|
 | 
						|
/*
 | 
						|
 * Concurrent directory operations test.
 | 
						|
 *
 | 
						|
 * Your system should survive this (without leaving a corrupted file
 | 
						|
 * system behind) once the file system assignment is complete.
 | 
						|
 */
 | 
						|
 | 
						|
#include <sys/types.h>
 | 
						|
#include <sys/wait.h>
 | 
						|
#include <sys/stat.h>
 | 
						|
#include <errno.h>
 | 
						|
#include <stdio.h>
 | 
						|
#include <unistd.h>
 | 
						|
#include <stdlib.h>
 | 
						|
#include <stdarg.h>
 | 
						|
#include <string.h>
 | 
						|
 | 
						|
#define NTRIES    100	/* loop count */
 | 
						|
#define NPROCS    5	/* actually totals 4x this +1 processes */
 | 
						|
 | 
						|
#define TESTDIR   "dirconc"
 | 
						|
#define NNAMES    4
 | 
						|
#define NAMESIZE  32
 | 
						|
 | 
						|
////////////////////////////////////////////////////////////
 | 
						|
 | 
						|
static const char *const names[NNAMES] = {
 | 
						|
	"aaaa",
 | 
						|
	"bbbb",
 | 
						|
	"cccc",
 | 
						|
	"dddd",
 | 
						|
};
 | 
						|
 | 
						|
static
 | 
						|
void
 | 
						|
choose_name(char *buf, size_t len)
 | 
						|
{
 | 
						|
	const char *a, *b, *c;
 | 
						|
 | 
						|
	a = names[random()%NNAMES];
 | 
						|
	if (random()%2==0) {
 | 
						|
		snprintf(buf, len, "%s", a);
 | 
						|
		return;
 | 
						|
	}
 | 
						|
	b = names[random()%NNAMES];
 | 
						|
	if (random()%2==0) {
 | 
						|
		snprintf(buf, len, "%s/%s", a, b);
 | 
						|
		return;
 | 
						|
	}
 | 
						|
	c = names[random()%NNAMES];
 | 
						|
	snprintf(buf, len, "%s/%s/%s", a, b, c);
 | 
						|
}
 | 
						|
 | 
						|
////////////////////////////////////////////////////////////
 | 
						|
 | 
						|
/*
 | 
						|
 * The purpose of this is to be atomic. In our world, straight
 | 
						|
 * tprintf tends not to be.
 | 
						|
 */
 | 
						|
static
 | 
						|
void
 | 
						|
#ifdef __GNUC__
 | 
						|
	__attribute__((__format__(__printf__, 1, 2)))
 | 
						|
#endif
 | 
						|
say(const char *fmt, ...)
 | 
						|
{
 | 
						|
	char buf[512];
 | 
						|
	va_list ap;
 | 
						|
	va_start(ap, fmt);
 | 
						|
	vsnprintf(buf, sizeof(buf), fmt, ap);
 | 
						|
	va_end(ap);
 | 
						|
	write(STDOUT_FILENO, buf, strlen(buf));
 | 
						|
}
 | 
						|
 | 
						|
////////////////////////////////////////////////////////////
 | 
						|
 | 
						|
static
 | 
						|
void
 | 
						|
dorename(const char *name1, const char *name2)
 | 
						|
{
 | 
						|
	if (rename(name1, name2) < 0) {
 | 
						|
		switch (errno) {
 | 
						|
		    case ENOENT:
 | 
						|
		    case ENOTEMPTY:
 | 
						|
		    case EINVAL:
 | 
						|
			break;
 | 
						|
		    default:
 | 
						|
			say("pid %d: rename %s -> %s: %s\n",
 | 
						|
			    getpid(), name1, name2, strerror(errno));
 | 
						|
			break;
 | 
						|
		}
 | 
						|
	}
 | 
						|
}
 | 
						|
 | 
						|
static
 | 
						|
void
 | 
						|
domkdir(const char *name)
 | 
						|
{
 | 
						|
	if (mkdir(name, 0775)<0) {
 | 
						|
		switch (errno) {
 | 
						|
		    case ENOENT:
 | 
						|
		    case EEXIST:
 | 
						|
			break;
 | 
						|
		    default:
 | 
						|
			say("pid %d: mkdir %s: %s\n",
 | 
						|
			    getpid(), name, strerror(errno));
 | 
						|
			break;
 | 
						|
		}
 | 
						|
	}
 | 
						|
}
 | 
						|
 | 
						|
static
 | 
						|
void
 | 
						|
dormdir(const char *name)
 | 
						|
{
 | 
						|
	if (rmdir(name)<0) {
 | 
						|
		switch (errno) {
 | 
						|
		    case ENOENT:
 | 
						|
		    case ENOTEMPTY:
 | 
						|
			break;
 | 
						|
		    default:
 | 
						|
			say("pid %d: rmdir %s: %s\n",
 | 
						|
			    getpid(), name, strerror(errno));
 | 
						|
			break;
 | 
						|
		}
 | 
						|
	}
 | 
						|
}
 | 
						|
 | 
						|
static
 | 
						|
void
 | 
						|
cleanup_rmdir(const char *name)
 | 
						|
{
 | 
						|
	if (rmdir(name)<0) {
 | 
						|
		switch (errno) {
 | 
						|
		    case ENOENT:
 | 
						|
			break;
 | 
						|
		    default:
 | 
						|
			say("cleanup (pid %d): rmdir %s: %s\n",
 | 
						|
			    getpid(), name, strerror(errno));
 | 
						|
			break;
 | 
						|
		}
 | 
						|
	}
 | 
						|
}
 | 
						|
 | 
						|
////////////////////////////////////////////////////////////
 | 
						|
 | 
						|
static
 | 
						|
void
 | 
						|
rename_proc(void)
 | 
						|
{
 | 
						|
	char name1[NAMESIZE], name2[NAMESIZE];
 | 
						|
	int ct;
 | 
						|
 | 
						|
	for (ct=0; ct<NTRIES; ct++) {
 | 
						|
		choose_name(name1, sizeof(name1));
 | 
						|
		choose_name(name2, sizeof(name2));
 | 
						|
		say("pid %2d: rename %s -> %s\n", (int)getpid(), name1, name2);
 | 
						|
		dorename(name1, name2);
 | 
						|
	}
 | 
						|
}
 | 
						|
 | 
						|
static
 | 
						|
void
 | 
						|
mkdir_proc(void)
 | 
						|
{
 | 
						|
	char name[NAMESIZE];
 | 
						|
	int ct;
 | 
						|
 | 
						|
	for (ct=0; ct<NTRIES; ct++) {
 | 
						|
		choose_name(name, sizeof(name));
 | 
						|
		say("pid %2d: mkdir  %s\n", (int)getpid(), name);
 | 
						|
		domkdir(name);
 | 
						|
	}
 | 
						|
}
 | 
						|
 | 
						|
static
 | 
						|
void
 | 
						|
rmdir_proc(void)
 | 
						|
{
 | 
						|
	char name[NAMESIZE];
 | 
						|
	int ct;
 | 
						|
 | 
						|
	for (ct=0; ct<NTRIES; ct++) {
 | 
						|
		choose_name(name, sizeof(name));
 | 
						|
		say("pid %2d: rmdir  %s\n", (int)getpid(), name);
 | 
						|
		dormdir(name);
 | 
						|
	}
 | 
						|
}
 | 
						|
 | 
						|
////////////////////////////////////////////////////////////
 | 
						|
 | 
						|
static
 | 
						|
pid_t
 | 
						|
dofork(void (*func)(void))
 | 
						|
{
 | 
						|
	pid_t pid;
 | 
						|
 | 
						|
	pid = fork();
 | 
						|
	if (pid < 0) {
 | 
						|
		say("fork: %s\n", strerror(errno));
 | 
						|
		return -1;
 | 
						|
	}
 | 
						|
	if (pid == 0) {
 | 
						|
		/* child */
 | 
						|
		func();
 | 
						|
		exit(0);
 | 
						|
	}
 | 
						|
	return pid;
 | 
						|
}
 | 
						|
 | 
						|
static
 | 
						|
void
 | 
						|
run(void)
 | 
						|
{
 | 
						|
	pid_t pids[NPROCS*4], wp;
 | 
						|
	int i, status;
 | 
						|
 | 
						|
	for (i=0; i<NPROCS; i++) {
 | 
						|
		pids[i*4] = dofork(mkdir_proc);
 | 
						|
		pids[i*4+1] = dofork(mkdir_proc);
 | 
						|
		pids[i*4+2] = dofork(rename_proc);
 | 
						|
		pids[i*4+3] = dofork(rmdir_proc);
 | 
						|
	}
 | 
						|
 | 
						|
	for (i=0; i<NPROCS*4; i++) {
 | 
						|
		if (pids[i]>=0) {
 | 
						|
			wp = waitpid(pids[i], &status, 0);
 | 
						|
			if (wp<0) {
 | 
						|
				say("waitpid %d: %s\n", (int) pids[i],
 | 
						|
				    strerror(errno));
 | 
						|
			}
 | 
						|
			else if (WIFSIGNALED(status)) {
 | 
						|
				say("pid %d: signal %d\n", (int) pids[i],
 | 
						|
				    WTERMSIG(status));
 | 
						|
			}
 | 
						|
			else if (WIFEXITED(status) && WEXITSTATUS(status)!=0) {
 | 
						|
				say("pid %d: exit %d\n", (int) pids[i],
 | 
						|
				    WEXITSTATUS(status));
 | 
						|
			}
 | 
						|
		}
 | 
						|
	}
 | 
						|
}
 | 
						|
 | 
						|
////////////////////////////////////////////////////////////
 | 
						|
 | 
						|
static
 | 
						|
void
 | 
						|
setup(const char *fs)
 | 
						|
{
 | 
						|
	if (chdir(fs)<0) {
 | 
						|
		say("chdir: %s: %s\n", fs, strerror(errno));
 | 
						|
		exit(1);
 | 
						|
	}
 | 
						|
	if (mkdir(TESTDIR, 0775)<0) {
 | 
						|
		say("mkdir: %s: %s\n", TESTDIR, strerror(errno));
 | 
						|
		exit(1);
 | 
						|
	}
 | 
						|
	if (chdir(TESTDIR)<0) {
 | 
						|
		say("chdir: %s: %s\n", TESTDIR, strerror(errno));
 | 
						|
		exit(1);
 | 
						|
	}
 | 
						|
}
 | 
						|
 | 
						|
static
 | 
						|
void
 | 
						|
recursive_cleanup(const char *sofar, int depth)
 | 
						|
{
 | 
						|
	char buf[NAMESIZE*32];
 | 
						|
	int i;
 | 
						|
 | 
						|
	for (i=0; i<NNAMES; i++) {
 | 
						|
		snprintf(buf, sizeof(buf), "%s/%s", sofar, names[i]);
 | 
						|
		if (rmdir(buf)<0) {
 | 
						|
			if (errno==ENOTEMPTY) {
 | 
						|
				recursive_cleanup(buf, depth+1);
 | 
						|
				cleanup_rmdir(buf);
 | 
						|
			}
 | 
						|
			else if (errno!=ENOENT) {
 | 
						|
				say("cleanup (pid %d): rmdir %s: %s\n",
 | 
						|
				    getpid(), buf, strerror(errno));
 | 
						|
			}
 | 
						|
		}
 | 
						|
	}
 | 
						|
}
 | 
						|
 | 
						|
static
 | 
						|
void
 | 
						|
cleanup(void)
 | 
						|
{
 | 
						|
	recursive_cleanup(".", 0);
 | 
						|
 | 
						|
	chdir("..");
 | 
						|
	cleanup_rmdir(TESTDIR);
 | 
						|
}
 | 
						|
 | 
						|
////////////////////////////////////////////////////////////
 | 
						|
 | 
						|
int
 | 
						|
main(int argc, char *argv[])
 | 
						|
{
 | 
						|
	const char *fs;
 | 
						|
	long seed = 0;
 | 
						|
 | 
						|
	say("Concurrent directory ops test\n");
 | 
						|
 | 
						|
	if (argc==0 || argv==NULL) {
 | 
						|
		say("Warning: argc is 0 - assuming you mean to run on lhd1: "
 | 
						|
		    "with seed 0\n");
 | 
						|
		fs = "lhd1:";
 | 
						|
	}
 | 
						|
	else if (argc==2) {
 | 
						|
		fs = argv[1];
 | 
						|
	}
 | 
						|
	else if (argc==3) {
 | 
						|
		fs = argv[1];
 | 
						|
		seed = atoi(argv[2]);
 | 
						|
	}
 | 
						|
	else {
 | 
						|
		say("Usage: dirconc filesystem [random-seed]\n");
 | 
						|
		exit(1);
 | 
						|
	}
 | 
						|
 | 
						|
	srandom(seed);
 | 
						|
	setup(fs);
 | 
						|
	say("Starting in %s/%s\n", fs, TESTDIR);
 | 
						|
 | 
						|
	run();
 | 
						|
 | 
						|
	say("Cleaning up\n");
 | 
						|
	cleanup();
 | 
						|
 | 
						|
	return 0;
 | 
						|
}
 |