162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later 262306a36Sopenharmony_ci 362306a36Sopenharmony_ci#define _GNU_SOURCE 462306a36Sopenharmony_ci#include <fcntl.h> 562306a36Sopenharmony_ci#include <sys/stat.h> 662306a36Sopenharmony_ci#include <sys/types.h> 762306a36Sopenharmony_ci#include <syscall.h> 862306a36Sopenharmony_ci#include <unistd.h> 962306a36Sopenharmony_ci 1062306a36Sopenharmony_ci#include "../kselftest.h" 1162306a36Sopenharmony_ci 1262306a36Sopenharmony_ciint sys_fchmodat2(int dfd, const char *filename, mode_t mode, int flags) 1362306a36Sopenharmony_ci{ 1462306a36Sopenharmony_ci int ret = syscall(__NR_fchmodat2, dfd, filename, mode, flags); 1562306a36Sopenharmony_ci 1662306a36Sopenharmony_ci return ret >= 0 ? ret : -errno; 1762306a36Sopenharmony_ci} 1862306a36Sopenharmony_ci 1962306a36Sopenharmony_ciint setup_testdir(void) 2062306a36Sopenharmony_ci{ 2162306a36Sopenharmony_ci int dfd, ret; 2262306a36Sopenharmony_ci char dirname[] = "/tmp/ksft-fchmodat2.XXXXXX"; 2362306a36Sopenharmony_ci 2462306a36Sopenharmony_ci /* Make the top-level directory. */ 2562306a36Sopenharmony_ci if (!mkdtemp(dirname)) 2662306a36Sopenharmony_ci ksft_exit_fail_msg("%s: failed to create tmpdir\n", __func__); 2762306a36Sopenharmony_ci 2862306a36Sopenharmony_ci dfd = open(dirname, O_PATH | O_DIRECTORY); 2962306a36Sopenharmony_ci if (dfd < 0) 3062306a36Sopenharmony_ci ksft_exit_fail_msg("%s: failed to open tmpdir\n", __func__); 3162306a36Sopenharmony_ci 3262306a36Sopenharmony_ci ret = openat(dfd, "regfile", O_CREAT | O_WRONLY | O_TRUNC, 0644); 3362306a36Sopenharmony_ci if (ret < 0) 3462306a36Sopenharmony_ci ksft_exit_fail_msg("%s: failed to create file in tmpdir\n", 3562306a36Sopenharmony_ci __func__); 3662306a36Sopenharmony_ci close(ret); 3762306a36Sopenharmony_ci 3862306a36Sopenharmony_ci ret = symlinkat("regfile", dfd, "symlink"); 3962306a36Sopenharmony_ci if (ret < 0) 4062306a36Sopenharmony_ci ksft_exit_fail_msg("%s: failed to create symlink in tmpdir\n", 4162306a36Sopenharmony_ci __func__); 4262306a36Sopenharmony_ci 4362306a36Sopenharmony_ci return dfd; 4462306a36Sopenharmony_ci} 4562306a36Sopenharmony_ci 4662306a36Sopenharmony_ciint expect_mode(int dfd, const char *filename, mode_t expect_mode) 4762306a36Sopenharmony_ci{ 4862306a36Sopenharmony_ci struct stat st; 4962306a36Sopenharmony_ci int ret = fstatat(dfd, filename, &st, AT_SYMLINK_NOFOLLOW); 5062306a36Sopenharmony_ci 5162306a36Sopenharmony_ci if (ret) 5262306a36Sopenharmony_ci ksft_exit_fail_msg("%s: %s: fstatat failed\n", 5362306a36Sopenharmony_ci __func__, filename); 5462306a36Sopenharmony_ci 5562306a36Sopenharmony_ci return (st.st_mode == expect_mode); 5662306a36Sopenharmony_ci} 5762306a36Sopenharmony_ci 5862306a36Sopenharmony_civoid test_regfile(void) 5962306a36Sopenharmony_ci{ 6062306a36Sopenharmony_ci int dfd, ret; 6162306a36Sopenharmony_ci 6262306a36Sopenharmony_ci dfd = setup_testdir(); 6362306a36Sopenharmony_ci 6462306a36Sopenharmony_ci ret = sys_fchmodat2(dfd, "regfile", 0640, 0); 6562306a36Sopenharmony_ci 6662306a36Sopenharmony_ci if (ret < 0) 6762306a36Sopenharmony_ci ksft_exit_fail_msg("%s: fchmodat2(noflag) failed\n", __func__); 6862306a36Sopenharmony_ci 6962306a36Sopenharmony_ci if (!expect_mode(dfd, "regfile", 0100640)) 7062306a36Sopenharmony_ci ksft_exit_fail_msg("%s: wrong file mode bits after fchmodat2\n", 7162306a36Sopenharmony_ci __func__); 7262306a36Sopenharmony_ci 7362306a36Sopenharmony_ci ret = sys_fchmodat2(dfd, "regfile", 0600, AT_SYMLINK_NOFOLLOW); 7462306a36Sopenharmony_ci 7562306a36Sopenharmony_ci if (ret < 0) 7662306a36Sopenharmony_ci ksft_exit_fail_msg("%s: fchmodat2(AT_SYMLINK_NOFOLLOW) failed\n", 7762306a36Sopenharmony_ci __func__); 7862306a36Sopenharmony_ci 7962306a36Sopenharmony_ci if (!expect_mode(dfd, "regfile", 0100600)) 8062306a36Sopenharmony_ci ksft_exit_fail_msg("%s: wrong file mode bits after fchmodat2 with nofollow\n", 8162306a36Sopenharmony_ci __func__); 8262306a36Sopenharmony_ci 8362306a36Sopenharmony_ci ksft_test_result_pass("fchmodat2(regfile)\n"); 8462306a36Sopenharmony_ci} 8562306a36Sopenharmony_ci 8662306a36Sopenharmony_civoid test_symlink(void) 8762306a36Sopenharmony_ci{ 8862306a36Sopenharmony_ci int dfd, ret; 8962306a36Sopenharmony_ci 9062306a36Sopenharmony_ci dfd = setup_testdir(); 9162306a36Sopenharmony_ci 9262306a36Sopenharmony_ci ret = sys_fchmodat2(dfd, "symlink", 0640, 0); 9362306a36Sopenharmony_ci 9462306a36Sopenharmony_ci if (ret < 0) 9562306a36Sopenharmony_ci ksft_exit_fail_msg("%s: fchmodat2(noflag) failed\n", __func__); 9662306a36Sopenharmony_ci 9762306a36Sopenharmony_ci if (!expect_mode(dfd, "regfile", 0100640)) 9862306a36Sopenharmony_ci ksft_exit_fail_msg("%s: wrong file mode bits after fchmodat2\n", 9962306a36Sopenharmony_ci __func__); 10062306a36Sopenharmony_ci 10162306a36Sopenharmony_ci if (!expect_mode(dfd, "symlink", 0120777)) 10262306a36Sopenharmony_ci ksft_exit_fail_msg("%s: wrong symlink mode bits after fchmodat2\n", 10362306a36Sopenharmony_ci __func__); 10462306a36Sopenharmony_ci 10562306a36Sopenharmony_ci ret = sys_fchmodat2(dfd, "symlink", 0600, AT_SYMLINK_NOFOLLOW); 10662306a36Sopenharmony_ci 10762306a36Sopenharmony_ci /* 10862306a36Sopenharmony_ci * On certain filesystems (xfs or btrfs), chmod operation fails. So we 10962306a36Sopenharmony_ci * first check the symlink target but if the operation fails we mark the 11062306a36Sopenharmony_ci * test as skipped. 11162306a36Sopenharmony_ci * 11262306a36Sopenharmony_ci * https://sourceware.org/legacy-ml/libc-alpha/2020-02/msg00467.html 11362306a36Sopenharmony_ci */ 11462306a36Sopenharmony_ci if (ret == 0 && !expect_mode(dfd, "symlink", 0120600)) 11562306a36Sopenharmony_ci ksft_exit_fail_msg("%s: wrong symlink mode bits after fchmodat2 with nofollow\n", 11662306a36Sopenharmony_ci __func__); 11762306a36Sopenharmony_ci 11862306a36Sopenharmony_ci if (!expect_mode(dfd, "regfile", 0100640)) 11962306a36Sopenharmony_ci ksft_exit_fail_msg("%s: wrong file mode bits after fchmodat2 with nofollow\n", 12062306a36Sopenharmony_ci __func__); 12162306a36Sopenharmony_ci 12262306a36Sopenharmony_ci if (ret != 0) 12362306a36Sopenharmony_ci ksft_test_result_skip("fchmodat2(symlink)\n"); 12462306a36Sopenharmony_ci else 12562306a36Sopenharmony_ci ksft_test_result_pass("fchmodat2(symlink)\n"); 12662306a36Sopenharmony_ci} 12762306a36Sopenharmony_ci 12862306a36Sopenharmony_ci#define NUM_TESTS 2 12962306a36Sopenharmony_ci 13062306a36Sopenharmony_ciint main(int argc, char **argv) 13162306a36Sopenharmony_ci{ 13262306a36Sopenharmony_ci ksft_print_header(); 13362306a36Sopenharmony_ci ksft_set_plan(NUM_TESTS); 13462306a36Sopenharmony_ci 13562306a36Sopenharmony_ci test_regfile(); 13662306a36Sopenharmony_ci test_symlink(); 13762306a36Sopenharmony_ci 13862306a36Sopenharmony_ci if (ksft_get_fail_cnt() + ksft_get_error_cnt() > 0) 13962306a36Sopenharmony_ci ksft_exit_fail(); 14062306a36Sopenharmony_ci else 14162306a36Sopenharmony_ci ksft_exit_pass(); 14262306a36Sopenharmony_ci} 143