]> git.sur5r.net Git - freertos/blobdiff - FreeRTOS-Plus/Source/Reliance-Edge/tests/posix/fsstress.c
Update Reliance Edge fail safe file system to the latest version.
[freertos] / FreeRTOS-Plus / Source / Reliance-Edge / tests / posix / fsstress.c
index a440dfe656427125a03251f5f6069a396b29d09f..abc2aa960bacfdf74665bb05956d29f00de6de63 100644 (file)
-/*\r
- * Copyright (c) 2000 Silicon Graphics, Inc.  All Rights Reserved.\r
- *\r
- * This program is free software; you can redistribute it and/or modify it\r
- * under the terms of version 2 of the GNU General Public License as\r
- * published by the Free Software Foundation.\r
- *\r
- * This program is distributed in the hope that it would be useful, but\r
- * WITHOUT ANY WARRANTY; without even the implied warranty of\r
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\r
- *\r
- * Further, this software is distributed without any warranty that it is\r
- * free of the rightful claim of any third person regarding infringement\r
- * or the like.  Any license provided herein, whether implied or\r
- * otherwise, applies only to this software file.  Patent licenses, if\r
- * any, provided herein do not apply to combinations of this program with\r
- * other software, or any other product whatsoever.\r
- *\r
- * You should have received a copy of the GNU General Public License along\r
- * with this program; if not, write the Free Software Foundation, Inc.,\r
- * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.\r
- *\r
- * Contact information: Silicon Graphics, Inc., 1600 Amphitheatre Pkwy,\r
- * Mountain View, CA  94043, or:\r
- *\r
- * http://www.sgi.com\r
- *\r
- * For further information regarding this notice, see:\r
- *\r
- * http://oss.sgi.com/projects/GenInfo/SGIGPLNoticeExplan/\r
- */\r
-/** @file\r
-    @brief File system stress test.\r
-\r
-    This version of SGI fsstress has been modified to be single-threaded and to\r
-    work with the Reliance Edge POSIX-like API.\r
-*/\r
-#include <stdio.h>\r
-#include <stdlib.h>\r
-#include <limits.h>\r
-#include <string.h>\r
-#include <stdarg.h>\r
-#include <time.h>\r
-\r
-#include <redposix.h>\r
-#include <redtests.h>\r
-\r
-#if FSSTRESS_SUPPORTED\r
-\r
-#include "redposixcompat.h"\r
-\r
-#include <redosserv.h>\r
-#include <redutils.h>\r
-#include <redmacs.h>\r
-#include <redvolume.h>\r
-#include <redgetopt.h>\r
-#include <redtoolcmn.h>\r
-\r
-#if REDCONF_CHECKER == 1\r
-#include <redcoreapi.h>\r
-#endif\r
-\r
-\r
-/*  Create POSIX types.  Use #define to avoid name conflicts in those\r
-    environments where the type names already exist.\r
-*/\r
-#define off_t int64_t\r
-#define off64_t off_t\r
-#define ino_t uint32_t\r
-#define mode_t uint16_t\r
-#define __int64_t int64_t\r
-\r
-\r
-/** @brief Generate a random number.\r
-\r
-    @return A nonnegative random number.\r
-*/\r
-#define random() ((int)(RedRand32(NULL) & 0x7FFFFFFF))\r
-\r
-\r
-/** @brief Seed the random number generator.\r
-*/\r
-#define srandom(seed) RedRandSeed(seed)\r
-\r
-\r
-#define _exit(status) exit(status)\r
-#define getpagesize() 4096U\r
-#define getpid() 1\r
-\r
-\r
-/** @brief Determine the maximum file size.\r
-\r
-    This is used for the MAXFSSIZE macro.\r
-*/\r
-static uint64_t MaxFileSize(void)\r
-{\r
-    REDSTATFS   info;\r
-    int32_t     iStatus;\r
-    REDSTATUS   errnoSave = errno;\r
-    uint64_t    ullMaxFileSize;\r
-\r
-    iStatus = red_statvfs("", &info);\r
-    if(iStatus == 0)\r
-    {\r
-        ullMaxFileSize = info.f_maxfsize;\r
-    }\r
-    else\r
-    {\r
-        /*  This function does not change errno.\r
-        */\r
-        errno = errnoSave;\r
-\r
-        ullMaxFileSize = 0x7FFFFFFFU;\r
-    }\r
-\r
-    return ullMaxFileSize;\r
-}\r
-\r
-\r
-/*-------------------------------------------------------------------\r
-    Simulated current working directory support\r
--------------------------------------------------------------------*/\r
-\r
-\r
-/*  Forward declaration for red_chdir().\r
-*/\r
-static int red_stat(const char *pszPath, REDSTAT *pStat);\r
-\r
-/*  The simulated CWD functions.\r
-*/\r
-#undef chdir\r
-#undef getcwd\r
-#define chdir(path) red_chdir(path)\r
-#define getcwd(buf, size) red_getcwd(buf, size)\r
-\r
-\r
-/*  Redefine the path-based APIs to call MakeFullPath() on their arguments\r
-    since there is no CWD support in the red_*() APIs.\r
-*/\r
-#undef open\r
-#undef unlink\r
-#undef mkdir\r
-#undef rmdir\r
-#undef rename\r
-#undef link\r
-#undef opendir\r
-#define open(path, oflag) red_open(MakeFullPath(path), oflag)\r
-#define unlink(path) red_unlink(MakeFullPath(path))\r
-#define mkdir(path) red_mkdir(MakeFullPath(path))\r
-#define rmdir(path) red_rmdir(MakeFullPath(path))\r
-#define rename(old, new) red_rename(MakeFullPath(old), MakeFullPath(new))\r
-#define link(path, hardlink) red_link(MakeFullPath(path), MakeFullPath(hardlink))\r
-#define opendir(path) red_opendir(MakeFullPath(path))\r
-\r
-\r
-/*  Stores the simulated current working directory.\r
-*/\r
-static char szLocalCwd[1024U] = "/";\r
-\r
-\r
-/** @brief Change the current working directory.\r
-\r
-    This function only supports a subset of what is possible with POSIX chdir().\r
-\r
-    @param pszPath  The new current working directory.\r
-\r
-    @return Upon successful completion, 0 shall be returned.  Otherwise, -1\r
-            shall be returned, and errno shall be set to indicate the error.\r
-*/\r
-static int red_chdir(\r
-    const char *pszPath)\r
-{\r
-    uint32_t    ulIdx;\r
-    int         iErrno = 0;\r
-\r
-    if(strcmp(pszPath, "..") == 0)\r
-    {\r
-        uint32_t ulLastSlashIdx = 0U;\r
-\r
-        /*  Chop off the last path separator and everything after it, so that\r
-            "/foo/bar/baz" becomes "/foo/bar", moving the CWD up one directory.\r
-        */\r
-        for(ulIdx = 0U; szLocalCwd[ulIdx] != '\0'; ulIdx++)\r
-        {\r
-            if(szLocalCwd[ulIdx] == '/')\r
-            {\r
-                ulLastSlashIdx = ulIdx;\r
-            }\r
-        }\r
-\r
-        if(ulLastSlashIdx != 0U)\r
-        {\r
-            szLocalCwd[ulLastSlashIdx] = '\0';\r
-        }\r
-    }\r
-    else\r
-    {\r
-        char    szOldCwd[1024U];\r
-\r
-        /*  chdir() must have no effect on the CWD if it fails, so save the CWD\r
-            so we can revert it if necessary.\r
-        */\r
-        strcpy(szOldCwd, szLocalCwd);\r
-\r
-        if(pszPath[0U] == '/')\r
-        {\r
-            if(strlen(pszPath) >= sizeof(szLocalCwd))\r
-            {\r
-                iErrno = RED_ENAMETOOLONG;\r
-            }\r
-            else\r
-            {\r
-                strcpy(szLocalCwd, pszPath);\r
-            }\r
-        }\r
-        else\r
-        {\r
-            ulIdx = strlen(szLocalCwd);\r
-\r
-            if((ulIdx + 1U + strlen(pszPath)) >= sizeof(szLocalCwd))\r
-            {\r
-                iErrno = RED_ENAMETOOLONG;\r
-            }\r
-            else\r
-            {\r
-                if(szLocalCwd[1U] != '\0')\r
-                {\r
-                    szLocalCwd[ulIdx] = '/';\r
-                    ulIdx++;\r
-                }\r
-\r
-                strcpy(&szLocalCwd[ulIdx], pszPath);\r
-            }\r
-        }\r
-\r
-        if(iErrno == 0)\r
-        {\r
-            REDSTAT s;\r
-            int     iStatus;\r
-\r
-            iStatus = red_stat(szLocalCwd, &s);\r
-            if(iStatus != 0)\r
-            {\r
-                iErrno = errno;\r
-            }\r
-            else if(!S_ISDIR(s.st_mode))\r
-            {\r
-                iErrno = RED_ENOTDIR;\r
-            }\r
-            else\r
-            {\r
-                /*  No error, new CWD checks out.\r
-                */\r
-            }\r
-        }\r
-\r
-        if(iErrno != 0)\r
-        {\r
-            strcpy(szLocalCwd, szOldCwd);\r
-        }\r
-    }\r
-\r
-    if(iErrno != 0)\r
-    {\r
-        errno = iErrno;\r
-    }\r
-\r
-    return iErrno == 0 ? 0 : -1;\r
-}\r
-\r
-\r
-/** @brief Retrieve the current working directory.\r
-\r
-    @param pszBuf   On successful return, populated with the current working\r
-                    directory.  If NULL, memory will be allocated for the CWD\r
-                    and returned by this function.\r
-    @param nSize    The size of @p pszBuf.\r
-\r
-    @return On success, if @p pszBuf was non-NULL, returns @p pszBuf; if\r
-            @p pszBuf was NULL, returns an allocated buffer populated with the\r
-            CWD which must be freed by the caller.  On failure, returns NULL\r
-            and errno will be set.\r
-*/\r
-static char *red_getcwd(\r
-    char   *pszBuf,\r
-    size_t  nSize)\r
-{\r
-    char   *pszRet;\r
-\r
-    if(pszBuf == NULL)\r
-    {\r
-        pszRet = malloc(strlen(szLocalCwd) + 1U);\r
-        if(pszRet == NULL)\r
-        {\r
-            errno = RED_ENOMEM;\r
-        }\r
-        else\r
-        {\r
-            strcpy(pszRet, szLocalCwd);\r
-        }\r
-    }\r
-    else if(nSize < strlen(szLocalCwd) + 1U)\r
-    {\r
-        errno = RED_ERANGE;\r
-        pszRet = NULL;\r
-    }\r
-    else\r
-    {\r
-        strcpy(pszBuf, szLocalCwd);\r
-        pszRet = pszBuf;\r
-    }\r
-\r
-    return pszRet;\r
-}\r
-\r
-\r
-/** @brief Make a relative path into a fully qualified path.\r
-\r
-    @param pszName  The relative path.\r
-\r
-    @return On success, a pointer to a fully qualified path.  On error, NULL.\r
-*/\r
-static const char *MakeFullPath(\r
-    const char     *pszName)\r
-{\r
-    #define         MAXVOLNAME 64U /* Enough for most configs. */\r
-    static char     aszFullPath[2U][MAXVOLNAME + 1U + 1024U];\r
-    static uint32_t ulWhich = 0U;\r
-\r
-    char           *pszFullPath = aszFullPath[ulWhich];\r
-    const char     *pszVolume = gpRedVolConf->pszPathPrefix;\r
-    int32_t         iLen;\r
-\r
-    if(pszName[0U] == '/')\r
-    {\r
-        iLen = RedSNPrintf(pszFullPath, sizeof(aszFullPath[0U]), "%s%s", pszVolume, pszName);\r
-    }\r
-    else if(strcmp(pszName, ".") == 0U)\r
-    {\r
-        iLen = RedSNPrintf(pszFullPath, sizeof(aszFullPath[0U]), "%s%s", pszVolume, szLocalCwd);\r
-    }\r
-    else if((szLocalCwd[0U] == '/') && (szLocalCwd[1U] == '\0'))\r
-    {\r
-        iLen = RedSNPrintf(pszFullPath, sizeof(aszFullPath[0U]), "%s/%s", pszVolume, pszName);\r
-    }\r
-    else\r
-    {\r
-        iLen = RedSNPrintf(pszFullPath, sizeof(aszFullPath[0U]), "%s%s/%s", pszVolume, szLocalCwd, pszName);\r
-    }\r
-\r
-    if(iLen == -1)\r
-    {\r
-        /*  Insufficient path buffer space.\r
-        */\r
-        pszFullPath = NULL;\r
-    }\r
-    else\r
-    {\r
-        /*  Toggle between two full path arrays; a kluge to make rename() and\r
-            link() work correctly.\r
-        */\r
-        ulWhich ^= 1U;\r
-    }\r
-\r
-    return pszFullPath;\r
-}\r
-\r
-\r
-/*-------------------------------------------------------------------\r
-    POSIX functions not implemented by the RED POSIX-like API\r
--------------------------------------------------------------------*/\r
-\r
-#define stat(p, s) red_stat(p, s)\r
-#define stat64(p, s) stat(p, s)\r
-#define lstat(p, s) stat(p, s)\r
-#define lstat64(p, s) stat(p, s)\r
-#define truncate(p, s) red_truncate(p, s)\r
-#define truncate64(p, s) truncate(p, s)\r
-\r
-\r
-/** @brief Get the status of a file or directory.\r
-*/\r
-static int red_stat(\r
-    const char *pszPath,\r
-    REDSTAT    *pStat)\r
-{\r
-    int         iFd;\r
-    int         iRet;\r
-\r
-    iFd = open(pszPath, O_RDONLY);\r
-    iRet = iFd;\r
-    if(iFd != -1)\r
-    {\r
-        iRet = fstat(iFd, pStat);\r
-\r
-        (void)close(iFd);\r
-    }\r
-\r
-    return iRet;\r
-}\r
-\r
-\r
-/** @brief Truncate a file to a specified length.\r
-*/\r
-static int red_truncate(\r
-    const char *pszPath,\r
-    off_t       llSize)\r
-{\r
-    int         iFd;\r
-    int         iRet;\r
-\r
-    iFd = open(pszPath, O_WRONLY);\r
-    iRet = iFd;\r
-    if(iFd != -1)\r
-    {\r
-        iRet = ftruncate(iFd, llSize);\r
-\r
-        (void)close(iFd);\r
-    }\r
-\r
-    return iRet;\r
-}\r
-\r
-\r
-/*-------------------------------------------------------------------\r
-    Begin ported fsstress code\r
--------------------------------------------------------------------*/\r
-\r
-/* Stuff from xfscompat.h */\r
-\r
-#define MAXNAMELEN (REDCONF_NAME_MAX+1U) /* Assumed to include NUL */\r
-\r
-struct dioattr {\r
-    int d_miniosz, d_maxiosz, d_mem;\r
-};\r
-\r
-#define MIN(a,b) ((a)<(b) ? (a):(b))\r
-#define MAX(a,b) ((a)>(b) ? (a):(b))\r
-\r
-/* End xfscompat.h */\r
-\r
-\r
-typedef enum {\r
-    OP_CREAT,\r
-    OP_FDATASYNC,\r
-    OP_FSYNC,\r
-    OP_GETDENTS,\r
-    OP_LINK,\r
-    OP_MKDIR,\r
-    OP_READ,\r
-    OP_RENAME,\r
-    OP_RMDIR,\r
-    OP_STAT,\r
-    OP_TRUNCATE,\r
-    OP_UNLINK,\r
-    OP_WRITE,\r
-  #if REDCONF_CHECKER == 1\r
-    OP_CHECK,\r
-  #endif\r
-    OP_LAST\r
-} opty_t;\r
-\r
-typedef void (*opfnc_t) (int, long);\r
-\r
-typedef struct opdesc {\r
-    opty_t op;\r
-    const char *name;\r
-    opfnc_t func;\r
-    int freq;\r
-    int iswrite;\r
-} opdesc_t;\r
-\r
-typedef struct fent {\r
-    int id;\r
-    int parent;\r
-} fent_t;\r
-\r
-typedef struct flist {\r
-    int nfiles;\r
-    int nslots;\r
-    int tag;\r
-    fent_t *fents;\r
-} flist_t;\r
-\r
-typedef struct pathname {\r
-    int len;\r
-    char *path;\r
-} pathname_t;\r
-\r
-#define FT_DIR      0\r
-#define FT_DIRm     (1 << FT_DIR)\r
-#define FT_REG      1\r
-#define FT_REGm     (1 << FT_REG)\r
-#define FT_SYM      2\r
-#define FT_SYMm     (1 << FT_SYM)\r
-#define FT_DEV      3\r
-#define FT_DEVm     (1 << FT_DEV)\r
-#define FT_RTF      4\r
-#define FT_RTFm     (1 << FT_RTF)\r
-#define FT_nft      5\r
-#define FT_ANYm     ((1 << FT_nft) - 1)\r
-#define FT_REGFILE  (FT_REGm | FT_RTFm)\r
-#define FT_NOTDIR   (FT_ANYm & ~FT_DIRm)\r
-\r
-#define FLIST_SLOT_INCR 16\r
-#define NDCACHE 64\r
-\r
-#define MAXFSIZE MaxFileSize()\r
-\r
-static void creat_f(int opno, long r);\r
-static void fdatasync_f(int opno, long r);\r
-static void fsync_f(int opno, long r);\r
-static void getdents_f(int opno, long r);\r
-static void link_f(int opno, long r);\r
-static void mkdir_f(int opno, long r);\r
-static void read_f(int opno, long r);\r
-static void rename_f(int opno, long r);\r
-static void rmdir_f(int opno, long r);\r
-static void stat_f(int opno, long r);\r
-static void truncate_f(int opno, long r);\r
-static void unlink_f(int opno, long r);\r
-static void write_f(int opno, long r);\r
-#if REDCONF_CHECKER == 1\r
-static void check_f(int opno, long r);\r
-#endif\r
-\r
-static opdesc_t ops[] = {\r
-    {OP_CREAT, "creat", creat_f, 4, 1},\r
-    {OP_FDATASYNC, "fdatasync", fdatasync_f, 1, 1},\r
-    {OP_FSYNC, "fsync", fsync_f, 1, 1},\r
-    {OP_GETDENTS, "getdents", getdents_f, 1, 0},\r
-    {OP_LINK, "link", link_f, 1, 1},\r
-    {OP_MKDIR, "mkdir", mkdir_f, 2, 1},\r
-    {OP_READ, "read", read_f, 1, 0},\r
-    {OP_RENAME, "rename", rename_f, 2, 1},\r
-    {OP_RMDIR, "rmdir", rmdir_f, 1, 1},\r
-    {OP_STAT, "stat", stat_f, 1, 0},\r
-    {OP_TRUNCATE, "truncate", truncate_f, 2, 1},\r
-    {OP_UNLINK, "unlink", unlink_f, 1, 1},\r
-    {OP_WRITE, "write", write_f, 4, 1},\r
-  #if REDCONF_CHECKER == 1\r
-    {OP_CHECK, "check", check_f, 1, 1},\r
-  #endif\r
-}, *ops_end;\r
-\r
-static flist_t flist[FT_nft] = {\r
-    {0, 0, 'd', NULL},\r
-    {0, 0, 'f', NULL},\r
-    {0, 0, 'l', NULL},\r
-    {0, 0, 'c', NULL},\r
-    {0, 0, 'r', NULL},\r
-};\r
-\r
-static int dcache[NDCACHE];\r
-static opty_t *freq_table;\r
-static int freq_table_size;\r
-static char *homedir;\r
-static int *ilist;\r
-static int ilistlen;\r
-static off64_t maxfsize;\r
-static int namerand;\r
-static int nameseq;\r
-static int nops;\r
-static int operations = 1;\r
-static int procid;\r
-static int rtpct;\r
-static unsigned long seed = 0;\r
-static ino_t top_ino;\r
-static int verbose = 0;\r
-\r
-static int delete_tree(const char *path);\r
-static void add_to_flist(int fd, int it, int parent);\r
-static void append_pathname(pathname_t *name, const char *str);\r
-static void check_cwd(void);\r
-static int creat_path(pathname_t *name, mode_t mode);\r
-static void dcache_enter(int dirid, int slot);\r
-static void dcache_init(void);\r
-static fent_t *dcache_lookup(int dirid);\r
-static void dcache_purge(int dirid);\r
-static void del_from_flist(int ft, int slot);\r
-static void doproc(void);\r
-static void fent_to_name(pathname_t *name, flist_t *flp, fent_t *fep);\r
-static void fix_parent(int oldid, int newid);\r
-static void free_pathname(pathname_t *name);\r
-static int generate_fname(fent_t *fep, int ft, pathname_t *name, int *idp, int *v);\r
-static int get_fname(int which, long r, pathname_t *name, flist_t **flpp, fent_t **fepp, int *v);\r
-static void init_pathname(pathname_t *name);\r
-static int link_path(pathname_t *name1, pathname_t *name2);\r
-static int lstat64_path(pathname_t *name, REDSTAT *sbuf);\r
-static void make_freq_table(void);\r
-static int mkdir_path(pathname_t *name, mode_t mode);\r
-static void namerandpad(int id, char *buf, int len);\r
-static int open_path(pathname_t *name, int oflag);\r
-static DIR *opendir_path(pathname_t *name);\r
-static int rename_path(pathname_t *name1, pathname_t *name2);\r
-static int rmdir_path(pathname_t *name);\r
-static void separate_pathname(pathname_t *name, char *buf, pathname_t *newname);\r
-static int stat64_path(pathname_t *name, REDSTAT *sbuf);\r
-static int truncate64_path(pathname_t *name, off64_t length);\r
-static int unlink_path(pathname_t *name);\r
-static void usage(const char *progname);\r
-\r
-\r
-/** @brief Parse parameters for fsstress.\r
-\r
-    @param argc         The number of arguments from main().\r
-    @param argv         The vector of arguments from main().\r
-    @param pParam       Populated with the fsstress parameters.\r
-    @param pbVolNum     If non-NULL, populated with the volume number.\r
-    @param ppszDevice   If non-NULL, populated with the device name argument or\r
-                        NULL if no device argument is provided.\r
-\r
-    @return The result of parsing the parameters.\r
-*/\r
-PARAMSTATUS FsstressParseParams(\r
-    int             argc,\r
-    char           *argv[],\r
-    FSSTRESSPARAM  *pParam,\r
-    uint8_t        *pbVolNum,\r
-    const char    **ppszDevice)\r
-{\r
-    int             c;\r
-    uint8_t         bVolNum;\r
-    const REDOPTION aLongopts[] =\r
-    {\r
-        { "no-cleanup", red_no_argument, NULL, 'c' },\r
-        { "loops", red_required_argument, NULL, 'l' },\r
-        { "nops", red_required_argument, NULL, 'n' },\r
-        { "namepad", red_no_argument, NULL, 'r' },\r
-        { "seed", red_required_argument, NULL, 's' },\r
-        { "verbose", red_no_argument, NULL, 'v' },\r
-        { "dev", red_required_argument, NULL, 'D' },\r
-        { "help", red_no_argument, NULL, 'H' },\r
-        { NULL }\r
-    };\r
-\r
-    /*  If run without parameters, treat as a help request.\r
-    */\r
-    if(argc <= 1)\r
-    {\r
-        goto Help;\r
-    }\r
-\r
-    /*  Assume no device argument to start with.\r
-    */\r
-    if(ppszDevice != NULL)\r
-    {\r
-        *ppszDevice = NULL;\r
-    }\r
-\r
-    /*  Set default parameters.\r
-    */\r
-    FsstressDefaultParams(pParam);\r
-\r
-    while((c = RedGetoptLong(argc, argv, "cl:n:rs:vD:H", aLongopts, NULL)) != -1)\r
-    {\r
-        switch(c)\r
-        {\r
-            case 'c': /* --no-cleanup */\r
-                pParam->fNoCleanup = true;\r
-                break;\r
-            case 'l': /* --loops */\r
-                pParam->ulLoops = RedAtoI(red_optarg);\r
-                break;\r
-            case 'n': /* --nops */\r
-                pParam->ulNops = RedAtoI(red_optarg);\r
-                break;\r
-            case 'r': /* --namepad */\r
-                pParam->fNamePad = true;\r
-                break;\r
-            case 's': /* --seed */\r
-                pParam->ulSeed = RedAtoI(red_optarg);\r
-                break;\r
-            case 'v': /* --verbose */\r
-                pParam->fVerbose = true;\r
-                break;\r
-            case 'D': /* --dev */\r
-                if(ppszDevice != NULL)\r
-                {\r
-                    *ppszDevice = red_optarg;\r
-                }\r
-                break;\r
-            case 'H': /* --help */\r
-                goto Help;\r
-            case '?': /* Unknown or ambiguous option */\r
-            case ':': /* Option missing required argument */\r
-            default:\r
-                goto BadOpt;\r
-        }\r
-    }\r
-\r
-    /*  RedGetoptLong() has permuted argv to move all non-option arguments to\r
-        the end.  We expect to find a volume identifier.\r
-    */\r
-    if(red_optind >= argc)\r
-    {\r
-        RedPrintf("Missing volume argument\n");\r
-        goto BadOpt;\r
-    }\r
-\r
-    bVolNum = RedFindVolumeNumber(argv[red_optind]);\r
-    if(bVolNum == REDCONF_VOLUME_COUNT)\r
-    {\r
-        RedPrintf("Error: \"%s\" is not a valid volume identifier.\n", argv[red_optind]);\r
-        goto BadOpt;\r
-    }\r
-\r
-    if(pbVolNum != NULL)\r
-    {\r
-        *pbVolNum = bVolNum;\r
-    }\r
-\r
-    red_optind++; /* Move past volume parameter. */\r
-    if(red_optind < argc)\r
-    {\r
-        int32_t ii;\r
-\r
-        for(ii = red_optind; ii < argc; ii++)\r
-        {\r
-            RedPrintf("Error: Unexpected command-line argument \"%s\".\n", argv[ii]);\r
-        }\r
-\r
-        goto BadOpt;\r
-    }\r
-\r
-    return PARAMSTATUS_OK;\r
-\r
-  BadOpt:\r
-\r
-    RedPrintf("%s - invalid parameters\n", argv[0U]);\r
-    usage(argv[0U]);\r
-    return PARAMSTATUS_BAD;\r
-\r
-  Help:\r
-\r
-    usage(argv[0U]);\r
-    return PARAMSTATUS_HELP;\r
-}\r
-\r
-\r
-/** @brief Set default fsstress parameters.\r
-\r
-    @param pParam   Populated with the default fsstress parameters.\r
-*/\r
-void FsstressDefaultParams(\r
-    FSSTRESSPARAM *pParam)\r
-{\r
-    RedMemSet(pParam, 0U, sizeof(*pParam));\r
-    pParam->ulLoops = 1U;\r
-    pParam->ulNops = 10000U;\r
-}\r
-\r
-\r
-/** @brief Start fsstress.\r
-\r
-    @param pParam   fsstress parameters, either from FsstressParseParams() or\r
-                    constructed programatically.\r
-\r
-    @return Zero on success, otherwise nonzero.\r
-*/\r
-int FsstressStart(\r
-    const FSSTRESSPARAM *pParam)\r
-{\r
-    char buf[10];\r
-    int fd;\r
-    int i;\r
-    int cleanup;\r
-    int loops;\r
-    int loopcntr = 1;\r
-\r
-    nops = sizeof(ops) / sizeof(ops[0]);\r
-    ops_end = &ops[nops];\r
-\r
-    /*  Copy the already-parsed parameters into the traditional variables.\r
-    */\r
-    cleanup = pParam->fNoCleanup ? 1 : 0;\r
-    loops = pParam->ulLoops;\r
-    operations = pParam->ulNops;\r
-    namerand = pParam->fNamePad ? 1 : 0;\r
-    seed = pParam->ulSeed;\r
-    verbose = pParam->fVerbose ? 1 : 0;\r
-\r
-    make_freq_table();\r
-\r
-    while ((loopcntr <= loops) || (loops == 0)) {\r
-        RedSNPrintf(buf, sizeof(buf), "fss%x", getpid());\r
-        fd = creat(buf, 0666);\r
-        maxfsize = (off64_t) MAXFSIZE;\r
-        dcache_init();\r
-        if (!seed) {\r
-            seed = (unsigned long)RedOsClockGetTime();\r
-            RedPrintf("seed = %ld\n", seed);\r
-        }\r
-        close(fd);\r
-        unlink(buf);\r
-        procid = 0;\r
-        doproc();\r
-        if (cleanup == 0) {\r
-            delete_tree("/");\r
-            for (i = 0; i < FT_nft; i++) {\r
-                flist[i].nslots = 0;\r
-                flist[i].nfiles = 0;\r
-                free(flist[i].fents);\r
-                flist[i].fents = NULL;\r
-            }\r
-        }\r
-        loopcntr++;\r
-    }\r
-    return 0;\r
-}\r
-\r
-static int delete_tree(const char *path)\r
-{\r
-    REDSTAT sb;\r
-    DIR *dp;\r
-    REDDIRENT *dep;\r
-    char *childpath;\r
-    size_t len;\r
-    int e;\r
-\r
-    e = stat(path, &sb);\r
-    if (e)\r
-        return errno;\r
-\r
-    if (!S_ISDIR(sb.st_mode))\r
-        return unlink(path) ? errno : 0;\r
-\r
-    dp = opendir(path);\r
-    if (dp == NULL)\r
-        return errno;\r
-\r
-    while((dep = readdir(dp)) != NULL) {\r
-        len = strlen(path) + 1 + strlen(dep->d_name) + 1;\r
-        childpath = malloc(len);\r
-\r
-        strcpy(childpath, path);\r
-        if (childpath[strlen(childpath) - 1] != '/')\r
-            strcat(childpath, "/");\r
-        strcat(childpath, dep->d_name);\r
-\r
-        e = delete_tree(childpath);\r
-\r
-        free(childpath);\r
-\r
-        if (e)\r
-            break;\r
-    }\r
-\r
-    if (e == 0 && strcmp(path, "/") != 0) {\r
-        e = rmdir(path) ? errno : 0;\r
-    }\r
-    closedir(dp);\r
-    return e;\r
-}\r
-\r
-static void add_to_flist(int ft, int id, int parent)\r
-{\r
-    fent_t *fep;\r
-    flist_t *ftp;\r
-\r
-    ftp = &flist[ft];\r
-    if (ftp->nfiles == ftp->nslots) {\r
-        ftp->nslots += FLIST_SLOT_INCR;\r
-        ftp->fents = realloc(ftp->fents, ftp->nslots * sizeof(fent_t));\r
-    }\r
-    fep = &ftp->fents[ftp->nfiles++];\r
-    fep->id = id;\r
-    fep->parent = parent;\r
-}\r
-\r
-static void append_pathname(pathname_t *name, const char *str)\r
-{\r
-    int len;\r
-\r
-    len = strlen(str);\r
-#ifdef DEBUG\r
-    if (len && *str == '/' && name->len == 0) {\r
-        RedPrintf("fsstress: append_pathname failure\n");\r
-        chdir(homedir);\r
-        abort();\r
-\r
-    }\r
-#endif\r
-    name->path = realloc(name->path, name->len + 1 + len);\r
-    strcpy(&name->path[name->len], str);\r
-    name->len += len;\r
-}\r
-\r
-static void check_cwd(void)\r
-{\r
-#ifdef DEBUG\r
-    REDSTAT statbuf;\r
-\r
-    if (stat64(".", &statbuf) == 0 && statbuf.st_ino == top_ino)\r
-        return;\r
-    chdir(homedir);\r
-    RedPrintf("fsstress: check_cwd failure\n");\r
-    abort();\r
-\r
-#endif\r
-}\r
-\r
-static int creat_path(pathname_t *name, mode_t mode)\r
-{\r
-    char buf[MAXNAMELEN];\r
-    pathname_t newname;\r
-    int rval;\r
-\r
-    rval = creat(name->path, mode);\r
-    if (rval >= 0 || errno != RED_ENAMETOOLONG)\r
-        return rval;\r
-    separate_pathname(name, buf, &newname);\r
-    if (chdir(buf) == 0) {\r
-        rval = creat_path(&newname, mode);\r
-        chdir("..");\r
-    }\r
-    free_pathname(&newname);\r
-    return rval;\r
-}\r
-\r
-static void dcache_enter(int dirid, int slot)\r
-{\r
-    dcache[dirid % NDCACHE] = slot;\r
-}\r
-\r
-static void dcache_init(void)\r
-{\r
-    int i;\r
-\r
-    for (i = 0; i < NDCACHE; i++)\r
-        dcache[i] = -1;\r
-}\r
-\r
-static fent_t *dcache_lookup(int dirid)\r
-{\r
-    fent_t *fep;\r
-    int i;\r
-\r
-    i = dcache[dirid % NDCACHE];\r
-    if (i >= 0 && (fep = &flist[FT_DIR].fents[i])->id == dirid)\r
-        return fep;\r
-    return NULL;\r
-}\r
-\r
-static void dcache_purge(int dirid)\r
-{\r
-    int *dcp;\r
-\r
-    dcp = &dcache[dirid % NDCACHE];\r
-    if (*dcp >= 0 && flist[FT_DIR].fents[*dcp].id == dirid)\r
-        *dcp = -1;\r
-}\r
-\r
-static void del_from_flist(int ft, int slot)\r
-{\r
-    flist_t *ftp;\r
-\r
-    ftp = &flist[ft];\r
-    if (ft == FT_DIR)\r
-        dcache_purge(ftp->fents[slot].id);\r
-    if (slot != ftp->nfiles - 1) {\r
-        if (ft == FT_DIR)\r
-            dcache_purge(ftp->fents[ftp->nfiles - 1].id);\r
-        ftp->fents[slot] = ftp->fents[--ftp->nfiles];\r
-    } else\r
-        ftp->nfiles--;\r
-}\r
-\r
-static fent_t *dirid_to_fent(int dirid)\r
-{\r
-    fent_t *efep;\r
-    fent_t *fep;\r
-    flist_t *flp;\r
-\r
-    if ((fep = dcache_lookup(dirid)))\r
-        return fep;\r
-    flp = &flist[FT_DIR];\r
-    for (fep = flp->fents, efep = &fep[flp->nfiles]; fep < efep; fep++) {\r
-        if (fep->id == dirid) {\r
-            dcache_enter(dirid, (int)(fep - flp->fents));\r
-            return fep;\r
-        }\r
-    }\r
-    return NULL;\r
-}\r
-\r
-static void doproc(void)\r
-{\r
-    REDSTAT statbuf;\r
-    char buf[10];\r
-    int opno;\r
-    opdesc_t *p;\r
-\r
-    RedSNPrintf(buf, sizeof(buf), "p%x", procid);\r
-    (void)mkdir(buf);\r
-    if (chdir(buf) < 0 || stat64(".", &statbuf) < 0) {\r
-        perror(buf);\r
-        _exit(1);\r
-    }\r
-    top_ino = statbuf.st_ino;\r
-    homedir = getcwd(NULL, 0);\r
-    seed += procid;\r
-    srandom(seed);\r
-    if (namerand)\r
-        namerand = random();\r
-    for (opno = 0; opno < operations; opno++) {\r
-        p = &ops[freq_table[random() % freq_table_size]];\r
-        if ((unsigned long)p->func < 4096)\r
-            abort();\r
-\r
-        p->func(opno, random());\r
-    }\r
-    free(homedir);\r
-}\r
-\r
-static void fent_to_name(pathname_t *name, flist_t *flp, fent_t *fep)\r
-{\r
-    char buf[MAXNAMELEN];\r
-    int i;\r
-    fent_t *pfep;\r
-\r
-    if (fep == NULL)\r
-        return;\r
-    if (fep->parent != -1) {\r
-        pfep = dirid_to_fent(fep->parent);\r
-        fent_to_name(name, &flist[FT_DIR], pfep);\r
-        append_pathname(name, "/");\r
-    }\r
-    i = RedSNPrintf(buf, sizeof(buf), "%c%x", flp->tag, fep->id);\r
-    namerandpad(fep->id, buf, i);\r
-    append_pathname(name, buf);\r
-}\r
-\r
-static void fix_parent(int oldid, int newid)\r
-{\r
-    fent_t *fep;\r
-    flist_t *flp;\r
-    int i;\r
-    int j;\r
-\r
-    for (i = 0, flp = flist; i < FT_nft; i++, flp++) {\r
-        for (j = 0, fep = flp->fents; j < flp->nfiles; j++, fep++) {\r
-            if (fep->parent == oldid)\r
-                fep->parent = newid;\r
-        }\r
-    }\r
-}\r
-\r
-static void free_pathname(pathname_t *name)\r
-{\r
-    if (name->path) {\r
-        free(name->path);\r
-        name->path = NULL;\r
-        name->len = 0;\r
-    }\r
-}\r
-\r
-static int generate_fname(fent_t *fep, int ft, pathname_t *name, int *idp, int *v)\r
-{\r
-    char buf[MAXNAMELEN];\r
-    flist_t *flp;\r
-    int id;\r
-    int j;\r
-    int len;\r
-\r
-    flp = &flist[ft];\r
-    len = RedSNPrintf(buf, sizeof(buf), "%c%x", flp->tag, id = nameseq++);\r
-    namerandpad(id, buf, len);\r
-    if (fep) {\r
-        fent_to_name(name, &flist[FT_DIR], fep);\r
-        append_pathname(name, "/");\r
-    }\r
-    append_pathname(name, buf);\r
-    *idp = id;\r
-    *v = verbose;\r
-    for (j = 0; !*v && j < ilistlen; j++) {\r
-        if (ilist[j] == id) {\r
-            *v = 1;\r
-            break;\r
-        }\r
-    }\r
-    return 1;\r
-}\r
-\r
-static int\r
-get_fname(int which, long r, pathname_t *name, flist_t **flpp, fent_t **fepp, int *v)\r
-{\r
-    int c;\r
-    fent_t *fep;\r
-    flist_t *flp;\r
-    int i;\r
-    int j;\r
-    int x;\r
-\r
-    for (i = 0, c = 0, flp = flist; i < FT_nft; i++, flp++) {\r
-        if (which & (1 << i))\r
-            c += flp->nfiles;\r
-    }\r
-    if (c == 0) {\r
-        if (flpp)\r
-            *flpp = NULL;\r
-        if (fepp)\r
-            *fepp = NULL;\r
-        *v = verbose;\r
-        return 0;\r
-    }\r
-    x = (int)(r % c);\r
-    for (i = 0, c = 0, flp = flist; i < FT_nft; i++, flp++) {\r
-        if (which & (1 << i)) {\r
-            if (x < c + flp->nfiles) {\r
-                fep = &flp->fents[x - c];\r
-                if (name)\r
-                    fent_to_name(name, flp, fep);\r
-                if (flpp)\r
-                    *flpp = flp;\r
-                if (fepp)\r
-                    *fepp = fep;\r
-                *v = verbose;\r
-                for (j = 0; !*v && j < ilistlen; j++) {\r
-                    if (ilist[j] == fep->id) {\r
-                        *v = 1;\r
-                        break;\r
-                    }\r
-                }\r
-                return 1;\r
-            }\r
-            c += flp->nfiles;\r
-        }\r
-    }\r
-#ifdef DEBUG\r
-    RedPrintf("fsstress: get_fname failure\n");\r
-    abort();\r
-#endif\r
-    return -1;\r
-\r
-}\r
-\r
-static void init_pathname(pathname_t *name)\r
-{\r
-    name->len = 0;\r
-    name->path = NULL;\r
-}\r
-\r
-static int link_path(pathname_t *name1, pathname_t *name2)\r
-{\r
-    char buf1[MAXNAMELEN];\r
-    char buf2[MAXNAMELEN];\r
-    int down1;\r
-    pathname_t newname1;\r
-    pathname_t newname2;\r
-    int rval;\r
-\r
-    rval = link(name1->path, name2->path);\r
-    if (rval >= 0 || errno != RED_ENAMETOOLONG)\r
-        return rval;\r
-    separate_pathname(name1, buf1, &newname1);\r
-    separate_pathname(name2, buf2, &newname2);\r
-    if (strcmp(buf1, buf2) == 0) {\r
-        if (chdir(buf1) == 0) {\r
-            rval = link_path(&newname1, &newname2);\r
-            chdir("..");\r
-        }\r
-    } else {\r
-        if (strcmp(buf1, "..") == 0)\r
-            down1 = 0;\r
-        else if (strcmp(buf2, "..") == 0)\r
-            down1 = 1;\r
-        else if (strlen(buf1) == 0)\r
-            down1 = 0;\r
-        else if (strlen(buf2) == 0)\r
-            down1 = 1;\r
-        else\r
-            down1 = MAX(newname1.len, 3 + name2->len) <=\r
-                MAX(3 + name1->len, newname2.len);\r
-        if (down1) {\r
-            free_pathname(&newname2);\r
-            append_pathname(&newname2, "../");\r
-            append_pathname(&newname2, name2->path);\r
-            if (chdir(buf1) == 0) {\r
-                rval = link_path(&newname1, &newname2);\r
-                chdir("..");\r
-            }\r
-        } else {\r
-            free_pathname(&newname1);\r
-            append_pathname(&newname1, "../");\r
-            append_pathname(&newname1, name1->path);\r
-            if (chdir(buf2) == 0) {\r
-                rval = link_path(&newname1, &newname2);\r
-                chdir("..");\r
-            }\r
-        }\r
-    }\r
-    free_pathname(&newname1);\r
-    free_pathname(&newname2);\r
-    return rval;\r
-}\r
-\r
-static int lstat64_path(pathname_t *name, REDSTAT *sbuf)\r
-{\r
-    char buf[MAXNAMELEN];\r
-    pathname_t newname;\r
-    int rval;\r
-\r
-    rval = lstat64(name->path, sbuf);\r
-    if (rval >= 0 || errno != RED_ENAMETOOLONG)\r
-        return rval;\r
-    separate_pathname(name, buf, &newname);\r
-    if (chdir(buf) == 0) {\r
-        rval = lstat64_path(&newname, sbuf);\r
-        chdir("..");\r
-    }\r
-    free_pathname(&newname);\r
-    return rval;\r
-}\r
-\r
-static void make_freq_table(void)\r
-{\r
-    int f;\r
-    int i;\r
-    opdesc_t *p;\r
-\r
-    for (p = ops, f = 0; p < ops_end; p++)\r
-        f += p->freq;\r
-    freq_table = malloc(f * sizeof(*freq_table));\r
-    freq_table_size = f;\r
-    for (p = ops, i = 0; p < ops_end; p++) {\r
-        for (f = 0; f < p->freq; f++, i++)\r
-            freq_table[i] = p->op;\r
-    }\r
-}\r
-\r
-static int mkdir_path(pathname_t *name, mode_t mode)\r
-{\r
-    char buf[MAXNAMELEN];\r
-    pathname_t newname;\r
-    int rval;\r
-\r
-    rval = mkdir(name->path);\r
-    if (rval >= 0 || errno != RED_ENAMETOOLONG)\r
-        return rval;\r
-    separate_pathname(name, buf, &newname);\r
-    if (chdir(buf) == 0) {\r
-        rval = mkdir_path(&newname, mode);\r
-        chdir("..");\r
-    }\r
-    free_pathname(&newname);\r
-    return rval;\r
-}\r
-\r
-static void namerandpad(int id, char *buf, int len)\r
-{\r
-    int bucket;\r
-    static int buckets[8] = {0};\r
-    static int bucket_count = 0;\r
-    int bucket_value;\r
-    int i;\r
-    int padlen;\r
-    int padmod;\r
-\r
-    if (namerand == 0)\r
-        return;\r
-\r
-    /*  buckets[] used to be a statically initialized array with the following\r
-        initializer: { 2, 4, 8, 16, 32, 64, 128, MAXNAMELEN - 1 }\r
-\r
-        The problem is that with Reliance Edge, the maximum name length might be\r
-        less than 128.  So the below code populates buckets[] in a similar\r
-        fashion but avoids name lengths longer than the maximum.  For example,\r
-        if the max name is 20, the resulting array is { 2, 4, 8, 16, 20 }.\r
-    */\r
-    if (!bucket_count) {\r
-        bucket_count = sizeof(buckets) / sizeof(buckets[0]);\r
-        bucket_value = 2;\r
-        for (i = 0; i < bucket_count; i++) {\r
-            if (bucket_value > 128 || bucket_value >= (int)MAXNAMELEN - 1)\r
-                break;\r
-            buckets[i] = bucket_value;\r
-            bucket_value *= 2;\r
-        }\r
-        if (i < bucket_count) {\r
-            buckets[i] = MAXNAMELEN - 1;\r
-            i++;\r
-        }\r
-        bucket_count = i;\r
-    }\r
-\r
-    bucket = (id ^ namerand) % bucket_count;\r
-    padmod = buckets[bucket] + 1 - len;\r
-    if (padmod <= 0)\r
-        return;\r
-    padlen = (id ^ namerand) % padmod;\r
-    if (padlen) {\r
-        memset(&buf[len], 'X', padlen);\r
-        buf[len + padlen] = '\0';\r
-    }\r
-}\r
-\r
-static int open_path(pathname_t *name, int oflag)\r
-{\r
-    char buf[MAXNAMELEN];\r
-    pathname_t newname;\r
-    int rval;\r
-\r
-    rval = open(name->path, oflag);\r
-    if (rval >= 0 || errno != RED_ENAMETOOLONG)\r
-        return rval;\r
-    separate_pathname(name, buf, &newname);\r
-    if (chdir(buf) == 0) {\r
-        rval = open_path(&newname, oflag);\r
-        chdir("..");\r
-    }\r
-    free_pathname(&newname);\r
-    return rval;\r
-}\r
-\r
-static DIR *opendir_path(pathname_t *name)\r
-{\r
-    char buf[MAXNAMELEN];\r
-    pathname_t newname;\r
-    DIR *rval;\r
-\r
-    rval = opendir(name->path);\r
-    if (rval || errno != RED_ENAMETOOLONG)\r
-        return rval;\r
-    separate_pathname(name, buf, &newname);\r
-    if (chdir(buf) == 0) {\r
-        rval = opendir_path(&newname);\r
-        chdir("..");\r
-    }\r
-    free_pathname(&newname);\r
-    return rval;\r
-}\r
-\r
-static int rename_path(pathname_t *name1, pathname_t *name2)\r
-{\r
-    char buf1[MAXNAMELEN];\r
-    char buf2[MAXNAMELEN];\r
-    int down1;\r
-    pathname_t newname1;\r
-    pathname_t newname2;\r
-    int rval;\r
-\r
-    rval = rename(name1->path, name2->path);\r
-    if (rval >= 0 || errno != RED_ENAMETOOLONG)\r
-        return rval;\r
-    separate_pathname(name1, buf1, &newname1);\r
-    separate_pathname(name2, buf2, &newname2);\r
-    if (strcmp(buf1, buf2) == 0) {\r
-        if (chdir(buf1) == 0) {\r
-            rval = rename_path(&newname1, &newname2);\r
-            chdir("..");\r
-        }\r
-    } else {\r
-        if (strcmp(buf1, "..") == 0)\r
-            down1 = 0;\r
-        else if (strcmp(buf2, "..") == 0)\r
-            down1 = 1;\r
-        else if (strlen(buf1) == 0)\r
-            down1 = 0;\r
-        else if (strlen(buf2) == 0)\r
-            down1 = 1;\r
-        else\r
-            down1 = MAX(newname1.len, 3 + name2->len) <=\r
-                MAX(3 + name1->len, newname2.len);\r
-        if (down1) {\r
-            free_pathname(&newname2);\r
-            append_pathname(&newname2, "../");\r
-            append_pathname(&newname2, name2->path);\r
-            if (chdir(buf1) == 0) {\r
-                rval = rename_path(&newname1, &newname2);\r
-                chdir("..");\r
-            }\r
-        } else {\r
-            free_pathname(&newname1);\r
-            append_pathname(&newname1, "../");\r
-            append_pathname(&newname1, name1->path);\r
-            if (chdir(buf2) == 0) {\r
-                rval = rename_path(&newname1, &newname2);\r
-                chdir("..");\r
-            }\r
-        }\r
-    }\r
-    free_pathname(&newname1);\r
-    free_pathname(&newname2);\r
-    return rval;\r
-}\r
-\r
-static int rmdir_path(pathname_t *name)\r
-{\r
-    char buf[MAXNAMELEN];\r
-    pathname_t newname;\r
-    int rval;\r
-\r
-    rval = rmdir(name->path);\r
-    if (rval >= 0 || errno != RED_ENAMETOOLONG)\r
-        return rval;\r
-    separate_pathname(name, buf, &newname);\r
-    if (chdir(buf) == 0) {\r
-        rval = rmdir_path(&newname);\r
-        chdir("..");\r
-    }\r
-    free_pathname(&newname);\r
-    return rval;\r
-}\r
-\r
-static void separate_pathname(pathname_t *name, char *buf, pathname_t *newname)\r
-{\r
-    char *slash;\r
-\r
-    init_pathname(newname);\r
-    slash = strchr(name->path, '/');\r
-    if (slash == NULL) {\r
-        buf[0] = '\0';\r
-        return;\r
-    }\r
-    *slash = '\0';\r
-    strcpy(buf, name->path);\r
-    *slash = '/';\r
-    append_pathname(newname, slash + 1);\r
-}\r
-\r
-static int stat64_path(pathname_t *name, REDSTAT *sbuf)\r
-{\r
-    char buf[MAXNAMELEN];\r
-    pathname_t newname;\r
-    int rval;\r
-\r
-    rval = stat64(name->path, sbuf);\r
-    if (rval >= 0 || errno != RED_ENAMETOOLONG)\r
-        return rval;\r
-    separate_pathname(name, buf, &newname);\r
-    if (chdir(buf) == 0) {\r
-        rval = stat64_path(&newname, sbuf);\r
-        chdir("..");\r
-    }\r
-    free_pathname(&newname);\r
-    return rval;\r
-}\r
-\r
-static int truncate64_path(pathname_t *name, off64_t length)\r
-{\r
-    char buf[MAXNAMELEN];\r
-    pathname_t newname;\r
-    int rval;\r
-\r
-    rval = truncate64(name->path, length);\r
-    if (rval >= 0 || errno != RED_ENAMETOOLONG)\r
-        return rval;\r
-    separate_pathname(name, buf, &newname);\r
-    if (chdir(buf) == 0) {\r
-        rval = truncate64_path(&newname, length);\r
-        chdir("..");\r
-    }\r
-    free_pathname(&newname);\r
-    return rval;\r
-}\r
-\r
-static int unlink_path(pathname_t *name)\r
-{\r
-    char buf[MAXNAMELEN];\r
-    pathname_t newname;\r
-    int rval;\r
-\r
-    rval = unlink(name->path);\r
-    if (rval >= 0 || errno != RED_ENAMETOOLONG)\r
-        return rval;\r
-    separate_pathname(name, buf, &newname);\r
-    if (chdir(buf) == 0) {\r
-        rval = unlink_path(&newname);\r
-        chdir("..");\r
-    }\r
-    free_pathname(&newname);\r
-    return rval;\r
-}\r
-\r
-static void usage(const char *progname)\r
-{\r
-    RedPrintf("usage: %s VolumeID [Options]\n", progname);\r
-    RedPrintf("File system stress test.\n\n");\r
-    RedPrintf("Where:\n");\r
-    RedPrintf("  VolumeID\n");\r
-    RedPrintf("      A volume number (e.g., 2) or a volume path prefix (e.g., VOL1: or /data)\n");\r
-    RedPrintf("      of the volume to test.\n");\r
-    RedPrintf("And 'Options' are any of the following:\n");\r
-    RedPrintf("  --no-cleanup, -c\n");\r
-    RedPrintf("      Specifies not to remove files (cleanup) after execution\n");\r
-    RedPrintf("  --loops=count, -l count\n");\r
-    RedPrintf("      Specifies the number of times the entire test should loop.  Use 0 for\n");\r
-    RedPrintf("      infinite.  Default 1.\n");\r
-    RedPrintf("  --nops=count, -n count\n");\r
-    RedPrintf("      Specifies the number of operations to run (default 10000).\n");\r
-    RedPrintf("  --namepad, -r\n");\r
-    RedPrintf("      Specifies to use random name padding (resulting in longer names).\n");\r
-    RedPrintf("  --seed=value, -s value\n");\r
-    RedPrintf("      Specifies the seed for the random number generator (default timestamp).\n");\r
-    RedPrintf("  --verbose, -v\n");\r
-    RedPrintf("      Specifies verbose mode (without this, test is very quiet).\n");\r
-    RedPrintf("  --dev=devname, -D devname\n");\r
-    RedPrintf("      Specifies the device name.  This is typically only meaningful when\n");\r
-    RedPrintf("      running the test on a host machine.  This can be \"ram\" to test on a RAM\n");\r
-    RedPrintf("      disk, the path and name of a file disk (e.g., red.bin); or an OS-specific\n");\r
-    RedPrintf("      reference to a device (on Windows, a drive letter like G: or a device name\n");\r
-    RedPrintf("      like \\\\.\\PhysicalDrive7).\n");\r
-    RedPrintf("  --help, -H\n");\r
-    RedPrintf("      Prints this usage text and exits.\n\n");\r
-    RedPrintf("Warning: This test will format the volume -- destroying all existing data.\n\n");\r
-}\r
-\r
-static void creat_f(int opno, long r)\r
-{\r
-    int e;\r
-    int e1;\r
-    pathname_t f;\r
-    int fd;\r
-    fent_t *fep;\r
-    int id;\r
-    int parid;\r
-    int type;\r
-    int v;\r
-    int v1;\r
-    int esz = 0;\r
-\r
-    if (!get_fname(FT_DIRm, r, NULL, NULL, &fep, &v1))\r
-        parid = -1;\r
-    else\r
-        parid = fep->id;\r
-    init_pathname(&f);\r
-    type = rtpct ? ((int)(random() % 100) > rtpct ? FT_REG : FT_RTF) : FT_REG;\r
-    e = generate_fname(fep, type, &f, &id, &v);\r
-    v |= v1;\r
-    if (!e) {\r
-        if (v) {\r
-            fent_to_name(&f, &flist[FT_DIR], fep);\r
-            RedPrintf("%d/%d: creat - no filename from %s\n",\r
-                   procid, opno, f.path);\r
-        }\r
-        free_pathname(&f);\r
-        return;\r
-    }\r
-    fd = creat_path(&f, 0666);\r
-    e = fd < 0 ? errno : 0;\r
-    e1 = 0;\r
-    check_cwd();\r
-    esz = 0;\r
-    if (fd >= 0) {\r
-        add_to_flist(type, id, parid);\r
-        close(fd);\r
-    }\r
-    if (v)\r
-        RedPrintf("%d/%d: creat %s x:%d %d %d\n", procid, opno, f.path,\r
-               esz, e, e1);\r
-    free_pathname(&f);\r
-}\r
-\r
-static void fdatasync_f(int opno, long r)\r
-{\r
-    int e;\r
-    pathname_t f;\r
-    int fd;\r
-    int v;\r
-\r
-    init_pathname(&f);\r
-    if (!get_fname(FT_REGFILE, r, &f, NULL, NULL, &v)) {\r
-        if (v)\r
-            RedPrintf("%d/%d: fdatasync - no filename\n",\r
-                   procid, opno);\r
-        free_pathname(&f);\r
-        return;\r
-    }\r
-    fd = open_path(&f, O_WRONLY);\r
-    e = fd < 0 ? errno : 0;\r
-    check_cwd();\r
-    if (fd < 0) {\r
-        if (v)\r
-            RedPrintf("%d/%d: fdatasync - open %s failed %d\n",\r
-                   procid, opno, f.path, e);\r
-        free_pathname(&f);\r
-        return;\r
-    }\r
-    e = fdatasync(fd) < 0 ? errno : 0;\r
-    if (v)\r
-        RedPrintf("%d/%d: fdatasync %s %d\n", procid, opno, f.path, e);\r
-    free_pathname(&f);\r
-    close(fd);\r
-}\r
-\r
-static void fsync_f(int opno, long r)\r
-{\r
-    int e;\r
-    pathname_t f;\r
-    int fd;\r
-    int v;\r
-\r
-    init_pathname(&f);\r
-    if (!get_fname(FT_REGFILE, r, &f, NULL, NULL, &v)) {\r
-        if (v)\r
-            RedPrintf("%d/%d: fsync - no filename\n", procid, opno);\r
-        free_pathname(&f);\r
-        return;\r
-    }\r
-    fd = open_path(&f, O_WRONLY);\r
-    e = fd < 0 ? errno : 0;\r
-    check_cwd();\r
-    if (fd < 0) {\r
-        if (v)\r
-            RedPrintf("%d/%d: fsync - open %s failed %d\n",\r
-                   procid, opno, f.path, e);\r
-        free_pathname(&f);\r
-        return;\r
-    }\r
-    e = fsync(fd) < 0 ? errno : 0;\r
-    if (v)\r
-        RedPrintf("%d/%d: fsync %s %d\n", procid, opno, f.path, e);\r
-    free_pathname(&f);\r
-    close(fd);\r
-}\r
-\r
-static void getdents_f(int opno, long r)\r
-{\r
-    DIR *dir;\r
-    pathname_t f;\r
-    int v;\r
-\r
-    init_pathname(&f);\r
-    if (!get_fname(FT_DIRm, r, &f, NULL, NULL, &v))\r
-        append_pathname(&f, ".");\r
-    dir = opendir_path(&f);\r
-    check_cwd();\r
-    if (dir == NULL) {\r
-        if (v)\r
-            RedPrintf("%d/%d: getdents - can't open %s\n",\r
-                   procid, opno, f.path);\r
-        free_pathname(&f);\r
-        return;\r
-    }\r
-    while (readdir64(dir) != NULL)\r
-        continue;\r
-    if (v)\r
-        RedPrintf("%d/%d: getdents %s 0\n", procid, opno, f.path);\r
-    free_pathname(&f);\r
-    closedir(dir);\r
-}\r
-\r
-static void link_f(int opno, long r)\r
-{\r
-    int e;\r
-    pathname_t f;\r
-    fent_t *fep;\r
-    flist_t *flp;\r
-    int id;\r
-    pathname_t l;\r
-    int parid;\r
-    int v;\r
-    int v1;\r
-\r
-    init_pathname(&f);\r
-    if (!get_fname(FT_NOTDIR, r, &f, &flp, NULL, &v1)) {\r
-        if (v1)\r
-            RedPrintf("%d/%d: link - no file\n", procid, opno);\r
-        free_pathname(&f);\r
-        return;\r
-    }\r
-    if (!get_fname(FT_DIRm, random(), NULL, NULL, &fep, &v))\r
-        parid = -1;\r
-    else\r
-        parid = fep->id;\r
-    v |= v1;\r
-    init_pathname(&l);\r
-    e = generate_fname(fep, (int)(flp - flist), &l, &id, &v1);\r
-    v |= v1;\r
-    if (!e) {\r
-        if (v) {\r
-            fent_to_name(&l, &flist[FT_DIR], fep);\r
-            RedPrintf("%d/%d: link - no filename from %s\n",\r
-                   procid, opno, l.path);\r
-        }\r
-        free_pathname(&l);\r
-        free_pathname(&f);\r
-        return;\r
-    }\r
-    e = link_path(&f, &l) < 0 ? errno : 0;\r
-    check_cwd();\r
-    if (e == 0)\r
-        add_to_flist((int)(flp - flist), id, parid);\r
-    if (v)\r
-        RedPrintf("%d/%d: link %s %s %d\n", procid, opno, f.path, l.path,\r
-               e);\r
-    free_pathname(&l);\r
-    free_pathname(&f);\r
-}\r
-\r
-static void mkdir_f(int opno, long r)\r
-{\r
-    int e;\r
-    pathname_t f;\r
-    fent_t *fep;\r
-    int id;\r
-    int parid;\r
-    int v;\r
-    int v1;\r
-\r
-    if (!get_fname(FT_DIRm, r, NULL, NULL, &fep, &v))\r
-        parid = -1;\r
-    else\r
-        parid = fep->id;\r
-    init_pathname(&f);\r
-    e = generate_fname(fep, FT_DIR, &f, &id, &v1);\r
-    v |= v1;\r
-    if (!e) {\r
-        if (v) {\r
-            fent_to_name(&f, &flist[FT_DIR], fep);\r
-            RedPrintf("%d/%d: mkdir - no filename from %s\n",\r
-                   procid, opno, f.path);\r
-        }\r
-        free_pathname(&f);\r
-        return;\r
-    }\r
-    e = mkdir_path(&f, 0777) < 0 ? errno : 0;\r
-    check_cwd();\r
-    if (e == 0)\r
-        add_to_flist(FT_DIR, id, parid);\r
-    if (v)\r
-        RedPrintf("%d/%d: mkdir %s %d\n", procid, opno, f.path, e);\r
-    free_pathname(&f);\r
-}\r
-\r
-static void read_f(int opno, long r)\r
-{\r
-    char *buf;\r
-    int e;\r
-    pathname_t f;\r
-    int fd;\r
-    uint32_t len;\r
-    __int64_t lr;\r
-    off64_t off;\r
-    REDSTAT stb;\r
-    int v;\r
-\r
-    init_pathname(&f);\r
-    if (!get_fname(FT_REGFILE, r, &f, NULL, NULL, &v)) {\r
-        if (v)\r
-            RedPrintf("%d/%d: read - no filename\n", procid, opno);\r
-        free_pathname(&f);\r
-        return;\r
-    }\r
-    fd = open_path(&f, O_RDONLY);\r
-    e = fd < 0 ? errno : 0;\r
-    check_cwd();\r
-    if (fd < 0) {\r
-        if (v)\r
-            RedPrintf("%d/%d: read - open %s failed %d\n",\r
-                   procid, opno, f.path, e);\r
-        free_pathname(&f);\r
-        return;\r
-    }\r
-    if (fstat64(fd, &stb) < 0) {\r
-        if (v)\r
-            RedPrintf("%d/%d: read - fstat64 %s failed %d\n",\r
-                   procid, opno, f.path, errno);\r
-        free_pathname(&f);\r
-        close(fd);\r
-        return;\r
-    }\r
-    if (stb.st_size == 0) {\r
-        if (v)\r
-            RedPrintf("%d/%d: read - %s zero size\n", procid, opno,\r
-                   f.path);\r
-        free_pathname(&f);\r
-        close(fd);\r
-        return;\r
-    }\r
-    lr = ((__int64_t) random() << 32) + random();\r
-    off = (off64_t) (lr % stb.st_size);\r
-    lseek64(fd, off, SEEK_SET);\r
-    len = (random() % (getpagesize() * 4)) + 1;\r
-    buf = malloc(len);\r
-    e = read(fd, buf, len) < 0 ? errno : 0;\r
-    free(buf);\r
-    if (v)\r
-        RedPrintf("%d/%d: read %s [%lld,%ld] %d\n",\r
-               procid, opno, f.path, (long long)off, (long int)len, e);\r
-    free_pathname(&f);\r
-    close(fd);\r
-}\r
-\r
-static void rename_f(int opno, long r)\r
-{\r
-    fent_t *dfep;\r
-    int e;\r
-    pathname_t f;\r
-    fent_t *fep;\r
-    flist_t *flp;\r
-    int id;\r
-    pathname_t newf;\r
-    int oldid;\r
-    int parid;\r
-    int v;\r
-    int v1;\r
-\r
-    init_pathname(&f);\r
-    if (!get_fname(FT_ANYm, r, &f, &flp, &fep, &v1)) {\r
-        if (v1)\r
-            RedPrintf("%d/%d: rename - no filename\n", procid, opno);\r
-        free_pathname(&f);\r
-        return;\r
-    }\r
-    if (!get_fname(FT_DIRm, random(), NULL, NULL, &dfep, &v))\r
-        parid = -1;\r
-    else\r
-        parid = dfep->id;\r
-    v |= v1;\r
-    init_pathname(&newf);\r
-    e = generate_fname(dfep, (int)(flp - flist), &newf, &id, &v1);\r
-    v |= v1;\r
-    if (!e) {\r
-        if (v) {\r
-            fent_to_name(&f, &flist[FT_DIR], dfep);\r
-            RedPrintf("%d/%d: rename - no filename from %s\n",\r
-                   procid, opno, f.path);\r
-        }\r
-        free_pathname(&newf);\r
-        free_pathname(&f);\r
-        return;\r
-    }\r
-    e = rename_path(&f, &newf) < 0 ? errno : 0;\r
-    check_cwd();\r
-    if (e == 0) {\r
-        if (flp - flist == FT_DIR) {\r
-            oldid = fep->id;\r
-            fix_parent(oldid, id);\r
-        }\r
-        del_from_flist((int)(flp - flist), (int)(fep - flp->fents));\r
-        add_to_flist((int)(flp - flist), id, parid);\r
-    }\r
-    if (v)\r
-        RedPrintf("%d/%d: rename %s to %s %d\n", procid, opno, f.path,\r
-               newf.path, e);\r
-    free_pathname(&newf);\r
-    free_pathname(&f);\r
-}\r
-\r
-static void rmdir_f(int opno, long r)\r
-{\r
-    int e;\r
-    pathname_t f;\r
-    fent_t *fep;\r
-    int v;\r
-\r
-    init_pathname(&f);\r
-    if (!get_fname(FT_DIRm, r, &f, NULL, &fep, &v)) {\r
-        if (v)\r
-            RedPrintf("%d/%d: rmdir - no directory\n", procid, opno);\r
-        free_pathname(&f);\r
-        return;\r
-    }\r
-    e = rmdir_path(&f) < 0 ? errno : 0;\r
-    check_cwd();\r
-    if (e == 0)\r
-        del_from_flist(FT_DIR, (int)(fep - flist[FT_DIR].fents));\r
-    if (v)\r
-        RedPrintf("%d/%d: rmdir %s %d\n", procid, opno, f.path, e);\r
-    free_pathname(&f);\r
-}\r
-\r
-static void stat_f(int opno, long r)\r
-{\r
-    int e;\r
-    pathname_t f;\r
-    REDSTAT stb;\r
-    int v;\r
-\r
-    init_pathname(&f);\r
-    if (!get_fname(FT_ANYm, r, &f, NULL, NULL, &v)) {\r
-        if (v)\r
-            RedPrintf("%d/%d: stat - no entries\n", procid, opno);\r
-        free_pathname(&f);\r
-        return;\r
-    }\r
-    e = lstat64_path(&f, &stb) < 0 ? errno : 0;\r
-    check_cwd();\r
-    if (v)\r
-        RedPrintf("%d/%d: stat %s %d\n", procid, opno, f.path, e);\r
-    free_pathname(&f);\r
-}\r
-\r
-static void truncate_f(int opno, long r)\r
-{\r
-    int e;\r
-    pathname_t f;\r
-    __int64_t lr;\r
-    off64_t off;\r
-    REDSTAT stb;\r
-    int v;\r
-\r
-    init_pathname(&f);\r
-    if (!get_fname(FT_REGFILE, r, &f, NULL, NULL, &v)) {\r
-        if (v)\r
-            RedPrintf("%d/%d: truncate - no filename\n", procid, opno);\r
-        free_pathname(&f);\r
-        return;\r
-    }\r
-    e = stat64_path(&f, &stb) < 0 ? errno : 0;\r
-    check_cwd();\r
-    if (e > 0) {\r
-        if (v)\r
-            RedPrintf("%d/%d: truncate - stat64 %s failed %d\n",\r
-                   procid, opno, f.path, e);\r
-        free_pathname(&f);\r
-        return;\r
-    }\r
-    lr = ((__int64_t) random() << 32) + random();\r
-    off = lr % MIN(stb.st_size + (1024 * 1024), MAXFSIZE);\r
-    off %= maxfsize;\r
-    e = truncate64_path(&f, off) < 0 ? errno : 0;\r
-    check_cwd();\r
-    if (v)\r
-        RedPrintf("%d/%d: truncate %s %lld %d\n", procid, opno, f.path,\r
-               (long long)off, e);\r
-    free_pathname(&f);\r
-}\r
-\r
-static void unlink_f(int opno, long r)\r
-{\r
-    int e;\r
-    pathname_t f;\r
-    fent_t *fep;\r
-    flist_t *flp;\r
-    int v;\r
-\r
-    init_pathname(&f);\r
-    if (!get_fname(FT_NOTDIR, r, &f, &flp, &fep, &v)) {\r
-        if (v)\r
-            RedPrintf("%d/%d: unlink - no file\n", procid, opno);\r
-        free_pathname(&f);\r
-        return;\r
-    }\r
-    e = unlink_path(&f) < 0 ? errno : 0;\r
-    check_cwd();\r
-    if (e == 0)\r
-        del_from_flist((int)(flp - flist), (int)(fep - flp->fents));\r
-    if (v)\r
-        RedPrintf("%d/%d: unlink %s %d\n", procid, opno, f.path, e);\r
-    free_pathname(&f);\r
-}\r
-\r
-static void write_f(int opno, long r)\r
-{\r
-    char *buf;\r
-    int e;\r
-    pathname_t f;\r
-    int fd;\r
-    uint32_t len;\r
-    __int64_t lr;\r
-    off64_t off;\r
-    REDSTAT stb;\r
-    int v;\r
-\r
-    init_pathname(&f);\r
-    if (!get_fname(FT_REGm, r, &f, NULL, NULL, &v)) {\r
-        if (v)\r
-            RedPrintf("%d/%d: write - no filename\n", procid, opno);\r
-        free_pathname(&f);\r
-        return;\r
-    }\r
-    fd = open_path(&f, O_WRONLY);\r
-    e = fd < 0 ? errno : 0;\r
-    check_cwd();\r
-    if (fd < 0) {\r
-        if (v)\r
-            RedPrintf("%d/%d: write - open %s failed %d\n",\r
-                   procid, opno, f.path, e);\r
-        free_pathname(&f);\r
-        return;\r
-    }\r
-    if (fstat64(fd, &stb) < 0) {\r
-        if (v)\r
-            RedPrintf("%d/%d: write - fstat64 %s failed %d\n",\r
-                   procid, opno, f.path, errno);\r
-        free_pathname(&f);\r
-        close(fd);\r
-        return;\r
-    }\r
-    lr = ((__int64_t) random() << 32) + random();\r
-    off = (off64_t) (lr % MIN(stb.st_size + (1024 * 1024), MAXFSIZE));\r
-    off %= maxfsize;\r
-    lseek64(fd, off, SEEK_SET);\r
-    len = (random() % (getpagesize() * 4)) + 1;\r
-    buf = malloc(len);\r
-    memset(buf, nameseq & 0xff, len);\r
-    e = write(fd, buf, len) < 0 ? errno : 0;\r
-    free(buf);\r
-    if (v)\r
-        RedPrintf("%d/%d: write %s [%lld,%ld] %d\n",\r
-               procid, opno, f.path, (long long)off, (long int)len, e);\r
-    free_pathname(&f);\r
-    close(fd);\r
-}\r
-\r
-\r
-#if REDCONF_CHECKER == 1\r
-static void check_f(int opno, long r)\r
-{\r
-    int32_t ret;\r
-    const char *pszVolume = gpRedVolConf->pszPathPrefix;\r
-\r
-    (void)r;\r
-\r
-    errno = 0;\r
-\r
-    ret = red_transact(pszVolume);\r
-\r
-    if(ret == 0)\r
-    {\r
-        ret = red_umount(pszVolume);\r
-\r
-        if(ret == 0)\r
-        {\r
-            int32_t ret2;\r
-\r
-            errno = -RedCoreVolCheck();\r
-            if(errno != 0)\r
-            {\r
-                ret = -1;\r
-            }\r
-\r
-            ret2 = red_mount(pszVolume);\r
-\r
-            if(ret == 0)\r
-            {\r
-                ret = ret2;\r
-            }\r
-\r
-            if(ret2 != 0)\r
-            {\r
-                exit(1);\r
-            }\r
-        }\r
-    }\r
-\r
-    if (verbose)\r
-    {\r
-        RedPrintf("%d/%d: check %s %d\n", procid, opno, pszVolume, errno);\r
-    }\r
-}\r
-#endif\r
-\r
-\r
-#endif /* FSSTRESS_SUPPORTED */\r
-\r
+/*
+ * Copyright (c) 2000 Silicon Graphics, Inc.  All Rights Reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of version 2 of the GNU General Public License as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it would be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+ *
+ * Further, this software is distributed without any warranty that it is
+ * free of the rightful claim of any third person regarding infringement
+ * or the like.  Any license provided herein, whether implied or
+ * otherwise, applies only to this software file.  Patent licenses, if
+ * any, provided herein do not apply to combinations of this program with
+ * other software, or any other product whatsoever.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Contact information: Silicon Graphics, Inc., 1600 Amphitheatre Pkwy,
+ * Mountain View, CA  94043, or:
+ *
+ * http://www.sgi.com
+ *
+ * For further information regarding this notice, see:
+ *
+ * http://oss.sgi.com/projects/GenInfo/SGIGPLNoticeExplan/
+ */
+/** @file
+    @brief File system stress test.
+
+    This version of SGI fsstress has been modified to be single-threaded and to
+    work with the Reliance Edge POSIX-like API.
+*/
+#include <stdio.h>
+#include <stdlib.h>
+#include <limits.h>
+#include <string.h>
+#include <stdarg.h>
+#include <time.h>
+
+#include <redposix.h>
+#include <redtests.h>
+
+#if FSSTRESS_SUPPORTED
+
+#include "redposixcompat.h"
+
+#include <redosserv.h>
+#include <redutils.h>
+#include <redmacs.h>
+#include <redvolume.h>
+#include <redgetopt.h>
+#include <redtoolcmn.h>
+
+#if REDCONF_CHECKER == 1
+#include <redcoreapi.h>
+#endif
+
+
+/*  Create POSIX types.  Use #define to avoid name conflicts in those
+    environments where the type names already exist.
+*/
+#define off_t int64_t
+#define off64_t off_t
+#define ino_t uint32_t
+#define mode_t uint16_t
+#define __int64_t int64_t
+
+
+/** @brief Generate a random number.
+
+    @return A nonnegative random number.
+*/
+#define random() ((int)(RedRand32(NULL) & 0x7FFFFFFF))
+
+
+/** @brief Seed the random number generator.
+*/
+#define srandom(seed) RedRandSeed(seed)
+
+
+#define _exit(status) exit(status)
+#define getpagesize() 4096U
+#define getpid() 1
+
+
+/** @brief Determine the maximum file size.
+
+    This is used for the MAXFSSIZE macro.
+*/
+static uint64_t MaxFileSize(void)
+{
+    REDSTATFS   info;
+    int32_t     iStatus;
+    REDSTATUS   errnoSave = errno;
+    uint64_t    ullMaxFileSize;
+
+    iStatus = red_statvfs("", &info);
+    if(iStatus == 0)
+    {
+        ullMaxFileSize = info.f_maxfsize;
+    }
+    else
+    {
+        /*  This function does not change errno.
+        */
+        errno = errnoSave;
+
+        ullMaxFileSize = 0x7FFFFFFFU;
+    }
+
+    return ullMaxFileSize;
+}
+
+
+/*-------------------------------------------------------------------
+    Simulated current working directory support
+-------------------------------------------------------------------*/
+
+
+/*  Forward declaration for red_chdir().
+*/
+static int red_stat(const char *pszPath, REDSTAT *pStat);
+
+/*  The simulated CWD functions.
+*/
+#undef chdir
+#undef getcwd
+#define chdir(path) red_chdir(path)
+#define getcwd(buf, size) red_getcwd(buf, size)
+
+
+/*  Redefine the path-based APIs to call MakeFullPath() on their arguments
+    since there is no CWD support in the red_*() APIs.
+*/
+#undef open
+#undef unlink
+#undef mkdir
+#undef rmdir
+#undef rename
+#undef link
+#undef opendir
+#define open(path, oflag) red_open(MakeFullPath(path), oflag)
+#define unlink(path) red_unlink(MakeFullPath(path))
+#define mkdir(path) red_mkdir(MakeFullPath(path))
+#define rmdir(path) red_rmdir(MakeFullPath(path))
+#define rename(old, new) red_rename(MakeFullPath(old), MakeFullPath(new))
+#define link(path, hardlink) red_link(MakeFullPath(path), MakeFullPath(hardlink))
+#define opendir(path) red_opendir(MakeFullPath(path))
+
+#define FSSTRESS_BUF_SIZE 1024U
+
+/*  Stores the simulated current working directory.
+*/
+static char szLocalCwd[FSSTRESS_BUF_SIZE] = "/";
+
+
+/** @brief Change the current working directory.
+
+    This function only supports a subset of what is possible with POSIX chdir().
+
+    @param pszPath  The new current working directory.
+
+    @return Upon successful completion, 0 shall be returned.  Otherwise, -1
+            shall be returned, and errno shall be set to indicate the error.
+*/
+static int red_chdir(
+    const char *pszPath)
+{
+    uint32_t    ulIdx;
+    int         iErrno = 0;
+
+    if(strcmp(pszPath, "..") == 0)
+    {
+        uint32_t ulLastSlashIdx = 0U;
+
+        /*  Chop off the last path separator and everything after it, so that
+            "/foo/bar/baz" becomes "/foo/bar", moving the CWD up one directory.
+        */
+        for(ulIdx = 0U; szLocalCwd[ulIdx] != '\0'; ulIdx++)
+        {
+            if(szLocalCwd[ulIdx] == '/')
+            {
+                ulLastSlashIdx = ulIdx;
+            }
+        }
+
+        if(ulLastSlashIdx != 0U)
+        {
+            szLocalCwd[ulLastSlashIdx] = '\0';
+        }
+    }
+    else
+    {
+        char    szOldCwd[FSSTRESS_BUF_SIZE];
+
+        /*  chdir() must have no effect on the CWD if it fails, so save the CWD
+            so we can revert it if necessary.
+        */
+        strcpy(szOldCwd, szLocalCwd);
+
+        if(pszPath[0U] == '/')
+        {
+            if(strlen(pszPath) >= sizeof(szLocalCwd))
+            {
+                iErrno = RED_ENAMETOOLONG;
+            }
+            else
+            {
+                strcpy(szLocalCwd, pszPath);
+            }
+        }
+        else
+        {
+            ulIdx = strlen(szLocalCwd);
+
+            if((ulIdx + 1U + strlen(pszPath)) >= sizeof(szLocalCwd))
+            {
+                iErrno = RED_ENAMETOOLONG;
+            }
+            else
+            {
+                if(szLocalCwd[1U] != '\0')
+                {
+                    szLocalCwd[ulIdx] = '/';
+                    ulIdx++;
+                }
+
+                strcpy(&szLocalCwd[ulIdx], pszPath);
+            }
+        }
+
+        if(iErrno == 0)
+        {
+            REDSTAT s;
+            int     iStatus;
+
+            iStatus = red_stat(szLocalCwd, &s);
+            if(iStatus != 0)
+            {
+                iErrno = errno;
+            }
+            else if(!S_ISDIR(s.st_mode))
+            {
+                iErrno = RED_ENOTDIR;
+            }
+            else
+            {
+                /*  No error, new CWD checks out.
+                */
+            }
+        }
+
+        if(iErrno != 0)
+        {
+            strcpy(szLocalCwd, szOldCwd);
+        }
+    }
+
+    if(iErrno != 0)
+    {
+        errno = iErrno;
+    }
+
+    return iErrno == 0 ? 0 : -1;
+}
+
+
+/** @brief Retrieve the current working directory.
+
+    @param pszBuf   On successful return, populated with the current working
+                    directory.  If NULL, memory will be allocated for the CWD
+                    and returned by this function.
+    @param nSize    The size of @p pszBuf.
+
+    @return On success, if @p pszBuf was non-NULL, returns @p pszBuf; if
+            @p pszBuf was NULL, returns an allocated buffer populated with the
+            CWD which must be freed by the caller.  On failure, returns NULL
+            and errno will be set.
+*/
+static char *red_getcwd(
+    char   *pszBuf,
+    size_t  nSize)
+{
+    char   *pszRet;
+
+    if(pszBuf == NULL)
+    {
+        pszRet = malloc(strlen(szLocalCwd) + 1U);
+        if(pszRet == NULL)
+        {
+            errno = RED_ENOMEM;
+        }
+        else
+        {
+            strcpy(pszRet, szLocalCwd);
+        }
+    }
+    else if(nSize < strlen(szLocalCwd) + 1U)
+    {
+        errno = RED_ERANGE;
+        pszRet = NULL;
+    }
+    else
+    {
+        strcpy(pszBuf, szLocalCwd);
+        pszRet = pszBuf;
+    }
+
+    return pszRet;
+}
+
+
+/** @brief Make a relative path into a fully qualified path.
+
+    @param pszName  The relative path.
+
+    @return On success, a pointer to a fully qualified path.  On error, NULL.
+*/
+static const char *MakeFullPath(
+    const char     *pszName)
+{
+    #define         MAXVOLNAME 64U /* Enough for most configs. */
+    static char     aszFullPath[2U][MAXVOLNAME + 1U + FSSTRESS_BUF_SIZE];
+    static uint32_t ulWhich = 0U;
+
+    char           *pszFullPath = aszFullPath[ulWhich];
+    const char     *pszVolume = gpRedVolConf->pszPathPrefix;
+    int32_t         iLen;
+
+    if(pszName[0U] == '/')
+    {
+        iLen = RedSNPrintf(pszFullPath, sizeof(aszFullPath[0U]), "%s%s", pszVolume, pszName);
+    }
+    else if(strcmp(pszName, ".") == 0U)
+    {
+        iLen = RedSNPrintf(pszFullPath, sizeof(aszFullPath[0U]), "%s%s", pszVolume, szLocalCwd);
+    }
+    else if((szLocalCwd[0U] == '/') && (szLocalCwd[1U] == '\0'))
+    {
+        iLen = RedSNPrintf(pszFullPath, sizeof(aszFullPath[0U]), "%s/%s", pszVolume, pszName);
+    }
+    else
+    {
+        iLen = RedSNPrintf(pszFullPath, sizeof(aszFullPath[0U]), "%s%s/%s", pszVolume, szLocalCwd, pszName);
+    }
+
+    if(iLen == -1)
+    {
+        /*  Insufficient path buffer space.
+        */
+        pszFullPath = NULL;
+    }
+    else
+    {
+        /*  Toggle between two full path arrays; a kluge to make rename() and
+            link() work correctly.
+        */
+        ulWhich ^= 1U;
+    }
+
+    return pszFullPath;
+}
+
+
+/*-------------------------------------------------------------------
+    POSIX functions not implemented by the RED POSIX-like API
+-------------------------------------------------------------------*/
+
+#define stat(p, s) red_stat(p, s)
+#define stat64(p, s) stat(p, s)
+#define lstat(p, s) stat(p, s)
+#define lstat64(p, s) stat(p, s)
+#define truncate(p, s) red_truncate(p, s)
+#define truncate64(p, s) truncate(p, s)
+
+
+/** @brief Get the status of a file or directory.
+*/
+static int red_stat(
+    const char *pszPath,
+    REDSTAT    *pStat)
+{
+    int         iFd;
+    int         iRet;
+
+    iFd = open(pszPath, O_RDONLY);
+    iRet = iFd;
+    if(iFd != -1)
+    {
+        iRet = fstat(iFd, pStat);
+
+        (void)close(iFd);
+    }
+
+    return iRet;
+}
+
+
+/** @brief Truncate a file to a specified length.
+*/
+static int red_truncate(
+    const char *pszPath,
+    off_t       llSize)
+{
+    int         iFd;
+    int         iRet;
+
+    iFd = open(pszPath, O_WRONLY);
+    iRet = iFd;
+    if(iFd != -1)
+    {
+        iRet = ftruncate(iFd, llSize);
+
+        (void)close(iFd);
+    }
+
+    return iRet;
+}
+
+
+/*-------------------------------------------------------------------
+    Begin ported fsstress code
+-------------------------------------------------------------------*/
+
+/* Stuff from xfscompat.h */
+
+#define MAXNAMELEN (REDCONF_NAME_MAX+1U) /* Assumed to include NUL */
+
+struct dioattr {
+    int d_miniosz, d_maxiosz, d_mem;
+};
+
+#define MIN(a,b) ((a)<(b) ? (a):(b))
+#define MAX(a,b) ((a)>(b) ? (a):(b))
+
+/* End xfscompat.h */
+
+
+typedef enum {
+    OP_CREAT,
+    OP_FDATASYNC,
+    OP_FSYNC,
+    OP_GETDENTS,
+    OP_LINK,
+    OP_MKDIR,
+    OP_READ,
+    OP_RENAME,
+    OP_RMDIR,
+    OP_STAT,
+    OP_TRUNCATE,
+    OP_UNLINK,
+    OP_WRITE,
+  #if REDCONF_CHECKER == 1
+    OP_CHECK,
+  #endif
+    OP_LAST
+} opty_t;
+
+typedef void (*opfnc_t) (int, long);
+
+typedef struct opdesc {
+    opty_t op;
+    const char *name;
+    opfnc_t func;
+    int freq;
+    int iswrite;
+} opdesc_t;
+
+typedef struct fent {
+    int id;
+    int parent;
+} fent_t;
+
+typedef struct flist {
+    int nfiles;
+    int nslots;
+    int tag;
+    fent_t *fents;
+} flist_t;
+
+typedef struct pathname {
+    int len;
+    char *path;
+} pathname_t;
+
+#define FT_DIR      0
+#define FT_DIRm     (1 << FT_DIR)
+#define FT_REG      1
+#define FT_REGm     (1 << FT_REG)
+#define FT_SYM      2
+#define FT_SYMm     (1 << FT_SYM)
+#define FT_DEV      3
+#define FT_DEVm     (1 << FT_DEV)
+#define FT_RTF      4
+#define FT_RTFm     (1 << FT_RTF)
+#define FT_nft      5
+#define FT_ANYm     ((1 << FT_nft) - 1)
+#define FT_REGFILE  (FT_REGm | FT_RTFm)
+#define FT_NOTDIR   (FT_ANYm & ~FT_DIRm)
+
+#define FLIST_SLOT_INCR 16
+#define NDCACHE 64
+
+#define MAXFSIZE MaxFileSize()
+
+static void creat_f(int opno, long r);
+static void fdatasync_f(int opno, long r);
+static void fsync_f(int opno, long r);
+static void getdents_f(int opno, long r);
+static void link_f(int opno, long r);
+static void mkdir_f(int opno, long r);
+static void read_f(int opno, long r);
+static void rename_f(int opno, long r);
+static void rmdir_f(int opno, long r);
+static void stat_f(int opno, long r);
+static void truncate_f(int opno, long r);
+static void unlink_f(int opno, long r);
+static void write_f(int opno, long r);
+#if REDCONF_CHECKER == 1
+static void check_f(int opno, long r);
+#endif
+
+static opdesc_t ops[] = {
+    {OP_CREAT, "creat", creat_f, 4, 1},
+    {OP_FDATASYNC, "fdatasync", fdatasync_f, 1, 1},
+    {OP_FSYNC, "fsync", fsync_f, 1, 1},
+    {OP_GETDENTS, "getdents", getdents_f, 1, 0},
+    {OP_LINK, "link", link_f, 1, 1},
+    {OP_MKDIR, "mkdir", mkdir_f, 2, 1},
+    {OP_READ, "read", read_f, 1, 0},
+    {OP_RENAME, "rename", rename_f, 2, 1},
+    {OP_RMDIR, "rmdir", rmdir_f, 1, 1},
+    {OP_STAT, "stat", stat_f, 1, 0},
+    {OP_TRUNCATE, "truncate", truncate_f, 2, 1},
+    {OP_UNLINK, "unlink", unlink_f, 1, 1},
+    {OP_WRITE, "write", write_f, 4, 1},
+  #if REDCONF_CHECKER == 1
+    {OP_CHECK, "check", check_f, 1, 1},
+  #endif
+}, *ops_end;
+
+static flist_t flist[FT_nft] = {
+    {0, 0, 'd', NULL},
+    {0, 0, 'f', NULL},
+    {0, 0, 'l', NULL},
+    {0, 0, 'c', NULL},
+    {0, 0, 'r', NULL},
+};
+
+static int dcache[NDCACHE];
+static opty_t *freq_table;
+static int freq_table_size;
+static char *homedir;
+static int *ilist;
+static int ilistlen;
+static off64_t maxfsize;
+static int namerand;
+static int nameseq;
+static int nops;
+static int operations = 1;
+static int procid;
+static int rtpct;
+static unsigned long seed = 0;
+static ino_t top_ino;
+static int verbose = 0;
+
+static int delete_tree(const char *path);
+static void add_to_flist(int fd, int it, int parent);
+static void append_pathname(pathname_t *name, const char *str);
+static void check_cwd(void);
+static int creat_path(pathname_t *name, mode_t mode);
+static void dcache_enter(int dirid, int slot);
+static void dcache_init(void);
+static fent_t *dcache_lookup(int dirid);
+static void dcache_purge(int dirid);
+static void del_from_flist(int ft, int slot);
+static void doproc(void);
+static void fent_to_name(pathname_t *name, flist_t *flp, fent_t *fep);
+static void fix_parent(int oldid, int newid);
+static void free_pathname(pathname_t *name);
+static int generate_fname(fent_t *fep, int ft, pathname_t *name, int *idp, int *v);
+static int get_fname(int which, long r, pathname_t *name, flist_t **flpp, fent_t **fepp, int *v);
+static void init_pathname(pathname_t *name);
+static int link_path(pathname_t *name1, pathname_t *name2);
+static int lstat64_path(pathname_t *name, REDSTAT *sbuf);
+static void make_freq_table(void);
+static int mkdir_path(pathname_t *name, mode_t mode);
+static void namerandpad(int id, char *buf, int len);
+static int open_path(pathname_t *name, int oflag);
+static DIR *opendir_path(pathname_t *name);
+static int rename_path(pathname_t *name1, pathname_t *name2);
+static int rmdir_path(pathname_t *name);
+static void separate_pathname(pathname_t *name, char *buf, pathname_t *newname);
+static int stat64_path(pathname_t *name, REDSTAT *sbuf);
+static int truncate64_path(pathname_t *name, off64_t length);
+static int unlink_path(pathname_t *name);
+static void usage(const char *progname);
+
+
+/** @brief Parse parameters for fsstress.
+
+    @param argc         The number of arguments from main().
+    @param argv         The vector of arguments from main().
+    @param pParam       Populated with the fsstress parameters.
+    @param pbVolNum     If non-NULL, populated with the volume number.
+    @param ppszDevice   If non-NULL, populated with the device name argument or
+                        NULL if no device argument is provided.
+
+    @return The result of parsing the parameters.
+*/
+PARAMSTATUS FsstressParseParams(
+    int             argc,
+    char           *argv[],
+    FSSTRESSPARAM  *pParam,
+    uint8_t        *pbVolNum,
+    const char    **ppszDevice)
+{
+    int             c;
+    uint8_t         bVolNum;
+    const REDOPTION aLongopts[] =
+    {
+        { "no-cleanup", red_no_argument, NULL, 'c' },
+        { "loops", red_required_argument, NULL, 'l' },
+        { "nops", red_required_argument, NULL, 'n' },
+        { "namepad", red_no_argument, NULL, 'r' },
+        { "seed", red_required_argument, NULL, 's' },
+        { "verbose", red_no_argument, NULL, 'v' },
+        { "dev", red_required_argument, NULL, 'D' },
+        { "help", red_no_argument, NULL, 'H' },
+        { NULL }
+    };
+
+    /*  If run without parameters, treat as a help request.
+    */
+    if(argc <= 1)
+    {
+        goto Help;
+    }
+
+    /*  Assume no device argument to start with.
+    */
+    if(ppszDevice != NULL)
+    {
+        *ppszDevice = NULL;
+    }
+
+    /*  Set default parameters.
+    */
+    FsstressDefaultParams(pParam);
+
+    while((c = RedGetoptLong(argc, argv, "cl:n:rs:vD:H", aLongopts, NULL)) != -1)
+    {
+        switch(c)
+        {
+            case 'c': /* --no-cleanup */
+                pParam->fNoCleanup = true;
+                break;
+            case 'l': /* --loops */
+                pParam->ulLoops = RedAtoI(red_optarg);
+                break;
+            case 'n': /* --nops */
+                pParam->ulNops = RedAtoI(red_optarg);
+                break;
+            case 'r': /* --namepad */
+                pParam->fNamePad = true;
+                break;
+            case 's': /* --seed */
+                pParam->ulSeed = RedAtoI(red_optarg);
+                break;
+            case 'v': /* --verbose */
+                pParam->fVerbose = true;
+                break;
+            case 'D': /* --dev */
+                if(ppszDevice != NULL)
+                {
+                    *ppszDevice = red_optarg;
+                }
+                break;
+            case 'H': /* --help */
+                goto Help;
+            case '?': /* Unknown or ambiguous option */
+            case ':': /* Option missing required argument */
+            default:
+                goto BadOpt;
+        }
+    }
+
+    /*  RedGetoptLong() has permuted argv to move all non-option arguments to
+        the end.  We expect to find a volume identifier.
+    */
+    if(red_optind >= argc)
+    {
+        RedPrintf("Missing volume argument\n");
+        goto BadOpt;
+    }
+
+    bVolNum = RedFindVolumeNumber(argv[red_optind]);
+    if(bVolNum == REDCONF_VOLUME_COUNT)
+    {
+        RedPrintf("Error: \"%s\" is not a valid volume identifier.\n", argv[red_optind]);
+        goto BadOpt;
+    }
+
+    if(pbVolNum != NULL)
+    {
+        *pbVolNum = bVolNum;
+    }
+
+    red_optind++; /* Move past volume parameter. */
+    if(red_optind < argc)
+    {
+        int32_t ii;
+
+        for(ii = red_optind; ii < argc; ii++)
+        {
+            RedPrintf("Error: Unexpected command-line argument \"%s\".\n", argv[ii]);
+        }
+
+        goto BadOpt;
+    }
+
+    return PARAMSTATUS_OK;
+
+  BadOpt:
+
+    RedPrintf("%s - invalid parameters\n", argv[0U]);
+    usage(argv[0U]);
+    return PARAMSTATUS_BAD;
+
+  Help:
+
+    usage(argv[0U]);
+    return PARAMSTATUS_HELP;
+}
+
+
+/** @brief Set default fsstress parameters.
+
+    @param pParam   Populated with the default fsstress parameters.
+*/
+void FsstressDefaultParams(
+    FSSTRESSPARAM *pParam)
+{
+    RedMemSet(pParam, 0U, sizeof(*pParam));
+    pParam->ulLoops = 1U;
+    pParam->ulNops = 10000U;
+}
+
+
+/** @brief Start fsstress.
+
+    @param pParam   fsstress parameters, either from FsstressParseParams() or
+                    constructed programatically.
+
+    @return Zero on success, otherwise nonzero.
+*/
+int FsstressStart(
+    const FSSTRESSPARAM *pParam)
+{
+    char buf[10];
+    int fd;
+    int i;
+    int cleanup;
+    int loops;
+    int loopcntr = 1;
+
+    nops = sizeof(ops) / sizeof(ops[0]);
+    ops_end = &ops[nops];
+
+    /*  Copy the already-parsed parameters into the traditional variables.
+    */
+    cleanup = pParam->fNoCleanup ? 1 : 0;
+    loops = pParam->ulLoops;
+    operations = pParam->ulNops;
+    namerand = pParam->fNamePad ? 1 : 0;
+    seed = pParam->ulSeed;
+    verbose = pParam->fVerbose ? 1 : 0;
+
+    make_freq_table();
+
+    while ((loopcntr <= loops) || (loops == 0)) {
+        RedSNPrintf(buf, sizeof(buf), "fss%x", getpid());
+        fd = creat(buf, 0666);
+        maxfsize = (off64_t) MAXFSIZE;
+        dcache_init();
+        if (!seed) {
+            seed = (unsigned long)RedOsClockGetTime();
+            RedPrintf("seed = %ld\n", seed);
+        }
+        close(fd);
+        unlink(buf);
+        procid = 0;
+        doproc();
+        if (cleanup == 0) {
+            delete_tree("/");
+            for (i = 0; i < FT_nft; i++) {
+                flist[i].nslots = 0;
+                flist[i].nfiles = 0;
+                free(flist[i].fents);
+                flist[i].fents = NULL;
+            }
+        }
+        loopcntr++;
+    }
+    return 0;
+}
+
+static int delete_tree(const char *path)
+{
+    REDSTAT sb;
+    DIR *dp;
+    REDDIRENT *dep;
+    char *childpath;
+    size_t len;
+    int e;
+
+    e = stat(path, &sb);
+    if (e)
+        return errno;
+
+    if (!S_ISDIR(sb.st_mode))
+        return unlink(path) ? errno : 0;
+
+    dp = opendir(path);
+    if (dp == NULL)
+        return errno;
+
+    while((dep = readdir(dp)) != NULL) {
+        len = strlen(path) + 1 + strlen(dep->d_name) + 1;
+        childpath = malloc(len);
+
+        strcpy(childpath, path);
+        if (childpath[strlen(childpath) - 1] != '/')
+            strcat(childpath, "/");
+        strcat(childpath, dep->d_name);
+
+        e = delete_tree(childpath);
+
+        free(childpath);
+
+        if (e)
+            break;
+    }
+
+    if (e == 0 && strcmp(path, "/") != 0) {
+        e = rmdir(path) ? errno : 0;
+    }
+    closedir(dp);
+    return e;
+}
+
+static void add_to_flist(int ft, int id, int parent)
+{
+    fent_t *fep;
+    flist_t *ftp;
+
+    ftp = &flist[ft];
+    if (ftp->nfiles == ftp->nslots) {
+        ftp->nslots += FLIST_SLOT_INCR;
+        ftp->fents = realloc(ftp->fents, ftp->nslots * sizeof(fent_t));
+    }
+    fep = &ftp->fents[ftp->nfiles++];
+    fep->id = id;
+    fep->parent = parent;
+}
+
+static void append_pathname(pathname_t *name, const char *str)
+{
+    int len;
+
+    len = strlen(str);
+#ifdef DEBUG
+    if (len && *str == '/' && name->len == 0) {
+        RedPrintf("fsstress: append_pathname failure\n");
+        chdir(homedir);
+        abort();
+
+    }
+#endif
+    name->path = realloc(name->path, name->len + 1 + len);
+    strcpy(&name->path[name->len], str);
+    name->len += len;
+}
+
+static void check_cwd(void)
+{
+#ifdef DEBUG
+    REDSTAT statbuf;
+
+    if (stat64(".", &statbuf) == 0 && statbuf.st_ino == top_ino)
+        return;
+    chdir(homedir);
+    RedPrintf("fsstress: check_cwd failure\n");
+    abort();
+
+#endif
+}
+
+static int creat_path(pathname_t *name, mode_t mode)
+{
+    char buf[MAXNAMELEN];
+    pathname_t newname;
+    int rval;
+
+    rval = creat(name->path, mode);
+    if (rval >= 0 || errno != RED_ENAMETOOLONG)
+        return rval;
+    separate_pathname(name, buf, &newname);
+    if (chdir(buf) == 0) {
+        rval = creat_path(&newname, mode);
+        chdir("..");
+    }
+    free_pathname(&newname);
+    return rval;
+}
+
+static void dcache_enter(int dirid, int slot)
+{
+    dcache[dirid % NDCACHE] = slot;
+}
+
+static void dcache_init(void)
+{
+    int i;
+
+    for (i = 0; i < NDCACHE; i++)
+        dcache[i] = -1;
+}
+
+static fent_t *dcache_lookup(int dirid)
+{
+    fent_t *fep;
+    int i;
+
+    i = dcache[dirid % NDCACHE];
+    if (i >= 0 && (fep = &flist[FT_DIR].fents[i])->id == dirid)
+        return fep;
+    return NULL;
+}
+
+static void dcache_purge(int dirid)
+{
+    int *dcp;
+
+    dcp = &dcache[dirid % NDCACHE];
+    if (*dcp >= 0 && flist[FT_DIR].fents[*dcp].id == dirid)
+        *dcp = -1;
+}
+
+static void del_from_flist(int ft, int slot)
+{
+    flist_t *ftp;
+
+    ftp = &flist[ft];
+    if (ft == FT_DIR)
+        dcache_purge(ftp->fents[slot].id);
+    if (slot != ftp->nfiles - 1) {
+        if (ft == FT_DIR)
+            dcache_purge(ftp->fents[ftp->nfiles - 1].id);
+        ftp->fents[slot] = ftp->fents[--ftp->nfiles];
+    } else
+        ftp->nfiles--;
+}
+
+static fent_t *dirid_to_fent(int dirid)
+{
+    fent_t *efep;
+    fent_t *fep;
+    flist_t *flp;
+
+    if ((fep = dcache_lookup(dirid)))
+        return fep;
+    flp = &flist[FT_DIR];
+    for (fep = flp->fents, efep = &fep[flp->nfiles]; fep < efep; fep++) {
+        if (fep->id == dirid) {
+            dcache_enter(dirid, (int)(fep - flp->fents));
+            return fep;
+        }
+    }
+    return NULL;
+}
+
+static void doproc(void)
+{
+    REDSTAT statbuf;
+    char buf[10];
+    int opno;
+    opdesc_t *p;
+
+    RedSNPrintf(buf, sizeof(buf), "p%x", procid);
+    (void)mkdir(buf);
+    if (chdir(buf) < 0 || stat64(".", &statbuf) < 0) {
+        perror(buf);
+        _exit(1);
+    }
+    top_ino = statbuf.st_ino;
+    homedir = getcwd(NULL, 0);
+    seed += procid;
+    srandom(seed);
+    if (namerand)
+        namerand = random();
+    for (opno = 0; opno < operations; opno++) {
+        p = &ops[freq_table[random() % freq_table_size]];
+        if ((unsigned long)p->func < 4096)
+            abort();
+
+        p->func(opno, random());
+    }
+    free(homedir);
+}
+
+static void fent_to_name(pathname_t *name, flist_t *flp, fent_t *fep)
+{
+    char buf[MAXNAMELEN];
+    int i;
+    fent_t *pfep;
+
+    if (fep == NULL)
+        return;
+    if (fep->parent != -1) {
+        pfep = dirid_to_fent(fep->parent);
+        fent_to_name(name, &flist[FT_DIR], pfep);
+        append_pathname(name, "/");
+    }
+    i = RedSNPrintf(buf, sizeof(buf), "%c%x", flp->tag, fep->id);
+    namerandpad(fep->id, buf, i);
+    append_pathname(name, buf);
+}
+
+static void fix_parent(int oldid, int newid)
+{
+    fent_t *fep;
+    flist_t *flp;
+    int i;
+    int j;
+
+    for (i = 0, flp = flist; i < FT_nft; i++, flp++) {
+        for (j = 0, fep = flp->fents; j < flp->nfiles; j++, fep++) {
+            if (fep->parent == oldid)
+                fep->parent = newid;
+        }
+    }
+}
+
+static void free_pathname(pathname_t *name)
+{
+    if (name->path) {
+        free(name->path);
+        name->path = NULL;
+        name->len = 0;
+    }
+}
+
+static int generate_fname(fent_t *fep, int ft, pathname_t *name, int *idp, int *v)
+{
+    char buf[MAXNAMELEN];
+    flist_t *flp;
+    int id;
+    int j;
+    int len;
+
+    flp = &flist[ft];
+    len = RedSNPrintf(buf, sizeof(buf), "%c%x", flp->tag, id = nameseq++);
+    namerandpad(id, buf, len);
+    if (fep) {
+        fent_to_name(name, &flist[FT_DIR], fep);
+        append_pathname(name, "/");
+    }
+    append_pathname(name, buf);
+    *idp = id;
+    *v = verbose;
+    for (j = 0; !*v && j < ilistlen; j++) {
+        if (ilist[j] == id) {
+            *v = 1;
+            break;
+        }
+    }
+    return 1;
+}
+
+static int
+get_fname(int which, long r, pathname_t *name, flist_t **flpp, fent_t **fepp, int *v)
+{
+    int c;
+    fent_t *fep;
+    flist_t *flp;
+    int i;
+    int j;
+    int x;
+
+    for (i = 0, c = 0, flp = flist; i < FT_nft; i++, flp++) {
+        if (which & (1 << i))
+            c += flp->nfiles;
+    }
+    if (c == 0) {
+        if (flpp)
+            *flpp = NULL;
+        if (fepp)
+            *fepp = NULL;
+        *v = verbose;
+        return 0;
+    }
+    x = (int)(r % c);
+    for (i = 0, c = 0, flp = flist; i < FT_nft; i++, flp++) {
+        if (which & (1 << i)) {
+            if (x < c + flp->nfiles) {
+                fep = &flp->fents[x - c];
+                if (name)
+                    fent_to_name(name, flp, fep);
+                if (flpp)
+                    *flpp = flp;
+                if (fepp)
+                    *fepp = fep;
+                *v = verbose;
+                for (j = 0; !*v && j < ilistlen; j++) {
+                    if (ilist[j] == fep->id) {
+                        *v = 1;
+                        break;
+                    }
+                }
+                return 1;
+            }
+            c += flp->nfiles;
+        }
+    }
+#ifdef DEBUG
+    RedPrintf("fsstress: get_fname failure\n");
+    abort();
+#endif
+    return -1;
+
+}
+
+static void init_pathname(pathname_t *name)
+{
+    name->len = 0;
+    name->path = NULL;
+}
+
+static int link_path(pathname_t *name1, pathname_t *name2)
+{
+    char buf1[MAXNAMELEN];
+    char buf2[MAXNAMELEN];
+    int down1;
+    pathname_t newname1;
+    pathname_t newname2;
+    int rval;
+
+    rval = link(name1->path, name2->path);
+    if (rval >= 0 || errno != RED_ENAMETOOLONG)
+        return rval;
+    separate_pathname(name1, buf1, &newname1);
+    separate_pathname(name2, buf2, &newname2);
+    if (strcmp(buf1, buf2) == 0) {
+        if (chdir(buf1) == 0) {
+            rval = link_path(&newname1, &newname2);
+            chdir("..");
+        }
+    } else {
+        if (strcmp(buf1, "..") == 0)
+            down1 = 0;
+        else if (strcmp(buf2, "..") == 0)
+            down1 = 1;
+        else if (strlen(buf1) == 0)
+            down1 = 0;
+        else if (strlen(buf2) == 0)
+            down1 = 1;
+        else
+            down1 = MAX(newname1.len, 3 + name2->len) <=
+                MAX(3 + name1->len, newname2.len);
+        if (down1) {
+            free_pathname(&newname2);
+            append_pathname(&newname2, "../");
+            append_pathname(&newname2, name2->path);
+            if (chdir(buf1) == 0) {
+                rval = link_path(&newname1, &newname2);
+                chdir("..");
+            }
+        } else {
+            free_pathname(&newname1);
+            append_pathname(&newname1, "../");
+            append_pathname(&newname1, name1->path);
+            if (chdir(buf2) == 0) {
+                rval = link_path(&newname1, &newname2);
+                chdir("..");
+            }
+        }
+    }
+    free_pathname(&newname1);
+    free_pathname(&newname2);
+    return rval;
+}
+
+static int lstat64_path(pathname_t *name, REDSTAT *sbuf)
+{
+    char buf[MAXNAMELEN];
+    pathname_t newname;
+    int rval;
+
+    rval = lstat64(name->path, sbuf);
+    if (rval >= 0 || errno != RED_ENAMETOOLONG)
+        return rval;
+    separate_pathname(name, buf, &newname);
+    if (chdir(buf) == 0) {
+        rval = lstat64_path(&newname, sbuf);
+        chdir("..");
+    }
+    free_pathname(&newname);
+    return rval;
+}
+
+static void make_freq_table(void)
+{
+    int f;
+    int i;
+    opdesc_t *p;
+
+    for (p = ops, f = 0; p < ops_end; p++)
+        f += p->freq;
+    freq_table = malloc(f * sizeof(*freq_table));
+    freq_table_size = f;
+    for (p = ops, i = 0; p < ops_end; p++) {
+        for (f = 0; f < p->freq; f++, i++)
+            freq_table[i] = p->op;
+    }
+}
+
+static int mkdir_path(pathname_t *name, mode_t mode)
+{
+    char buf[MAXNAMELEN];
+    pathname_t newname;
+    int rval;
+
+    rval = mkdir(name->path);
+    if (rval >= 0 || errno != RED_ENAMETOOLONG)
+        return rval;
+    separate_pathname(name, buf, &newname);
+    if (chdir(buf) == 0) {
+        rval = mkdir_path(&newname, mode);
+        chdir("..");
+    }
+    free_pathname(&newname);
+    return rval;
+}
+
+static void namerandpad(int id, char *buf, int len)
+{
+    int bucket;
+    static int buckets[8] = {0};
+    static int bucket_count = 0;
+    int bucket_value;
+    int i;
+    int padlen;
+    int padmod;
+
+    if (namerand == 0)
+        return;
+
+    /*  buckets[] used to be a statically initialized array with the following
+        initializer: { 2, 4, 8, 16, 32, 64, 128, MAXNAMELEN - 1 }
+
+        The problem is that with Reliance Edge, the maximum name length might be
+        less than 128.  So the below code populates buckets[] in a similar
+        fashion but avoids name lengths longer than the maximum.  For example,
+        if the max name is 20, the resulting array is { 2, 4, 8, 16, 20 }.
+    */
+    if (!bucket_count) {
+        bucket_count = sizeof(buckets) / sizeof(buckets[0]);
+        bucket_value = 2;
+        for (i = 0; i < bucket_count; i++) {
+            if (bucket_value > 128 || bucket_value >= (int)MAXNAMELEN - 1)
+                break;
+            buckets[i] = bucket_value;
+            bucket_value *= 2;
+        }
+        if (i < bucket_count) {
+            buckets[i] = MAXNAMELEN - 1;
+            i++;
+        }
+        bucket_count = i;
+    }
+
+    bucket = (id ^ namerand) % bucket_count;
+    padmod = buckets[bucket] + 1 - len;
+    if (padmod <= 0)
+        return;
+    padlen = (id ^ namerand) % padmod;
+    if (padlen) {
+        memset(&buf[len], 'X', padlen);
+        buf[len + padlen] = '\0';
+    }
+}
+
+static int open_path(pathname_t *name, int oflag)
+{
+    char buf[MAXNAMELEN];
+    pathname_t newname;
+    int rval;
+
+    rval = open(name->path, oflag);
+    if (rval >= 0 || errno != RED_ENAMETOOLONG)
+        return rval;
+    separate_pathname(name, buf, &newname);
+    if (chdir(buf) == 0) {
+        rval = open_path(&newname, oflag);
+        chdir("..");
+    }
+    free_pathname(&newname);
+    return rval;
+}
+
+static DIR *opendir_path(pathname_t *name)
+{
+    char buf[MAXNAMELEN];
+    pathname_t newname;
+    DIR *rval;
+
+    rval = opendir(name->path);
+    if (rval || errno != RED_ENAMETOOLONG)
+        return rval;
+    separate_pathname(name, buf, &newname);
+    if (chdir(buf) == 0) {
+        rval = opendir_path(&newname);
+        chdir("..");
+    }
+    free_pathname(&newname);
+    return rval;
+}
+
+static int rename_path(pathname_t *name1, pathname_t *name2)
+{
+    char buf1[MAXNAMELEN];
+    char buf2[MAXNAMELEN];
+    int down1;
+    pathname_t newname1;
+    pathname_t newname2;
+    int rval;
+
+    rval = rename(name1->path, name2->path);
+    if (rval >= 0 || errno != RED_ENAMETOOLONG)
+        return rval;
+    separate_pathname(name1, buf1, &newname1);
+    separate_pathname(name2, buf2, &newname2);
+    if (strcmp(buf1, buf2) == 0) {
+        if (chdir(buf1) == 0) {
+            rval = rename_path(&newname1, &newname2);
+            chdir("..");
+        }
+    } else {
+        if (strcmp(buf1, "..") == 0)
+            down1 = 0;
+        else if (strcmp(buf2, "..") == 0)
+            down1 = 1;
+        else if (strlen(buf1) == 0)
+            down1 = 0;
+        else if (strlen(buf2) == 0)
+            down1 = 1;
+        else
+            down1 = MAX(newname1.len, 3 + name2->len) <=
+                MAX(3 + name1->len, newname2.len);
+        if (down1) {
+            free_pathname(&newname2);
+            append_pathname(&newname2, "../");
+            append_pathname(&newname2, name2->path);
+            if (chdir(buf1) == 0) {
+                rval = rename_path(&newname1, &newname2);
+                chdir("..");
+            }
+        } else {
+            free_pathname(&newname1);
+            append_pathname(&newname1, "../");
+            append_pathname(&newname1, name1->path);
+            if (chdir(buf2) == 0) {
+                rval = rename_path(&newname1, &newname2);
+                chdir("..");
+            }
+        }
+    }
+    free_pathname(&newname1);
+    free_pathname(&newname2);
+    return rval;
+}
+
+static int rmdir_path(pathname_t *name)
+{
+    char buf[MAXNAMELEN];
+    pathname_t newname;
+    int rval;
+
+    rval = rmdir(name->path);
+    if (rval >= 0 || errno != RED_ENAMETOOLONG)
+        return rval;
+    separate_pathname(name, buf, &newname);
+    if (chdir(buf) == 0) {
+        rval = rmdir_path(&newname);
+        chdir("..");
+    }
+    free_pathname(&newname);
+    return rval;
+}
+
+static void separate_pathname(pathname_t *name, char *buf, pathname_t *newname)
+{
+    char *slash;
+
+    init_pathname(newname);
+    slash = strchr(name->path, '/');
+    if (slash == NULL) {
+        buf[0] = '\0';
+        return;
+    }
+    *slash = '\0';
+    strcpy(buf, name->path);
+    *slash = '/';
+    append_pathname(newname, slash + 1);
+}
+
+static int stat64_path(pathname_t *name, REDSTAT *sbuf)
+{
+    char buf[MAXNAMELEN];
+    pathname_t newname;
+    int rval;
+
+    rval = stat64(name->path, sbuf);
+    if (rval >= 0 || errno != RED_ENAMETOOLONG)
+        return rval;
+    separate_pathname(name, buf, &newname);
+    if (chdir(buf) == 0) {
+        rval = stat64_path(&newname, sbuf);
+        chdir("..");
+    }
+    free_pathname(&newname);
+    return rval;
+}
+
+static int truncate64_path(pathname_t *name, off64_t length)
+{
+    char buf[MAXNAMELEN];
+    pathname_t newname;
+    int rval;
+
+    rval = truncate64(name->path, length);
+    if (rval >= 0 || errno != RED_ENAMETOOLONG)
+        return rval;
+    separate_pathname(name, buf, &newname);
+    if (chdir(buf) == 0) {
+        rval = truncate64_path(&newname, length);
+        chdir("..");
+    }
+    free_pathname(&newname);
+    return rval;
+}
+
+static int unlink_path(pathname_t *name)
+{
+    char buf[MAXNAMELEN];
+    pathname_t newname;
+    int rval;
+
+    rval = unlink(name->path);
+    if (rval >= 0 || errno != RED_ENAMETOOLONG)
+        return rval;
+    separate_pathname(name, buf, &newname);
+    if (chdir(buf) == 0) {
+        rval = unlink_path(&newname);
+        chdir("..");
+    }
+    free_pathname(&newname);
+    return rval;
+}
+
+static void usage(const char *progname)
+{
+    RedPrintf("usage: %s VolumeID [Options]\n", progname);
+    RedPrintf("File system stress test.\n\n");
+    RedPrintf("Where:\n");
+    RedPrintf("  VolumeID\n");
+    RedPrintf("      A volume number (e.g., 2) or a volume path prefix (e.g., VOL1: or /data)\n");
+    RedPrintf("      of the volume to test.\n");
+    RedPrintf("And 'Options' are any of the following:\n");
+    RedPrintf("  --no-cleanup, -c\n");
+    RedPrintf("      Specifies not to remove files (cleanup) after execution\n");
+    RedPrintf("  --loops=count, -l count\n");
+    RedPrintf("      Specifies the number of times the entire test should loop.  Use 0 for\n");
+    RedPrintf("      infinite.  Default 1.\n");
+    RedPrintf("  --nops=count, -n count\n");
+    RedPrintf("      Specifies the number of operations to run (default 10000).\n");
+    RedPrintf("  --namepad, -r\n");
+    RedPrintf("      Specifies to use random name padding (resulting in longer names).\n");
+    RedPrintf("  --seed=value, -s value\n");
+    RedPrintf("      Specifies the seed for the random number generator (default timestamp).\n");
+    RedPrintf("  --verbose, -v\n");
+    RedPrintf("      Specifies verbose mode (without this, test is very quiet).\n");
+    RedPrintf("  --dev=devname, -D devname\n");
+    RedPrintf("      Specifies the device name.  This is typically only meaningful when\n");
+    RedPrintf("      running the test on a host machine.  This can be \"ram\" to test on a RAM\n");
+    RedPrintf("      disk, the path and name of a file disk (e.g., red.bin); or an OS-specific\n");
+    RedPrintf("      reference to a device (on Windows, a drive letter like G: or a device name\n");
+    RedPrintf("      like \\\\.\\PhysicalDrive7).\n");
+    RedPrintf("  --help, -H\n");
+    RedPrintf("      Prints this usage text and exits.\n\n");
+    RedPrintf("Warning: This test will format the volume -- destroying all existing data.\n\n");
+}
+
+static void creat_f(int opno, long r)
+{
+    int e;
+    int e1;
+    pathname_t f;
+    int fd;
+    fent_t *fep;
+    int id;
+    int parid;
+    int type;
+    int v;
+    int v1;
+    int esz = 0;
+
+    if (!get_fname(FT_DIRm, r, NULL, NULL, &fep, &v1))
+        parid = -1;
+    else
+        parid = fep->id;
+    init_pathname(&f);
+    type = rtpct ? ((int)(random() % 100) > rtpct ? FT_REG : FT_RTF) : FT_REG;
+    e = generate_fname(fep, type, &f, &id, &v);
+    v |= v1;
+    if (!e) {
+        if (v) {
+            fent_to_name(&f, &flist[FT_DIR], fep);
+            RedPrintf("%d/%d: creat - no filename from %s\n",
+                   procid, opno, f.path);
+        }
+        free_pathname(&f);
+        return;
+    }
+    fd = creat_path(&f, 0666);
+    e = fd < 0 ? errno : 0;
+    e1 = 0;
+    check_cwd();
+    esz = 0;
+    if (fd >= 0) {
+        add_to_flist(type, id, parid);
+        close(fd);
+    }
+    if (v)
+        RedPrintf("%d/%d: creat %s x:%d %d %d\n", procid, opno, f.path,
+               esz, e, e1);
+    free_pathname(&f);
+}
+
+static void fdatasync_f(int opno, long r)
+{
+    int e;
+    pathname_t f;
+    int fd;
+    int v;
+
+    init_pathname(&f);
+    if (!get_fname(FT_REGFILE, r, &f, NULL, NULL, &v)) {
+        if (v)
+            RedPrintf("%d/%d: fdatasync - no filename\n",
+                   procid, opno);
+        free_pathname(&f);
+        return;
+    }
+    fd = open_path(&f, O_WRONLY);
+    e = fd < 0 ? errno : 0;
+    check_cwd();
+    if (fd < 0) {
+        if (v)
+            RedPrintf("%d/%d: fdatasync - open %s failed %d\n",
+                   procid, opno, f.path, e);
+        free_pathname(&f);
+        return;
+    }
+    e = fdatasync(fd) < 0 ? errno : 0;
+    if (v)
+        RedPrintf("%d/%d: fdatasync %s %d\n", procid, opno, f.path, e);
+    free_pathname(&f);
+    close(fd);
+}
+
+static void fsync_f(int opno, long r)
+{
+    int e;
+    pathname_t f;
+    int fd;
+    int v;
+
+    init_pathname(&f);
+    if (!get_fname(FT_REGFILE, r, &f, NULL, NULL, &v)) {
+        if (v)
+            RedPrintf("%d/%d: fsync - no filename\n", procid, opno);
+        free_pathname(&f);
+        return;
+    }
+    fd = open_path(&f, O_WRONLY);
+    e = fd < 0 ? errno : 0;
+    check_cwd();
+    if (fd < 0) {
+        if (v)
+            RedPrintf("%d/%d: fsync - open %s failed %d\n",
+                   procid, opno, f.path, e);
+        free_pathname(&f);
+        return;
+    }
+    e = fsync(fd) < 0 ? errno : 0;
+    if (v)
+        RedPrintf("%d/%d: fsync %s %d\n", procid, opno, f.path, e);
+    free_pathname(&f);
+    close(fd);
+}
+
+static void getdents_f(int opno, long r)
+{
+    DIR *dir;
+    pathname_t f;
+    int v;
+
+    init_pathname(&f);
+    if (!get_fname(FT_DIRm, r, &f, NULL, NULL, &v))
+        append_pathname(&f, ".");
+    dir = opendir_path(&f);
+    check_cwd();
+    if (dir == NULL) {
+        if (v)
+            RedPrintf("%d/%d: getdents - can't open %s\n",
+                   procid, opno, f.path);
+        free_pathname(&f);
+        return;
+    }
+    while (readdir64(dir) != NULL)
+        continue;
+    if (v)
+        RedPrintf("%d/%d: getdents %s 0\n", procid, opno, f.path);
+    free_pathname(&f);
+    closedir(dir);
+}
+
+static void link_f(int opno, long r)
+{
+    int e;
+    pathname_t f;
+    fent_t *fep;
+    flist_t *flp;
+    int id;
+    pathname_t l;
+    int parid;
+    int v;
+    int v1;
+
+    init_pathname(&f);
+    if (!get_fname(FT_NOTDIR, r, &f, &flp, NULL, &v1)) {
+        if (v1)
+            RedPrintf("%d/%d: link - no file\n", procid, opno);
+        free_pathname(&f);
+        return;
+    }
+    if (!get_fname(FT_DIRm, random(), NULL, NULL, &fep, &v))
+        parid = -1;
+    else
+        parid = fep->id;
+    v |= v1;
+    init_pathname(&l);
+    e = generate_fname(fep, (int)(flp - flist), &l, &id, &v1);
+    v |= v1;
+    if (!e) {
+        if (v) {
+            fent_to_name(&l, &flist[FT_DIR], fep);
+            RedPrintf("%d/%d: link - no filename from %s\n",
+                   procid, opno, l.path);
+        }
+        free_pathname(&l);
+        free_pathname(&f);
+        return;
+    }
+    e = link_path(&f, &l) < 0 ? errno : 0;
+    check_cwd();
+    if (e == 0)
+        add_to_flist((int)(flp - flist), id, parid);
+    if (v)
+        RedPrintf("%d/%d: link %s %s %d\n", procid, opno, f.path, l.path,
+               e);
+    free_pathname(&l);
+    free_pathname(&f);
+}
+
+static void mkdir_f(int opno, long r)
+{
+    int e;
+    pathname_t f;
+    fent_t *fep;
+    int id;
+    int parid;
+    int v;
+    int v1;
+
+    if (!get_fname(FT_DIRm, r, NULL, NULL, &fep, &v))
+        parid = -1;
+    else
+        parid = fep->id;
+    init_pathname(&f);
+    e = generate_fname(fep, FT_DIR, &f, &id, &v1);
+    v |= v1;
+    if (!e) {
+        if (v) {
+            fent_to_name(&f, &flist[FT_DIR], fep);
+            RedPrintf("%d/%d: mkdir - no filename from %s\n",
+                   procid, opno, f.path);
+        }
+        free_pathname(&f);
+        return;
+    }
+    e = mkdir_path(&f, 0777) < 0 ? errno : 0;
+    check_cwd();
+    if (e == 0)
+        add_to_flist(FT_DIR, id, parid);
+    if (v)
+        RedPrintf("%d/%d: mkdir %s %d\n", procid, opno, f.path, e);
+    free_pathname(&f);
+}
+
+static void read_f(int opno, long r)
+{
+    char *buf;
+    int e;
+    pathname_t f;
+    int fd;
+    uint32_t len;
+    __int64_t lr;
+    off64_t off;
+    REDSTAT stb;
+    int v;
+
+    init_pathname(&f);
+    if (!get_fname(FT_REGFILE, r, &f, NULL, NULL, &v)) {
+        if (v)
+            RedPrintf("%d/%d: read - no filename\n", procid, opno);
+        free_pathname(&f);
+        return;
+    }
+    fd = open_path(&f, O_RDONLY);
+    e = fd < 0 ? errno : 0;
+    check_cwd();
+    if (fd < 0) {
+        if (v)
+            RedPrintf("%d/%d: read - open %s failed %d\n",
+                   procid, opno, f.path, e);
+        free_pathname(&f);
+        return;
+    }
+    if (fstat64(fd, &stb) < 0) {
+        if (v)
+            RedPrintf("%d/%d: read - fstat64 %s failed %d\n",
+                   procid, opno, f.path, errno);
+        free_pathname(&f);
+        close(fd);
+        return;
+    }
+    if (stb.st_size == 0) {
+        if (v)
+            RedPrintf("%d/%d: read - %s zero size\n", procid, opno,
+                   f.path);
+        free_pathname(&f);
+        close(fd);
+        return;
+    }
+    lr = ((__int64_t) random() << 32) + random();
+    off = (off64_t) (lr % stb.st_size);
+    lseek64(fd, off, SEEK_SET);
+    len = (random() % (getpagesize() * 4)) + 1;
+    buf = malloc(len);
+    e = read(fd, buf, len) < 0 ? errno : 0;
+    free(buf);
+    if (v)
+        RedPrintf("%d/%d: read %s [%lld,%ld] %d\n",
+               procid, opno, f.path, (long long)off, (long int)len, e);
+    free_pathname(&f);
+    close(fd);
+}
+
+static void rename_f(int opno, long r)
+{
+    fent_t *dfep;
+    int e;
+    pathname_t f;
+    fent_t *fep;
+    flist_t *flp;
+    int id;
+    pathname_t newf;
+    int oldid;
+    int parid;
+    int v;
+    int v1;
+
+    init_pathname(&f);
+    if (!get_fname(FT_ANYm, r, &f, &flp, &fep, &v1)) {
+        if (v1)
+            RedPrintf("%d/%d: rename - no filename\n", procid, opno);
+        free_pathname(&f);
+        return;
+    }
+    if (!get_fname(FT_DIRm, random(), NULL, NULL, &dfep, &v))
+        parid = -1;
+    else
+        parid = dfep->id;
+    v |= v1;
+    init_pathname(&newf);
+    e = generate_fname(dfep, (int)(flp - flist), &newf, &id, &v1);
+    v |= v1;
+    if (!e) {
+        if (v) {
+            fent_to_name(&f, &flist[FT_DIR], dfep);
+            RedPrintf("%d/%d: rename - no filename from %s\n",
+                   procid, opno, f.path);
+        }
+        free_pathname(&newf);
+        free_pathname(&f);
+        return;
+    }
+    e = rename_path(&f, &newf) < 0 ? errno : 0;
+    check_cwd();
+    if (e == 0) {
+        if (flp - flist == FT_DIR) {
+            oldid = fep->id;
+            fix_parent(oldid, id);
+        }
+        del_from_flist((int)(flp - flist), (int)(fep - flp->fents));
+        add_to_flist((int)(flp - flist), id, parid);
+    }
+    if (v)
+        RedPrintf("%d/%d: rename %s to %s %d\n", procid, opno, f.path,
+               newf.path, e);
+    free_pathname(&newf);
+    free_pathname(&f);
+}
+
+static void rmdir_f(int opno, long r)
+{
+    int e;
+    pathname_t f;
+    fent_t *fep;
+    int v;
+
+    init_pathname(&f);
+    if (!get_fname(FT_DIRm, r, &f, NULL, &fep, &v)) {
+        if (v)
+            RedPrintf("%d/%d: rmdir - no directory\n", procid, opno);
+        free_pathname(&f);
+        return;
+    }
+    e = rmdir_path(&f) < 0 ? errno : 0;
+    check_cwd();
+    if (e == 0)
+        del_from_flist(FT_DIR, (int)(fep - flist[FT_DIR].fents));
+    if (v)
+        RedPrintf("%d/%d: rmdir %s %d\n", procid, opno, f.path, e);
+    free_pathname(&f);
+}
+
+static void stat_f(int opno, long r)
+{
+    int e;
+    pathname_t f;
+    REDSTAT stb;
+    int v;
+
+    init_pathname(&f);
+    if (!get_fname(FT_ANYm, r, &f, NULL, NULL, &v)) {
+        if (v)
+            RedPrintf("%d/%d: stat - no entries\n", procid, opno);
+        free_pathname(&f);
+        return;
+    }
+    e = lstat64_path(&f, &stb) < 0 ? errno : 0;
+    check_cwd();
+    if (v)
+        RedPrintf("%d/%d: stat %s %d\n", procid, opno, f.path, e);
+    free_pathname(&f);
+}
+
+static void truncate_f(int opno, long r)
+{
+    int e;
+    pathname_t f;
+    __int64_t lr;
+    off64_t off;
+    REDSTAT stb;
+    int v;
+
+    init_pathname(&f);
+    if (!get_fname(FT_REGFILE, r, &f, NULL, NULL, &v)) {
+        if (v)
+            RedPrintf("%d/%d: truncate - no filename\n", procid, opno);
+        free_pathname(&f);
+        return;
+    }
+    e = stat64_path(&f, &stb) < 0 ? errno : 0;
+    check_cwd();
+    if (e > 0) {
+        if (v)
+            RedPrintf("%d/%d: truncate - stat64 %s failed %d\n",
+                   procid, opno, f.path, e);
+        free_pathname(&f);
+        return;
+    }
+    lr = ((__int64_t) random() << 32) + random();
+    off = lr % MIN(stb.st_size + (1024 * 1024), MAXFSIZE);
+    off %= maxfsize;
+    e = truncate64_path(&f, off) < 0 ? errno : 0;
+    check_cwd();
+    if (v)
+        RedPrintf("%d/%d: truncate %s %lld %d\n", procid, opno, f.path,
+               (long long)off, e);
+    free_pathname(&f);
+}
+
+static void unlink_f(int opno, long r)
+{
+    int e;
+    pathname_t f;
+    fent_t *fep;
+    flist_t *flp;
+    int v;
+
+    init_pathname(&f);
+    if (!get_fname(FT_NOTDIR, r, &f, &flp, &fep, &v)) {
+        if (v)
+            RedPrintf("%d/%d: unlink - no file\n", procid, opno);
+        free_pathname(&f);
+        return;
+    }
+    e = unlink_path(&f) < 0 ? errno : 0;
+    check_cwd();
+    if (e == 0)
+        del_from_flist((int)(flp - flist), (int)(fep - flp->fents));
+    if (v)
+        RedPrintf("%d/%d: unlink %s %d\n", procid, opno, f.path, e);
+    free_pathname(&f);
+}
+
+static void write_f(int opno, long r)
+{
+    char *buf;
+    int e;
+    pathname_t f;
+    int fd;
+    uint32_t len;
+    __int64_t lr;
+    off64_t off;
+    REDSTAT stb;
+    int v;
+
+    init_pathname(&f);
+    if (!get_fname(FT_REGm, r, &f, NULL, NULL, &v)) {
+        if (v)
+            RedPrintf("%d/%d: write - no filename\n", procid, opno);
+        free_pathname(&f);
+        return;
+    }
+    fd = open_path(&f, O_WRONLY);
+    e = fd < 0 ? errno : 0;
+    check_cwd();
+    if (fd < 0) {
+        if (v)
+            RedPrintf("%d/%d: write - open %s failed %d\n",
+                   procid, opno, f.path, e);
+        free_pathname(&f);
+        return;
+    }
+    if (fstat64(fd, &stb) < 0) {
+        if (v)
+            RedPrintf("%d/%d: write - fstat64 %s failed %d\n",
+                   procid, opno, f.path, errno);
+        free_pathname(&f);
+        close(fd);
+        return;
+    }
+    lr = ((__int64_t) random() << 32) + random();
+    off = (off64_t) (lr % MIN(stb.st_size + (1024 * 1024), MAXFSIZE));
+    off %= maxfsize;
+    lseek64(fd, off, SEEK_SET);
+    len = (random() % (getpagesize() * 4)) + 1;
+    buf = malloc(len);
+    memset(buf, nameseq & 0xff, len);
+    e = write(fd, buf, len) < 0 ? errno : 0;
+    free(buf);
+    if (v)
+        RedPrintf("%d/%d: write %s [%lld,%ld] %d\n",
+               procid, opno, f.path, (long long)off, (long int)len, e);
+    free_pathname(&f);
+    close(fd);
+}
+
+
+#if REDCONF_CHECKER == 1
+static void check_f(int opno, long r)
+{
+    int32_t ret;
+    const char *pszVolume = gpRedVolConf->pszPathPrefix;
+
+    (void)r;
+
+    errno = 0;
+
+    ret = red_transact(pszVolume);
+
+    if(ret == 0)
+    {
+        ret = red_umount(pszVolume);
+
+        if(ret == 0)
+        {
+            int32_t ret2;
+
+            errno = -RedCoreVolCheck();
+            if(errno != 0)
+            {
+                ret = -1;
+            }
+
+            ret2 = red_mount(pszVolume);
+
+            if(ret == 0)
+            {
+                ret = ret2;
+            }
+
+            if(ret2 != 0)
+            {
+                exit(1);
+            }
+        }
+    }
+
+    if (verbose)
+    {
+        RedPrintf("%d/%d: check %s %d\n", procid, opno, pszVolume, errno);
+    }
+}
+#endif
+
+
+#endif /* FSSTRESS_SUPPORTED */
+