1f08c3bdfSopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later 2f08c3bdfSopenharmony_ci/* 3f08c3bdfSopenharmony_ci * Copyright (c) 2022 FUJITSU LIMITED. All rights reserved. 4f08c3bdfSopenharmony_ci * Author: Yang Xu <xuyang2018.jy@fujitsu.com> 5f08c3bdfSopenharmony_ci */ 6f08c3bdfSopenharmony_ci 7f08c3bdfSopenharmony_ci/*\ 8f08c3bdfSopenharmony_ci * [Description] 9f08c3bdfSopenharmony_ci * 10f08c3bdfSopenharmony_ci * Tests basic error handling of the quotactl syscall without visible quota files 11f08c3bdfSopenharmony_ci * (use quotactl and quotactl_fd syscall): 12f08c3bdfSopenharmony_ci * 13f08c3bdfSopenharmony_ci * - EFAULT when addr or special is invalid 14f08c3bdfSopenharmony_ci * - EINVAL when cmd or type is invalid 15f08c3bdfSopenharmony_ci * - ENOTBLK when special is not a block device 16f08c3bdfSopenharmony_ci * - ERANGE when cmd is Q_SETQUOTA, but the specified limits are out of the range 17f08c3bdfSopenharmony_ci * allowed by the quota format 18f08c3bdfSopenharmony_ci * - EPERM when the caller lacked the required privilege (CAP_SYS_ADMIN) for the 19f08c3bdfSopenharmony_ci * specified operation 20f08c3bdfSopenharmony_ci * - ENOSYS when cmd is Q_QUOTAON, but the fd refers to a socket 21f08c3bdfSopenharmony_ci * 22f08c3bdfSopenharmony_ci * Minimum e2fsprogs version required is 1.43. 23f08c3bdfSopenharmony_ci */ 24f08c3bdfSopenharmony_ci 25f08c3bdfSopenharmony_ci#include <errno.h> 26f08c3bdfSopenharmony_ci#include <sys/quota.h> 27f08c3bdfSopenharmony_ci#include <sys/socket.h> 28f08c3bdfSopenharmony_ci#include "tst_test.h" 29f08c3bdfSopenharmony_ci#include "tst_capability.h" 30f08c3bdfSopenharmony_ci#include "quotactl_syscall_var.h" 31f08c3bdfSopenharmony_ci 32f08c3bdfSopenharmony_ci#define OPTION_INVALID 999 33f08c3bdfSopenharmony_ci 34f08c3bdfSopenharmony_cistatic int32_t fmt_id = QFMT_VFS_V1; 35f08c3bdfSopenharmony_cistatic int test_id; 36f08c3bdfSopenharmony_cistatic int getnextquota_nsup, socket_fd = -1; 37f08c3bdfSopenharmony_ci 38f08c3bdfSopenharmony_cistatic struct if_nextdqblk res_ndq; 39f08c3bdfSopenharmony_ci 40f08c3bdfSopenharmony_cistatic struct dqblk set_dqmax = { 41f08c3bdfSopenharmony_ci .dqb_bsoftlimit = 0x7fffffffffffffffLL, /* 2^63-1 */ 42f08c3bdfSopenharmony_ci .dqb_valid = QIF_BLIMITS 43f08c3bdfSopenharmony_ci}; 44f08c3bdfSopenharmony_ci 45f08c3bdfSopenharmony_cistatic struct tst_cap dropadmin = { 46f08c3bdfSopenharmony_ci .action = TST_CAP_DROP, 47f08c3bdfSopenharmony_ci .id = CAP_SYS_ADMIN, 48f08c3bdfSopenharmony_ci .name = "CAP_SYS_ADMIN", 49f08c3bdfSopenharmony_ci}; 50f08c3bdfSopenharmony_ci 51f08c3bdfSopenharmony_cistatic struct tst_cap needadmin = { 52f08c3bdfSopenharmony_ci .action = TST_CAP_REQ, 53f08c3bdfSopenharmony_ci .id = CAP_SYS_ADMIN, 54f08c3bdfSopenharmony_ci .name = "CAP_SYS_ADMIN", 55f08c3bdfSopenharmony_ci}; 56f08c3bdfSopenharmony_ci 57f08c3bdfSopenharmony_cistatic struct tcase { 58f08c3bdfSopenharmony_ci int cmd; 59f08c3bdfSopenharmony_ci int *id; 60f08c3bdfSopenharmony_ci void *addr; 61f08c3bdfSopenharmony_ci int exp_err; 62f08c3bdfSopenharmony_ci int on_flag; 63f08c3bdfSopenharmony_ci char *des; 64f08c3bdfSopenharmony_ci} tcases[] = { 65f08c3bdfSopenharmony_ci {QCMD(Q_SETQUOTA, USRQUOTA), &fmt_id, NULL, EFAULT, 1, 66f08c3bdfSopenharmony_ci "EFAULT when addr or special is invalid"}, 67f08c3bdfSopenharmony_ci 68f08c3bdfSopenharmony_ci {QCMD(OPTION_INVALID, USRQUOTA), &fmt_id, NULL, EINVAL, 0, 69f08c3bdfSopenharmony_ci "EINVAL when cmd or type is invalid"}, 70f08c3bdfSopenharmony_ci 71f08c3bdfSopenharmony_ci {QCMD(Q_QUOTAON, USRQUOTA), &fmt_id, NULL, ENOTBLK, 0, 72f08c3bdfSopenharmony_ci "ENOTBLK when special is not a block device"}, 73f08c3bdfSopenharmony_ci 74f08c3bdfSopenharmony_ci {QCMD(Q_SETQUOTA, USRQUOTA), &test_id, &set_dqmax, ERANGE, 1, 75f08c3bdfSopenharmony_ci "ERANGE when cmd is Q_SETQUOTA, but the specified limits are out of the range"}, 76f08c3bdfSopenharmony_ci 77f08c3bdfSopenharmony_ci {QCMD(Q_QUOTAON, USRQUOTA), &fmt_id, NULL, EPERM, 0, 78f08c3bdfSopenharmony_ci "EPERM when the caller lacks required privilege(CAP_SYS_ADMIN)"}, 79f08c3bdfSopenharmony_ci 80f08c3bdfSopenharmony_ci {QCMD(Q_QUOTAON, USRQUOTA), &fmt_id, NULL, ENOSYS, 0, 81f08c3bdfSopenharmony_ci "EINVAL when cmd is Q_QUOTAON, but the fd refers to a socket"} 82f08c3bdfSopenharmony_ci}; 83f08c3bdfSopenharmony_ci 84f08c3bdfSopenharmony_cistatic void verify_quotactl(unsigned int n) 85f08c3bdfSopenharmony_ci{ 86f08c3bdfSopenharmony_ci struct tcase *tc = &tcases[n]; 87f08c3bdfSopenharmony_ci int quota_on = 0; 88f08c3bdfSopenharmony_ci int drop_flag = 0; 89f08c3bdfSopenharmony_ci 90f08c3bdfSopenharmony_ci tst_res(TINFO, "Testing %s", tc->des); 91f08c3bdfSopenharmony_ci if (tc->cmd == QCMD(Q_GETNEXTQUOTA, USRQUOTA) && getnextquota_nsup) { 92f08c3bdfSopenharmony_ci tst_res(TCONF, "current system doesn't support Q_GETNEXTQUOTA"); 93f08c3bdfSopenharmony_ci return; 94f08c3bdfSopenharmony_ci } 95f08c3bdfSopenharmony_ci 96f08c3bdfSopenharmony_ci if (tc->on_flag) { 97f08c3bdfSopenharmony_ci TST_EXP_PASS_SILENT(do_quotactl(fd, QCMD(Q_QUOTAON, USRQUOTA), tst_device->dev, 98f08c3bdfSopenharmony_ci fmt_id, NULL), "do_quotactl(QCMD(Q_QUOTAON, USRQUOTA))"); 99f08c3bdfSopenharmony_ci if (!TST_PASS) 100f08c3bdfSopenharmony_ci return; 101f08c3bdfSopenharmony_ci quota_on = 1; 102f08c3bdfSopenharmony_ci } 103f08c3bdfSopenharmony_ci 104f08c3bdfSopenharmony_ci if (tc->exp_err == EPERM) { 105f08c3bdfSopenharmony_ci tst_cap_action(&dropadmin); 106f08c3bdfSopenharmony_ci drop_flag = 1; 107f08c3bdfSopenharmony_ci } 108f08c3bdfSopenharmony_ci 109f08c3bdfSopenharmony_ci if (tst_variant) { 110f08c3bdfSopenharmony_ci if (tc->exp_err == ENOTBLK) { 111f08c3bdfSopenharmony_ci tst_res(TCONF, "quotactl_fd() doesn't have this error, skip"); 112f08c3bdfSopenharmony_ci return; 113f08c3bdfSopenharmony_ci } 114f08c3bdfSopenharmony_ci if (tc->exp_err == ENOSYS) { 115f08c3bdfSopenharmony_ci TST_EXP_FAIL(syscall(__NR_quotactl_fd, socket_fd, tc->cmd, *tc->id, 116f08c3bdfSopenharmony_ci tc->addr), tc->exp_err, "syscall(quotactl_fd)"); 117f08c3bdfSopenharmony_ci return; 118f08c3bdfSopenharmony_ci } 119f08c3bdfSopenharmony_ci } else { 120f08c3bdfSopenharmony_ci if (tc->exp_err == ENOSYS) { 121f08c3bdfSopenharmony_ci tst_res(TCONF, "quotactl() doesn't use fd, skip"); 122f08c3bdfSopenharmony_ci return; 123f08c3bdfSopenharmony_ci } 124f08c3bdfSopenharmony_ci } 125f08c3bdfSopenharmony_ci if (tc->exp_err == ENOTBLK) 126f08c3bdfSopenharmony_ci TST_EXP_FAIL(do_quotactl(fd, tc->cmd, "/dev/null", *tc->id, tc->addr), 127f08c3bdfSopenharmony_ci ENOTBLK, "do_quotactl()"); 128f08c3bdfSopenharmony_ci else 129f08c3bdfSopenharmony_ci TST_EXP_FAIL(do_quotactl(fd, tc->cmd, tst_device->dev, *tc->id, tc->addr), 130f08c3bdfSopenharmony_ci tc->exp_err, "do_quotactl()"); 131f08c3bdfSopenharmony_ci 132f08c3bdfSopenharmony_ci if (quota_on) { 133f08c3bdfSopenharmony_ci TST_EXP_PASS_SILENT(do_quotactl(fd, QCMD(Q_QUOTAOFF, USRQUOTA), tst_device->dev, 134f08c3bdfSopenharmony_ci fmt_id, NULL), "do_quotactl(QCMD(Q_QUOTAOFF, USRQUOTA)"); 135f08c3bdfSopenharmony_ci if (!TST_PASS) 136f08c3bdfSopenharmony_ci return; 137f08c3bdfSopenharmony_ci } 138f08c3bdfSopenharmony_ci 139f08c3bdfSopenharmony_ci if (drop_flag) 140f08c3bdfSopenharmony_ci tst_cap_action(&needadmin); 141f08c3bdfSopenharmony_ci} 142f08c3bdfSopenharmony_ci 143f08c3bdfSopenharmony_cistatic void setup(void) 144f08c3bdfSopenharmony_ci{ 145f08c3bdfSopenharmony_ci unsigned int i; 146f08c3bdfSopenharmony_ci 147f08c3bdfSopenharmony_ci quotactl_info(); 148f08c3bdfSopenharmony_ci 149f08c3bdfSopenharmony_ci socket_fd = SAFE_SOCKET(PF_INET, SOCK_STREAM, 0); 150f08c3bdfSopenharmony_ci fd = SAFE_OPEN(MNTPOINT, O_RDONLY); 151f08c3bdfSopenharmony_ci TEST(do_quotactl(fd, QCMD(Q_GETNEXTQUOTA, USRQUOTA), tst_device->dev, 152f08c3bdfSopenharmony_ci test_id, (void *) &res_ndq)); 153f08c3bdfSopenharmony_ci if (TST_ERR == EINVAL || TST_ERR == ENOSYS) 154f08c3bdfSopenharmony_ci getnextquota_nsup = 1; 155f08c3bdfSopenharmony_ci 156f08c3bdfSopenharmony_ci for (i = 0; i < ARRAY_SIZE(tcases); i++) { 157f08c3bdfSopenharmony_ci if (!tcases[i].addr) 158f08c3bdfSopenharmony_ci tcases[i].addr = tst_get_bad_addr(NULL); 159f08c3bdfSopenharmony_ci } 160f08c3bdfSopenharmony_ci} 161f08c3bdfSopenharmony_ci 162f08c3bdfSopenharmony_cistatic void cleanup(void) 163f08c3bdfSopenharmony_ci{ 164f08c3bdfSopenharmony_ci if (fd > -1) 165f08c3bdfSopenharmony_ci SAFE_CLOSE(fd); 166f08c3bdfSopenharmony_ci if (socket_fd > -1) 167f08c3bdfSopenharmony_ci SAFE_CLOSE(socket_fd); 168f08c3bdfSopenharmony_ci} 169f08c3bdfSopenharmony_ci 170f08c3bdfSopenharmony_cistatic struct tst_test test = { 171f08c3bdfSopenharmony_ci .setup = setup, 172f08c3bdfSopenharmony_ci .cleanup = cleanup, 173f08c3bdfSopenharmony_ci .needs_drivers = (const char *const []) { 174f08c3bdfSopenharmony_ci "quota_v2", 175f08c3bdfSopenharmony_ci NULL 176f08c3bdfSopenharmony_ci }, 177f08c3bdfSopenharmony_ci .tcnt = ARRAY_SIZE(tcases), 178f08c3bdfSopenharmony_ci .test = verify_quotactl, 179f08c3bdfSopenharmony_ci .dev_fs_opts = (const char *const[]){"-O quota", NULL}, 180f08c3bdfSopenharmony_ci .dev_fs_type = "ext4", 181f08c3bdfSopenharmony_ci .mntpoint = MNTPOINT, 182f08c3bdfSopenharmony_ci .mount_device = 1, 183f08c3bdfSopenharmony_ci .needs_root = 1, 184f08c3bdfSopenharmony_ci .test_variants = QUOTACTL_SYSCALL_VARIANTS, 185f08c3bdfSopenharmony_ci .needs_cmds = (const char *[]) { 186f08c3bdfSopenharmony_ci "mkfs.ext4 >= 1.43.0", 187f08c3bdfSopenharmony_ci NULL 188f08c3bdfSopenharmony_ci } 189f08c3bdfSopenharmony_ci}; 190