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