1 #include "bonnie.h"
2 #include <fcntl.h>
3 #include <dirent.h>
4 #include <unistd.h>
5 #include <string.h>
6 #include <stdlib.h>
7
8 #include "bon_file.h"
9 #include "bon_time.h"
10 #include "duration.h"
11
12 CPCCHAR rand_chars = "abcdefghijklmnopqrstuvwxyz0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ";
13
COpenTest(int chunk_size,bool use_sync,bool * doExit)14 COpenTest::COpenTest(int chunk_size, bool use_sync, bool *doExit)
15 : m_chunk_size(chunk_size)
16 , m_number(0)
17 , m_number_directories(1)
18 , m_max(0)
19 , m_min(0)
20 , m_size_range(0)
21 , m_dirname(NULL)
22 , m_file_name_buf(NULL)
23 , m_file_names(NULL)
24 , m_sync(use_sync)
25 , m_directoryHandles(NULL)
26 , m_dirIndex(NULL)
27 , m_buf(new char[m_chunk_size])
28 , m_exit(doExit)
29 , m_sync_dir(true)
30 {
31 }
32
random_sort(Rand & r)33 void COpenTest::random_sort(Rand &r)
34 {
35 for(int i = 0; i < m_number; i++)
36 {
37 char *tmp = m_file_names[i];
38 int newind = r.getNum() % m_number;
39 m_file_names[i] = m_file_names[newind];
40 m_file_names[newind] = tmp;
41 if(m_dirIndex)
42 {
43 int tmpInd = m_dirIndex[i];
44 m_dirIndex[i] = m_dirIndex[newind];
45 m_dirIndex[newind] = tmpInd;
46 }
47 if(*m_exit) return;
48 }
49 }
50
~COpenTest()51 COpenTest::~COpenTest()
52 {
53 int i;
54 if(m_dirname)
55 {
56 fprintf(stderr, "Cleaning up test directory after error.\n");
57 if(m_file_names)
58 {
59 for(i = 0; i < m_number; i++)
60 unlink(m_file_names[i]);
61 }
62 if(m_number_directories > 1)
63 {
64 char buf[6];
65 for(i = 0; i < m_number_directories; i++)
66 {
67 sprintf(buf, "%05d", i);
68 if(rmdir(buf))
69 io_error("rmdir");
70 }
71 }
72 if(chdir("..") || rmdir(m_dirname))
73 io_error("rmdir");
74 delete m_dirname;
75 }
76 if(m_directoryHandles)
77 {
78 for(i = 0; i < m_number_directories; i++)
79 close(m_directoryHandles[i]);
80 delete m_directoryHandles;
81 }
82 delete m_file_name_buf;
83 delete m_file_names;
84 delete m_dirIndex;
85 delete m_buf;
86 }
87
make_names(Rand & r,bool do_random)88 void COpenTest::make_names(Rand &r, bool do_random)
89 {
90 delete m_file_name_buf;
91 delete m_file_names;
92 int names_per_directory = m_number / m_number_directories;
93 int names_in_dir = 0;
94 int directory_num = 0;
95 if(!m_dirIndex && m_sync)
96 m_dirIndex = new int[m_number];
97 if(m_number_directories == 1)
98 {
99 m_file_name_buf = new char[(MaxNameLen + 1) * m_number];
100 }
101 else
102 {
103 m_file_name_buf = new char[(MaxNameLen + 1 + 6) * m_number];
104 }
105 m_file_names = new PCHAR[m_number];
106 PCHAR buf = m_file_name_buf;
107 int num_rand_chars = strlen(rand_chars);
108 for(int i = 0; i < m_number; i++)
109 {
110 if(*m_exit)
111 {
112 delete m_file_names;
113 m_file_names = NULL;
114 return;
115 }
116 char rand_buf[RandExtraLen + 1];
117 int len = r.getNum() % (RandExtraLen + 1);
118 int j;
119 for(j = 0; j < len; j++)
120 {
121 rand_buf[j] = rand_chars[r.getNum() % num_rand_chars];
122 }
123 rand_buf[j] = '\0';
124 m_file_names[i] = buf;
125 if(m_number_directories != 1)
126 {
127 sprintf(buf, "%05d/", directory_num);
128 buf += strlen(buf);
129 }
130 if(m_sync)
131 m_dirIndex[i] = directory_num;
132 names_in_dir++;
133 if(names_in_dir > names_per_directory)
134 {
135 names_in_dir = 0;
136 directory_num++;
137 }
138 if(do_random)
139 {
140 sprintf(buf, "%s%010x", rand_buf, i);
141 }
142 else
143 {
144 sprintf(buf, "%010x%s", i, rand_buf);
145 }
146 buf += strlen(buf) + 1;
147 }
148 }
149
create_a_file(const char * filename,char * buf,int size,int dir)150 int COpenTest::create_a_file(const char *filename, char *buf, int size, int dir)
151 {
152 FILE_TYPE fd = 0;
153 int flags = S_IRUSR | S_IWUSR;
154 fd = file_open(filename, O_CREAT|O_EXCL|O_WRONLY, flags);
155
156 if(fd == -1)
157 {
158 fprintf(stderr, "Can't create file %s\n", filename);
159 return -1;
160 }
161 if(m_max)
162 {
163 for(int i = 0; i < size; i += m_chunk_size)
164 {
165 int to_write = size - i;
166 if(to_write > m_chunk_size) to_write = m_chunk_size;
167 if(to_write != write(fd, static_cast<void *>(buf), to_write))
168 {
169 fprintf(stderr, "Can't write data.\n");
170 return -1;
171 }
172 }
173 }
174 if(m_sync)
175 {
176 if(fsync(fd))
177 {
178 fprintf(stderr, "Can't sync file.\n");
179 return -1;
180 }
181 if(m_sync_dir && fsync(m_directoryHandles[dir]))
182 {
183 fprintf(stderr, "Can't sync directory, turning off dir-sync.\n");
184 m_sync_dir = false;
185 }
186 }
187 close(fd);
188 return 0;
189 }
190
create_a_link(const char * original,const char * filename,int dir)191 int COpenTest::create_a_link(const char *original, const char *filename, int dir)
192 {
193 if(m_max == -1)
194 {
195 if(link(original, filename))
196 {
197 fprintf(stderr, "Can't create link %s\n", filename);
198 return -1;
199 }
200 if(m_sync)
201 {
202 if(fsync(m_directoryHandles[dir]))
203 {
204 fprintf(stderr, "Can't sync file.\n");
205 return -1;
206 }
207 }
208 }
209 else
210 {
211 const char *name = strchr(original, '/');
212 if(name)
213 name++;
214 else
215 name = original;
216 if(symlink(name, filename))
217 {
218 fprintf(stderr, "Can't create symlink %s\n", filename);
219 return -1;
220 }
221 if(m_sync)
222 {
223 if(fsync(m_directoryHandles[dir]))
224 {
225 fprintf(stderr, "Can't sync file.\n");
226 return -1;
227 }
228 }
229 }
230 return 0;
231 }
232
create(CPCCHAR dirname,BonTimer & timer,int num,int max_size,int min_size,int num_directories,bool do_random)233 int COpenTest::create(CPCCHAR dirname, BonTimer &timer, int num, int max_size
234 , int min_size, int num_directories, bool do_random)
235 {
236 if(num_directories >= 100000)
237 {
238 fprintf(stderr, "Can't have more than 99,999 directories.\n");
239 return -1;
240 }
241
242 m_number = num * DirectoryUnit;
243 m_number_directories = num_directories;
244 make_names(timer.random_source, do_random);
245 m_max = max_size;
246 m_min = min_size;
247 m_size_range = m_max - m_min;
248 m_dirname = new char[strlen(dirname) + 1];
249 strcpy(m_dirname, dirname);
250
251 if(num_directories >= 100000)
252 {
253 fprintf(stderr, "Can't have more than 99,999 directories.\n");
254 return -1;
255 }
256 if(mkdir(dirname, S_IRWXU))
257 {
258 fprintf(stderr, "Can't make directory %s\n", dirname);
259 return -1;
260 }
261 if(chdir(dirname))
262 {
263 fprintf(stderr, "Can't change to directory %s\n", dirname);
264 return -1;
265 }
266 int i;
267 if(m_sync)
268 m_directoryHandles = new FILE_TYPE[num_directories];
269 if(num_directories > 1)
270 {
271 for(i = 0; i < num_directories; i++)
272 {
273 sprintf(m_buf, "%05d", i);
274 if(mkdir(m_buf, S_IRWXU))
275 {
276 fprintf(stderr, "Can't make directory %s\n", m_buf);
277 return -1;
278 }
279 if(m_sync)
280 {
281 m_directoryHandles[i] = open(m_buf, O_RDONLY);
282 if(m_directoryHandles[i] == -1)
283 {
284 fprintf(stderr, "Can't get directory handle.\n");
285 return -1;
286 }
287 }
288 }
289 }
290 else if(m_sync)
291 {
292 m_directoryHandles[0] = open(".", O_RDONLY);
293 if(m_directoryHandles[0] == -1)
294 {
295 fprintf(stderr, "Can't get directory handle.\n");
296 return -1;
297 }
298 }
299
300 Duration dur;
301 timer.start();
302 for(i = 0; i < m_number; i++)
303 {
304 if(*m_exit)
305 {
306 if(m_number_directories != 1 && chdir(".."))
307 {
308 fprintf(stderr, "Can't change to directory ..\n");
309 return -1;
310 }
311 return eCtrl_C;
312 }
313 dur.start();
314 // m_max < 0 means link or sym-link
315 if(m_max < 0)
316 {
317 if(i == 0)
318 {
319 if(create_a_file(m_file_names[0], m_buf, 0, m_dirIndex ? m_dirIndex[0] : 0))
320 return -1;
321 }
322 else
323 {
324 // create_a_link() looks at m_max to see what to do
325 if(create_a_link(m_file_names[0], m_file_names[i], m_dirIndex ? m_dirIndex[i] : 0))
326 return -1;
327 }
328 }
329 else
330 {
331 int size;
332 if(m_size_range)
333 size = m_min + (timer.random_source.getNum() % (m_size_range + 1));
334 else
335 size = m_max;
336 if(create_a_file(m_file_names[i], m_buf, size, m_dirIndex ? m_dirIndex[i] : 0))
337 return -1;
338 }
339 dur.stop();
340 }
341 sync();
342 timer.stop_and_record(do_random ? CreateRand : CreateSeq);
343 timer.add_latency(do_random ? CreateRand : CreateSeq, dur.getMax());
344 return 0;
345 }
346
delete_random(BonTimer & timer)347 int COpenTest::delete_random(BonTimer &timer)
348 {
349 random_sort(timer.random_source);
350 timer.start();
351 int i;
352 Duration dur;
353 for(i = 0; i < m_number; i++)
354 {
355 dur.start();
356 if(unlink(m_file_names[i]))
357 {
358 fprintf(stderr, "Can't delete file %s\n", m_file_names[i]);
359 return -1;
360 }
361 if(m_sync && m_sync_dir)
362 {
363 if(fsync(m_directoryHandles[m_dirIndex[i]]))
364 {
365 fprintf(stderr, "Can't sync directory, turning off dir-sync.\n");
366 m_sync_dir = false;
367 }
368 }
369 dur.stop();
370 }
371 if(m_number_directories > 1)
372 {
373 char buf[6];
374 for(i = 0; i < m_number_directories; i++)
375 {
376 sprintf(buf, "%05d", i);
377 if(m_sync)
378 {
379 close(m_directoryHandles[i]);
380 }
381 if(rmdir(buf))
382 {
383 io_error("rmdir");
384 return -1;
385 }
386 }
387 }
388 else
389 {
390 if(m_sync)
391 {
392 close(m_directoryHandles[0]);
393 }
394 }
395 if(chdir("..") || rmdir(m_dirname))
396 {
397 io_error("rmdir");
398 return -1;
399 }
400 delete m_dirname;
401 m_dirname = NULL;
402 sync();
403 timer.stop_and_record(DelRand);
404 timer.add_latency(DelRand, dur.getMax());
405 return 0;
406 }
407
delete_sequential(BonTimer & timer)408 int COpenTest::delete_sequential(BonTimer &timer)
409 {
410 timer.start();
411 int count = 0;
412 Duration dur;
413 for(int i = 0; i < m_number_directories; i++)
414 {
415 char buf[6];
416 if(m_number_directories != 1)
417 {
418 sprintf(buf, "%05d", i);
419 if(chdir(buf))
420 {
421 fprintf(stderr, "Can't change to directory %s\n", buf);
422 return -1;
423 }
424 }
425 DIR *d = opendir(".");
426 if(!d)
427 {
428 fprintf(stderr, "Can't open directory.\n");
429 if(m_number_directories != 1)
430 {
431 if(chdir(".."))
432 fprintf(stderr, "Can't chdir().\n");
433 }
434 return -1;
435 }
436 dirent *file_ent;
437
438 while(1)
439 {
440 dur.start();
441 file_ent = readdir(d);
442 if(file_ent == NULL)
443 break;
444 if(file_ent->d_name[0] != '.')
445 {
446 if(unlink(file_ent->d_name))
447 {
448 fprintf(stderr, "Can't delete file %s\n", file_ent->d_name);
449 return -1;
450 }
451
452
453 if(m_sync && m_sync_dir)
454 {
455 if(fsync(m_directoryHandles[i]))
456 {
457 fprintf(stderr, "Can't sync directory, turning off dir-sync.\n");
458 m_sync_dir = false;
459 }
460 }
461 count++;
462 }
463 dur.stop();
464 }
465 closedir(d);
466 if(m_sync)
467 {
468 close(m_directoryHandles[i]);
469 }
470 if(m_number_directories != 1)
471 {
472 if(chdir("..") || rmdir(buf))
473 {
474 io_error("rmdir");
475 return -1;
476 }
477 }
478 }
479 if(chdir("..") || rmdir(m_dirname))
480 {
481 io_error("rmdir");
482 return -1;
483 }
484 delete m_dirname;
485 m_dirname = NULL;
486 if(count != m_number)
487 {
488 fprintf(stderr, "Expected %d files but only got %d\n", m_number, count);
489 return -1;
490 }
491 sync();
492 timer.stop_and_record(DelSeq);
493 timer.add_latency(DelSeq, dur.getMax());
494 return 0;
495 }
496
stat_file(CPCCHAR file)497 int COpenTest::stat_file(CPCCHAR file)
498 {
499 struct stat st;
500 if(stat(file, &st))
501 {
502 fprintf(stderr, "Can't stat file %s\n", file);
503 return -1;
504 }
505 if(st.st_size)
506 {
507 FILE_TYPE fd = 0;
508 int flags = O_RDONLY;
509 fd = open(file, flags);
510 if(fd == -1)
511 {
512 fprintf(stderr, "Can't open file %s\n", file);
513 return -1;
514 }
515 for(int i = 0; i < st.st_size; i += m_chunk_size)
516 {
517 int to_read = st.st_size - i;
518 if(to_read > m_chunk_size)
519 to_read = m_chunk_size;
520
521 if(to_read != read(fd, static_cast<void *>(m_buf), to_read))
522 {
523 fprintf(stderr, "Can't read data.\n");
524 return -1;
525 }
526 }
527 close(fd);
528 }
529 return 0;
530 }
531
stat_random(BonTimer & timer)532 int COpenTest::stat_random(BonTimer &timer)
533 {
534 random_sort(timer.random_source);
535 timer.start();
536
537 int i;
538 Duration dur;
539 for(i = 0; i < m_number; i++)
540 {
541 dur.start();
542 if(-1 == stat_file(m_file_names[i]))
543 return -1;
544 dur.stop();
545 }
546 timer.stop_and_record(StatRand);
547 timer.add_latency(StatRand, dur.getMax());
548 return 0;
549 }
550
stat_sequential(BonTimer & timer)551 int COpenTest::stat_sequential(BonTimer &timer)
552 {
553 timer.start();
554 int count = 0;
555 Duration dur;
556 for(int i = 0; i < m_number_directories; i++)
557 {
558 char buf[6];
559 if(m_number_directories != 1)
560 {
561 sprintf(buf, "%05d", i);
562 if(chdir(buf))
563 {
564 fprintf(stderr, "Can't change to directory %s\n", buf);
565 return -1;
566 }
567 }
568 DIR *d = opendir(".");
569 if(!d)
570 {
571 fprintf(stderr, "Can't open directory.\n");
572 if(m_number_directories != 1)
573 {
574 if(chdir(".."))
575 fprintf(stderr, "Can't chdir().\n");
576 }
577 return -1;
578 }
579 dirent *file_ent;
580 while(1)
581 {
582 dur.start();
583 file_ent = readdir(d);
584 if(file_ent == NULL)
585 break;
586 if(*m_exit)
587 {
588 if(m_number_directories != 1 && chdir(".."))
589 {
590 fprintf(stderr, "Can't change to directory ..\n");
591 return -1;
592 }
593 return eCtrl_C;
594 }
595 if(file_ent->d_name[0] != '.') // our files do not start with a dot
596 {
597 if(-1 == stat_file(file_ent->d_name))
598 {
599 if(m_number_directories != 1)
600 {
601 if(chdir(".."))
602 {
603 fprintf(stderr, "Can't chdir().\n");
604 return -1;
605 }
606 }
607 dur.stop();
608 return -1;
609 }
610 count++;
611 dur.stop();
612 }
613 }
614 closedir(d);
615 if(m_number_directories != 1)
616 {
617 if(chdir(".."))
618 {
619 fprintf(stderr, "Can't change to directory ..\n");
620 return -1;
621 }
622 }
623 }
624 if(count != m_number)
625 {
626 fprintf(stderr, "Expected %d files but only got %d\n", m_number, count);
627 return -1;
628 }
629 timer.stop_and_record(StatSeq);
630 timer.add_latency(StatSeq, dur.getMax());
631 return 0;
632 }
633
634