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