modernc.org/cc@v1.0.1/v2/testdata/_sqlite/src/test_syscall.c (about)

     1  /*
     2  ** 2011 March 28
     3  **
     4  ** The author disclaims copyright to this source code.  In place of
     5  ** a legal notice, here is a blessing:
     6  **
     7  **    May you do good and not evil.
     8  **    May you find forgiveness for yourself and forgive others.
     9  **    May you share freely, never taking more than you give.
    10  **
    11  *************************************************************************
    12  **
    13  ** The code in this file implements a Tcl interface used to test error
    14  ** handling in the os_unix.c module. Wrapper functions that support fault
    15  ** injection are registered as the low-level OS functions using the 
    16  ** xSetSystemCall() method of the VFS. The Tcl interface is as follows:
    17  **
    18  **
    19  **   test_syscall install LIST
    20  **     Install wrapper functions for all system calls in argument LIST.
    21  **     LIST must be a list consisting of zero or more of the following
    22  **     literal values:
    23  **
    24  **         open        close      access   getcwd   stat      fstat    
    25  **         ftruncate   fcntl      read     pread    pread64   write
    26  **         pwrite      pwrite64   fchmod   fallocate mmap
    27  **
    28  **   test_syscall uninstall
    29  **     Uninstall all wrapper functions.
    30  **
    31  **   test_syscall fault ?COUNT PERSIST?
    32  **     If [test_syscall fault] is invoked without the two arguments, fault
    33  **     injection is disabled. Otherwise, fault injection is configured to
    34  **     cause a failure on the COUNT'th next call to a system call with a
    35  **     wrapper function installed. A COUNT value of 1 means fail the next
    36  **     system call. 
    37  ** 
    38  **     Argument PERSIST is interpreted as a boolean. If true, the all
    39  **     system calls following the initial failure also fail. Otherwise, only
    40  **     the single transient failure is injected.
    41  **
    42  **   test_syscall errno CALL ERRNO
    43  **     Set the value that the global "errno" is set to following a fault
    44  **     in call CALL. Argument CALL must be one of the system call names
    45  **     listed above (under [test_syscall install]). ERRNO is a symbolic
    46  **     name (i.e. "EACCES"). Not all errno codes are supported. Add extra
    47  **     to the aErrno table in function test_syscall_errno() below as 
    48  **     required.
    49  **
    50  **   test_syscall reset ?SYSTEM-CALL?
    51  **     With no argument, this is an alias for the [uninstall] command. However,
    52  **     this command uses a VFS call of the form:
    53  **
    54  **       xSetSystemCall(pVfs, 0, 0);
    55  **
    56  **     To restore the default system calls. The [uninstall] command restores
    57  **     each system call individually by calling (i.e.):
    58  **
    59  **       xSetSystemCall(pVfs, "open", 0);
    60  **
    61  **     With an argument, this command attempts to reset the system call named
    62  **     by the parameter using the same method as [uninstall].
    63  **
    64  **   test_syscall exists SYSTEM-CALL
    65  **     Return true if the named system call exists. Or false otherwise.
    66  **
    67  **   test_syscall list
    68  **     Return a list of all system calls. The list is constructed using
    69  **     the xNextSystemCall() VFS method.
    70  **
    71  **   test_syscall pagesize PGSZ
    72  **     If PGSZ is a power of two greater than 256, install a wrapper around
    73  **     OS function getpagesize() that reports the system page size as PGSZ.
    74  **     Or, if PGSZ is less than zero, remove any wrapper already installed.
    75  */
    76  
    77  #include "sqliteInt.h"
    78  #include "sqlite3.h"
    79  #if defined(INCLUDE_SQLITE_TCL_H)
    80  #  include "sqlite_tcl.h"
    81  #else
    82  #  include "tcl.h"
    83  #endif
    84  #include <stdlib.h>
    85  #include <string.h>
    86  #include <assert.h>
    87  
    88  #if SQLITE_OS_UNIX
    89  
    90  /* From main.c */
    91  extern const char *sqlite3ErrName(int);
    92  
    93  #include <sys/mman.h>
    94  #include <sys/types.h>
    95  #include <errno.h>
    96  
    97  static struct TestSyscallGlobal {
    98    int bPersist;                   /* 1 for persistent errors, 0 for transient */
    99    int nCount;                     /* Fail after this many more calls */
   100    int nFail;                      /* Number of failures that have occurred */
   101    int pgsz;
   102    sqlite3_syscall_ptr orig_getpagesize;
   103  } gSyscall = { 0, 0, 0, 0, 0 };
   104  
   105  static int ts_open(const char *, int, int);
   106  static int ts_close(int fd);
   107  static int ts_access(const char *zPath, int mode);
   108  static char *ts_getcwd(char *zPath, size_t nPath);
   109  static int ts_stat(const char *zPath, struct stat *p);
   110  static int ts_fstat(int fd, struct stat *p);
   111  static int ts_ftruncate(int fd, off_t n);
   112  static int ts_fcntl(int fd, int cmd, ... );
   113  static int ts_read(int fd, void *aBuf, size_t nBuf);
   114  static int ts_pread(int fd, void *aBuf, size_t nBuf, off_t off);
   115  /* Note:  pread64() and pwrite64() actually use off64_t as the type on their
   116  ** last parameter.  But that datatype is not defined on many systems 
   117  ** (ex: Mac, OpenBSD).  So substitute a likely equivalent: sqlite3_uint64 */
   118  static int ts_pread64(int fd, void *aBuf, size_t nBuf, sqlite3_uint64 off);
   119  static int ts_write(int fd, const void *aBuf, size_t nBuf);
   120  static int ts_pwrite(int fd, const void *aBuf, size_t nBuf, off_t off);
   121  static int ts_pwrite64(int fd, const void *aBuf, size_t nBuf, sqlite3_uint64 off);
   122  static int ts_fchmod(int fd, mode_t mode);
   123  static int ts_fallocate(int fd, off_t off, off_t len);
   124  static void *ts_mmap(void *, size_t, int, int, int, off_t);
   125  static void *ts_mremap(void*, size_t, size_t, int, ...);
   126  
   127  struct TestSyscallArray {
   128    const char *zName;
   129    sqlite3_syscall_ptr xTest;
   130    sqlite3_syscall_ptr xOrig;
   131    int default_errno;              /* Default value for errno following errors */
   132    int custom_errno;               /* Current value for errno if error */
   133  } aSyscall[] = {
   134    /*  0 */ { "open",      (sqlite3_syscall_ptr)ts_open,      0, EACCES, 0 },
   135    /*  1 */ { "close",     (sqlite3_syscall_ptr)ts_close,     0, 0, 0 },
   136    /*  2 */ { "access",    (sqlite3_syscall_ptr)ts_access,    0, 0, 0 },
   137    /*  3 */ { "getcwd",    (sqlite3_syscall_ptr)ts_getcwd,    0, 0, 0 },
   138    /*  4 */ { "stat",      (sqlite3_syscall_ptr)ts_stat,      0, 0, 0 },
   139    /*  5 */ { "fstat",     (sqlite3_syscall_ptr)ts_fstat,     0, 0, 0 },
   140    /*  6 */ { "ftruncate", (sqlite3_syscall_ptr)ts_ftruncate, 0, EIO, 0 },
   141    /*  7 */ { "fcntl",     (sqlite3_syscall_ptr)ts_fcntl,     0, EACCES, 0 },
   142    /*  8 */ { "read",      (sqlite3_syscall_ptr)ts_read,      0, 0, 0 },
   143    /*  9 */ { "pread",     (sqlite3_syscall_ptr)ts_pread,     0, 0, 0 },
   144    /* 10 */ { "pread64",   (sqlite3_syscall_ptr)ts_pread64,   0, 0, 0 },
   145    /* 11 */ { "write",     (sqlite3_syscall_ptr)ts_write,     0, 0, 0 },
   146    /* 12 */ { "pwrite",    (sqlite3_syscall_ptr)ts_pwrite,    0, 0, 0 },
   147    /* 13 */ { "pwrite64",  (sqlite3_syscall_ptr)ts_pwrite64,  0, 0, 0 },
   148    /* 14 */ { "fchmod",    (sqlite3_syscall_ptr)ts_fchmod,    0, 0, 0 },
   149    /* 15 */ { "fallocate", (sqlite3_syscall_ptr)ts_fallocate, 0, 0, 0 },
   150    /* 16 */ { "mmap",      (sqlite3_syscall_ptr)ts_mmap,      0, 0, 0 },
   151    /* 17 */ { "mremap",    (sqlite3_syscall_ptr)ts_mremap,    0, 0, 0 },
   152             { 0, 0, 0, 0, 0 }
   153  };
   154  
   155  #define orig_open      ((int(*)(const char *, int, int))aSyscall[0].xOrig)
   156  #define orig_close     ((int(*)(int))aSyscall[1].xOrig)
   157  #define orig_access    ((int(*)(const char*,int))aSyscall[2].xOrig)
   158  #define orig_getcwd    ((char*(*)(char*,size_t))aSyscall[3].xOrig)
   159  #define orig_stat      ((int(*)(const char*,struct stat*))aSyscall[4].xOrig)
   160  #define orig_fstat     ((int(*)(int,struct stat*))aSyscall[5].xOrig)
   161  #define orig_ftruncate ((int(*)(int,off_t))aSyscall[6].xOrig)
   162  #define orig_fcntl     ((int(*)(int,int,...))aSyscall[7].xOrig)
   163  #define orig_read      ((ssize_t(*)(int,void*,size_t))aSyscall[8].xOrig)
   164  #define orig_pread     ((ssize_t(*)(int,void*,size_t,off_t))aSyscall[9].xOrig)
   165  #define orig_pread64   ((ssize_t(*)(int,void*,size_t,sqlite3_uint64))aSyscall[10].xOrig)
   166  #define orig_write     ((ssize_t(*)(int,const void*,size_t))aSyscall[11].xOrig)
   167  #define orig_pwrite    ((ssize_t(*)(int,const void*,size_t,off_t))\
   168                         aSyscall[12].xOrig)
   169  #define orig_pwrite64  ((ssize_t(*)(int,const void*,size_t,sqlite3_uint64))\
   170                         aSyscall[13].xOrig)
   171  #define orig_fchmod    ((int(*)(int,mode_t))aSyscall[14].xOrig)
   172  #define orig_fallocate ((int(*)(int,off_t,off_t))aSyscall[15].xOrig)
   173  #define orig_mmap      ((void*(*)(void*,size_t,int,int,int,off_t))aSyscall[16].xOrig)
   174  #define orig_mremap    ((void*(*)(void*,size_t,size_t,int,...))aSyscall[17].xOrig)
   175  
   176  /*
   177  ** This function is called exactly once from within each invocation of a
   178  ** system call wrapper in this file. It returns 1 if the function should
   179  ** fail, or 0 if it should succeed.
   180  */
   181  static int tsIsFail(void){
   182    gSyscall.nCount--;
   183    if( gSyscall.nCount==0 || (gSyscall.nFail && gSyscall.bPersist) ){
   184      gSyscall.nFail++;
   185      return 1;
   186    }
   187    return 0;
   188  }
   189  
   190  /*
   191  ** Return the current error-number value for function zFunc. zFunc must be
   192  ** the name of a system call in the aSyscall[] table.
   193  **
   194  ** Usually, the current error-number is the value that errno should be set
   195  ** to if the named system call fails. The exception is "fallocate". See 
   196  ** comments above the implementation of ts_fallocate() for details.
   197  */
   198  static int tsErrno(const char *zFunc){
   199    int i;
   200    int nFunc = strlen(zFunc);
   201    for(i=0; aSyscall[i].zName; i++){
   202      if( strlen(aSyscall[i].zName)!=nFunc ) continue;
   203      if( memcmp(aSyscall[i].zName, zFunc, nFunc) ) continue;
   204      return aSyscall[i].custom_errno;
   205    }
   206  
   207    assert(0);
   208    return 0;
   209  }
   210  
   211  /*
   212  ** A wrapper around tsIsFail(). If tsIsFail() returns non-zero, set the
   213  ** value of errno before returning.
   214  */ 
   215  static int tsIsFailErrno(const char *zFunc){
   216    if( tsIsFail() ){
   217      errno = tsErrno(zFunc);
   218      return 1;
   219    }
   220    return 0;
   221  }
   222  
   223  /*
   224  ** A wrapper around open().
   225  */
   226  static int ts_open(const char *zFile, int flags, int mode){
   227    if( tsIsFailErrno("open") ){
   228      return -1;
   229    }
   230    return orig_open(zFile, flags, mode);
   231  }
   232  
   233  /*
   234  ** A wrapper around close().
   235  */
   236  static int ts_close(int fd){
   237    if( tsIsFail() ){
   238      /* Even if simulating an error, close the original file-descriptor. 
   239      ** This is to stop the test process from running out of file-descriptors
   240      ** when running a long test. If a call to close() appears to fail, SQLite
   241      ** never attempts to use the file-descriptor afterwards (or even to close
   242      ** it a second time).  */
   243      orig_close(fd);
   244      return -1;
   245    }
   246    return orig_close(fd);
   247  }
   248  
   249  /*
   250  ** A wrapper around access().
   251  */
   252  static int ts_access(const char *zPath, int mode){
   253    if( tsIsFail() ){
   254      return -1;
   255    }
   256    return orig_access(zPath, mode);
   257  }
   258  
   259  /*
   260  ** A wrapper around getcwd().
   261  */
   262  static char *ts_getcwd(char *zPath, size_t nPath){
   263    if( tsIsFail() ){
   264      return NULL;
   265    }
   266    return orig_getcwd(zPath, nPath);
   267  }
   268  
   269  /*
   270  ** A wrapper around stat().
   271  */
   272  static int ts_stat(const char *zPath, struct stat *p){
   273    if( tsIsFail() ){
   274      return -1;
   275    }
   276    return orig_stat(zPath, p);
   277  }
   278  
   279  /*
   280  ** A wrapper around fstat().
   281  */
   282  static int ts_fstat(int fd, struct stat *p){
   283    if( tsIsFailErrno("fstat") ){
   284      return -1;
   285    }
   286    return orig_fstat(fd, p);
   287  }
   288  
   289  /*
   290  ** A wrapper around ftruncate().
   291  */
   292  static int ts_ftruncate(int fd, off_t n){
   293    if( tsIsFailErrno("ftruncate") ){
   294      return -1;
   295    }
   296    return orig_ftruncate(fd, n);
   297  }
   298  
   299  /*
   300  ** A wrapper around fcntl().
   301  */
   302  static int ts_fcntl(int fd, int cmd, ... ){
   303    va_list ap;
   304    void *pArg;
   305    if( tsIsFailErrno("fcntl") ){
   306      return -1;
   307    }
   308    va_start(ap, cmd);
   309    pArg = va_arg(ap, void *);
   310    return orig_fcntl(fd, cmd, pArg);
   311  }
   312  
   313  /*
   314  ** A wrapper around read().
   315  */
   316  static int ts_read(int fd, void *aBuf, size_t nBuf){
   317    if( tsIsFailErrno("read") ){
   318      return -1;
   319    }
   320    return orig_read(fd, aBuf, nBuf);
   321  }
   322  
   323  /*
   324  ** A wrapper around pread().
   325  */
   326  static int ts_pread(int fd, void *aBuf, size_t nBuf, off_t off){
   327    if( tsIsFailErrno("pread") ){
   328      return -1;
   329    }
   330    return orig_pread(fd, aBuf, nBuf, off);
   331  }
   332  
   333  /*
   334  ** A wrapper around pread64().
   335  */
   336  static int ts_pread64(int fd, void *aBuf, size_t nBuf, sqlite3_uint64 off){
   337    if( tsIsFailErrno("pread64") ){
   338      return -1;
   339    }
   340    return orig_pread64(fd, aBuf, nBuf, off);
   341  }
   342  
   343  /*
   344  ** A wrapper around write().
   345  */
   346  static int ts_write(int fd, const void *aBuf, size_t nBuf){
   347    if( tsIsFailErrno("write") ){
   348      if( tsErrno("write")==EINTR ) orig_write(fd, aBuf, nBuf/2);
   349      return -1;
   350    }
   351    return orig_write(fd, aBuf, nBuf);
   352  }
   353  
   354  /*
   355  ** A wrapper around pwrite().
   356  */
   357  static int ts_pwrite(int fd, const void *aBuf, size_t nBuf, off_t off){
   358    if( tsIsFailErrno("pwrite") ){
   359      return -1;
   360    }
   361    return orig_pwrite(fd, aBuf, nBuf, off);
   362  }
   363  
   364  /*
   365  ** A wrapper around pwrite64().
   366  */
   367  static int ts_pwrite64(int fd, const void *aBuf, size_t nBuf, sqlite3_uint64 off){
   368    if( tsIsFailErrno("pwrite64") ){
   369      return -1;
   370    }
   371    return orig_pwrite64(fd, aBuf, nBuf, off);
   372  }
   373  
   374  /*
   375  ** A wrapper around fchmod().
   376  */
   377  static int ts_fchmod(int fd, mode_t mode){
   378    if( tsIsFail() ){
   379      return -1;
   380    }
   381    return orig_fchmod(fd, mode);
   382  }
   383  
   384  /*
   385  ** A wrapper around fallocate().
   386  **
   387  ** SQLite assumes that the fallocate() function is compatible with
   388  ** posix_fallocate(). According to the Linux man page (2009-09-30):
   389  **
   390  **   posix_fallocate() returns  zero on success, or an error number on
   391  **   failure. Note that errno is not set.
   392  */
   393  static int ts_fallocate(int fd, off_t off, off_t len){
   394    if( tsIsFail() ){
   395      return tsErrno("fallocate");
   396    }
   397    return orig_fallocate(fd, off, len);
   398  }
   399  
   400  static void *ts_mmap(
   401    void *pAddr, 
   402    size_t nByte, 
   403    int prot, 
   404    int flags, 
   405    int fd, 
   406    off_t iOff
   407  ){
   408    if( tsIsFailErrno("mmap") ){
   409      return MAP_FAILED;
   410    }
   411    return orig_mmap(pAddr, nByte, prot, flags, fd, iOff);
   412  }
   413  
   414  static void *ts_mremap(void *a, size_t b, size_t c, int d, ...){
   415    va_list ap;
   416    void *pArg;
   417    if( tsIsFailErrno("mremap") ){
   418      return MAP_FAILED;
   419    }
   420    va_start(ap, d);
   421    pArg = va_arg(ap, void *);
   422    return orig_mremap(a, b, c, d, pArg);
   423  }
   424  
   425  static int SQLITE_TCLAPI test_syscall_install(
   426    void * clientData,
   427    Tcl_Interp *interp,
   428    int objc,
   429    Tcl_Obj *CONST objv[]
   430  ){
   431    sqlite3_vfs *pVfs; 
   432    int nElem;
   433    int i;
   434    Tcl_Obj **apElem;
   435  
   436    if( objc!=3 ){
   437      Tcl_WrongNumArgs(interp, 2, objv, "SYSCALL-LIST");
   438      return TCL_ERROR;
   439    }
   440    if( Tcl_ListObjGetElements(interp, objv[2], &nElem, &apElem) ){
   441      return TCL_ERROR;
   442    }
   443    pVfs = sqlite3_vfs_find(0);
   444  
   445    for(i=0; i<nElem; i++){
   446      int iCall;
   447      int rc = Tcl_GetIndexFromObjStruct(interp, 
   448          apElem[i], aSyscall, sizeof(aSyscall[0]), "system-call", 0, &iCall
   449      );
   450      if( rc ) return rc;
   451      if( aSyscall[iCall].xOrig==0 ){
   452        aSyscall[iCall].xOrig = pVfs->xGetSystemCall(pVfs, aSyscall[iCall].zName);
   453        pVfs->xSetSystemCall(pVfs, aSyscall[iCall].zName, aSyscall[iCall].xTest);
   454      }
   455      aSyscall[iCall].custom_errno = aSyscall[iCall].default_errno;
   456    }
   457  
   458    return TCL_OK;
   459  }
   460  
   461  static int SQLITE_TCLAPI test_syscall_uninstall(
   462    void * clientData,
   463    Tcl_Interp *interp,
   464    int objc,
   465    Tcl_Obj *CONST objv[]
   466  ){
   467    sqlite3_vfs *pVfs; 
   468    int i;
   469  
   470    if( objc!=2 ){
   471      Tcl_WrongNumArgs(interp, 2, objv, "");
   472      return TCL_ERROR;
   473    }
   474  
   475    pVfs = sqlite3_vfs_find(0);
   476    for(i=0; aSyscall[i].zName; i++){
   477      if( aSyscall[i].xOrig ){
   478        pVfs->xSetSystemCall(pVfs, aSyscall[i].zName, 0);
   479        aSyscall[i].xOrig = 0;
   480      }
   481    }
   482    return TCL_OK;
   483  }
   484  
   485  static int SQLITE_TCLAPI test_syscall_reset(
   486    void * clientData,
   487    Tcl_Interp *interp,
   488    int objc,
   489    Tcl_Obj *CONST objv[]
   490  ){
   491    sqlite3_vfs *pVfs; 
   492    int i;
   493    int rc;
   494  
   495    if( objc!=2 && objc!=3 ){
   496      Tcl_WrongNumArgs(interp, 2, objv, "");
   497      return TCL_ERROR;
   498    }
   499  
   500    pVfs = sqlite3_vfs_find(0);
   501    if( objc==2 ){
   502      rc = pVfs->xSetSystemCall(pVfs, 0, 0);
   503      for(i=0; aSyscall[i].zName; i++) aSyscall[i].xOrig = 0;
   504    }else{
   505      int nFunc;
   506      char *zFunc = Tcl_GetStringFromObj(objv[2], &nFunc);
   507      rc = pVfs->xSetSystemCall(pVfs, Tcl_GetString(objv[2]), 0);
   508      for(i=0; rc==SQLITE_OK && aSyscall[i].zName; i++){
   509        if( strlen(aSyscall[i].zName)!=nFunc ) continue;
   510        if( memcmp(aSyscall[i].zName, zFunc, nFunc) ) continue;
   511        aSyscall[i].xOrig = 0;
   512      }
   513    }
   514    if( rc!=SQLITE_OK ){
   515      Tcl_SetObjResult(interp, Tcl_NewStringObj(sqlite3ErrName(rc), -1));
   516      return TCL_ERROR;
   517    }
   518  
   519    Tcl_ResetResult(interp);
   520    return TCL_OK;
   521  }
   522  
   523  static int SQLITE_TCLAPI test_syscall_exists(
   524    void * clientData,
   525    Tcl_Interp *interp,
   526    int objc,
   527    Tcl_Obj *CONST objv[]
   528  ){
   529    sqlite3_vfs *pVfs; 
   530    sqlite3_syscall_ptr x;
   531  
   532    if( objc!=3 ){
   533      Tcl_WrongNumArgs(interp, 2, objv, "");
   534      return TCL_ERROR;
   535    }
   536  
   537    pVfs = sqlite3_vfs_find(0);
   538    x = pVfs->xGetSystemCall(pVfs, Tcl_GetString(objv[2]));
   539  
   540    Tcl_SetObjResult(interp, Tcl_NewBooleanObj(x!=0));
   541    return TCL_OK;
   542  }
   543  
   544  static int SQLITE_TCLAPI test_syscall_fault(
   545    void * clientData,
   546    Tcl_Interp *interp,
   547    int objc,
   548    Tcl_Obj *CONST objv[]
   549  ){
   550    int nCount = 0;
   551    int bPersist = 0;
   552  
   553    if( objc!=2 && objc!=4 ){
   554      Tcl_WrongNumArgs(interp, 2, objv, "?COUNT PERSIST?");
   555      return TCL_ERROR;
   556    }
   557  
   558    if( objc==4 ){
   559      if( Tcl_GetIntFromObj(interp, objv[2], &nCount)
   560       || Tcl_GetBooleanFromObj(interp, objv[3], &bPersist)
   561      ){
   562        return TCL_ERROR;
   563      }
   564    }
   565  
   566    Tcl_SetObjResult(interp, Tcl_NewIntObj(gSyscall.nFail));
   567    gSyscall.nCount = nCount;
   568    gSyscall.bPersist = bPersist;
   569    gSyscall.nFail = 0;
   570    return TCL_OK;
   571  }
   572  
   573  static int SQLITE_TCLAPI test_syscall_errno(
   574    void * clientData,
   575    Tcl_Interp *interp,
   576    int objc,
   577    Tcl_Obj *CONST objv[]
   578  ){
   579    int iCall;
   580    int iErrno;
   581    int rc;
   582  
   583    struct Errno {
   584      const char *z;
   585      int i;
   586    } aErrno[] = {
   587      { "EACCES",    EACCES },
   588      { "EINTR",     EINTR },
   589      { "EIO",       EIO },
   590      { "EOVERFLOW", EOVERFLOW },
   591      { "ENOMEM",    ENOMEM },
   592      { "EAGAIN",    EAGAIN },
   593      { "ETIMEDOUT", ETIMEDOUT },
   594      { "EBUSY",     EBUSY },
   595      { "EPERM",     EPERM },
   596      { "EDEADLK",   EDEADLK },
   597      { "ENOLCK",    ENOLCK },
   598      { 0, 0 }
   599    };
   600  
   601    if( objc!=4 ){
   602      Tcl_WrongNumArgs(interp, 2, objv, "SYSCALL ERRNO");
   603      return TCL_ERROR;
   604    }
   605  
   606    rc = Tcl_GetIndexFromObjStruct(interp, 
   607        objv[2], aSyscall, sizeof(aSyscall[0]), "system-call", 0, &iCall
   608    );
   609    if( rc!=TCL_OK ) return rc;
   610    rc = Tcl_GetIndexFromObjStruct(interp, 
   611        objv[3], aErrno, sizeof(aErrno[0]), "errno", 0, &iErrno
   612    );
   613    if( rc!=TCL_OK ) return rc;
   614  
   615    aSyscall[iCall].custom_errno = aErrno[iErrno].i;
   616    return TCL_OK;
   617  }
   618  
   619  static int SQLITE_TCLAPI test_syscall_list(
   620    void * clientData,
   621    Tcl_Interp *interp,
   622    int objc,
   623    Tcl_Obj *CONST objv[]
   624  ){
   625    const char *zSys;
   626    sqlite3_vfs *pVfs; 
   627    Tcl_Obj *pList;
   628  
   629    if( objc!=2 ){
   630      Tcl_WrongNumArgs(interp, 2, objv, "");
   631      return TCL_ERROR;
   632    }
   633  
   634    pVfs = sqlite3_vfs_find(0);
   635    pList = Tcl_NewObj();
   636    Tcl_IncrRefCount(pList);
   637    for(zSys = pVfs->xNextSystemCall(pVfs, 0); 
   638        zSys!=0;
   639        zSys = pVfs->xNextSystemCall(pVfs, zSys)
   640    ){
   641      Tcl_ListObjAppendElement(interp, pList, Tcl_NewStringObj(zSys, -1));
   642    }
   643  
   644    Tcl_SetObjResult(interp, pList);
   645    Tcl_DecrRefCount(pList);
   646    return TCL_OK;
   647  }
   648  
   649  static int SQLITE_TCLAPI test_syscall_defaultvfs(
   650    void * clientData,
   651    Tcl_Interp *interp,
   652    int objc,
   653    Tcl_Obj *CONST objv[]
   654  ){
   655    sqlite3_vfs *pVfs; 
   656  
   657    if( objc!=2 ){
   658      Tcl_WrongNumArgs(interp, 2, objv, "");
   659      return TCL_ERROR;
   660    }
   661  
   662    pVfs = sqlite3_vfs_find(0);
   663    Tcl_SetObjResult(interp, Tcl_NewStringObj(pVfs->zName, -1));
   664    return TCL_OK;
   665  }
   666  
   667  static int ts_getpagesize(void){
   668    return gSyscall.pgsz;
   669  }
   670  
   671  static int SQLITE_TCLAPI test_syscall_pagesize(
   672    void * clientData,
   673    Tcl_Interp *interp,
   674    int objc,
   675    Tcl_Obj *CONST objv[]
   676  ){
   677    sqlite3_vfs *pVfs = sqlite3_vfs_find(0);
   678    int pgsz;
   679    if( objc!=3 ){
   680      Tcl_WrongNumArgs(interp, 2, objv, "PGSZ");
   681      return TCL_ERROR;
   682    }
   683    if( Tcl_GetIntFromObj(interp, objv[2], &pgsz) ){
   684      return TCL_ERROR;
   685    }
   686  
   687    if( pgsz<0 ){
   688      if( gSyscall.orig_getpagesize ){
   689        pVfs->xSetSystemCall(pVfs, "getpagesize", gSyscall.orig_getpagesize);
   690      }
   691    }else{
   692      if( pgsz<512 || (pgsz & (pgsz-1)) ){
   693        Tcl_AppendResult(interp, "pgsz out of range", 0);
   694        return TCL_ERROR;
   695      }
   696      gSyscall.orig_getpagesize = pVfs->xGetSystemCall(pVfs, "getpagesize");
   697      gSyscall.pgsz = pgsz;
   698      pVfs->xSetSystemCall(
   699          pVfs, "getpagesize", (sqlite3_syscall_ptr)ts_getpagesize
   700      );
   701    }
   702  
   703    return TCL_OK;
   704  }
   705  
   706  static int SQLITE_TCLAPI test_syscall(
   707    void * clientData,
   708    Tcl_Interp *interp,
   709    int objc,
   710    Tcl_Obj *CONST objv[]
   711  ){
   712    struct SyscallCmd {
   713      const char *zName;
   714      Tcl_ObjCmdProc *xCmd;
   715    } aCmd[] = {
   716      { "fault",      test_syscall_fault },
   717      { "install",    test_syscall_install },
   718      { "uninstall",  test_syscall_uninstall },
   719      { "reset",      test_syscall_reset },
   720      { "errno",      test_syscall_errno },
   721      { "exists",     test_syscall_exists },
   722      { "list",       test_syscall_list },
   723      { "defaultvfs", test_syscall_defaultvfs },
   724      { "pagesize",   test_syscall_pagesize },
   725      { 0, 0 }
   726    };
   727    int iCmd;
   728    int rc;
   729    sqlite3_vfs *pVfs = sqlite3_vfs_find(0);
   730  
   731    if( objc<2 ){
   732      Tcl_WrongNumArgs(interp, 1, objv, "SUB-COMMAND ...");
   733      return TCL_ERROR;
   734    }
   735    if( pVfs->iVersion<3 || pVfs->xSetSystemCall==0 ){
   736      Tcl_AppendResult(interp, "VFS does not support xSetSystemCall", 0);
   737      rc = TCL_ERROR;
   738    }else{
   739      rc = Tcl_GetIndexFromObjStruct(interp, 
   740          objv[1], aCmd, sizeof(aCmd[0]), "sub-command", 0, &iCmd
   741      );
   742    }
   743    if( rc!=TCL_OK ) return rc;
   744    return aCmd[iCmd].xCmd(clientData, interp, objc, objv);
   745  }
   746  
   747  int SqlitetestSyscall_Init(Tcl_Interp *interp){
   748    struct SyscallCmd {
   749      const char *zName;
   750      Tcl_ObjCmdProc *xCmd;
   751    } aCmd[] = {
   752      { "test_syscall",     test_syscall},
   753    };
   754    int i;
   755  
   756    for(i=0; i<sizeof(aCmd)/sizeof(aCmd[0]); i++){
   757      Tcl_CreateObjCommand(interp, aCmd[i].zName, aCmd[i].xCmd, 0, 0);
   758    }
   759    return TCL_OK;
   760  }
   761  #else
   762  int SqlitetestSyscall_Init(Tcl_Interp *interp){
   763    return TCL_OK;
   764  }
   765  #endif