pathtest.c 20 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859
  1. /*++
  2. Copyright (c) 2013 Minoca Corp. All Rights Reserved
  3. Module Name:
  4. pathtest.c
  5. Abstract:
  6. This module implements the tests used to verify that system's paths are
  7. functioning properly.
  8. Author:
  9. Chris Stevens 17-Sept-2013
  10. Environment:
  11. User Mode
  12. --*/
  13. //
  14. // ------------------------------------------------------------------- Includes
  15. //
  16. #include <stdio.h>
  17. #include <stdlib.h>
  18. #include <stdbool.h>
  19. #include <unistd.h>
  20. #include <errno.h>
  21. #include <sys/stat.h>
  22. #include <sys/wait.h>
  23. //
  24. // --------------------------------------------------------------------- Macros
  25. //
  26. #define PATHTEST_DEBUG_PRINT(...) \
  27. if (PathTestVerbose != false) { \
  28. printf(__VA_ARGS__); \
  29. }
  30. #define PATHTEST_ERROR(...) printf(__VA_ARGS__)
  31. //
  32. // ---------------------------------------------------------------- Definitions
  33. //
  34. //
  35. // ------------------------------------------------------ Data Type Definitions
  36. //
  37. //
  38. // ----------------------------------------------- Internal Function Prototypes
  39. //
  40. int
  41. RunAllPathTests (
  42. void
  43. );
  44. int
  45. RunSerialDirectoryTests (
  46. void
  47. );
  48. int
  49. RunParallelDirectoryTests (
  50. int Index
  51. );
  52. int
  53. RunHardLinkTests (
  54. void
  55. );
  56. //
  57. // -------------------------------------------------------------------- Globals
  58. //
  59. //
  60. // Set this to TRUE to enable more verbose debug output.
  61. //
  62. bool PathTestVerbose = false;
  63. //
  64. // ------------------------------------------------------------------ Functions
  65. //
  66. int
  67. main (
  68. int ArgumentCount,
  69. char **Arguments
  70. )
  71. /*++
  72. Routine Description:
  73. This routine implements the path test program.
  74. Arguments:
  75. ArgumentCount - Supplies the number of elements in the arguments array.
  76. Arguments - Supplies an array of strings. The array count is bounded by the
  77. previous parameter, and the strings are null-terminated.
  78. Return Value:
  79. 0 on success.
  80. Non-zero on failure.
  81. --*/
  82. {
  83. if ((ArgumentCount == 2) && (strcmp(Arguments[1], "-v") == 0)) {
  84. PathTestVerbose = true;
  85. }
  86. return RunAllPathTests();
  87. }
  88. //
  89. // --------------------------------------------------------- Internal Functions
  90. //
  91. int
  92. RunAllPathTests (
  93. void
  94. )
  95. /*++
  96. Routine Description:
  97. This routine executes all path tests.
  98. Arguments:
  99. None.
  100. Return Value:
  101. Returns the number of failures in the test suite.
  102. --*/
  103. {
  104. int Failures;
  105. int Index;
  106. Failures = 0;
  107. //
  108. // Run the serial tests a few times.
  109. //
  110. PATHTEST_DEBUG_PRINT("Start Serial Tests\n");
  111. for (Index = 0; Index < 5; Index += 1) {
  112. Failures += RunSerialDirectoryTests();
  113. }
  114. PATHTEST_DEBUG_PRINT("End Serial Tests\n");
  115. //
  116. // Run the parallel tests a few times.
  117. //
  118. PATHTEST_DEBUG_PRINT("Start Parallel Tests\n");
  119. for (Index = 0; Index < 6; Index += 1) {
  120. Failures += RunParallelDirectoryTests(Index);
  121. }
  122. PATHTEST_DEBUG_PRINT("End Parallel Tests\n");
  123. //
  124. // Run the hard link tests a few times.
  125. //
  126. PATHTEST_DEBUG_PRINT("Start Hard Link Tests\n");
  127. for (Index = 0; Index < 5; Index += 1) {
  128. Failures += RunHardLinkTests();
  129. }
  130. PATHTEST_DEBUG_PRINT("End Hard Link Tests\n");
  131. //
  132. // Display the test pass state.
  133. //
  134. if (Failures != 0) {
  135. PATHTEST_ERROR("*** %d failures in path tests. ***\n", Failures);
  136. } else {
  137. printf("All path tests pass.\n");
  138. }
  139. return Failures;
  140. }
  141. int
  142. RunSerialDirectoryTests (
  143. void
  144. )
  145. /*++
  146. Routine Description:
  147. This routine runs tests on directories serially.
  148. Arguments:
  149. None.
  150. Return Value:
  151. Returns the number of failures in the test.
  152. --*/
  153. {
  154. int Failures;
  155. int Result;
  156. //
  157. // Make a directory and remove it. It will be a child of the current
  158. // directory.
  159. //
  160. Failures = 0;
  161. Result = mkdir("pathtest1", S_IRWXU | S_IRWXG | S_IRWXO);
  162. if (Result != 0) {
  163. Failures += 1;
  164. PATHTEST_ERROR("Failed to create directory pathtest1!\n");
  165. goto SerialDirectoryTestsEnd;
  166. }
  167. Result = rmdir("pathtest1");
  168. if (Result != 0) {
  169. Failures += 1;
  170. PATHTEST_ERROR("Failed to remove directory pathtest1!\n");
  171. goto SerialDirectoryTestsEnd;
  172. }
  173. //
  174. // Great, it cleaned up well. Create it again.
  175. //
  176. Result = mkdir("pathtest1", S_IRWXU | S_IRWXG | S_IRWXO);
  177. if (Result != 0) {
  178. Failures += 1;
  179. PATHTEST_ERROR("Failed to create directory pathtest1!\n");
  180. goto SerialDirectoryTestsEnd;
  181. }
  182. //
  183. // Now make a child directory.
  184. //
  185. Result = mkdir("pathtest1/pathtest2", S_IRWXU | S_IRWXG | S_IRWXO);
  186. if (Result != 0) {
  187. Failures += 1;
  188. PATHTEST_ERROR("Failed to create directory pathtest1/pathtest2!\n");
  189. goto SerialDirectoryTestsEnd;
  190. }
  191. //
  192. // Change directories into it and back.
  193. //
  194. Result = chdir("pathtest1/pathtest2");
  195. if (Result != 0) {
  196. Failures += 1;
  197. PATHTEST_ERROR("Failed to change directory to pathtest1/pathtest2\n");
  198. goto SerialDirectoryTestsEnd;
  199. }
  200. Result = chdir("../..");
  201. if (Result != 0) {
  202. Failures += 1;
  203. PATHTEST_ERROR("Failed to change directory to ../..\n");
  204. goto SerialDirectoryTestsEnd;
  205. }
  206. //
  207. // Now try to remove the parent directory. This should fail.
  208. //
  209. Result = rmdir("pathtest1");
  210. if (Result == 0) {
  211. Failures += 1;
  212. PATHTEST_ERROR("Succeeded to remove directory pathtest1, expected to "
  213. "fail with status ENOTEMPTY!\n");
  214. goto SerialDirectoryTestsEnd;
  215. }
  216. if (errno != ENOTEMPTY) {
  217. Failures += 1;
  218. PATHTEST_ERROR("'rmdir' failed with incorrect status. Expected %d, "
  219. "received %d.\n", ENOTEMPTY, errno);
  220. goto SerialDirectoryTestsEnd;
  221. }
  222. //
  223. // Now remove the child directory.
  224. //
  225. Result = rmdir("pathtest1/pathtest2");
  226. if (Result != 0) {
  227. Failures += 1;
  228. PATHTEST_ERROR("Failed to remove directory pathtest1/pathtest2!\n");
  229. goto SerialDirectoryTestsEnd;
  230. }
  231. //
  232. // Now try to change directories into pathtest1\pathtest2.
  233. //
  234. Result = chdir("pathtest1/pathtest2");
  235. if (Result == 0) {
  236. Failures += 1;
  237. PATHTEST_ERROR("Successfully changed directories to "
  238. "pathtest1/pathtest2. Expected to fail.\n");
  239. goto SerialDirectoryTestsEnd;
  240. }
  241. if (errno != ENOENT) {
  242. Failures += 1;
  243. PATHTEST_ERROR("Failed to change directories to pathtest1/pathtest2. "
  244. "Failure expected %d, received %d.\n",
  245. ENOENT,
  246. errno);
  247. goto SerialDirectoryTestsEnd;
  248. }
  249. //
  250. // Try to create a directory inside of the removed directory.
  251. //
  252. Result = mkdir("pathtest1/pathtest2/pathtest3",
  253. S_IRWXU | S_IRWXG | S_IRWXO);
  254. if (Result == 0) {
  255. Failures += 1;
  256. PATHTEST_ERROR("Successfully created directory "
  257. "pathtest1/pathtest2/pathtest3. Expected to fail.\n");
  258. goto SerialDirectoryTestsEnd;
  259. }
  260. if (errno != ENOENT) {
  261. Failures += 1;
  262. PATHTEST_ERROR("Failed to make directory to "
  263. "pathtest1/pathtest2/pathtest3. Failure expected %d, "
  264. "received %d.\n",
  265. ENOENT,
  266. errno);
  267. goto SerialDirectoryTestsEnd;
  268. }
  269. //
  270. // Ok. Now clean it up.
  271. //
  272. Result = rmdir("pathtest1");
  273. if (Result != 0) {
  274. Failures += 1;
  275. PATHTEST_ERROR("Failed to remove directory pathtest1!\n");
  276. goto SerialDirectoryTestsEnd;
  277. }
  278. SerialDirectoryTestsEnd:
  279. return Failures;
  280. }
  281. int
  282. RunParallelDirectoryTests (
  283. int Index
  284. )
  285. /*++
  286. Routine Description:
  287. This routine runs tests on directories in parallel.
  288. Arguments:
  289. Index - Supplies a value indicating which iteration of the parallel test
  290. this is.
  291. Return Value:
  292. Returns the number of failures in the test.
  293. --*/
  294. {
  295. pid_t Child;
  296. int ChildResult;
  297. int Failures;
  298. char *OriginalDirectory;
  299. int Result;
  300. int Status;
  301. pid_t WaitPid;
  302. Failures = 0;
  303. //
  304. // Save the original directory.
  305. //
  306. OriginalDirectory = getcwd(NULL, 0);
  307. if (OriginalDirectory == NULL) {
  308. Failures += 1;
  309. PATHTEST_ERROR("Failed to get original directory.\n");
  310. goto ParallelDirectoryTestsEnd;
  311. }
  312. //
  313. // Test adding a child to a directory while it is in the middle of being
  314. // removed. First create the directory.
  315. //
  316. Result = mkdir("pathtest1", S_IRWXU | S_IRWXG | S_IRWXO);
  317. if (Result != 0) {
  318. Failures += 1;
  319. PATHTEST_ERROR("Failed to create directory pathtest1 with error %d.\n",
  320. errno);
  321. goto ParallelDirectoryTestsEnd;
  322. }
  323. Child = fork();
  324. if (Child == -1) {
  325. Failures += 1;
  326. PATHTEST_ERROR("Failed to create child process.\n");
  327. goto ParallelDirectoryTestsEnd;
  328. }
  329. //
  330. // If this is the child process, try to create a directory if the index is
  331. // even and try to remove the directory if the index is odd.
  332. //
  333. if (Child == 0) {
  334. if ((Index % 2) == 0) {
  335. Result = mkdir("pathtest1/pathtest2", S_IRWXU | S_IRWXG | S_IRWXO);
  336. if (Result == 0) {
  337. PATHTEST_DEBUG_PRINT("Child created pathtest1/pathtest2.\n");
  338. } else {
  339. PATHTEST_DEBUG_PRINT("Child failed to create "
  340. "pathtest1/pathtest2 with error %d.\n",
  341. errno);
  342. if (errno != ENOENT) {
  343. Result = 1;
  344. PATHTEST_ERROR("Child failed to create pathtest1/pathtest2 "
  345. "with error %d, expected error %d.\n",
  346. errno,
  347. ENOENT);
  348. }
  349. }
  350. } else {
  351. Result = rmdir("pathtest1");
  352. if (Result == 0) {
  353. PATHTEST_DEBUG_PRINT("Child removed pathtest1.\n");
  354. } else {
  355. PATHTEST_DEBUG_PRINT("Child failed to remove pathtest1 with "
  356. "error %d.\n",
  357. errno);
  358. if (errno != ENOTEMPTY) {
  359. Result = 1;
  360. PATHTEST_ERROR("Child failed to remove pathtest1 with "
  361. "error %d, expected error %d.\n",
  362. errno,
  363. ENOTEMPTY);
  364. }
  365. }
  366. }
  367. PATHTEST_DEBUG_PRINT("Child %d exiting with status %d.\n",
  368. getpid(),
  369. Result);
  370. exit(Result);
  371. //
  372. // If this is the parent process, try to remove the directory if the index
  373. // is even and try to create a new directory if the index is odd.
  374. //
  375. } else {
  376. if ((Index % 2) == 0) {
  377. Result = rmdir("pathtest1");
  378. if (Result == 0) {
  379. PATHTEST_DEBUG_PRINT("Parent removed pathtest1.\n");
  380. } else {
  381. PATHTEST_DEBUG_PRINT("Parent failed to remove pathtest1 with "
  382. "error %d.\n",
  383. errno);
  384. if (errno != ENOTEMPTY) {
  385. Result = 1;
  386. PATHTEST_ERROR("Parent failed to remove pathtest1 with "
  387. "error %d, expected error %d.\n",
  388. errno,
  389. ENOTEMPTY);
  390. }
  391. }
  392. } else {
  393. Result = mkdir("pathtest1/pathtest2", S_IRWXU | S_IRWXG | S_IRWXO);
  394. if (Result == 0) {
  395. PATHTEST_DEBUG_PRINT("Parent created pathtest1/pathtest2.\n");
  396. } else {
  397. PATHTEST_DEBUG_PRINT("Parent failed to create "
  398. "pathtest1/pathtest2 with error %d.\n",
  399. errno);
  400. if (errno != ENOENT) {
  401. Result = 1;
  402. PATHTEST_ERROR("Parent failed to create "
  403. "pathtest1/pathtest2 with error %d, "
  404. "expected error %d.\n",
  405. errno,
  406. ENOENT);
  407. }
  408. }
  409. }
  410. //
  411. // Wait for the child to exit.
  412. //
  413. WaitPid = waitpid(Child, &Status, WUNTRACED | WCONTINUED);
  414. if (WaitPid != Child) {
  415. Failures += 1;
  416. PATHTEST_ERROR("waitpid returned %d instead of child pid %d.\n",
  417. WaitPid,
  418. Child);
  419. }
  420. //
  421. // Check the flags and return value.
  422. //
  423. if ((!WIFEXITED(Status)) ||
  424. (WIFCONTINUED(Status)) ||
  425. (WIFSIGNALED(Status)) ||
  426. (WIFSTOPPED(Status))) {
  427. Failures += 1;
  428. PATHTEST_ERROR("Child status was not exited as expected. Was %x\n",
  429. Status);
  430. }
  431. //
  432. // The child exit status is the result of it's attempt to create a
  433. // directory. Make sure the child's experience matches the parent's.
  434. //
  435. ChildResult = WEXITSTATUS(Status);
  436. //
  437. // Both should not have succeeded.
  438. //
  439. if ((ChildResult == 0) && (Result == 0)) {
  440. Failures += 1;
  441. PATHTEST_ERROR("Both parent and child succeeded. One of them "
  442. "should have failed.\n");
  443. //
  444. // If the one succeeded and the other did not fail with the appropriate
  445. // error (-1), then something went wrong.
  446. //
  447. } else if ((ChildResult == 0) && (Result != -1)) {
  448. Failures += 1;
  449. PATHTEST_ERROR("Child succeeded, but parent failed with unexpected "
  450. "error.\n");
  451. } else if ((Result == 0) &&
  452. ((unsigned char)ChildResult != (unsigned char)-1)) {
  453. Failures += 1;
  454. PATHTEST_ERROR("Parent succeeded, but child failed with unexpected "
  455. "error.\n");
  456. }
  457. }
  458. ParallelDirectoryTestsEnd:
  459. //
  460. // Change back to the original directory and try to remove the created
  461. // directory.
  462. //
  463. if (OriginalDirectory != NULL) {
  464. Result = chdir(OriginalDirectory);
  465. if (Result != 0) {
  466. Failures += 1;
  467. PATHTEST_ERROR("Failed to 'cd' to %s.\n", OriginalDirectory);
  468. } else {
  469. rmdir("pathtest1/pathtest2");
  470. rmdir("pathtest1");
  471. }
  472. free(OriginalDirectory);
  473. }
  474. return Failures;
  475. }
  476. int
  477. RunHardLinkTests (
  478. void
  479. )
  480. /*++
  481. Routine Description:
  482. This routine runs tests on directory hard links.
  483. Arguments:
  484. None.
  485. Return Value:
  486. Returns the number of failures in the test.
  487. --*/
  488. {
  489. pid_t Child;
  490. int ChildResult;
  491. int Failures;
  492. char *OriginalDirectory;
  493. int Result;
  494. struct stat Stat;
  495. int Status;
  496. pid_t WaitPid;
  497. Failures = 0;
  498. //
  499. // Save the original directory.
  500. //
  501. OriginalDirectory = getcwd(NULL, 0);
  502. if (OriginalDirectory == NULL) {
  503. Failures += 1;
  504. PATHTEST_ERROR("Failed to get original directory.\n");
  505. goto HardLinkTestsEnd;
  506. }
  507. //
  508. // Test stating a directory after it has been removed. First create the
  509. // directory and 'cd' into it.
  510. //
  511. Result = mkdir("pathtest1", S_IRWXU | S_IRWXG | S_IRWXO);
  512. if (Result != 0) {
  513. Failures += 1;
  514. PATHTEST_ERROR("Failed to create directory pathtest1 with error %d.\n",
  515. errno);
  516. goto HardLinkTestsEnd;
  517. }
  518. Result = chdir("pathtest1");
  519. if (Result != 0) {
  520. Failures += 1;
  521. PATHTEST_ERROR("Failed to change directories to pathtest1 with error "
  522. "%d.\n",
  523. errno);
  524. goto HardLinkTestsEnd;
  525. }
  526. Child = fork();
  527. if (Child == -1) {
  528. Failures += 1;
  529. PATHTEST_ERROR("Failed to create child process.\n");
  530. goto HardLinkTestsEnd;
  531. }
  532. //
  533. // If this is the child process, 'cd' out of the directory and then
  534. // remove it.
  535. //
  536. if (Child == 0) {
  537. Result = chdir("..");
  538. if (Result != 0) {
  539. PATHTEST_ERROR("Child failed to change directories to .. with "
  540. "error %d.\n",
  541. errno);
  542. exit(errno);
  543. }
  544. //
  545. // Try to remove the directory.
  546. //
  547. Result = rmdir("pathtest1");
  548. if (Result != 0) {
  549. PATHTEST_ERROR("Child failed to remove pathtest1 with error %d.\n",
  550. errno);
  551. exit(errno);
  552. }
  553. PATHTEST_DEBUG_PRINT("Child %d exiting with status %d.\n",
  554. getpid(),
  555. Result);
  556. exit(Result);
  557. //
  558. // If this is the parent process, wait for the child to exit and then make
  559. // sure that the current directory is 'pathtest1' and stat the directory to
  560. // check to make sure it has a hard link count of 0.
  561. //
  562. } else {
  563. //
  564. // Wait for the child to exit.
  565. //
  566. WaitPid = waitpid(Child, &Status, WUNTRACED | WCONTINUED);
  567. if (WaitPid != Child) {
  568. PATHTEST_ERROR("waitpid returned %d instead of child pid %d.\n",
  569. WaitPid,
  570. Child);
  571. Failures += 1;
  572. }
  573. //
  574. // Check the flags and return value.
  575. //
  576. if ((!WIFEXITED(Status)) ||
  577. (WIFCONTINUED(Status)) ||
  578. (WIFSIGNALED(Status)) ||
  579. (WIFSTOPPED(Status))) {
  580. Failures += 1;
  581. PATHTEST_ERROR("Child status was not exited as expected. Was %x\n",
  582. Status);
  583. }
  584. //
  585. // The child exit status is the result of it's attempt to create a
  586. // directory. Make sure the child's experience matches the parent's.
  587. //
  588. ChildResult = WEXITSTATUS(Status);
  589. if (ChildResult != 0) {
  590. Failures += 1;
  591. PATHTEST_ERROR("Child did not exit with expected status. Expected "
  592. "0, received %d.\n",
  593. ChildResult);
  594. goto HardLinkTestsEnd;
  595. }
  596. //
  597. // Validate the current working directory is not accessible via
  598. // 'getcwd' which performs a reverse path walk.
  599. //
  600. if (creat("myfile", 0777) == 0) {
  601. Failures += 1;
  602. PATHTEST_ERROR("Succeeded in creating a file in a deleted "
  603. "directory.\n");
  604. }
  605. //
  606. // Now stat the current directory.
  607. //
  608. Result = stat(".", &Stat);
  609. if (Result != 0) {
  610. Failures += 1;
  611. PATHTEST_ERROR("Failed to stat current directory in parent with "
  612. "error %d.\n",
  613. errno);
  614. goto HardLinkTestsEnd;
  615. }
  616. //
  617. // Make sure the hard link count is 0.
  618. //
  619. if (Stat.st_nlink != 0) {
  620. Failures += 1;
  621. PATHTEST_ERROR("Unexpected hard link count for directory "
  622. "pathtest1. Expected 0, but it has %lu hard "
  623. "links.\n",
  624. Stat.st_nlink);
  625. goto HardLinkTestsEnd;
  626. }
  627. }
  628. HardLinkTestsEnd:
  629. //
  630. // Change back to the original directory and try to remove the created
  631. // directory.
  632. //
  633. if (OriginalDirectory != NULL) {
  634. Result = chdir(OriginalDirectory);
  635. if (Result != 0) {
  636. Failures += 1;
  637. PATHTEST_ERROR("Failed to 'cd' to %s.\n", OriginalDirectory);
  638. } else {
  639. rmdir("pathtest1");
  640. }
  641. free(OriginalDirectory);
  642. }
  643. return Failures;
  644. }