• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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
build_dirs(struct benchfiles * bf)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 
init_filelist(struct benchfiles * b,char * basedir,char * basename,uint32_t numsubdirs,int builddirs)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 
file_destructor(struct ffsb_file * file)101 static void file_destructor(struct ffsb_file *file)
102 {
103 	free(file->name);
104 	free(file);
105 }
106 
destroy_filelist(struct benchfiles * bf)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 
add_file(struct benchfiles * b,uint64_t size,randdata_t * rd)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 
add_dir(struct benchfiles * b,uint64_t size,randdata_t * rd)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  */
add_file_named(struct benchfiles * b,uint64_t size,char * name)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 
remove_file(struct benchfiles * b,struct ffsb_file * entry)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 
choose_file(struct benchfiles * b,randdata_t * rd)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 
choose_file_reader(struct benchfiles * bf,randdata_t * rd)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 
choose_file_writer(struct benchfiles * bf,randdata_t * rd)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 
unlock_file_reader(struct ffsb_file * file)345 void unlock_file_reader(struct ffsb_file *file)
346 {
347 	rw_unlock_read(&file->lock);
348 }
349 
unlock_file_writer(struct ffsb_file * file)350 void unlock_file_writer(struct ffsb_file *file)
351 {
352 	rw_unlock_write(&file->lock);
353 }
354 
rename_file(struct ffsb_file * file)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 
validate_filename(struct benchfiles * bf,char * name)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 
validate_dirname(struct benchfiles * bf,char * name)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  */
add_dir_to_filelist(struct benchfiles * bf,DIR * subdir,char * subdir_path,fl_validation_func_t vfunc,void * vf_data)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 
grab_old_fileset(struct benchfiles * bf,char * basename,fl_validation_func_t vfunc,void * vfunc_data)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 */
get_listsize(struct benchfiles * bf)490 uint32_t get_listsize(struct benchfiles * bf)
491 {
492 	return bf->listsize;
493 }
494 
495 /* Get the number of subdirectories */
get_numsubdirs(struct benchfiles * bf)496 uint32_t get_numsubdirs(struct benchfiles * bf)
497 {
498 	return bf->numsubdirs;
499 }
500