source: rtems/testsuites/libtests/flashdisk01/test-file-system.c @ c499856

4.11
Last change on this file since c499856 was c499856, checked in by Chris Johns <chrisj@…>, on Mar 20, 2014 at 9:10:47 PM

Change all references of rtems.com to rtems.org.

  • Property mode set to 100644
File size: 17.3 KB
Line 
1/*
2 * Copyright (c) 2010-2012 embedded brains GmbH.  All rights reserved.
3 *
4 *  embedded brains GmbH
5 *  Obere Lagerstr. 30
6 *  82178 Puchheim
7 *  Germany
8 *  <info@embedded-brains.de>
9 *
10 * The license and distribution terms for this file may be
11 * found in the file LICENSE in this distribution or at
12 * http://www.rtems.org/license/LICENSE.
13 */
14
15#include "test-file-system.h"
16
17#include <bsp.h>
18
19#if !BSP_SMALL_MEMORY
20
21#include <sys/types.h>
22#include <sys/param.h>
23#include <sys/stat.h>
24#include <assert.h>
25#include <stdio.h>
26#include <stdlib.h>
27#include <inttypes.h>
28#include <dirent.h>
29#include <unistd.h>
30#include <errno.h>
31#include <fcntl.h>
32#include <string.h>
33
34#include <rtems.h>
35#include <rtems/libio.h>
36#include <rtems/endian.h>
37
38#define ASSERT_SC(sc) assert((sc) == RTEMS_SUCCESSFUL)
39
40#define BUFFER_SIZE (32U * 1024U)
41
42#define HEADER_SIZE 8
43
44/**
45 * @brief Test states.
46 *
47 * digraph {
48 *   INIT -> MOUNT;
49 *   MOUNT -> INIT_ROOT;
50 *   MOUNT -> FORMAT;
51 *   INIT_ROOT -> CHOOSE_DIR_ACTION
52 *   INIT_ROOT -> ERROR;
53 *   CHOOSE_DIR_ACTION -> DIR_OPEN;
54 *   CHOOSE_DIR_ACTION -> DIR_CLOSE;
55 *   CHOOSE_DIR_ACTION -> DIR_CREATE;
56 *   CHOOSE_DIR_ACTION -> DIR_DELETE;
57 *   CHOOSE_DIR_ACTION -> DIR_SLEEP;
58 *   DIR_OPEN -> CHOOSE_DIR_ACTION;
59 *   DIR_OPEN -> CHOOSE_FILE_ACTION;
60 *   DIR_OPEN -> ERROR;
61 *   DIR_CLOSE -> CHOOSE_DIR_ACTION;
62 *   DIR_CLOSE -> ERROR;
63 *   DIR_CREATE -> CHOOSE_DIR_ACTION;
64 *   DIR_CREATE -> DIR_DELETE;
65 *   DIR_CREATE -> ERROR;
66 *   DIR_DELETE -> CHOOSE_DIR_ACTION;
67 *   DIR_DELETE -> ERROR;
68 *   DIR_SLEEP -> CHOOSE_DIR_ACTION;
69 *   CHOOSE_FILE_ACTION -> FILE_CLOSE;
70 *   CHOOSE_FILE_ACTION -> FILE_APPEND;
71 *   CHOOSE_FILE_ACTION -> FILE_READ;
72 *   CHOOSE_FILE_ACTION -> FILE_SLEEP;
73 *   FILE_CLOSE -> CHOOSE_DIR_ACTION;
74 *   FILE_CLOSE -> ERROR;
75 *   FILE_APPEND -> CHOOSE_FILE_ACTION;
76 *   FILE_APPEND -> FILE_CLOSE_AND_UNLINK;
77 *   FILE_APPEND -> ERROR;
78 *   FILE_READ -> CHOOSE_FILE_ACTION;
79 *   FILE_READ -> ERROR;
80 *   FILE_SLEEP -> CHOOSE_FILE_ACTION;
81 *   FILE_CLOSE_AND_UNLINK -> CHOOSE_DIR_ACTION;
82 *   FILE_CLOSE_AND_UNLINK -> ERROR;
83 *   ERROR -> FORMAT;
84 *   FORMAT -> MOUNT;
85 *   FORMAT -> FINISH;
86 * }
87 */
88typedef enum {
89  CHOOSE_DIR_ACTION,
90  CHOOSE_FILE_ACTION,
91  DIR_CLOSE,
92  DIR_CREATE,
93  DIR_DELETE,
94  DIR_OPEN,
95  DIR_SLEEP,
96  ERROR,
97  FILE_APPEND,
98  FILE_READ,
99  FILE_CLOSE,
100  FILE_SLEEP,
101  FILE_CLOSE_AND_UNLINK,
102  FINISH,
103  FORMAT,
104  INIT,
105  INIT_ROOT,
106  MOUNT
107} test_state;
108
109typedef struct {
110  DIR *dir;
111  unsigned level;
112  unsigned content_count;
113  int fd;
114  int eno;
115  uint8_t buf [BUFFER_SIZE];
116  char file_path [MAXPATHLEN];
117} fs_state;
118
119static test_state do_format(
120  unsigned index,
121  fs_state *fs,
122  const char *disk_path,
123  const test_file_system_handler *handler,
124  void *handler_arg
125)
126{
127  int rv = 0;
128
129  printf("[%02u]: format: %s\n", index, disk_path);
130
131  rv = (*handler->format)(disk_path, handler_arg);
132  if (rv == 0) {
133    return MOUNT;
134  } else {
135    fs->eno = errno;
136
137    return FINISH;
138  }
139}
140
141static uint32_t simple_random(uint32_t v)
142{
143        v *= 1664525;
144        v += 1013904223;
145
146        return v;
147}
148
149static unsigned get_bucket_with_random(unsigned count, long random)
150{
151  uint32_t unit = ((uint32_t) 1U << 31) / count;
152  uint32_t bucket = (uint32_t) random / unit;
153
154  if (bucket != count) {
155    return bucket;
156  } else {
157    return bucket - 1;
158  }
159}
160
161static unsigned get_bucket(unsigned count)
162{
163  return get_bucket_with_random(count, lrand48());
164}
165
166static test_state do_choose_dir_action(void)
167{
168  switch (get_bucket(8)) {
169    case 0:
170    case 1:
171      return DIR_CLOSE;
172    case 2:
173    case 3:
174      return DIR_CREATE;
175    case 4:
176    case 5:
177      return DIR_OPEN;
178    case 6:
179      return DIR_DELETE;
180    case 7:
181      return DIR_SLEEP;
182    default:
183      assert(false);
184      break;
185  }
186}
187
188static test_state do_choose_file_action(void)
189{
190  switch (get_bucket(4)) {
191    case 0:
192      return FILE_CLOSE;
193    case 1:
194      return FILE_SLEEP;
195    case 2:
196      return FILE_APPEND;
197    case 3:
198      return FILE_READ;
199    default:
200      assert(false);
201      break;
202  }
203}
204
205static bool is_normal_entry(const char *entry_name)
206{
207  static const char *black_list [] = {
208    ".",
209    "..",
210    "lost+found"
211  };
212  size_t n = sizeof(black_list) / sizeof(black_list [0]);
213  size_t i = 0;
214  bool ok = true;
215
216  while (ok && i < n) {
217    ok = ok && strcmp(entry_name, black_list [i]) != 0;
218    ++i;
219  }
220
221  return ok;
222}
223
224static bool open_dir(fs_state *fs, const char *dir_path)
225{
226  int rv = 0;
227  bool change_dir = true;
228
229  if (dir_path == NULL) {
230    if (fs->level > 1) {
231      rv = chdir("..");
232      if (rv != 0) {
233        fs->eno = errno;
234
235        return false;
236      }
237
238      --fs->level;
239    } else {
240      return true;
241    }
242    dir_path = ".";
243    change_dir = false;
244  }
245
246  if (fs->dir != NULL) {
247    rv = closedir(fs->dir);
248    if (rv != 0) {
249      fs->eno = errno;
250
251      return false;
252    }
253  }
254
255  fs->content_count = 0;
256  fs->dir = opendir(dir_path);
257
258  if (fs->dir != NULL) {
259    struct dirent *de = NULL;
260
261    rewinddir(fs->dir);
262    while ((de = readdir(fs->dir)) != NULL) {
263      if (is_normal_entry(de->d_name)) {
264        ++fs->content_count;
265      }
266    }
267  } else {
268    fs->eno = errno;
269
270    return false;
271  }
272
273  if (change_dir) {
274    rv = chdir(dir_path);
275    if (rv != 0) {
276      fs->eno = errno;
277
278      return false;
279    }
280
281    ++fs->level;
282  }
283
284  return true;
285}
286
287static char *get_dir_entry(fs_state *fs, bool *is_dir)
288{
289  int rv = 0;
290  char *entry_name = NULL;
291
292  if (fs->content_count > 0) {
293    struct dirent *de = NULL;
294    unsigned bucket = get_bucket(fs->content_count);
295    unsigned i = 0;
296
297    rewinddir(fs->dir);
298    while ((de = readdir(fs->dir)) != NULL) {
299      if (is_normal_entry(de->d_name)) {
300        if (i != bucket) {
301          ++i;
302        } else {
303          break;
304        }
305      }
306    }
307
308    if (de != NULL) {
309      struct stat st;
310
311      rv = stat(de->d_name, &st);
312      if (rv == 0) {
313        *is_dir = S_ISDIR(st.st_mode);
314
315        entry_name = strdup(de->d_name);
316      }
317    }
318  }
319
320  return entry_name;
321}
322
323
324static test_state do_init_root(unsigned index, fs_state *fs, const char *mount_path)
325{
326  printf("[%02u]: init root: %s\n", index, mount_path);
327
328  if (open_dir(fs, mount_path)) {
329    return CHOOSE_DIR_ACTION;
330  } else {
331    return ERROR;
332  }
333}
334
335static test_state do_dir_close(unsigned index, fs_state *fs)
336{
337  if (fs->level > 1) {
338    printf("[%02u]: close dir\n", index);
339
340    if (open_dir(fs, NULL)) {
341      return CHOOSE_DIR_ACTION;
342    } else {
343      return ERROR;
344    }
345  } else {
346    return CHOOSE_DIR_ACTION;
347  }
348}
349
350static int open_file(fs_state *fs, const char *path, int flags, mode_t mode)
351{
352  size_t n = strlcpy(fs->file_path, path, sizeof(fs->file_path));
353  assert(n < sizeof(fs->file_path));
354
355  return open(fs->file_path, flags, mode);
356}
357
358static test_state do_dir_create(unsigned index, fs_state *fs)
359{
360  int rv = 0;
361  test_state state = ERROR;
362  long number = lrand48();
363  char name [64];
364
365  snprintf(name, sizeof(name), "%li", number);
366
367  if ((number % 2) == 0) {
368    printf("[%02u]: create file: %s\n", index, name);
369
370    rv = open_file(fs, name, O_RDONLY | O_CREAT, 0777);
371
372    if (rv >= 0) {
373      rv = close(rv);
374
375      if (rv == 0) {
376        state = CHOOSE_DIR_ACTION;
377      }
378    } else if (errno == ENOSPC) {
379      state = DIR_DELETE;
380    } else {
381      fs->eno = errno;
382    }
383  } else {
384    printf("[%02u]: create dir: %s\n", index, name);
385
386    rv = mkdir(name, 0777);
387
388    if (rv == 0) {
389      ++fs->content_count;
390
391      state = CHOOSE_DIR_ACTION;
392    } else if (errno == EEXIST) {
393      state = CHOOSE_DIR_ACTION;
394    } else if (errno == ENOSPC) {
395      state = DIR_DELETE;
396    } else {
397      fs->eno = errno;
398    }
399  }
400
401  return state;
402}
403
404static test_state remove_node(unsigned index, fs_state *fs, const char *path, bool is_dir)
405{
406  test_state state = ERROR;
407  int rv = 0;
408
409  if (is_dir) {
410    printf("[%02u]: remove dir: %s\n", index, path);
411
412    rv = rmdir(path);
413  } else {
414    printf("[%02u]: remove file: %s\n", index, path);
415
416    rv = unlink(path);
417  }
418
419  if (rv == 0) {
420    --fs->content_count;
421
422    state = CHOOSE_DIR_ACTION;
423  } else if (errno == ENOTEMPTY) {
424    state = CHOOSE_DIR_ACTION;
425  } else {
426    fs->eno = errno;
427  }
428
429  return state;
430}
431
432static test_state do_dir_delete(unsigned index, fs_state *fs)
433{
434  test_state state = ERROR;
435
436  if (fs->content_count > 0) {
437    bool is_dir = false;
438    char *dir_entry_path = get_dir_entry(fs, &is_dir);
439
440    if (dir_entry_path != NULL) {
441      state = remove_node(index, fs, dir_entry_path, is_dir);
442      free(dir_entry_path);
443    }
444  } else {
445    state = CHOOSE_DIR_ACTION;
446  }
447
448  return state;
449}
450
451static test_state do_dir_open(unsigned index, fs_state *fs)
452{
453  test_state state = ERROR;
454
455  if (fs->content_count > 0) {
456    bool is_dir = false;
457    char *dir_entry_path = get_dir_entry(fs, &is_dir);
458
459    if (dir_entry_path != NULL) {
460      if (is_dir) {
461        printf("[%02u]: open dir: %s\n", index, dir_entry_path);
462
463        if (open_dir(fs, dir_entry_path)) {
464          state = CHOOSE_DIR_ACTION;
465        }
466      } else {
467        printf("[%02u]: open file: %s\n", index, dir_entry_path);
468
469        fs->fd = open_file(fs, dir_entry_path, O_RDWR, 0);
470
471        if (fs->fd >= 0) {
472          state = CHOOSE_FILE_ACTION;
473        } else {
474          fs->eno = errno;
475        }
476      }
477
478      free(dir_entry_path);
479    }
480  } else {
481    state = CHOOSE_DIR_ACTION;
482  }
483
484  return state;
485}
486
487#if BYTE_ORDER == LITTLE_ENDIAN
488  #define VALUE_SHIFT_INIT 0
489  #define VALUE_SHIFT_INC 8
490#else
491  #define VALUE_SHIFT_INIT 24
492  #define VALUE_SHIFT_INC -8
493#endif
494
495static test_state do_file_read(unsigned index, fs_state *fs)
496{
497  test_state state = ERROR;
498  int fd = fs->fd;
499  off_t pos = lseek(fd, 0, SEEK_SET);
500  struct stat st;
501  int rv = fstat(fd, &st);
502  off_t file_size = st.st_size;
503
504  printf("[%02u]: read from file\n", index);
505
506  if (pos == 0 && rv == 0) {
507    if (file_size >= HEADER_SIZE) {
508      size_t remaining = (size_t) file_size;
509      size_t header = HEADER_SIZE;
510      size_t data = 0;
511      uint32_t header_buf [2];
512      uint8_t *header_pos = (uint8_t *) header_buf;
513      uint32_t value = 0;
514      unsigned value_shift = VALUE_SHIFT_INIT;
515
516      while (remaining > 0) {
517        size_t buf_size = sizeof(fs->buf);
518        size_t in = remaining < buf_size ? remaining : buf_size;
519        ssize_t in_actual = 0;
520        uint8_t *buf = fs->buf;
521
522        in_actual = read(fd, buf, in);
523        if (in_actual > 0) {
524          while (in_actual > 0) {
525            if (header > 0) {
526              size_t copy = header <= (size_t) in_actual ?
527                header : (size_t) in_actual;
528
529              memcpy(header_pos, buf, copy);
530
531              in_actual -= (ssize_t) copy;
532              remaining -= copy;
533              buf += copy;
534              header -= copy;
535              header_pos += copy;
536
537              if (header == 0) {
538                data = header_buf [0];
539                value = header_buf [1];
540                value_shift = VALUE_SHIFT_INIT;
541                value = simple_random(value);
542              }
543            }
544
545            if (data > 0) {
546              size_t compare = data <= (size_t) in_actual ?
547                data : (size_t) in_actual;
548              size_t c = 0;
549
550              for (c = 0; c < compare; ++c) {
551                assert(buf [c] == (uint8_t) (value >> value_shift));
552                value_shift = (value_shift + VALUE_SHIFT_INC) % 32;
553                if (value_shift == VALUE_SHIFT_INIT) {
554                  value = simple_random(value);
555                }
556              }
557
558              in_actual -= (ssize_t) compare;
559              remaining -= compare;
560              buf += compare;
561              data -= compare;
562
563              if (data == 0) {
564                header = HEADER_SIZE;
565                header_pos = (uint8_t *) header_buf;
566              }
567            }
568          }
569        } else if (in_actual == 0) {
570          /* This is either a severe bug or a damaged file system */
571          printf("[%02u]: error: invalid file size\n", index);
572          remaining = 0;
573        } else {
574          fs->eno = errno;
575          remaining = 0;
576        }
577      }
578
579      if (remaining == 0) {
580        state = CHOOSE_FILE_ACTION;
581      }
582    } else if (file_size == 0) {
583      state = CHOOSE_FILE_ACTION;
584    } else {
585      printf("[%02u]: error: unexpected file size\n", index);
586    }
587  } else {
588    fs->eno = errno;
589  }
590
591  return state;
592}
593
594static test_state do_file_append(unsigned index, fs_state *fs)
595{
596  test_state state = ERROR;
597  int fd = fs->fd;
598  off_t pos = lseek(fd, 0, SEEK_END);
599
600  printf("[%02u]: append to file\n", index);
601
602  if (pos != (off_t) -1) {
603    size_t buf_size = sizeof(fs->buf);
604    long random = lrand48();
605    size_t out = get_bucket_with_random(buf_size, random) + 1;
606    ssize_t out_actual = 0;
607    uint8_t *buf = fs->buf;
608    uint32_t value = (uint32_t) random;
609    uint32_t *words = (uint32_t *) &buf [0];
610    size_t word_count = 0;
611    size_t w = 0;
612
613    /* Must be big enough for the header */
614    out = out >= HEADER_SIZE ? out : HEADER_SIZE;
615
616    /*
617     * In case out is not an integral multiple of four we will write a bit to
618     * much.  This does not hurt since the buffer is big enough.
619     */
620    word_count = (out + 3) / 4;
621
622    /* The first word will contain the byte count with random data */
623    words [0] = out - HEADER_SIZE;
624
625    for (w = 1; w < word_count; ++w) {
626      words [w] = value;
627      value = simple_random(value);
628    }
629
630    out_actual = write(fd, buf, out);
631
632    if (out_actual == (ssize_t) out) {
633      state = CHOOSE_FILE_ACTION;
634    } else if (out_actual >= 0 || errno == ENOSPC) {
635      state = FILE_CLOSE_AND_UNLINK;
636    } else {
637      fs->eno = errno;
638    }
639  } else {
640    fs->eno = errno;
641  }
642
643  return state;
644}
645
646static test_state do_file_close(unsigned index, fs_state *fs)
647{
648  int rv = 0;
649  test_state state = ERROR;
650
651  printf("[%02u]: close file\n", index);
652
653  rv = close(fs->fd);
654  fs->fd = -1;
655
656  if (rv == 0) {
657    state = CHOOSE_DIR_ACTION;
658  } else {
659    fs->eno = errno;
660  }
661
662  return state;
663}
664
665static test_state do_file_close_and_unlink(unsigned index, fs_state *fs)
666{
667  test_state state = do_file_close(index, fs);
668
669  if (state != ERROR) {
670    state = remove_node(index, fs, fs->file_path, false);
671  }
672
673  return state;
674}
675
676static test_state do_sleep(unsigned index, test_state state)
677{
678  rtems_status_code sc = RTEMS_SUCCESSFUL;
679  rtems_interval ms = ((rtems_interval) get_bucket(10) + 1) * 100;
680  rtems_interval interval = ms / rtems_configuration_get_milliseconds_per_tick();
681
682  printf("[%02u]: sleep: %" PRIu32 " ms\n", index, ms);
683
684  sc = rtems_task_wake_after(interval);
685  ASSERT_SC(sc);
686
687  return state;
688}
689
690static test_state do_mount(
691  unsigned index,
692  fs_state *fs,
693  const char *disk_path,
694  const char *mount_path,
695  const test_file_system_handler *handler,
696  void *handler_arg
697)
698{
699  int rv = 0;
700
701  printf("[%02u]: mount: %s -> %s\n", index, disk_path, mount_path);
702
703  rv = (*handler->mount)(disk_path, mount_path, handler_arg);
704  if (rv == 0) {
705    return INIT_ROOT;
706  } else {
707    fs->eno = errno;
708
709    return FORMAT;
710  }
711}
712
713static test_state do_error(unsigned index, fs_state *fs, const char *mount_path)
714{
715  int rv = 0;
716
717  if (fs->eno > 0) {
718    printf("[%02u]: error: %s\n", index, strerror(fs->eno));
719  } else {
720    printf("[%02u]: error\n", index);
721  }
722  fs->eno = 0;
723
724  if (fs->fd >= 0) {
725    close(fs->fd);
726    fs->fd = -1;
727  }
728
729  if (fs->dir != NULL) {
730    closedir(fs->dir);
731    fs->dir = NULL;
732  }
733
734  chdir("/");
735  fs->level = 0;
736
737  rv = unmount(mount_path);
738  if (rv == 0) {
739    return FORMAT;
740  } else {
741    return FINISH;
742  }
743}
744
745void test_file_system_with_handler(
746  unsigned index,
747  const char *disk_path,
748  const char *mount_path,
749  const test_file_system_handler *handler,
750  void *handler_arg
751)
752{
753  int counter = 0;
754  test_state state = INIT;
755  fs_state *fs = calloc(1, sizeof(*fs));
756
757  if (fs == NULL) {
758    printf("[%02u]: not enough memory\n", index);
759    return;
760  }
761
762  fs->fd = -1;
763
764  printf("[%02u]: start\n", index);
765
766  while (state != FINISH && counter < 600) {
767    switch (state) {
768      case CHOOSE_DIR_ACTION:
769        state = do_choose_dir_action();
770        break;
771      case CHOOSE_FILE_ACTION:
772        state = do_choose_file_action();
773        break;
774      case DIR_CLOSE:
775        state = do_dir_close(index, fs);
776        break;
777      case DIR_CREATE:
778        state = do_dir_create(index, fs);
779        break;
780      case DIR_DELETE:
781        state = do_dir_delete(index, fs);
782        break;
783      case DIR_OPEN:
784        state = do_dir_open(index, fs);
785        break;
786      case DIR_SLEEP:
787        state = do_sleep(index, CHOOSE_DIR_ACTION);
788        break;
789      case ERROR:
790        state = do_error(index, fs, mount_path);
791        break;
792      case FILE_APPEND:
793        state = do_file_append(index, fs);
794        break;
795      case FILE_READ:
796        state = do_file_read(index, fs);
797        break;
798      case FILE_CLOSE:
799        state = do_file_close(index, fs);
800        break;
801      case FILE_CLOSE_AND_UNLINK:
802        state = do_file_close_and_unlink(index, fs);
803        break;
804      case FILE_SLEEP:
805        state = do_sleep(index, CHOOSE_FILE_ACTION);
806        break;
807      case FORMAT:
808        state = do_format(index, fs, disk_path, handler, handler_arg);
809        break;
810      case INIT:
811        state = MOUNT;
812        break;
813      case INIT_ROOT:
814        state = do_init_root(index, fs, mount_path);
815        break;
816      case MOUNT:
817        state = do_mount(
818          index,
819         fs,
820          disk_path,
821          mount_path,
822          handler,
823          handler_arg
824        );
825        break;
826      default:
827        assert(false);
828        break;
829    }
830
831    ++counter;
832  }
833
834  printf("[%02u]: finish\n", index);
835
836  free(fs);
837}
838
839#endif /* !BSP_SMALL_MEMORY */
Note: See TracBrowser for help on using the repository browser.