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