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