[dbench @ tridge@samba.org-20070717095043-tifyvmnopab19ztq]
[tridge/dbench.git] / fileio.c
index bc9a0ad6b7f9e679f77d9fe6caac8545d4af8bb4..0c830306092f9194328ec3d6c6f2099f82653c01 100644 (file)
--- a/fileio.c
+++ b/fileio.c
@@ -1,10 +1,12 @@
 /* 
-   dbench version 1
-   Copyright (C) Andrew Tridgell 1999
+   dbench version 4
+
+   Copyright (C) 1999-2007 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
+   the Free Software Foundation; either version 3 of the License, or
    (at your option) any later version.
    
    This program is distributed in the hope that it will be useful,
    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.
+   along with this program; if not, see <http://www.gnu.org/licenses/>.
 */
 
 #include "dbench.h"
 
-#define MAX_FILES 1000
-
-static char buf[70000];
-extern int line_count;
-
-char *server = NULL;
-extern int sync_ops;
+#define MAX_FILES 200
 
-static struct {
+struct ftable {
+       char *name;
        int fd;
        int handle;
-} ftable[MAX_FILES];
+};
 
-void nb_setup(int client)
+static int find_handle(struct child_struct *child, int handle)
 {
-       /* nothing to do */
+       struct ftable *ftable = child->private;
+       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);
 }
 
-void nb_unlink(char *fname)
+
+/* 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(const char *fname)
 {
-       strupper(fname);
+       char *copy_name;
+       int dir_fd;
+       char *slash;
 
-       if (unlink(fname) != 0) {
-               printf("(%d) unlink %s failed (%s)\n", 
-                      line_count, fname, strerror(errno));
+       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);
 }
 
-void expand_file(int fd, int size)
+static void xattr_fd_read_hook(int fd)
 {
-       int s;
-       while (size) {
-               s = MIN(sizeof(buf), size);
-               write(fd, buf, s);
-               size -= s;
+#if HAVE_EA_SUPPORT
+       char buf[44];
+       if (options.ea_enable) {
+               memset(buf, 0, sizeof(buf));
+               sys_fgetxattr(fd, "user.DosAttrib", buf, sizeof(buf));
        }
+#else
+       (void)fd;
+#endif
 }
 
-void nb_open(char *fname, int handle, int size)
+static void xattr_fname_read_hook(const char *fname)
+{
+#if HAVE_EA_SUPPORT
+       if (options.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
+       if (options.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;
+       }
+       if (strncmp(status, "0x", 2) == 0 &&
+           strtoul(status, NULL, 16) == 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 (name == NULL) return;
+
+       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)
+{
+       struct ftable *ftable;
+       ftable = calloc(MAX_FILES, sizeof(struct ftable));
+       child->private = ftable;
+       child->rate.last_time = timeval_current();
+       child->rate.last_bytes = 0;
+}
+
+void nb_unlink(struct child_struct *child, const 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 (options.sync_dirs) sync_parent(fname);
+}
+
+void nb_mkdir(struct child_struct *child, const char *dname, const char *status)
+{
+       (void)child;
+       (void)status;
+       resolve_name(dname);
+       mkdir(dname, 0777);
+}
+
+void nb_rmdir(struct child_struct *child, const 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 (options.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|O_CREAT;
+       int flags = O_RDWR;
        struct stat st;
-       static int count;
+       struct ftable *ftable = (struct ftable *)child->private;
 
-       strupper(fname);
+       resolve_name(fname);
 
-       if (size == 0) flags |= O_TRUNC;
+       if (options.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;
 
-       if (sync_ops)
-               flags |= O_SYNC;
-       
        fd = open(fname, flags, 0600);
+       if (fd == -1 && errno == EISDIR) {
+               flags = O_RDONLY|O_DIRECTORY;
+               fd = open(fname, flags, 0600);
+       }
        if (fd == -1) {
-               printf("(%d) open %s failed for handle %d (%s)\n", 
-                      line_count, fname, handle, strerror(errno));
+               if (expected_status(status) == 0) {
+                       printf("(%d) open %s failed for handle %d (%s)\n", 
+                              child->line, fname, fnum, strerror(errno));
+               }
                return;
        }
-       fstat(fd, &st);
-       if (size > st.st_size) {
-#if DEBUG
-               printf("(%d) expanding %s to %d from %d\n", 
-                      line_count, fname, size, (int)st.st_size);
-#endif
-               expand_file(fd, size - st.st_size);
-       } else if (size < st.st_size) {
-               printf("truncating %s to %d from %d\n", 
-                      fname, size, (int)st.st_size);
-               ftruncate(fd, size);
+       if (expected_status(status) != 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;
        }
@@ -96,120 +285,285 @@ void nb_open(char *fname, int handle, int size)
                printf("file table full for %s\n", fname);
                exit(1);
        }
-       ftable[i].handle = handle;
+       ftable[i].name = strdup(fname);
+       ftable[i].handle = fnum;
        ftable[i].fd = fd;
-       if (count++ % 100 == 0) {
-               printf(".");
+
+       fstat(fd, &st);
+
+       if (!S_ISDIR(st.st_mode)) {
+               xattr_fd_write_hook(fd);
        }
 }
 
-void nb_write(int handle, int size, int offset)
+void nb_writex(struct child_struct *child, int handle, int offset, 
+              int size, int ret_size, const char *status)
 {
-       int i;
+       int i = find_handle(child, handle);
+       void *buf;
+       struct stat st;
+       struct ftable *ftable = (struct ftable *)child->private;
 
-       if (buf[0] == 0) memset(buf, 1, sizeof(buf));
+       (void)status;
 
-       for (i=0;i<MAX_FILES;i++) {
-               if (ftable[i].handle == handle) break;
-       }
-       if (i == MAX_FILES) {
-#if 1
-               printf("(%d) nb_write: handle %d was not open size=%d ofs=%d\n", 
-                      line_count, handle, size, offset);
-#endif
-               return;
+       buf = calloc(size, 1);
+
+       if (size == 1 && fstat(ftable[i].fd, &st) == 0) {
+               if (st.st_size > offset) {
+                       unsigned char c;
+                       pread(ftable[i].fd, &c, 1, offset);
+                       if (c == ((unsigned char *)buf)[0]) {
+                               free(buf);
+                               child->bytes += size;
+                               return;
+                       }
+               } else if (((unsigned char *)buf)[0] == 0) {
+                       ftruncate(ftable[i].fd, offset+1);
+                       free(buf);
+                       child->bytes += size;
+                       return;
+               } 
        }
-       lseek(ftable[i].fd, offset, SEEK_SET);
-       if (write(ftable[i].fd, buf, size) != size) {
-               printf("write failed on handle %d\n", handle);
+
+       if (pwrite(ftable[i].fd, buf, size, offset) != ret_size) {
+               printf("write failed on handle %d (%s)\n", handle, strerror(errno));
+               exit(1);
        }
+
+       if (options.do_fsync) fsync(ftable[i].fd);
+
+       free(buf);
+
+       child->bytes += size;
+       child->bytes_since_fsync += size;
 }
 
-void nb_read(int handle, int size, int offset)
+void nb_readx(struct child_struct *child, int handle, int offset, 
+             int size, int ret_size, const char *status)
 {
-       int i;
-       for (i=0;i<MAX_FILES;i++) {
-               if (ftable[i].handle == handle) break;
-       }
-       if (i == MAX_FILES) {
-               printf("(%d) nb_read: handle %d was not open size=%d ofs=%d\n", 
-                      line_count, handle, size, offset);
-               return;
+       int i = find_handle(child, handle);
+       void *buf;
+       struct ftable *ftable = (struct ftable *)child->private;
+
+       (void)status;
+
+       buf = malloc(size);
+
+       if (pread(ftable[i].fd, buf, size, offset) != ret_size) {
+               printf("read failed on handle %d (%s)\n", handle, strerror(errno));
        }
-       lseek(ftable[i].fd, offset, SEEK_SET);
-       read(ftable[i].fd, buf, size);
+
+       free(buf);
+
+       child->bytes += size;
 }
 
-void nb_close(int handle)
+void nb_close(struct child_struct *child, int handle, const char *status)
 {
-       int i;
-       for (i=0;i<MAX_FILES;i++) {
-               if (ftable[i].handle == handle) break;
-       }
-       if (i == MAX_FILES) {
-               printf("(%d) nb_close: handle %d was not open\n", 
-                      line_count, handle);
-               return;
-       }
+       struct ftable *ftable = (struct ftable *)child->private;
+       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_mkdir(char *fname)
+void nb_rename(struct child_struct *child, const char *old, const char *new, const char *status)
 {
-       strupper(fname);
+       resolve_name(old);
+       resolve_name(new);
 
-       if (mkdir(fname, 0700) != 0) {
-#if DEBUG
-               printf("mkdir %s failed (%s)\n", 
-                      fname, strerror(errno));
-#endif
+       if (rename(old, new) != expected_status(status)) {
+               printf("rename %s %s failed (%s) - expected %s\n", 
+                      old, new, strerror(errno), status);
+               failed(child);
        }
+       if (options.sync_dirs) sync_parent(new);
 }
 
-void nb_rmdir(char *fname)
+void nb_flush(struct child_struct *child, int handle, const char *status)
 {
-       strupper(fname);
+       struct ftable *ftable = (struct ftable *)child->private;
+       int i = find_handle(child, handle);
+       (void)status;
+       fsync(ftable[i].fd);
+}
 
-       if (rmdir(fname) != 0) {
-               printf("rmdir %s failed (%s)\n", 
-                      fname, strerror(errno));
-       }
+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_rename(char *old, char *new)
+void nb_qfileinfo(struct child_struct *child, int handle, int level, const char *status)
 {
-       strupper(old);
-       strupper(new);
+       struct ftable *ftable = (struct ftable *)child->private;
+       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);
+}
 
-       if (rename(old, new) != 0) {
-               printf("rename %s %s failed (%s)\n", 
-                      old, new, strerror(errno));
+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, const 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, const char *dname)
+{
+       DIR *d;
+       struct dirent *de;
+       (void)child;
+       
+       d = opendir(dname);
+       if (d == NULL) return;
+
+       for (de=readdir(d);de;de=readdir(d)) {
+               struct stat st;
+               char *fname = NULL;
+               if (strcmp(de->d_name, ".") == 0 ||
+                   strcmp(de->d_name, "..") == 0) {
+                       continue;
+               }
+               asprintf(&fname, "%s/%s", dname, de->d_name);
+               if (fname == NULL) {
+                       printf("Out of memory\n");
+                       exit(1);
+               }
+               if (stat(fname, &st) != 0) {
+                       continue;
+               }
+               if (S_ISDIR(st.st_mode)) {
+                       nb_deltree(child, fname);
+               } else {
+                       if (unlink(fname) != 0) {
+                               printf("[%d] unlink '%s' failed - %s\n",
+                                      child->line, fname, strerror(errno));
+                       }
+               }
+               free(fname);
+       }
+       closedir(d);
+}
 
-void nb_stat(char *fname, int size)
+void nb_sfileinfo(struct child_struct *child, int handle, int level, const char *status)
 {
+       struct ftable *ftable = (struct ftable *)child->private;
+       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);
 
-       strupper(fname);
+       fstat(ftable[i].fd, &st);
 
-       if (stat(fname, &st) != 0) {
-               printf("(%d) nb_stat: %s size=%d %s\n", 
-                      line_count, fname, size, strerror(errno));
-               return;
-       }
-       if (S_ISDIR(st.st_mode)) return;
+       tm.actime = st.st_atime - 10;
+       tm.modtime = st.st_mtime - 12;
 
-       if (st.st_size != size) {
-               printf("(%d) nb_stat: %s wrong size %d %d\n", 
-                      line_count, fname, (int)st.st_size, size);
+       utime(ftable[i].name, &tm);
+
+       if (!S_ISDIR(st.st_mode)) {
+               xattr_fd_write_hook(ftable[i].fd);
        }
 }
 
-void nb_create(char *fname, int size)
+void nb_lockx(struct child_struct *child, int handle, uint32_t offset, int size, 
+             const char *status)
+{
+       struct ftable *ftable = (struct ftable *)child->private;
+       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)
+{
+       struct ftable *ftable = (struct ftable *)child->private;
+       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)
 {
-       nb_open(fname, 5000, size);
-       nb_close(5000);
+       (void)child;
+       (void)usec;
+       (void)status;
+       usleep(usec);
 }