/* * Copyright (c) International Business Machines Corp., 2001-2004 * * 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include #include #include #include #include #include #include #include #include "rand.h" #include "filelist.h" #include "util.h" #include "rwlock.h" #include "rbt.h" #include "cirlist.h" #if 0 static void print_cl(struct cirlist *cl) { struct cnode *cur = cl->head; printf("curlist: "); if (cur == NULL) { printf("\n"); return; } do { printf("%d ", cur->obj->num); cur = cur->next; } while (cur != cl->head); printf("\n"); } #endif #if 0 static int node_cmp(struct ffsb_file *a, struct ffsb_file *b) { return a->num - b->num; } #endif static void build_dirs(struct benchfiles *bf) { char buf[FILENAME_MAX]; int i; if (mkdir(bf->basedir, S_IRWXU) < 0) if (errno != EEXIST) { perror(bf->basedir); exit(1); } for (i = 0; i < bf->numsubdirs; i++) { snprintf(buf, FILENAME_MAX, "%s/%s%s%d", bf->basedir, bf->basename, SUBDIRNAME_BASE, i); if (mkdir(buf, S_IRWXU) == -1) if (errno != EEXIST) { perror(buf); exit(1); } } } void init_filelist(struct benchfiles *b, char *basedir, char *basename, uint32_t numsubdirs, int builddirs) { memset(b, 0, sizeof(struct benchfiles)); b->basedir = ffsb_strdup(basedir); b->basename = ffsb_strdup(basename); b->numsubdirs = numsubdirs; init_rwlock(&b->fileslock); b->files = rbtree_construct(); b->dirs = rbtree_construct(); b->holes = ffsb_malloc(sizeof(struct cirlist)); b->dholes = ffsb_malloc(sizeof(struct cirlist)); init_cirlist(b->holes); init_cirlist(b->dholes); if (builddirs) build_dirs(b); } static void file_destructor(struct ffsb_file *file) { free(file->name); free(file); } void destroy_filelist(struct benchfiles *bf) { free(bf->basedir); free(bf->basename); while (!cl_empty(bf->holes)) { struct ffsb_file *cur = cl_remove_head(bf->holes); file_destructor(cur); } free(bf->holes); rbtree_clean(bf->files, file_destructor); free(bf->files); } struct ffsb_file *add_file(struct benchfiles *b, uint64_t size, randdata_t * rd) { struct ffsb_file *newfile, *oldfile = NULL; int filenum = 0; /* We pre-allocate here, because I don't want to spend time * malloc'ing while the list is locked we free it later if * necessary */ newfile = ffsb_malloc(sizeof(struct ffsb_file)); newfile->size = size; init_rwlock(&(newfile->lock)); /* Write lock the filelist, begin critical section */ rw_lock_write(&b->fileslock); /* First check "holes" for a file */ if (!cl_empty(b->holes)) { oldfile = cl_remove_head(b->holes); rbtree_insert(b->files, oldfile); rw_lock_write(&oldfile->lock); } else { filenum = b->listsize; b->listsize++; newfile->num = filenum; rbtree_insert(b->files, newfile); rw_lock_write(&newfile->lock); } /* unlock filelist */ rw_unlock_write(&b->fileslock); if (oldfile == NULL) { char buf[FILENAME_MAX]; int randdir = getrandom(rd, b->numsubdirs + 1); int namesize = 0; if (randdir == 0) namesize = snprintf(buf, FILENAME_MAX, "%s/%s%s%d", b->basedir, b->basename, FILENAME_BASE, filenum); else namesize = snprintf(buf, FILENAME_MAX, "%s/%s%s%d/%s%s%d", b->basedir, b->basename, SUBDIRNAME_BASE, randdir - 1, b->basename, FILENAME_BASE, filenum); if (namesize >= FILENAME_MAX) /* !!! do something about this ? */ printf("warning: filename \"%s\" too long\n", buf); newfile->name = ffsb_strdup(buf); return newfile; } else { free(newfile); return oldfile; } } struct ffsb_file *add_dir(struct benchfiles *b, uint64_t size, randdata_t * rd) { struct ffsb_file *newdir, *olddir = NULL; int dirnum = 0; newdir = ffsb_malloc(sizeof(struct ffsb_file)); init_rwlock(&newdir->lock); /* write lock the filelist, beging critical section */ rw_lock_write(&b->fileslock); /* First check "holes" for a file */ if (!cl_empty(b->dholes)) { olddir = cl_remove_head(b->dholes); rbtree_insert(b->files, olddir); rw_lock_write(&olddir->lock); } else { dirnum = b->numsubdirs; b->numsubdirs++; printf("dirnum: %d\n", dirnum); newdir->num = dirnum; rbtree_insert(b->dirs, newdir); rw_lock_write(&newdir->lock); } /* unlock filelist */ rw_unlock_write(&b->fileslock); if (olddir == NULL) { char buf[FILENAME_MAX]; int namesize = 0; namesize = snprintf(buf, FILENAME_MAX, "%s/%s%s%d", b->basedir, b->basename, SUBDIRNAME_BASE, dirnum); if (namesize >= FILENAME_MAX) printf("warning: filename \"%s\" too long\n", buf); /* TODO: take action here... */ newdir->name = ffsb_strdup(buf); return newdir; } else { free(newdir); return olddir; } } /* Private version of above function used only for reusing a * fileset. */ static struct ffsb_file *add_file_named(struct benchfiles *b, uint64_t size, char *name) { struct ffsb_file *newfile = NULL; newfile = ffsb_malloc(sizeof(struct ffsb_file)); memset(newfile, 0, sizeof(struct ffsb_file)); newfile->name = ffsb_strdup(name); newfile->size = size; init_rwlock(&newfile->lock); /* Write lock the filelist, begin critical section */ rw_lock_write(&b->fileslock); newfile->num = b->listsize; b->listsize++; /* Add a new file to the rbtree */ rbtree_insert(b->files, newfile); rw_lock_write(&newfile->lock); /* Unlock filelist */ rw_unlock_write(&b->fileslock); return newfile; } #if 0 static void print_rb_helper(rb_node * cur) { if (cur != NULL) { print_rb_helper(cur->left); printf("%d ", cur->object->num); print_rb_helper(cur->right); } } static void print_rb(rb_tree * tree) { print_rb_helper(tree->root); } #endif void remove_file(struct benchfiles *b, struct ffsb_file *entry) { rw_lock_write(&b->fileslock); rbtree_remove(b->files, entry, NULL); /* add node to the cir. list of "holes" */ cl_insert_tail(b->holes, entry); rw_unlock_write(&b->fileslock); } static struct ffsb_file *choose_file(struct benchfiles *b, randdata_t * rd) { rb_node *cur = NULL; int chosen = 0; struct ffsb_file temp; temp.num = chosen; if (b->listsize == 0) { fprintf(stderr, "No more files to operate on," " try making more initial files " "or fewer delete operations\n"); exit(0); } while (cur == NULL) { chosen = getrandom(rd, b->listsize); temp.num = chosen; cur = rbtree_find(b->files, &temp); } return cur->object; } struct ffsb_file *choose_file_reader(struct benchfiles *bf, randdata_t * rd) { struct ffsb_file *ret; rw_lock_read(&bf->fileslock); /* If b->holes->count == bf->listsize, all files have been * deleted! */ assert(bf->holes->count != bf->listsize); ret = choose_file(bf, rd); if (rw_trylock_read(&ret->lock)) { rw_unlock_read(&bf->fileslock); return choose_file_reader(bf, rd); } rw_unlock_read(&bf->fileslock); return ret; } struct ffsb_file *choose_file_writer(struct benchfiles *bf, randdata_t * rd) { struct ffsb_file *ret; rw_lock_read(&bf->fileslock); assert(bf->holes->count != bf->listsize); ret = choose_file(bf, rd); if (rw_trylock_write(&ret->lock)) { rw_unlock_read(&bf->fileslock); return choose_file_writer(bf, rd); } rw_unlock_read(&bf->fileslock); return ret; } void unlock_file_reader(struct ffsb_file *file) { rw_unlock_read(&file->lock); } void unlock_file_writer(struct ffsb_file *file) { rw_unlock_write(&file->lock); } void rename_file(struct ffsb_file *file) { char *newname = malloc(strlen(file->name) + 2); sprintf(newname, "%sa", file->name); file->name = newname; } int validate_filename(struct benchfiles *bf, char *name) { int retval = -1; char fmt_str[FILENAME_MAX]; if (FILENAME_MAX <= snprintf(fmt_str, FILENAME_MAX, "%s%s%%d", bf->basename, FILENAME_BASE)) { printf("filename is too long declaring it invalid\n"); return -1; } sscanf(name, fmt_str, &retval); return retval; } int validate_dirname(struct benchfiles *bf, char *name) { int retval = -1; char fmt_str[FILENAME_MAX]; if (FILENAME_MAX <= snprintf(fmt_str, FILENAME_MAX, "%s%s%%d", bf->basename, SUBDIRNAME_BASE)) { printf("dirname is too long declaring it invalid\n"); return -1; } sscanf(name, fmt_str, &retval); return retval; } /* Do all the dirty work of recursing through a directory structure * check everything for validitiy and update everything properly. * Note it does not check filesizes !!!, it doesn't know anything * about them */ static int add_dir_to_filelist(struct benchfiles *bf, DIR * subdir, char *subdir_path, fl_validation_func_t vfunc, void *vf_data) { int retval = 0; struct dirent *d_ent = NULL; while ((d_ent = readdir(subdir)) != NULL) { DIR *tmp = NULL; char filename_buf[FILENAME_MAX * 2]; if (FILENAME_MAX < snprintf(filename_buf, FILENAME_MAX, "%s/%s", subdir_path, d_ent->d_name)) { printf("filename \"%s\" too long aborting\n", filename_buf); return -1; } tmp = opendir(filename_buf); if (tmp == NULL) { struct ffsb_file *ffsb_file = NULL; if (validate_filename(bf, d_ent->d_name) < 0) { printf("filename \"%s\" is invalid aborting\n", d_ent->d_name); return -1; } /* Verify size/other attributes via callback */ if (vfunc(bf, filename_buf, vf_data)) { printf("filename \"%s\" didn't pass " "validation\n", d_ent->d_name); return -1; } /* Add file to data structure */ ffsb_file = add_file_named(bf, ffsb_get_filesize(filename_buf), filename_buf); unlock_file_writer(ffsb_file); } else { /* Check for the usual suspects and skip them */ if ((0 == strcmp(".", d_ent->d_name)) || (0 == strcmp("..", d_ent->d_name))) { closedir(tmp); continue; } if (validate_dirname(bf, d_ent->d_name) < 0) { printf("dirname \"%s\" is invalid aborting\n", d_ent->d_name); closedir(tmp); return -1; } if (vfunc(bf, filename_buf, vf_data)) { printf("dir \"%s\" didn't pass validation\n", d_ent->d_name); closedir(tmp); return -1; } /* Update filelist */ bf->numsubdirs++; /* recurse */ retval += add_dir_to_filelist(bf, tmp, filename_buf, vfunc, vf_data); /* clean up */ closedir(tmp); } } return retval; } int grab_old_fileset(struct benchfiles *bf, char *basename, fl_validation_func_t vfunc, void *vfunc_data) { int retval = 0; char buf[FILENAME_MAX * 2]; DIR *lc_dir = NULL; if (FILENAME_MAX < snprintf(buf, FILENAME_MAX, "%s", bf->basedir)) { printf("filename \"%s\" is too long aborting\n", buf); return -1; } lc_dir = opendir(buf); if (lc_dir == NULL) { perror("opendir"); return -1; } retval = add_dir_to_filelist(bf, lc_dir, buf, vfunc, vfunc_data); closedir(lc_dir); return retval; } /* Get the number of files */ uint32_t get_listsize(struct benchfiles * bf) { return bf->listsize; } /* Get the number of subdirectories */ uint32_t get_numsubdirs(struct benchfiles * bf) { return bf->numsubdirs; }