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