mnttest.c 35 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461
  1. /*++
  2. Copyright (c) 2013 Minoca Corp.
  3. This file is licensed under the terms of the GNU General Public License
  4. version 3. Alternative licensing terms are available. Contact
  5. info@minocacorp.com for details. See the LICENSE file at the root of this
  6. project for complete licensing information.
  7. Module Name:
  8. mnttest.c
  9. Abstract:
  10. This module implements the tests used to verify that basic mount operations
  11. are working.
  12. Author:
  13. Chris Stevens 25-Nov-2013
  14. Environment:
  15. User Mode
  16. --*/
  17. //
  18. // ------------------------------------------------------------------- Includes
  19. //
  20. #include <minoca/lib/types.h>
  21. #include <assert.h>
  22. #include <errno.h>
  23. #include <fcntl.h>
  24. #include <getopt.h>
  25. #include <stdio.h>
  26. #include <stdlib.h>
  27. #include <string.h>
  28. #include <sys/stat.h>
  29. #include <sys/wait.h>
  30. #include <sys/time.h>
  31. #include <time.h>
  32. #include <unistd.h>
  33. #include <math.h>
  34. //
  35. // --------------------------------------------------------------------- Macros
  36. //
  37. #define DEBUG_PRINT(...) \
  38. if (MountTestVerbosity >= TestVerbosityDebug) { \
  39. printf(__VA_ARGS__); \
  40. }
  41. #define PRINT(...) \
  42. if (MountTestVerbosity >= TestVerbosityNormal) { \
  43. printf(__VA_ARGS__); \
  44. }
  45. #define PRINT_ERROR(...) fprintf(stderr, "\nmnttest: " __VA_ARGS__)
  46. //
  47. // ---------------------------------------------------------------- Definitions
  48. //
  49. #define MOUNT_TEST_VERSION_MAJOR 1
  50. #define MOUNT_TEST_VERSION_MINOR 0
  51. #define MOUNT_TEST_USAGE \
  52. "Usage: mnttest [options] \n" \
  53. "This utility tests the mount and umount programs. Options are:\n" \
  54. " -c, --mount-count <count> -- Set the number of mounts to create.\n" \
  55. " -i, --iterations <count> -- Set the number of operations to perform.\n" \
  56. " -p, --threads <count> -- Set the number of threads to spin up.\n" \
  57. " -t, --test -- Set the test to perform. Valid values are all, \n" \
  58. " file, directory, and concurrency.\n" \
  59. " --debug -- Print lots of information about what's happening.\n" \
  60. " --quiet -- Print only errors.\n" \
  61. " --no-cleanup -- Leave test mount points and files around for \n" \
  62. " debugging.\n" \
  63. " --help -- Print this help text and exit.\n" \
  64. " --version -- Print the test version and exit.\n" \
  65. #define MOUNT_TEST_OPTIONS_STRING "c:i:t:p:ndqhV"
  66. #define MOUNT_TEST_CREATE_PERMISSIONS (S_IRUSR | S_IWUSR)
  67. #define DEFAULT_MOUNT_COUNT 20
  68. #define DEFAULT_OPERATION_COUNT (DEFAULT_MOUNT_COUNT * 1)
  69. #define DEFAULT_THREAD_COUNT 1
  70. #define MOUNT_TEST_LOG "mnttest.log"
  71. #define MOUNT_TEST_MOUNT_FORMAT "mount --bind %s %s 2>> %s"
  72. #define MOUNT_TEST_MOUNT_RECURSIVE_FORMAT "mount --rbind %s %s 2>> %s"
  73. #define MOUNT_TEST_UNMOUNT_FORMAT "umount %s 2>> %s"
  74. #define MOUNT_TEST_UNMOUNT_LAZY_FORMAT "umount -l %s 2>> %s"
  75. //
  76. // ------------------------------------------------------ Data Type Definitions
  77. //
  78. typedef enum _MOUNT_TEST_ACTION {
  79. MountTestActionMount,
  80. MountTestActionUnmount,
  81. MountTestActionStat,
  82. MountTestActionDelete,
  83. MountTestActionCount
  84. } MOUNT_TEST_ACTION, *PMOUNT_TEST_ACTION;
  85. typedef enum _TEST_VERBOSITY {
  86. TestVerbosityQuiet,
  87. TestVerbosityNormal,
  88. TestVerbosityDebug
  89. } TEST_VERBOSITY, *PTEST_VERBOSITY;
  90. typedef enum _MOUNT_TEST_TYPE {
  91. MountTestAll,
  92. MountTestFile,
  93. MountTestDirectory,
  94. MountTestConcurrency,
  95. MountTestTypeCount
  96. } MOUNT_TEST_TYPE, *PMOUNT_TEST_TYPE;
  97. //
  98. // ----------------------------------------------- Internal Function Prototypes
  99. //
  100. ULONG
  101. RunMountFileTest (
  102. INT FileCount,
  103. INT Iterations
  104. );
  105. ULONG
  106. RunMountDirectoryTest (
  107. INT DirectoryCount,
  108. INT Iterations
  109. );
  110. ULONG
  111. RunMountConcurrencyTest (
  112. INT MountCount,
  113. INT Iterations
  114. );
  115. ULONG
  116. RunMountGenericTest (
  117. MOUNT_TEST_TYPE TestType,
  118. INT MountCount,
  119. INT Iterations
  120. );
  121. INT
  122. MountTestCreateFile (
  123. PSTR FileName,
  124. BOOL FileIsDirectory
  125. );
  126. INT
  127. MountTestDeleteFile (
  128. PSTR FileName,
  129. BOOL FileIsDirectory
  130. );
  131. PSTR
  132. AppendPaths (
  133. PSTR Path1,
  134. PSTR Path2
  135. );
  136. //
  137. // -------------------------------------------------------------------- Globals
  138. //
  139. //
  140. // Higher levels here print out more stuff.
  141. //
  142. TEST_VERBOSITY MountTestVerbosity = TestVerbosityNormal;
  143. //
  144. // Set this boolean to skip cleaning up files.
  145. //
  146. BOOL MountTestNoCleanup = FALSE;
  147. struct option MountTestLongOptions[] = {
  148. {"mount-count", required_argument, 0, 'c'},
  149. {"iterations", required_argument, 0, 'i'},
  150. {"threads", required_argument, 0, 'p'},
  151. {"test", required_argument, 0, 't'},
  152. {"no-cleanup", no_argument, 0, 'n'},
  153. {"debug", no_argument, 0, 'd'},
  154. {"quiet", no_argument, 0, 'q'},
  155. {"help", no_argument, 0, 'h'},
  156. {"version", no_argument, 0, 'V'},
  157. {NULL, 0, 0, 0},
  158. };
  159. PSTR MountTestName[MountTestTypeCount] = {
  160. NULL,
  161. "file",
  162. "directory",
  163. "concurrency"
  164. };
  165. PSTR MountTestFileNameFormat[MountTestTypeCount] = {
  166. NULL,
  167. "mft%x-%06x",
  168. "mdt%x-%06x",
  169. "mct-%06x"
  170. };
  171. PSTR MountTestProgressCharacter[MountTestTypeCount] = {
  172. NULL,
  173. "f",
  174. "d",
  175. "c"
  176. };
  177. //
  178. // ------------------------------------------------------------------ Functions
  179. //
  180. int
  181. main (
  182. int ArgumentCount,
  183. char **Arguments
  184. )
  185. /*++
  186. Routine Description:
  187. This routine implements the file test program.
  188. Arguments:
  189. ArgumentCount - Supplies the number of elements in the arguments array.
  190. Arguments - Supplies an array of strings. The array count is bounded by the
  191. previous parameter, and the strings are null-terminated.
  192. Return Value:
  193. 0 on success.
  194. Non-zero on failure.
  195. --*/
  196. {
  197. PSTR AfterScan;
  198. pid_t Child;
  199. INT ChildIndex;
  200. pid_t *Children;
  201. INT Failures;
  202. BOOL IsParent;
  203. INT Iterations;
  204. INT MountCount;
  205. INT Option;
  206. INT Status;
  207. MOUNT_TEST_TYPE Test;
  208. INT Threads;
  209. Children = NULL;
  210. Failures = 0;
  211. MountCount = DEFAULT_MOUNT_COUNT;
  212. Iterations = DEFAULT_OPERATION_COUNT;
  213. Test = MountTestAll;
  214. Threads = DEFAULT_THREAD_COUNT;
  215. Status = 0;
  216. setvbuf(stdout, NULL, _IONBF, 0);
  217. setvbuf(stderr, NULL, _IONBF, 0);
  218. srand(time(NULL));
  219. //
  220. // Process the control arguments.
  221. //
  222. while (TRUE) {
  223. Option = getopt_long(ArgumentCount,
  224. Arguments,
  225. MOUNT_TEST_OPTIONS_STRING,
  226. MountTestLongOptions,
  227. NULL);
  228. if (Option == -1) {
  229. break;
  230. }
  231. if ((Option == '?') || (Option == ':')) {
  232. Status = 1;
  233. goto MainEnd;
  234. }
  235. switch (Option) {
  236. case 'c':
  237. MountCount = strtol(optarg, &AfterScan, 0);
  238. if ((MountCount <= 0) || (AfterScan == optarg)) {
  239. PRINT_ERROR("Invalid file count %s.\n", optarg);
  240. Status = 1;
  241. goto MainEnd;
  242. }
  243. break;
  244. case 'i':
  245. Iterations = strtol(optarg, &AfterScan, 0);
  246. if ((Iterations < 0) || (AfterScan == optarg)) {
  247. PRINT_ERROR("Invalid iteration count %s.\n", optarg);
  248. Status = 1;
  249. goto MainEnd;
  250. }
  251. break;
  252. case 'n':
  253. MountTestNoCleanup = TRUE;
  254. break;
  255. case 'p':
  256. Threads = strtol(optarg, &AfterScan, 0);
  257. if ((Threads <= 0) || (AfterScan == optarg)) {
  258. PRINT_ERROR("Invalid thread count %s.\n", optarg);
  259. Status = 1;
  260. goto MainEnd;
  261. }
  262. break;
  263. case 't':
  264. if (strcasecmp(optarg, "all") == 0) {
  265. Test = MountTestAll;
  266. } else if (strcasecmp(optarg, "file") == 0) {
  267. Test = MountTestFile;
  268. } else if (strcasecmp(optarg, "directory") == 0) {
  269. Test = MountTestDirectory;
  270. } else if (strcasecmp(optarg, "concurrency") == 0) {
  271. Test = MountTestConcurrency;
  272. } else {
  273. PRINT_ERROR("Invalid test: %s.\n", optarg);
  274. Status = 1;
  275. goto MainEnd;
  276. }
  277. break;
  278. case 'd':
  279. MountTestVerbosity = TestVerbosityDebug;
  280. break;
  281. case 'q':
  282. MountTestVerbosity = TestVerbosityQuiet;
  283. break;
  284. case 'V':
  285. printf("Minoca mnttest version %d.%d\n",
  286. MOUNT_TEST_VERSION_MAJOR,
  287. MOUNT_TEST_VERSION_MINOR);
  288. return 1;
  289. case 'h':
  290. printf(MOUNT_TEST_USAGE);
  291. return 1;
  292. default:
  293. assert(FALSE);
  294. Status = 1;
  295. goto MainEnd;
  296. }
  297. }
  298. //
  299. // Destroy the mount test log file.
  300. //
  301. unlink(MOUNT_TEST_LOG);
  302. //
  303. // Fork off any children test processes.
  304. //
  305. IsParent = TRUE;
  306. if (Threads > 1) {
  307. Children = malloc(sizeof(pid_t) * (Threads - 1));
  308. if (Children == NULL) {
  309. Status = ENOMEM;
  310. goto MainEnd;
  311. }
  312. memset(Children, 0, sizeof(pid_t) * (Threads - 1));
  313. for (ChildIndex = 0; ChildIndex < Threads - 1; ChildIndex += 1) {
  314. Child = fork();
  315. //
  316. // If this is the child, break out and run the tests.
  317. //
  318. if (Child == 0) {
  319. srand(time(NULL) + ChildIndex);
  320. IsParent = FALSE;
  321. break;
  322. }
  323. Children[ChildIndex] = Child;
  324. }
  325. }
  326. //
  327. // Run the tests.
  328. //
  329. if ((Test == MountTestAll) || (Test == MountTestFile)) {
  330. Failures += RunMountFileTest(MountCount, Iterations);
  331. }
  332. if ((Test == MountTestAll) || (Test == MountTestDirectory)) {
  333. Failures += RunMountDirectoryTest(MountCount, Iterations);
  334. }
  335. if ((Test == MountTestAll) || (Test == MountTestConcurrency)) {
  336. Failures += RunMountConcurrencyTest(MountCount, Iterations);
  337. }
  338. //
  339. // Wait for any children.
  340. //
  341. if (IsParent != FALSE) {
  342. if (Threads > 1) {
  343. for (ChildIndex = 0; ChildIndex < Threads - 1; ChildIndex += 1) {
  344. Child = waitpid(Children[ChildIndex], &Status, 0);
  345. if (Child == -1) {
  346. PRINT_ERROR("Failed to wait for child %d: %s.\n",
  347. Children[ChildIndex],
  348. strerror(errno));
  349. Status = errno;
  350. } else {
  351. assert(Child == Children[ChildIndex]);
  352. if (!WIFEXITED(Status)) {
  353. PRINT_ERROR("Child %d returned with status %x\n",
  354. Status);
  355. Failures += 1;
  356. }
  357. Failures += WEXITSTATUS(Status);
  358. Status = 0;
  359. }
  360. }
  361. }
  362. //
  363. // If this is a child, just report back the number of failures to the
  364. // parent.
  365. //
  366. } else {
  367. if (Failures > 100) {
  368. exit(100);
  369. } else {
  370. exit(Failures);
  371. }
  372. }
  373. MainEnd:
  374. if (Children != NULL) {
  375. free(Children);
  376. }
  377. if (Status != 0) {
  378. PRINT_ERROR("Error: %d.\n", Status);
  379. }
  380. if (Failures != 0) {
  381. PRINT_ERROR("\n *** %d failures in mnttest ***\n", Failures);
  382. return Failures;
  383. }
  384. return 0;
  385. }
  386. //
  387. // --------------------------------------------------------- Internal Functions
  388. //
  389. ULONG
  390. RunMountFileTest (
  391. INT FileCount,
  392. INT Iterations
  393. )
  394. /*++
  395. Routine Description:
  396. This routine executes the mount file test.
  397. Arguments:
  398. FileCount - Supplies the number of files to work with.
  399. Iterations - Supplies the number of iterations to perform.
  400. Return Value:
  401. Returns the number of failures in the test suite.
  402. --*/
  403. {
  404. return RunMountGenericTest(MountTestFile, FileCount, Iterations);
  405. }
  406. ULONG
  407. RunMountDirectoryTest (
  408. INT DirectoryCount,
  409. INT Iterations
  410. )
  411. /*++
  412. Routine Description:
  413. This routine executes the mount directory test.
  414. Arguments:
  415. DirectoryCount - Supplies the number of directories to work with.
  416. Iterations - Supplies the number of iterations to perform.
  417. Return Value:
  418. Returns the number of failures in the test suite.
  419. --*/
  420. {
  421. return RunMountGenericTest(MountTestDirectory, DirectoryCount, Iterations);
  422. }
  423. ULONG
  424. RunMountConcurrencyTest (
  425. INT MountCount,
  426. INT Iterations
  427. )
  428. /*++
  429. Routine Description:
  430. This routine tests creating and removing mount points concurrently.
  431. Arguments:
  432. MountCount - Supplies the number of mount points to work with.
  433. Iterations - Supplies the number of iterations to perform.
  434. Return Value:
  435. Returns the number of failures in the test suite.
  436. --*/
  437. {
  438. MOUNT_TEST_ACTION Action;
  439. ULONG Failures;
  440. INT File;
  441. INT FileIndex;
  442. CHAR FileName[16];
  443. struct stat FileStat;
  444. ino_t Inode;
  445. INT Iteration;
  446. CHAR MountCommand[64];
  447. INT OpenFlags;
  448. INT Percent;
  449. pid_t Process;
  450. INT Result;
  451. INT TargetIndex;
  452. CHAR TargetName[16];
  453. Failures = 0;
  454. //
  455. // Announce the test.
  456. //
  457. Process = getpid();
  458. PRINT("Process %d Running mount %s test with %d files, %d iterations.\n",
  459. Process,
  460. MountTestName[MountTestConcurrency],
  461. MountCount,
  462. Iterations);
  463. Percent = Iterations / 100;
  464. if (Percent == 0) {
  465. Percent = 1;
  466. }
  467. //
  468. // Perform the mount operations. This test creates files and then mounts
  469. // them, unmounts them, or checks their file ID. The files are shared
  470. // across all threads running the test.
  471. //
  472. for (Iteration = 0; Iteration < Iterations; Iteration += 1) {
  473. //
  474. // Pick a random file and a random action.
  475. //
  476. FileIndex = rand() % MountCount;
  477. snprintf(FileName,
  478. sizeof(FileName),
  479. MountTestFileNameFormat[MountTestConcurrency],
  480. FileIndex);
  481. Action = rand() % MountTestActionCount;
  482. switch (Action) {
  483. case MountTestActionStat:
  484. OpenFlags = O_RDWR | O_CREAT;
  485. File = open(FileName, OpenFlags, MOUNT_TEST_CREATE_PERMISSIONS);
  486. if (File < 0) {
  487. PRINT_ERROR("Failed to open file %s (flags %x): %s.\n",
  488. FileName,
  489. OpenFlags,
  490. strerror(errno));
  491. Failures += 1;
  492. continue;
  493. }
  494. Result = fstat(File, &FileStat);
  495. if (Result < 0) {
  496. PRINT_ERROR("Failed to stat file %s: %s.\n",
  497. FileName,
  498. strerror(errno));
  499. Failures += 1;
  500. continue;
  501. }
  502. if (FileStat.st_size == 0) {
  503. Inode = FileStat.st_ino;
  504. do {
  505. Result = write(File, &Inode, sizeof(ino_t));
  506. } while ((Result < 0) && (errno == EINTR));
  507. if (Result != sizeof(ino_t)) {
  508. PRINT_ERROR("Write failed. Wrote %d of %d bytes to file "
  509. "%s: %s.\n",
  510. Result,
  511. sizeof(ino_t),
  512. FileName,
  513. strerror(errno));
  514. Failures += 1;
  515. }
  516. } else {
  517. Inode = 0;
  518. do {
  519. Result = read(File, &Inode, sizeof(ino_t));
  520. } while ((Result < 0) && (errno == EINTR));
  521. if (Result != sizeof(ino_t)) {
  522. PRINT_ERROR("Read failed. Read %d of %d bytes from file "
  523. "%s: %s\n.",
  524. Result,
  525. sizeof(ino_t),
  526. FileName,
  527. strerror(errno));
  528. Failures += 1;
  529. }
  530. if (Inode != FileStat.st_ino) {
  531. PRINT_ERROR("Mismatching inode for file %s. Read %d, "
  532. "expected %d.\n",
  533. FileName,
  534. Inode,
  535. FileStat.st_ino);
  536. Failures += 1;
  537. }
  538. }
  539. Result = close(File);
  540. if (Result != 0) {
  541. PRINT_ERROR("Failed to close: %s.\n", strerror(errno));
  542. Failures += 1;
  543. continue;
  544. }
  545. break;
  546. case MountTestActionMount:
  547. //
  548. // Pick another random file to mount on.
  549. //
  550. TargetIndex = rand() % MountCount;
  551. snprintf(TargetName,
  552. sizeof(TargetName),
  553. MountTestFileNameFormat[MountTestConcurrency],
  554. TargetIndex);
  555. //
  556. // Create both files to give the mount some chance of working.
  557. //
  558. Result = MountTestCreateFile(FileName, FALSE);
  559. if (Result < 0) {
  560. Failures += 1;
  561. continue;
  562. }
  563. Result = MountTestCreateFile(TargetName, FALSE);
  564. if (Result < 0) {
  565. Failures += 1;
  566. continue;
  567. }
  568. DEBUG_PRINT("Mount file %s onto file %s\n", FileName, TargetName);
  569. snprintf(MountCommand,
  570. sizeof(MountCommand),
  571. MOUNT_TEST_MOUNT_FORMAT,
  572. FileName,
  573. TargetName,
  574. MOUNT_TEST_LOG);
  575. Result = system(MountCommand);
  576. if (WIFSIGNALED(Result) &&
  577. ((WTERMSIG(Result) == SIGINT) ||
  578. (WTERMSIG(Result) == SIGQUIT))) {
  579. goto RunMountConcurrencyTestEnd;
  580. }
  581. if ((Result != 0) && (Result != ENOFILE)) {
  582. PRINT_ERROR("Failed to mount %s onto %s: %s.\n",
  583. FileName,
  584. TargetName,
  585. strerror(errno));
  586. Failures += 1;
  587. continue;
  588. }
  589. break;
  590. case MountTestActionUnmount:
  591. DEBUG_PRINT("Unmounting file %s\n", FileName);
  592. snprintf(MountCommand,
  593. sizeof(MountCommand),
  594. MOUNT_TEST_UNMOUNT_LAZY_FORMAT,
  595. FileName,
  596. MOUNT_TEST_LOG);
  597. Result = system(MountCommand);
  598. if (WIFSIGNALED(Result) &&
  599. ((WTERMSIG(Result) == SIGINT) ||
  600. (WTERMSIG(Result) == SIGQUIT))) {
  601. goto RunMountConcurrencyTestEnd;
  602. }
  603. if ((Result != 0) && (Result != EINVAL) && (Result != ENOFILE)) {
  604. PRINT_ERROR("Failed to unmount %s: %d.\n",
  605. FileName,
  606. Result);
  607. Failures += 1;
  608. continue;
  609. }
  610. break;
  611. case MountTestActionDelete:
  612. DEBUG_PRINT("Deleting file %s\n", FileName);
  613. Result = MountTestDeleteFile(FileName, TRUE);
  614. if ((Result != 0) && (errno != EBUSY) && (errno != ENOFILE)) {
  615. PRINT_ERROR("Failed to delete %s: %s.\n",
  616. FileName,
  617. strerror(errno));
  618. Failures += 1;
  619. continue;
  620. }
  621. break;
  622. default:
  623. assert(FALSE);
  624. break;
  625. }
  626. if ((Iteration % Percent) == 0) {
  627. PRINT("%s", MountTestProgressCharacter[MountTestConcurrency]);
  628. }
  629. }
  630. //
  631. // Clean up all files.
  632. //
  633. if (MountTestNoCleanup == FALSE) {
  634. for (FileIndex = 0; FileIndex < MountCount; FileIndex += 1) {
  635. snprintf(FileName,
  636. sizeof(FileName),
  637. MountTestFileNameFormat[MountTestConcurrency],
  638. FileIndex);
  639. snprintf(MountCommand,
  640. sizeof(MountCommand),
  641. MOUNT_TEST_UNMOUNT_LAZY_FORMAT,
  642. FileName,
  643. MOUNT_TEST_LOG);
  644. while (TRUE) {
  645. Result = system(MountCommand);
  646. if (WIFSIGNALED(Result) &&
  647. ((WTERMSIG(Result) == SIGINT) ||
  648. (WTERMSIG(Result) == SIGQUIT))) {
  649. goto RunMountConcurrencyTestEnd;
  650. }
  651. if (Result != 0) {
  652. if ((Result != EINVAL) && (Result != ENOFILE)) {
  653. PRINT_ERROR("Failed to unmount %s: %s.\n",
  654. FileName,
  655. Result,
  656. strerror(errno));
  657. Failures += 1;
  658. }
  659. break;
  660. }
  661. }
  662. Result = MountTestDeleteFile(FileName, FALSE);
  663. if ((Result != 0) && (errno != ENOFILE)) {
  664. PRINT_ERROR("Failed to delete %s: %s.\n",
  665. FileName,
  666. strerror(errno));
  667. Failures += 1;
  668. }
  669. }
  670. }
  671. PRINT("\n");
  672. RunMountConcurrencyTestEnd:
  673. return Failures;
  674. }
  675. ULONG
  676. RunMountGenericTest (
  677. MOUNT_TEST_TYPE TestType,
  678. INT MountCount,
  679. INT Iterations
  680. )
  681. /*++
  682. Routine Description:
  683. This routine executes a generic non-concurrent mount test.
  684. Arguments:
  685. TestType - Supplies the type of generic test being executed.
  686. MountCount - Supplies the number of mountable items to work with.
  687. Iterations - Supplies the number of iterations to perform.
  688. Return Value:
  689. Returns the number of failures in the test suite.
  690. --*/
  691. {
  692. MOUNT_TEST_ACTION Action;
  693. ULONG Failures;
  694. PINT FileId;
  695. INT FileIndex;
  696. BOOL FileIsDirectory;
  697. CHAR FileName[16];
  698. struct stat FileStat;
  699. INT Iteration;
  700. INT MaxSimultaneousMounts;
  701. CHAR MountCommand[64];
  702. PINT MountCounts;
  703. INT Percent;
  704. pid_t Process;
  705. INT Result;
  706. INT SimultaneousMounts;
  707. INT TargetIndex;
  708. CHAR TargetName[16];
  709. Failures = 0;
  710. FileId = NULL;
  711. MountCounts = NULL;
  712. //
  713. // Announce the test.
  714. //
  715. Process = getpid();
  716. PRINT("Process %d Running mount %s test with %d mount points, "
  717. "%d iterations.\n",
  718. Process,
  719. MountTestName[TestType],
  720. MountCount,
  721. Iterations);
  722. Percent = Iterations / 100;
  723. if (Percent == 0) {
  724. Percent = 1;
  725. }
  726. if (TestType == MountTestFile) {
  727. FileIsDirectory = FALSE;
  728. } else {
  729. assert(TestType == MountTestDirectory);
  730. FileIsDirectory = TRUE;
  731. }
  732. MaxSimultaneousMounts = 0;
  733. SimultaneousMounts = 0;
  734. FileId = malloc(MountCount * sizeof(INT));
  735. if (FileId == NULL) {
  736. Failures += 1;
  737. goto RunMountGenericTestEnd;
  738. }
  739. for (FileIndex = 0; FileIndex < MountCount; FileIndex += 1) {
  740. FileId[FileIndex] = -1;
  741. }
  742. MountCounts = malloc(MountCount * sizeof(INT));
  743. if (MountCounts == NULL) {
  744. Failures += 1;
  745. goto RunMountGenericTestEnd;
  746. }
  747. memset(MountCounts, 0, MountCount * sizeof(INT));
  748. //
  749. // Perform the mount operations. This test creates files and then mounts
  750. // them, unmounts them, or checks their file ID.
  751. //
  752. for (Iteration = 0; Iteration < Iterations; Iteration += 1) {
  753. //
  754. // Pick a random file and a random action.
  755. //
  756. FileIndex = rand() % MountCount;
  757. snprintf(FileName,
  758. sizeof(FileName),
  759. MountTestFileNameFormat[TestType],
  760. Process & 0xFFFF,
  761. FileIndex);
  762. Action = rand() % MountTestActionCount;
  763. //
  764. // If the file has yet to be created, then the action must be a stat.
  765. //
  766. if (FileId[FileIndex] == -1) {
  767. Action = MountTestActionStat;
  768. //
  769. // If the file has yet to be mounted, do not unmount it.
  770. //
  771. } else if ((Action == MountTestActionUnmount) &&
  772. (MountCounts[FileIndex] == 0)) {
  773. Action = MountTestActionMount;
  774. }
  775. switch (Action) {
  776. case MountTestActionStat:
  777. if (FileId[FileIndex] == -1) {
  778. Result = MountTestCreateFile(FileName, FileIsDirectory);
  779. if (Result < 0) {
  780. Failures += 1;
  781. continue;
  782. }
  783. }
  784. Result = stat(FileName, &FileStat);
  785. if (Result < 0) {
  786. PRINT_ERROR("Failed to stat file %s: %s.\n",
  787. FileName,
  788. strerror(errno));
  789. Failures += 1;
  790. continue;
  791. }
  792. if (FileId[FileIndex] == -1) {
  793. FileId[FileIndex] = FileStat.st_ino;
  794. DEBUG_PRINT("Set file %s ID to %d.\n",
  795. FileName,
  796. FileStat.st_ino);
  797. } else if (FileId[FileIndex] != FileStat.st_ino) {
  798. PRINT_ERROR("Failed to match file ID for file %s. Expected %d "
  799. "but read %d.\n",
  800. FileName,
  801. FileId[FileIndex],
  802. FileStat.st_ino);
  803. Failures += 1;
  804. continue;
  805. } else {
  806. DEBUG_PRINT("File %s ID is %d, as expected.\n",
  807. FileName,
  808. FileStat.st_ino);
  809. }
  810. break;
  811. case MountTestActionMount:
  812. //
  813. // Pick another random file to mount on.
  814. //
  815. TargetIndex = rand() % MountCount;
  816. snprintf(TargetName,
  817. sizeof(TargetName),
  818. MountTestFileNameFormat[TestType],
  819. Process & 0xFFFF,
  820. TargetIndex);
  821. if (FileId[TargetIndex] == -1) {
  822. Result = MountTestCreateFile(TargetName, FileIsDirectory);
  823. if (Result < 0) {
  824. Failures += 1;
  825. continue;
  826. }
  827. Result = stat(TargetName, &FileStat);
  828. if (Result < 0) {
  829. PRINT_ERROR("Failed to stat file %s: %s.\n",
  830. TargetName,
  831. strerror(errno));
  832. Failures += 1;
  833. continue;
  834. }
  835. FileId[TargetIndex] = FileStat.st_ino;
  836. }
  837. DEBUG_PRINT("Mount file %s onto file %s\n", FileName, TargetName);
  838. snprintf(MountCommand,
  839. sizeof(MountCommand),
  840. MOUNT_TEST_MOUNT_FORMAT,
  841. FileName,
  842. TargetName,
  843. MOUNT_TEST_LOG);
  844. Result = system(MountCommand);
  845. if (WIFSIGNALED(Result) &&
  846. ((WTERMSIG(Result) == SIGINT) ||
  847. (WTERMSIG(Result) == SIGQUIT))) {
  848. goto RunMountGenericTestEnd;
  849. }
  850. if (Result != 0) {
  851. PRINT_ERROR("Failed to mount %s onto %s: %s.\n",
  852. FileName,
  853. TargetName,
  854. strerror(errno));
  855. Failures += 1;
  856. continue;
  857. }
  858. SimultaneousMounts += 1;
  859. if (SimultaneousMounts > MaxSimultaneousMounts) {
  860. MaxSimultaneousMounts = SimultaneousMounts;
  861. }
  862. //
  863. // Update the targets mount count and file ID information.
  864. //
  865. MountCounts[TargetIndex] += 1;
  866. FileId[TargetIndex] = FileId[FileIndex];
  867. DEBUG_PRINT("Set file %s ID to %d.\n",
  868. TargetName,
  869. FileId[TargetIndex]);
  870. break;
  871. case MountTestActionUnmount:
  872. DEBUG_PRINT("Unmounting file %s\n", FileName);
  873. snprintf(MountCommand,
  874. sizeof(MountCommand),
  875. MOUNT_TEST_UNMOUNT_FORMAT,
  876. FileName,
  877. MOUNT_TEST_LOG);
  878. Result = system(MountCommand);
  879. if (WIFSIGNALED(Result) &&
  880. ((WTERMSIG(Result) == SIGINT) ||
  881. (WTERMSIG(Result) == SIGQUIT))) {
  882. goto RunMountGenericTestEnd;
  883. }
  884. if (Result != 0) {
  885. if (MountCounts[FileIndex] != 0) {
  886. PRINT_ERROR("Failed to unmount %s: %s.\n",
  887. FileName,
  888. strerror(errno));
  889. Failures += 1;
  890. }
  891. continue;
  892. }
  893. //
  894. // Recollect the stat information now that a file has been
  895. // unmounted at this path.
  896. //
  897. Result = stat(FileName, &FileStat);
  898. if (Result < 0) {
  899. PRINT_ERROR("Failed to stat file %s: %s.\n",
  900. FileName,
  901. strerror(errno));
  902. Failures += 1;
  903. continue;
  904. }
  905. FileId[FileIndex] = FileStat.st_ino;
  906. DEBUG_PRINT("Set file %s ID to %d.\n",
  907. FileName,
  908. FileId[FileIndex]);
  909. MountCounts[FileIndex] -= 1;
  910. SimultaneousMounts -= 1;
  911. break;
  912. case MountTestActionDelete:
  913. DEBUG_PRINT("Deleting file %s\n", FileName);
  914. Result = MountTestDeleteFile(FileName, FileIsDirectory);
  915. if (Result != 0) {
  916. if ((MountCounts[FileIndex] == 0) || (errno != EBUSY)) {
  917. PRINT_ERROR("Failed to delete %s: %s.\n",
  918. FileName,
  919. strerror(errno));
  920. Failures += 1;
  921. }
  922. continue;
  923. }
  924. assert(MountCounts[FileIndex] == 0);
  925. FileId[FileIndex] = -1;
  926. break;
  927. default:
  928. assert(FALSE);
  929. break;
  930. }
  931. if ((Iteration % Percent) == 0) {
  932. PRINT("%s", MountTestProgressCharacter[TestType]);
  933. }
  934. }
  935. //
  936. // Clean up all files.
  937. //
  938. if (MountTestNoCleanup == FALSE) {
  939. for (FileIndex = 0; FileIndex < MountCount; FileIndex += 1) {
  940. if (FileId[FileIndex] != -1) {
  941. snprintf(FileName,
  942. sizeof(FileName),
  943. MountTestFileNameFormat[TestType],
  944. Process & 0xFFFF,
  945. FileIndex);
  946. while (MountCounts[FileIndex] != 0) {
  947. snprintf(MountCommand,
  948. sizeof(MountCommand),
  949. MOUNT_TEST_UNMOUNT_LAZY_FORMAT,
  950. FileName,
  951. MOUNT_TEST_LOG);
  952. Result = system(MountCommand);
  953. if (WIFSIGNALED(Result) &&
  954. ((WTERMSIG(Result) == SIGINT) ||
  955. (WTERMSIG(Result) == SIGQUIT))) {
  956. goto RunMountGenericTestEnd;
  957. }
  958. if (Result != 0) {
  959. PRINT_ERROR("Failed to unmount %s: %s.\n",
  960. FileName,
  961. strerror(errno));
  962. Failures += 1;
  963. break;
  964. }
  965. MountCounts[FileIndex] -= 1;
  966. }
  967. if (MountCounts[FileIndex] == 0) {
  968. Result = MountTestDeleteFile(FileName, FileIsDirectory);
  969. if (Result != 0) {
  970. PRINT_ERROR("Failed to delete %s: %s.\n",
  971. FileName,
  972. strerror(errno));
  973. Failures += 1;
  974. }
  975. }
  976. }
  977. }
  978. }
  979. PRINT("\nMax usage: %d mounts.\n", MaxSimultaneousMounts);
  980. RunMountGenericTestEnd:
  981. if (FileId != NULL) {
  982. free(FileId);
  983. }
  984. if (MountCounts != NULL) {
  985. free(MountCounts);
  986. }
  987. return Failures;
  988. }
  989. INT
  990. MountTestCreateFile (
  991. PSTR FileName,
  992. BOOL FileIsDirectory
  993. )
  994. /*++
  995. Routine Description:
  996. This routine creates a file with the given name.
  997. Arguments:
  998. FileName - Supplies a string representing the desired file name.
  999. FileIsDirectory - Supplies a boolean indicating if the file is a directory.
  1000. Return Value:
  1001. 0 on success, or -1 on failure.
  1002. --*/
  1003. {
  1004. INT File;
  1005. INT OpenFlags;
  1006. INT Result;
  1007. if (FileIsDirectory == FALSE) {
  1008. OpenFlags = O_RDWR | O_CREAT;
  1009. Result = open(FileName, OpenFlags, MOUNT_TEST_CREATE_PERMISSIONS);
  1010. if (Result < 0) {
  1011. PRINT_ERROR("Failed to create file %s (flags %x): %s.\n",
  1012. FileName,
  1013. OpenFlags,
  1014. strerror(errno));
  1015. goto CreateFileEnd;
  1016. }
  1017. File = Result;
  1018. Result = close(File);
  1019. if (Result != 0) {
  1020. PRINT_ERROR("Failed to close: %s.\n", strerror(errno));
  1021. goto CreateFileEnd;
  1022. }
  1023. } else {
  1024. Result = mkdir(FileName, MOUNT_TEST_CREATE_PERMISSIONS);
  1025. if (Result < 0) {
  1026. PRINT_ERROR("Failed to create directory %s: %s.\n",
  1027. FileName,
  1028. strerror(errno));
  1029. goto CreateFileEnd;
  1030. }
  1031. }
  1032. CreateFileEnd:
  1033. return Result;
  1034. }
  1035. INT
  1036. MountTestDeleteFile (
  1037. PSTR FileName,
  1038. BOOL FileIsDirectory
  1039. )
  1040. /*++
  1041. Routine Description:
  1042. This routine delets a file with the given name.
  1043. Arguments:
  1044. FileName - Supplies a string representing the desired file name.
  1045. FileIsDirectory - Supplies a boolean indicating if the file is a directory.
  1046. Return Value:
  1047. 0 on success, or -1 on failure.
  1048. --*/
  1049. {
  1050. INT Result;
  1051. if (FileIsDirectory == FALSE) {
  1052. Result = unlink(FileName);
  1053. } else {
  1054. Result = rmdir(FileName);
  1055. }
  1056. return Result;
  1057. }
  1058. PSTR
  1059. AppendPaths (
  1060. PSTR Path1,
  1061. PSTR Path2
  1062. )
  1063. /*++
  1064. Routine Description:
  1065. This routine creates a concatenated string of "Path1/Path2".
  1066. Arguments:
  1067. Path1 - Supplies a pointer to the prefix of the combined path.
  1068. Path2 - Supplies a pointer to the path to append.
  1069. Return Value:
  1070. Returns a pointer to the appended path on success. The caller is
  1071. responsible for freeing this memory.
  1072. NULL on failure.
  1073. --*/
  1074. {
  1075. PSTR AppendedPath;
  1076. size_t Offset;
  1077. size_t Path1Length;
  1078. size_t Path2Length;
  1079. BOOL SlashNeeded;
  1080. assert((Path1 != NULL) && (Path2 != NULL));
  1081. Path1Length = strlen(Path1);
  1082. Path2Length = strlen(Path2);
  1083. SlashNeeded = TRUE;
  1084. if ((Path1Length == 0) || (Path1[Path1Length - 1] == '/') ||
  1085. (Path1[Path1Length - 1] == '\\')) {
  1086. SlashNeeded = FALSE;
  1087. }
  1088. AppendedPath = malloc(Path1Length + Path2Length + 2);
  1089. if (AppendedPath == NULL) {
  1090. return NULL;
  1091. }
  1092. Offset = 0;
  1093. if (Path1Length != 0) {
  1094. memcpy(AppendedPath, Path1, Path1Length);
  1095. Offset += Path1Length;
  1096. }
  1097. if (SlashNeeded != FALSE) {
  1098. AppendedPath[Offset] = '/';
  1099. Offset += 1;
  1100. }
  1101. strcpy(AppendedPath + Offset, Path2);
  1102. return AppendedPath;
  1103. }