fchmodat2_test.c 3.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142
  1. // SPDX-License-Identifier: GPL-2.0-or-later
  2. #define _GNU_SOURCE
  3. #include <fcntl.h>
  4. #include <sys/stat.h>
  5. #include <sys/types.h>
  6. #include <syscall.h>
  7. #include <unistd.h>
  8. #include "kselftest.h"
  9. int sys_fchmodat2(int dfd, const char *filename, mode_t mode, int flags)
  10. {
  11. int ret = syscall(__NR_fchmodat2, dfd, filename, mode, flags);
  12. return ret >= 0 ? ret : -errno;
  13. }
  14. int setup_testdir(void)
  15. {
  16. int dfd, ret;
  17. char dirname[] = "/tmp/ksft-fchmodat2.XXXXXX";
  18. /* Make the top-level directory. */
  19. if (!mkdtemp(dirname))
  20. ksft_exit_fail_msg("%s: failed to create tmpdir\n", __func__);
  21. dfd = open(dirname, O_PATH | O_DIRECTORY);
  22. if (dfd < 0)
  23. ksft_exit_fail_msg("%s: failed to open tmpdir\n", __func__);
  24. ret = openat(dfd, "regfile", O_CREAT | O_WRONLY | O_TRUNC, 0644);
  25. if (ret < 0)
  26. ksft_exit_fail_msg("%s: failed to create file in tmpdir\n",
  27. __func__);
  28. close(ret);
  29. ret = symlinkat("regfile", dfd, "symlink");
  30. if (ret < 0)
  31. ksft_exit_fail_msg("%s: failed to create symlink in tmpdir\n",
  32. __func__);
  33. return dfd;
  34. }
  35. int expect_mode(int dfd, const char *filename, mode_t expect_mode)
  36. {
  37. struct stat st;
  38. int ret = fstatat(dfd, filename, &st, AT_SYMLINK_NOFOLLOW);
  39. if (ret)
  40. ksft_exit_fail_msg("%s: %s: fstatat failed\n",
  41. __func__, filename);
  42. return (st.st_mode == expect_mode);
  43. }
  44. void test_regfile(void)
  45. {
  46. int dfd, ret;
  47. dfd = setup_testdir();
  48. ret = sys_fchmodat2(dfd, "regfile", 0640, 0);
  49. if (ret < 0)
  50. ksft_exit_fail_msg("%s: fchmodat2(noflag) failed\n", __func__);
  51. if (!expect_mode(dfd, "regfile", 0100640))
  52. ksft_exit_fail_msg("%s: wrong file mode bits after fchmodat2\n",
  53. __func__);
  54. ret = sys_fchmodat2(dfd, "regfile", 0600, AT_SYMLINK_NOFOLLOW);
  55. if (ret < 0)
  56. ksft_exit_fail_msg("%s: fchmodat2(AT_SYMLINK_NOFOLLOW) failed\n",
  57. __func__);
  58. if (!expect_mode(dfd, "regfile", 0100600))
  59. ksft_exit_fail_msg("%s: wrong file mode bits after fchmodat2 with nofollow\n",
  60. __func__);
  61. ksft_test_result_pass("fchmodat2(regfile)\n");
  62. }
  63. void test_symlink(void)
  64. {
  65. int dfd, ret;
  66. dfd = setup_testdir();
  67. ret = sys_fchmodat2(dfd, "symlink", 0640, 0);
  68. if (ret < 0)
  69. ksft_exit_fail_msg("%s: fchmodat2(noflag) failed\n", __func__);
  70. if (!expect_mode(dfd, "regfile", 0100640))
  71. ksft_exit_fail_msg("%s: wrong file mode bits after fchmodat2\n",
  72. __func__);
  73. if (!expect_mode(dfd, "symlink", 0120777))
  74. ksft_exit_fail_msg("%s: wrong symlink mode bits after fchmodat2\n",
  75. __func__);
  76. ret = sys_fchmodat2(dfd, "symlink", 0600, AT_SYMLINK_NOFOLLOW);
  77. /*
  78. * On certain filesystems (xfs or btrfs), chmod operation fails. So we
  79. * first check the symlink target but if the operation fails we mark the
  80. * test as skipped.
  81. *
  82. * https://sourceware.org/legacy-ml/libc-alpha/2020-02/msg00467.html
  83. */
  84. if (ret == 0 && !expect_mode(dfd, "symlink", 0120600))
  85. ksft_exit_fail_msg("%s: wrong symlink mode bits after fchmodat2 with nofollow\n",
  86. __func__);
  87. if (!expect_mode(dfd, "regfile", 0100640))
  88. ksft_exit_fail_msg("%s: wrong file mode bits after fchmodat2 with nofollow\n",
  89. __func__);
  90. if (ret != 0)
  91. ksft_test_result_skip("fchmodat2(symlink)\n");
  92. else
  93. ksft_test_result_pass("fchmodat2(symlink)\n");
  94. }
  95. #define NUM_TESTS 2
  96. int main(int argc, char **argv)
  97. {
  98. ksft_print_header();
  99. ksft_set_plan(NUM_TESTS);
  100. test_regfile();
  101. test_symlink();
  102. if (ksft_get_fail_cnt() + ksft_get_error_cnt() > 0)
  103. ksft_exit_fail();
  104. else
  105. ksft_exit_pass();
  106. }