]> git.sur5r.net Git - freertos/blob - 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
1 /*
2  * Copyright (c) 2000 Silicon Graphics, Inc.  All Rights Reserved.
3  *
4  * This program is free software; you can redistribute it and/or modify it
5  * under the terms of version 2 of the GNU General Public License as
6  * published by the Free Software Foundation.
7  *
8  * This program is distributed in the hope that it would be useful, but
9  * WITHOUT ANY WARRANTY; without even the implied warranty of
10  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
11  *
12  * Further, this software is distributed without any warranty that it is
13  * free of the rightful claim of any third person regarding infringement
14  * or the like.  Any license provided herein, whether implied or
15  * otherwise, applies only to this software file.  Patent licenses, if
16  * any, provided herein do not apply to combinations of this program with
17  * other software, or any other product whatsoever.
18  *
19  * You should have received a copy of the GNU General Public License along
20  * with this program; if not, write the Free Software Foundation, Inc.,
21  * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
22  *
23  * Contact information: Silicon Graphics, Inc., 1600 Amphitheatre Pkwy,
24  * Mountain View, CA  94043, or:
25  *
26  * http://www.sgi.com
27  *
28  * For further information regarding this notice, see:
29  *
30  * http://oss.sgi.com/projects/GenInfo/SGIGPLNoticeExplan/
31  */
32 /** @file
33     @brief File system stress test.
34
35     This version of SGI fsstress has been modified to be single-threaded and to
36     work with the Reliance Edge POSIX-like API.
37 */
38 #include <stdio.h>
39 #include <stdlib.h>
40 #include <limits.h>
41 #include <string.h>
42 #include <stdarg.h>
43 #include <time.h>
44
45 #include <redposix.h>
46 #include <redtests.h>
47
48 #if FSSTRESS_SUPPORTED
49
50 #include "redposixcompat.h"
51
52 #include <redosserv.h>
53 #include <redutils.h>
54 #include <redmacs.h>
55 #include <redvolume.h>
56 #include <redgetopt.h>
57 #include <redtoolcmn.h>
58
59 #if REDCONF_CHECKER == 1
60 #include <redcoreapi.h>
61 #endif
62
63
64 /*  Create POSIX types.  Use #define to avoid name conflicts in those
65     environments where the type names already exist.
66 */
67 #define off_t int64_t
68 #define off64_t off_t
69 #define ino_t uint32_t
70 #define mode_t uint16_t
71 #define __int64_t int64_t
72
73
74 /** @brief Generate a random number.
75
76     @return A nonnegative random number.
77 */
78 #define random() ((int)(RedRand32(NULL) & 0x7FFFFFFF))
79
80
81 /** @brief Seed the random number generator.
82 */
83 #define srandom(seed) RedRandSeed(seed)
84
85
86 #define _exit(status) exit(status)
87 #define getpagesize() 4096U
88 #define getpid() 1
89
90
91 /** @brief Determine the maximum file size.
92
93     This is used for the MAXFSSIZE macro.
94 */
95 static uint64_t MaxFileSize(void)
96 {
97     REDSTATFS   info;
98     int32_t     iStatus;
99     REDSTATUS   errnoSave = errno;
100     uint64_t    ullMaxFileSize;
101
102     iStatus = red_statvfs("", &info);
103     if(iStatus == 0)
104     {
105         ullMaxFileSize = info.f_maxfsize;
106     }
107     else
108     {
109         /*  This function does not change errno.
110         */
111         errno = errnoSave;
112
113         ullMaxFileSize = 0x7FFFFFFFU;
114     }
115
116     return ullMaxFileSize;
117 }
118
119
120 /*-------------------------------------------------------------------
121     Simulated current working directory support
122 -------------------------------------------------------------------*/
123
124
125 /*  Forward declaration for red_chdir().
126 */
127 static int red_stat(const char *pszPath, REDSTAT *pStat);
128
129 /*  The simulated CWD functions.
130 */
131 #undef chdir
132 #undef getcwd
133 #define chdir(path) red_chdir(path)
134 #define getcwd(buf, size) red_getcwd(buf, size)
135
136
137 /*  Redefine the path-based APIs to call MakeFullPath() on their arguments
138     since there is no CWD support in the red_*() APIs.
139 */
140 #undef open
141 #undef unlink
142 #undef mkdir
143 #undef rmdir
144 #undef rename
145 #undef link
146 #undef opendir
147 #define open(path, oflag) red_open(MakeFullPath(path), oflag)
148 #define unlink(path) red_unlink(MakeFullPath(path))
149 #define mkdir(path) red_mkdir(MakeFullPath(path))
150 #define rmdir(path) red_rmdir(MakeFullPath(path))
151 #define rename(old, new) red_rename(MakeFullPath(old), MakeFullPath(new))
152 #define link(path, hardlink) red_link(MakeFullPath(path), MakeFullPath(hardlink))
153 #define opendir(path) red_opendir(MakeFullPath(path))
154
155 #define FSSTRESS_BUF_SIZE 1024U
156
157 /*  Stores the simulated current working directory.
158 */
159 static char szLocalCwd[FSSTRESS_BUF_SIZE] = "/";
160
161
162 /** @brief Change the current working directory.
163
164     This function only supports a subset of what is possible with POSIX chdir().
165
166     @param pszPath  The new current working directory.
167
168     @return Upon successful completion, 0 shall be returned.  Otherwise, -1
169             shall be returned, and errno shall be set to indicate the error.
170 */
171 static int red_chdir(
172     const char *pszPath)
173 {
174     uint32_t    ulIdx;
175     int         iErrno = 0;
176
177     if(strcmp(pszPath, "..") == 0)
178     {
179         uint32_t ulLastSlashIdx = 0U;
180
181         /*  Chop off the last path separator and everything after it, so that
182             "/foo/bar/baz" becomes "/foo/bar", moving the CWD up one directory.
183         */
184         for(ulIdx = 0U; szLocalCwd[ulIdx] != '\0'; ulIdx++)
185         {
186             if(szLocalCwd[ulIdx] == '/')
187             {
188                 ulLastSlashIdx = ulIdx;
189             }
190         }
191
192         if(ulLastSlashIdx != 0U)
193         {
194             szLocalCwd[ulLastSlashIdx] = '\0';
195         }
196     }
197     else
198     {
199         char    szOldCwd[FSSTRESS_BUF_SIZE];
200
201         /*  chdir() must have no effect on the CWD if it fails, so save the CWD
202             so we can revert it if necessary.
203         */
204         strcpy(szOldCwd, szLocalCwd);
205
206         if(pszPath[0U] == '/')
207         {
208             if(strlen(pszPath) >= sizeof(szLocalCwd))
209             {
210                 iErrno = RED_ENAMETOOLONG;
211             }
212             else
213             {
214                 strcpy(szLocalCwd, pszPath);
215             }
216         }
217         else
218         {
219             ulIdx = strlen(szLocalCwd);
220
221             if((ulIdx + 1U + strlen(pszPath)) >= sizeof(szLocalCwd))
222             {
223                 iErrno = RED_ENAMETOOLONG;
224             }
225             else
226             {
227                 if(szLocalCwd[1U] != '\0')
228                 {
229                     szLocalCwd[ulIdx] = '/';
230                     ulIdx++;
231                 }
232
233                 strcpy(&szLocalCwd[ulIdx], pszPath);
234             }
235         }
236
237         if(iErrno == 0)
238         {
239             REDSTAT s;
240             int     iStatus;
241
242             iStatus = red_stat(szLocalCwd, &s);
243             if(iStatus != 0)
244             {
245                 iErrno = errno;
246             }
247             else if(!S_ISDIR(s.st_mode))
248             {
249                 iErrno = RED_ENOTDIR;
250             }
251             else
252             {
253                 /*  No error, new CWD checks out.
254                 */
255             }
256         }
257
258         if(iErrno != 0)
259         {
260             strcpy(szLocalCwd, szOldCwd);
261         }
262     }
263
264     if(iErrno != 0)
265     {
266         errno = iErrno;
267     }
268
269     return iErrno == 0 ? 0 : -1;
270 }
271
272
273 /** @brief Retrieve the current working directory.
274
275     @param pszBuf   On successful return, populated with the current working
276                     directory.  If NULL, memory will be allocated for the CWD
277                     and returned by this function.
278     @param nSize    The size of @p pszBuf.
279
280     @return On success, if @p pszBuf was non-NULL, returns @p pszBuf; if
281             @p pszBuf was NULL, returns an allocated buffer populated with the
282             CWD which must be freed by the caller.  On failure, returns NULL
283             and errno will be set.
284 */
285 static char *red_getcwd(
286     char   *pszBuf,
287     size_t  nSize)
288 {
289     char   *pszRet;
290
291     if(pszBuf == NULL)
292     {
293         pszRet = malloc(strlen(szLocalCwd) + 1U);
294         if(pszRet == NULL)
295         {
296             errno = RED_ENOMEM;
297         }
298         else
299         {
300             strcpy(pszRet, szLocalCwd);
301         }
302     }
303     else if(nSize < strlen(szLocalCwd) + 1U)
304     {
305         errno = RED_ERANGE;
306         pszRet = NULL;
307     }
308     else
309     {
310         strcpy(pszBuf, szLocalCwd);
311         pszRet = pszBuf;
312     }
313
314     return pszRet;
315 }
316
317
318 /** @brief Make a relative path into a fully qualified path.
319
320     @param pszName  The relative path.
321
322     @return On success, a pointer to a fully qualified path.  On error, NULL.
323 */
324 static const char *MakeFullPath(
325     const char     *pszName)
326 {
327     #define         MAXVOLNAME 64U /* Enough for most configs. */
328     static char     aszFullPath[2U][MAXVOLNAME + 1U + FSSTRESS_BUF_SIZE];
329     static uint32_t ulWhich = 0U;
330
331     char           *pszFullPath = aszFullPath[ulWhich];
332     const char     *pszVolume = gpRedVolConf->pszPathPrefix;
333     int32_t         iLen;
334
335     if(pszName[0U] == '/')
336     {
337         iLen = RedSNPrintf(pszFullPath, sizeof(aszFullPath[0U]), "%s%s", pszVolume, pszName);
338     }
339     else if(strcmp(pszName, ".") == 0U)
340     {
341         iLen = RedSNPrintf(pszFullPath, sizeof(aszFullPath[0U]), "%s%s", pszVolume, szLocalCwd);
342     }
343     else if((szLocalCwd[0U] == '/') && (szLocalCwd[1U] == '\0'))
344     {
345         iLen = RedSNPrintf(pszFullPath, sizeof(aszFullPath[0U]), "%s/%s", pszVolume, pszName);
346     }
347     else
348     {
349         iLen = RedSNPrintf(pszFullPath, sizeof(aszFullPath[0U]), "%s%s/%s", pszVolume, szLocalCwd, pszName);
350     }
351
352     if(iLen == -1)
353     {
354         /*  Insufficient path buffer space.
355         */
356         pszFullPath = NULL;
357     }
358     else
359     {
360         /*  Toggle between two full path arrays; a kluge to make rename() and
361             link() work correctly.
362         */
363         ulWhich ^= 1U;
364     }
365
366     return pszFullPath;
367 }
368
369
370 /*-------------------------------------------------------------------
371     POSIX functions not implemented by the RED POSIX-like API
372 -------------------------------------------------------------------*/
373
374 #define stat(p, s) red_stat(p, s)
375 #define stat64(p, s) stat(p, s)
376 #define lstat(p, s) stat(p, s)
377 #define lstat64(p, s) stat(p, s)
378 #define truncate(p, s) red_truncate(p, s)
379 #define truncate64(p, s) truncate(p, s)
380
381
382 /** @brief Get the status of a file or directory.
383 */
384 static int red_stat(
385     const char *pszPath,
386     REDSTAT    *pStat)
387 {
388     int         iFd;
389     int         iRet;
390
391     iFd = open(pszPath, O_RDONLY);
392     iRet = iFd;
393     if(iFd != -1)
394     {
395         iRet = fstat(iFd, pStat);
396
397         (void)close(iFd);
398     }
399
400     return iRet;
401 }
402
403
404 /** @brief Truncate a file to a specified length.
405 */
406 static int red_truncate(
407     const char *pszPath,
408     off_t       llSize)
409 {
410     int         iFd;
411     int         iRet;
412
413     iFd = open(pszPath, O_WRONLY);
414     iRet = iFd;
415     if(iFd != -1)
416     {
417         iRet = ftruncate(iFd, llSize);
418
419         (void)close(iFd);
420     }
421
422     return iRet;
423 }
424
425
426 /*-------------------------------------------------------------------
427     Begin ported fsstress code
428 -------------------------------------------------------------------*/
429
430 /* Stuff from xfscompat.h */
431
432 #define MAXNAMELEN (REDCONF_NAME_MAX+1U) /* Assumed to include NUL */
433
434 struct dioattr {
435     int d_miniosz, d_maxiosz, d_mem;
436 };
437
438 #define MIN(a,b) ((a)<(b) ? (a):(b))
439 #define MAX(a,b) ((a)>(b) ? (a):(b))
440
441 /* End xfscompat.h */
442
443
444 typedef enum {
445     OP_CREAT,
446     OP_FDATASYNC,
447     OP_FSYNC,
448     OP_GETDENTS,
449     OP_LINK,
450     OP_MKDIR,
451     OP_READ,
452     OP_RENAME,
453     OP_RMDIR,
454     OP_STAT,
455     OP_TRUNCATE,
456     OP_UNLINK,
457     OP_WRITE,
458   #if REDCONF_CHECKER == 1
459     OP_CHECK,
460   #endif
461     OP_LAST
462 } opty_t;
463
464 typedef void (*opfnc_t) (int, long);
465
466 typedef struct opdesc {
467     opty_t op;
468     const char *name;
469     opfnc_t func;
470     int freq;
471     int iswrite;
472 } opdesc_t;
473
474 typedef struct fent {
475     int id;
476     int parent;
477 } fent_t;
478
479 typedef struct flist {
480     int nfiles;
481     int nslots;
482     int tag;
483     fent_t *fents;
484 } flist_t;
485
486 typedef struct pathname {
487     int len;
488     char *path;
489 } pathname_t;
490
491 #define FT_DIR      0
492 #define FT_DIRm     (1 << FT_DIR)
493 #define FT_REG      1
494 #define FT_REGm     (1 << FT_REG)
495 #define FT_SYM      2
496 #define FT_SYMm     (1 << FT_SYM)
497 #define FT_DEV      3
498 #define FT_DEVm     (1 << FT_DEV)
499 #define FT_RTF      4
500 #define FT_RTFm     (1 << FT_RTF)
501 #define FT_nft      5
502 #define FT_ANYm     ((1 << FT_nft) - 1)
503 #define FT_REGFILE  (FT_REGm | FT_RTFm)
504 #define FT_NOTDIR   (FT_ANYm & ~FT_DIRm)
505
506 #define FLIST_SLOT_INCR 16
507 #define NDCACHE 64
508
509 #define MAXFSIZE MaxFileSize()
510
511 static void creat_f(int opno, long r);
512 static void fdatasync_f(int opno, long r);
513 static void fsync_f(int opno, long r);
514 static void getdents_f(int opno, long r);
515 static void link_f(int opno, long r);
516 static void mkdir_f(int opno, long r);
517 static void read_f(int opno, long r);
518 static void rename_f(int opno, long r);
519 static void rmdir_f(int opno, long r);
520 static void stat_f(int opno, long r);
521 static void truncate_f(int opno, long r);
522 static void unlink_f(int opno, long r);
523 static void write_f(int opno, long r);
524 #if REDCONF_CHECKER == 1
525 static void check_f(int opno, long r);
526 #endif
527
528 static opdesc_t ops[] = {
529     {OP_CREAT, "creat", creat_f, 4, 1},
530     {OP_FDATASYNC, "fdatasync", fdatasync_f, 1, 1},
531     {OP_FSYNC, "fsync", fsync_f, 1, 1},
532     {OP_GETDENTS, "getdents", getdents_f, 1, 0},
533     {OP_LINK, "link", link_f, 1, 1},
534     {OP_MKDIR, "mkdir", mkdir_f, 2, 1},
535     {OP_READ, "read", read_f, 1, 0},
536     {OP_RENAME, "rename", rename_f, 2, 1},
537     {OP_RMDIR, "rmdir", rmdir_f, 1, 1},
538     {OP_STAT, "stat", stat_f, 1, 0},
539     {OP_TRUNCATE, "truncate", truncate_f, 2, 1},
540     {OP_UNLINK, "unlink", unlink_f, 1, 1},
541     {OP_WRITE, "write", write_f, 4, 1},
542   #if REDCONF_CHECKER == 1
543     {OP_CHECK, "check", check_f, 1, 1},
544   #endif
545 }, *ops_end;
546
547 static flist_t flist[FT_nft] = {
548     {0, 0, 'd', NULL},
549     {0, 0, 'f', NULL},
550     {0, 0, 'l', NULL},
551     {0, 0, 'c', NULL},
552     {0, 0, 'r', NULL},
553 };
554
555 static int dcache[NDCACHE];
556 static opty_t *freq_table;
557 static int freq_table_size;
558 static char *homedir;
559 static int *ilist;
560 static int ilistlen;
561 static off64_t maxfsize;
562 static int namerand;
563 static int nameseq;
564 static int nops;
565 static int operations = 1;
566 static int procid;
567 static int rtpct;
568 static unsigned long seed = 0;
569 static ino_t top_ino;
570 static int verbose = 0;
571
572 static int delete_tree(const char *path);
573 static void add_to_flist(int fd, int it, int parent);
574 static void append_pathname(pathname_t *name, const char *str);
575 static void check_cwd(void);
576 static int creat_path(pathname_t *name, mode_t mode);
577 static void dcache_enter(int dirid, int slot);
578 static void dcache_init(void);
579 static fent_t *dcache_lookup(int dirid);
580 static void dcache_purge(int dirid);
581 static void del_from_flist(int ft, int slot);
582 static void doproc(void);
583 static void fent_to_name(pathname_t *name, flist_t *flp, fent_t *fep);
584 static void fix_parent(int oldid, int newid);
585 static void free_pathname(pathname_t *name);
586 static int generate_fname(fent_t *fep, int ft, pathname_t *name, int *idp, int *v);
587 static int get_fname(int which, long r, pathname_t *name, flist_t **flpp, fent_t **fepp, int *v);
588 static void init_pathname(pathname_t *name);
589 static int link_path(pathname_t *name1, pathname_t *name2);
590 static int lstat64_path(pathname_t *name, REDSTAT *sbuf);
591 static void make_freq_table(void);
592 static int mkdir_path(pathname_t *name, mode_t mode);
593 static void namerandpad(int id, char *buf, int len);
594 static int open_path(pathname_t *name, int oflag);
595 static DIR *opendir_path(pathname_t *name);
596 static int rename_path(pathname_t *name1, pathname_t *name2);
597 static int rmdir_path(pathname_t *name);
598 static void separate_pathname(pathname_t *name, char *buf, pathname_t *newname);
599 static int stat64_path(pathname_t *name, REDSTAT *sbuf);
600 static int truncate64_path(pathname_t *name, off64_t length);
601 static int unlink_path(pathname_t *name);
602 static void usage(const char *progname);
603
604
605 /** @brief Parse parameters for fsstress.
606
607     @param argc         The number of arguments from main().
608     @param argv         The vector of arguments from main().
609     @param pParam       Populated with the fsstress parameters.
610     @param pbVolNum     If non-NULL, populated with the volume number.
611     @param ppszDevice   If non-NULL, populated with the device name argument or
612                         NULL if no device argument is provided.
613
614     @return The result of parsing the parameters.
615 */
616 PARAMSTATUS FsstressParseParams(
617     int             argc,
618     char           *argv[],
619     FSSTRESSPARAM  *pParam,
620     uint8_t        *pbVolNum,
621     const char    **ppszDevice)
622 {
623     int             c;
624     uint8_t         bVolNum;
625     const REDOPTION aLongopts[] =
626     {
627         { "no-cleanup", red_no_argument, NULL, 'c' },
628         { "loops", red_required_argument, NULL, 'l' },
629         { "nops", red_required_argument, NULL, 'n' },
630         { "namepad", red_no_argument, NULL, 'r' },
631         { "seed", red_required_argument, NULL, 's' },
632         { "verbose", red_no_argument, NULL, 'v' },
633         { "dev", red_required_argument, NULL, 'D' },
634         { "help", red_no_argument, NULL, 'H' },
635         { NULL }
636     };
637
638     /*  If run without parameters, treat as a help request.
639     */
640     if(argc <= 1)
641     {
642         goto Help;
643     }
644
645     /*  Assume no device argument to start with.
646     */
647     if(ppszDevice != NULL)
648     {
649         *ppszDevice = NULL;
650     }
651
652     /*  Set default parameters.
653     */
654     FsstressDefaultParams(pParam);
655
656     while((c = RedGetoptLong(argc, argv, "cl:n:rs:vD:H", aLongopts, NULL)) != -1)
657     {
658         switch(c)
659         {
660             case 'c': /* --no-cleanup */
661                 pParam->fNoCleanup = true;
662                 break;
663             case 'l': /* --loops */
664                 pParam->ulLoops = RedAtoI(red_optarg);
665                 break;
666             case 'n': /* --nops */
667                 pParam->ulNops = RedAtoI(red_optarg);
668                 break;
669             case 'r': /* --namepad */
670                 pParam->fNamePad = true;
671                 break;
672             case 's': /* --seed */
673                 pParam->ulSeed = RedAtoI(red_optarg);
674                 break;
675             case 'v': /* --verbose */
676                 pParam->fVerbose = true;
677                 break;
678             case 'D': /* --dev */
679                 if(ppszDevice != NULL)
680                 {
681                     *ppszDevice = red_optarg;
682                 }
683                 break;
684             case 'H': /* --help */
685                 goto Help;
686             case '?': /* Unknown or ambiguous option */
687             case ':': /* Option missing required argument */
688             default:
689                 goto BadOpt;
690         }
691     }
692
693     /*  RedGetoptLong() has permuted argv to move all non-option arguments to
694         the end.  We expect to find a volume identifier.
695     */
696     if(red_optind >= argc)
697     {
698         RedPrintf("Missing volume argument\n");
699         goto BadOpt;
700     }
701
702     bVolNum = RedFindVolumeNumber(argv[red_optind]);
703     if(bVolNum == REDCONF_VOLUME_COUNT)
704     {
705         RedPrintf("Error: \"%s\" is not a valid volume identifier.\n", argv[red_optind]);
706         goto BadOpt;
707     }
708
709     if(pbVolNum != NULL)
710     {
711         *pbVolNum = bVolNum;
712     }
713
714     red_optind++; /* Move past volume parameter. */
715     if(red_optind < argc)
716     {
717         int32_t ii;
718
719         for(ii = red_optind; ii < argc; ii++)
720         {
721             RedPrintf("Error: Unexpected command-line argument \"%s\".\n", argv[ii]);
722         }
723
724         goto BadOpt;
725     }
726
727     return PARAMSTATUS_OK;
728
729   BadOpt:
730
731     RedPrintf("%s - invalid parameters\n", argv[0U]);
732     usage(argv[0U]);
733     return PARAMSTATUS_BAD;
734
735   Help:
736
737     usage(argv[0U]);
738     return PARAMSTATUS_HELP;
739 }
740
741
742 /** @brief Set default fsstress parameters.
743
744     @param pParam   Populated with the default fsstress parameters.
745 */
746 void FsstressDefaultParams(
747     FSSTRESSPARAM *pParam)
748 {
749     RedMemSet(pParam, 0U, sizeof(*pParam));
750     pParam->ulLoops = 1U;
751     pParam->ulNops = 10000U;
752 }
753
754
755 /** @brief Start fsstress.
756
757     @param pParam   fsstress parameters, either from FsstressParseParams() or
758                     constructed programatically.
759
760     @return Zero on success, otherwise nonzero.
761 */
762 int FsstressStart(
763     const FSSTRESSPARAM *pParam)
764 {
765     char buf[10];
766     int fd;
767     int i;
768     int cleanup;
769     int loops;
770     int loopcntr = 1;
771
772     nops = sizeof(ops) / sizeof(ops[0]);
773     ops_end = &ops[nops];
774
775     /*  Copy the already-parsed parameters into the traditional variables.
776     */
777     cleanup = pParam->fNoCleanup ? 1 : 0;
778     loops = pParam->ulLoops;
779     operations = pParam->ulNops;
780     namerand = pParam->fNamePad ? 1 : 0;
781     seed = pParam->ulSeed;
782     verbose = pParam->fVerbose ? 1 : 0;
783
784     make_freq_table();
785
786     while ((loopcntr <= loops) || (loops == 0)) {
787         RedSNPrintf(buf, sizeof(buf), "fss%x", getpid());
788         fd = creat(buf, 0666);
789         maxfsize = (off64_t) MAXFSIZE;
790         dcache_init();
791         if (!seed) {
792             seed = (unsigned long)RedOsClockGetTime();
793             RedPrintf("seed = %ld\n", seed);
794         }
795         close(fd);
796         unlink(buf);
797         procid = 0;
798         doproc();
799         if (cleanup == 0) {
800             delete_tree("/");
801             for (i = 0; i < FT_nft; i++) {
802                 flist[i].nslots = 0;
803                 flist[i].nfiles = 0;
804                 free(flist[i].fents);
805                 flist[i].fents = NULL;
806             }
807         }
808         loopcntr++;
809     }
810     return 0;
811 }
812
813 static int delete_tree(const char *path)
814 {
815     REDSTAT sb;
816     DIR *dp;
817     REDDIRENT *dep;
818     char *childpath;
819     size_t len;
820     int e;
821
822     e = stat(path, &sb);
823     if (e)
824         return errno;
825
826     if (!S_ISDIR(sb.st_mode))
827         return unlink(path) ? errno : 0;
828
829     dp = opendir(path);
830     if (dp == NULL)
831         return errno;
832
833     while((dep = readdir(dp)) != NULL) {
834         len = strlen(path) + 1 + strlen(dep->d_name) + 1;
835         childpath = malloc(len);
836
837         strcpy(childpath, path);
838         if (childpath[strlen(childpath) - 1] != '/')
839             strcat(childpath, "/");
840         strcat(childpath, dep->d_name);
841
842         e = delete_tree(childpath);
843
844         free(childpath);
845
846         if (e)
847             break;
848     }
849
850     if (e == 0 && strcmp(path, "/") != 0) {
851         e = rmdir(path) ? errno : 0;
852     }
853     closedir(dp);
854     return e;
855 }
856
857 static void add_to_flist(int ft, int id, int parent)
858 {
859     fent_t *fep;
860     flist_t *ftp;
861
862     ftp = &flist[ft];
863     if (ftp->nfiles == ftp->nslots) {
864         ftp->nslots += FLIST_SLOT_INCR;
865         ftp->fents = realloc(ftp->fents, ftp->nslots * sizeof(fent_t));
866     }
867     fep = &ftp->fents[ftp->nfiles++];
868     fep->id = id;
869     fep->parent = parent;
870 }
871
872 static void append_pathname(pathname_t *name, const char *str)
873 {
874     int len;
875
876     len = strlen(str);
877 #ifdef DEBUG
878     if (len && *str == '/' && name->len == 0) {
879         RedPrintf("fsstress: append_pathname failure\n");
880         chdir(homedir);
881         abort();
882
883     }
884 #endif
885     name->path = realloc(name->path, name->len + 1 + len);
886     strcpy(&name->path[name->len], str);
887     name->len += len;
888 }
889
890 static void check_cwd(void)
891 {
892 #ifdef DEBUG
893     REDSTAT statbuf;
894
895     if (stat64(".", &statbuf) == 0 && statbuf.st_ino == top_ino)
896         return;
897     chdir(homedir);
898     RedPrintf("fsstress: check_cwd failure\n");
899     abort();
900
901 #endif
902 }
903
904 static int creat_path(pathname_t *name, mode_t mode)
905 {
906     char buf[MAXNAMELEN];
907     pathname_t newname;
908     int rval;
909
910     rval = creat(name->path, mode);
911     if (rval >= 0 || errno != RED_ENAMETOOLONG)
912         return rval;
913     separate_pathname(name, buf, &newname);
914     if (chdir(buf) == 0) {
915         rval = creat_path(&newname, mode);
916         chdir("..");
917     }
918     free_pathname(&newname);
919     return rval;
920 }
921
922 static void dcache_enter(int dirid, int slot)
923 {
924     dcache[dirid % NDCACHE] = slot;
925 }
926
927 static void dcache_init(void)
928 {
929     int i;
930
931     for (i = 0; i < NDCACHE; i++)
932         dcache[i] = -1;
933 }
934
935 static fent_t *dcache_lookup(int dirid)
936 {
937     fent_t *fep;
938     int i;
939
940     i = dcache[dirid % NDCACHE];
941     if (i >= 0 && (fep = &flist[FT_DIR].fents[i])->id == dirid)
942         return fep;
943     return NULL;
944 }
945
946 static void dcache_purge(int dirid)
947 {
948     int *dcp;
949
950     dcp = &dcache[dirid % NDCACHE];
951     if (*dcp >= 0 && flist[FT_DIR].fents[*dcp].id == dirid)
952         *dcp = -1;
953 }
954
955 static void del_from_flist(int ft, int slot)
956 {
957     flist_t *ftp;
958
959     ftp = &flist[ft];
960     if (ft == FT_DIR)
961         dcache_purge(ftp->fents[slot].id);
962     if (slot != ftp->nfiles - 1) {
963         if (ft == FT_DIR)
964             dcache_purge(ftp->fents[ftp->nfiles - 1].id);
965         ftp->fents[slot] = ftp->fents[--ftp->nfiles];
966     } else
967         ftp->nfiles--;
968 }
969
970 static fent_t *dirid_to_fent(int dirid)
971 {
972     fent_t *efep;
973     fent_t *fep;
974     flist_t *flp;
975
976     if ((fep = dcache_lookup(dirid)))
977         return fep;
978     flp = &flist[FT_DIR];
979     for (fep = flp->fents, efep = &fep[flp->nfiles]; fep < efep; fep++) {
980         if (fep->id == dirid) {
981             dcache_enter(dirid, (int)(fep - flp->fents));
982             return fep;
983         }
984     }
985     return NULL;
986 }
987
988 static void doproc(void)
989 {
990     REDSTAT statbuf;
991     char buf[10];
992     int opno;
993     opdesc_t *p;
994
995     RedSNPrintf(buf, sizeof(buf), "p%x", procid);
996     (void)mkdir(buf);
997     if (chdir(buf) < 0 || stat64(".", &statbuf) < 0) {
998         perror(buf);
999         _exit(1);
1000     }
1001     top_ino = statbuf.st_ino;
1002     homedir = getcwd(NULL, 0);
1003     seed += procid;
1004     srandom(seed);
1005     if (namerand)
1006         namerand = random();
1007     for (opno = 0; opno < operations; opno++) {
1008         p = &ops[freq_table[random() % freq_table_size]];
1009         if ((unsigned long)p->func < 4096)
1010             abort();
1011
1012         p->func(opno, random());
1013     }
1014     free(homedir);
1015 }
1016
1017 static void fent_to_name(pathname_t *name, flist_t *flp, fent_t *fep)
1018 {
1019     char buf[MAXNAMELEN];
1020     int i;
1021     fent_t *pfep;
1022
1023     if (fep == NULL)
1024         return;
1025     if (fep->parent != -1) {
1026         pfep = dirid_to_fent(fep->parent);
1027         fent_to_name(name, &flist[FT_DIR], pfep);
1028         append_pathname(name, "/");
1029     }
1030     i = RedSNPrintf(buf, sizeof(buf), "%c%x", flp->tag, fep->id);
1031     namerandpad(fep->id, buf, i);
1032     append_pathname(name, buf);
1033 }
1034
1035 static void fix_parent(int oldid, int newid)
1036 {
1037     fent_t *fep;
1038     flist_t *flp;
1039     int i;
1040     int j;
1041
1042     for (i = 0, flp = flist; i < FT_nft; i++, flp++) {
1043         for (j = 0, fep = flp->fents; j < flp->nfiles; j++, fep++) {
1044             if (fep->parent == oldid)
1045                 fep->parent = newid;
1046         }
1047     }
1048 }
1049
1050 static void free_pathname(pathname_t *name)
1051 {
1052     if (name->path) {
1053         free(name->path);
1054         name->path = NULL;
1055         name->len = 0;
1056     }
1057 }
1058
1059 static int generate_fname(fent_t *fep, int ft, pathname_t *name, int *idp, int *v)
1060 {
1061     char buf[MAXNAMELEN];
1062     flist_t *flp;
1063     int id;
1064     int j;
1065     int len;
1066
1067     flp = &flist[ft];
1068     len = RedSNPrintf(buf, sizeof(buf), "%c%x", flp->tag, id = nameseq++);
1069     namerandpad(id, buf, len);
1070     if (fep) {
1071         fent_to_name(name, &flist[FT_DIR], fep);
1072         append_pathname(name, "/");
1073     }
1074     append_pathname(name, buf);
1075     *idp = id;
1076     *v = verbose;
1077     for (j = 0; !*v && j < ilistlen; j++) {
1078         if (ilist[j] == id) {
1079             *v = 1;
1080             break;
1081         }
1082     }
1083     return 1;
1084 }
1085
1086 static int
1087 get_fname(int which, long r, pathname_t *name, flist_t **flpp, fent_t **fepp, int *v)
1088 {
1089     int c;
1090     fent_t *fep;
1091     flist_t *flp;
1092     int i;
1093     int j;
1094     int x;
1095
1096     for (i = 0, c = 0, flp = flist; i < FT_nft; i++, flp++) {
1097         if (which & (1 << i))
1098             c += flp->nfiles;
1099     }
1100     if (c == 0) {
1101         if (flpp)
1102             *flpp = NULL;
1103         if (fepp)
1104             *fepp = NULL;
1105         *v = verbose;
1106         return 0;
1107     }
1108     x = (int)(r % c);
1109     for (i = 0, c = 0, flp = flist; i < FT_nft; i++, flp++) {
1110         if (which & (1 << i)) {
1111             if (x < c + flp->nfiles) {
1112                 fep = &flp->fents[x - c];
1113                 if (name)
1114                     fent_to_name(name, flp, fep);
1115                 if (flpp)
1116                     *flpp = flp;
1117                 if (fepp)
1118                     *fepp = fep;
1119                 *v = verbose;
1120                 for (j = 0; !*v && j < ilistlen; j++) {
1121                     if (ilist[j] == fep->id) {
1122                         *v = 1;
1123                         break;
1124                     }
1125                 }
1126                 return 1;
1127             }
1128             c += flp->nfiles;
1129         }
1130     }
1131 #ifdef DEBUG
1132     RedPrintf("fsstress: get_fname failure\n");
1133     abort();
1134 #endif
1135     return -1;
1136
1137 }
1138
1139 static void init_pathname(pathname_t *name)
1140 {
1141     name->len = 0;
1142     name->path = NULL;
1143 }
1144
1145 static int link_path(pathname_t *name1, pathname_t *name2)
1146 {
1147     char buf1[MAXNAMELEN];
1148     char buf2[MAXNAMELEN];
1149     int down1;
1150     pathname_t newname1;
1151     pathname_t newname2;
1152     int rval;
1153
1154     rval = link(name1->path, name2->path);
1155     if (rval >= 0 || errno != RED_ENAMETOOLONG)
1156         return rval;
1157     separate_pathname(name1, buf1, &newname1);
1158     separate_pathname(name2, buf2, &newname2);
1159     if (strcmp(buf1, buf2) == 0) {
1160         if (chdir(buf1) == 0) {
1161             rval = link_path(&newname1, &newname2);
1162             chdir("..");
1163         }
1164     } else {
1165         if (strcmp(buf1, "..") == 0)
1166             down1 = 0;
1167         else if (strcmp(buf2, "..") == 0)
1168             down1 = 1;
1169         else if (strlen(buf1) == 0)
1170             down1 = 0;
1171         else if (strlen(buf2) == 0)
1172             down1 = 1;
1173         else
1174             down1 = MAX(newname1.len, 3 + name2->len) <=
1175                 MAX(3 + name1->len, newname2.len);
1176         if (down1) {
1177             free_pathname(&newname2);
1178             append_pathname(&newname2, "../");
1179             append_pathname(&newname2, name2->path);
1180             if (chdir(buf1) == 0) {
1181                 rval = link_path(&newname1, &newname2);
1182                 chdir("..");
1183             }
1184         } else {
1185             free_pathname(&newname1);
1186             append_pathname(&newname1, "../");
1187             append_pathname(&newname1, name1->path);
1188             if (chdir(buf2) == 0) {
1189                 rval = link_path(&newname1, &newname2);
1190                 chdir("..");
1191             }
1192         }
1193     }
1194     free_pathname(&newname1);
1195     free_pathname(&newname2);
1196     return rval;
1197 }
1198
1199 static int lstat64_path(pathname_t *name, REDSTAT *sbuf)
1200 {
1201     char buf[MAXNAMELEN];
1202     pathname_t newname;
1203     int rval;
1204
1205     rval = lstat64(name->path, sbuf);
1206     if (rval >= 0 || errno != RED_ENAMETOOLONG)
1207         return rval;
1208     separate_pathname(name, buf, &newname);
1209     if (chdir(buf) == 0) {
1210         rval = lstat64_path(&newname, sbuf);
1211         chdir("..");
1212     }
1213     free_pathname(&newname);
1214     return rval;
1215 }
1216
1217 static void make_freq_table(void)
1218 {
1219     int f;
1220     int i;
1221     opdesc_t *p;
1222
1223     for (p = ops, f = 0; p < ops_end; p++)
1224         f += p->freq;
1225     freq_table = malloc(f * sizeof(*freq_table));
1226     freq_table_size = f;
1227     for (p = ops, i = 0; p < ops_end; p++) {
1228         for (f = 0; f < p->freq; f++, i++)
1229             freq_table[i] = p->op;
1230     }
1231 }
1232
1233 static int mkdir_path(pathname_t *name, mode_t mode)
1234 {
1235     char buf[MAXNAMELEN];
1236     pathname_t newname;
1237     int rval;
1238
1239     rval = mkdir(name->path);
1240     if (rval >= 0 || errno != RED_ENAMETOOLONG)
1241         return rval;
1242     separate_pathname(name, buf, &newname);
1243     if (chdir(buf) == 0) {
1244         rval = mkdir_path(&newname, mode);
1245         chdir("..");
1246     }
1247     free_pathname(&newname);
1248     return rval;
1249 }
1250
1251 static void namerandpad(int id, char *buf, int len)
1252 {
1253     int bucket;
1254     static int buckets[8] = {0};
1255     static int bucket_count = 0;
1256     int bucket_value;
1257     int i;
1258     int padlen;
1259     int padmod;
1260
1261     if (namerand == 0)
1262         return;
1263
1264     /*  buckets[] used to be a statically initialized array with the following
1265         initializer: { 2, 4, 8, 16, 32, 64, 128, MAXNAMELEN - 1 }
1266
1267         The problem is that with Reliance Edge, the maximum name length might be
1268         less than 128.  So the below code populates buckets[] in a similar
1269         fashion but avoids name lengths longer than the maximum.  For example,
1270         if the max name is 20, the resulting array is { 2, 4, 8, 16, 20 }.
1271     */
1272     if (!bucket_count) {
1273         bucket_count = sizeof(buckets) / sizeof(buckets[0]);
1274         bucket_value = 2;
1275         for (i = 0; i < bucket_count; i++) {
1276             if (bucket_value > 128 || bucket_value >= (int)MAXNAMELEN - 1)
1277                 break;
1278             buckets[i] = bucket_value;
1279             bucket_value *= 2;
1280         }
1281         if (i < bucket_count) {
1282             buckets[i] = MAXNAMELEN - 1;
1283             i++;
1284         }
1285         bucket_count = i;
1286     }
1287
1288     bucket = (id ^ namerand) % bucket_count;
1289     padmod = buckets[bucket] + 1 - len;
1290     if (padmod <= 0)
1291         return;
1292     padlen = (id ^ namerand) % padmod;
1293     if (padlen) {
1294         memset(&buf[len], 'X', padlen);
1295         buf[len + padlen] = '\0';
1296     }
1297 }
1298
1299 static int open_path(pathname_t *name, int oflag)
1300 {
1301     char buf[MAXNAMELEN];
1302     pathname_t newname;
1303     int rval;
1304
1305     rval = open(name->path, oflag);
1306     if (rval >= 0 || errno != RED_ENAMETOOLONG)
1307         return rval;
1308     separate_pathname(name, buf, &newname);
1309     if (chdir(buf) == 0) {
1310         rval = open_path(&newname, oflag);
1311         chdir("..");
1312     }
1313     free_pathname(&newname);
1314     return rval;
1315 }
1316
1317 static DIR *opendir_path(pathname_t *name)
1318 {
1319     char buf[MAXNAMELEN];
1320     pathname_t newname;
1321     DIR *rval;
1322
1323     rval = opendir(name->path);
1324     if (rval || errno != RED_ENAMETOOLONG)
1325         return rval;
1326     separate_pathname(name, buf, &newname);
1327     if (chdir(buf) == 0) {
1328         rval = opendir_path(&newname);
1329         chdir("..");
1330     }
1331     free_pathname(&newname);
1332     return rval;
1333 }
1334
1335 static int rename_path(pathname_t *name1, pathname_t *name2)
1336 {
1337     char buf1[MAXNAMELEN];
1338     char buf2[MAXNAMELEN];
1339     int down1;
1340     pathname_t newname1;
1341     pathname_t newname2;
1342     int rval;
1343
1344     rval = rename(name1->path, name2->path);
1345     if (rval >= 0 || errno != RED_ENAMETOOLONG)
1346         return rval;
1347     separate_pathname(name1, buf1, &newname1);
1348     separate_pathname(name2, buf2, &newname2);
1349     if (strcmp(buf1, buf2) == 0) {
1350         if (chdir(buf1) == 0) {
1351             rval = rename_path(&newname1, &newname2);
1352             chdir("..");
1353         }
1354     } else {
1355         if (strcmp(buf1, "..") == 0)
1356             down1 = 0;
1357         else if (strcmp(buf2, "..") == 0)
1358             down1 = 1;
1359         else if (strlen(buf1) == 0)
1360             down1 = 0;
1361         else if (strlen(buf2) == 0)
1362             down1 = 1;
1363         else
1364             down1 = MAX(newname1.len, 3 + name2->len) <=
1365                 MAX(3 + name1->len, newname2.len);
1366         if (down1) {
1367             free_pathname(&newname2);
1368             append_pathname(&newname2, "../");
1369             append_pathname(&newname2, name2->path);
1370             if (chdir(buf1) == 0) {
1371                 rval = rename_path(&newname1, &newname2);
1372                 chdir("..");
1373             }
1374         } else {
1375             free_pathname(&newname1);
1376             append_pathname(&newname1, "../");
1377             append_pathname(&newname1, name1->path);
1378             if (chdir(buf2) == 0) {
1379                 rval = rename_path(&newname1, &newname2);
1380                 chdir("..");
1381             }
1382         }
1383     }
1384     free_pathname(&newname1);
1385     free_pathname(&newname2);
1386     return rval;
1387 }
1388
1389 static int rmdir_path(pathname_t *name)
1390 {
1391     char buf[MAXNAMELEN];
1392     pathname_t newname;
1393     int rval;
1394
1395     rval = rmdir(name->path);
1396     if (rval >= 0 || errno != RED_ENAMETOOLONG)
1397         return rval;
1398     separate_pathname(name, buf, &newname);
1399     if (chdir(buf) == 0) {
1400         rval = rmdir_path(&newname);
1401         chdir("..");
1402     }
1403     free_pathname(&newname);
1404     return rval;
1405 }
1406
1407 static void separate_pathname(pathname_t *name, char *buf, pathname_t *newname)
1408 {
1409     char *slash;
1410
1411     init_pathname(newname);
1412     slash = strchr(name->path, '/');
1413     if (slash == NULL) {
1414         buf[0] = '\0';
1415         return;
1416     }
1417     *slash = '\0';
1418     strcpy(buf, name->path);
1419     *slash = '/';
1420     append_pathname(newname, slash + 1);
1421 }
1422
1423 static int stat64_path(pathname_t *name, REDSTAT *sbuf)
1424 {
1425     char buf[MAXNAMELEN];
1426     pathname_t newname;
1427     int rval;
1428
1429     rval = stat64(name->path, sbuf);
1430     if (rval >= 0 || errno != RED_ENAMETOOLONG)
1431         return rval;
1432     separate_pathname(name, buf, &newname);
1433     if (chdir(buf) == 0) {
1434         rval = stat64_path(&newname, sbuf);
1435         chdir("..");
1436     }
1437     free_pathname(&newname);
1438     return rval;
1439 }
1440
1441 static int truncate64_path(pathname_t *name, off64_t length)
1442 {
1443     char buf[MAXNAMELEN];
1444     pathname_t newname;
1445     int rval;
1446
1447     rval = truncate64(name->path, length);
1448     if (rval >= 0 || errno != RED_ENAMETOOLONG)
1449         return rval;
1450     separate_pathname(name, buf, &newname);
1451     if (chdir(buf) == 0) {
1452         rval = truncate64_path(&newname, length);
1453         chdir("..");
1454     }
1455     free_pathname(&newname);
1456     return rval;
1457 }
1458
1459 static int unlink_path(pathname_t *name)
1460 {
1461     char buf[MAXNAMELEN];
1462     pathname_t newname;
1463     int rval;
1464
1465     rval = unlink(name->path);
1466     if (rval >= 0 || errno != RED_ENAMETOOLONG)
1467         return rval;
1468     separate_pathname(name, buf, &newname);
1469     if (chdir(buf) == 0) {
1470         rval = unlink_path(&newname);
1471         chdir("..");
1472     }
1473     free_pathname(&newname);
1474     return rval;
1475 }
1476
1477 static void usage(const char *progname)
1478 {
1479     RedPrintf("usage: %s VolumeID [Options]\n", progname);
1480     RedPrintf("File system stress test.\n\n");
1481     RedPrintf("Where:\n");
1482     RedPrintf("  VolumeID\n");
1483     RedPrintf("      A volume number (e.g., 2) or a volume path prefix (e.g., VOL1: or /data)\n");
1484     RedPrintf("      of the volume to test.\n");
1485     RedPrintf("And 'Options' are any of the following:\n");
1486     RedPrintf("  --no-cleanup, -c\n");
1487     RedPrintf("      Specifies not to remove files (cleanup) after execution\n");
1488     RedPrintf("  --loops=count, -l count\n");
1489     RedPrintf("      Specifies the number of times the entire test should loop.  Use 0 for\n");
1490     RedPrintf("      infinite.  Default 1.\n");
1491     RedPrintf("  --nops=count, -n count\n");
1492     RedPrintf("      Specifies the number of operations to run (default 10000).\n");
1493     RedPrintf("  --namepad, -r\n");
1494     RedPrintf("      Specifies to use random name padding (resulting in longer names).\n");
1495     RedPrintf("  --seed=value, -s value\n");
1496     RedPrintf("      Specifies the seed for the random number generator (default timestamp).\n");
1497     RedPrintf("  --verbose, -v\n");
1498     RedPrintf("      Specifies verbose mode (without this, test is very quiet).\n");
1499     RedPrintf("  --dev=devname, -D devname\n");
1500     RedPrintf("      Specifies the device name.  This is typically only meaningful when\n");
1501     RedPrintf("      running the test on a host machine.  This can be \"ram\" to test on a RAM\n");
1502     RedPrintf("      disk, the path and name of a file disk (e.g., red.bin); or an OS-specific\n");
1503     RedPrintf("      reference to a device (on Windows, a drive letter like G: or a device name\n");
1504     RedPrintf("      like \\\\.\\PhysicalDrive7).\n");
1505     RedPrintf("  --help, -H\n");
1506     RedPrintf("      Prints this usage text and exits.\n\n");
1507     RedPrintf("Warning: This test will format the volume -- destroying all existing data.\n\n");
1508 }
1509
1510 static void creat_f(int opno, long r)
1511 {
1512     int e;
1513     int e1;
1514     pathname_t f;
1515     int fd;
1516     fent_t *fep;
1517     int id;
1518     int parid;
1519     int type;
1520     int v;
1521     int v1;
1522     int esz = 0;
1523
1524     if (!get_fname(FT_DIRm, r, NULL, NULL, &fep, &v1))
1525         parid = -1;
1526     else
1527         parid = fep->id;
1528     init_pathname(&f);
1529     type = rtpct ? ((int)(random() % 100) > rtpct ? FT_REG : FT_RTF) : FT_REG;
1530     e = generate_fname(fep, type, &f, &id, &v);
1531     v |= v1;
1532     if (!e) {
1533         if (v) {
1534             fent_to_name(&f, &flist[FT_DIR], fep);
1535             RedPrintf("%d/%d: creat - no filename from %s\n",
1536                    procid, opno, f.path);
1537         }
1538         free_pathname(&f);
1539         return;
1540     }
1541     fd = creat_path(&f, 0666);
1542     e = fd < 0 ? errno : 0;
1543     e1 = 0;
1544     check_cwd();
1545     esz = 0;
1546     if (fd >= 0) {
1547         add_to_flist(type, id, parid);
1548         close(fd);
1549     }
1550     if (v)
1551         RedPrintf("%d/%d: creat %s x:%d %d %d\n", procid, opno, f.path,
1552                esz, e, e1);
1553     free_pathname(&f);
1554 }
1555
1556 static void fdatasync_f(int opno, long r)
1557 {
1558     int e;
1559     pathname_t f;
1560     int fd;
1561     int v;
1562
1563     init_pathname(&f);
1564     if (!get_fname(FT_REGFILE, r, &f, NULL, NULL, &v)) {
1565         if (v)
1566             RedPrintf("%d/%d: fdatasync - no filename\n",
1567                    procid, opno);
1568         free_pathname(&f);
1569         return;
1570     }
1571     fd = open_path(&f, O_WRONLY);
1572     e = fd < 0 ? errno : 0;
1573     check_cwd();
1574     if (fd < 0) {
1575         if (v)
1576             RedPrintf("%d/%d: fdatasync - open %s failed %d\n",
1577                    procid, opno, f.path, e);
1578         free_pathname(&f);
1579         return;
1580     }
1581     e = fdatasync(fd) < 0 ? errno : 0;
1582     if (v)
1583         RedPrintf("%d/%d: fdatasync %s %d\n", procid, opno, f.path, e);
1584     free_pathname(&f);
1585     close(fd);
1586 }
1587
1588 static void fsync_f(int opno, long r)
1589 {
1590     int e;
1591     pathname_t f;
1592     int fd;
1593     int v;
1594
1595     init_pathname(&f);
1596     if (!get_fname(FT_REGFILE, r, &f, NULL, NULL, &v)) {
1597         if (v)
1598             RedPrintf("%d/%d: fsync - no filename\n", procid, opno);
1599         free_pathname(&f);
1600         return;
1601     }
1602     fd = open_path(&f, O_WRONLY);
1603     e = fd < 0 ? errno : 0;
1604     check_cwd();
1605     if (fd < 0) {
1606         if (v)
1607             RedPrintf("%d/%d: fsync - open %s failed %d\n",
1608                    procid, opno, f.path, e);
1609         free_pathname(&f);
1610         return;
1611     }
1612     e = fsync(fd) < 0 ? errno : 0;
1613     if (v)
1614         RedPrintf("%d/%d: fsync %s %d\n", procid, opno, f.path, e);
1615     free_pathname(&f);
1616     close(fd);
1617 }
1618
1619 static void getdents_f(int opno, long r)
1620 {
1621     DIR *dir;
1622     pathname_t f;
1623     int v;
1624
1625     init_pathname(&f);
1626     if (!get_fname(FT_DIRm, r, &f, NULL, NULL, &v))
1627         append_pathname(&f, ".");
1628     dir = opendir_path(&f);
1629     check_cwd();
1630     if (dir == NULL) {
1631         if (v)
1632             RedPrintf("%d/%d: getdents - can't open %s\n",
1633                    procid, opno, f.path);
1634         free_pathname(&f);
1635         return;
1636     }
1637     while (readdir64(dir) != NULL)
1638         continue;
1639     if (v)
1640         RedPrintf("%d/%d: getdents %s 0\n", procid, opno, f.path);
1641     free_pathname(&f);
1642     closedir(dir);
1643 }
1644
1645 static void link_f(int opno, long r)
1646 {
1647     int e;
1648     pathname_t f;
1649     fent_t *fep;
1650     flist_t *flp;
1651     int id;
1652     pathname_t l;
1653     int parid;
1654     int v;
1655     int v1;
1656
1657     init_pathname(&f);
1658     if (!get_fname(FT_NOTDIR, r, &f, &flp, NULL, &v1)) {
1659         if (v1)
1660             RedPrintf("%d/%d: link - no file\n", procid, opno);
1661         free_pathname(&f);
1662         return;
1663     }
1664     if (!get_fname(FT_DIRm, random(), NULL, NULL, &fep, &v))
1665         parid = -1;
1666     else
1667         parid = fep->id;
1668     v |= v1;
1669     init_pathname(&l);
1670     e = generate_fname(fep, (int)(flp - flist), &l, &id, &v1);
1671     v |= v1;
1672     if (!e) {
1673         if (v) {
1674             fent_to_name(&l, &flist[FT_DIR], fep);
1675             RedPrintf("%d/%d: link - no filename from %s\n",
1676                    procid, opno, l.path);
1677         }
1678         free_pathname(&l);
1679         free_pathname(&f);
1680         return;
1681     }
1682     e = link_path(&f, &l) < 0 ? errno : 0;
1683     check_cwd();
1684     if (e == 0)
1685         add_to_flist((int)(flp - flist), id, parid);
1686     if (v)
1687         RedPrintf("%d/%d: link %s %s %d\n", procid, opno, f.path, l.path,
1688                e);
1689     free_pathname(&l);
1690     free_pathname(&f);
1691 }
1692
1693 static void mkdir_f(int opno, long r)
1694 {
1695     int e;
1696     pathname_t f;
1697     fent_t *fep;
1698     int id;
1699     int parid;
1700     int v;
1701     int v1;
1702
1703     if (!get_fname(FT_DIRm, r, NULL, NULL, &fep, &v))
1704         parid = -1;
1705     else
1706         parid = fep->id;
1707     init_pathname(&f);
1708     e = generate_fname(fep, FT_DIR, &f, &id, &v1);
1709     v |= v1;
1710     if (!e) {
1711         if (v) {
1712             fent_to_name(&f, &flist[FT_DIR], fep);
1713             RedPrintf("%d/%d: mkdir - no filename from %s\n",
1714                    procid, opno, f.path);
1715         }
1716         free_pathname(&f);
1717         return;
1718     }
1719     e = mkdir_path(&f, 0777) < 0 ? errno : 0;
1720     check_cwd();
1721     if (e == 0)
1722         add_to_flist(FT_DIR, id, parid);
1723     if (v)
1724         RedPrintf("%d/%d: mkdir %s %d\n", procid, opno, f.path, e);
1725     free_pathname(&f);
1726 }
1727
1728 static void read_f(int opno, long r)
1729 {
1730     char *buf;
1731     int e;
1732     pathname_t f;
1733     int fd;
1734     uint32_t len;
1735     __int64_t lr;
1736     off64_t off;
1737     REDSTAT stb;
1738     int v;
1739
1740     init_pathname(&f);
1741     if (!get_fname(FT_REGFILE, r, &f, NULL, NULL, &v)) {
1742         if (v)
1743             RedPrintf("%d/%d: read - no filename\n", procid, opno);
1744         free_pathname(&f);
1745         return;
1746     }
1747     fd = open_path(&f, O_RDONLY);
1748     e = fd < 0 ? errno : 0;
1749     check_cwd();
1750     if (fd < 0) {
1751         if (v)
1752             RedPrintf("%d/%d: read - open %s failed %d\n",
1753                    procid, opno, f.path, e);
1754         free_pathname(&f);
1755         return;
1756     }
1757     if (fstat64(fd, &stb) < 0) {
1758         if (v)
1759             RedPrintf("%d/%d: read - fstat64 %s failed %d\n",
1760                    procid, opno, f.path, errno);
1761         free_pathname(&f);
1762         close(fd);
1763         return;
1764     }
1765     if (stb.st_size == 0) {
1766         if (v)
1767             RedPrintf("%d/%d: read - %s zero size\n", procid, opno,
1768                    f.path);
1769         free_pathname(&f);
1770         close(fd);
1771         return;
1772     }
1773     lr = ((__int64_t) random() << 32) + random();
1774     off = (off64_t) (lr % stb.st_size);
1775     lseek64(fd, off, SEEK_SET);
1776     len = (random() % (getpagesize() * 4)) + 1;
1777     buf = malloc(len);
1778     e = read(fd, buf, len) < 0 ? errno : 0;
1779     free(buf);
1780     if (v)
1781         RedPrintf("%d/%d: read %s [%lld,%ld] %d\n",
1782                procid, opno, f.path, (long long)off, (long int)len, e);
1783     free_pathname(&f);
1784     close(fd);
1785 }
1786
1787 static void rename_f(int opno, long r)
1788 {
1789     fent_t *dfep;
1790     int e;
1791     pathname_t f;
1792     fent_t *fep;
1793     flist_t *flp;
1794     int id;
1795     pathname_t newf;
1796     int oldid;
1797     int parid;
1798     int v;
1799     int v1;
1800
1801     init_pathname(&f);
1802     if (!get_fname(FT_ANYm, r, &f, &flp, &fep, &v1)) {
1803         if (v1)
1804             RedPrintf("%d/%d: rename - no filename\n", procid, opno);
1805         free_pathname(&f);
1806         return;
1807     }
1808     if (!get_fname(FT_DIRm, random(), NULL, NULL, &dfep, &v))
1809         parid = -1;
1810     else
1811         parid = dfep->id;
1812     v |= v1;
1813     init_pathname(&newf);
1814     e = generate_fname(dfep, (int)(flp - flist), &newf, &id, &v1);
1815     v |= v1;
1816     if (!e) {
1817         if (v) {
1818             fent_to_name(&f, &flist[FT_DIR], dfep);
1819             RedPrintf("%d/%d: rename - no filename from %s\n",
1820                    procid, opno, f.path);
1821         }
1822         free_pathname(&newf);
1823         free_pathname(&f);
1824         return;
1825     }
1826     e = rename_path(&f, &newf) < 0 ? errno : 0;
1827     check_cwd();
1828     if (e == 0) {
1829         if (flp - flist == FT_DIR) {
1830             oldid = fep->id;
1831             fix_parent(oldid, id);
1832         }
1833         del_from_flist((int)(flp - flist), (int)(fep - flp->fents));
1834         add_to_flist((int)(flp - flist), id, parid);
1835     }
1836     if (v)
1837         RedPrintf("%d/%d: rename %s to %s %d\n", procid, opno, f.path,
1838                newf.path, e);
1839     free_pathname(&newf);
1840     free_pathname(&f);
1841 }
1842
1843 static void rmdir_f(int opno, long r)
1844 {
1845     int e;
1846     pathname_t f;
1847     fent_t *fep;
1848     int v;
1849
1850     init_pathname(&f);
1851     if (!get_fname(FT_DIRm, r, &f, NULL, &fep, &v)) {
1852         if (v)
1853             RedPrintf("%d/%d: rmdir - no directory\n", procid, opno);
1854         free_pathname(&f);
1855         return;
1856     }
1857     e = rmdir_path(&f) < 0 ? errno : 0;
1858     check_cwd();
1859     if (e == 0)
1860         del_from_flist(FT_DIR, (int)(fep - flist[FT_DIR].fents));
1861     if (v)
1862         RedPrintf("%d/%d: rmdir %s %d\n", procid, opno, f.path, e);
1863     free_pathname(&f);
1864 }
1865
1866 static void stat_f(int opno, long r)
1867 {
1868     int e;
1869     pathname_t f;
1870     REDSTAT stb;
1871     int v;
1872
1873     init_pathname(&f);
1874     if (!get_fname(FT_ANYm, r, &f, NULL, NULL, &v)) {
1875         if (v)
1876             RedPrintf("%d/%d: stat - no entries\n", procid, opno);
1877         free_pathname(&f);
1878         return;
1879     }
1880     e = lstat64_path(&f, &stb) < 0 ? errno : 0;
1881     check_cwd();
1882     if (v)
1883         RedPrintf("%d/%d: stat %s %d\n", procid, opno, f.path, e);
1884     free_pathname(&f);
1885 }
1886
1887 static void truncate_f(int opno, long r)
1888 {
1889     int e;
1890     pathname_t f;
1891     __int64_t lr;
1892     off64_t off;
1893     REDSTAT stb;
1894     int v;
1895
1896     init_pathname(&f);
1897     if (!get_fname(FT_REGFILE, r, &f, NULL, NULL, &v)) {
1898         if (v)
1899             RedPrintf("%d/%d: truncate - no filename\n", procid, opno);
1900         free_pathname(&f);
1901         return;
1902     }
1903     e = stat64_path(&f, &stb) < 0 ? errno : 0;
1904     check_cwd();
1905     if (e > 0) {
1906         if (v)
1907             RedPrintf("%d/%d: truncate - stat64 %s failed %d\n",
1908                    procid, opno, f.path, e);
1909         free_pathname(&f);
1910         return;
1911     }
1912     lr = ((__int64_t) random() << 32) + random();
1913     off = lr % MIN(stb.st_size + (1024 * 1024), MAXFSIZE);
1914     off %= maxfsize;
1915     e = truncate64_path(&f, off) < 0 ? errno : 0;
1916     check_cwd();
1917     if (v)
1918         RedPrintf("%d/%d: truncate %s %lld %d\n", procid, opno, f.path,
1919                (long long)off, e);
1920     free_pathname(&f);
1921 }
1922
1923 static void unlink_f(int opno, long r)
1924 {
1925     int e;
1926     pathname_t f;
1927     fent_t *fep;
1928     flist_t *flp;
1929     int v;
1930
1931     init_pathname(&f);
1932     if (!get_fname(FT_NOTDIR, r, &f, &flp, &fep, &v)) {
1933         if (v)
1934             RedPrintf("%d/%d: unlink - no file\n", procid, opno);
1935         free_pathname(&f);
1936         return;
1937     }
1938     e = unlink_path(&f) < 0 ? errno : 0;
1939     check_cwd();
1940     if (e == 0)
1941         del_from_flist((int)(flp - flist), (int)(fep - flp->fents));
1942     if (v)
1943         RedPrintf("%d/%d: unlink %s %d\n", procid, opno, f.path, e);
1944     free_pathname(&f);
1945 }
1946
1947 static void write_f(int opno, long r)
1948 {
1949     char *buf;
1950     int e;
1951     pathname_t f;
1952     int fd;
1953     uint32_t len;
1954     __int64_t lr;
1955     off64_t off;
1956     REDSTAT stb;
1957     int v;
1958
1959     init_pathname(&f);
1960     if (!get_fname(FT_REGm, r, &f, NULL, NULL, &v)) {
1961         if (v)
1962             RedPrintf("%d/%d: write - no filename\n", procid, opno);
1963         free_pathname(&f);
1964         return;
1965     }
1966     fd = open_path(&f, O_WRONLY);
1967     e = fd < 0 ? errno : 0;
1968     check_cwd();
1969     if (fd < 0) {
1970         if (v)
1971             RedPrintf("%d/%d: write - open %s failed %d\n",
1972                    procid, opno, f.path, e);
1973         free_pathname(&f);
1974         return;
1975     }
1976     if (fstat64(fd, &stb) < 0) {
1977         if (v)
1978             RedPrintf("%d/%d: write - fstat64 %s failed %d\n",
1979                    procid, opno, f.path, errno);
1980         free_pathname(&f);
1981         close(fd);
1982         return;
1983     }
1984     lr = ((__int64_t) random() << 32) + random();
1985     off = (off64_t) (lr % MIN(stb.st_size + (1024 * 1024), MAXFSIZE));
1986     off %= maxfsize;
1987     lseek64(fd, off, SEEK_SET);
1988     len = (random() % (getpagesize() * 4)) + 1;
1989     buf = malloc(len);
1990     memset(buf, nameseq & 0xff, len);
1991     e = write(fd, buf, len) < 0 ? errno : 0;
1992     free(buf);
1993     if (v)
1994         RedPrintf("%d/%d: write %s [%lld,%ld] %d\n",
1995                procid, opno, f.path, (long long)off, (long int)len, e);
1996     free_pathname(&f);
1997     close(fd);
1998 }
1999
2000
2001 #if REDCONF_CHECKER == 1
2002 static void check_f(int opno, long r)
2003 {
2004     int32_t ret;
2005     const char *pszVolume = gpRedVolConf->pszPathPrefix;
2006
2007     (void)r;
2008
2009     errno = 0;
2010
2011     ret = red_transact(pszVolume);
2012
2013     if(ret == 0)
2014     {
2015         ret = red_umount(pszVolume);
2016
2017         if(ret == 0)
2018         {
2019             int32_t ret2;
2020
2021             errno = -RedCoreVolCheck();
2022             if(errno != 0)
2023             {
2024                 ret = -1;
2025             }
2026
2027             ret2 = red_mount(pszVolume);
2028
2029             if(ret == 0)
2030             {
2031                 ret = ret2;
2032             }
2033
2034             if(ret2 != 0)
2035             {
2036                 exit(1);
2037             }
2038         }
2039     }
2040
2041     if (verbose)
2042     {
2043         RedPrintf("%d/%d: check %s %d\n", procid, opno, pszVolume, errno);
2044     }
2045 }
2046 #endif
2047
2048
2049 #endif /* FSSTRESS_SUPPORTED */
2050