123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461 |
- /*++
- Copyright (c) 2013 Minoca Corp.
- This file is licensed under the terms of the GNU General Public License
- version 3. Alternative licensing terms are available. Contact
- info@minocacorp.com for details. See the LICENSE file at the root of this
- project for complete licensing information.
- Module Name:
- mnttest.c
- Abstract:
- This module implements the tests used to verify that basic mount operations
- are working.
- Author:
- Chris Stevens 25-Nov-2013
- Environment:
- User Mode
- --*/
- //
- // ------------------------------------------------------------------- Includes
- //
- #include <minoca/lib/types.h>
- #include <assert.h>
- #include <errno.h>
- #include <fcntl.h>
- #include <getopt.h>
- #include <stdio.h>
- #include <stdlib.h>
- #include <string.h>
- #include <sys/stat.h>
- #include <sys/wait.h>
- #include <sys/time.h>
- #include <time.h>
- #include <unistd.h>
- #include <math.h>
- //
- // --------------------------------------------------------------------- Macros
- //
- #define DEBUG_PRINT(...) \
- if (MountTestVerbosity >= TestVerbosityDebug) { \
- printf(__VA_ARGS__); \
- }
- #define PRINT(...) \
- if (MountTestVerbosity >= TestVerbosityNormal) { \
- printf(__VA_ARGS__); \
- }
- #define PRINT_ERROR(...) fprintf(stderr, "\nmnttest: " __VA_ARGS__)
- //
- // ---------------------------------------------------------------- Definitions
- //
- #define MOUNT_TEST_VERSION_MAJOR 1
- #define MOUNT_TEST_VERSION_MINOR 0
- #define MOUNT_TEST_USAGE \
- "Usage: mnttest [options] \n" \
- "This utility tests the mount and umount programs. Options are:\n" \
- " -c, --mount-count <count> -- Set the number of mounts to create.\n" \
- " -i, --iterations <count> -- Set the number of operations to perform.\n" \
- " -p, --threads <count> -- Set the number of threads to spin up.\n" \
- " -t, --test -- Set the test to perform. Valid values are all, \n" \
- " file, directory, and concurrency.\n" \
- " --debug -- Print lots of information about what's happening.\n" \
- " --quiet -- Print only errors.\n" \
- " --no-cleanup -- Leave test mount points and files around for \n" \
- " debugging.\n" \
- " --help -- Print this help text and exit.\n" \
- " --version -- Print the test version and exit.\n" \
- #define MOUNT_TEST_OPTIONS_STRING "c:i:t:p:ndqhV"
- #define MOUNT_TEST_CREATE_PERMISSIONS (S_IRUSR | S_IWUSR)
- #define DEFAULT_MOUNT_COUNT 20
- #define DEFAULT_OPERATION_COUNT (DEFAULT_MOUNT_COUNT * 1)
- #define DEFAULT_THREAD_COUNT 1
- #define MOUNT_TEST_LOG "mnttest.log"
- #define MOUNT_TEST_MOUNT_FORMAT "mount --bind %s %s 2>> %s"
- #define MOUNT_TEST_MOUNT_RECURSIVE_FORMAT "mount --rbind %s %s 2>> %s"
- #define MOUNT_TEST_UNMOUNT_FORMAT "umount %s 2>> %s"
- #define MOUNT_TEST_UNMOUNT_LAZY_FORMAT "umount -l %s 2>> %s"
- //
- // ------------------------------------------------------ Data Type Definitions
- //
- typedef enum _MOUNT_TEST_ACTION {
- MountTestActionMount,
- MountTestActionUnmount,
- MountTestActionStat,
- MountTestActionDelete,
- MountTestActionCount
- } MOUNT_TEST_ACTION, *PMOUNT_TEST_ACTION;
- typedef enum _TEST_VERBOSITY {
- TestVerbosityQuiet,
- TestVerbosityNormal,
- TestVerbosityDebug
- } TEST_VERBOSITY, *PTEST_VERBOSITY;
- typedef enum _MOUNT_TEST_TYPE {
- MountTestAll,
- MountTestFile,
- MountTestDirectory,
- MountTestConcurrency,
- MountTestTypeCount
- } MOUNT_TEST_TYPE, *PMOUNT_TEST_TYPE;
- //
- // ----------------------------------------------- Internal Function Prototypes
- //
- ULONG
- RunMountFileTest (
- INT FileCount,
- INT Iterations
- );
- ULONG
- RunMountDirectoryTest (
- INT DirectoryCount,
- INT Iterations
- );
- ULONG
- RunMountConcurrencyTest (
- INT MountCount,
- INT Iterations
- );
- ULONG
- RunMountGenericTest (
- MOUNT_TEST_TYPE TestType,
- INT MountCount,
- INT Iterations
- );
- INT
- MountTestCreateFile (
- PSTR FileName,
- BOOL FileIsDirectory
- );
- INT
- MountTestDeleteFile (
- PSTR FileName,
- BOOL FileIsDirectory
- );
- PSTR
- AppendPaths (
- PSTR Path1,
- PSTR Path2
- );
- //
- // -------------------------------------------------------------------- Globals
- //
- //
- // Higher levels here print out more stuff.
- //
- TEST_VERBOSITY MountTestVerbosity = TestVerbosityNormal;
- //
- // Set this boolean to skip cleaning up files.
- //
- BOOL MountTestNoCleanup = FALSE;
- struct option MountTestLongOptions[] = {
- {"mount-count", required_argument, 0, 'c'},
- {"iterations", required_argument, 0, 'i'},
- {"threads", required_argument, 0, 'p'},
- {"test", required_argument, 0, 't'},
- {"no-cleanup", no_argument, 0, 'n'},
- {"debug", no_argument, 0, 'd'},
- {"quiet", no_argument, 0, 'q'},
- {"help", no_argument, 0, 'h'},
- {"version", no_argument, 0, 'V'},
- {NULL, 0, 0, 0},
- };
- PSTR MountTestName[MountTestTypeCount] = {
- NULL,
- "file",
- "directory",
- "concurrency"
- };
- PSTR MountTestFileNameFormat[MountTestTypeCount] = {
- NULL,
- "mft%x-%06x",
- "mdt%x-%06x",
- "mct-%06x"
- };
- PSTR MountTestProgressCharacter[MountTestTypeCount] = {
- NULL,
- "f",
- "d",
- "c"
- };
- //
- // ------------------------------------------------------------------ Functions
- //
- int
- main (
- int ArgumentCount,
- char **Arguments
- )
- /*++
- Routine Description:
- This routine implements the file test program.
- Arguments:
- ArgumentCount - Supplies the number of elements in the arguments array.
- Arguments - Supplies an array of strings. The array count is bounded by the
- previous parameter, and the strings are null-terminated.
- Return Value:
- 0 on success.
- Non-zero on failure.
- --*/
- {
- PSTR AfterScan;
- pid_t Child;
- INT ChildIndex;
- pid_t *Children;
- INT Failures;
- BOOL IsParent;
- INT Iterations;
- INT MountCount;
- INT Option;
- INT Status;
- MOUNT_TEST_TYPE Test;
- INT Threads;
- Children = NULL;
- Failures = 0;
- MountCount = DEFAULT_MOUNT_COUNT;
- Iterations = DEFAULT_OPERATION_COUNT;
- Test = MountTestAll;
- Threads = DEFAULT_THREAD_COUNT;
- Status = 0;
- setvbuf(stdout, NULL, _IONBF, 0);
- setvbuf(stderr, NULL, _IONBF, 0);
- srand(time(NULL));
- //
- // Process the control arguments.
- //
- while (TRUE) {
- Option = getopt_long(ArgumentCount,
- Arguments,
- MOUNT_TEST_OPTIONS_STRING,
- MountTestLongOptions,
- NULL);
- if (Option == -1) {
- break;
- }
- if ((Option == '?') || (Option == ':')) {
- Status = 1;
- goto MainEnd;
- }
- switch (Option) {
- case 'c':
- MountCount = strtol(optarg, &AfterScan, 0);
- if ((MountCount <= 0) || (AfterScan == optarg)) {
- PRINT_ERROR("Invalid file count %s.\n", optarg);
- Status = 1;
- goto MainEnd;
- }
- break;
- case 'i':
- Iterations = strtol(optarg, &AfterScan, 0);
- if ((Iterations < 0) || (AfterScan == optarg)) {
- PRINT_ERROR("Invalid iteration count %s.\n", optarg);
- Status = 1;
- goto MainEnd;
- }
- break;
- case 'n':
- MountTestNoCleanup = TRUE;
- break;
- case 'p':
- Threads = strtol(optarg, &AfterScan, 0);
- if ((Threads <= 0) || (AfterScan == optarg)) {
- PRINT_ERROR("Invalid thread count %s.\n", optarg);
- Status = 1;
- goto MainEnd;
- }
- break;
- case 't':
- if (strcasecmp(optarg, "all") == 0) {
- Test = MountTestAll;
- } else if (strcasecmp(optarg, "file") == 0) {
- Test = MountTestFile;
- } else if (strcasecmp(optarg, "directory") == 0) {
- Test = MountTestDirectory;
- } else if (strcasecmp(optarg, "concurrency") == 0) {
- Test = MountTestConcurrency;
- } else {
- PRINT_ERROR("Invalid test: %s.\n", optarg);
- Status = 1;
- goto MainEnd;
- }
- break;
- case 'd':
- MountTestVerbosity = TestVerbosityDebug;
- break;
- case 'q':
- MountTestVerbosity = TestVerbosityQuiet;
- break;
- case 'V':
- printf("Minoca mnttest version %d.%d\n",
- MOUNT_TEST_VERSION_MAJOR,
- MOUNT_TEST_VERSION_MINOR);
- return 1;
- case 'h':
- printf(MOUNT_TEST_USAGE);
- return 1;
- default:
- assert(FALSE);
- Status = 1;
- goto MainEnd;
- }
- }
- //
- // Destroy the mount test log file.
- //
- unlink(MOUNT_TEST_LOG);
- //
- // Fork off any children test processes.
- //
- IsParent = TRUE;
- if (Threads > 1) {
- Children = malloc(sizeof(pid_t) * (Threads - 1));
- if (Children == NULL) {
- Status = ENOMEM;
- goto MainEnd;
- }
- memset(Children, 0, sizeof(pid_t) * (Threads - 1));
- for (ChildIndex = 0; ChildIndex < Threads - 1; ChildIndex += 1) {
- Child = fork();
- //
- // If this is the child, break out and run the tests.
- //
- if (Child == 0) {
- srand(time(NULL) + ChildIndex);
- IsParent = FALSE;
- break;
- }
- Children[ChildIndex] = Child;
- }
- }
- //
- // Run the tests.
- //
- if ((Test == MountTestAll) || (Test == MountTestFile)) {
- Failures += RunMountFileTest(MountCount, Iterations);
- }
- if ((Test == MountTestAll) || (Test == MountTestDirectory)) {
- Failures += RunMountDirectoryTest(MountCount, Iterations);
- }
- if ((Test == MountTestAll) || (Test == MountTestConcurrency)) {
- Failures += RunMountConcurrencyTest(MountCount, Iterations);
- }
- //
- // Wait for any children.
- //
- if (IsParent != FALSE) {
- if (Threads > 1) {
- for (ChildIndex = 0; ChildIndex < Threads - 1; ChildIndex += 1) {
- Child = waitpid(Children[ChildIndex], &Status, 0);
- if (Child == -1) {
- PRINT_ERROR("Failed to wait for child %d: %s.\n",
- Children[ChildIndex],
- strerror(errno));
- Status = errno;
- } else {
- assert(Child == Children[ChildIndex]);
- if (!WIFEXITED(Status)) {
- PRINT_ERROR("Child %d returned with status %x\n",
- Status);
- Failures += 1;
- }
- Failures += WEXITSTATUS(Status);
- Status = 0;
- }
- }
- }
- //
- // If this is a child, just report back the number of failures to the
- // parent.
- //
- } else {
- if (Failures > 100) {
- exit(100);
- } else {
- exit(Failures);
- }
- }
- MainEnd:
- if (Children != NULL) {
- free(Children);
- }
- if (Status != 0) {
- PRINT_ERROR("Error: %d.\n", Status);
- }
- if (Failures != 0) {
- PRINT_ERROR("\n *** %d failures in mnttest ***\n", Failures);
- return Failures;
- }
- return 0;
- }
- //
- // --------------------------------------------------------- Internal Functions
- //
- ULONG
- RunMountFileTest (
- INT FileCount,
- INT Iterations
- )
- /*++
- Routine Description:
- This routine executes the mount file test.
- Arguments:
- FileCount - Supplies the number of files to work with.
- Iterations - Supplies the number of iterations to perform.
- Return Value:
- Returns the number of failures in the test suite.
- --*/
- {
- return RunMountGenericTest(MountTestFile, FileCount, Iterations);
- }
- ULONG
- RunMountDirectoryTest (
- INT DirectoryCount,
- INT Iterations
- )
- /*++
- Routine Description:
- This routine executes the mount directory test.
- Arguments:
- DirectoryCount - Supplies the number of directories to work with.
- Iterations - Supplies the number of iterations to perform.
- Return Value:
- Returns the number of failures in the test suite.
- --*/
- {
- return RunMountGenericTest(MountTestDirectory, DirectoryCount, Iterations);
- }
- ULONG
- RunMountConcurrencyTest (
- INT MountCount,
- INT Iterations
- )
- /*++
- Routine Description:
- This routine tests creating and removing mount points concurrently.
- Arguments:
- MountCount - Supplies the number of mount points to work with.
- Iterations - Supplies the number of iterations to perform.
- Return Value:
- Returns the number of failures in the test suite.
- --*/
- {
- MOUNT_TEST_ACTION Action;
- ULONG Failures;
- INT File;
- INT FileIndex;
- CHAR FileName[16];
- struct stat FileStat;
- ino_t Inode;
- INT Iteration;
- CHAR MountCommand[64];
- INT OpenFlags;
- INT Percent;
- pid_t Process;
- INT Result;
- INT TargetIndex;
- CHAR TargetName[16];
- Failures = 0;
- //
- // Announce the test.
- //
- Process = getpid();
- PRINT("Process %d Running mount %s test with %d files, %d iterations.\n",
- Process,
- MountTestName[MountTestConcurrency],
- MountCount,
- Iterations);
- Percent = Iterations / 100;
- if (Percent == 0) {
- Percent = 1;
- }
- //
- // Perform the mount operations. This test creates files and then mounts
- // them, unmounts them, or checks their file ID. The files are shared
- // across all threads running the test.
- //
- for (Iteration = 0; Iteration < Iterations; Iteration += 1) {
- //
- // Pick a random file and a random action.
- //
- FileIndex = rand() % MountCount;
- snprintf(FileName,
- sizeof(FileName),
- MountTestFileNameFormat[MountTestConcurrency],
- FileIndex);
- Action = rand() % MountTestActionCount;
- switch (Action) {
- case MountTestActionStat:
- OpenFlags = O_RDWR | O_CREAT;
- File = open(FileName, OpenFlags, MOUNT_TEST_CREATE_PERMISSIONS);
- if (File < 0) {
- PRINT_ERROR("Failed to open file %s (flags %x): %s.\n",
- FileName,
- OpenFlags,
- strerror(errno));
- Failures += 1;
- continue;
- }
- Result = fstat(File, &FileStat);
- if (Result < 0) {
- PRINT_ERROR("Failed to stat file %s: %s.\n",
- FileName,
- strerror(errno));
- Failures += 1;
- continue;
- }
- if (FileStat.st_size == 0) {
- Inode = FileStat.st_ino;
- do {
- Result = write(File, &Inode, sizeof(ino_t));
- } while ((Result < 0) && (errno == EINTR));
- if (Result != sizeof(ino_t)) {
- PRINT_ERROR("Write failed. Wrote %d of %d bytes to file "
- "%s: %s.\n",
- Result,
- sizeof(ino_t),
- FileName,
- strerror(errno));
- Failures += 1;
- }
- } else {
- Inode = 0;
- do {
- Result = read(File, &Inode, sizeof(ino_t));
- } while ((Result < 0) && (errno == EINTR));
- if (Result != sizeof(ino_t)) {
- PRINT_ERROR("Read failed. Read %d of %d bytes from file "
- "%s: %s\n.",
- Result,
- sizeof(ino_t),
- FileName,
- strerror(errno));
- Failures += 1;
- }
- if (Inode != FileStat.st_ino) {
- PRINT_ERROR("Mismatching inode for file %s. Read %d, "
- "expected %d.\n",
- FileName,
- Inode,
- FileStat.st_ino);
- Failures += 1;
- }
- }
- Result = close(File);
- if (Result != 0) {
- PRINT_ERROR("Failed to close: %s.\n", strerror(errno));
- Failures += 1;
- continue;
- }
- break;
- case MountTestActionMount:
- //
- // Pick another random file to mount on.
- //
- TargetIndex = rand() % MountCount;
- snprintf(TargetName,
- sizeof(TargetName),
- MountTestFileNameFormat[MountTestConcurrency],
- TargetIndex);
- //
- // Create both files to give the mount some chance of working.
- //
- Result = MountTestCreateFile(FileName, FALSE);
- if (Result < 0) {
- Failures += 1;
- continue;
- }
- Result = MountTestCreateFile(TargetName, FALSE);
- if (Result < 0) {
- Failures += 1;
- continue;
- }
- DEBUG_PRINT("Mount file %s onto file %s\n", FileName, TargetName);
- snprintf(MountCommand,
- sizeof(MountCommand),
- MOUNT_TEST_MOUNT_FORMAT,
- FileName,
- TargetName,
- MOUNT_TEST_LOG);
- Result = system(MountCommand);
- if (WIFSIGNALED(Result) &&
- ((WTERMSIG(Result) == SIGINT) ||
- (WTERMSIG(Result) == SIGQUIT))) {
- goto RunMountConcurrencyTestEnd;
- }
- if ((Result != 0) && (Result != ENOFILE)) {
- PRINT_ERROR("Failed to mount %s onto %s: %s.\n",
- FileName,
- TargetName,
- strerror(errno));
- Failures += 1;
- continue;
- }
- break;
- case MountTestActionUnmount:
- DEBUG_PRINT("Unmounting file %s\n", FileName);
- snprintf(MountCommand,
- sizeof(MountCommand),
- MOUNT_TEST_UNMOUNT_LAZY_FORMAT,
- FileName,
- MOUNT_TEST_LOG);
- Result = system(MountCommand);
- if (WIFSIGNALED(Result) &&
- ((WTERMSIG(Result) == SIGINT) ||
- (WTERMSIG(Result) == SIGQUIT))) {
- goto RunMountConcurrencyTestEnd;
- }
- if ((Result != 0) && (Result != EINVAL) && (Result != ENOFILE)) {
- PRINT_ERROR("Failed to unmount %s: %d.\n",
- FileName,
- Result);
- Failures += 1;
- continue;
- }
- break;
- case MountTestActionDelete:
- DEBUG_PRINT("Deleting file %s\n", FileName);
- Result = MountTestDeleteFile(FileName, TRUE);
- if ((Result != 0) && (errno != EBUSY) && (errno != ENOFILE)) {
- PRINT_ERROR("Failed to delete %s: %s.\n",
- FileName,
- strerror(errno));
- Failures += 1;
- continue;
- }
- break;
- default:
- assert(FALSE);
- break;
- }
- if ((Iteration % Percent) == 0) {
- PRINT("%s", MountTestProgressCharacter[MountTestConcurrency]);
- }
- }
- //
- // Clean up all files.
- //
- if (MountTestNoCleanup == FALSE) {
- for (FileIndex = 0; FileIndex < MountCount; FileIndex += 1) {
- snprintf(FileName,
- sizeof(FileName),
- MountTestFileNameFormat[MountTestConcurrency],
- FileIndex);
- snprintf(MountCommand,
- sizeof(MountCommand),
- MOUNT_TEST_UNMOUNT_LAZY_FORMAT,
- FileName,
- MOUNT_TEST_LOG);
- while (TRUE) {
- Result = system(MountCommand);
- if (WIFSIGNALED(Result) &&
- ((WTERMSIG(Result) == SIGINT) ||
- (WTERMSIG(Result) == SIGQUIT))) {
- goto RunMountConcurrencyTestEnd;
- }
- if (Result != 0) {
- if ((Result != EINVAL) && (Result != ENOFILE)) {
- PRINT_ERROR("Failed to unmount %s: %s.\n",
- FileName,
- Result,
- strerror(errno));
- Failures += 1;
- }
- break;
- }
- }
- Result = MountTestDeleteFile(FileName, FALSE);
- if ((Result != 0) && (errno != ENOFILE)) {
- PRINT_ERROR("Failed to delete %s: %s.\n",
- FileName,
- strerror(errno));
- Failures += 1;
- }
- }
- }
- PRINT("\n");
- RunMountConcurrencyTestEnd:
- return Failures;
- }
- ULONG
- RunMountGenericTest (
- MOUNT_TEST_TYPE TestType,
- INT MountCount,
- INT Iterations
- )
- /*++
- Routine Description:
- This routine executes a generic non-concurrent mount test.
- Arguments:
- TestType - Supplies the type of generic test being executed.
- MountCount - Supplies the number of mountable items to work with.
- Iterations - Supplies the number of iterations to perform.
- Return Value:
- Returns the number of failures in the test suite.
- --*/
- {
- MOUNT_TEST_ACTION Action;
- ULONG Failures;
- PINT FileId;
- INT FileIndex;
- BOOL FileIsDirectory;
- CHAR FileName[16];
- struct stat FileStat;
- INT Iteration;
- INT MaxSimultaneousMounts;
- CHAR MountCommand[64];
- PINT MountCounts;
- INT Percent;
- pid_t Process;
- INT Result;
- INT SimultaneousMounts;
- INT TargetIndex;
- CHAR TargetName[16];
- Failures = 0;
- FileId = NULL;
- MountCounts = NULL;
- //
- // Announce the test.
- //
- Process = getpid();
- PRINT("Process %d Running mount %s test with %d mount points, "
- "%d iterations.\n",
- Process,
- MountTestName[TestType],
- MountCount,
- Iterations);
- Percent = Iterations / 100;
- if (Percent == 0) {
- Percent = 1;
- }
- if (TestType == MountTestFile) {
- FileIsDirectory = FALSE;
- } else {
- assert(TestType == MountTestDirectory);
- FileIsDirectory = TRUE;
- }
- MaxSimultaneousMounts = 0;
- SimultaneousMounts = 0;
- FileId = malloc(MountCount * sizeof(INT));
- if (FileId == NULL) {
- Failures += 1;
- goto RunMountGenericTestEnd;
- }
- for (FileIndex = 0; FileIndex < MountCount; FileIndex += 1) {
- FileId[FileIndex] = -1;
- }
- MountCounts = malloc(MountCount * sizeof(INT));
- if (MountCounts == NULL) {
- Failures += 1;
- goto RunMountGenericTestEnd;
- }
- memset(MountCounts, 0, MountCount * sizeof(INT));
- //
- // Perform the mount operations. This test creates files and then mounts
- // them, unmounts them, or checks their file ID.
- //
- for (Iteration = 0; Iteration < Iterations; Iteration += 1) {
- //
- // Pick a random file and a random action.
- //
- FileIndex = rand() % MountCount;
- snprintf(FileName,
- sizeof(FileName),
- MountTestFileNameFormat[TestType],
- Process & 0xFFFF,
- FileIndex);
- Action = rand() % MountTestActionCount;
- //
- // If the file has yet to be created, then the action must be a stat.
- //
- if (FileId[FileIndex] == -1) {
- Action = MountTestActionStat;
- //
- // If the file has yet to be mounted, do not unmount it.
- //
- } else if ((Action == MountTestActionUnmount) &&
- (MountCounts[FileIndex] == 0)) {
- Action = MountTestActionMount;
- }
- switch (Action) {
- case MountTestActionStat:
- if (FileId[FileIndex] == -1) {
- Result = MountTestCreateFile(FileName, FileIsDirectory);
- if (Result < 0) {
- Failures += 1;
- continue;
- }
- }
- Result = stat(FileName, &FileStat);
- if (Result < 0) {
- PRINT_ERROR("Failed to stat file %s: %s.\n",
- FileName,
- strerror(errno));
- Failures += 1;
- continue;
- }
- if (FileId[FileIndex] == -1) {
- FileId[FileIndex] = FileStat.st_ino;
- DEBUG_PRINT("Set file %s ID to %d.\n",
- FileName,
- FileStat.st_ino);
- } else if (FileId[FileIndex] != FileStat.st_ino) {
- PRINT_ERROR("Failed to match file ID for file %s. Expected %d "
- "but read %d.\n",
- FileName,
- FileId[FileIndex],
- FileStat.st_ino);
- Failures += 1;
- continue;
- } else {
- DEBUG_PRINT("File %s ID is %d, as expected.\n",
- FileName,
- FileStat.st_ino);
- }
- break;
- case MountTestActionMount:
- //
- // Pick another random file to mount on.
- //
- TargetIndex = rand() % MountCount;
- snprintf(TargetName,
- sizeof(TargetName),
- MountTestFileNameFormat[TestType],
- Process & 0xFFFF,
- TargetIndex);
- if (FileId[TargetIndex] == -1) {
- Result = MountTestCreateFile(TargetName, FileIsDirectory);
- if (Result < 0) {
- Failures += 1;
- continue;
- }
- Result = stat(TargetName, &FileStat);
- if (Result < 0) {
- PRINT_ERROR("Failed to stat file %s: %s.\n",
- TargetName,
- strerror(errno));
- Failures += 1;
- continue;
- }
- FileId[TargetIndex] = FileStat.st_ino;
- }
- DEBUG_PRINT("Mount file %s onto file %s\n", FileName, TargetName);
- snprintf(MountCommand,
- sizeof(MountCommand),
- MOUNT_TEST_MOUNT_FORMAT,
- FileName,
- TargetName,
- MOUNT_TEST_LOG);
- Result = system(MountCommand);
- if (WIFSIGNALED(Result) &&
- ((WTERMSIG(Result) == SIGINT) ||
- (WTERMSIG(Result) == SIGQUIT))) {
- goto RunMountGenericTestEnd;
- }
- if (Result != 0) {
- PRINT_ERROR("Failed to mount %s onto %s: %s.\n",
- FileName,
- TargetName,
- strerror(errno));
- Failures += 1;
- continue;
- }
- SimultaneousMounts += 1;
- if (SimultaneousMounts > MaxSimultaneousMounts) {
- MaxSimultaneousMounts = SimultaneousMounts;
- }
- //
- // Update the targets mount count and file ID information.
- //
- MountCounts[TargetIndex] += 1;
- FileId[TargetIndex] = FileId[FileIndex];
- DEBUG_PRINT("Set file %s ID to %d.\n",
- TargetName,
- FileId[TargetIndex]);
- break;
- case MountTestActionUnmount:
- DEBUG_PRINT("Unmounting file %s\n", FileName);
- snprintf(MountCommand,
- sizeof(MountCommand),
- MOUNT_TEST_UNMOUNT_FORMAT,
- FileName,
- MOUNT_TEST_LOG);
- Result = system(MountCommand);
- if (WIFSIGNALED(Result) &&
- ((WTERMSIG(Result) == SIGINT) ||
- (WTERMSIG(Result) == SIGQUIT))) {
- goto RunMountGenericTestEnd;
- }
- if (Result != 0) {
- if (MountCounts[FileIndex] != 0) {
- PRINT_ERROR("Failed to unmount %s: %s.\n",
- FileName,
- strerror(errno));
- Failures += 1;
- }
- continue;
- }
- //
- // Recollect the stat information now that a file has been
- // unmounted at this path.
- //
- Result = stat(FileName, &FileStat);
- if (Result < 0) {
- PRINT_ERROR("Failed to stat file %s: %s.\n",
- FileName,
- strerror(errno));
- Failures += 1;
- continue;
- }
- FileId[FileIndex] = FileStat.st_ino;
- DEBUG_PRINT("Set file %s ID to %d.\n",
- FileName,
- FileId[FileIndex]);
- MountCounts[FileIndex] -= 1;
- SimultaneousMounts -= 1;
- break;
- case MountTestActionDelete:
- DEBUG_PRINT("Deleting file %s\n", FileName);
- Result = MountTestDeleteFile(FileName, FileIsDirectory);
- if (Result != 0) {
- if ((MountCounts[FileIndex] == 0) || (errno != EBUSY)) {
- PRINT_ERROR("Failed to delete %s: %s.\n",
- FileName,
- strerror(errno));
- Failures += 1;
- }
- continue;
- }
- assert(MountCounts[FileIndex] == 0);
- FileId[FileIndex] = -1;
- break;
- default:
- assert(FALSE);
- break;
- }
- if ((Iteration % Percent) == 0) {
- PRINT("%s", MountTestProgressCharacter[TestType]);
- }
- }
- //
- // Clean up all files.
- //
- if (MountTestNoCleanup == FALSE) {
- for (FileIndex = 0; FileIndex < MountCount; FileIndex += 1) {
- if (FileId[FileIndex] != -1) {
- snprintf(FileName,
- sizeof(FileName),
- MountTestFileNameFormat[TestType],
- Process & 0xFFFF,
- FileIndex);
- while (MountCounts[FileIndex] != 0) {
- snprintf(MountCommand,
- sizeof(MountCommand),
- MOUNT_TEST_UNMOUNT_LAZY_FORMAT,
- FileName,
- MOUNT_TEST_LOG);
- Result = system(MountCommand);
- if (WIFSIGNALED(Result) &&
- ((WTERMSIG(Result) == SIGINT) ||
- (WTERMSIG(Result) == SIGQUIT))) {
- goto RunMountGenericTestEnd;
- }
- if (Result != 0) {
- PRINT_ERROR("Failed to unmount %s: %s.\n",
- FileName,
- strerror(errno));
- Failures += 1;
- break;
- }
- MountCounts[FileIndex] -= 1;
- }
- if (MountCounts[FileIndex] == 0) {
- Result = MountTestDeleteFile(FileName, FileIsDirectory);
- if (Result != 0) {
- PRINT_ERROR("Failed to delete %s: %s.\n",
- FileName,
- strerror(errno));
- Failures += 1;
- }
- }
- }
- }
- }
- PRINT("\nMax usage: %d mounts.\n", MaxSimultaneousMounts);
- RunMountGenericTestEnd:
- if (FileId != NULL) {
- free(FileId);
- }
- if (MountCounts != NULL) {
- free(MountCounts);
- }
- return Failures;
- }
- INT
- MountTestCreateFile (
- PSTR FileName,
- BOOL FileIsDirectory
- )
- /*++
- Routine Description:
- This routine creates a file with the given name.
- Arguments:
- FileName - Supplies a string representing the desired file name.
- FileIsDirectory - Supplies a boolean indicating if the file is a directory.
- Return Value:
- 0 on success, or -1 on failure.
- --*/
- {
- INT File;
- INT OpenFlags;
- INT Result;
- if (FileIsDirectory == FALSE) {
- OpenFlags = O_RDWR | O_CREAT;
- Result = open(FileName, OpenFlags, MOUNT_TEST_CREATE_PERMISSIONS);
- if (Result < 0) {
- PRINT_ERROR("Failed to create file %s (flags %x): %s.\n",
- FileName,
- OpenFlags,
- strerror(errno));
- goto CreateFileEnd;
- }
- File = Result;
- Result = close(File);
- if (Result != 0) {
- PRINT_ERROR("Failed to close: %s.\n", strerror(errno));
- goto CreateFileEnd;
- }
- } else {
- Result = mkdir(FileName, MOUNT_TEST_CREATE_PERMISSIONS);
- if (Result < 0) {
- PRINT_ERROR("Failed to create directory %s: %s.\n",
- FileName,
- strerror(errno));
- goto CreateFileEnd;
- }
- }
- CreateFileEnd:
- return Result;
- }
- INT
- MountTestDeleteFile (
- PSTR FileName,
- BOOL FileIsDirectory
- )
- /*++
- Routine Description:
- This routine delets a file with the given name.
- Arguments:
- FileName - Supplies a string representing the desired file name.
- FileIsDirectory - Supplies a boolean indicating if the file is a directory.
- Return Value:
- 0 on success, or -1 on failure.
- --*/
- {
- INT Result;
- if (FileIsDirectory == FALSE) {
- Result = unlink(FileName);
- } else {
- Result = rmdir(FileName);
- }
- return Result;
- }
- PSTR
- AppendPaths (
- PSTR Path1,
- PSTR Path2
- )
- /*++
- Routine Description:
- This routine creates a concatenated string of "Path1/Path2".
- Arguments:
- Path1 - Supplies a pointer to the prefix of the combined path.
- Path2 - Supplies a pointer to the path to append.
- Return Value:
- Returns a pointer to the appended path on success. The caller is
- responsible for freeing this memory.
- NULL on failure.
- --*/
- {
- PSTR AppendedPath;
- size_t Offset;
- size_t Path1Length;
- size_t Path2Length;
- BOOL SlashNeeded;
- assert((Path1 != NULL) && (Path2 != NULL));
- Path1Length = strlen(Path1);
- Path2Length = strlen(Path2);
- SlashNeeded = TRUE;
- if ((Path1Length == 0) || (Path1[Path1Length - 1] == '/') ||
- (Path1[Path1Length - 1] == '\\')) {
- SlashNeeded = FALSE;
- }
- AppendedPath = malloc(Path1Length + Path2Length + 2);
- if (AppendedPath == NULL) {
- return NULL;
- }
- Offset = 0;
- if (Path1Length != 0) {
- memcpy(AppendedPath, Path1, Path1Length);
- Offset += Path1Length;
- }
- if (SlashNeeded != FALSE) {
- AppendedPath[Offset] = '/';
- Offset += 1;
- }
- strcpy(AppendedPath + Offset, Path2);
- return AppendedPath;
- }
|