/* 
   dbench version 2
   Copyright (C) 1999 by Andrew Tridgell <tridge@samba.org>
   Copyright (C) 2001 by Martin Pool <mbp@samba.org>
   
   This program is free software; you can redistribute it and/or modify
   it under the terms of the GNU General Public License as published by
   the Free Software Foundation; either version 2 of the License, or
   (at your option) any later version.
   
   This program is distributed in the hope that it will be useful,
   but WITHOUT ANY WARRANTY; without even the implied warranty of
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
   GNU General Public License for more details.
   
   You should have received a copy of the GNU General Public License
   along with this program; if not, write to the Free Software
   Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/

#include "dbench.h"

#define MAX_FILES 200

char *server = NULL;
extern int sync_open, sync_dirs;

static struct {
	char *name;
	int fd;
	int handle;
} ftable[MAX_FILES];

static int find_handle(struct child_struct *child, int handle)
{
	int i;
	for (i=0;i<MAX_FILES;i++) {
		if (ftable[i].handle == handle) return i;
	}
	printf("(%d) ERROR: handle %d was not found\n", 
	       child->line, handle);
	exit(1);
}


/* Find the directory holding a file, and flush it to disk.  We do
   this in -S mode after a directory-modifying mode, to simulate the
   way knfsd tries to flush directories.  MKDIR and similar operations
   are meant to be synchronous on NFSv2. */
static void sync_parent(char *fname)
{
	char *copy_name;
	int dir_fd;
	char *slash;

	if (strchr(fname, '/')) {
		copy_name = strdup(fname);
		slash = strrchr(copy_name, '/');
		*slash = '\0';
	} else {
		copy_name = strdup(".");
	} 
	
	dir_fd = open(copy_name, O_RDONLY);
	if (dir_fd == -1) {
		printf("open directory \"%s\" for sync failed: %s\n",
		       copy_name,
		       strerror(errno));
	} else {
#if defined(HAVE_FDATASYNC)
		if (fdatasync(dir_fd) == -1) {
#else
		if (fsync(dir_fd) == -1) {
#endif
			printf("datasync directory \"%s\" failed: %s\n",
			       copy_name,
			       strerror(errno));
		}
		if (close(dir_fd) == -1) {
			printf("close directory failed: %s\n",
			       strerror(errno));
		}
	}
	free(copy_name);
}

static void xattr_fd_read_hook(int fd)
{
#if HAVE_EA_SUPPORT
	extern int ea_enable;
	char buf[44];
	if (ea_enable) {
		memset(buf, 0, sizeof(buf));
		sys_fgetxattr(fd, "user.DosAttrib", buf, sizeof(buf));
	}
#else
	(void)fd;
#endif
}

static void xattr_fname_read_hook(const char *fname)
{
#if HAVE_EA_SUPPORT
	extern int ea_enable;
	if (ea_enable) {
		char buf[44];
		sys_getxattr(fname, "user.DosAttrib", buf, sizeof(buf));
	}
#else
	(void)fname;
#endif
}

static void xattr_fd_write_hook(int fd)
{
#if HAVE_EA_SUPPORT
	extern int ea_enable;
	if (ea_enable) {
		struct timeval tv;
		char buf[44];
		sys_fgetxattr(fd, "user.DosAttrib", buf, sizeof(buf));
		memset(buf, 0, sizeof(buf));
		/* give some probability of sharing */
		if (random() % 10 < 2) {
			*(time_t *)buf = time(NULL);
		} else {
			gettimeofday(&tv, NULL);
			memcpy(buf, &tv, sizeof(tv));
		}
		if (sys_fsetxattr(fd, "user.DosAttrib", buf, sizeof(buf), 0) != 0) {
			printf("fsetxattr failed - %s\n", strerror(errno));
			exit(1);
		}
	}
#else
	(void)fd;
#endif
}

static int expected_status(const char *status)
{
	if (strcmp(status, "NT_STATUS_OK") == 0) return 0;
	return -1;
}

/*
  simulate pvfs_resolve_name()
*/
static void resolve_name(const char *name)
{
	struct stat st;
	char *dname, *fname;
	DIR *dir;
	char *p;
	struct dirent *d;

	if (stat(name, &st) == 0) {
		xattr_fname_read_hook(name);
		return;
	}

	dname = strdup(name);
	p = strrchr(dname, '/');
	if (!p) return;
	*p = 0;
	fname = p+1;

	dir = opendir(dname);
	if (!dir) {
		free(dname);
		return;
	}
	while ((d = readdir(dir))) {
		if (strcasecmp(fname, d->d_name) == 0) break;
	}
	closedir(dir);
	free(dname);
}

static void failed(struct child_struct *child)
{
	child->failed = 1;
	printf("ERROR: child %d failed\n", child->id);
	exit(1);
}

void nb_setup(struct child_struct *child)
{
	(void)child;
}

void nb_unlink(struct child_struct *child, char *fname, int attr, const char *status)
{
	(void)attr;

	resolve_name(fname);

	if (unlink(fname) != expected_status(status)) {
		printf("(%d) unlink %s failed (%s) - expected %s\n", 
		       child->line, fname, strerror(errno), status);
		failed(child);
	}
	if (sync_dirs) sync_parent(fname);
}

void nb_mkdir(struct child_struct *child, char *dname, const char *status)
{
	(void)child;
	(void)status;
	resolve_name(dname);
	mkdir(dname, 0777);
}

void nb_rmdir(struct child_struct *child, char *fname, const char *status)
{
	resolve_name(fname);

	if (rmdir(fname) != expected_status(status)) {
		printf("(%d) rmdir %s failed (%s) - expected %s\n", 
		       child->line, fname, strerror(errno), status);
		failed(child);
	}
	if (sync_dirs) sync_parent(fname);
}

void nb_createx(struct child_struct *child, const char *fname, 
		uint32_t create_options, uint32_t create_disposition, int fnum,
		const char *status)
{
	int fd, i;
	int flags = O_RDWR;
	struct stat st;

	resolve_name(fname);

	if (sync_open) flags |= O_SYNC;

	if (create_disposition == FILE_CREATE) {
		flags |= O_CREAT;
	}

	if (create_disposition == FILE_OVERWRITE ||
	    create_disposition == FILE_OVERWRITE_IF) {
		flags |= O_CREAT | O_TRUNC;
	}

	if (create_options & FILE_DIRECTORY_FILE) {
		/* not strictly correct, but close enough */
		mkdir(fname, 0700);
	}

	if (create_options & FILE_DIRECTORY_FILE) flags = O_RDONLY|O_DIRECTORY;

	fd = open(fname, flags, 0600);
	if (fd == -1) {
		if (strcmp(status, "NT_STATUS_OK") == 0) {
			printf("(%d) open %s failed for handle %d (%s)\n", 
			       child->line, fname, fnum, strerror(errno));
		}
		return;
	}
	if (strcmp(status, "NT_STATUS_OK") != 0) {
		printf("(%d) open %s succeeded for handle %d\n", 
		       child->line, fname, fnum);
		close(fd);
		return;
	}
	
	for (i=0;i<MAX_FILES;i++) {
		if (ftable[i].handle == 0) break;
	}
	if (i == MAX_FILES) {
		printf("file table full for %s\n", fname);
		exit(1);
	}
	ftable[i].name = strdup(fname);
	ftable[i].handle = fnum;
	ftable[i].fd = fd;

	fstat(fd, &st);

	if (!S_ISDIR(st.st_mode)) {
		xattr_fd_write_hook(fd);
	}
}

void nb_writex(struct child_struct *child, int handle, int offset, 
	       int size, int ret_size, const char *status)
{
	int i = find_handle(child, handle);
	void *buf;

	(void)status;

	buf = calloc(size, 1);

	if (pwrite(ftable[i].fd, buf, size, offset) != ret_size) {
		printf("write failed on handle %d\n", handle);
		exit(1);
	}

	free(buf);

	child->bytes += size;
}

void nb_readx(struct child_struct *child, int handle, int offset, 
	      int size, int ret_size, const char *status)
{
	int i = find_handle(child, handle);
	void *buf;

	(void)status;

	buf = malloc(size);

	if (pread(ftable[i].fd, buf, size, offset) != ret_size) {
		printf("read failed on handle %d\n", handle);
	}

	free(buf);

	child->bytes += size;
}

void nb_close(struct child_struct *child, int handle, const char *status)
{
	int i = find_handle(child, handle);
	(void)status;
	close(ftable[i].fd);
	ftable[i].handle = 0;
	if (ftable[i].name) free(ftable[i].name);
	ftable[i].name = NULL;
}

void nb_rename(struct child_struct *child, char *old, char *new, const char *status)
{
	resolve_name(old);
	resolve_name(new);

	if (rename(old, new) != expected_status(status)) {
		printf("rename %s %s failed (%s) - expected %s\n", 
		       old, new, strerror(errno), status);
		failed(child);
	}
	if (sync_dirs) sync_parent(new);
}

void nb_flush(struct child_struct *child, int handle, const char *status)
{
	(void)status;
	find_handle(child, handle);
	/* noop */
}

void nb_qpathinfo(struct child_struct *child, const char *fname, int level, 
		  const char *status)
{
	(void)child;
	(void)level;
	(void)status;
	resolve_name(fname);
}

void nb_qfileinfo(struct child_struct *child, int handle, int level, const char *status)
{
	struct stat st;
	int i = find_handle(child, handle);
	(void)child;
	(void)level;
	(void)status;
	fstat(ftable[i].fd, &st);
	xattr_fd_read_hook(ftable[i].fd);
}

void nb_qfsinfo(struct child_struct *child, int level, const char *status)
{
	struct statvfs st;

	(void)level;
	(void)status;

	statvfs(child->directory, &st);
}

void nb_findfirst(struct child_struct *child, char *fname, int level, int maxcnt, 
		  int count, const char *status)
{
	DIR *dir;
	struct dirent *d;
	char *p;

	(void)child;
	(void)level;
	(void)count;
	(void)status;

	resolve_name(fname);

	if (strpbrk(fname, "<>*?\"") == NULL) {
		return;
	}

	p = strrchr(fname, '/');
	if (!p) return;
	*p = 0;
	dir = opendir(fname);
	if (!dir) return;
	while (maxcnt && (d = readdir(dir))) maxcnt--;
	closedir(dir);
}

void nb_cleanup(struct child_struct *child)
{
	char *dname;

	asprintf(&dname, "%s/clients/client%d", child->directory, child->id);
	nb_deltree(child, dname);
	free(dname);

	asprintf(&dname, "%s%s", child->directory, "/clients");
	rmdir(dname);
	free(dname);
}

void nb_deltree(struct child_struct *child, char *dname)
{
	char *path;
	(void)child;
	asprintf(&path, "/bin/rm -rf %s", dname);
	system(path);
	free(path);
}

void nb_sfileinfo(struct child_struct *child, int handle, int level, const char *status)
{
	int i = find_handle(child, handle);
	struct utimbuf tm;
	struct stat st;
	(void)child;
	(void)handle;
	(void)level;
	(void)status;
	xattr_fd_read_hook(ftable[i].fd);

	fstat(ftable[i].fd, &st);

	tm.actime = st.st_atime - 10;
	tm.modtime = st.st_mtime - 12;

	utime(ftable[i].name, &tm);

	if (!S_ISDIR(st.st_mode)) {
		xattr_fd_write_hook(ftable[i].fd);
	}
}

void nb_lockx(struct child_struct *child, int handle, uint32_t offset, int size, 
	      const char *status)
{
	int i = find_handle(child, handle);
	struct flock lock;

	(void)child;
	(void)status;

	lock.l_type = F_WRLCK;
	lock.l_whence = SEEK_SET;
	lock.l_start = offset;
	lock.l_len = size;
	lock.l_pid = 0;

	fcntl(ftable[i].fd, F_SETLKW, &lock);
}

void nb_unlockx(struct child_struct *child,
		int handle, uint32_t offset, int size, const char *status)
{
	int i = find_handle(child, handle);
	struct flock lock;

	(void)child;
	(void)status;

	lock.l_type = F_UNLCK;
	lock.l_whence = SEEK_SET;
	lock.l_start = offset;
	lock.l_len = size;
	lock.l_pid = 0;

	fcntl(ftable[i].fd, F_SETLKW, &lock);
}

void nb_sleep(struct child_struct *child, int usec, const char *status)
{
	(void)child;
	(void)usec;
	(void)status;
	usleep(usec);
}


syntax highlighted by Code2HTML, v. 0.9.1