2 * Copyright (c) 2000 Silicon Graphics, Inc. All Rights Reserved.
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.
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.
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.
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.
23 * Contact information: Silicon Graphics, Inc., 1600 Amphitheatre Pkwy,
24 * Mountain View, CA 94043, or:
28 * For further information regarding this notice, see:
30 * http://oss.sgi.com/projects/GenInfo/SGIGPLNoticeExplan/
33 @brief File system stress test.
35 This version of SGI fsstress has been modified to be single-threaded and to
36 work with the Reliance Edge POSIX-like API.
48 #if FSSTRESS_SUPPORTED
50 #include "redposixcompat.h"
52 #include <redosserv.h>
55 #include <redvolume.h>
56 #include <redgetopt.h>
57 #include <redtoolcmn.h>
59 #if REDCONF_CHECKER == 1
60 #include <redcoreapi.h>
64 /* Create POSIX types. Use #define to avoid name conflicts in those
65 environments where the type names already exist.
69 #define ino_t uint32_t
70 #define mode_t uint16_t
71 #define __int64_t int64_t
74 /** @brief Generate a random number.
76 @return A nonnegative random number.
78 #define random() ((int)(RedRand32(NULL) & 0x7FFFFFFF))
81 /** @brief Seed the random number generator.
83 #define srandom(seed) RedRandSeed(seed)
86 #define _exit(status) exit(status)
87 #define getpagesize() 4096U
91 /** @brief Determine the maximum file size.
93 This is used for the MAXFSSIZE macro.
95 static uint64_t MaxFileSize(void)
99 REDSTATUS errnoSave = errno;
100 uint64_t ullMaxFileSize;
102 iStatus = red_statvfs("", &info);
105 ullMaxFileSize = info.f_maxfsize;
109 /* This function does not change errno.
113 ullMaxFileSize = 0x7FFFFFFFU;
116 return ullMaxFileSize;
120 /*-------------------------------------------------------------------
121 Simulated current working directory support
122 -------------------------------------------------------------------*/
125 /* Forward declaration for red_chdir().
127 static int red_stat(const char *pszPath, REDSTAT *pStat);
129 /* The simulated CWD functions.
133 #define chdir(path) red_chdir(path)
134 #define getcwd(buf, size) red_getcwd(buf, size)
137 /* Redefine the path-based APIs to call MakeFullPath() on their arguments
138 since there is no CWD support in the red_*() APIs.
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))
155 #define FSSTRESS_BUF_SIZE 1024U
157 /* Stores the simulated current working directory.
159 static char szLocalCwd[FSSTRESS_BUF_SIZE] = "/";
162 /** @brief Change the current working directory.
164 This function only supports a subset of what is possible with POSIX chdir().
166 @param pszPath The new current working directory.
168 @return Upon successful completion, 0 shall be returned. Otherwise, -1
169 shall be returned, and errno shall be set to indicate the error.
171 static int red_chdir(
177 if(strcmp(pszPath, "..") == 0)
179 uint32_t ulLastSlashIdx = 0U;
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.
184 for(ulIdx = 0U; szLocalCwd[ulIdx] != '\0'; ulIdx++)
186 if(szLocalCwd[ulIdx] == '/')
188 ulLastSlashIdx = ulIdx;
192 if(ulLastSlashIdx != 0U)
194 szLocalCwd[ulLastSlashIdx] = '\0';
199 char szOldCwd[FSSTRESS_BUF_SIZE];
201 /* chdir() must have no effect on the CWD if it fails, so save the CWD
202 so we can revert it if necessary.
204 strcpy(szOldCwd, szLocalCwd);
206 if(pszPath[0U] == '/')
208 if(strlen(pszPath) >= sizeof(szLocalCwd))
210 iErrno = RED_ENAMETOOLONG;
214 strcpy(szLocalCwd, pszPath);
219 ulIdx = strlen(szLocalCwd);
221 if((ulIdx + 1U + strlen(pszPath)) >= sizeof(szLocalCwd))
223 iErrno = RED_ENAMETOOLONG;
227 if(szLocalCwd[1U] != '\0')
229 szLocalCwd[ulIdx] = '/';
233 strcpy(&szLocalCwd[ulIdx], pszPath);
242 iStatus = red_stat(szLocalCwd, &s);
247 else if(!S_ISDIR(s.st_mode))
249 iErrno = RED_ENOTDIR;
253 /* No error, new CWD checks out.
260 strcpy(szLocalCwd, szOldCwd);
269 return iErrno == 0 ? 0 : -1;
273 /** @brief Retrieve the current working directory.
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.
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.
285 static char *red_getcwd(
293 pszRet = malloc(strlen(szLocalCwd) + 1U);
300 strcpy(pszRet, szLocalCwd);
303 else if(nSize < strlen(szLocalCwd) + 1U)
310 strcpy(pszBuf, szLocalCwd);
318 /** @brief Make a relative path into a fully qualified path.
320 @param pszName The relative path.
322 @return On success, a pointer to a fully qualified path. On error, NULL.
324 static const char *MakeFullPath(
327 #define MAXVOLNAME 64U /* Enough for most configs. */
328 static char aszFullPath[2U][MAXVOLNAME + 1U + FSSTRESS_BUF_SIZE];
329 static uint32_t ulWhich = 0U;
331 char *pszFullPath = aszFullPath[ulWhich];
332 const char *pszVolume = gpRedVolConf->pszPathPrefix;
335 if(pszName[0U] == '/')
337 iLen = RedSNPrintf(pszFullPath, sizeof(aszFullPath[0U]), "%s%s", pszVolume, pszName);
339 else if(strcmp(pszName, ".") == 0U)
341 iLen = RedSNPrintf(pszFullPath, sizeof(aszFullPath[0U]), "%s%s", pszVolume, szLocalCwd);
343 else if((szLocalCwd[0U] == '/') && (szLocalCwd[1U] == '\0'))
345 iLen = RedSNPrintf(pszFullPath, sizeof(aszFullPath[0U]), "%s/%s", pszVolume, pszName);
349 iLen = RedSNPrintf(pszFullPath, sizeof(aszFullPath[0U]), "%s%s/%s", pszVolume, szLocalCwd, pszName);
354 /* Insufficient path buffer space.
360 /* Toggle between two full path arrays; a kluge to make rename() and
361 link() work correctly.
370 /*-------------------------------------------------------------------
371 POSIX functions not implemented by the RED POSIX-like API
372 -------------------------------------------------------------------*/
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)
382 /** @brief Get the status of a file or directory.
391 iFd = open(pszPath, O_RDONLY);
395 iRet = fstat(iFd, pStat);
404 /** @brief Truncate a file to a specified length.
406 static int red_truncate(
413 iFd = open(pszPath, O_WRONLY);
417 iRet = ftruncate(iFd, llSize);
426 /*-------------------------------------------------------------------
427 Begin ported fsstress code
428 -------------------------------------------------------------------*/
430 /* Stuff from xfscompat.h */
432 #define MAXNAMELEN (REDCONF_NAME_MAX+1U) /* Assumed to include NUL */
435 int d_miniosz, d_maxiosz, d_mem;
438 #define MIN(a,b) ((a)<(b) ? (a):(b))
439 #define MAX(a,b) ((a)>(b) ? (a):(b))
441 /* End xfscompat.h */
458 #if REDCONF_CHECKER == 1
464 typedef void (*opfnc_t) (int, long);
466 typedef struct opdesc {
474 typedef struct fent {
479 typedef struct flist {
486 typedef struct pathname {
492 #define FT_DIRm (1 << FT_DIR)
494 #define FT_REGm (1 << FT_REG)
496 #define FT_SYMm (1 << FT_SYM)
498 #define FT_DEVm (1 << FT_DEV)
500 #define FT_RTFm (1 << FT_RTF)
502 #define FT_ANYm ((1 << FT_nft) - 1)
503 #define FT_REGFILE (FT_REGm | FT_RTFm)
504 #define FT_NOTDIR (FT_ANYm & ~FT_DIRm)
506 #define FLIST_SLOT_INCR 16
509 #define MAXFSIZE MaxFileSize()
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);
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},
547 static flist_t flist[FT_nft] = {
555 static int dcache[NDCACHE];
556 static opty_t *freq_table;
557 static int freq_table_size;
558 static char *homedir;
561 static off64_t maxfsize;
565 static int operations = 1;
568 static unsigned long seed = 0;
569 static ino_t top_ino;
570 static int verbose = 0;
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);
605 /** @brief Parse parameters for fsstress.
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.
614 @return The result of parsing the parameters.
616 PARAMSTATUS FsstressParseParams(
619 FSSTRESSPARAM *pParam,
621 const char **ppszDevice)
625 const REDOPTION aLongopts[] =
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' },
638 /* If run without parameters, treat as a help request.
645 /* Assume no device argument to start with.
647 if(ppszDevice != NULL)
652 /* Set default parameters.
654 FsstressDefaultParams(pParam);
656 while((c = RedGetoptLong(argc, argv, "cl:n:rs:vD:H", aLongopts, NULL)) != -1)
660 case 'c': /* --no-cleanup */
661 pParam->fNoCleanup = true;
663 case 'l': /* --loops */
664 pParam->ulLoops = RedAtoI(red_optarg);
666 case 'n': /* --nops */
667 pParam->ulNops = RedAtoI(red_optarg);
669 case 'r': /* --namepad */
670 pParam->fNamePad = true;
672 case 's': /* --seed */
673 pParam->ulSeed = RedAtoI(red_optarg);
675 case 'v': /* --verbose */
676 pParam->fVerbose = true;
678 case 'D': /* --dev */
679 if(ppszDevice != NULL)
681 *ppszDevice = red_optarg;
684 case 'H': /* --help */
686 case '?': /* Unknown or ambiguous option */
687 case ':': /* Option missing required argument */
693 /* RedGetoptLong() has permuted argv to move all non-option arguments to
694 the end. We expect to find a volume identifier.
696 if(red_optind >= argc)
698 RedPrintf("Missing volume argument\n");
702 bVolNum = RedFindVolumeNumber(argv[red_optind]);
703 if(bVolNum == REDCONF_VOLUME_COUNT)
705 RedPrintf("Error: \"%s\" is not a valid volume identifier.\n", argv[red_optind]);
714 red_optind++; /* Move past volume parameter. */
715 if(red_optind < argc)
719 for(ii = red_optind; ii < argc; ii++)
721 RedPrintf("Error: Unexpected command-line argument \"%s\".\n", argv[ii]);
727 return PARAMSTATUS_OK;
731 RedPrintf("%s - invalid parameters\n", argv[0U]);
733 return PARAMSTATUS_BAD;
738 return PARAMSTATUS_HELP;
742 /** @brief Set default fsstress parameters.
744 @param pParam Populated with the default fsstress parameters.
746 void FsstressDefaultParams(
747 FSSTRESSPARAM *pParam)
749 RedMemSet(pParam, 0U, sizeof(*pParam));
750 pParam->ulLoops = 1U;
751 pParam->ulNops = 10000U;
755 /** @brief Start fsstress.
757 @param pParam fsstress parameters, either from FsstressParseParams() or
758 constructed programatically.
760 @return Zero on success, otherwise nonzero.
763 const FSSTRESSPARAM *pParam)
772 nops = sizeof(ops) / sizeof(ops[0]);
773 ops_end = &ops[nops];
775 /* Copy the already-parsed parameters into the traditional variables.
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;
786 while ((loopcntr <= loops) || (loops == 0)) {
787 RedSNPrintf(buf, sizeof(buf), "fss%x", getpid());
788 fd = creat(buf, 0666);
789 maxfsize = (off64_t) MAXFSIZE;
792 seed = (unsigned long)RedOsClockGetTime();
793 RedPrintf("seed = %ld\n", seed);
801 for (i = 0; i < FT_nft; i++) {
804 free(flist[i].fents);
805 flist[i].fents = NULL;
813 static int delete_tree(const char *path)
826 if (!S_ISDIR(sb.st_mode))
827 return unlink(path) ? errno : 0;
833 while((dep = readdir(dp)) != NULL) {
834 len = strlen(path) + 1 + strlen(dep->d_name) + 1;
835 childpath = malloc(len);
837 strcpy(childpath, path);
838 if (childpath[strlen(childpath) - 1] != '/')
839 strcat(childpath, "/");
840 strcat(childpath, dep->d_name);
842 e = delete_tree(childpath);
850 if (e == 0 && strcmp(path, "/") != 0) {
851 e = rmdir(path) ? errno : 0;
857 static void add_to_flist(int ft, int id, int parent)
863 if (ftp->nfiles == ftp->nslots) {
864 ftp->nslots += FLIST_SLOT_INCR;
865 ftp->fents = realloc(ftp->fents, ftp->nslots * sizeof(fent_t));
867 fep = &ftp->fents[ftp->nfiles++];
869 fep->parent = parent;
872 static void append_pathname(pathname_t *name, const char *str)
878 if (len && *str == '/' && name->len == 0) {
879 RedPrintf("fsstress: append_pathname failure\n");
885 name->path = realloc(name->path, name->len + 1 + len);
886 strcpy(&name->path[name->len], str);
890 static void check_cwd(void)
895 if (stat64(".", &statbuf) == 0 && statbuf.st_ino == top_ino)
898 RedPrintf("fsstress: check_cwd failure\n");
904 static int creat_path(pathname_t *name, mode_t mode)
906 char buf[MAXNAMELEN];
910 rval = creat(name->path, mode);
911 if (rval >= 0 || errno != RED_ENAMETOOLONG)
913 separate_pathname(name, buf, &newname);
914 if (chdir(buf) == 0) {
915 rval = creat_path(&newname, mode);
918 free_pathname(&newname);
922 static void dcache_enter(int dirid, int slot)
924 dcache[dirid % NDCACHE] = slot;
927 static void dcache_init(void)
931 for (i = 0; i < NDCACHE; i++)
935 static fent_t *dcache_lookup(int dirid)
940 i = dcache[dirid % NDCACHE];
941 if (i >= 0 && (fep = &flist[FT_DIR].fents[i])->id == dirid)
946 static void dcache_purge(int dirid)
950 dcp = &dcache[dirid % NDCACHE];
951 if (*dcp >= 0 && flist[FT_DIR].fents[*dcp].id == dirid)
955 static void del_from_flist(int ft, int slot)
961 dcache_purge(ftp->fents[slot].id);
962 if (slot != ftp->nfiles - 1) {
964 dcache_purge(ftp->fents[ftp->nfiles - 1].id);
965 ftp->fents[slot] = ftp->fents[--ftp->nfiles];
970 static fent_t *dirid_to_fent(int dirid)
976 if ((fep = dcache_lookup(dirid)))
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));
988 static void doproc(void)
995 RedSNPrintf(buf, sizeof(buf), "p%x", procid);
997 if (chdir(buf) < 0 || stat64(".", &statbuf) < 0) {
1001 top_ino = statbuf.st_ino;
1002 homedir = getcwd(NULL, 0);
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)
1012 p->func(opno, random());
1017 static void fent_to_name(pathname_t *name, flist_t *flp, fent_t *fep)
1019 char buf[MAXNAMELEN];
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, "/");
1030 i = RedSNPrintf(buf, sizeof(buf), "%c%x", flp->tag, fep->id);
1031 namerandpad(fep->id, buf, i);
1032 append_pathname(name, buf);
1035 static void fix_parent(int oldid, int newid)
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;
1050 static void free_pathname(pathname_t *name)
1059 static int generate_fname(fent_t *fep, int ft, pathname_t *name, int *idp, int *v)
1061 char buf[MAXNAMELEN];
1068 len = RedSNPrintf(buf, sizeof(buf), "%c%x", flp->tag, id = nameseq++);
1069 namerandpad(id, buf, len);
1071 fent_to_name(name, &flist[FT_DIR], fep);
1072 append_pathname(name, "/");
1074 append_pathname(name, buf);
1077 for (j = 0; !*v && j < ilistlen; j++) {
1078 if (ilist[j] == id) {
1087 get_fname(int which, long r, pathname_t *name, flist_t **flpp, fent_t **fepp, int *v)
1096 for (i = 0, c = 0, flp = flist; i < FT_nft; i++, flp++) {
1097 if (which & (1 << i))
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];
1114 fent_to_name(name, flp, fep);
1120 for (j = 0; !*v && j < ilistlen; j++) {
1121 if (ilist[j] == fep->id) {
1132 RedPrintf("fsstress: get_fname failure\n");
1139 static void init_pathname(pathname_t *name)
1145 static int link_path(pathname_t *name1, pathname_t *name2)
1147 char buf1[MAXNAMELEN];
1148 char buf2[MAXNAMELEN];
1150 pathname_t newname1;
1151 pathname_t newname2;
1154 rval = link(name1->path, name2->path);
1155 if (rval >= 0 || errno != RED_ENAMETOOLONG)
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);
1165 if (strcmp(buf1, "..") == 0)
1167 else if (strcmp(buf2, "..") == 0)
1169 else if (strlen(buf1) == 0)
1171 else if (strlen(buf2) == 0)
1174 down1 = MAX(newname1.len, 3 + name2->len) <=
1175 MAX(3 + name1->len, newname2.len);
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);
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);
1194 free_pathname(&newname1);
1195 free_pathname(&newname2);
1199 static int lstat64_path(pathname_t *name, REDSTAT *sbuf)
1201 char buf[MAXNAMELEN];
1205 rval = lstat64(name->path, sbuf);
1206 if (rval >= 0 || errno != RED_ENAMETOOLONG)
1208 separate_pathname(name, buf, &newname);
1209 if (chdir(buf) == 0) {
1210 rval = lstat64_path(&newname, sbuf);
1213 free_pathname(&newname);
1217 static void make_freq_table(void)
1223 for (p = ops, f = 0; p < ops_end; p++)
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;
1233 static int mkdir_path(pathname_t *name, mode_t mode)
1235 char buf[MAXNAMELEN];
1239 rval = mkdir(name->path);
1240 if (rval >= 0 || errno != RED_ENAMETOOLONG)
1242 separate_pathname(name, buf, &newname);
1243 if (chdir(buf) == 0) {
1244 rval = mkdir_path(&newname, mode);
1247 free_pathname(&newname);
1251 static void namerandpad(int id, char *buf, int len)
1254 static int buckets[8] = {0};
1255 static int bucket_count = 0;
1264 /* buckets[] used to be a statically initialized array with the following
1265 initializer: { 2, 4, 8, 16, 32, 64, 128, MAXNAMELEN - 1 }
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 }.
1272 if (!bucket_count) {
1273 bucket_count = sizeof(buckets) / sizeof(buckets[0]);
1275 for (i = 0; i < bucket_count; i++) {
1276 if (bucket_value > 128 || bucket_value >= (int)MAXNAMELEN - 1)
1278 buckets[i] = bucket_value;
1281 if (i < bucket_count) {
1282 buckets[i] = MAXNAMELEN - 1;
1288 bucket = (id ^ namerand) % bucket_count;
1289 padmod = buckets[bucket] + 1 - len;
1292 padlen = (id ^ namerand) % padmod;
1294 memset(&buf[len], 'X', padlen);
1295 buf[len + padlen] = '\0';
1299 static int open_path(pathname_t *name, int oflag)
1301 char buf[MAXNAMELEN];
1305 rval = open(name->path, oflag);
1306 if (rval >= 0 || errno != RED_ENAMETOOLONG)
1308 separate_pathname(name, buf, &newname);
1309 if (chdir(buf) == 0) {
1310 rval = open_path(&newname, oflag);
1313 free_pathname(&newname);
1317 static DIR *opendir_path(pathname_t *name)
1319 char buf[MAXNAMELEN];
1323 rval = opendir(name->path);
1324 if (rval || errno != RED_ENAMETOOLONG)
1326 separate_pathname(name, buf, &newname);
1327 if (chdir(buf) == 0) {
1328 rval = opendir_path(&newname);
1331 free_pathname(&newname);
1335 static int rename_path(pathname_t *name1, pathname_t *name2)
1337 char buf1[MAXNAMELEN];
1338 char buf2[MAXNAMELEN];
1340 pathname_t newname1;
1341 pathname_t newname2;
1344 rval = rename(name1->path, name2->path);
1345 if (rval >= 0 || errno != RED_ENAMETOOLONG)
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);
1355 if (strcmp(buf1, "..") == 0)
1357 else if (strcmp(buf2, "..") == 0)
1359 else if (strlen(buf1) == 0)
1361 else if (strlen(buf2) == 0)
1364 down1 = MAX(newname1.len, 3 + name2->len) <=
1365 MAX(3 + name1->len, newname2.len);
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);
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);
1384 free_pathname(&newname1);
1385 free_pathname(&newname2);
1389 static int rmdir_path(pathname_t *name)
1391 char buf[MAXNAMELEN];
1395 rval = rmdir(name->path);
1396 if (rval >= 0 || errno != RED_ENAMETOOLONG)
1398 separate_pathname(name, buf, &newname);
1399 if (chdir(buf) == 0) {
1400 rval = rmdir_path(&newname);
1403 free_pathname(&newname);
1407 static void separate_pathname(pathname_t *name, char *buf, pathname_t *newname)
1411 init_pathname(newname);
1412 slash = strchr(name->path, '/');
1413 if (slash == NULL) {
1418 strcpy(buf, name->path);
1420 append_pathname(newname, slash + 1);
1423 static int stat64_path(pathname_t *name, REDSTAT *sbuf)
1425 char buf[MAXNAMELEN];
1429 rval = stat64(name->path, sbuf);
1430 if (rval >= 0 || errno != RED_ENAMETOOLONG)
1432 separate_pathname(name, buf, &newname);
1433 if (chdir(buf) == 0) {
1434 rval = stat64_path(&newname, sbuf);
1437 free_pathname(&newname);
1441 static int truncate64_path(pathname_t *name, off64_t length)
1443 char buf[MAXNAMELEN];
1447 rval = truncate64(name->path, length);
1448 if (rval >= 0 || errno != RED_ENAMETOOLONG)
1450 separate_pathname(name, buf, &newname);
1451 if (chdir(buf) == 0) {
1452 rval = truncate64_path(&newname, length);
1455 free_pathname(&newname);
1459 static int unlink_path(pathname_t *name)
1461 char buf[MAXNAMELEN];
1465 rval = unlink(name->path);
1466 if (rval >= 0 || errno != RED_ENAMETOOLONG)
1468 separate_pathname(name, buf, &newname);
1469 if (chdir(buf) == 0) {
1470 rval = unlink_path(&newname);
1473 free_pathname(&newname);
1477 static void usage(const char *progname)
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");
1510 static void creat_f(int opno, long r)
1524 if (!get_fname(FT_DIRm, r, NULL, NULL, &fep, &v1))
1529 type = rtpct ? ((int)(random() % 100) > rtpct ? FT_REG : FT_RTF) : FT_REG;
1530 e = generate_fname(fep, type, &f, &id, &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);
1541 fd = creat_path(&f, 0666);
1542 e = fd < 0 ? errno : 0;
1547 add_to_flist(type, id, parid);
1551 RedPrintf("%d/%d: creat %s x:%d %d %d\n", procid, opno, f.path,
1556 static void fdatasync_f(int opno, long r)
1564 if (!get_fname(FT_REGFILE, r, &f, NULL, NULL, &v)) {
1566 RedPrintf("%d/%d: fdatasync - no filename\n",
1571 fd = open_path(&f, O_WRONLY);
1572 e = fd < 0 ? errno : 0;
1576 RedPrintf("%d/%d: fdatasync - open %s failed %d\n",
1577 procid, opno, f.path, e);
1581 e = fdatasync(fd) < 0 ? errno : 0;
1583 RedPrintf("%d/%d: fdatasync %s %d\n", procid, opno, f.path, e);
1588 static void fsync_f(int opno, long r)
1596 if (!get_fname(FT_REGFILE, r, &f, NULL, NULL, &v)) {
1598 RedPrintf("%d/%d: fsync - no filename\n", procid, opno);
1602 fd = open_path(&f, O_WRONLY);
1603 e = fd < 0 ? errno : 0;
1607 RedPrintf("%d/%d: fsync - open %s failed %d\n",
1608 procid, opno, f.path, e);
1612 e = fsync(fd) < 0 ? errno : 0;
1614 RedPrintf("%d/%d: fsync %s %d\n", procid, opno, f.path, e);
1619 static void getdents_f(int opno, long r)
1626 if (!get_fname(FT_DIRm, r, &f, NULL, NULL, &v))
1627 append_pathname(&f, ".");
1628 dir = opendir_path(&f);
1632 RedPrintf("%d/%d: getdents - can't open %s\n",
1633 procid, opno, f.path);
1637 while (readdir64(dir) != NULL)
1640 RedPrintf("%d/%d: getdents %s 0\n", procid, opno, f.path);
1645 static void link_f(int opno, long r)
1658 if (!get_fname(FT_NOTDIR, r, &f, &flp, NULL, &v1)) {
1660 RedPrintf("%d/%d: link - no file\n", procid, opno);
1664 if (!get_fname(FT_DIRm, random(), NULL, NULL, &fep, &v))
1670 e = generate_fname(fep, (int)(flp - flist), &l, &id, &v1);
1674 fent_to_name(&l, &flist[FT_DIR], fep);
1675 RedPrintf("%d/%d: link - no filename from %s\n",
1676 procid, opno, l.path);
1682 e = link_path(&f, &l) < 0 ? errno : 0;
1685 add_to_flist((int)(flp - flist), id, parid);
1687 RedPrintf("%d/%d: link %s %s %d\n", procid, opno, f.path, l.path,
1693 static void mkdir_f(int opno, long r)
1703 if (!get_fname(FT_DIRm, r, NULL, NULL, &fep, &v))
1708 e = generate_fname(fep, FT_DIR, &f, &id, &v1);
1712 fent_to_name(&f, &flist[FT_DIR], fep);
1713 RedPrintf("%d/%d: mkdir - no filename from %s\n",
1714 procid, opno, f.path);
1719 e = mkdir_path(&f, 0777) < 0 ? errno : 0;
1722 add_to_flist(FT_DIR, id, parid);
1724 RedPrintf("%d/%d: mkdir %s %d\n", procid, opno, f.path, e);
1728 static void read_f(int opno, long r)
1741 if (!get_fname(FT_REGFILE, r, &f, NULL, NULL, &v)) {
1743 RedPrintf("%d/%d: read - no filename\n", procid, opno);
1747 fd = open_path(&f, O_RDONLY);
1748 e = fd < 0 ? errno : 0;
1752 RedPrintf("%d/%d: read - open %s failed %d\n",
1753 procid, opno, f.path, e);
1757 if (fstat64(fd, &stb) < 0) {
1759 RedPrintf("%d/%d: read - fstat64 %s failed %d\n",
1760 procid, opno, f.path, errno);
1765 if (stb.st_size == 0) {
1767 RedPrintf("%d/%d: read - %s zero size\n", procid, opno,
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;
1778 e = read(fd, buf, len) < 0 ? errno : 0;
1781 RedPrintf("%d/%d: read %s [%lld,%ld] %d\n",
1782 procid, opno, f.path, (long long)off, (long int)len, e);
1787 static void rename_f(int opno, long r)
1802 if (!get_fname(FT_ANYm, r, &f, &flp, &fep, &v1)) {
1804 RedPrintf("%d/%d: rename - no filename\n", procid, opno);
1808 if (!get_fname(FT_DIRm, random(), NULL, NULL, &dfep, &v))
1813 init_pathname(&newf);
1814 e = generate_fname(dfep, (int)(flp - flist), &newf, &id, &v1);
1818 fent_to_name(&f, &flist[FT_DIR], dfep);
1819 RedPrintf("%d/%d: rename - no filename from %s\n",
1820 procid, opno, f.path);
1822 free_pathname(&newf);
1826 e = rename_path(&f, &newf) < 0 ? errno : 0;
1829 if (flp - flist == FT_DIR) {
1831 fix_parent(oldid, id);
1833 del_from_flist((int)(flp - flist), (int)(fep - flp->fents));
1834 add_to_flist((int)(flp - flist), id, parid);
1837 RedPrintf("%d/%d: rename %s to %s %d\n", procid, opno, f.path,
1839 free_pathname(&newf);
1843 static void rmdir_f(int opno, long r)
1851 if (!get_fname(FT_DIRm, r, &f, NULL, &fep, &v)) {
1853 RedPrintf("%d/%d: rmdir - no directory\n", procid, opno);
1857 e = rmdir_path(&f) < 0 ? errno : 0;
1860 del_from_flist(FT_DIR, (int)(fep - flist[FT_DIR].fents));
1862 RedPrintf("%d/%d: rmdir %s %d\n", procid, opno, f.path, e);
1866 static void stat_f(int opno, long r)
1874 if (!get_fname(FT_ANYm, r, &f, NULL, NULL, &v)) {
1876 RedPrintf("%d/%d: stat - no entries\n", procid, opno);
1880 e = lstat64_path(&f, &stb) < 0 ? errno : 0;
1883 RedPrintf("%d/%d: stat %s %d\n", procid, opno, f.path, e);
1887 static void truncate_f(int opno, long r)
1897 if (!get_fname(FT_REGFILE, r, &f, NULL, NULL, &v)) {
1899 RedPrintf("%d/%d: truncate - no filename\n", procid, opno);
1903 e = stat64_path(&f, &stb) < 0 ? errno : 0;
1907 RedPrintf("%d/%d: truncate - stat64 %s failed %d\n",
1908 procid, opno, f.path, e);
1912 lr = ((__int64_t) random() << 32) + random();
1913 off = lr % MIN(stb.st_size + (1024 * 1024), MAXFSIZE);
1915 e = truncate64_path(&f, off) < 0 ? errno : 0;
1918 RedPrintf("%d/%d: truncate %s %lld %d\n", procid, opno, f.path,
1923 static void unlink_f(int opno, long r)
1932 if (!get_fname(FT_NOTDIR, r, &f, &flp, &fep, &v)) {
1934 RedPrintf("%d/%d: unlink - no file\n", procid, opno);
1938 e = unlink_path(&f) < 0 ? errno : 0;
1941 del_from_flist((int)(flp - flist), (int)(fep - flp->fents));
1943 RedPrintf("%d/%d: unlink %s %d\n", procid, opno, f.path, e);
1947 static void write_f(int opno, long r)
1960 if (!get_fname(FT_REGm, r, &f, NULL, NULL, &v)) {
1962 RedPrintf("%d/%d: write - no filename\n", procid, opno);
1966 fd = open_path(&f, O_WRONLY);
1967 e = fd < 0 ? errno : 0;
1971 RedPrintf("%d/%d: write - open %s failed %d\n",
1972 procid, opno, f.path, e);
1976 if (fstat64(fd, &stb) < 0) {
1978 RedPrintf("%d/%d: write - fstat64 %s failed %d\n",
1979 procid, opno, f.path, errno);
1984 lr = ((__int64_t) random() << 32) + random();
1985 off = (off64_t) (lr % MIN(stb.st_size + (1024 * 1024), MAXFSIZE));
1987 lseek64(fd, off, SEEK_SET);
1988 len = (random() % (getpagesize() * 4)) + 1;
1990 memset(buf, nameseq & 0xff, len);
1991 e = write(fd, buf, len) < 0 ? errno : 0;
1994 RedPrintf("%d/%d: write %s [%lld,%ld] %d\n",
1995 procid, opno, f.path, (long long)off, (long int)len, e);
2001 #if REDCONF_CHECKER == 1
2002 static void check_f(int opno, long r)
2005 const char *pszVolume = gpRedVolConf->pszPathPrefix;
2011 ret = red_transact(pszVolume);
2015 ret = red_umount(pszVolume);
2021 errno = -RedCoreVolCheck();
2027 ret2 = red_mount(pszVolume);
2043 RedPrintf("%d/%d: check %s %d\n", procid, opno, pszVolume, errno);
2049 #endif /* FSSTRESS_SUPPORTED */