LCOV - code coverage report
Current view: directory - src/shared/platform - nacl_host_dir_test.c (source / functions) Found Hit Coverage
Test: coverage.lcov Lines: 219 161 73.5 %
Date: 2014-09-25 Functions: 0 0 -

       1                 : /*
       2                 :  * Copyright 2011 The Native Client Authors. All rights reserved.
       3                 :  * Use of this source code is governed by a BSD-style license that can be
       4                 :  * found in the LICENSE file.
       5                 :  */
       6                 : 
       7                 : /*
       8                 :  * Exercise the NaClHostDir NaClHostDirGetdents interface.
       9                 :  *
      10                 :  * We use testdata input pairs: a file containing an expected
      11                 :  * directory listing, and a sample directory.  The contents are just
      12                 :  * the d_name portion of the directory entry, since none of the other
      13                 :  * data will be preserved across SCM checkouts, etc.  We check, as a
      14                 :  * heurstic, that the d_ino part is identical to that which results
      15                 :  * from a NaClHostDescStat call (platform library) after
      16                 :  * NaClAbiStatHostDescStatXlate processing (desc library),
      17                 :  *
      18                 :  * NB: the interface being tested is not uniform/cross platform.  In
      19                 :  * particular, unless UNC paths are used on Windows (which we are not
      20                 :  * doing), the total path name for a file is MAX_PATH, or 260
      21                 :  * characters.  Since this maximum depends on where in the directory
      22                 :  * tree is a file located, it is impossible to know a priori whether
      23                 :  * creating a file will succeed or not without constructing the full
      24                 :  * path.  Anyway, since directory descriptors are not made available
      25                 :  * except with the -d flag, and the likely use of this code, if at
      26                 :  * all, will be in non-browser sandboxing contexts where we won't run
      27                 :  * on that many host OSes, we're not going to try to abstract away the
      28                 :  * host OS differences.  For WebFS, there will be a separate directory
      29                 :  * abstraction that is provided by (and is the responsibility of) the
      30                 :  * host browser, and different code paths will be used, hopefully
      31                 :  * providing a host-OS platform independent view.
      32                 :  */
      33                 : 
      34                 : #include <stdio.h>
      35                 : #include <stdlib.h>
      36                 : #include <string.h>
      37                 : #include <sys/types.h>
      38                 : #include <sys/stat.h>
      39                 : 
      40                 : #include "native_client/src/include/portability_io.h"
      41                 : 
      42                 : #include "native_client/src/shared/platform/nacl_check.h"
      43                 : #include "native_client/src/shared/platform/nacl_host_desc.h"
      44                 : #include "native_client/src/shared/platform/nacl_host_dir.h"
      45                 : #include "native_client/src/shared/platform/platform_init.h"
      46                 : #include "native_client/src/trusted/desc/nacl_desc_base.h"
      47                 : #include "native_client/src/trusted/service_runtime/include/sys/dirent.h"
      48                 : #include "native_client/src/trusted/service_runtime/include/sys/errno.h"
      49                 : #include "native_client/src/trusted/service_runtime/include/sys/stat.h"
      50                 : 
      51                 : #define MAXLINE 1024  /* 256 = NAME_MAX+1 should suffice */
      52                 : #define JOINED_MAX 4096
      53                 : 
      54                 : struct string_array {
      55                 :   char    **strings;
      56                 :   size_t  nelts;
      57                 :   size_t  allocated_elts;
      58                 : };
      59                 : 
      60                 : #if NACL_WINDOWS
      61                 : static char const path_sep = '\\';
      62                 : #else
      63                 : static char const path_sep = '/';
      64                 : #endif
      65                 : 
      66                 : int verbosity = 0;
      67                 : 
      68               1 : void StringArrayCtor(struct string_array *self) {
      69               1 :   self->strings = NULL;
      70               1 :   self->nelts = 0;
      71               1 :   self->allocated_elts = 0;
      72               1 : }
      73                 : 
      74               1 : void StringArrayGrow(struct string_array *self) {
      75               1 :   size_t desired = 2 * self->allocated_elts;
      76                 :   char   **new_strings;
      77                 : 
      78               1 :   if (0 == desired) desired = 16;
      79                 :   new_strings = (char **) realloc(self->strings,
      80               1 :                                   desired * sizeof *self->strings);
      81               1 :   CHECK(NULL != new_strings);
      82               1 :   self->strings = new_strings;
      83               1 :   self->allocated_elts = desired;
      84               1 : }
      85                 : 
      86                 : void StringArrayAdd(struct string_array *self,
      87               1 :                     char                *entry) {
      88               1 :   CHECK(self->nelts <= self->allocated_elts);
      89               1 :   if (self->nelts == self->allocated_elts) {
      90               1 :     StringArrayGrow(self);
      91                 :   }
      92               1 :   self->strings[self->nelts++] = entry;
      93               1 : }
      94                 : 
      95               1 : static int strcmp_wrapper(const void *left, const void *right) {
      96               1 :   return strcmp(*(const char **) left, *(const char **) right);
      97               1 : }
      98                 : 
      99               1 : void StringArraySort(struct string_array *self) {
     100               1 :   qsort(self->strings, self->nelts, sizeof(*self->strings), strcmp_wrapper);
     101               1 : }
     102                 : 
     103                 : uint32_t ExtractNames(struct string_array *dest_array,
     104                 :                       void                *buffer,
     105                 :                       size_t              buffer_size,
     106               1 :                       char const          *dir_path) {
     107                 :   struct nacl_abi_dirent  *nadp;
     108                 :   char                    path[4096];
     109                 :   nacl_host_stat_t        host_stat;
     110                 :   int                     rv;
     111                 :   int32_t                 rv32;
     112                 :   struct nacl_abi_stat    nabi_stat;
     113               1 :   uint32_t                error_count = 0;
     114                 : 
     115               1 :   while (buffer_size > 0) {
     116               1 :     nadp = (struct nacl_abi_dirent *) buffer;
     117               1 :     StringArrayAdd(dest_array, strdup(nadp->nacl_abi_d_name));
     118                 : 
     119                 :     rv = SNPRINTF(path, sizeof path, "%s%c%s",
     120               1 :                   dir_path, path_sep, nadp->nacl_abi_d_name);
     121               1 :     if (rv < 0) {
     122               0 :       fprintf(stderr, "snprintf failed?!?  name %s\n", nadp->nacl_abi_d_name);
     123               0 :       ++error_count;
     124               0 :       goto next_entry;
     125                 :     }
     126               1 :     if ((size_t) rv >= sizeof path) {
     127               0 :       fprintf(stderr, "path too long\n");
     128               0 :       ++error_count;
     129               0 :       goto next_entry;
     130                 :     }
     131               1 :     if (0 != (rv = NaClHostDescStat(path, &host_stat))) {
     132               0 :       fprintf(stderr, "could not stat %s: %d\n", path, rv);
     133               0 :       ++error_count;
     134               0 :       goto next_entry;
     135                 :     }
     136               1 :     rv32 = NaClAbiStatHostDescStatXlateCtor(&nabi_stat, &host_stat);
     137               1 :     if (0 != rv32) {
     138               0 :       fprintf(stderr, "NaClAbiStatHostDescStatXlateCtor failed: %d\n", rv32);
     139               0 :       ++error_count;
     140               0 :       goto next_entry;
     141                 :     }
     142                 :     /*
     143                 :      * the inode number may be masked, but both
     144                 :      * NaClAbiStatHostDescStatXlateCtor and NaClHostDirGetdents should
     145                 :      * mask identically.
     146                 :      */
     147               1 :     if (nabi_stat.nacl_abi_st_ino != nadp->nacl_abi_d_ino) {
     148                 :       fprintf(stderr,
     149                 :               "inode values differ: expected 0x%"NACL_PRIx64
     150                 :               ", got 0x%"NACL_PRIx64"\n",
     151               0 :               nabi_stat.nacl_abi_st_ino, nadp->nacl_abi_d_ino);
     152               0 :       ++error_count;
     153                 :     }
     154                 :  next_entry:
     155               1 :     buffer = (void *) ((char *) buffer + nadp->nacl_abi_d_reclen);
     156               1 :     CHECK(buffer_size >= nadp->nacl_abi_d_reclen);
     157               1 :     buffer_size -= nadp->nacl_abi_d_reclen;
     158               1 :   }
     159               1 :   return error_count;
     160               1 : }
     161                 : 
     162                 : int OperateOnDir(char *dir_name, struct string_array *file_list,
     163               1 :                  int (*op)(char const *path)) {
     164                 :   char    joined[JOINED_MAX];
     165                 :   size_t  ix;
     166               1 :   int     errors = 0;
     167                 : 
     168               1 :   for (ix = 0; ix < file_list->nelts; ++ix) {
     169                 :     if (0 == strcmp(file_list->strings[ix], ".") ||
     170               1 :         0 == strcmp(file_list->strings[ix], "..")) {
     171               1 :       continue;
     172                 :     }
     173                 :     if ((size_t) SNPRINTF(joined, sizeof joined, "%s%c%s",
     174                 :                           dir_name, path_sep, file_list->strings[ix])
     175               1 :         >= sizeof joined) {
     176                 :       fprintf(stderr, "path buffer too small for %s%c%s\n",
     177               0 :               dir_name, path_sep, file_list->strings[ix]);
     178               0 :       ++errors;
     179               0 :       continue;
     180                 :     }
     181                 : #if NACL_WINDOWS
     182               1 :     if (strlen(joined) > MAX_PATH) {
     183                 :       /*
     184                 :        * Yuck.  We remove this entry from the list, since later we are
     185                 :        * going to compare against what's actually read in, and if an
     186                 :        * ignored entry is left in, then the comparison will fail.  We
     187                 :        * mark the entry as deleted, and then we repair the string
     188                 :        * list.
     189                 :        */
     190               1 :       free(file_list->strings[ix]);
     191               1 :       file_list->strings[ix] = NULL;
     192               1 :       continue;
     193                 :     }
     194                 : #endif
     195               1 :     errors += (*op)(joined);
     196               1 :   }
     197                 : #if NACL_WINDOWS
     198               1 :   if (1) {
     199                 :     size_t dest_ix;
     200               1 :     for (ix = dest_ix = 0; ix < file_list->nelts; ++ix) {
     201               1 :       if (0 != file_list->strings[ix]) {
     202               1 :         file_list->strings[dest_ix] = file_list->strings[ix];
     203               1 :         ++dest_ix;
     204                 :       }
     205               1 :     }
     206               1 :     file_list->nelts = dest_ix;
     207                 :   }
     208                 : #endif
     209               1 :   return errors;
     210               1 : }
     211                 : 
     212               1 : static int NaClCreateFile(char const *joined) {
     213                 :   int fd;
     214                 : 
     215               1 :   if (1 < verbosity) {
     216               0 :     printf("CreateFile(%s)\n", joined);
     217                 :   }
     218               1 :   fd = OPEN(joined, O_CREAT | O_TRUNC | O_WRONLY, 0777);
     219               1 :   if (-1 == fd) {
     220               0 :     perror("nacl_host_dir_test:CreateFile");
     221               0 :     fprintf(stderr, "could not create file %s\n", joined);
     222               0 :     return 1;
     223                 :   }
     224               1 :   (void) CLOSE(fd);
     225               1 :   return 0;
     226               1 : }
     227                 : 
     228               1 : int PopulateDirectory(char *dir_name, struct string_array *file_list) {
     229                 :   /*
     230                 :    * Typical use is from SCons, where Python's tempfile module picks a
     231                 :    * directory name and creates it, so it would pre-exist.  For ease
     232                 :    * of direct, non-SCons test execution, we try to create the
     233                 :    * directory here.
     234                 :    */
     235               1 :   (void) MKDIR(dir_name, 0777);
     236               1 :   return OperateOnDir(dir_name, file_list, NaClCreateFile);
     237               1 : }
     238                 : 
     239               1 : static int NaClDeleteFile(char const *joined) {
     240                 :   int retval;
     241                 : 
     242               1 :   if (1 < verbosity) {
     243               0 :     printf("DeleteFile(%s)\n", joined);
     244                 :   }
     245               1 :   retval = UNLINK(joined);
     246               1 :   if (-1 == retval) {
     247               0 :     perror("nacl_hst_dir_test:DeleteFile");
     248               0 :     return 1;
     249                 :   }
     250               1 :   return 0;
     251               1 : }
     252                 : 
     253               1 : int CleanupDirectory(char *dirname, struct string_array *file_list) {
     254               1 :   int errors = 0;
     255                 : 
     256               1 :   if (0 != (errors += OperateOnDir(dirname, file_list, NaClDeleteFile))) {
     257               0 :     return errors;
     258                 :   }
     259               1 :   if (1 < verbosity) {
     260               0 :     printf("rmdir(%s)\n", dirname);
     261                 :   }
     262               1 :   if (0 != rmdir(dirname)) {
     263               1 :     perror("nacl_host_dir_test: rmdir");
     264                 : #if !NACL_WINDOWS
     265                 :     /*
     266                 :      * Spurious errors found on Windows due to other processes looking
     267                 :      * cross-eyed at the directory.  A similar problem could occur, in
     268                 :      * principle, on the unlink, but so far this has't been observed.
     269                 :      */
     270                 :     ++errors;
     271                 : #endif
     272                 :   }
     273               1 :   return errors;
     274               1 : }
     275                 : 
     276                 : struct dirent_buffers {
     277                 :   char const  *description;
     278                 :   size_t      buffer_bytes;
     279                 : };
     280                 : 
     281               1 : int main(int argc, char **argv) {
     282                 :   int                 opt;
     283               1 :   char                *test_dir = NULL;
     284               1 :   char                *test_file = NULL;
     285                 :   FILE                *test_iop;
     286                 :   struct string_array expected;
     287                 :   char                line_buf[MAXLINE];
     288                 :   size_t              ix;
     289                 :   ssize_t             nbytes;
     290                 :   int                 retval;
     291                 :   struct NaClHostDir  nhd;
     292                 :   struct string_array actual;
     293                 : 
     294                 :   union {
     295                 :     struct nacl_abi_dirent  nad;
     296                 :     char                    buffer[1024 + sizeof(uint64_t)];
     297                 :   }                   actual_buffer;
     298                 :   void                *aligned_buffer =
     299                 :       (void *) (((uintptr_t) &actual_buffer.buffer + sizeof(uint64_t) - 1)
     300               1 :                 & ~(sizeof(uint64_t) - 1));
     301                 :   /*
     302                 :    * our ABI requires -malign-double in untrusted code but our trusted
     303                 :    * build does not (and cannot, since this affects the interface to
     304                 :    * system libraries), so we have to manually align.
     305                 :    */
     306                 : 
     307                 :   struct dirent_buffers buffers[] = {
     308               1 :     { "small", 20, },
     309               1 :     { "medium", 24, },
     310               1 :     { "large", 300, },
     311               1 :     { "extra large", 1024, },
     312                 :   };
     313                 : 
     314                 :   size_t              min_elts;
     315               1 :   uint32_t            error_count = 0;
     316                 : 
     317               1 :   NaClPlatformInit();
     318                 : 
     319               1 :   while (EOF != (opt = getopt(argc, argv, "f:d:v"))) {
     320               1 :     switch (opt) {
     321                 :       case 'f':
     322               1 :         test_file = optarg;
     323               1 :         break;
     324                 :       case 'd':
     325               1 :         test_dir = optarg;
     326               1 :         break;
     327                 :       case 'v':
     328               0 :         ++verbosity;
     329               0 :         break;
     330                 :       default:
     331                 :         fprintf(stderr,
     332                 :                 "Usage: nacl_host_dir_test [-v] [-f file] [-d dir]\n"
     333                 :                 "\n"
     334                 :                 "       -v increases verbosity level\n"
     335                 :                 "       -f <file> specifes a file containing the expected\n"
     336                 :                 "          directory contents,\n"
     337                 :                 "       -d <dir> is the test directory to be scanned\n"
     338               0 :                 "          using the NaClHostDir routines.\n");
     339               0 :         return -1;
     340                 :     }
     341               1 :   }
     342               1 :   if (NULL == test_file) {
     343                 :     fprintf(stderr,
     344                 :             "nacl_host_dir_test: expected directory content file"
     345               0 :             " (-f) required\n");
     346               0 :     return 1;
     347                 :   }
     348               1 :   if (NULL == test_dir) {
     349                 :     fprintf(stderr,
     350                 :             "nacl_host_dir_test: test directory"
     351               0 :             " (-d) required\n");
     352               0 :     return 2;
     353                 :   }
     354               1 :   test_iop = fopen(test_file, "r");
     355               1 :   if (NULL == test_iop) {
     356                 :     fprintf(stderr,
     357                 :             "nacl_host_dir_test: could not open expected directory"
     358                 :             " content file %s\n",
     359               0 :             test_file);
     360               0 :     return 3;
     361                 :   }
     362               1 :   StringArrayCtor(&expected);
     363               1 :   while (NULL != fgets(line_buf, sizeof line_buf, test_iop)) {
     364                 :     /*
     365                 :      * We do not have newlines in the file names, though they are
     366                 :      * permitted on Linux -- actually, any POSIX-compliant system.
     367                 :      * Ideally, the test data would use NUL as filename terminators.
     368                 :      *
     369                 :      * On Windows, we do not support names greater than NAME_MAX
     370                 :      * characters...
     371                 :      */
     372               1 :     size_t len = strlen(line_buf);
     373                 : 
     374               1 :     CHECK(0 < len);
     375               1 :     CHECK('\n' == line_buf[len-1]);
     376               1 :     line_buf[len-1] = '\0';
     377               1 :     StringArrayAdd(&expected, strdup(line_buf));
     378               1 :   }
     379               1 :   StringArraySort(&expected);
     380                 : 
     381               1 :   printf("\n\nAttempting to create directory contents:\n\n");
     382               1 :   for (ix = 0; ix < expected.nelts; ++ix) {
     383               1 :     printf("%s\n", expected.strings[ix]);
     384               1 :   }
     385                 : 
     386               1 :   if (0 != PopulateDirectory(test_dir, &expected)) {
     387               0 :     retval = 4;
     388               0 :     goto cleanup;
     389                 :   }
     390                 : 
     391                 :   printf("\n\nExpected actual directory contents"
     392               1 :          " (some may get omitted on Windows):\n\n");
     393               1 :   for (ix = 0; ix < expected.nelts; ++ix) {
     394               1 :     printf("%s\n", expected.strings[ix]);
     395               1 :   }
     396                 : 
     397                 : 
     398               1 :   StringArrayCtor(&actual);
     399                 : 
     400               1 :   retval = NaClHostDirOpen(&nhd, test_dir);
     401               1 :   if (0 != retval) {
     402               0 :     fprintf(stderr, "Could not open directory %s\n", test_dir);
     403               0 :     fprintf(stderr, "Error code %d\n", retval);
     404               0 :     goto cleanup;
     405                 :   }
     406                 :   for (;;) {
     407               1 :     for (ix = 0; ix < sizeof buffers/sizeof buffers[0]; ++ix) {
     408               1 :       if (0 < verbosity) {
     409               0 :         printf("Using %s buffer\n", buffers[ix].description);
     410                 :       }
     411                 :       nbytes = NaClHostDirGetdents(&nhd,
     412                 :                                    aligned_buffer,
     413               1 :                                    buffers[ix].buffer_bytes);
     414               1 :       if (0 == nbytes) {
     415               1 :         if (0 != ix) {
     416               0 :           fprintf(stderr, "EOF on larger buffer but not smallest?!?\n");
     417               0 :           retval = 5;
     418               0 :           goto cleanup;
     419               1 :         }
     420                 :         goto double_break;
     421               1 :       } else if (0 < nbytes) {
     422               1 :         error_count += ExtractNames(&actual, aligned_buffer, nbytes, test_dir);
     423               1 :         break;
     424                 :       }
     425                 :       /* nbytes < 0 must hold */
     426               1 :       if (-NACL_ABI_EINVAL != nbytes) {
     427               0 :         fprintf(stderr, "Unexpected error return %d\n", retval);
     428               0 :         retval = 6;
     429               0 :         goto cleanup;
     430                 :       }
     431               1 :       if (ix + 1 == sizeof buffers/sizeof buffers[0]) {
     432               0 :         fprintf(stderr, "Largest buffer insufficient!?!\n");
     433               0 :         retval = 7;
     434               0 :         goto cleanup;
     435                 :       }
     436               1 :     }
     437               1 :   }
     438                 : double_break:
     439               1 :   StringArraySort(&actual);
     440                 : 
     441               1 :   printf("\n\nActual directory contents:\n\n");
     442               1 :   for (ix = 0; ix < actual.nelts; ++ix) {
     443               1 :     printf("%s\n", actual.strings[ix]);
     444               1 :   }
     445                 : 
     446               1 :   printf("Comparing...\n");
     447               1 :   retval = 0;
     448               1 :   if (expected.nelts != actual.nelts) {
     449                 :     fprintf(stderr,
     450                 :             "Number of directory entries differ"
     451                 :             " (%"NACL_PRIuS" expected, vs %"NACL_PRIuS" actual)\n",
     452               0 :             expected.nelts, actual.nelts);
     453               0 :     retval = 8;
     454                 :   }
     455                 : 
     456               1 :   min_elts = (expected.nelts > actual.nelts) ? actual.nelts : expected.nelts;
     457                 : 
     458               1 :   for (ix = 0; ix < min_elts; ++ix) {
     459               1 :     if (0 != strcmp(expected.strings[ix], actual.strings[ix])) {
     460                 :       fprintf(stderr,
     461                 :               "Entry %"NACL_PRIuS" differs: expected %s, actual %s\n",
     462               0 :               ix, expected.strings[ix], actual.strings[ix]);
     463               0 :       retval = 9;
     464                 :     }
     465               1 :   }
     466               1 :   if (0 == retval && 0 != error_count) {
     467               0 :     retval = 10;
     468                 :   }
     469                 : cleanup:
     470               1 :   if (0 != CleanupDirectory(test_dir, &expected)) {
     471               0 :     if (0 == retval) {
     472               0 :       retval = 11;
     473                 :     }
     474                 :   }
     475                 : 
     476                 :   /*
     477                 :    * Negative tests:  open file as dir, expect consistent error.
     478                 :    */
     479                 : 
     480               1 :   printf(0 == retval ? "PASS\n" : "FAIL\n");
     481                 : 
     482               1 :   return retval;
     483               1 : }

Generated by: LCOV version 1.7