Home | History | Annotate | Download | only in ffsb-6.0-rc2
      1 /*
      2  *   Copyright (c) International Business Machines Corp., 2001-2004
      3  *
      4  *   This program is free software;  you can redistribute it and/or modify
      5  *   it under the terms of the GNU General Public License as published by
      6  *   the Free Software Foundation; either version 2 of the License, or
      7  *   (at your option) any later version.
      8  *
      9  *   This program is distributed in the hope that it will be useful,
     10  *   but WITHOUT ANY WARRANTY;  without even the implied warranty of
     11  *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See
     12  *   the GNU General Public License for more details.
     13  *
     14  *   You should have received a copy of the GNU General Public License
     15  *   along with this program;  if not, write to the Free Software
     16  *   Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
     17  */
     18 #include <limits.h>
     19 #include <sys/stat.h>
     20 #include <sys/types.h>
     21 #include <dirent.h>
     22 #include <string.h>
     23 #include <stdio.h>
     24 #include <stdlib.h>
     25 #include <errno.h>
     26 
     27 #include "rand.h"
     28 #include "filelist.h"
     29 #include "util.h"
     30 #include "rwlock.h"
     31 #include "rbt.h"
     32 #include "cirlist.h"
     33 
     34 #if 0
     35 static
     36 void print_cl(struct cirlist *cl)
     37 {
     38 	struct cnode *cur = cl->head;
     39 	printf("curlist: ");
     40 	if (cur == NULL) {
     41 		printf("\n");
     42 		return;
     43 	}
     44 	do {
     45 		printf("%d ", cur->obj->num);
     46 		cur = cur->next;
     47 	} while (cur != cl->head);
     48 	printf("\n");
     49 }
     50 #endif
     51 
     52 #if 0
     53 static
     54 int node_cmp(struct ffsb_file *a, struct ffsb_file *b)
     55 {
     56 	return a->num - b->num;
     57 }
     58 #endif
     59 
     60 static
     61 void build_dirs(struct benchfiles *bf)
     62 {
     63 	char buf[FILENAME_MAX];
     64 	int i;
     65 
     66 	if (mkdir(bf->basedir, S_IRWXU) < 0)
     67 		if (errno != EEXIST) {
     68 			perror(bf->basedir);
     69 			exit(1);
     70 		}
     71 	for (i = 0; i < bf->numsubdirs; i++) {
     72 		snprintf(buf, FILENAME_MAX, "%s/%s%s%d",
     73 			 bf->basedir, bf->basename, SUBDIRNAME_BASE, i);
     74 		if (mkdir(buf, S_IRWXU) == -1)
     75 			if (errno != EEXIST) {
     76 				perror(buf);
     77 				exit(1);
     78 			}
     79 	}
     80 }
     81 
     82 void init_filelist(struct benchfiles *b, char *basedir, char *basename,
     83 		   uint32_t numsubdirs, int builddirs)
     84 {
     85 	memset(b, 0, sizeof(struct benchfiles));
     86 	b->basedir = ffsb_strdup(basedir);
     87 	b->basename = ffsb_strdup(basename);
     88 	b->numsubdirs = numsubdirs;
     89 	init_rwlock(&b->fileslock);
     90 	b->files = rbtree_construct();
     91 	b->dirs = rbtree_construct();
     92 	b->holes = ffsb_malloc(sizeof(struct cirlist));
     93 	b->dholes = ffsb_malloc(sizeof(struct cirlist));
     94 	init_cirlist(b->holes);
     95 	init_cirlist(b->dholes);
     96 
     97 	if (builddirs)
     98 		build_dirs(b);
     99 }
    100 
    101 static void file_destructor(struct ffsb_file *file)
    102 {
    103 	free(file->name);
    104 	free(file);
    105 }
    106 
    107 void destroy_filelist(struct benchfiles *bf)
    108 {
    109 	free(bf->basedir);
    110 	free(bf->basename);
    111 
    112 	while (!cl_empty(bf->holes)) {
    113 		struct ffsb_file *cur = cl_remove_head(bf->holes);
    114 		file_destructor(cur);
    115 	}
    116 	free(bf->holes);
    117 	rbtree_clean(bf->files, file_destructor);
    118 	free(bf->files);
    119 }
    120 
    121 struct ffsb_file *add_file(struct benchfiles *b, uint64_t size, randdata_t * rd)
    122 {
    123 	struct ffsb_file *newfile, *oldfile = NULL;
    124 	int filenum = 0;
    125 
    126 	/* We pre-allocate here, because I don't want to spend time
    127 	 * malloc'ing while the list is locked we free it later if
    128 	 * necessary
    129 	 */
    130 	newfile = ffsb_malloc(sizeof(struct ffsb_file));
    131 
    132 	newfile->size = size;
    133 	init_rwlock(&(newfile->lock));
    134 
    135 	/* Write lock the filelist, begin critical section */
    136 	rw_lock_write(&b->fileslock);
    137 
    138 	/* First check "holes" for a file  */
    139 	if (!cl_empty(b->holes)) {
    140 		oldfile = cl_remove_head(b->holes);
    141 		rbtree_insert(b->files, oldfile);
    142 		rw_lock_write(&oldfile->lock);
    143 	} else {
    144 		filenum = b->listsize;
    145 		b->listsize++;
    146 
    147 		newfile->num = filenum;
    148 		rbtree_insert(b->files, newfile);
    149 
    150 		rw_lock_write(&newfile->lock);
    151 	}
    152 
    153 	/* unlock filelist */
    154 	rw_unlock_write(&b->fileslock);
    155 
    156 	if (oldfile == NULL) {
    157 		char buf[FILENAME_MAX];
    158 		int randdir = getrandom(rd, b->numsubdirs + 1);
    159 		int namesize = 0;
    160 		if (randdir == 0)
    161 			namesize = snprintf(buf, FILENAME_MAX, "%s/%s%s%d",
    162 					    b->basedir, b->basename,
    163 					    FILENAME_BASE, filenum);
    164 		else
    165 			namesize = snprintf(buf, FILENAME_MAX,
    166 					    "%s/%s%s%d/%s%s%d", b->basedir,
    167 					    b->basename, SUBDIRNAME_BASE,
    168 					    randdir - 1, b->basename,
    169 					    FILENAME_BASE, filenum);
    170 		if (namesize >= FILENAME_MAX)
    171 			/* !!! do something about this ? */
    172 			printf("warning: filename \"%s\" too long\n", buf);
    173 		newfile->name = ffsb_strdup(buf);
    174 		return newfile;
    175 	} else {
    176 		free(newfile);
    177 		return oldfile;
    178 	}
    179 }
    180 
    181 struct ffsb_file *add_dir(struct benchfiles *b, uint64_t size, randdata_t * rd)
    182 {
    183 	struct ffsb_file *newdir, *olddir = NULL;
    184 	int dirnum = 0;
    185 
    186 	newdir = ffsb_malloc(sizeof(struct ffsb_file));
    187 
    188 	init_rwlock(&newdir->lock);
    189 
    190 	/* write lock the filelist, beging critical section */
    191 	rw_lock_write(&b->fileslock);
    192 
    193 	/* First check "holes" for a file  */
    194 	if (!cl_empty(b->dholes)) {
    195 		olddir = cl_remove_head(b->dholes);
    196 		rbtree_insert(b->files, olddir);
    197 		rw_lock_write(&olddir->lock);
    198 	} else {
    199 		dirnum = b->numsubdirs;
    200 		b->numsubdirs++;
    201 		printf("dirnum: %d\n", dirnum);
    202 		newdir->num = dirnum;
    203 		rbtree_insert(b->dirs, newdir);
    204 
    205 		rw_lock_write(&newdir->lock);
    206 	}
    207 
    208 	/* unlock filelist */
    209 	rw_unlock_write(&b->fileslock);
    210 
    211 	if (olddir == NULL) {
    212 		char buf[FILENAME_MAX];
    213 		int namesize = 0;
    214 		namesize = snprintf(buf, FILENAME_MAX, "%s/%s%s%d",
    215 				    b->basedir, b->basename,
    216 				    SUBDIRNAME_BASE, dirnum);
    217 		if (namesize >= FILENAME_MAX)
    218 			printf("warning: filename \"%s\" too long\n", buf);
    219 		/* TODO: take action here... */
    220 		newdir->name = ffsb_strdup(buf);
    221 		return newdir;
    222 	} else {
    223 		free(newdir);
    224 		return olddir;
    225 	}
    226 }
    227 
    228 /* Private version of above function used only for reusing a
    229  * fileset.
    230  */
    231 static struct ffsb_file *add_file_named(struct benchfiles *b, uint64_t size,
    232 					char *name)
    233 {
    234 	struct ffsb_file *newfile = NULL;
    235 
    236 	newfile = ffsb_malloc(sizeof(struct ffsb_file));
    237 	memset(newfile, 0, sizeof(struct ffsb_file));
    238 	newfile->name = ffsb_strdup(name);
    239 	newfile->size = size;
    240 	init_rwlock(&newfile->lock);
    241 
    242 	/* Write lock the filelist, begin critical section */
    243 	rw_lock_write(&b->fileslock);
    244 
    245 	newfile->num = b->listsize;
    246 	b->listsize++;
    247 
    248 	/* Add a new file to the rbtree */
    249 	rbtree_insert(b->files, newfile);
    250 
    251 	rw_lock_write(&newfile->lock);
    252 
    253 	/* Unlock filelist */
    254 	rw_unlock_write(&b->fileslock);
    255 
    256 	return newfile;
    257 }
    258 
    259 #if 0
    260 static void print_rb_helper(rb_node * cur)
    261 {
    262 	if (cur != NULL) {
    263 		print_rb_helper(cur->left);
    264 		printf("%d ", cur->object->num);
    265 		print_rb_helper(cur->right);
    266 	}
    267 }
    268 
    269 static void print_rb(rb_tree * tree)
    270 {
    271 	print_rb_helper(tree->root);
    272 }
    273 #endif
    274 
    275 void remove_file(struct benchfiles *b, struct ffsb_file *entry)
    276 {
    277 	rw_lock_write(&b->fileslock);
    278 
    279 	rbtree_remove(b->files, entry, NULL);
    280 	/* add node to the cir. list of "holes" */
    281 	cl_insert_tail(b->holes, entry);
    282 
    283 	rw_unlock_write(&b->fileslock);
    284 }
    285 
    286 static struct ffsb_file *choose_file(struct benchfiles *b, randdata_t * rd)
    287 {
    288 	rb_node *cur = NULL;
    289 	int chosen = 0;
    290 	struct ffsb_file temp;
    291 	temp.num = chosen;
    292 
    293 	if (b->listsize == 0) {
    294 		fprintf(stderr, "No more files to operate on,"
    295 			" try making more initial files "
    296 			"or fewer delete operations\n");
    297 		exit(0);
    298 	}
    299 
    300 	while (cur == NULL) {
    301 		chosen = getrandom(rd, b->listsize);
    302 		temp.num = chosen;
    303 		cur = rbtree_find(b->files, &temp);
    304 	}
    305 	return cur->object;
    306 }
    307 
    308 struct ffsb_file *choose_file_reader(struct benchfiles *bf, randdata_t * rd)
    309 {
    310 	struct ffsb_file *ret;
    311 
    312 	rw_lock_read(&bf->fileslock);
    313 	/* If b->holes->count == bf->listsize, all files have been
    314 	 * deleted!
    315 	 */
    316 	assert(bf->holes->count != bf->listsize);
    317 
    318 	ret = choose_file(bf, rd);
    319 	if (rw_trylock_read(&ret->lock)) {
    320 		rw_unlock_read(&bf->fileslock);
    321 		return choose_file_reader(bf, rd);
    322 	}
    323 
    324 	rw_unlock_read(&bf->fileslock);
    325 	return ret;
    326 }
    327 
    328 struct ffsb_file *choose_file_writer(struct benchfiles *bf, randdata_t * rd)
    329 {
    330 	struct ffsb_file *ret;
    331 
    332 	rw_lock_read(&bf->fileslock);
    333 	assert(bf->holes->count != bf->listsize);
    334 	ret = choose_file(bf, rd);
    335 
    336 	if (rw_trylock_write(&ret->lock)) {
    337 		rw_unlock_read(&bf->fileslock);
    338 		return choose_file_writer(bf, rd);
    339 	}
    340 
    341 	rw_unlock_read(&bf->fileslock);
    342 	return ret;
    343 }
    344 
    345 void unlock_file_reader(struct ffsb_file *file)
    346 {
    347 	rw_unlock_read(&file->lock);
    348 }
    349 
    350 void unlock_file_writer(struct ffsb_file *file)
    351 {
    352 	rw_unlock_write(&file->lock);
    353 }
    354 
    355 void rename_file(struct ffsb_file *file)
    356 {
    357 	char *newname = malloc(strlen(file->name) + 2);
    358 	sprintf(newname, "%sa", file->name);
    359 	file->name = newname;
    360 }
    361 
    362 int validate_filename(struct benchfiles *bf, char *name)
    363 {
    364 	int retval = -1;
    365 	char fmt_str[FILENAME_MAX];
    366 	if (FILENAME_MAX <= snprintf(fmt_str, FILENAME_MAX,
    367 				     "%s%s%%d", bf->basename, FILENAME_BASE)) {
    368 		printf("filename is too long declaring it invalid\n");
    369 		return -1;
    370 	}
    371 
    372 	sscanf(name, fmt_str, &retval);
    373 	return retval;
    374 }
    375 
    376 int validate_dirname(struct benchfiles *bf, char *name)
    377 {
    378 	int retval = -1;
    379 	char fmt_str[FILENAME_MAX];
    380 	if (FILENAME_MAX <= snprintf(fmt_str, FILENAME_MAX, "%s%s%%d",
    381 				     bf->basename, SUBDIRNAME_BASE)) {
    382 		printf("dirname is too long declaring it invalid\n");
    383 		return -1;
    384 	}
    385 
    386 	sscanf(name, fmt_str, &retval);
    387 	return retval;
    388 }
    389 
    390 /* Do all the dirty work of recursing through a directory structure
    391  * check everything for validitiy and update everything properly.
    392  * Note it does not check filesizes !!!, it doesn't know anything
    393  * about them
    394  */
    395 static int add_dir_to_filelist(struct benchfiles *bf, DIR * subdir,
    396 			       char *subdir_path, fl_validation_func_t vfunc,
    397 			       void *vf_data)
    398 {
    399 	int retval = 0;
    400 	struct dirent *d_ent = NULL;
    401 
    402 	while ((d_ent = readdir(subdir)) != NULL) {
    403 		DIR *tmp = NULL;
    404 		char filename_buf[FILENAME_MAX * 2];
    405 
    406 		if (FILENAME_MAX < snprintf(filename_buf, FILENAME_MAX, "%s/%s",
    407 					    subdir_path, d_ent->d_name)) {
    408 			printf("filename \"%s\" too long aborting\n",
    409 			       filename_buf);
    410 			return -1;
    411 		}
    412 		tmp = opendir(filename_buf);
    413 		if (tmp == NULL) {
    414 			struct ffsb_file *ffsb_file = NULL;
    415 
    416 			if (validate_filename(bf, d_ent->d_name) < 0) {
    417 				printf("filename \"%s\" is invalid aborting\n",
    418 				       d_ent->d_name);
    419 				return -1;
    420 			}
    421 			/* Verify size/other attributes via callback  */
    422 			if (vfunc(bf, filename_buf, vf_data)) {
    423 				printf("filename \"%s\" didn't pass "
    424 				       "validation\n", d_ent->d_name);
    425 				return -1;
    426 			}
    427 			/* Add file to data structure */
    428 			ffsb_file =
    429 			    add_file_named(bf, ffsb_get_filesize(filename_buf),
    430 					   filename_buf);
    431 			unlock_file_writer(ffsb_file);
    432 		} else {
    433 			/* Check for the usual suspects and skip them */
    434 			if ((0 == strcmp(".", d_ent->d_name)) ||
    435 			    (0 == strcmp("..", d_ent->d_name))) {
    436 				closedir(tmp);
    437 				continue;
    438 			}
    439 			if (validate_dirname(bf, d_ent->d_name) < 0) {
    440 				printf("dirname \"%s\" is invalid aborting\n",
    441 				       d_ent->d_name);
    442 				closedir(tmp);
    443 				return -1;
    444 			}
    445 			if (vfunc(bf, filename_buf, vf_data)) {
    446 				printf("dir \"%s\" didn't pass validation\n",
    447 				       d_ent->d_name);
    448 				closedir(tmp);
    449 				return -1;
    450 			}
    451 			/* Update filelist */
    452 			bf->numsubdirs++;
    453 
    454 			/* recurse */
    455 			retval += add_dir_to_filelist(bf, tmp, filename_buf,
    456 						      vfunc, vf_data);
    457 
    458 			/* clean up */
    459 			closedir(tmp);
    460 		}
    461 	}
    462 	return retval;
    463 }
    464 
    465 int grab_old_fileset(struct benchfiles *bf, char *basename,
    466 		     fl_validation_func_t vfunc, void *vfunc_data)
    467 {
    468 	int retval = 0;
    469 	char buf[FILENAME_MAX * 2];
    470 	DIR *lc_dir = NULL;
    471 
    472 	if (FILENAME_MAX < snprintf(buf, FILENAME_MAX, "%s", bf->basedir)) {
    473 		printf("filename \"%s\" is too long aborting\n", buf);
    474 		return -1;
    475 	}
    476 
    477 	lc_dir = opendir(buf);
    478 	if (lc_dir == NULL) {
    479 		perror("opendir");
    480 		return -1;
    481 	}
    482 
    483 	retval = add_dir_to_filelist(bf, lc_dir, buf, vfunc, vfunc_data);
    484 
    485 	closedir(lc_dir);
    486 	return retval;
    487 }
    488 
    489 /* Get the number of files */
    490 uint32_t get_listsize(struct benchfiles * bf)
    491 {
    492 	return bf->listsize;
    493 }
    494 
    495 /* Get the number of subdirectories */
    496 uint32_t get_numsubdirs(struct benchfiles * bf)
    497 {
    498 	return bf->numsubdirs;
    499 }
    500