view src/cs/drivers/drv_app/ffs/board/tffs.c @ 636:57e67ca2e1cb

pcmdata.c: default +CGMI to "FreeCalypso" and +CGMM to model The present change has no effect whatsoever on Falconia-made and Openmoko-made devices on which /pcm/CGMI and /pcm/CGMM files have been programmed in FFS with sensible ID strings by the respective factories, but what should AT+CGMI and AT+CGMM queries return when the device is a Huawei GTM900 or Tango modem that has been converted to FreeCalypso with a firmware change? Before the present change they would return compiled-in defaults of "<manufacturer>" and "<model>", respectively; with the present change the firmware will self-identify as "FreeCalypso GTM900-FC" or "FreeCalypso Tango" on the two respective targets. This firmware identification will become important if someone incorporates an FC-converted GTM900 or Tango modem into a ZeroPhone-style smartphone where some high-level software like ofono will be talking to the modem and will need to properly identify this modem as FreeCalypso, as opposed to some other AT command modem flavor with different quirks. In technical terms, the compiled-in default for the AT+CGMI query (which will always be overridden by the /pcm/CGMI file in FFS if one is present) is now "FreeCalypso" in all configs on all targets; the compiled-in default for the AT+CGMM query (likewise always overridden by /pcm/CGMM if present) is "GTM900-FC" if CONFIG_TARGET_GTM900 or "Tango" if CONFIG_TARGET_TANGO or the original default of "<model>" otherwise.
author Mychaela Falconia <falcon@freecalypso.org>
date Sun, 19 Jan 2020 20:14:58 +0000
parents 945cf7f506b2
children
line wrap: on
line source

  /******************************************************************************
 * Flash File System (ffs)
 * Idea, design and coding by Mads Meisner-Jensen, mmj@ti.com
 *
 * ffs test scaffold/framework
 *
 * $Id: tffs.c 1.12.1.1.1.20 Fri, 19 Dec 2003 12:00:13 +0100 tsj $
 *
 ******************************************************************************/

#ifndef TARGET
#include "ffs.cfg"
#endif

#include "ffs/ffs.h"
#include "ffs/board/core.h"
#include "ffs/board/tffs.h"
#include "ffs/board/tdata.h"
#include "ffs/board/tmffs.h"
#include "ffs/board/ffstrace.h"

#if (TARGET == 1)
#include "ffs/board/task.h"
#endif

#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <ctype.h>


/******************************************************************************
 * Prototypes and Globals
 ******************************************************************************/

struct dir_s dir;
struct stat_s stat;
struct xstat_s xstat;

int error;
#if (TARGET == 1)
int smallbuf_size = 512;
int bigbuf_size = 65536;

#else
int smallbuf_size = 1024;
int bigbuf_size = 1024*1024*8;
#endif

char *smallbuf = 0;
char *bigbuf = 0;


/******************************************************************************
 * Globals and Main
 ******************************************************************************/

extern struct testcase_s testcase[];

struct test_s {
    char *name;     // name of currently executing test case
    int  numcases;  // number of test cases run so far
    int  numcalls;  // total number of ffs function calls so far
    int  numfails;  // total number of failed test cases
    int  keepgoing; // keep going when a test case fails
} test;

struct ffs_params_s param;


/******************************************************************************
 * Main Functions
 ******************************************************************************/

effs_t ffs_initialize(void);
effs_t ffs_exit(void);

// Each test case returns zero on success, non-zero on failure.
// test_execute() decides whether to continue with remaining test cases or
// not.

#if (TARGET == 0)
void test_listall(void)
{
    struct testcase_s *p;

    printf("Test Cases:\n");
    for (p = testcase; p->name != 0; p++) {
        printf("%8s: %s\n", p->name, p->comment);
    }
}
#endif

// <tests> is a comma-separated string of names of the test cases to
// run. Return number of test cases that failed. This is typically only 1,
// unless arg_keepgoing is set in which case it can be several.
int test_run(char *tests)
{
    struct testcase_s *ptc;
    int i, failed, tparams[FFS_TESTCASE_PARAMS_MAX];
    struct this_test_s this;
    char *pname, *testsnew;

    failed = 0;

    while (*tests != 0 && (failed == 0 || test.keepgoing))
    {
        // Make local copy of test case name. We have to make local copies
        // because we can be recursively called
        pname = this.name;
        while (isalpha(*tests) && *tests != 0 && *tests != ';')
            *pname++ = *tests++;
        *pname = 0;

        // Reset test case parameter(s)
        for (i = 0; i < FFS_TESTCASE_PARAMS_MAX; i++)
            tparams[i] = 0;

        // Collect parameter(s) for test case
        i = 0;
        while (isdigit(*tests)) {
            tparams[i] = strtol(tests, &testsnew, 0);
            if (tests == testsnew)
                break;
            tests = testsnew;
            if (*tests == ',')
                tests++;
            i++;
            if (i > FFS_TESTCASE_PARAMS_MAX) {
                ttw(ttr(TTrTest, "TEST %s has TOO MANY PARAMS" NL, this.name));
                tw(tr(TR_FUNC, TrTest, "TEST %s TOO MANY PARAMS\n", this.name));
                return 1;
            }
        }
                
        if (*tests == ';')
            tests++;

        // Lookup the test name in the array of test cases
        for (ptc = testcase; ptc->name != 0; ptc++) {
            if (strcmp(this.name, ptc->name) == 0)
                break;
        }
        if (ptc->name == 0) {
            ttw(ttr(TTrTest, "TEST %s UNKNOWN" NL, this.name));
            tw(tr(TR_FUNC, TrTest, "TEST %s UNKNOWN\n", this.name));
            return 1;
        }

        this.numcalls = test.numcalls;
        test_begin(this.name, tparams);
        i = ptc->function(tparams[0], tparams[1]);
        if (i != 0) {
            failed++;
            test_error(&this, test.numcalls);
        }
        test_end(&this, test.numcalls);
    }

    return failed;
}

// Overall test initialization. Read static ffs params with ffs_query()
void test_init(int keepgoing)
{
    test.numcases = 0;
    test.numcalls = 0;
    test.numfails = 0;
    test.keepgoing = keepgoing;

    memset(&param, 0, sizeof(struct ffs_params_s));

    if (smallbuf == 0) {
#if (TARGET == 1)
        smallbuf = (char*)target_malloc(smallbuf_size);
#else
        smallbuf = malloc(smallbuf_size);
#endif
        tw(tr(TR_FUNC, TrTest, "smallbuf = 0x%X, %d\n",
              smallbuf, smallbuf_size));
    }
    if (bigbuf == 0) {
#if (TARGET == 1)
        // We continuously halve the buffer size until we succeed to allocate
        // it.
        while(1) {
            if ((bigbuf = (char*)target_malloc(bigbuf_size)) != 0)
                break;
            bigbuf_size /= 2;
        } 
#else
        bigbuf = malloc(bigbuf_size);
#endif
        tw(tr(TR_FUNC, TrTest, "bigbuf   = 0x%X, %d\n", bigbuf, bigbuf_size));
    }

    test_tdata_init();

    tffs_initialize();
}

void test_exit(void)
{
    test_state_print(0);
}


// Begin new test case
void test_begin(char *name, int *params)
{
    test.numcases++;

    tw(tr(TR_BEGIN, TrTest, "TEST %s(%d,%d)\n", name, params[0], params[1]));
    ttw(ttr(TTrTest, "TEST %s(%d,%d)" NL, name, params[0], params[1]));
}

void test_end(struct this_test_s *test, int n)
{
    int objects = 0;

    ffs_query(Q_TOTAL_OBJECTS, (uint16 *) &objects);
    tw(tr(TR_FUNC, TrTestHigh, "(total objects = %d)\n", objects));
    tw(tr(TR_END, TrTest, ""));
    //tw(tr(TR_END, TrTest, "TEST END   %s (calls = %d)\n",
    //      test->name, n - test->numcalls));
}

void test_error(struct this_test_s *test, int n)
{
    ttw(ttr(TTrTest, "TEST FAIL %s, call %d" NL,
            test->name, n - test->numcalls));
    tw(tr(TR_FUNC, TrTest, "TEST FAIL %s, call %d\n",
          test->name, n - test->numcalls));
}


/******************************************************************************
 * Miscellaneous
 ******************************************************************************/

int test_ffs_state_get(struct ffs_state_s *s)
{
    memset(s, 0, sizeof(struct ffs_state_s));

    error  = ffs_query(Q_INODES_USED,  (uint16 *) &s->inodes_used);
    error += ffs_query(Q_INODES_LOST,  (uint16 *) &s->inodes_lost);
    error += ffs_query(Q_OBJECTS_FREE,  (uint16 *) &s->objects_free);
    error += ffs_query(Q_TOTAL_OBJECTS, (uint16 *) &s->objects_total);

    error += ffs_query(Q_BYTES_USED, (uint16 *) &s->bytes_used);
    error += ffs_query(Q_BYTES_LOST, (uint16 *) &s->bytes_lost);
    error += ffs_query(Q_BYTES_FREE, (uint16 *) &s->bytes_free);

    error += ffs_query(Q_BLOCKS_FREE, (uint16 *) &s->blocks_free);

    return error;
}

void test_ffs_state_copy(struct ffs_state_s *dst, struct ffs_state_s *src)
{
    memcpy(dst, src, sizeof(struct ffs_state_s));
}

void test_state_print(struct ffs_state_s *state)
{
    struct ffs_state_s mystate;

    if (state == 0) {
        state = &mystate;
        test_ffs_state_get(state);
    }

    tw(tr(TR_FUNC, TrTest, "\nFFS State Summary:\n\n"));
    ttw(str(TTrTest, NL "FFS State Summary:" NL NL));

    tw(tr(TR_FUNC, TrTest, "  block_size = %d\n", param.block_size));
    tw(tr(TR_FUNC, TrTest, "  bytes_avail = %d\n\n", param.bytes_avail));

    ttw(ttr(TTrTest, "  block_size = %d" NL, param.block_size));
    ttw(ttr(TTrTest, "  bytes_avail = %d" NL NL, param.bytes_avail));

    test_state_bytes_print(state, 0);
    test_state_objects_print(state, 0);

}

void test_state_objects_print(struct ffs_state_s *old, struct ffs_state_s *new)
{
    ttw(str(TTrTest, "              inodes            objects" NL));
    ttw(str(TTrTest, "  -------------------------------------------" NL));
    ttw(str(TTrTest, "  objects:    used     lost     free    total" NL));
    ttw(ttr(TTrTest, "  old:      %6d   %6d   %6d   %6d" NL,
          old->inodes_used, old->inodes_lost,
          old->objects_free, old->objects_total));

    tw(tr(TR_FUNC, TrTest, "              inodes            objects\n"));
    tw(tr(TR_FUNC, TrTest, "  -------------------------------------------\n"));
    tw(tr(TR_FUNC, TrTest, "  objects:    used     lost     free    total\n"));
    tw(tr(TR_FUNC, TrTest, "  old:      %6d   %6d   %6d   %6d\n",
          old->inodes_used, old->inodes_lost,
          old->objects_free, old->objects_total));

    if (new != NULL)
    {
        ttw(ttr(TTrTest,
              "  new:      %6d   %6d   %6d   %6d" NL,
              new->inodes_used, new->inodes_lost,
              new->objects_free, new->objects_total));
        ttw(ttr(TTrTest,
              "  diff:     %6d   %6d   %6d   %6d" NL,
              new->inodes_used - old->inodes_used,
              new->inodes_lost - old->inodes_lost,
              new->objects_free - old->objects_free,
              new->objects_total - old->objects_total));

        tw(tr(TR_FUNC, TrTest,
              "  new:      %6d   %6d   %6d   %6d\n",
              new->inodes_used, new->inodes_lost,
              new->objects_free, new->objects_total));
        tw(tr(TR_FUNC, TrTest,
              "  diff:     %6d   %6d   %6d   %6d\n",
              new->inodes_used - old->inodes_used,
              new->inodes_lost - old->inodes_lost,
              new->objects_free - old->objects_free,
              new->objects_total - old->objects_total));
    }
    ttw(str(TTrTest, "" NL));
    tw(tr(TR_FUNC, TrTest, "\n"));
}

void test_state_bytes_print(struct ffs_state_s *old, struct ffs_state_s *new)
{
    tw(tr(TR_FUNC, TrTest, "  bytes:      used     lost     free    total\n"));
    tw(tr(TR_FUNC, TrTest, "  old:    %8d %8d %8d %8d\n",
          old->bytes_used, old->bytes_lost,
          old->bytes_free, param.bytes_max));
    tw(tr(TR_FUNC, TrTest, "  +/-:    %8d          %8d\n",
          old->bytes_used - old->bytes_lost,
          old->bytes_free + old->bytes_lost));

    ttw(str(TTrTest, "  bytes:      used     lost     free    total" NL));
    ttw(ttr(TTrTest, "  old:    %8d %8d %8d %8d" NL,
          old->bytes_used, old->bytes_lost,
          old->bytes_free, param.bytes_max));
    ttw(ttr(TTrTest, "  +/-:    %8d          %8d" NL,
          old->bytes_used - old->bytes_lost,
          old->bytes_free + old->bytes_lost));

    if (new != NULL) {
        tw(tr(TR_FUNC, TrTest, "  new:    %8d %8d %8d\n",
              new->bytes_used, new->bytes_lost,
              new->bytes_free));
        tw(tr(TR_FUNC, TrTest, "  diff:   %8d %8d %8d\n",
              new->bytes_used - old->bytes_used,
              new->bytes_lost - old->bytes_lost,
              new->bytes_free - old->bytes_free));

        ttw(ttr(TTrTest, "  new:    %8d %8d %8d" NL,
              new->bytes_used, new->bytes_lost,
              new->bytes_free));
        ttw(ttr(TTrTest, "  diff:   %8d %8d %8d" NL,
              new->bytes_used - old->bytes_used,
              new->bytes_lost - old->bytes_lost,
              new->bytes_free - old->bytes_free));
    }
    tw(tr(TR_FUNC, TrTest, "\n"));
    ttw(str(TTrTest, "" NL));
}


// Retrieve all static ffs parameters with ffs_query()
int test_ffs_params_get(void)
{
    error  = ffs_query(Q_FILENAME_MAX,    &param.filename_max);
    error += ffs_query(Q_PATH_DEPTH_MAX,  &param.pathdepth_max);
    error += ffs_query(Q_INODES_MAX,      &param.inodes_max);
    error += ffs_query(Q_BYTES_MAX,       &param.bytes_max);
    error += ffs_query(Q_DEV_BLOCKS,      &param.numblocks);
    error += ffs_query(Q_DEV_ATOMSIZE,    &param.atomsize);
    error += ffs_query(Q_BLOCKS_FREE_MIN, &param.blocks_free_min);

    // Compute block size
    param.block_size = param.bytes_max / param.numblocks;

    // Compute total number of available storage space, subtracting
    // fs.blocks_free_min plus one block for inodes
    param.bytes_avail =
        param.bytes_max - (param.block_size * (1 + param.blocks_free_min));

    // Compute number of blocks available for data storage
    param.data_blocks = param.numblocks - (1 + param.blocks_free_min);

    return error;
}

void test_statistics_print(void)
{
    tw(tr(TR_FUNC, TrTest, "Data allocated(%dMB)\n", stats.data_allocated>>20)); 
    tw(tr(TR_FUNC, TrTest, "Reclaim candidates: most-lost(%d), most-unused(%d), youngest(%d)\n", stats.drec.most_lost, stats.drec.most_unused, stats.drec.youngest)); 

    tw(tr(TR_FUNC, TrTest, "Data reclaimed:     lost(%dMB), valid(%dMB)\n", 
          stats.drec.lost[0]>>20 , stats.drec.valid[0]>>20)); 

    tw(tr(TR_FUNC, TrTest, "Inodes reclaimed:   num(%d), valid(%d), lost(%d)\n",
          stats.irec.num, stats.irec.valid, stats.irec.lost));

    ttw(ttr(TTrTest, "Data allocated(%dMB)\n" NL, stats.data_allocated>>20)); 
    ttw(ttr(TTrTest, "Reclaim candidates: most-lost(%d), most-unused(%d), youngest(%d)\n" NL, stats.drec.most_lost, stats.drec.most_unused, stats.drec.youngest)); 

    ttw(ttr(TTrTest, "Data reclaimed:     lost(%dMB), valid(%dMB)\n" NL, 
          stats.drec.lost[0]>>20 , stats.drec.valid[0]>>20)); 

    ttw(ttr(TTrTest, "Inodes reclaimed:   num(%d), valid(%d), lost(%d)\n" NL,
          stats.irec.num, stats.irec.valid, stats.irec.lost));
}

/******************************************************************************
 * Test and Expect Functions
 ******************************************************************************/

int test_expect(int n, int xn)
{
    if (n == xn)
        return 0;

    tw(tr(TR_FUNC, TrTest,
          "ERROR: expect(%d,%d): got %d, '%s', expected %d, '%s'\n",
          n, xn, n, ffs_strerror(n), xn, ffs_strerror(xn)));
    ttw(ttr(TTrTest, "ERROR: expect(%d,%d)" NL, n, xn));

    return -1;
}

// Expect a return code >= 0 meaning EFFS_OK.
int test_expect_ok(int n)
{
    if (n >= 0)
        return 0;

    tw(tr(TR_FUNC, TrTest,
          "ERROR: expect_ok(%d) got %d, '%s', expected >= EFFS_OK\n",
          n, ffs_strerror(n)));
    ttw(ttr(TTrTest, "ERROR: expect_ok(%d)" NL, n));

    return -1;
}

int test_expect_equal(int n, int xn)
{
    if (n == xn)
        return 0;

    tw(tr(TR_FUNC, TrTest, "ERROR: got %d, expected %d\n", n, xn));
    ttw(ttr(TTrTest, "ERROR: expect_eq(%d,%d" NL, n, xn));

    return -1;
}

int test_expect_not_equal(int n, int xn)
{
    if (n != xn)
        return 0;

    tw(tr(TR_FUNC, TrTest, "ERROR: expect_ne(%d)\n", n));
    ttw(ttr(TTrTest, "ERROR: expect_ne(%d)" NL, n));

    return -1;
}

int test_expect_greater_than(int n, int xn)
{
    if (n > xn)
        return 0;

    tw(tr(TR_FUNC, TrTest, "ERROR: expect_gt(%d,%d) got %d but expected > %d\n",
          n, xn, n, xn));
    ttw(ttr(TTrTest, "ERROR: expect_gt(%d,%d)" NL, n, xn));

    return -1;
}

int test_expect_data(const void *data1, const void *data2, int size)
{
    if (memcmp(data1, data2, size) == 0)
        return 0;
    
    tw(tr(TR_FUNC, TrTest,
          "ERROR: expect_data(%d) got unexpected data\n", size));
    ttw(ttr(TTrTest, "ERROR: expect_data(%d)" NL, size));

    return -1;
}

// Check that contents of file with name <name> is the same as <data> of
// size <size>.
int test_expect_file(const char *name, const void *data, int size)
{
    test.numcalls++;
    if (size > bigbuf_size) {
        tw(tr(TR_FUNC, TrTest, "WARNING: expect_file(%d) buffer too small\n",
              size));
        ttw(ttr(TTrTest, "WARNING: expect_file(%d) buffer too small" NL, size));
#if (TARGET == 1)
        return 0;
#endif
        return -1;
    }
        
    error = ffs_file_read(name, bigbuf, size);
    if (test_expect_greater_than(error, EFFS_OK - 1))
        return -1;
    return test_expect_data(bigbuf, data, size);
}

int test_expect_state(struct ffs_state_s *old, struct ffs_state_s *new)
{
    int old_total, new_total;

    old_total = old->inodes_used - old->inodes_lost;
    new_total = new->inodes_used - new->inodes_lost;

    if (old->objects_total == new->objects_total &&
        old->objects_total == new_total &&
        new->objects_total == old_total &&
        old->bytes_used == new->bytes_used &&
        old->bytes_lost == new->bytes_lost &&
        old->bytes_free == new->bytes_free) {
        return 0;
    }

    ttw(str(TTrTest, "ERROR: ffs state mismatch:" NL NL));
    tw(tr(TR_FUNC, TrTest, "ERROR: ffs state mismatch:\n\n"));
    test_state_objects_print(old, new);
    test_state_bytes_print(old, new);

    return -1;
}

// Check if number of objects is unchanged
int test_expect_objects(struct ffs_state_s *old, struct ffs_state_s *new)
{
    int old_total, new_total;

    test_ffs_state_get(new);

    old_total = old->inodes_used - old->inodes_lost;
    new_total = new->inodes_used - new->inodes_lost;

    if (old->objects_total == new->objects_total &&
        old->objects_total == new_total &&
        new->objects_total == old_total) {
        return 0;
    }

    ttw(ttr(TTrTest, "ERROR: expect_objects(%d, %d, %d, %d, %d, %d)" NL));
    tw(tr(TR_FUNC, TrTest, "ERROR: ffs state mismatch:\n\n"));
    test_state_objects_print(old, new);

    return -1;
}


/******************************************************************************
 * FFS Functions
 ******************************************************************************/

effs_t tffs_fcreate(const char *name, void *addr, int size)
{
    test.numcalls++;
    return ffs_fcreate(name, addr, size);
}

effs_t tffs_fupdate(const char *name, void *addr, int size)
{
    test.numcalls++;
    return ffs_fupdate(name, addr, size);
}

effs_t tffs_fwrite(const char *name, void *addr, int size)
{
    test.numcalls++;
    return ffs_fwrite(name, addr, size);
}

effs_t tffs_file_write(const char *name, void *addr, int size, uint16 option)
{
    test.numcalls++;
    return ffs_file_write(name, addr, size, option);
}

effs_t tffs_mkdir(const char *name)
{
    test.numcalls++;
    return ffs_mkdir(name);
}

effs_t tffs_symlink(const char *name, const char *actualpath)
{
    test.numcalls++;
    return ffs_symlink(name, actualpath);
}

effs_t tffs_remove(const char *name)
{
    test.numcalls++;
    return ffs_remove(name);
}

effs_t tffs_fcontrol(const char *name, int8 action, uint16 param)
{
    test.numcalls++;
    return ffs_fcontrol(name, action, param);
}

effs_t tffs_preformat(uint16 magic)
{
    test.numcalls++;
    return ffs_preformat(magic);
}

effs_t tffs_format(const char *name, uint16 magic)
{
    test.numcalls++;
    return ffs_format(name, magic);
}


int tffs_fread(const char *name, void *addr, int size)
{
    test.numcalls++;
    return ffs_file_read(name, addr, size);
}

int tffs_file_read(const char *name, void *addr, int size)
{
    test.numcalls++;
    return ffs_file_read(name, addr, size);
}

int tffs_opendir(const char *name, struct dir_s *dir)
{
    test.numcalls++;
    return ffs_opendir(name, dir);
}

int tffs_readdir (struct dir_s *dir, char *name, int8 size)
{
    test.numcalls++;
    return ffs_readdir(dir, name, size);
}

int tffs_readlink(const char *name, char *addr, int size)
{
    test.numcalls++;
    return ffs_readlink(name, addr, size);
}

int tffs_rename(const char *oldname, const char *newname)
{
    test.numcalls++;
    return ffs_rename(oldname, newname);
}

effs_t tffs_stat(const char *name, struct stat_s *stat)
{
    test.numcalls++;
    return ffs_stat(name, stat);
}

effs_t tffs_fstat(fd_t fdi, struct stat_s *stat)
{
    test.numcalls++;
    return ffs_fstat(fdi, stat);
}

effs_t tffs_linkstat(const char *name, struct stat_s *stat)
{
    test.numcalls++;
    return ffs_lstat(name, stat);
}

effs_t tffs_lstat(const char *name, struct stat_s *stat)
{
    test.numcalls++;
    return ffs_lstat(name, stat);
}

effs_t tffs_xlstat(const char *name, struct xstat_s *stat)
{
    test.numcalls++;
    return ffs_xlstat(name, stat);
}

effs_t tffs_query(int8 query, void *p)
{
    return ffs_query(query, p);
}


effs_t tffs_initialize(void)
{
    effs_t myerror;
    struct ffs_stats_s old_stats;

    test.numcalls++;

    memcpy(&old_stats, &stats, sizeof(struct ffs_stats_s));
    myerror = ffs_initialize();
    memcpy(&stats, &old_stats, sizeof(struct ffs_stats_s));

    test_ffs_params_get();
    return myerror;
}

effs_t tffs_exit(void)
{
    test.numcalls++;
    return ffs_exit();
}

fd_t tffs_open(const char *pathname, ffs_options_t options)
{
    test.numcalls++;
    return ffs_open(pathname, options);
}

effs_t tffs_close(fd_t fdi)
{
    test.numcalls++;
    return ffs_close(fdi);
}

int tffs_write(fd_t fdi, void *addr, int size)
{
   test.numcalls++;
   return ffs_write(fdi, addr, size);
}

int tffs_read(fd_t fdi, void *addr, int size)
{
   test.numcalls++;
   return ffs_read(fdi, addr, size);
}

int tffs_seek(fd_t fdi, int offset, int whence)
{
  test.numcalls++;
  return ffs_seek(fdi, offset, whence);
}

effs_t tffs_truncate(const char *path, offset_t length) 
{
    test.numcalls++;
    return ffs_truncate(path, length); 
}

effs_t tffs_ftruncate(fd_t fdi, offset_t length) 
{
    test.numcalls++;
    return ffs_ftruncate(fdi, length); 
}

effs_t tffs_fdatasync(fd_t fdi) 
{
    test.numcalls++;
    return ffs_fdatasync(fdi); 
}